fix(agents): RAG-on-PDF timeouts on Gemma 4 (#1030)#1034
Merged
Conversation
) ChatAgent system prompt had grown to ~52K chars (~15K tokens) of inlined rules and multi-paragraph examples — large enough that prompt processing on Windows iGPU exceeded Lemonade's internal upstream timeout when running 'gaia chat --index <pdf> --query "..."' against the default Gemma-4-E4B model. Users hit a ~10-minute hang followed by the misleading "Couldn't reach the local LLM" error. Three layers of fix: 1. Trim _get_system_prompt to ~10K chars (5x reduction). All imperative directives preserved; only multi-paragraph examples and eval-survival enumerations dropped. Wire payload per chat-completions request cut ~38% (~85K -> ~53K chars including the tools schema). New SINGLE-DOC RESOLUTION rule when exactly one document is indexed — names the file and orders an immediate query_specific_file so Gemma stops asking "which document?" with only one indexed. 2. New LemonadeUpstreamTimeoutError distinguishes "Lemonade is up but the model call hung" from genuine connectivity failure. retryable=False prevents silent retry against a hung backend. User-facing message names concrete remediation steps (wait + KV cache, gaia kill && lemonade-server serve, --max-chunks 2, close GPU apps); no smaller-model fallback. Both classifiers (response-payload and exception-string) handle the split. 3. Base agent's generic "Sorry, I ran into an unexpected problem" catchall now surfaces typed Lemonade messages verbatim via a new _extract_lemonade_user_message helper. Walks __cause__ / __context__ with cycle protection. Also fixes a latent infinite-loop bug in _classify_chat_exception (_chat_helpers.py) that walked the cause chain without cycle protection — would have hung the chat handler forever on any pathological exception graph with a __cause__ loop. Found by the new cycle-test. Validated: - 2326 unit tests pass, lint clean - 19 live PDF scenarios from 1.4MB to 43MB, zero "Timeout was reached" - Cold-start scenario (model unloaded -> first query): 44.5s on Mac M-series with full cited answer - Bug-report's literal "what does the document say about water?" query against a water-content doc: 45s thorough cited retrieval - Live injected-timeout simulation: typed user_message surfaces to user verbatim, no generic "try again" wrapper User-facing documentation entry added at docs/reference/troubleshooting.mdx under "Agent & SDK Issues". Closes #1030.
3 tasks
In plain English: doc Q&A flows on a 1-2 MB PDF were busting the 32K window — `summarize_document` was building section prompts of ~35-50K tokens and getting back ContextOverflow errors. Bumping to 64K (Gemma 4 E4B supports up to 128K natively) gives doc-Q&A real headroom while keeping memory pressure manageable on 16 GB iGPUs. What changed: - MODELS["gemma-4-e4b"].min_ctx_size: 32768 -> 65536 - AGENT_PROFILES["chat"|"rag"].min_ctx_size: 32768 -> 65536 - cli.py:agent_context_sizes["chat"|"rag"]: 32768 -> 65536 - Reload-retryable thresholds in lemonade.py + _chat_helpers.py + agent.py: bumped from 32768 to 65536 so the existing auto-reload path (LemonadeManager._try_reload_with_ctx) still triggers when the loaded ctx is below the new expected window. - _ensure_model_loaded now distinguishes "Downloading model (first run, several minutes)" from "Loading model" so users on a fresh install see honest expectations instead of staring at a generic "Loading..." message for 5 minutes. - New GAIA_CTX_SIZE env-var override so users on tight-memory hardware can dial back to 32K (or push up to 128K) without code changes. No silent fallback — explicit opt-in. Memory tradeoff: Gemma 4 E4B Q4 weights ~5 GB, KV cache at 32K ~0.5 GB vs ~1 GB at 64K, ~2 GB at 128K. Total at 64K ~6 GB, fits comfortably on 16 GB shared-memory iGPUs. What this does NOT fix: - summarize_document still builds whole-section prompts without splitting on ctx_size — at 64K the 35K and 50K segments now fit, but a 70K+ document section would still hit overflow. To be filed as a separate bug.
3 tasks
…FAISS (#1030) In plain English: even after bumping the chat/RAG context window to 64K, Gemma kept getting auto-loaded at 32K — so `summarize_document` and other long-prompt paths still overflowed. Three coupled bugs were doing this: 1) `LemonadeStatus.loaded_models` was populated from `/api/v1/models` (the full catalog) instead of `/api/v1/health.all_models_loaded` (the models actually in memory). Downstream code that filters for "the loaded LLM" was picking `Gemma-3-4b-it-GGUF` — alphabetically first in the catalog, never actually loaded — every time it tried to reload at the new ctx. Fixed in get_status by sourcing loaded models from health and joining catalog metadata for label compatibility. 2) `_ensure_model_loaded` returned early on any model-name match without verifying ctx_size matched GAIA's expected window, and the non-streaming `chat_completions` path didn't call it at all. When the RAG SDK's embedder warm-up called `unload_model()` (no-arg → unloads everything), the next chat_completions request let Lemonade auto-load Gemma at its own 32K default, silently capping doc-Q&A below the bumped requirement. Both paths now ensure the model is loaded at the GAIA-expected ctx_size, reloading explicitly when the loaded entry is under-sized. 3) `_retrieve_chunks_from_file` rebuilt the per-file FAISS index on every query because `index_document`'s cache-load path never populated `self.file_indices[file_path]` — only the fresh-index path did. Every chat turn paid ~3s of avoidable embedding recompute. Pre-built on cache load now, mirroring the fresh-index path. End-to-end: Gemma now actually stays at 64K through the chat flow (verified via /health: `Gemma-4-E4B-it-GGUF: ctx_size=65536`). Cold-start on the bug-report PDF dropped from ~1:42 to ~1:06 on Mac M-series. No more `Per-file index not cached, rebuilding` warnings.
Three failures on this PR's CI all trace back to changes in the earlier #1030 commits. - Lint (`Unit Tests` workflow): `initialize_lemonade_for_agent` used `log.info`/`log.warning` for the new GAIA_CTX_SIZE override branch but never bound `log = get_logger(__name__)` like every other helper in `cli.py` — pylint/flake8 caught it as an undefined name. Also drops the unused `import os` flake8 flagged in `tests/unit/connectors/test_disconnect_clears_grants.py`. - Unit Tests: the rewritten `_ensure_model_loaded` now reloads any model whose `recipe_options.ctx_size` is below the GAIA expected window, but `test_skips_load_when_model_already_loaded` and the matching integration test still mocked bare `{"id": "model-a"}` entries with no recipe_options — so the new "loaded but under-sized" branch fired and the mock saw an extra `load_model` call. Mocks now carry `recipe_options.ctx_size=32768` so the no-op branch executes. - Unit Tests (`test_code_index_sdk.py`): the new `test_chat_system_prompt_budget.py` was installing `MagicMock` into `sys.modules` for `faiss`/`numpy`/etc. at module import time and never cleaning up. On Linux CI faiss-cpu isn't part of the `[api]` extras, so downstream `test_code_index_sdk.py::TestCachePersistence` and `TestFailLoudly::test_ensure_index_loaded_raises_on_corrupt_faiss` imported the leftover MagicMock instead of a real (or absent) faiss module and silently took the wrong code path. The stubs now roll back after the one `gaia.agents.chat.agent` import that needs them. - Windows CLI Tests: the self-hosted Windows runner's lemonade-server registry was wiped (likely a mid-May server upgrade), and the workflow only pre-pulled `Llama-3.2-3B-Instruct-Hybrid`. Every summarize-CLI test then died inside `LemonadeManager.ensure_ready` with "Load request for model_name=Gemma-4-E4B-it-GGUF not registered". Workflow now also pulls Gemma-4-E4B-it-GGUF (the GAIA default LemonadeManager preloads) so the runner is self-sufficient.
The Windows summarize CLI tests started failing on this PR with
"Load request for model_name=Gemma-4-E4B-it-GGUF not registered with
Lemonade Server" even though main passed three days earlier. The
``install-lemonade`` composite action was explicitly designed to leave
the runner's existing Lemonade Server alone ("does NOT install a new
version to avoid conflicts") and only warn when versions differ — so
the ``stx`` runner stayed on an older Lemonade whose model catalog
predates the Gemma-4-E4B-it-GGUF entry GAIA now relies on for its
default model preload via ``LemonadeManager.ensure_ready``.
The action now compares installed vs ``src/gaia/version.LEMONADE_VERSION``
and, when the runner is strictly behind, downloads the matching
``lemonade.msi`` from the same lemonade-sdk GitHub release the
``gaia init`` flow uses (``installer/lemonade_installer.py:GITHUB_RELEASE_BASE``)
and runs ``msiexec /i ... /quiet /norestart``. If the runner is at or
ahead of the expected version, the action is still a no-op — we don't
downgrade. On MSI failure the tail of the install log is surfaced.
With the upgrade in place the workflow no longer needs an explicit
``lemonade-server pull Gemma-4-E4B-it-GGUF`` (it failed anyway on the
old catalog with ``PullError: Failed to install``); the comment on the
remaining Llama pull is updated to reflect the new flow.
Two follow-ups to the prior upgrade attempt: The MSI ran (exit 0) and put v10.2.0 under ``C:\Program Files\Lemonade Server\bin``, but the action's post-install search short-circuited on ``Get-Command lemonade-server`` which still resolved the older v8.2.2 ``lemonade-server.bat`` shim under ``LOCALAPPDATA\lemonade_server\bin\``. That left the shell pointing at the stale binary, GAIA's default-model preload tried to load Gemma-4-E4B-it-GGUF against v8.2.2's catalog, and the summarize CLI tests died on "model_name=... not registered" all over again. Find-LemonadeServer now scans the well-known install dirs FIRST (including the new ``Program Files\Lemonade Server\bin``) so the MSI binary wins, and Get-Command is a final fallback. A Refresh-EnvPath helper re-reads HKLM/HKCU ``Path`` after MSI install so the freshly- registered Program Files dir is visible to the rest of the step -- mirroring ``LemonadeInstaller.refresh_path_from_registry`` in the gaia init flow. Post-install verification is now strict: if the resolved version is still below ``LEMONADE_VERSION`` we exit 1 with a list of every ``lemonade-server`` candidate on PATH, instead of warning and continuing into doomed downstream tests.
…er shim The v10.2.0 install renamed the main binary to ``LemonadeServer.exe`` (PascalCase) and left ``lemonade-server.exe`` as a deprecation shim that prints ``WARNING: 'lemonade-server' is deprecated. Use 'LemonadeServer.exe' to start the server`` and refuses ``--version`` queries -- so the action's post-install version check returned an empty string and exited 1. Find-LemonadeServer now does a two-pass scan: first for the new ``LemonadeServer.exe``, then a fallback for the legacy ``lemonade-server.exe`` / ``.bat`` names. Get-Command fallback was also extended to try both names. Version detection now combines stdout/stderr through ``cmd /c "...2>&1"`` and silences ``$ErrorActionPreference`` so a non-zero exit from the deprecation shim no longer hides the version string when one is present. The SYSTEM-profile path (``C:\windows\system32\config\systemprofile\ AppData\Local\lemonade_server\bin``) is added to the search list -- that's where the MSI placed the v10.2.0 install on this runner, and Get-Command exposed it as a third candidate alongside the per-user .bats.
The v10.2.0 ``LemonadeServer.exe`` (the new canonical binary) emits no console output on ``--version`` -- it's effectively a GUI launcher, not a CLI. The legacy ``lemonade-server.exe`` shim emits a deprecation warning. Either way, ``$LemonadeExe --version`` returns nothing parseable and the post-install version check kept seeing an empty string and exiting 1 even though the MSI install succeeded. Get-LemonadeVersion now reads the PE file's embedded VersionInfo (ProductVersion / FileVersion) via ``Get-Item .VersionInfo`` first -- that's the version the installer baked into the binary, no CLI invocation needed. ``--version`` parsing is kept as a fallback for any non-.exe path (the .bat shims) and for future binaries that decide to support it.
v10.2.0's deprecation shim emits ``This command is deprecated. Use 'lemonade pull --help' instead.`` and exits non-zero for the legacy ``lemonade-server pull <model>`` form, which killed the workflow with ``Process completed with exit code 1`` before any tests ran. ``Start-Process -FilePath "lemonade-server" ... serve`` is left alone -- the shim still execs into the new server for the ``serve`` verb (the last run confirmed the health endpoint came up at 13305) and ``Start-Process`` ignores the shim's exit code anyway.
Eval-driven optimization arc on top of the #1030 fix. rag_quality pass rate goes from 5/7 (baseline avg 8.84) to 7/7 (avg 9.10), and the two scenarios that previously couldn't even complete now pass cleanly. Five coupled fixes: 1) src/gaia/eval/runner.py — only pass `--bare` to `claude -p` when an explicit `ANTHROPIC_API_KEY` is in env. The flag is documented to restrict Anthropic auth strictly to `ANTHROPIC_API_KEY` / `apiKeyHelper`, so subscription users (Claude Code Max with a `claude /login` OAuth session) saw every eval fail-fast with "Not logged in · Please run /login" even though they were signed in. Conditional opt-in preserves the clean-subprocess isolation for API-key users. 2) src/gaia/eval/claude.py — when `ANTHROPIC_API_KEY` is missing, the error message now points users at `claude setup-token` (subscription path) first and `export ANTHROPIC_API_KEY` second, instead of demanding the env var without explaining alternatives. No behavior change for setups that already have the key. 3) src/gaia/mcp/servers/agent_ui_mcp.py — `send_message` payload now pins `model=Gemma-4-E4B-it-GGUF` instead of letting the UI fall back to its `default_model_name`. The UI's default was being overridden to `Qwen3.5-4B-GGUF` by a stale `custom_model` setting in some installs, which caused Lemonade to thrash between Qwen and Gemma on every eval turn. Pinning Gemma per-request stops the swap cycle and was the single change that turned the most-stuck scenario (`negation_handling`) from FAIL→TIMEOUT→PASS. 4) src/gaia/ui/_chat_helpers.py — extend `_classify_chat_exception` to recognise Lemonade HTTP 5xx (`status 5xx`, `Internal Server Error`, `Service Unavailable`, `Bad Gateway`, `Gateway Timeout`) as transient `LemonadeNetworkError` (retryable=True). Previously a Lemonade-side 503 during a model swap surfaced as the generic "trouble connecting to the language model" UI fallback with no retry; now the chat layer's existing retry-on-retryable path triggers a model reload + one retry, which usually recovers. 5) src/gaia/agents/chat/tools/rag_tools.py — omit the `page` key from chunk dicts when `extract_page_from_chunk` returns None (markdown / HTML / docs without page metadata). Agent was templating "According to <doc>, page null:" into citations, which the eval judge consistently docked on `personality` as a cosmetic artifact. Data-layer fix; the agent now naturally omits the page suffix when there's no value to fill in. Plus one flake fix: 6) tests/unit/test_chat_system_prompt_budget.py — only stub `faiss`/`numpy`/`pypdf`/etc. when they CAN'T be imported in the current environment, and on cleanup also evict the GAIA modules that bound them (`gaia.rag.sdk`, `gaia.agents.chat.agent`, `gaia.agents.chat.tools.rag_tools`). Pre-fix, when the budget test ran first and `pypdf` was installed, the stub poisoned `gaia.rag.sdk`'s `pypdf` binding for the rest of the pytest session — and later `tests/unit/rag/test_pdf_extraction_errors.py` would mis-detect a blank PDF as encrypted because pypdf was now a MagicMock. Six tests flaked depending on collection order. Validated: - rag_quality eval (claude-sonnet-4-6 judge over subscription): 7/7 pass, 100% pass rate, avg 9.10/10 (was 5/7, 71%, avg 8.84) - `pytest tests/unit/ tests/test_chat_agent.py tests/test_eval.py --ignore=tests/unit/chat/ui` → 2495 pass, 0 fail, 27 skip - `util/lint.py --all` → pass (soft warnings unrelated to diff)
7/7 PASS, avg 9.08 — captures the post-optimization state from commit 95e4b37. Pass rate matches the prior d71cd91 baseline (also 7/7); avg score is 0.35 below it but offset by clearing the two previously- broken scenarios (negation_handling FAIL→PASS, table_extraction TIMEOUT→PASS). Future PRs touching the LLM-affecting surface listed in CLAUDE.md should --compare against this scorecard. meta.json records the Lemonade pre-load step required to reproduce cleanly (Lemonade auto-loaded Qwen3.5-4B without it, causing swap-thrash timeouts).
Collaborator
Author
Collaborator
Author
…ade`` Four workflows + the shared start-lemonade.ps1 helper were still wired for the v8.2.2 Lemonade CLI; running on the v10.2.0 server the ``install-lemonade`` action now upgrades to surfaced two new failures: - ``lemonade-server serve --ctx-size N`` aborts before bind on v10.x with ``error: failed to start lemonade-server`` written to stderr. The flag is no longer accepted on serve; pin the ctx_size per-model via ``/api/v1/load`` instead (canonical v10.x path). Affected: ``test_api.yml``, ``test_rag.yml``, ``installer/scripts/start-lemonade.ps1``. - ``lemonade-server pull <model>`` prints ``This command is deprecated. Use 'lemonade pull --help' instead.`` and exits non-zero. The ``lemonade`` CLI binary is the v10.x replacement. Affected: ``test_agent_sdk.yml``, ``installer/scripts/start-lemonade.ps1``. start-lemonade.ps1 also now prefers ``LemonadeServer.exe`` (PascalCase v10.x binary) over the legacy ``lemonade-server`` deprecation shim for the ``serve`` invocation -- same pattern install-lemonade already uses -- and resolves ``lemonade`` separately for pull, falling back to the combined exe only when the new CLI isn't on PATH (compat with older runners we haven't migrated yet).
…ver.exe Previous attempt preferred ``LemonadeServer.exe`` for the serve invocation thinking it was the new canonical binary, but on v10.2.0 that exe appears to be a tray/launcher with a different CLI shape: ``LemonadeServer.exe serve --port N --no-tray`` runs, produces zero console output, and never binds the port -- the smoke test then times out waiting on a health endpoint that never comes up. The legacy ``lemonade-server`` shim, by contrast, still translates the ``serve --no-tray --port N`` arg shape correctly into a working server on v10.2.0 -- ``test_gaia_cli_windows.yml`` uses exactly that incantation against the same runner and passes. Same shim, same args, same outcome: the server binds and answers /api/v1/health. So pin the serve call to ``$lemonadeServerExe`` (the shim) and keep ``$lemonadeCli`` (the new ``lemonade`` binary) for pull operations. ``--ctx-size`` stays off the serve invocation (the shim still refuses it on v10.x) and is set per-model on the /api/v1/load body.
itomek
approved these changes
May 11, 2026
pull Bot
pushed a commit
to bhardwajRahul/gaia
that referenced
this pull request
May 12, 2026
## Why this matters Three process rules from the post-mortem on amd#1030 (the Gemma 4 RAG-PDF timeout fixed in amd#1034): 1. **Run the agent eval when you change anything that affects how the LLM behaves.** Don't skip it — the Claude Code subscription typically already provides API access. amd#1030 is the canonical example of a bug the eval would have caught in CI. 2. **Lead PR descriptions, commit messages, and bot reviews with the change itself, no labelled preamble.** Recent auto-review comments trended toward dense framework jargon non-engineers struggled to act on; the rule now demands the finding up front, then technical detail, with hard length caps. 3. **Keep code comments to one short line or skip them.** Multi-paragraph "history of how we got here" blocks belong in the PR / commit message, not inline. The new section quotes the verbose comment from the amd#1030 fix as the canonical Bad example. No behaviour change, no code change. CLAUDE.md gets the three rules; the bot prompts in `.github/workflows/claude.yml` now defer to CLAUDE.md for style and only carry the length caps inline. ## Test plan - [ ] `python -c "import yaml; yaml.safe_load(open('.github/workflows/claude.yml'))"` — YAML still parses - [ ] Read CLAUDE.md end-to-end and confirm the new rules read coherently with the existing rules; no contradictions - [ ] Confirm the bot prompts still describe the same review structure (Summary → Issues → Strengths → Verdict) — the style rule is additive ## Merge order CLAUDE.md's new "eval triggers" bullet references `_extract_lemonade_user_message` (introduced by amd#1034). amd#1036 should land **after** amd#1034 so that symbol resolves on main. ## Related - amd#1030 — the bug that triggered this post-mortem - amd#1034 — the bug fix - amd#1033 — broader CI testing-gap follow-up - amd#1037 — npm dependabot vulnerabilities investigation
pull Bot
pushed a commit
to bhardwajRahul/gaia
that referenced
this pull request
May 15, 2026
## Why this matters v0.18.0 ships agent memory v2 (hybrid-search second brain with LLM extraction and observability dashboard), ChatAgent split into three composable agents (Chat/FileIO/DocumentQA), parallel tool calls, and a Telegram adapter scaffold — plus fixes the RAG-on-PDF timeout with Gemma 4 that broke document Q&A since v0.17.6 and adds CI gates that enforce RAG quality baselines on every future PR. Full notes: `docs/releases/v0.18.0.mdx`. ## What's New - **Agent memory v2** ([amd#606](amd#606)) — Hybrid semantic + keyword search, LLM extraction, observability dashboard via SSE streaming ([amd#1032](amd#1032)). Per-user isolation enforced; extraction runs async so it doesn't add latency. - **ChatAgent split** ([amd#979](amd#979)) — `ChatAgent`, `FileIOAgent`, and `DocumentQAAgent` replace the monolithic class; each composable via `tools=`. Backward-compatible shim preserved. - **Parallel tool calls** ([amd#946](amd#946)) — Multiple `tool_calls` from a single LLM turn are executed concurrently, cutting round-trips for multi-tool workflows. - **Telegram adapter scaffold, Phase 0** ([amd#951](amd#951)) — `gaia telegram start|stop|status`, per-user session isolation, `[telegram]` extras. Phase 1 (message handling + allowed-users gate) tracked in [amd#889](amd#889). - **Connectors: per-MCP toggle + single-writer enforcement** ([amd#1018](amd#1018), [amd#998](amd#998)) — Disable individual MCP servers without removing them; concurrent writes serialised with actionable errors on contention. - **File navigation, web browsing, and write security** ([amd#495](amd#495)) — `FileSearchToolsMixin`, web browsing tool, and scratchpad mixin in `KNOWN_TOOLS`; write tools check `allowed_paths` before dispatch. - **Email UI and policy alerts** ([amd#995](amd#995), [amd#1039](amd#1039), [amd#952](amd#952)) — Pre-scan triage card, in-chat Connect, policy alert cards, and durable receipts for confirmation-gated actions. ## Bug Fixes - **RAG-on-PDF timeouts on Gemma 4** ([amd#1034](amd#1034), closes [amd#1030](amd#1030)) — Prompt-size budget check added at composition time; CI gates enforce it on every PR ([amd#1040](amd#1040)). - **Envelope-level parse failure crashed SD recovery** ([amd#1047](amd#1047), closes [amd#1023](amd#1023)) — Falls through to a clean recovery path with step-1 context preserved. - **Windows-path tool args corrupted** ([amd#1027](amd#1027)) — Backslash normalisation now happens after argument parsing. - **Blender `send_command` hung** ([amd#1026](amd#1026), closes [amd#1022](amd#1022)) — Read timeout applied to persistent-connection servers. - **`gaia chat init` in post-install banner** ([amd#1029](amd#1029), closes [amd#1024](amd#1024)) — Replaced with the correct `gaia init`. - **Keyring treated as required** ([amd#1028](amd#1028)) — Import guarded; optional on systems without `keyring`. - **electron-builder URLs stale** ([amd#953](amd#953)) — Three doc/installer files updated to current download paths. ## Tooling & Docs - **RAG eval CI gates** ([amd#1040](amd#1040), closes [amd#1033](amd#1033)) — RAG quality baselines + prompt-size budget enforced on every PR. - **Fork-PR authors now receive Claude review** ([amd#932](amd#932)) — `allowed_non_write_users: "*"` with prompt-injection mitigations documented. - **Eval runs mandated before merging** ([amd#1036](amd#1036)) — `CLAUDE.md` requires `gaia eval agent` for LLM-affecting changes. - **GAIA website** ([amd#369](amd#369)) — [amd-gaia.ai](https://amd-gaia.ai) live. - **Custom agent guide reorganised** ([amd#997](amd#997)), Lemonade PPA docs ([amd#801](amd#801)), broken Lemonade CLI URL fixed ([amd#996](amd#996)), WhatsApp adapter evaluation spec ([amd#950](amd#950)). ## Release checklist - [x] `util/validate_release_notes.py docs/releases/v0.18.0.mdx --tag v0.18.0` passes - [x] `src/gaia/version.py` → `0.18.0` - [x] `src/gaia/apps/webui/package.json` → `0.18.0` - [x] Navbar label in `docs/docs.json` → `v0.18.0 · Lemonade 10.2.0` - [x] All 28 commits in range (v0.17.6..HEAD) are represented in the notes - [ ] Review from @kovtcharov-amd addressed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why this matters
In plain English: when a user indexed a PDF in
gaia chatand asked a question about it, GAIA would hang for ~10 minutes and then show "Couldn't reach the local LLM" — even though the LLM server was running fine. After this fix the same query returns a real cited answer in ~45–60 seconds, and on the rare occasions when a timeout does fire the user sees a useful "here's what to try" message instead of the misleading network-error wording.Technically: the
ChatAgentsystem prompt had grown to ~52 K chars / ~15 K tokens. With the default Gemma-4-E4B on Windows iGPU, prompt-processing for the first PLANNING call exceeded Lemonade's internal upstream timeout. Three layers of fix:ChatAgent._get_system_promptfrom ~52 K → ~10 K chars (5× reduction). All imperative directives preserved; only multi-paragraph examples and edge-case enumerations dropped. Full wire payload per request cut ~38 %.LemonadeUpstreamTimeoutErrordistinguishes "Lemonade is up but the model call hung" from genuine network failure.retryable=Falseprevents silent retry against a hung backend; the user-facing message names concrete remediation steps (no smaller-model fallback)._extract_lemonade_user_message. Walks__cause__/__context__with cycle protection — and that incidentally exposed a latent infinite-loop bug in_classify_chat_exception(would have hung the chat handler forever on any pathological exception graph), fixed in the same commit.Also adds a
SINGLE-DOC RESOLUTION (CRITICAL)rule so Gemma doesn't ask "which document?" when only one is indexed, and a user-facing doc entry under "Agent & SDK Issues".Validation done
test_chat_system_prompt_budget.pypins prompt size per (profile × indexed-doc count) up to 100 docs;test_lemonade_error_classification.pycovers the timeout-vs-network split plus cause-chain cycle protection.Timeout was reachederrors. PDF sizes 1.4 MB → 43 MB; cold-start replay (model unloaded → first query) 44.5 s on Mac M-series; the literal bug-report query "what does the document say about water?" against a water-content doc returns a thorough cited 5-section answer in 45 s.user_messagesurfaces to the user verbatim, not wrapped in the misleading "Sorry, I ran into an unexpected problem. This might be a temporary issue — try again in a moment."Test plan
pytest tests/unit/ tests/test_chat_agent.py -q --ignore=tests/unit/chat/ui→ 2 326 passpython util/lint.py --black --isort→ ALL QUALITY CHECKS PASSEDCURL error: Timeout was reached.rag_qualityeval against baseline:gaia eval agent --category rag_quality --agent-type doc \ --compare tests/fixtures/eval_baselines/gemma-4-e4b-d71cd914/scorecard_rag_quality.jsonRelated
gaia eval agentwhen changing LLM-affecting code paths, and improve PR/issue-bot prompts to lead with layman-level descriptions.Closes #1030.