Commit graph

127 commits

Author SHA1 Message Date
Yiorgis Gozadinos
ca2e28559e
Split the multi-database tests by subject
Two files of 1,405 and 814 lines become seven: scope resolution, lifecycle,
search, documents, expansion, citations and capabilities. `_config`, `_seed`
and the rest move to `helpers.py`, importable by the sandbox tests that share
them, and the package points VCR back at `tests/cassettes/multi_db/`.
2026-08-26 13:43:53 +03:00
Yiorgis Gozadinos
fdb5710491
Ask and analyze across several databases
Chunk 2 gave search a configured set to fan out over. ask and analyze
covered one database still: the RAG capability had no way to be told which
databases a question spanned, and the analysis sandbox mounted one
document tree.

The selection travels as sources on EvidenceState, beside the filter it
scopes with, so both capabilities read it the same way. clients_covering
is the one rule that turns a selection into clients, used by search, the
sandbox mount and the cite fallback, so a question scoped to some
databases cannot search, mount or cite another. Citations carry the
database they came from, and format_for_agent names it, so the model can
attribute evidence while it answers rather than only afterwards.

The sandbox keeps one flat /documents/{id}/ namespace and resolves each id
to the client holding it, which rests on ids being UUID4. A database
copied from another breaks that, so an id held twice is refused rather
than resolved to whichever arrived last.

On the CLI, search, ask and analyze cover the configured set and label
each result with its database. Every other command works on one, named
with --database NAME (a name reaches a database behind a URI, which --db
cannot) or --db PATH, and refuses a set it cannot choose from instead of
silently reading the default database. Cold databases open together, so a
first query costs the slowest open rather than their sum.
2026-08-24 10:03:46 +03:00
Yiorgis Gozadinos
2da294b850
Put multi-table writes behind one transaction boundary
Four call sites repeated lock, snapshot, try/except, restore. Each used
restore_table_versions, which restores in _tables() order — documents
first, contradicting RESTORE_TABLE_ORDER — and each caught Exception, so
a cancellation mid-write skipped rollback and left the earlier table
writes committed.

Store.write_transaction() holds the lock, snapshots under it, and rolls
back through _rollback_to_snapshot: RESTORE_TABLE_ORDER, shielded from
cancellation, absorbed cancellation re-delivered, rollback failure raised
with the original as cause. The two single-table update_meta sites keep
the bare lock.

The batch documents write moves inside the guarded body; it was outside
the try, so a failure there was never rolled back. Auto-vacuum is
scheduled after the transaction rather than inside it.

restore_table_versions is removed; those four sites were its only callers.
2026-08-19 14:05:40 +03:00
Yiorgis Gozadinos
73944f91ea
Close the source adapter one-shot ingestion builds
create_document_from_source resolves a fetcher per call and never closed
it, so every one-shot URL or WebDAV ingest leaked an httpx connection
pool. Close it, but only when we built it: resolve_adhoc_fetcher returns
a caller-supplied source when one matches the URI, and the ingester keeps
those open across jobs.

Directory ingestion also yielded symlinked files resolving outside the
directory it was given. rglob does not recurse into symlinked
directories, so a symlinked file was the only way out of the tree; skip
those, as FSSource.discover already does.

The chunk repository docstring named client._ensure_chunks_embedded,
which does not exist.
2026-08-19 13:14:21 +03:00
Yiorgis Gozadinos
895b5f0532
Refresh source-backed documents in place on a FULL rebuild
FULL rebuild deleted a document before re-ingesting it from its URI, and
the handler around that logged and continued. A 404, a timeout or any
conversion error therefore removed the document permanently.

Deleting after a successful create is not an alternative:
create_document_from_source resolves the same URI to the existing
document and updates it in place, so a trailing delete would remove the
freshly rebuilt row.

Refresh in place instead. create_document_from_source takes an internal
force flag that skips the revision and MD5 short-circuits, so an
unchanged source is still re-converted, re-chunked and re-embedded into
the existing document, and the document id survives a rebuild.

A failed refresh now falls through to the stored-content path rather
than skipping the document: FULL recreates the chunks table before the
loop, so skipping left the document present but unsearchable until the
next rebuild.

The pending-batch flush moves out of the try. A failed flush is a lost
write and should abort the rebuild, not be logged and skipped.
2026-08-19 13:06:39 +03:00
Yiorgis Gozadinos
94befc0dfd
Recover from worker death and deny metadata writes
A crashed worker used to poison the rest of the run. execute() reported the
crash as a failed result, but kept the dead session, and every later call then
raised RuntimeError out of the tool. Clear the session so the next call checks
out a replacement. Keep the session for a syntax or runtime error, which leaves
the worker healthy, and say in the failure text that a restart loses the
variables.

A MemoryFile accepts writes, so metadata.json took them while the other three
document files refused. Mount it through the same read and deny pair. The
write-denial test now covers all four files.
2026-07-28 19:11:19 +03:00
Yiorgis Gozadinos
5c8df37af1
Support open()/with in the analysis sandbox
Document files can be read with open() and with-blocks (.read(),
.readline(), .readlines()); writes raise PermissionError. File objects
remain non-iterable and the collections module is still unavailable.
2026-07-28 19:10:53 +03:00
Yiorgis Gozadinos
f13a3fb677
Report tool failures with ToolFailed 2026-07-27 18:26:42 +03:00
Yiorgis Gozadinos
f96a428ef1
Fix defects found reviewing the coverage work
check_source_accessible narrowed its handler to ValueError, but Path.exists
re-raises errno values outside its ignored set (EACCES, ENAMETOOLONG). Those
were swallowed before and now escaped into the rebuild sweep the guard exists
to protect. Catch OSError too.

Restore the arity guard in _common_path_prefix: without it an empty list
raises from min() and a single label yields a prefix covering the whole path.

Two tests would have hung rather than failed on regression (the vacuum skip
and the protected-wait cancellation); both are now bounded. The import
vacuum test raced against the done-callback that discards the task, and now
spies on the call instead, with a negative control.

Replace assertions that could not fail: blank-query search against an empty
corpus, a batch flush counted against an empty table, a picture description
asserting its own input state, and an FS scheme check with nothing on disk to
resolve. The get_model matrix asserted only the returned type across 26
cases and now pins the per-provider settings. The three batching tests now
count flushes, which revealed embed-only writes through chunks_table.add
rather than _flush_rebuild_batch.
2026-07-27 10:44:32 +03:00
Yiorgis Gozadinos
f6acb65e95
Reach and enforce 100% coverage
Cover the remaining paths in the client, context, downloads, title
generation, document tools and store models, and add fail_under=100 so
uncovered lines fail CI.

Six lines that no test can reach get a pragma with its reason: the docling
import guard, the nameless PDF attachment, the FS symlink OSError guard that
resolve(strict=False) absorbs, the docling bbox and LanceDB document-id
shape guards, the tag-retention branch vacuum makes unreachable, and Monty's
Rust-thread print callback.

Fix test_find_config_file_user_config, which wrote its config into the cwd it
had chdir'd to, so the cwd branch answered first and the user-directory
lookup it names was never exercised.
2026-07-26 20:11:02 +03:00
Yiorgis Gozadinos
2afe1bd28c
Cover store engine and repository paths
Add vector-index creation tests including the warned failure, chunk
repository get_by_id, list_all pagination, blank-query and precomputed-vector
search, the unknown-score-column guard, settings row recreation, and
replace_for_document with no items.
2026-07-26 19:36:37 +03:00
Yiorgis Gozadinos
dc5ad8f699
Cover the rebuild paths
Add tests for the vacuum-failure warning, documents deleted mid-rebuild,
chunkless documents, batch flushes in the embed-only, descriptions and full
paths, a missing docling blob under rechunk, missing and recoverable picture
bytes, and the source-missing fallback. Direct-call tests cover the staging
helpers and the idempotent phase-1 marker.
2026-07-26 19:32:09 +03:00
Yiorgis Gozadinos
e2b273ee2a
Consolidate duplicated client-side tests
Parametrize sibling tests that differed only in a literal value, and fold
two strict-subset tests into the survivors that already covered their
scenario. Every case that ran before still runs; the union of assertions
is applied to each case, strengthening list_all, get_pages_data and
resolve_doc_items.

Replace four hand-rolled log-capture handlers with a shared
capture_logs() contextmanager in conftest.

13 fewer test functions, 348 fewer lines.
2026-07-26 13:30:59 +03:00
Yiorgis Gozadinos
9deb1f2bd4
replace haiku.skills with native Pydantic AI capabilities 2026-07-24 15:26:17 +03:00
Yiorgis Gozadinos
a1cf405cae
Rename document_meta identity column document_id to id 2026-07-08 16:32:55 +03:00
Yiorgis Gozadinos
23bbca5998
Re-check URI under the write lock to prevent duplicate ingestion 2026-07-08 11:26:45 +03:00
Yiorgis Gozadinos
e4b1f16f97
Shrink the docling-serve context-expansion cassette 2026-06-29 15:15:26 +03:00
Yiorgis Gozadinos
e1fbaf8aad
Re-record remaining docling-serve cassettes against 1.25.0 2026-06-23 16:44:58 +03:00
Yiorgis Gozadinos
3cc3c98986
Store docling-serve chunk bodies via raw_text 2026-06-23 16:35:21 +03:00
Yiorgis Gozadinos
68715c9657
Request page images explicitly from docling-serve 2026-06-23 16:15:02 +03:00
Yiorgis Gozadinos
3586ac30a7
Add Cohere multimodal embedder 2026-06-23 15:23:10 +03:00
Yiorgis Gozadinos
a1ec310bf4
Add VoyageAI multimodal embedder 2026-06-23 15:23:10 +03:00
Yiorgis Gozadinos
1e65d36766
Pass fetched FetchResult to ingester metadata providers 2026-06-16 16:37:26 +03:00
Yiorgis Gozadinos
b47583258e
Run analysis sandbox VFS reads on the calling loop via the skill connection 2026-06-05 16:20:11 +03:00
Yiorgis Gozadinos
7906688bf1
Fix analysis sandbox "Already borrowed" crash on VFS reads 2026-06-05 11:50:19 +03:00
Yiorgis Gozadinos
710276ffd8
record cassettes against new fixtures, fix stale assertions 2026-05-26 11:44:46 +03:00
Yiorgis Gozadinos
8c57a3ca99
Drop the multi-agent research workflow 2026-05-20 12:46:48 +03:00
Yiorgis Gozadinos
6f95e2bc27
Delete the standalone QA and analysis agents 2026-05-19 11:39:20 +03:00
Yiorgis Gozadinos
947a26b391
client.analyze routes through the rag-analysis skill; drop documents= and AnalysisResult.program. Re-record all cassettes that are relevant 2026-05-19 10:46:32 +03:00
Yiorgis Gozadinos
d96d2eeb0f
client.ask routes through the rag skill; always show citations in CLI 2026-05-19 10:19:39 +03:00
Yiorgis Gozadinos
387c4f12a9
multimodal sandbox: show_image + picture_refs, drop llm 2026-05-18 16:48:34 +03:00
Yiorgis Gozadinos
849eca94d0
Make rebuild --embed-only idempotent across crashes 2026-05-14 15:02:12 +03:00
Yiorgis Gozadinos
a035b0f9e4
Stream rebuild --embed-only through a staging table to bound memory 2026-05-14 15:02:12 +03:00
Yiorgis Gozadinos
704bea6f4a
Stabilize test_client_search ranking assertion 2026-05-14 14:37:27 +03:00
Yiorgis Gozadinos
9f8c9fe3b1
cover s3 uri override, unsupported extension, watcher error path 2026-05-11 11:29:56 +03:00
Yiorgis Gozadinos
6631e339ee
record VCR cassettes for s3 source tests 2026-05-11 11:29:56 +03:00
Yiorgis Gozadinos
6af463699d
record VCR cassette for vllm embedder end-to-end test 2026-05-06 20:01:22 +03:00
Yiorgis Gozadinos
08696773fe
Fix vcr test 2026-05-05 14:38:03 +03:00
Yiorgis Gozadinos
a45d82f206
Update benchmark 2026-05-05 13:58:48 +03:00
Yiorgis Gozadinos
e88321767d
always store picture bytes; collapse converter to a single zip path 2026-05-05 10:40:40 +03:00
Yiorgis Gozadinos
aa3e9406cf
auto-append /v1 to per-model Ollama base_url, fix flaky tests 2026-05-04 13:12:37 +03:00
Yiorgis Gozadinos
c9fdad15a9
bump docling-core to >=2.74.1 so that local docling-local and docling-serve chunkers run the same MarkdownTableSerializer 2026-05-03 18:40:44 +03:00
Yiorgis Gozadinos
c9227c649f
emit synthetic picture chunks at ingest under multimodal embedders.
processing.chunk() merges text chunks with one synthetic Chunk per PictureItem-with-bytes,
sorted by iterate_items() position so chunk.order is structural.
embed_chunks dispatches on a Chunk._picture_data PrivateAttr
(text through embed_documents, picture through embed_image_query)
2026-05-03 17:04:43 +03:00
Yiorgis Gozadinos
75c8e83515
Allow uri override on create_document_from_source 2026-04-30 16:36:35 +03:00
Yiorgis Gozadinos
191cdc636b
retrieve picture image bytes via referenced+zip path, fix docling-serve not returning images even when set to do so. Remove xfail from relevant test 2026-04-30 11:23:29 +03:00
Yiorgis Gozadinos
821b7361e9
Push document-id filter into chunk search query 2026-04-24 16:07:07 +03:00
Yiorgis Gozadinos
ddcc4518ad
Cover FULL rebuild source-failure branch 2026-04-24 15:32:53 +03:00
Yiorgis Gozadinos
258ffa41fa
Improve test coverage 2026-04-24 14:43:59 +03:00
Yiorgis Gozadinos
d0ed9e213f
Pin FTS search column to content_fts 2026-04-24 14:42:53 +03:00
Yiorgis Gozadinos
b6ea07d2de
Track all in-flight background vacuum tasks, not just the last one 2026-04-24 14:42:53 +03:00