Commit graph

62 commits

Author SHA1 Message Date
Yiorgis Gozadinos
5653e876a8
Return search results as agent text, images and structured content
search_documents and search_documents_by_image return a ToolResult: the
format_for_agent rendering with rank, Document ID and Collection so the
text alone drives the document tools; one ImageContent per distinct
picture, labelled with its result; and the SearchResult list without
image_data as structured content. format_for_agent gains an opt-in
include_document_id, so the capabilities' rendering is unchanged.
collect_pictures is the one place pictures are deduplicated and
validated for both wire formats.

Refs #599
2026-09-04 13:00:15 +03:00
Yiorgis Gozadinos
15afb97a6e
Navigate documents by outline and section from the MCP server
build_toc moves from the sandbox into haiku.rag.context; the sandbox
keeps its toc.json unchanged. get_document_outline returns the heading
tree with page numbers and get_document_section one section's text,
subsections included, both resolved in the database holding the
document. Chunk ids never leave the server. ask_question drops `cite`
and always appends its citations.

Refs #599
2026-09-04 13:00:15 +03:00
Yiorgis Gozadinos
88cd2b1d6c
Describe the MCP server and its tools to clients
FastMCP gets instructions, the haiku.rag-slim version and a lifespan.
Every tool carries read-only ToolAnnotations with a title, a description
that says when to use it, and a description on every parameter. The
filter description lists the document columns from DocumentMetaRecord
and how to match metadata; filter is accepted by both search tools.
One strict base64 decoder serves every image parameter.
DocumentInfo carries metadata.

Refs #599
2026-09-04 13:00:15 +03:00
Yiorgis Gozadinos
40d40bcbf2
Cover the configured database set from the MCP server
`haiku-rag mcp` passes covers_set=True and _covering no longer refuses a
scope over several databases. search_documents, search_documents_by_image,
ask_question and analyze take `sources`; get_document takes `source`;
DocumentInfo carries `source`. format_citations gains include_source, which
ask_question sets from covers_multiple so citations name their database
only when the server covers several.

Refs #599
2026-09-04 13:00:14 +03:00
Yiorgis Gozadinos
d9f489dcc8
Deduplicate search results within one model response
Sibling searches emitted in one response overlap heavily (40.6% of
returned chunk slots on Glimmer ORB fan-out cases). A result whose
rendered evidence a sibling already showed keeps its rank slot but
collapses to a reference line, and a picture attaches once per response
keyed on (source, document_id, self_ref). Equivalence is the
format_for_agent rendering at neutral rank/total plus picture keys,
bucketed under the qualified chunk id, so another database's copy or a
different expansion of the same anchor formats in full.

Search state now commits only after formatting and image construction
succeed: a raising image build no longer leaves results citable that
the model never saw, notes evidence for them, or suppresses a later
sibling.
2026-09-02 12:29:12 +03:00
Yiorgis Gozadinos
afdef92b5b
Finish the comment pass, and escape document fields everywhere Rich renders
`_rich_print_document` escapes uri, title and metadata, the sibling of
the escaped search-result renderer. The remaining comments and
docstrings that narrated rejected alternatives, consequences or history
now state the current invariant. The Sandbox class docstring names the
held connection close() releases, and wrapped docs paragraphs join to
one line.
2026-08-28 15:34:47 +03:00
Yiorgis Gozadinos
590719fca7
Read a document, chunk or picture from the database that names it
`resolve_document` and `find_document` selected a document through a listing,
then dropped its source and looked the id up across the set. Ids repeat between
copies of a database, so a title that matched in one could be answered by
another's document.

`get_document_by_id` and `get_chunk_by_id` join `get_picture_bytes` in taking an
optional `source`, and all three route it through `clients_covering`, so a name
the client does not cover raises `UnknownDatabaseError` rather than being
answered by the database it does cover. Without a source the reads are as they
were, answering from the first database in configured order that holds the id.
2026-08-28 13:30:05 +03:00
Yiorgis Gozadinos
fa319596cc
Name the collection on a retrieved image, not only in the text
Search results and capsule entries name the collection they came from; the
images attached beside them carried only the chunk id and reference. Two
collections can return the same picture of the same document, so the two
labels were identical and the model could place neither.

The decision is the one already made for the text: `covers_multiple` at the
generic search tool, and the flag `search_corpus` computed for the capability
tools, which it now returns.
2026-08-28 12:02:16 +03:00
Yiorgis Gozadinos
225a37ca73
Tell one collection's pictures from another's
Three places identified a picture by document and reference alone, and one
identified a citation's images by chunk id alone. Both repeat between copies of
a database, so a search returning a figure from two collections sent one, a
capsule retained one, and a citation rendered the other collection's figures.

Keyed on the source as well: `(source, document_id, self_ref)` for search
pictures, the capability and source for retained ones, and `qualified_id` for
the chat's citation images.
2026-08-28 10:35:46 +03:00
Yiorgis Gozadinos
0d7810c78a
Render collection identity only for multi-collection searches
`format_for_agent` named the database whenever one was named, so a search over
a single named database carried a line with nothing to distinguish. It now takes
`include_collection` from the caller, which decides from the search selection
rather than from the hits: a search that could have drawn on two collections
names them even when everything came back from one.

`Collection:` at the model boundary, database in configuration and
administration. `source` on results, documents, citations and analysis
dictionaries is unchanged.
2026-08-27 12:41:05 +03:00
Yiorgis Gozadinos
2000098e16
Say which commands cover a set, and what a shared id does
Three groups, not two: `search`, `ask`, `analyze` and `chat` cover the set,
`settings`, `init-config` and `download-models` open no database, and every
other command works on one — or on a configured set of one, which is
unambiguous and keeps its name. A database named in `lancedb.databases` keeps
that name whether or not it is the only one covered; only `lancedb.uri` places
one without naming it.

Document ids repeat between copies of a database, where the sandbox refuses a
duplicate but the chat filter's `id IN (...)` matches the document in every
copy. `build_document_id_filter` claimed ids never widen a selection.

Document the facade: `covers_multiple`, `source_names`, `source`,
`reader_for`, `clients_for`, the lifetime of a borrowed client, and
`sources=None` against `sources=[]`.

Drop the vision callout from the README, which the features list already
covers.
2026-08-26 13:03:58 +03:00
Yiorgis Gozadinos
307e250f29
Select documents to filter by id, not by displayed name
A title repeats within a corpus and across databases, so a substring
match on the displayed name widened the filter to documents the user did
not pick. The label names the database.
2026-08-24 15:27:59 +03:00
Yiorgis Gozadinos
2cd568847e
Move the wire rewrite into the compaction capability
`_compact_old_tool_returns`, `PRIOR_TURN_NOTICE` and `turn_start` leave
`RAGCapabilityBase`, along with its `wrap_model_request` hook. The evidence
capabilities now retrieve and validate, and nothing else. Registering the compaction
capability is what rewrites a request; leaving it out sends the transcript untouched,
which was never a choice a host could make before.

The boundary is the recorded question identity rather than message shape, so a
resumption compacts what lies below the question in progress instead of switching
compaction off for the whole run. The newest earlier evidence return carries the
capsule and every other becomes a receipt, so one capsule exists by construction and
every return stays paired with its call.

Pictures of cited evidence are fetched through the capability that retrieved them and
re-attached beside the capsule with fresh labels. Ownership of a picture on the wire
requires the machine tag we write and an image directly after it, since neither
position nor prose is proof: several tools' results can arrive in one request, and a
user quoting our wording above their own picture had it removed. A picture that cannot
be fetched or decoded is emitted with neither its image nor its label.

The chat TUI and the example backend register the compactor, being multi-turn.
`client.ask`, `client.analyze` and the MCP tools do not: a single-shot question has
nothing earlier to compact.
2026-08-13 13:00:02 +03:00
Yiorgis Gozadinos
46f7ab8d97
Name the search result each page image belongs to
ToolReturn.content reaches the model as a user-role message and the pictures
arrive bare, so nothing connects a figure to the chunk it came from:
BinaryContent.identifier does not survive serialization to the vision API, and
the captions in the result text correlate only by position.

Precede each picture with its position, source chunk id and self_ref.
build_binary_parts_from_results becomes build_image_content_from_results and
returns the labels interleaved with the pictures, so both attachment sites emit
them the same way.

This does not stop a model narrating retrieved pictures as user-supplied.
Measured on gemma4-26b with a single note ahead of the batch, and again with
per-image labels: it quotes the label and still says the user provided them.
The message role wins over its text.
2026-08-13 13:00:01 +03:00
Yiorgis Gozadinos
f13a3fb677
Report tool failures with ToolFailed 2026-07-27 18:26:42 +03:00
Yiorgis Gozadinos
a6e5664c78
Verify picture bytes before attaching to multimodal tool returns 2026-05-13 16:01:39 +03:00
Yiorgis Gozadinos
c96894a0f0
fix cross-document picture dedup in agent and skill search tools 2026-05-05 15:56:10 +03:00
Yiorgis Gozadinos
ee41bc676f
remove defensive code 2026-05-04 15:12:27 +03:00
Yiorgis Gozadinos
21f12cd770
add vision: bool flag on ModelConfig (default False). Gate the agent search tool's BinaryContent attachment on qa.model.vision so picture bytes are only sent to vision-capable QA models. 2026-05-04 10:47:21 +03:00
Yiorgis Gozadinos
b01c649684
surface picture image bytes in SearchResult and emit multimodal ToolReturn from the agent search tool 2026-04-30 12:43:05 +03:00
Yiorgis Gozadinos
eb4e1721ce
remove dead code, add tool descriptions in frontend, update changelog 2026-04-20 15:38:17 +03:00
Yiorgis Gozadinos
aa4ce07dc9
remove filter parameter from analyze skill tool and clean up unused filter helpers 2026-04-17 18:35:16 +03:00
Yiorgis Gozadinos
499a843a43
remove unused create_analysis_toolset and AnalysisResult 2026-04-17 18:32:01 +03:00
Yiorgis Gozadinos
bf8eec68e3
Use dict-keyed counter for concurrent-safe search cap 2026-03-11 12:19:19 +02:00
Yiorgis Gozadinos
e2749ad2a6
Cap QA agent search iterations to reduce response time 2026-03-11 12:13:43 +02:00
Yiorgis Gozadinos
70273ecf5b
Remove filter from the search tool 2026-03-02 17:15:38 +02:00
Yiorgis Gozadinos
d3c6322481
Simplify sandbox with run_monty_async, replace manual ThreadPoolExecutor start/resume loop 2026-02-24 11:35:39 +02:00
Yiorgis Gozadinos
c61ded1271
Replace Docker sandbox with pydantic-monty 2026-02-24 09:55:40 +02:00
Yiorgis Gozadinos
492f5cf1e4
Avoid mutable defaults 2026-02-20 17:59:35 +02:00
Yiorgis Gozadinos
ed89ff0fc9
Simplify tools/ — remove AG-UI state machinery, keep core toolsets 2026-02-20 16:36:48 +02:00
Yiorgis Gozadinos
524647c501
Add unified RAG skill with session context and reuse existing tools; 2026-02-20 16:36:47 +02:00
Yiorgis Gozadinos
a870052ee9
Fix coverage 2026-02-17 13:22:23 +02:00
Yiorgis Gozadinos
f4735667df
emit StateDeltaEvent with client-aware baseline 2026-02-17 12:24:09 +02:00
Yiorgis Gozadinos
8c76897af2
Update tui and app to use the toolkit 2026-02-16 12:54:44 +02:00
Yiorgis Gozadinos
7ff2123806
Introduce toolkit to reduce toolset creation ceremony 2026-02-16 11:51:02 +02:00
Yiorgis Gozadinos
969c64d106
Add citations_history to SessionState for unified citation rendering between search and ask 2026-02-13 16:21:50 +02:00
Yiorgis Gozadinos
c0ed93da2d
Expand tests 2026-02-13 14:34:24 +02:00
Yiorgis Gozadinos
f5c562db9b
Fix double summarization, cleanup 2026-02-13 12:16:23 +02:00
Yiorgis Gozadinos
d9acdfba9d
prompt builder 2026-02-13 11:21:09 +02:00
Yiorgis Gozadinos
5f6d9e9812
handle state_key duplication between deps and ToolContext 2026-02-13 10:23:12 +02:00
Yiorgis Gozadinos
a5b8e3be7e
Add AgentDeps and prepare_context for custom agent DX 2026-02-12 17:37:37 +02:00
Yiorgis Gozadinos
8f180f707b
Decouple QA toolset from chat agent, add ToolContext state methods 2026-02-12 17:27:47 +02:00
Yiorgis Gozadinos
0c6d73409d
Move SessionContext from agents/chat/state.py to tools/session.py, change QASessionState.session_context to use it 2026-02-12 17:13:01 +02:00
Yiorgis Gozadinos
05ebd781d4
Move client and tool_context from toolset factories to RunContext.deps 2026-02-12 15:50:46 +02:00
Yiorgis Gozadinos
51f1d9cf13
Issue a full state snapshot from ask() tool to preserve server side background context 2026-02-11 17:49:09 +02:00
Yiorgis Gozadinos
58c2bd49ea
Clean up server vs client session setting priorities 2026-02-11 16:54:00 +02:00
Yiorgis Gozadinos
9e63ff1ff7
Remove session_id from state layer, remove module-level caches 2026-02-11 14:55:53 +02:00
Yiorgis Gozadinos
d88f2f003a
Remove incoming_* fields, simplify delta computation 2026-02-11 14:31:37 +02:00
Yiorgis Gozadinos
d67df09cce
Introduce ToolContextCache 2026-02-11 14:18:26 +02:00
Yiorgis Gozadinos
c916356a42
Clean up toolset exports, simplify update_session_context(), fix health check 2026-02-11 12:46:50 +02:00