feat(library): P3 — heavy tier download, quality preferences, storage accounting, per-source rules - #2070
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
Warning Review limit reachedNext included review available in 1 minute. View limit detailsLimit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesThe library now supports source rules, storage summaries, heavy YouTube downloads, download status tracking, HTMX-rendered routes, and a browser interface. Tests cover storage, pipeline, download, rule, usage, and error-handling behavior. Library application
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Sequence Diagram(s)sequenceDiagram
participant Client
participant LibraryRoutes
participant HeavyDownloadProcessor
participant LibraryStore
Client->>LibraryRoutes: request YouTube download
LibraryRoutes->>HeavyDownloadProcessor: run_heavy_pipeline(item_id, quality)
HeavyDownloadProcessor->>LibraryStore: create heavy_download job
HeavyDownloadProcessor->>LibraryStore: persist download artifact and metadata
LibraryRoutes-->>Client: return download status
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| quality = item.get("quality", "") or "720" | ||
|
|
||
| # Create a job entry | ||
| await store.create_job(item_id, "heavy_download") |
There was a problem hiding this comment.
WARNING: create_job returns the new job id, but it is discarded here. The success/error branches below re-fetch with (await store.get_item_jobs(item_id))[-1]["id"] to locate "the" heavy-download job. That is racy and incorrect if more than one job exists for the item (e.g. the cheap-tier pipeline already created jobs, or a manual download and an auto-download run concurrently). get_item_jobs orders by created_at, and with time.time() timestamps two jobs created in the same tick can sort unpredictably, so [-1] may point at the wrong job and mark the wrong entry done/error.
Capture the id directly and reuse it:
| await store.create_job(item_id, "heavy_download") | |
| job_id = await store.create_job(item_id, "heavy_download") |
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| return None | ||
| except Exception: | ||
| logger.exception("Heavy pipeline failed for item %s", item_id) | ||
| await store.update_item_status(item_id, "error") |
There was a problem hiding this comment.
WARNING: An unexpected exception in the optional heavy-download tier flips the whole item to error via update_item_status(item_id, "error"). The heavy download is opt-in and runs after the cheap-tier pipeline has already produced a ready item with valid text artifacts. A failed secondary media download should not overwrite that ready status and effectively hide the successfully-ingested content in the UI. Consider recording the failure in meta_json/the job entry (as the in-band path is None branch already does) and leaving the item status untouched.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| # Read body with a size cap to avoid OOM on large/hostile pages. | ||
| body_chunks: list[bytes] = [] | ||
| total = 0 | ||
| async for chunk in resp.aiter_bytes(8192): |
There was a problem hiding this comment.
WARNING: This size cap does not actually protect against OOM. resp = await client.get(current_url) (line 413) fully reads and buffers the entire response body into memory before this loop runs, so aiter_bytes() here just replays already-downloaded bytes. A hostile/large page is fully loaded regardless of _MAX_WEB_BYTES. To enforce the cap during download, stream the request instead, e.g. async with client.stream("GET", current_url) as resp: and read/cap inside that context (this also changes the redirect handling, since a streamed redirect response must be inspected before the body is consumed).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| logger.info("Heavy download complete for item %s: %s", item_id, result) | ||
| except Exception: | ||
| logger.exception("Heavy download crashed for item %s", item_id) | ||
| await store.update_item_status(item_id, "error") |
There was a problem hiding this comment.
WARNING: Same concern as run_heavy_pipeline: if the background heavy download crashes, this marks the entire item as error, overwriting the ready status and text artifacts already produced by the cheap tier. Since heavy download is an opt-in, additive step, a failure should be surfaced via the download job/status rather than degrading the item's overall state. (Note also that run_heavy_pipeline already swallows exceptions internally and sets error itself, so this outer handler will rarely trigger.)
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous Review Summaries (7 snapshots, latest commit 93870c3)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 93870c3)Status: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous review (commit 9d43626)Status: 2 Issues Found | Recommendation: Address before merge Overview| Severity | Count | Issue Details (click to expand)WARNING
Files Reviewed (5 files)
Fix these issues in Kilo Cloud Previous review (commit 13f6664)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (1 files)
Fix these issues in Kilo Cloud Previous review (commit 5b8fac3)Status: No Issues Found | Recommendation: Merge Files Reviewed (1 files)
Previous review (commit 7bd5912)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (3 files)
Note: the previous WARNING on Fix these issues in Kilo Cloud Previous review (commit 1b3fcc7)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (5 files)
Fix these issues in Kilo Cloud Previous review (commit 74cb317)Status: 4 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (7 files)
Reviewed by step-3.7-flash · Input: 203K · Output: 18.7K · Cached: 1.6M |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (3)
tests/test_library.py (2)
409-419: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueIncomplete assertion —
updated_titleis computed but never checked.
updated_titleis read on Line 416 and the comment describes verifying the title, but no assertion follows, so this block is a no-op. Either assert the expected title behavior or drop the dead variable and comments.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_library.py` around lines 409 - 419, Complete the title verification in the test block following the updated_title assignment by asserting the expected title behavior after processing. If no title value is guaranteed, remove updated_title and its accompanying comments instead of leaving unused test code.
1096-1097: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winReplace fixed
asyncio.sleep(0.5)waits with polling to avoid flaky tests. All three tests block on a hardcoded 0.5s sleep for the background ingest task to complete, which is timing-dependent and can fail under CI load or pass spuriously; poll the item status (or download state) with a short timeout instead.
tests/test_library.py#L1096-L1097: pollGET /api/library/items/{item_id}until status is terminal (bounded timeout) before posting the download.tests/test_library.py#L1122-L1123: same polling before asserting the non-YouTube download rejection.tests/test_library.py#L1135-L1136: same polling before requesting/download/status.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_library.py` around lines 1096 - 1097, Replace the fixed asyncio.sleep waits in tests/test_library.py at lines 1096-1097, 1122-1123, and 1135-1136 with bounded polling of GET /api/library/items/{item_id}. Wait until the item reaches a terminal status before posting the download, asserting the non-YouTube rejection, or requesting /download/status, using a short polling interval and timeout.tinyagentos/library_store.py (1)
85-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: fold the P3 columns into
CREATE TABLEand keep the migration for legacy DBs only.
quality,auto_download,downloaded_at,download_path, anddownload_bytesare absent from thelibrary_itemsCREATE TABLE(Lines 25-36) and exist only because_post_initalways runs theALTER TABLEpath. It works, but a reader inspecting the schema would not see these columns, and theif col_names:guard on Line 100 is always truthy (the table always exists), so it does not actually gate the commit on a migration having run. Declaring the columns in the base schema and scoping the migration to legacy P1/P2 databases makes the intended shape self-documenting.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tinyagentos/library_store.py` around lines 85 - 101, Update the library_items CREATE TABLE definition to declare quality, auto_download, downloaded_at, download_path, and download_bytes with their existing types and defaults. In _post_init, retain the ALTER TABLE migration only for legacy databases missing those columns, and commit only when at least one migration was applied rather than checking col_names, while preserving existing schema behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tinyagentos/library_pipeline.py`:
- Around line 667-684: The missing-file fallback in the download handling flow
must not select an arbitrary newest entry from the shared download directory.
Update the logic around the Path(path) check to locate only the file matching
the current video’s expected ID or output stem, exclude temporary or in-progress
files such as .part, and retain the existing download_error update when no
matching completed file exists.
- Around line 745-766: The heavy download flow should retain the ID returned by
create_job and use it for both update_job calls. Update the job creation
assignment and replace each get_item_jobs(item_id)[-1]["id"] lookup with that
captured job_id, preserving the existing done and error states.
- Around line 404-439: Update the redirect-fetch loop around current_url, resp,
and httpx.AsyncClient so requests use client.stream("GET", current_url) instead
of client.get(). Keep the response context open while checking redirects,
calling raise_for_status(), and consuming aiter_bytes(); enforce _MAX_WEB_BYTES
during streaming before accumulating chunks, then close the stream before
proceeding.
In `@tinyagentos/routes/library.py`:
- Around line 181-213: Update _ingest_task to fetch and validate the item’s
terminal status immediately after run_pipeline completes, and only perform rule
matching and run_heavy_pipeline when the status is ready. Return without
auto-download when the pipeline leaves the item in error, while preserving the
existing auto-download behavior for successful items.
- Around line 287-289: Remove the unnecessary f-string prefixes from the static
`<div class="info">` and `<div class="meta">` fragments in the surrounding HTML
construction, while retaining the f-string prefix on the title-containing `<h3>`
fragment.
In `@tinyagentos/templates/library.html`:
- Around line 76-152: Add a shared HTMX configuration in library.html that
injects the X-CSRF-Token header for every HTMX request, sourcing the token from
the page’s existing CSRF mechanism. Ensure the configuration applies to the
ingest form/drop-zone and source-rules write requests so session-cookie users
pass verify_csrf, without changing their endpoints or targets.
---
Nitpick comments:
In `@tests/test_library.py`:
- Around line 409-419: Complete the title verification in the test block
following the updated_title assignment by asserting the expected title behavior
after processing. If no title value is guaranteed, remove updated_title and its
accompanying comments instead of leaving unused test code.
- Around line 1096-1097: Replace the fixed asyncio.sleep waits in
tests/test_library.py at lines 1096-1097, 1122-1123, and 1135-1136 with bounded
polling of GET /api/library/items/{item_id}. Wait until the item reaches a
terminal status before posting the download, asserting the non-YouTube
rejection, or requesting /download/status, using a short polling interval and
timeout.
In `@tinyagentos/library_store.py`:
- Around line 85-101: Update the library_items CREATE TABLE definition to
declare quality, auto_download, downloaded_at, download_path, and download_bytes
with their existing types and defaults. In _post_init, retain the ALTER TABLE
migration only for legacy databases missing those columns, and commit only when
at least one migration was applied rather than checking col_names, while
preserving existing schema behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 793490c3-123c-4664-941e-416e7481397c
📒 Files selected for processing (7)
tests/test_library.pytinyagentos/library_collections.pytinyagentos/library_pipeline.pytinyagentos/library_store.pytinyagentos/routes/__init__.pytinyagentos/routes/library.pytinyagentos/templates/library.html
|
Bot-fix round pushed — addresses all 10 inline findings (4 Kilo WARNING + 6 CodeRabbit actionable) plus 3 CodeRabbit nitpicks: Kilo (4 WARNING):
CodeRabbit (6 actionable): CodeRabbit (3 nitpicks): |
| timeout=httpx.Timeout(30), | ||
| follow_redirects=False, | ||
| ) as client: | ||
| resp = await client.get(current_url) |
There was a problem hiding this comment.
WARNING: Phase 1 client.get() still fully buffers the final URL's body, partially defeating the OOM protection the Phase 2 streaming was added to provide.
This loop uses client.get() (line 413), which downloads and buffers the entire response body. When the final URL is reached (no redirect), the full page is buffered in memory here, then discarded, and Phase 2 re-downloads it via client.stream(). The _MAX_WEB_BYTES cap therefore only guards the second fetch; a hostile or oversized final page can still exhaust memory in Phase 1. Since Phase 1 only needs redirect detection, prefer client.stream() and read only the headers (or issue a streamed/HEAD request) so the body is never buffered here. This also removes the redundant double download.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| candidates = sorted( | ||
| [ | ||
| f for f in download_dir.glob("*") | ||
| if expected_stem in f.name and not f.suffix == ".part" |
There was a problem hiding this comment.
SUGGESTION: Substring stem match may select an unrelated downloaded file.
expected_stem in f.name matches any file whose name contains the expected stem, so a concurrent or similarly-named download (e.g. myvideo vs myvideo_part2) could be picked instead of the intended one. Consider an exact-stem (or extension-aware) match, e.g. f.stem == expected_stem, to avoid mis-selection.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| <title>Library — TinyAgentOS</title> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> | ||
| <script src="https://unpkg.com/htmx.org@2"></script> | ||
| <meta name="csrf-token" content=""> |
There was a problem hiding this comment.
SUGGESTION: The <meta name="csrf-token" content=""> tag is unused dead markup.
The CSRF token is actually read from the csrf_token cookie (lines 14-17) and injected via hx-headers; this empty meta tag is never populated or read, which is misleading. Remove it, or wire it up if a meta-based token is intended.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_library.py (1)
1089-1096: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract duplicated pipeline polling logic into a shared helper function. The bounded polling loop to wait for the pipeline to reach a terminal status is identically duplicated across three tests, and it currently fails to assert that a terminal status was actually reached before proceeding. If a timeout occurs, tests might fail downstream with confusing error messages.
tests/test_library.py#L1089-L1096: replace this polling loop with a call to a shared helper function that asserts a terminal status was reached.tests/test_library.py#L1121-L1127: replace this polling loop with the shared helper call.tests/test_library.py#L1139-L1145: replace this polling loop with the shared helper call.♻️ Proposed refactor (add helper and replace)
Add a helper function to the test module (or test class):
async def _wait_for_terminal_status(client, item_id, timeout_sec=5.0): import asyncio import pytest iterations = int(timeout_sec / 0.25) for _ in range(iterations): resp = await client.get(f"/api/library/items/{item_id}") if resp.status_code == 200 and resp.json().get("status") in ("ready", "error"): return resp.json()["status"] await asyncio.sleep(0.25) pytest.fail(f"Timeout waiting for pipeline terminal status on item {item_id}")Then replace the duplicated loops in your tests with:
await _wait_for_terminal_status(client, item_id)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_library.py` around lines 1089 - 1096, Extract the duplicated terminal-status polling into a shared async helper, such as _wait_for_terminal_status, that polls for ready or error within the bounded timeout and explicitly fails when no terminal status is reached. Replace the loops at tests/test_library.py lines 1089-1096, 1121-1127, and 1139-1145 with calls to this helper; all three sites require the same direct change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/test_library.py`:
- Around line 1089-1096: Extract the duplicated terminal-status polling into a
shared async helper, such as _wait_for_terminal_status, that polls for ready or
error within the bounded timeout and explicitly fails when no terminal status is
reached. Replace the loops at tests/test_library.py lines 1089-1096, 1121-1127,
and 1139-1145 with calls to this helper; all three sites require the same direct
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 08deba4a-d024-49f9-aa21-d724194bd929
📒 Files selected for processing (5)
tests/test_library.pytinyagentos/library_pipeline.pytinyagentos/library_store.pytinyagentos/routes/library.pytinyagentos/templates/library.html
🚧 Files skipped from review as they are similar to previous changes (4)
- tinyagentos/templates/library.html
- tinyagentos/library_store.py
- tinyagentos/library_pipeline.py
- tinyagentos/routes/library.py
| ) as client: | ||
| try: | ||
| resp = await client.head(current_url) | ||
| except Exception: |
There was a problem hiding this comment.
SUGGESTION: Fallback to GET only triggers on an exception, not when the server rejects HEAD with a 405 status.
The comment on line 405 says the code "falls back to GET if the server rejects HEAD", but except Exception here only catches errors raised by client.head() (network failures, timeouts). A server that answers HEAD with 405 Method Not Allowed returns a normal, non-raising response, so resp.is_redirect is False, the loop breaks, and the redirect for that source is silently never followed. Either tighten the comment to "falls back to GET on request error", or also fall back to GET when resp.status_code == 405 (or more generally when the response is neither a redirect nor a 2xx).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
|
Round 2 pushed — Kilo WARNING resolved (HEAD for redirect resolution). Round 2 bot review: 0 CRITICAL, 0 WARNING. Only 1 SUGGESTION (clarified HEAD comment) + 1 CodeRabbit nitpick (extract polling helper — deferred as low-value refactor). All 71 targeted library tests pass. Ready for maintainer review. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tinyagentos/library_pipeline.py (1)
404-419: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winUse a streamed GET for the fallback to prevent buffering large payloads.
The current fallback uses
client.get(), which reads the entire response body into memory. If a malicious or oversized site dropsHEADrequests to force this fallback, it bypasses the_MAX_WEB_BYTESprotection completely, potentially causing an out-of-memory (OOM) error. Additionally, catching a blindExceptionsuppresses potential logical errors, and servers that return405 Method Not AllowedforHEADare not handled—breaking redirect traversal for those sites.Catch
httpx.RequestErrorinstead of a blindException(which resolves the static analysis warning) and use a streamedGETto fetch headers without buffering the body. Explicitly trigger this fallback on405responses as well.🔒 Proposed fix to properly stream the GET fallback
- # Phase 1: resolve redirects using HEAD to avoid buffering - # bodies. Falls back to GET on connection/network errors, but - # a server that rejects HEAD (e.g. 405) won't be a redirect - # response either — so the loop exits and Phase 2 streams. + # Phase 1: resolve redirects using HEAD to avoid buffering bodies. + # Falls back to a streamed GET on connection/network errors or if + # the server rejects HEAD (e.g. 405) to ensure redirects can still + # be followed without fully buffering large malicious payloads. current_url = source_url for _hop in range(_MAX_WEB_REDIRECTS + 1): validate_url_or_raise(current_url) async with httpx.AsyncClient( timeout=httpx.Timeout(30), follow_redirects=False, ) as client: - try: - resp = await client.head(current_url) - except Exception: - resp = await client.get(current_url) + try: + resp = await client.head(current_url) + except httpx.RequestError: + resp = None + + if resp is None or resp.status_code == 405: + async with client.stream("GET", current_url) as resp: + pass🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tinyagentos/library_pipeline.py` around lines 404 - 419, Update the redirect-resolution loop around the `client.head` call to catch only `httpx.RequestError`, and trigger the fallback for both request failures and `405 Method Not Allowed` responses. Replace the fallback `client.get` with a streamed GET that reads only the response headers or redirect metadata, closes the response, and preserves `_MAX_WEB_BYTES` enforcement for the later body download.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tinyagentos/library_pipeline.py`:
- Around line 404-419: Update the redirect-resolution loop around the
`client.head` call to catch only `httpx.RequestError`, and trigger the fallback
for both request failures and `405 Method Not Allowed` responses. Replace the
fallback `client.get` with a streamed GET that reads only the response headers
or redirect metadata, closes the response, and preserves `_MAX_WEB_BYTES`
enforcement for the later body download.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 12f46456-f528-4924-a05e-869f4048c38d
📒 Files selected for processing (3)
tests/test_library.pytinyagentos/library_pipeline.pytinyagentos/templates/library.html
💤 Files with no reviewable changes (1)
- tinyagentos/templates/library.html
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_library.py
|
Library P1 is merged (#2062, dev sha 4498855). Three rounds of folds, all closed, including a data-loss fix inside an hour. Good work. This PR is now CONFLICTING against dev and needs a rebase before it can be reviewed. Both P2 and P3 touch the four files P1 substantially rewrote:
Please rebase onto current dev rather than merging dev in, so the diff stays reviewable. Read the merged P1 before resolving, because several things changed underneath you and a mechanical conflict resolution will reintroduce defects that were just fixed:
The canonical envelope reference is taosmd No rush on ordering: P2 first makes sense since P3 builds on it. |
|
@hognek Rebase needed - this has conflicts against current dev (GitHub reports Everything else on it looks fine from my side; it is purely staleness. Rebase onto current dev and I will take another look. |
|
This has gone to CONFLICT against Before you spend time on it though: please rebase #2068 first, and expect this one to change as a result. I have just posted a detailed re-review on #2068 asking for it to be rebased down to only the P2 delta, because in its current form it silently reverts merged P1 hardening and the 10MB download cap buffers the whole body first (I measured 130MB peak heap for a 10MB cap against a 60MB response). Since P3 builds on P2, rebasing this one on top of a P2 that is about to be reshaped will just mean doing it twice. Order I would suggest: fix and rebase #2068, get it merged, then rebase this on the result. |
|
P2 has landed (#2177, merged as This went CONFLICT because Two things to carry forward into P3 while you rebase, since they are the same class of issue I found in P2:
|
|
@hognek routing this to you rather than the free build lanes, per Jay. This PR is CONFLICTING against dev, so none of the four required checks (test 3.12, test 3.13, lint, spa-build) have ever run on it. Worth stating precisely: a green check on a conflicted branch tested the OLD base, so "no red" here is evidence of nothing. Rebasing is what makes it testable at all. It cannot go to the free lanes: their harness builds a fresh worktree off origin/dev and gates on producing a commit, so any work on an EXISTING branch produces nothing and the card is destroyed. I proved that the expensive way today, losing four cards to it. Ask: rebase onto current origin/dev, preserve the author's commits, do not change behaviour while resolving, and comment listing what conflicted and how each hunk was resolved so a reviewer can check the resolution instead of re-deriving it. Please do NOT merge; the rebase is the whole job and I will review the new head. Context: A2A 1760 went to the bus, which you are not on. That was my error, so this is the same request on the channel you actually use. |
|
@hognek status check on this one: the other three rebases from the 14:54 batch landed within minutes, this is the only one without a response. It is the largest rebase of the four, so if it is in progress, fine, just say so here. If it did not get picked up, it is first in line: dev has not moved since #2177 merged, so the conflict set is stable. Rebase onto current origin/dev, keep P2's semantics where they collide (P2 is merged truth), and post the conflict report like the others. |
|
In progress — rebase running now, test suite phase. Will post conflict report when complete. |
Rebase onto origin/dev (d728dd8)Reason: P2 landed via #2177 (af08174). PR was CONFLICTING. Conflicts resolved (3 stages, 6 commits rebased)Stage 1 — P1 commit (
Stage 2 — CodeRabbit fix (
Stage 3 — Kilo fix (
Post-rebase fixes applied
Test gate
Files changed vs origin/dev5 files, +1,462 / −92 lines (additive P3 on top of P2):
|
|
All contributors have signed the CLA ✍️ ✅ |
…ponse-size cap - WebProcessor: disable auto-redirects, manually validate every redirect hop with validate_url_or_raise() against the SSRF blocklist (same pattern as knowledge_ingest._download_article). Max 5 redirects. - WebProcessor: cap response body at 10 MB, stream with aiter_bytes() to avoid OOM on large/hostile pages. - Tests: update _mock_httpx_response with is_redirect=False, encoding, and aiter_bytes() to match the new fetch flow. All library tests pass.
… accounting, per-source rules - LibraryStore: add library_rules table, new columns (quality, auto_download, download_path, download_bytes, downloaded_at) with safe migration in _post_init, rule CRUD methods, fnmatch-based match_rules, get_storage_summary - HeavyDownloadProcessor: yt-dlp-based media download for url:youtube items with quality preference (360/480/720/1080/best), fallback heuristics for missing output paths - run_heavy_pipeline: rule-aware quality resolution (explicit > rule > item > default 720), job tracking via library_jobs table - Routes: POST /download, GET /download/status, POST/GET/DELETE /rules, GET /usage with HTMX-aware HTML responses - Auto-download: _ingest_task checks matching rules with auto_download=True and triggers heavy pipeline after cheap tier completes - Template: storage summary bar (polled via htmx), per-item download button with quality selector for YouTube items, rules management <details> panel with add/delete - Tests: 24 new tests (7 store rules/storage, 4 HeavyDownloadProcessor, 4 run_heavy_pipeline, 9 routes) - 70/70 library tests pass (1 slow route test skipped)
…old-6 page removal, patch imports
|
Rebased onto current origin/dev. Clean: zero conflicts, all 6 commits applied. Bot-fix findings (4 Kilo + 6 CodeRabbit) were already addressed in commits c23e2d4 + d5f6a8b on this branch — 2nd round came back clean with 0 CRITICAL / 0 WARNING. New head: 93870c3 on hognek/feat/library-p3 Dev changes since P1 merged: P1 (4498855), P2 with streaming download + content-type gating (af08174), plus dev commits through fc1de1d. No overlapping conflicts — the branch adds new functionality (rules table, HeavyDownloadProcessor, storage summary) that was orthogonal to the P1/P2 rewrites. |
|
BLOCKING. GitHub says CLEAN/MERGEABLE but that green is misleading: the merge-base is Aug 4 (aed3c95) and for the five touched files dev is textually identical to it, so everything below merges without a conflict marker. Test-merged against current dev to verify.
Also noted (non-blocking): a failed OPTIONAL heavy download flips a ready item to error; the newest-file fallback in The P3 server substance (rules table + upgrade-safe migration, storage summary, heavy pipeline) is sound and worth keeping. Please re-cut on current dev: drop the duplicated processor block, leave the three fixed regions untouched, drop the dead template/ |
…xes, drop dead client template (jaylfc#2070)
|
Re-cut per your Aug 9 review, pushed as
|
|
@coderabbitai review |
|
|
The three headline claims check out — each processor defined exactly once and byte-identical to dev's hardened versions, the TOCTOU lock real, the restored params actually consumed downstream — and this time the branch reverts nothing on dev (merge-tree clean, including the lora-studio additions). The stale-replay era is over. Two regressions and one ruling:
All three are small; one more push clears the label. |
|
@coderabbitai full review |
|
|
@hognek — this one is on us, and I would rather say it plainly than dress it up. This PR, #2048 and #2043 have been sitting clean and mergeable with no human review for five weeks. That is a queue failure at our end, and the part I regret most is that you got no signal either way. If I had been on the receiving end I would have assumed the work was not wanted. What makes it worse, and why I want to be specific rather than vague: you did not submit these and drift off. You worked them the whole time.
Same story on the others: #2048 got the D1 security blockers on 18 Aug, #2043 got revoke-match reporting and revoke-all-fingerprints the same day. You answered every piece of bot feedback, kept all three branches green and mergeable for a month, and heard nothing back. Sorry. Where this goes now. The bot reviews on this PR are from 20 Jul, which predates your 18 Aug re-cut, so they no longer describe the code that is actually here. I have queued fresh CodeRabbit passes on all three at their current heads. The free tier is rate limited to roughly one review every 24 minutes and it is retrying, so you will see those land over the next few hours rather than all at once. After the bot pass I am reading the three myself, and specifically the security-relevant paths: the SSRF and download handling here, the sponsor metadata and delegation handshake in #2048, and the revoke and fingerprint logic in #2043. Your commit messages say those are security fixes. I am not treating that as something to take on trust from any author, mine included, so it gets read properly rather than merged on green. I will come back to you on each PR individually as it clears, not sit on all three and deliver one batch at the end. If a review turns up something real you will get the file and the line, not a vague "needs work". If it turns up nothing, it merges. I am not putting a date on it, because the rate limit is not mine to control and you have had enough of promises that go quiet. The concrete next step is the fresh review on this PR, and it is already queued. #1910 is the one I will need from you eventually. It has gone conflicting against dev while it waited, so it cannot merge as-is. A rebase should be all it needs, and it has not been rejected or superseded. Given how long it has been queued I am not going to ask for that before the other three are moving, so treat it as no rush. One last thing: dev has moved a long way under all four of these. If you would rather rebase the other three while you are in there you are welcome to, but it is not required. They are clean right now and I would rather merge them as they are than ask you to touch working code. |
|
@hognek — this is the security read I committed to in my earlier note, done against the current head Result: no blocking security findings. I went looking specifically for the things that usually go wrong in a fetch-and-store pipeline, and the fetch path is genuinely well built. Details so you can check my work rather than take my word. What I verified, and how1. SSRF — correct, and it honours the helper's contract exactly. 2. Body cap is applied while streaming, not after. 3. Content-type gate fails closed. 4. No command injection through 5. SQL is fully parameterised. Every statement in the 6. Auth/CSRF. The new endpoints carry no explicit 7. The source-file unlink guard is present at Two notes, neither of them yours to fix hereDNS rebinding is still open, and it is in the shared helper, not your code. It affects Minor: Where this leaves the PRFrom my side the security review you were promised is done and it is clean. The fresh CodeRabbit pass at current head is still queued — the free tier is rate-limiting it and has refused several attempts today, which is not something either of us controls, so I am not going to make you wait behind it for this verdict. If that pass turns up anything, it comes back to you per-PR with file and line, as promised. Sorry again that this sat as long as it did. |
|
@coderabbitai full review |
|
|
@coderabbitai full review |
|
|
@coderabbitai full review |
|
|
@hognek — merging this now, and you are owed the reason it took 39 days, because it was not your code. Why no bot ever reviewed this at its current head. I said I would get you a fresh CodeRabbit pass. I could not, and I should have known that before promising it: CodeRabbit was dropped from this repo on 2026-06-13 when the free tier ran out, and our own contributor docs say plainly not to retrigger it. Every retry I ran was refused, eight times, against an instruction already written down. Then I checked why the replacement bot never appeared either, and it is worse than an oversight: Gitar does not run on fork PRs. Kilo did review these, but back on 2026-07-20 — before your 18 Aug re-cut — so it does not describe this code. So all three of your PRs were structurally un-reviewable by any bot from the day you opened them. They were not waiting in a queue. Nothing was ever going to arrive. That is a defect in how this project treats outside contributors, it is now filed as a fleet item, and it is not something you could have worked around. What this merge is actually gated on. I read the security paths myself at this exact head (
No blocking findings. Merging. One finding I want to be explicit is not yours: #2048 and #2043 are next, and each has one thing to send back to you — #2043's is blocking (a fingerprint column added with a default and never backfilled, so pre-existing contact rows cannot be revoked). Per-PR, with file and line, as promised. Thank you for staying with these through the re-cut; the work is good and the delay was ours. |
Rebase Conflict Resolution Report (tsk-4xcxcm)Summary: All 7 PR commits rebased successfully onto current dev (617 commits ahead of the original merge base Key finding: The library feature (P1-P3) was already merged into dev through separate PRs ( Net tree delta vs dev: 0 files changed (changelog fragment is the only new file). Conflicts and resolutions1. 2.
3.
CivitaiProcessor decisionThe PR's final commit (``, 're-cut P3 — dedup stale processors') intended to remove Verification
|
Resolve merge conflicts on PR #2070 (library: P3 heavy tier download, quality prefs, storage)
Epic #2057 — Phase P3: Heavy tier
Builds on PR #2068 (P2 — YouTube cheap tier + web ingestor).
What this adds
Opt-in media download (heavy tier):
HeavyDownloadProcessorwrapping yt-dlp'sdownload_video()POST /api/library/items/{item_id}/download— trigger heavy downloadGET /api/library/items/{item_id}/download/status— check progressQuality preference settings:
qualitycolumn on library_items (P3 migration, safe ALTER TABLE)Storage accounting:
LibraryStore.get_storage_summary()— total bytes, item count, per-kind breakdownGET /api/library/usageendpoint with HTMX-aware HTML outputPer-source rules engine:
library_rulestable — source_pattern (fnmatch), quality, auto_download, enabledPOST/GET/DELETE /api/library/rules— CRUD endpointsLibraryStore.match_rules(source_url)— fnmatch-based matching<details>panel)_ingest_taskchecks matching rules withauto_download=Trueand triggers heavy pipeline after cheap tier completesFiles changed (5)
tinyagentos/library_store.pytinyagentos/library_pipeline.pytinyagentos/routes/library.pytinyagentos/templates/library.htmltests/test_library.pyTest gate
test_filter_by_kind) skipped — hits real YouTube APIDesign doc
Per
docs/design/library-app.mdsection 3 (heavy tier) and section 4 step 5.Summary by CodeRabbit