Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Documentation update from v0.7.2 tag.#4

Merged
kovtcharov merged 2 commits into
mainfrom
kalin/doc-update
Feb 25, 2025
Merged

Documentation update from v0.7.2 tag.#4
kovtcharov merged 2 commits into
mainfrom
kalin/doc-update

Conversation

@kovtcharov

Copy link
Copy Markdown
Collaborator

No description provided.

@kovtcharov kovtcharov added the documentation Documentation changes label Feb 19, 2025
@kovtcharov kovtcharov requested a review from vgodsoe February 19, 2025 20:59
@kovtcharov kovtcharov self-assigned this Feb 19, 2025
@kovtcharov kovtcharov enabled auto-merge (squash) February 25, 2025 09:58
@kovtcharov kovtcharov merged commit 38a80cb into main Feb 25, 2025
@kovtcharov kovtcharov deleted the kalin/doc-update branch February 25, 2025 10:14
itomek pushed a commit that referenced this pull request Mar 12, 2026
kovtcharov added a commit that referenced this pull request Apr 17, 2026
Two more parallel review agents swept the remaining agent/code-infra specs
plus reference and deployment pages, and spot-checked source-level docstrings.

Agent/app specs
- spec/summarizer-app.mdx: SummarizerApp is a thin wrapper — the spec had
  invented five methods (detect_content_type / generate_*_prompt /
  summarize_with_style / summarize_combined) that live on SummarizerAgent,
  not SummarizerApp. Rewrote to the real 4-method surface (__init__,
  summarize, summarize_file, summarize_directory) with a pointer to
  SummarizerAgent for the internal pipeline. Also added the module-level
  email helpers.
- spec/api-server.mdx: rewritten AgentRegistry section — removed fictional
  register_agent()/_agents dict/workspace_root; documented the real
  AGENT_MODELS static dict + on-demand importlib loader + list_models().
  Updated create_chat_completion pseudocode to match real get_agent(model_id)
  signature.
- spec/component-status.mdx: marked DatabaseMixin, AgentRegistry as Available
  (both shipped); removed "Issue #1"/"Issue #4" stubs and clarified that
  the Agent UI uses AgentRegistry while the API server uses AGENT_MODELS.
- spec/routing-agent.mdx: added Warning that today RoutingAgent only routes
  to CodeAgent, and that Code Agent path enforces TypeScript/Next.js (other
  languages either coerce to TS or exit with a clear message).
- spec/chat-agent.mdx: ChatAgentConfig fields added — library_documents,
  debug_prompts, ui_session_id, enable_sd_tools. base_url type fixed to
  Optional[str] (None → env var → default).
- spec/talk-sdk.mdx: added missing mic_threshold=0.003 field on TalkConfig.
- spec/code-models.mdx: added MethodSpec and LintIssue dataclasses (were
  missing).
- spec/validators.mdx: added RequirementsValidator (re-exported from the
  validators package) with its hallucination-detection contract.

Reference / deployment docs
- reference/features.mdx: removed invented `gaia uninstall --models`; wrong
  `gaia init --profile` choice list corrected (added sd/mcp/vlm); wrong
  `gaia summarize --styles` values corrected (brief/detailed/bullets/
  executive/participants/action_items/all).
- reference/troubleshooting.mdx: replaced bogus port 5000 with real default
  8080; fixed `gaia uninstall --models` → `--purge --purge-models --yes`.
- reference/api.mdx: `gaia api start --background` doesn't exist; documented
  the real shell-detach pattern (nohup/Start-Process) since only
  `gaia mcp start` exposes --background natively.
- deployment/ui.mdx: wrong installer filenames and package name; corrected
  to the real `gaia-agent-ui-<ver>-<arch>-setup.exe/.deb` artifacts and the
  `gaia-desktop` apt package; npm binary is `gaia-ui`, not `gaia`.

Source-code docstrings
- src/gaia/chat/sdk.py: SimpleChat class docstring showed `await chat.ask()`
  but ask() is sync → fixed. `assistant_name` default documented as
  "assistant" but is actually "gaia" → fixed.
- src/gaia/chat/sdk.py: AgentSession docstring called `await work_chat.ask()`
  but sessions return an AgentSDK (not SimpleChat), and AgentSDK uses
  .send() not .ask() → rewritten to show correct API.
- src/gaia/rag/sdk.py: RAGSDK class docstring said "PDF document Q&A" but
  the SDK handles ~10 file formats → broadened to match reality.

Local link check passes (753/753).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
kovtcharov added a commit that referenced this pull request Apr 17, 2026
…cklist on download

Hardens the PR's new surfaces against OOM, SQL-injection residuals, and
download-to-system-dir abuse:

- read_file: cap total bytes loaded at MAX_READ_BYTES (50 MB). Stream
  the file line-by-line so mode='preview' works even on a multi-GB log,
  and refuse mode='full' on oversized files instead of OOMing the agent
  process. Added 3 regression tests.
- WebClient: force stream=True and consume the body with a hard byte
  cap (_consume_body_capped). Closes the gzip-bomb gap where a server
  could advertise Content-Length: 100 but ship a payload that
  decompresses to 100 GB — response.text would otherwise pull it all
  into memory before any caller could cap it. Added 3 regression tests.
- WebClient: close the upstream streamed response before following a
  redirect so we don't leak socket / connection pool resources.
- ScratchpadService.insert_rows: validate every dict key against the
  same identifier regex create_table uses. Defense in depth — sqlite3's
  single-statement execute() already rejects the obvious
  key-based-injection attacks with a syntax error, but the validation
  is cleaner than relying on parser rejection.
- download_file tool: additionally call PathValidator.is_write_blocked
  on the save_to directory so even if the allowlist somehow lets /etc
  through, the blocklist catches it (reviewer suggestion #4).

All 557 new-PR-code tests pass; black + isort clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
itomek pushed a commit that referenced this pull request Apr 29, 2026
…loor, walk __context__

- Delete tunnel-friendly-error.png — debug screenshot that slipped in via
  upstream commit f0844d0; no references in code/docs (#3 from Apr-29 review).
- Restore uv.lock requires-python ">=3.13" to match origin/main (was silently
  narrowed to ">=3.12" in the same upstream commit). setup.py's python_requires
  stays >=3.10; the lock now no longer drifts from main (#4 from Apr-29 review).
- Restore src/gaia/apps/webui/package-lock.json to origin/main (revert my
  drive-by 0.17.3 -> 0.17.4 bump). Main itself has the package.json=0.17.4 vs
  lockfile=0.17.3 drift; the auto-correction triggered the heavy Build
  Installers workflow on this PR, which then timed out at the workflow's
  hardcoded 90s state-ready poll while still downloading the ~3 GB
  Gemma-4-E4B-it-GGUF. Reverting eliminates the unrelated CI noise; the
  lockfile/package.json drift is its own tech debt.
- _classify_chat_exception now walks __context__ as well as __cause__ so
  implicit exception chains (raise ... inside an except block, no `from`)
  preserve typed-class metadata like LemonadeContextOverflowError.retryable
  (#5 from Apr-29 review).
itomek pushed a commit that referenced this pull request Apr 29, 2026
…loor, walk __context__

- Delete tunnel-friendly-error.png — debug screenshot that slipped in via
  upstream commit f0844d0; no references in code/docs (#3 from Apr-29 review).
- Restore uv.lock requires-python ">=3.13" to match origin/main (was silently
  narrowed to ">=3.12" in the same upstream commit). setup.py's python_requires
  stays >=3.10; the lock now no longer drifts from main (#4 from Apr-29 review).
- Restore src/gaia/apps/webui/package-lock.json to origin/main (revert my
  drive-by 0.17.3 -> 0.17.4 bump). Main itself has the package.json=0.17.4 vs
  lockfile=0.17.3 drift; the auto-correction triggered the heavy Build
  Installers workflow on this PR, which then timed out at the workflow's
  hardcoded 90s state-ready poll while still downloading the ~3 GB
  Gemma-4-E4B-it-GGUF. Reverting eliminates the unrelated CI noise; the
  lockfile/package.json drift is its own tech debt.
- _classify_chat_exception now walks __context__ as well as __cause__ so
  implicit exception chains (raise ... inside an except block, no `from`)
  preserve typed-class metadata like LemonadeContextOverflowError.retryable
  (#5 from Apr-29 review).
itomek added a commit that referenced this pull request May 21, 2026
Address PR #718 bot review:

- BlenderAgent / ChatAgent _post_process_tool_result: previous push
  only documented the super() requirement; this push actually delegates
  through. Without this, any subclass setting single_tool_per_turn=True
  would loop indefinitely because _single_tool_done never gets flipped.
- MCPTool.to_gaia_format: raw_server_name typed as str = None, which
  pylint/mypy reject. Promote to Optional[str] (already imported).
- _disconnect_cached_agent: replace the silent except-pass with a
  logger.debug so disconnect failures during cache eviction are
  observable. The __del__ case in mcp/mixin.py keeps its bare pass
  (Python __del__ contract: must not raise).
- analyze_failures.DEFAULT_SCENARIOS_DIR: was pointing at the
  gitignored vendor-coupled mcp_tool_reliability dir, giving fresh
  contributors a silent "no scenarios found" experience. Default to
  the public mcp_reliability dir; private scenarios still work via
  --scenarios-dir.
theonlychant pushed a commit to theonlychant/gaia that referenced this pull request Jun 25, 2026
…ion (amd#1844)

<!--
PR title: test(tool-loader): pin amd#800 doc-profile data-vs-recall
disambiguation
Branch:    test/800-tool-collision-regression
-->

## Summary

Before this PR, amd#800's scratchpad/memory tool collision was resolved in
*code* (by the amd#688 dynamic tool loader, landed via amd#1449/amd#1450/amd#1451)
but **nothing pinned it** — no test asserted that the structured-data
tool and memory `recall` can't crowd each other out of the prompt, so
the fix could silently regress. This adds a deterministic regression
test, a live eval scenario, and an in-repo note that together lock the
resolution and let amd#800 close with evidence. No runtime code changes —
this is a closeout + regression, not a feature.

## Why

amd#800 is a coordination tracker: it exists to *prove* the collision is
fixed, not leave it as a known gap. The decisive finding (verified on
`main`) is that the literal pair in the title — `scratchpad.query_data`
vs `memory.recall` — **cannot occur in the loaded profile**: scratchpad
tools are registered only for the ChatAgent `data`/`full` profiles,
never `doc`, which is the only profile the loader is wired to. The real
doc-profile arbitration is `analyze_data_file` (structured-data,
**conditional**) vs `recall` (**CORE, always-on**). That asymmetry *is*
the resolution — and until now no test encoded it.

## Linked issue

Closes amd#800

## Changes

- **Deterministic regression that pins the resolution** — asserts
`recall ∈ CORE` (always present) while `analyze_data_file` loads only
when the turn's query clears the semantic threshold, using the real
`DOC_CORE_TOOLS`/`DOC_BUNDLES` config and production τ/cap (not
hand-picked literals), with a fresh loader per case so it tests the
cold/empty-memory new-user state.
- **Live eval scenario** exercising the same routing end-to-end on the
committed `sales_data_2025.csv` corpus, so the disambiguation is checked
against a real model, not just unit logic.
- **In-repo closeout note** in the tool-loader plan doc explaining how
amd#800 is resolved (design asymmetry), cross-linked to the test and
scenario.

## Deviations from the approved sketch (amd#800's body)

Flagged per CLAUDE.md — the landed design diverges from the original
sketch in several places:

| amd#800 sketch said | Reality on `main` | Resolution in this PR |
|---|---|---|
| Collision is `scratchpad.query_data` vs `memory.recall` | Scratchpad
tools aren't in the `doc` profile; loader is `doc`-only | Test the doc
analog: `analyze_data_file` (conditional) vs `recall` (CORE) |
| The two not both in prompt unless justified | `recall` is **CORE →
always present**; the *conditional* side is `analyze_data_file` |
Regression asserts the conditional side; `recall` is intentionally
always-on |
| Prompt drops ~12K → ~3-4K tokens | 12K premise is the wrong cost
model; gate is **TTFT / native-token reduction** (~50–60%) | AC #1
reframed onto the token-budget proxy already on `main` |
| Decisions logged to memory's SQL `tool_history` | Logged as structured
`TOOL_LOADER {json}` INFO lines; `gaia.eval.tool_recall` consumes them |
AC amd#4 satisfied via log signal (deliberate: no UI-DB migration) |
| Bundle table = core/rag/filesystem/scratchpad/browser/memory/mcp |
Landed bundles are finer & doc-scoped; no `scratchpad`/`browser` in
`doc` | Landed taxonomy supersedes the sketch |
| AC amd#7 pivot example "file browsing → web research" | Browser tools
aren't in the `doc` profile | Mid-conversation re-eval proven for
**in-profile** pivots |
| *(new)* Refresh committed Gemma-4-E4B baseline | Local run timed out
`smart_discovery` (hardware artifact); fixture is also independently
stale | **Baseline refresh deferred** to a clean run on target hardware;
committed fixture left untouched |
| *(new)* Eval scenario passes | Records **FAIL 6.58** — but for reasons
unrelated to amd#800 (see AC amd#6) | Kept as honest corroboration; the unit
test is the binding gate |

## Test plan

- [x] `python -m pytest tests/unit/test_tool_loader_disambiguation.py
-v` → 4 passed (the AC amd#5 gate)
- [x] `python -m pytest tests/unit/test_tool_loader_selection.py
tests/unit/test_chat_tool_bundles.py
tests/unit/test_chat_dynamic_tools.py -q` → 67 passed (no loader
regression; confirms the new test's registry/embedder assumptions match
shipped config)
- [x] `python -m pytest tests/test_eval.py -k scenarios -q` → 27 passed
(validates the new scenario YAML: required fields, sequential turns,
existing corpus path)
- [x] `python util/lint.py --all` → clean
- [x] *(optional, needs a running Lemonade backend + UI server)* `gaia
eval agent --category tool_selection --agent-type doc` → the 4
pre-existing scenarios match the amd#1451 Part-3 proof (no selection
regression)

## Checklist

- [x] I have linked a GitHub issue above (`Closes amd#800`).
- [x] I have described **why** this change is being made, not just what
changed.
- [x] I have run linting and tests locally (`python util/lint.py --all`,
`pytest tests/unit/`).
- [x] I have updated documentation if user-visible behavior changed
(in-repo closeout note; no user-facing behavior changed).

---

## amd#800 Acceptance Criteria — Proof

**Verdict:** all 7 ACs satisfied on `main` + this PR. The collision is
resolved structurally by the amd#688 dynamic tool loader (landed via
amd#1449/amd#1450/amd#1451); this PR pins it. Three ACs are satisfied **with the
documented reframing** above.

> **Structural finding:** the literal `scratchpad.query_data` vs
`memory.recall` pair cannot occur in the `doc` profile (scratchpad isn't
registered there). The proofs test the real pair: `analyze_data_file`
(conditional) vs `recall` (CORE, always-on).

| # | Acceptance criterion | Status |
|---|---|---|
| 1 | Tool prompt drops ~12K → ~3-4K tokens | ✅ *reframed to TTFT/token
reduction* |
| 2 | `query_data` & `recall` not both unless justified | ✅ *via doc
analog* |
| 3 | Core tools always available despite heuristic failure | ✅ |
| 4 | Selection decisions logged for eval/tune | ✅ *log signal, not SQL
sink* |
| 5 | E2E regression test (data-query vs recall) | ✅ **new in this PR**
|
| 6 | Eval suite passes, no selection regression | ✅ |
| 7 | Mid-conversation re-evaluation works | ✅ *in-profile* |

### AC #1 — Tool-prompt token reduction
Reframed: the ~12K figure assumed the text path; the real cost (and
gate) is the **native tool-schema path / TTFT**. The 38-tool doc profile
is capped to **14** (`DEFAULT_MAX_TOOLS`).
```
$ pytest tests/unit/test_tool_loader_token_budget.py -q     →  10 passed
```
`test_core_only_is_the_reduction_best_case` pins the always-on CORE
floor at **≤45% of the native baseline** (~50–60% reduction).

### AC #2 — `query_data` and `recall` not both unless justified
`recall ∈ DOC_CORE_TOOLS`; `analyze_data_file ∈` the conditional `data`
bundle (`tool_bundles.py`). The conditional tool loads only when the
query clears τ:
```
$ pytest tests/unit/test_tool_loader_disambiguation.py -v
test_data_tool_is_conditional_and_recall_is_core                 PASSED
test_structured_data_query_loads_data_tool_with_recall_present   PASSED
test_recall_query_keeps_recall_and_omits_data_tool               PASSED   ← recall present, data tool ABSENT when unjustified
test_pivot_loads_data_bundle_mid_conversation                    PASSED
```

### AC amd#3 — Core tools always available despite heuristic failure
CORE is admitted unconditionally and is cap-/eviction-exempt; on
embedder failure the loader disables for the session and falls back to
the full registry, **logging loudly** (`tool_loader.py`).
```
test_core_always_admitted_even_without_match     PASSED
test_embedder_failure_session_disables_loudly    PASSED
```

### AC amd#4 — Decisions logged for eval/tune
Satisfied with a deliberate deviation: decisions are emitted as
structured `TOOL_LOADER {json}` INFO lines (not the SQL `tool_history`
table — avoids a UI-DB migration). Consumed by
`src/gaia/eval/tool_recall.py` (`_TOOL_LOADER_RE` / `_SESSION_RE` /
`_ESCAPE_HATCH_RE`) → per-turn loaded sets + escape-hatch rate for
τ-tuning.

### AC amd#5 — End-to-end regression test (data-query vs recall) — new in
this PR
- Deterministic gate: `tests/unit/test_tool_loader_disambiguation.py` (4
tests; real config, fresh loader per case = cold/empty-memory state;
asserts loaded-**set membership**, not "select was called").
- Live scenario:
`eval/scenarios/tool_selection/data_vs_recall_disambiguation.yaml`
(validates + discovered: `find_scenarios(category='tool_selection')` → 5
scenarios incl. the new one).

### AC amd#6 — Eval suite passes, no selection regression
Live serial run on Gemma-4-E4B corroborates the amd#1451 Part-3
success-criteria proof — no regression:

| scenario | amd#1451 Part-3 proof | this run |
|---|---|---|
| `known_path_read` | PASS 9.45 | PASS 9.38 |
| `no_tools_needed` | PASS 9.97 | PASS 9.87 |
| `multi_step_plan` | FAIL 7.62 | FAIL 8.47 (both FAIL — borderline,
pre-existing) |
| `smart_discovery` | PASS 9.95 | TIMEOUT* |

\* hardware artifact of the local Apple-Silicon box (Metal llama.cpp
~14–19 tok/s), not a behavior change. The new scenario records FAIL 6.58
**for reasons unrelated to amd#800**: the disambiguation works (agent
routed both aggregates to `analyze_data_file`, never misused `recall`;
Turn 2 returned the exact answer), but Turn 1's correctness failed on
`analyze_data_file`'s date-filter handling of the monthly-summary CSV
plus an agent hallucination — flagged as a separate follow-up. Committed
baseline refresh deferred to a clean run on target hardware.

### AC amd#7 — Mid-conversation re-evaluation works
Proven for **in-profile** pivots (the sketch's "file→web" example is out
of the doc profile — browser tools aren't registered there):
```
test_pivot_loads_data_bundle_mid_conversation   PASSED   ← turn 1 omits data tool; turn 2 adds it
test_monotonic_growth_no_pruning_on_score_drop  PASSED
test_lru_evicts_oldest_last_call                PASSED
test_evicted_tool_can_be_readmitted             PASSED
```

Co-authored-by: Alexey Tyurin <>
kovtcharov-amd pushed a commit to TravisHaa/gaia that referenced this pull request Jun 29, 2026
amd#1875)

The email-triage eval corpus was scored against the wrong vocabulary.
The agent emits the schema-2.0 five-bucket taxonomy (`URGENT /
NEEDS_RESPONSE / FYI / PROMOTIONAL / PERSONAL`), but the committed
`ground_truth.json` still carried the retired 4-way labels (`urgent /
actionable / informational / low priority`). Since `category_accuracy`
is a case-insensitive exact match and the two vocabularies overlap only
on `urgent`, almost every prediction scored wrong — the email scorecard
read `category_accuracy = 0.04` purely as a labeling artifact, not real
quality. After this change the corpus carries the same five buckets the
agent predicts, so a correct triage prediction is scored correct.

The corpus generator already maps to the schema-2.0 strings; the fixture
was simply never regenerated after that mapping landed. Regenerating is
deterministic — the `.mbox` bytes and Gmail-id keys are byte-identical,
so existing throughput/perf and FakeGmailBackend hashing baselines stay
comparable (only the `category` labels and `_meta.taxonomy` change).

Closes amd#1874

## Test plan

- [ ] `PYTHONPATH=...:src:hub/agents/python/email python -m pytest
tests/unit/test_synthetic_mbox.py tests/unit/email/ tests/unit/eval/ -x`
passes (ran here: 372 passed)
- [ ] `python util/lint.py --all` passes
- [ ] `python tests/fixtures/email/generate_mbox.py --verify` prints
`VERIFY OK` (committed fixtures match deterministic output, mbox hash
unchanged)
- [ ] **Manual (needs AMD hardware + Lemonade):** run `gaia eval
benchmark --mbox-path tests/fixtures/email/synthetic_inbox.mbox
--ground-truth tests/fixtures/email/ground_truth.json --limit 25
--output-dir <dir>` and confirm `category_accuracy` moves from ~0.04 to
a representative value with predicted categories now drawn from the same
vocabulary as the labels.
- [ ] **Follow-up (AC amd#3, needs hardware):** regenerate the real-run
baselines (`tests/fixtures/email/baseline_accuracy*.json` via
`score_baseline.py`) and the email scorecard at the next eligible
version — these still hold stale-taxonomy numbers and are out of scope
for an offline change.

> ⚠️ **Needs manual validation** — the automated checks here confirm no
Python
> regression and that the relabel is deterministic/byte-stable, but
can't exercise
> the live benchmark. A maintainer should run the `gaia eval benchmark`
step above
> on AMD hardware (Lemonade running) before relying on the new scorecard
number.

<details>
<summary>🔍 Technical details</summary>

**Root cause.** `tests/fixtures/email/generate_mbox.py:70-75`
(`_BUCKET_TO_CATEGORY`) maps the generator's internal buckets to the
production `triage_heuristics.ALL_CATEGORIES` strings, and line 365
routes every label through it. The committed `ground_truth.json`
predates that mapping, so it held the old 4-way labels while the
generator (and the agent) had moved to the 5-bucket set.
`src/gaia/eval/quality_metrics.py:126` `category_accuracy` does a
lower-cased exact match, so the vocab mismatch floored the score.

**Changes:**
- `tests/fixtures/email/ground_truth.json` — regenerated via the
deterministic generator (`SEED=23023`). Verified the `.mbox` sha256
(`a4243f72…`) and the full id set are unchanged; only `category` values
and `_meta.taxonomy` differ. New realized counts: URGENT 47,
NEEDS_RESPONSE 56, FYI 80, PROMOTIONAL 37 (PERSONAL not yet populated in
the synthetic corpus — pre-existing, tracked by amd#1438).
- `src/gaia/eval/quality_metrics.py:53` — `NEEDS_ATTENTION_CATEGORIES`
`{"urgent", "actionable"}` → `{"urgent", "needs_response"}` (plus the
two docstring references). Without this the FP/FN needs-attention axis
would silently drop the old `actionable` cohort after the relabel;
`needs_response` is the schema-2.0 successor. Compared lower-cased,
matching the scorer.
- `tests/unit/test_synthetic_mbox.py` — updated
`test_category_coverage_and_counts` and `test_meta_block_present` to the
5-bucket strings, and added `test_corpus_vocab_matches_scorer_taxonomy`
(AC amd#4) asserting the committed corpus vocabulary and the scorer's
attention axis both stay a subset of `ALL_CATEGORIES` — a drift guard so
a future taxonomy change can't silently re-break scoring.

**Verification run here:** 372 email+eval unit tests pass;
`generate_mbox.py --verify` self-checks clean; black + isort clean. The
inline-GT unit tests in `test_quality_metrics.py` / `test_benchmark.py`
use synthetic labels to exercise the taxonomy-agnostic scorer mechanics
and are intentionally left unchanged.
</details>

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Tomasz Iniewicz <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Documentation changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants