close
Skip to content

fix(knowledge): make the three auto-ingest paths opt-in - #2448

Merged
iamwhatever merged 1 commit into
mainfrom
fix/knowledge-auto-ingest-opt-in
Aug 10, 2026
Merged

fix(knowledge): make the three auto-ingest paths opt-in#2448
iamwhatever merged 1 commit into
mainfrom
fix/knowledge-auto-ingest-opt-in

Conversation

@bolichen97

Copy link
Copy Markdown
Collaborator

Problem

A fresh install starts filling the Knowledge Library on its own. Three separate
auto-ingest paths default to on:

Key What it does unasked
knowledge.auto_register_project_docs Registers every project you open as a Knowledge source and scans its documents
knowledge.auto_add_documents Lets the agent write documents it reads during normal work into the Library
knowledge.auto_ingest_artifacts Mirrors every artifact you save into the Library

Each ingested chunk costs one LLM extraction call on a pool of billed sessions,
so the user's first hour with a project spends money on a Library they never
asked for — and on a large repository the same automatic machinery is what put
the gateway into a stall/respawn loop (fixed separately in #2175 and #2336).

The toggles already existed. Their defaults were the problem.

Why it matters

The cost and the writes are both invisible until after they happen. A user who
opens a monorepo gets a source registered, chunks extracted, and a scheduled
dedup pass running against their Library before they have opened the Knowledge
page once. "Auto-ingest is on unless you find and flip three switches" is the
wrong side of the default for anything that spends money and mutates a store.

Fix (symptoms → root cause → change)

Symptom: the Library grows and extraction calls are spent with no user
action.
Root cause: the three gate flags default to True, so the opt-out is the
only control and it is opt-out by construction.
Change: flip all three to False. The features, their gates, their UI
toggles and their budgets are otherwise untouched — this changes only which side
of the switch you start on.

Each flag has more than one default source, and flipping one in isolation
would make KnowledgeConfig() and KiroCrewConfig.load() disagree, so all six
were changed:

  • the dataclass field defaults (KnowledgeConfig.auto_*)
  • the loader's inline .get(..., True) fallbacks for auto_ingest_artifacts
    and auto_register_project_docs
  • _read_auto_add_documents's no-key-present fallback (the legacy-spelling
    reader)

auto_ingest_doc_links (the legacy spelling) still wins when a config sets it
explicitly, so an existing config that opted in keeps its value — only
configs that never mentioned the key change behavior.

The frontend carries its own copy of each default (?? true in
ChatPanel.tsx) and was flipped to match. Left alone, the switch would have
rendered ON while the gateway ingested nothing, with no key in config.json for
the user to inspect and settle which one was telling the truth.

Tests

  • website/src/test/ChatPanel.knowledgeAutoIngest.test.tsx (new, 6 cases) —
    pins the UI side of the default pair: with no knowledge section in the
    config at all, all three switches render aria-checked=false, and the first
    click writes true. Both assertions wait for the row to leave its disabled
    loading state first, so they cannot pass on a placeholder. Verified
    non-vacuous: restoring one ?? true turns it red.
  • test/test_config_loader.pytest_auto_add_documents_defaults_off,
    test_project_docs_defaults_off, test_artifact_ingest_defaults_off, plus
    test_an_empty_knowledge_section_leaves_every_auto_path_off, which pins all
    three together because they arrive through three different readers.
  • test/test_knowledge_artifact_ingest.py
    TestKnowledgeConfigDefaults.test_auto_ingest_defaults_off asserts the
    dataclass default and the loader agree.
  • The pre-existing legacy-spelling tests (test_legacy_spelling_is_honoured,
    test_canonical_wins_over_legacy, test_round_trip_settles_on_the_canonical_key)
    are unchanged and still pass — they set the key explicitly, which is exactly
    the upgrade path that must not regress.

Manual verification

Full gates from the worktree venv (CI-parity, no faiss, mypy 1.14.1 matching the
pyproject.toml pin):

Gate Result
pytest (full backend) 39814 passed, 17 failed
isort --check-only / flake8 clean
mypy src/kiro_crew/ 3 errors, all in untouched hooks.py
tsc -b clean
vitest run (full frontend) 11633 passed, 0 failed
vitest run src/i18n/ 597 passed (all 11 i18n gate checks)
scripts/docs-lint.sh 190 files, pass

The 17 backend failures and the 3 mypy errors are pre-existing and
host-specific
, not from this diff:

  • The same 17 node IDs were re-run against a clean origin/main checkout using
    the same interpreter (PYTHONPATH pointed at the baseline tree) and all 17
    failed there identically
    . They are macOS-only: Seatbelt sandbox spawn,
    nested-pytest harness, a macOS temp-path redaction artifact, and a
    __CF_USER_TEXT_ENCODING env leak.
  • The mypy errors are os.listxattr / getxattr / setxattr in hooks.py,
    which do not exist on macOS. hooks.py is not in this diff.

No i18n catalog changes were needed: the three toggle descriptions never
claimed "on by default", so no locale string changed and the 11-locale parity
gate is untouched. Only backend metadata text and docs prose moved.

Screenshots

Captured against an isolated instance serving this branch's built bundle
(dev-backend.sh on its own port with its own data home, torn down afterwards).
The capture script asserted aria-checked=false on all three switches before
shooting, so a stale bundle fails loudly instead of being quietly photographed.

Knowledge Library card with all three auto-ingest switches off

Full Settings → Chat page, in context

Settings Chat page showing the Knowledge Library section in context

Known consequences, disclosed rather than hidden

Two pre-existing rough edges that this change moves onto the default path.
Both are out of scope here (each needs work inside the artifact ingest pipeline,
not the config layer) and I will file a follow-up issue:

  1. The artifacts toggle needs a gateway restart to take effect.
    _start_artifact_ingest_async reads auto_ingest_artifacts once at startup,
    whereas its two neighbours in the same card re-read config every watcher
    sweep and apply immediately. Previously the flag shipped on, so nobody met
    this; now it is the one switch in the card that appears to do nothing until
    restart, with no hint in the UI.
  2. The first-enable artifact backfill is unbounded. backfill_artifacts
    iterates every eligible artifact with no chunk budget. Under the old
    default it ran at first boot against an empty store; now it runs the first
    time a user opts in, when the store may hold hundreds of artifacts — a burst
    of extraction calls with no pacing. (auto_ingest_chunk_budget paces folder
    sources, not this path.)

Not included

No CHANGELOG.md entry. This is a behavior change for existing installs — a
user on 0.2.0 who never set these keys loses auto-ingest on upgrade and deserves
a release note — but 0.2.0 is already tagged and shipped, and the repo keeps no
Unreleased section, so I did not edit a released section unilaterally. Say
where you want it (a new 0.2.1 section now, or folded into the next release)
and I will add it.

Auto-add documents, auto-register project docs and auto-ingest artifacts all defaulted on, so a fresh install started writing to the Knowledge Library and spending LLM extraction calls without being asked.
@bolichen97
bolichen97 requested a review from a team August 10, 2026 01:20
@bolichen97
bolichen97 requested a review from a team as a code owner August 10, 2026 01:20
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 70f68ece797fa21912c0674908a3693812e71490 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 70f68ec

Verdict parsed from the review's SHA-scoped output markers for commit 70f68ece797fa21912c0674908a3693812e71490.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 70f68ece797fa21912c0674908a3693812e71490: <one-sentence reason>

@github-actions

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

Advisory UX-level review of 70f68ece797fa21912c0674908a3693812e71490 — updated in place on each push; does not block merge.

UX-Verdict: CONCERNS

Flipping the defaults is right, but it moves two silent-failure moments onto the very flow the flip creates: the first opt-in.

Watch

  • "Auto-Add Saved Artifacts" now silently does nothing when switched on. The PR itself discloses that _start_artifact_ingest_async reads the flag once at startup; under opt-in, every user who enables it sees the switch flip to ON while zero artifacts appear, with no hint — first-time opt-in (every adopter, once, task-failure-shaped confusion next to two siblings that apply instantly). Smallest fix inside this PR's surface: an "applies after restart" line in the toggle's description, or an inline notice on change.
  • First opt-in triggers an unbounded, unpaced backfill of every existing artifact — a billing burst the toggle's copy ("Mirror documents you save…") gives no warning about. One clause in the description ("adds your existing artifacts too") sets the expectation.
  • Upgrade silence: existing installs that never set the keys lose auto-ingest with no release note (CHANGELOG deferred). The Library stops growing with no in-product explanation; make sure the release note actually lands.

[UX-REVIEWED] 70f68ec

@github-actions

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — 🔴 changes requested (blocking)

GPT 5.6 found at least one blocking issue that must be resolved before merging 70f68ece797fa21912c0674908a3693812e71490.

This comment is updated in place on each push.

BLOCKING -- src/kiro_crew/config/loader.py:5070 -- Missing-key upgrades leave later artifact opt-in stale
auto_ingest_artifacts=bool(knowledge_data.get("auto_ingest_artifacts", False)),
Existing source row -> upgrade disables listener -> artifacts change -> later opt-in skips backfill -> Knowledge remains stale.
Fix: Preserve the True fallback until migration records the prior effective setting.
[BLOCK-MERGE] 70f68ec
[GPT-REVIEWED] 70f68ec
False positive or not applicable? A repository writer can comment:
/ai-review override gpt 70f68ece797fa21912c0674908a3693812e71490: <one-sentence reason>

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Advisory design-level review of 70f68ece797fa21912c0674908a3693812e71490 — updated in place on each push; does not block merge.

Design-Verdict: CONCERNS

Right fix at the right layer (all six default sites flipped together), but it silently changes behavior on existing installs and routes new opt-ins into two known-broken paths.

Watch

  • Existing installs that never wrote the keys silently lose auto-ingest on upgrade — no config write-back, no release note yet ("a user on 0.2.0 … loses auto-ingest on upgrade"). The loader already has write-back migration machinery (loader.py:5544); consider stamping the old true into pre-existing configs so only fresh installs flip, or land the changelog note with this PR, not after.
  • Opt-in is now the first-contact path, and both disclosed defects sit exactly there: the artifacts switch "appears to do nothing until restart" and first-enable backfill_artifacts is an unbounded extraction burst against a populated store. Sequencing matters — the follow-up fixes should land before or with a release carrying this flip, or every user who opts in hits them.

Suggestions

  • Don't merge temp-screenshots/ (540 KB of binaries whose own path says temporary) into main's permanent history; attach the images via GitHub's PR upload instead.

[DESIGN-REVIEWED] 70f68ec

@iamwhatever
iamwhatever merged commit bdd55fc into main Aug 10, 2026
46 of 48 checks passed
@iamwhatever
iamwhatever deleted the fix/knowledge-auto-ingest-opt-in branch August 10, 2026 01:25
@github-actions github-actions Bot removed the readiness: checking Automated validation is still running label Aug 10, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator Author

Dispositions for 70f68ece797fa21912c0674908a3693812e71490. This PR was merged by a maintainer ~4 minutes after it opened, while CI was still running, so these arrive after the fact. Recording them anyway, and following up with a fix-forward PR.

GPT 5.6 — BLOCKING, loader.py:5070, "Missing-key upgrades leave later artifact opt-in stale" — ACCEPTED, the finding is correct.

I verified it against ensure_artifact_source (src/kiro_crew/knowledge/artifact_ingest.py): created is True only on the call that inserts the row, and the docstring states the row's existence is the idempotency marker for the first-enable backfill. On any existing 0.2.x install the row already exists, because the feature shipped on. So the sequence is real and silent:

  1. upgrade → key absent → flag now False_start_artifact_ingest_async returns early, listener never registered
  2. artifacts are created / edited / deleted during the off period → nothing mirrors them
  3. user opts back in → ensure_artifact_source returns created=False_run_backfill never runs
  4. the drift from step 2 is never reconciled

My own local review reasoned about the fresh-install case and missed the upgrade case where the row pre-exists. That was the gap.

I do not think the suggested fix (keep the True fallback) is the one to take, because it re-lands the default this PR exists to change. The two candidates are:

  • Write-back migration — stamp the prior effective value into config.json on upgrade, so an existing install that was implicitly on stays on and a fresh install starts off. This also closes the Design Review concern below in the same move, and loader.py already has the machinery (the write-back path Design Review pointed at).
  • Decouple the backfill from created — reconcile on listener start (diff the artifact store against artifact_item_state) rather than keying off row insertion. Strictly more correct: it also repairs drift from any other cause, not just this one.

Design Review (Fable 5) — 🟡 CONCERNS, "existing installs silently lose auto-ingest; loader has write-back machinery at loader.py:5544" — ACCEPTED. Same root cause as the GPT blocker, same fix. The pointer to the existing write-back path is the useful part and is what makes the migration option cheap.

UX Review (Fable 5) — 🟡 CONCERNS, "Auto-Add Saved Artifacts now silently does nothing when switched on" — ACCEPTED. This is the startup-read issue I disclosed in the PR body under "Known consequences" (§1). Under opt-in it stops being an edge case and becomes what every adopter hits once. It needs either a live config re-read on the artifacts path (matching its two neighbours in the same card, which re-read every watcher sweep) or an explicit restart hint on the toggle.

Opus 4.8 — ✅ no findings. Nothing to disposition.

Follow-up covering all three accepted items is next; I will link it here.

bolichen97 added a commit that referenced this pull request Aug 10, 2026
Auto-add documents, auto-register project docs and auto-ingest artifacts all defaulted on, so a fresh install started writing to the Knowledge Library and spending LLM extraction calls without being asked.

(cherry picked from commit bdd55fc)
@bolichen97

Copy link
Copy Markdown
Collaborator Author

Follow-up opened: #2452 — takes the reconcile-on-start route rather than restoring the True fallback, so it repairs drift from any cause and also closes the unbounded first-enable burst (§2 of this PR's "Known consequences"). The remaining item, the artifacts toggle needing a gateway restart, is still open and called out there.

iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever pushed a commit that referenced this pull request Aug 10, 2026
…ble extraction model

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.
iamwhatever added a commit that referenced this pull request Aug 10, 2026
…ble extraction model (#2468)

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (#2175, #2336, #2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.

Co-authored-by: Joe Guo <zejiangg@amazon.com>
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
)

Auto-add documents, auto-register project docs and auto-ingest artifacts all defaulted on, so a fresh install started writing to the Knowledge Library and spending LLM extraction calls without being asked.
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…ble extraction model (kirodotdev#2468)

The per-source chunk budgets (auto_ingest_chunk_budget, folder_ingest_chunk_budget)
bound cost per folder per sweep, but with many directories registered the total work
per sweep = N × per-source budget -- causing LLM pool saturation, embedding backlogs,
and gateway stalls (kirodotdev#2175, kirodotdev#2336, kirodotdev#2448).

Add five new KnowledgeConfig fields:

- sweep_chunk_budget (default 500): hard cap on total chunks across ALL sources in
  one watcher sweep. Once reached, remaining sources defer to next sweep.
- max_sources (default 50): cap on registered source count. Auto-discovery paths
  stop registering once the cap is reached.
- embed_rate_limit (default 120/min): token-bucket throttle on embedding generation,
  preventing CPU/memory saturation from parallel embed batches.
- extraction_model (default empty = agent.model): extraction LLM is no longer
  hardcoded to claude-haiku-4.5; it uses the user's default model, overridable.
- extraction_pool_size (default 3): configurable concurrent extraction workers.

All five are live-reloaded (no restart needed except pool size) and exposed in
the Settings PATCH schema. 0 = unbounded for all numeric fields.

Implementation:
- watcher._scan() tracks chunks_used across all folder sources, breaking when
  the global budget is exhausted
- folder_watcher.scan_source() reports chunks_ingested in stats
- store.create_auto_source_unless_dismissed() enforces max_sources atomically
- EmbedRateLimiter token bucket in ingestion.py, called from _embed_item()
- _install_knowledge_agent() reads extraction_model from config
- LLMPool.start() reads extraction_pool_size from config
- handlers/core.py allows PATCH for all 5 new keys

Tests: 24 new tests covering config defaults, rate limiter, source cap, sweep
budget, pool size config, and extraction model resolution.

Co-authored-by: Joe Guo <zejiangg@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants