diff --git a/.github/requirements/base-deps.txt b/.github/requirements/base-deps.txt index 8dac3d63..751d7dad 100644 --- a/.github/requirements/base-deps.txt +++ b/.github/requirements/base-deps.txt @@ -543,7 +543,7 @@ cuda-pathfinder==1.6.0 \ # via # -c requirements-ci.txt # cuda-bindings -cuda-toolkit==13.0.3.0 \ +cuda-toolkit==13.0.3 \ --hash=sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f # via # -c requirements-ci.txt diff --git a/.github/requirements/benchmark-extra.txt b/.github/requirements/benchmark-extra.txt index 5754a2ba..a39c037e 100644 --- a/.github/requirements/benchmark-extra.txt +++ b/.github/requirements/benchmark-extra.txt @@ -403,7 +403,7 @@ cuda-pathfinder==1.6.0 \ # via # -c requirements-ci.txt # cuda-bindings -cuda-toolkit==13.0.3.0 \ +cuda-toolkit==13.0.3 \ --hash=sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f # via # -c requirements-ci.txt diff --git a/.github/requirements/explorer-extra-py311.txt b/.github/requirements/explorer-extra-py311.txt index ca70fab2..0528b3f6 100644 --- a/.github/requirements/explorer-extra-py311.txt +++ b/.github/requirements/explorer-extra-py311.txt @@ -547,7 +547,7 @@ cuda-pathfinder==1.6.0 \ # via # -c requirements-ci.txt # cuda-bindings -cuda-toolkit==13.0.3.0 \ +cuda-toolkit==13.0.3 \ --hash=sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f # via # -c requirements-ci.txt diff --git a/.github/requirements/explorer-extra-py313.txt b/.github/requirements/explorer-extra-py313.txt index 967ec205..bed437ba 100644 --- a/.github/requirements/explorer-extra-py313.txt +++ b/.github/requirements/explorer-extra-py313.txt @@ -600,7 +600,7 @@ cuda-pathfinder==1.6.0 \ # via # -c requirements-ci.txt # cuda-bindings -cuda-toolkit==13.0.3.0 \ +cuda-toolkit==13.0.3 \ --hash=sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f # via # -c requirements-ci.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a060562..0fb781a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -168,3 +168,5 @@ jobs: print("Explorer frontend is packaged") PY + - name: Run Google ADK Integration Tests + run: pytest tests/integrations/google_adk/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9f3bccc..c20c4778 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,6 +67,12 @@ jobs: run: | pip install -r .github/requirements/twine.txt --require-hashes twine check dist/* + # pypi-publish uploads everything under packages-dir (default: dist/) with + # no glob/include filter, so it must run before anything else writes a + # non-distribution file into dist/ - the Sigstore step below does exactly + # that (dist/*.sigstore.json), and pypi-publish fails on it with + # "InvalidDistribution: Unknown distribution format" if it runs after. + - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 - name: Attest build provenance uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4 with: @@ -75,7 +81,7 @@ jobs: # OpenSSF Scorecard's Signed-Releases check does not inspect - it looks for # signature files attached as release assets. Sign here too so # `dist/*.sigstore.json` bundles ship alongside the wheel/sdist on the - # GitHub Release itself. + # GitHub Release itself. This must run after pypi-publish (see above). - name: Sign artifacts with Sigstore uses: sigstore/gh-action-sigstore-python@790bc6befb9d733738f18d8f895854b453640ec9 # v3.5.0 with: @@ -88,4 +94,3 @@ jobs: dist/*.whl dist/*.tar.gz dist/*.sigstore.json - - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 04523815..427a5e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.8] - 2026-09-05 + ### Added - **Salesforce ingestor** (#1240) by @Sameer6305 @@ -18,7 +20,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New `pip install semantica[db-salesforce]` extra (`simple-salesforce>=1.12.0`) - New `tests/test_salesforce_ingestor.py` - Docs: `docs/integrations/salesforce.md` - - **`ErasureCoordinator` completes the erasure workflow `purge_node()` only starts — the graph node was removed while the same content survived verbatim in `AgentMemory` and as an embedding** (closes #1018) by @pravit-amp - New `semantica/context/erasure.py`, exporting `ErasureCoordinator` and `ErasureReceipt` from `semantica.context`. `purge_node()`/`purge_edge()` (#957) are graph-scope by design and their changelog entry documents this gap explicitly; the changelog also names GDPR Article 17 as the motivation, and an Article 17 erasure that removes the node while the content stays retrievable by similarity search is not an erasure — it is worse than not offering one, because `purge_node()` returns `True` and writes a tombstone attesting the content is gone - The coordinator **composes** the existing public APIs — nothing in `context_graph.py` or `agent_memory.py` changes behaviorally, and `ContextGraph` keeps its documented graph-scope contract rather than acquiring references to `AgentMemory`/`vector_store` that would invert the dependency @@ -39,6 +40,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Fixed during review** (Qodo): the constructor's "at least one store" guard used `not vector_store`, rejecting a valid store whose `__bool__`/`__len__` makes an empty instance falsey, and reporting `vector_store=None` in the error when an object had been passed; it now distinguishes `None` (absent) from `False` (deliberately disabled) from any other value (provided), and echoes what it actually received - **Fixed during review** (Qodo): `at` annotations accepted only `str`/`datetime` while the shared `ContextGraph` normalizer they delegate to also takes epoch seconds; widened to `int`/`float` with the docstrings updated, so the coordinator no longer advertises less than the graph API it wraps - **Known limitation, unchanged by this PR**: erasure still cannot be *completed* on FAISS/Milvus/Weaviate — `delete_vectors()` is declared on the `VectorStore` facade (`vector_store.py:786`) but not implemented across the backend set, under at least three different names. That is worth its own issue; the coordinator ships reporting `unsupported` and starts reporting `erased` for those backends once it is fixed, with no API change here +- **Ontology package gains a deterministic, CI-friendly quality gate for ontologies and knowledge graphs** (#1397, closes #1393) by @T1mn — machine-readable quality findings with severities, metrics, statistics, and configurable thresholds; deterministic checks cover ontology structure, class/property coverage, domain/range references, and KG relationship endpoints. Reuses the existing `OntologyValidator`, `OntologyEvaluator`, and `GraphValidator` with no new runtime dependencies. Exposed through `semantica.ontology` and `OntologyEngine`. New `semantica/ontology/quality_gate.py`; new `tests/ontology/test_ontology_quality_gate.py`, and the full targeted ontology/graph-validator suite the author ran alongside it: 63 passed, 4 skipped. This first version reports findings only — no auto-fix, dashboard, or benchmark integration yet. +- **`VectorStore` gains `scan_vectors()`/`iter_vectors()` enumeration, and `store migrate` becomes functional** (#1264, part of #1265) by @ZohaibHassan16 — previously vector stores exposed only `get_vector(id)`/`count()`, so there was no way to loop over all vectors, and `semantica store migrate` always told users to export/reindex manually. Adds `scan_vectors(offset, limit)` to `FAISSStore`, `SQLiteVecStore`, and `PgVectorStore` — backends that can support normal positional pagination; in-memory is handled directly by the facade, other backends delegate when they support it, and unsupported backends raise `NotImplementedError` rather than silently returning nothing. `store migrate` now actually migrates between faiss/sqlite/pgvector, copying vectors and metadata in batches and stamping `--namespace` onto metadata that doesn't already have one. Pinecone/Qdrant/Milvus/Weaviate are deferred to follow-up PRs since each backend paginates differently. 22 new tests covering backend scanning, facade behavior, and the migrate CLI. +- **`VectorStore.iter_vectors()` dispatches to a new `iter_all()` cursor primitive, with Qdrant as the first cursor-based backend** (#1316, part of #1265) by @ZohaibHassan16 — none of Qdrant/Pinecone/Milvus/Weaviate's native pagination APIs can properly implement positional `scan_vectors(offset, limit)` (Qdrant's cursor is a point ID, Pinecone's is an opaque continuation token, Milvus's `offset` is capped at a 16,384-result window, Weaviate's cursor is the previous object's UUID), so rather than faking positional offsets, backends can now implement `iter_all(batch_size)` as a generator over their native paging API; the facade uses it via `callable()` when present (consistent with existing `count()` dispatch) and falls back to the `scan_vectors()` loop otherwise. `QdrantStore.iter_all()` threads `scroll()`'s `next_page_offset` between requests — correctly yielding a final non-empty page even when the cursor is already exhausted — and raises on an uninitialized store rather than returning empty, so `store migrate` can't report success after copying zero vectors (the failure mode from #1083). Also fixes `store migrate` inferring vector dimension from a nonexistent `._backend_store.dimension` attribute on Qdrant/Milvus/Weaviate (silently falling back to a wrong default of 768) by reading dimension off the first scanned record and chaining it back into the iterator. Qdrant is added to `store migrate`'s supported backends. 6 facade dispatch tests, new `tests/vector_store/test_qdrant_store.py` (10 tests), and 5 CLI dimension-inference tests. +- **`WeaviateStore.iter_all()` adds cursor-based full-collection iteration for Weaviate** (#1317, part of #1265, stacked on #1316) by @ZohaibHassan16 — Weaviate's `fetch_objects(after=)` pagination has no way to map a numeric offset to a cursor, so this reuses/extracts the cursor-loop and version-fallback logic already in `filter_by_metadata`. Unlike that method's `seen_ids` set (unbounded memory over a full scan), `iter_all()` detects a stalled scan by checking whether the next cursor advanced, keeping memory use O(1). An empty page under cursor pagination is not treated as end-of-scan on its own — `after` has no server-issued continuation value of its own, so a batch could in principle land entirely on a gap (tombstoned objects) with live data past it, the same risk previously confirmed for Qdrant's scroll cursor — so the iterator falls back to an offset-based check once before ending the scan. Also fixes `_extract_vector()` silently producing a corrupted 0-d array against a real (non-mocked) Weaviate collection by unwrapping weaviate-client v4's `{'default': [...]}` vector shape. New `tests/vector_store/test_weaviate_store.py` covering cursor threading, short-page termination, the empty-page/gap fallback, stalled-cursor termination, and the offset fallback when a client rejects `after`. Wiring Weaviate into `store migrate` itself is deferred to #1335 — the facade's write dispatch (`store_vectors()` only recognizes `add`/`add_vectors`, not Weaviate's `add_objects`) and initialization (the facade never calls `connect()`/collection-selection) aren't ready for a backend shaped like this one. +- **`MilvusStore.iter_all()` adds Milvus to the `iter_vectors()` cursor family via Milvus's query iterator** (#1326, part of #1265, stacked on #1316) by @ZohaibHassan16 — Milvus's `query(offset=...)` caps `offset + limit` at a documented 16,384-result window, so an offset-based scan would silently truncate any collection larger than that; `query_iterator()` is the primitive actually meant for scans beyond it. The iterator is closed in a `finally` block since it holds server-side state, covered by tests for both normal exhaustion and early/exception-path abandonment. Matches the missing-iterator-raises-rather-than-returns-empty behavior established for Qdrant (#1316) and Weaviate (#1317), so an unsupported `pymilvus` version can't make `store migrate` look like it copied an empty collection successfully; `store migrate` wiring for Milvus is left for a separate PR. New `tests/vector_store/test_milvus_store.py`: 13 tests (Milvus had no dedicated test file before). +- **`WeaviateStore` gains `delete_vectors()`, completing Weaviate support for `ErasureCoordinator`** (#1392) by @pkupt — Weaviate half of #1374 (Milvus landed in #1391; FAISS stays unsupported since flat indices can't delete in place). IDs are the object UUIDs `store_vectors()` returns, deleted one at a time via `collection.data.delete_by_id`, which returns `False` rather than raising for a missing UUID, so the erasure receipt's `backend_result` count stays honest. 10 tests cover single/multi-id deletes, not-found-uuid counting, empty ids, and the missing-collection path, plus two integration tests binding `WeaviateStore` as a backend; author notes this is logic-level coverage since Weaviate wasn't available locally to verify live wire behavior. +- **`semantica.llms` gains a first-class `Anthropic` provider wrapper** (#1255, closes #1253) by @ZohaibHassan16 — matches the existing `Groq`/`OpenAI` wrapper pattern (`generate`, `generate_structured`, `generate_typed`, `is_available`) over the `AnthropicProvider` already used internally by semantic extraction; previously reachable only through the generic LiteLLM passthrough. New docs section in `docs/guides/llm-integrations.md`; 6 new tests in `tests/test_llm_anthropic.py`. +- **`semantica.llms` gains `Gemini`, `Ollama`, `DeepSeek`, and `Novita` provider wrappers** (#1262, closes #1261) by @ZohaibHassan16 — these four providers already existed in `semantic_extract/providers.py` but weren't exposed from the public `semantica.llms` API. Each follows the same `generate`/`generate_structured`/`generate_typed`/`is_available` pattern as `Groq`/`OpenAI`/`Anthropic`. Adds the missing `llm-novita` extra to `pyproject.toml` (uses the `openai` dependency, like DeepSeek), included in `llm-all`; docs added for Gemini/Ollama/DeepSeek, and the existing Novita docs updated to use the new wrapper instead of calling `create_provider()` directly. 32 new tests (8 per provider), following the `test_llm_anthropic.py` pattern. +- **Explorer's read-only Markdown viewer becomes a full editor for live `ContextGraph` nodes and host-supplied `AgentMemory` items** (#1349, closes #1327) by @genni613 + - New canonical single-resource Markdown export/apply methods on `ContextGraph` and `AgentMemory`; resource IDs are validated against frontmatter before mutation, stale writes are rejected via `expected_revision` with HTTP 409, and writes validate fully before commit so failures can't leave a partial mutation. Edits apply to the live in-memory runtime object only — this PR does not introduce disk or restart persistence. + - New Explorer endpoints: `GET`/`PUT /api/markdown/{kind}/{resource_id:path}` and paginated `GET /api/memories`, returning structured 404/409/422/500 responses behind existing Explorer auth; `/api/info` now exposes `capabilities.agent_memory` so the UI can detect whether a host app supplied a memory store. + - Explorer UI gains Edit/Apply/Cancel alongside the existing Preview/Source/Copy; edits validate against the full canonical document (including supported frontmatter), no-op Applies are disabled, drafts persist across validation/conflict/network/server errors, navigation is guarded when a draft has unapplied changes, and Apply refreshes canonical source, revision, graph content, and labels. A new Memories workspace appears only when the host app supplies `create_app(agent_memory=...)`. + - Test coverage: domain round-trip/identity/validation/rollback tests, API success/conflict/authorization/failure-path tests, editor interaction tests (Apply/Cancel/dirty-navigation/retry), and capability/Memories-workspace tests. Author-reported: targeted Python acceptance suite 133 passed, 2 skipped; `npm run test:graph-workspace` 106 passed; `test:graph-store`, `test:deterministic-e2e`, and `test:plugin-registry` (7 passed) all green; `npm run build` passed. Full Python test collection was blocked locally by unrelated NumPy/h5py/spaCy binary incompatibilities. +- **New deterministic Explorer rendering example and end-to-end test covering build -> persist -> API -> frontend hydration -> canvas rendering** (#1041, closes #1037) by @alexsmolya — new `examples/explorer_deterministic_rendering_example.py` builds a canonical 4-node/3-edge graph (`Alice --WORKS_AT--> Acme`, `Bob --KNOWS--> Alice`, `Acme --LOCATED_IN--> New York`), persists it with `ContextGraph.save_to_file()`, and reloads with `GraphSession.from_file()`, printing setup/auth/launch guidance. New backend test `tests/explorer/test_explorer_deterministic_rendering_e2e.py` covers graph construction/serialization, `GraphSession`, and exact `/api/graph/*` node/edge/label responses across auth modes. New frontend tests (`deterministicExplorerRendering.test.ts`, `.e2e.ts`) mount the real Explorer app in Chromium, hydrate the real graph store through `useLoadGraph`, render the real Sigma canvas, and assert `WORKS_AT`/`KNOWS`/`LOCATED_IN` are actually drawn and stay labeled after zoom; redundant extra `label` plumbing is removed now that edge labels render from the already-hydrated `edgeType`. Author-reported: backend e2e 5 passed; frontend deterministic suites 49 graph-workspace + 1 graph-store + 7 plugin + 1 Chromium canvas E2E test passed; broader `tests/explorer` run 261 passed, 2 skipped, 2 pre-existing unrelated SHACL failures. +- **`integrations/google_adk`: first-class Google ADK support** (#1312, resubmit) by @Hitesh-XS — new `integrations/google_adk/` package (`kg_tools.py`, `decision_tools.py`, `session_service.py`) exposing Semantica's context-graph and decision-intelligence APIs as Google ADK tools and a session service, with its own README. Bundles `google-adk` into the Agentic Framework Integrations section of `pyproject.toml` and into the `all` extra. Also restores packaging state that had regressed on `main` (pinned `anthropic`/`pyarrow` bounds, `ingest-sap`, `langchain`, and package-data fixes) and replaces the deprecated `pinecone-client` dependency with the official `pinecone` package, which had been crashing context-graph initialization — and with it every integration test touching Pinecone. `mcp/` is renamed to `semantica_mcp/mcp/`, with import paths updated across MCP tests and tools. Author reports all 36 tests in `tests/integrations/google_adk/` passing against the corrected Pinecone dependency. + +### Changed + +- **`docs/guides/decision-intelligence.md`: fixed a broken `add_decision` pattern and a wrong hybrid-search description** (#1466) by @ZohaibHassan16 — the alternative "build a `Decision` object, pass to `add_decision`" pattern silently produced nodes invisible to `find_precedents`/`get_causal_chain`/`get_decision_insights` and raised `ValueError` on trace; replaced with the working keyword-argument form. Corrected the hybrid search description (was described as semantic similarity + Node2Vec embeddings at 0.7/0.3; actually word-level Jaccard overlap + connection-count structural similarity) and fixed a wrong decision id in the banking loan example that silently attached to a phantom node +- **Tightened prose for clarity and conciseness across the setup, architecture, cookbook, resources, glossary, modules, contributing, and community-facing docs** (#1459, #1458, #1457, #1456, #1454, #1453, #1452, #1442) by @Deep070203 — `cli-setup.md`, `explorer-setup.md`, `installation.md`, `quickstart.md`, `architecture.md`, `cookbook.md`, `citation.md`, `faq.md`, `learning-more.md`, `project-license.md`, `glossary.md`, `choose-your-module.md`, `modules.md`, `contributing-guide.md`, `community-projects.md`, `community.md`, and `governance.md`; no technical content changed +- **`docs/reference/ontology.md`: documented Quality Gate threshold semantics** (#1450) by @KaifAhmad1 — added a `### Thresholds` table covering `min_coverage`, `max_errors`, `max_warnings`, and `fail_on_warnings` (noting the latter is a separate constructor/call parameter, not a `thresholds` key), verified against `OntologyQualityGate.DEFAULT_THRESHOLDS` +- **Replaced the retired `claude-sonnet-4-20250514` model id in docs and LLM wrappers** (#1449) by @ZohaibHassan16 — updated roughly 15 examples across `graphrag.md`, `llm-integrations.md`, `multi-agent.md`, `ontology.md`, and `reference/llms.md` (plus the LiteLLM/Anthropic wrapper defaults) to `claude-sonnet-5`, `claude-opus-4-7`, and a current Bedrock model id +- **`docs/guides/semantic-extraction.md`: fixed a wrong triplet count and a retired model id** (#1448) by @ZohaibHassan16 — the pipeline example printed `{}/{} triplets valid` using the Turtle output's string length instead of the triplet count (producing output like `7/4231`); now uses a real `triplets_total` value. Also replaced `claude-sonnet-4-6` with the dated model id used elsewhere, and clarified the sample NER output is illustrative +- **`docs/reference/reasoning.md`: clarified Datalog query result ordering** (#1447) by @ZohaibHassan16 — the `datalog.query(...)` example implied a fixed result order; results are set-backed and unordered, so the comment no longer implies otherwise +- **`docs/index.md`: rewrote the landing page as a lean developer welcome** (#1446) by @KaifAhmad1 — replaced the long feature-dump page with a shorter one built around Semantica's deterministic semantic/context-infrastructure positioning, trimming the module table, use-case grid, and duplicate link lists (kept as a collapsed accordion so the module-coverage check still passes) +- **`docs/guides/pipeline.md`: fixed the retry-policy example** (#1444) by @ZohaibHassan16 — the example configured a `FailureHandler` with custom retry policies but never assigned it to the `ExecutionEngine`, which builds its own handler, so the configured policies were silently ignored; added `engine.failure_handler = handler`. Also replaced a hardcoded node/edge-count output comment with a shape-only example +- **`docs/modules.md`: fixed code examples across the module catalogue to match the current API** (#1443) by @ZohaibHassan16 — corrected snippets using nonexistent or outdated APIs (e.g. `NERExtractor`'s `method="llm"`, `SimilarityCalculator.calculate_similarity()`, `Reasoner.apply_transitivity()`/`infer()`, `EntityResolver`, `ConflictDetector.resolve()`, treating `Pipeline` as a builder/runtime API) across extraction, graph building, reasoning, deduplication, conflicts, embeddings, vector store, export, pipeline, seed data, and evals sections; all 31 code blocks now parse and were run against current source +- **`docs/integrations/langchain.md`: tightened integration prose** (#1432) by @taljeon — replaced a remaining em dash with direct sentences and reformatted the component list as name/type pairs; no technical content changed +- **`docs/guides/graphrag.md`: fixed broken example strings and clarified `max_hops`** (#1431) by @ZohaibHassan16 — the banking example's multi-line string literals raised `IndentationError`; wrapped in parentheses to match the working Clinical example. Clarified that `AgentContext.retrieve(max_hops=)` only bounds anchored proximity scoring rather than graph-expansion depth (`max_expansion_hops` controls that); also fixed a made-up node/edge count comment +- **Tightened prose and fixed two broken relative links in `concepts.md`, `guides/graphrag.md`, and `reference/context.md`** (#1422) by @KaifAhmad1 — removed em dashes from explanatory prose (left intact in simulated document/alert examples); fixed `reference/context.md` links to `reasoning`/`provenance` that were missing a leading slash and would 404; updated `concepts.md`'s intro tagline to match #1421 +- **`docs/index.md`: rewrote landing-page prose to be crisp and direct** (#1421) by @KaifAhmad1 — cut the marketing/storytelling framing and all em dashes; updated the tagline to "The Context and Semantic Layer for AI in High-Stakes Domains" across `docs.json` and `index.md`, keeping audit trail/accountability as a property rather than the headline +- **Restructured the docs nav** (#1419) by @KaifAhmad1 — dropped the standalone FAQ and Changelog tabs (their pages moved under Overview) and added a dedicated API Reference tab holding the `reference/*` pages split out of Modules +- **`docs/assets/custom.css`: replaced decorative hover/fade animations with static styling** (#1418) by @KaifAhmad1 — removed the page-load fade-in and hover lift/glow effects on code blocks, cards, buttons, and nav links site-wide, keeping the existing color palette and accessibility focus rings +- **`docs/concepts.md`: fixed 9 of 13 code examples that no longer matched the current API** (#1417) by @ZohaibHassan16 — corrected the `GraphBuilder`, GraphRAG, forward-chaining/Rete/Datalog reasoning, `GraphReasoner`, `SimilarityCalculator`, provenance, and `MethodRegistry` snippets, plus the distance-band terminology and engine comparison table +- **`docs/quickstart.md`: fixed the parsed-document example to read `full_text`** (#1415) by @ZohaibHassan16 +- **`docs/getting-started.md`: fixed broken Knowledge Graph and GraphRAG "Choose Your Path" examples** (#1414) by @ZohaibHassan16 — the extractor calls now pass parsed text instead of a `FileObject`, and the GraphRAG example uses `context.store()` + `retrieve(use_graph=True, ...)` instead of the nonexistent `load_graph()`/`query(mode=...)` APIs +- **Fixed ~300 relative body links across 74 docs pages that 404'd on the live site** (#1407, closes #1405) by @Duansg — GitHub Pages' trailing-slash redirect resolved hand-written relative Markdown links against the wrong base path; links are now rewritten as root paths +- **Fixed two broken cookbook notebook links** (#1403) by @ZohaibHassan16 — `docs/learning-more.md` pointed to a nonexistent `09_Embeddings.ipynb` (now the correct `12_Embedding_Generation.ipynb`), and `docs/reference/distance.md`'s dead link to a nonexistent Distance Intelligence notebook was removed +- **`docs/quickstart.md`/`docs/faq.md`: addressed Qodo review findings** (#1402, follow-up to #1401) by @ZohaibHassan16 +- **`docs/quickstart.md`: fixed the Full Pipeline walkthrough against current APIs** (#1401) by @ZohaibHassan16 — corrected the parse, extract, ingest (`WebIngestor`/`XMLIngestor`), export (`ArangoAQLExporter`, Parquet), OCR, and `PipelineBuilder` examples, and fixed a stale `Pipeline(workers=N)` example also present in `faq.md` +- **Updated stale latest-version references to v0.6.7** across `docs/faq.md`, `docs/index.md`, and `docs/quickstart.md` (#1400) by @ZohaibHassan16 +- **`docs/reference/mcp_server.md` and related pages: documented all 15 MCP tools** (#1399) by @ZohaibHassan16 — added the three previously-undocumented tools (`query_graph`, `update_node`, `delete_node`) and corrected the tool count everywhere it appeared +- **`docs/reference/evals.md`: rewritten to match the shipped `semantica.evals` API** (#1398) by @ZohaibHassan16 — replaced the stale "not yet implemented" placeholder with `evaluate()`, `list_evaluators()`, `EvalMetric`/`CaseResult`/`EvalSummary`, all 10 built-in evaluators, and the `decision_scores` sub-checks +- **README: propagated SAP OData connector mentions consistently and trimmed the audience list** (#1396) by @KaifAhmad1 — added SAP mentions to the Enterprise Data Platforms bullet, ingest summary, module reference table, and supported-sources line (previously only in "What's New"); tightened the "Who it's for" bullets; removed sample `semantica doctor` output from the quickstart snippet +- **Rewrote the Semantic Layer Basics cookbook lesson as a runnable introductory workflow** (#1361, closes #1325) by @taoche — replaced the removed `advanced/09_Semantic_Layer_Construction.ipynb`, which never used `TripletStore`, left mappings empty, and never executed a query, with `introduction/26_Semantic_Layer_Basics.ipynb`, whose ontology, mappings, RDF, and SPARQL query now agree end to end +- **Rewrote cookbook notebook 08 into a real, rerunnable knowledge-graph workflow** (#1359, closes #1289) by @taoche — it previously read the wrong parser key, substituted hard-coded extraction fixtures, bypassed `GraphBuilder`, and never called `KGVisualizer`; it now runs parse → NER/relation extraction → `GraphBuilder` → `KGVisualizer` end to end +- **Fixed cookbook notebook 07's graph mapping and deduplication output** (#1357, closes #1287) by @taoche — edges were built from loop indices instead of extracted relation endpoints, and the dedup output showed only merge operations, making 5 mentions falsely appear to collapse to 1 entity instead of the correct 4 +- **README: repositioned Semantica's opening pitch around the semantic/context/knowledge layer** (#1348) by @KaifAhmad1 — leads with Context Graph, KG, and ontology governance (OWL/SHACL/SKOS) rather than framing audit trails as the flagship pattern; reordered the hero pillar list to lead with Context Management/Knowledge Modeling ahead of Decision Intelligence +- **Hash-pin every pip install across the Dockerfile and CI workflows for Scorecard Pinned-Dependencies** (#1338) by @KaifAhmad1 — CI/build hardening, no runtime behavior change. Closes 21 OpenSSF Scorecard alerts: existing `pkg==X.Y.Z` version pins (even installs already reading a hashed `requirements-ci.txt`) still scored low because no hash is visible on the install command itself. Adds hash-locked `.github/requirements/*.txt` files (via `uv pip compile --generate-hashes`) for every pip target not already covered, adds `--require-hashes` to all `-r requirements-ci.txt` installs, and splits local-source installs into `pip install --no-deps -e .` plus a separately hash-pinned dependency install (a local source tree has nothing to hash directly). The Dockerfile now installs from a pre-generated `explorer-extra.txt` rather than extracting constraints at build time +- **Test-only contributions**: fixed `sys.modules` mock leakage in `test_extractors_dispatch.py` that made 132 tests pass in isolation but fail in a full-suite run, by installing the mocks per-test via `patch.dict`/`addCleanup` instead of at module scope (#1337, closes #1336, by @dex0shubham); added missing `__init__.py` package markers to `tests/integrations/crewai/` and `tests/integrations/langchain/`, fixing a pytest collection abort from two same-named `test_degradation.py` files colliding under prepend import mode (#1252, closes #1251, by @dex0shubham); guarded fastapi-dependent Explorer test modules so `tests/explorer/` and `tests/test_security_regression.py` collect successfully without the `explorer` extra installed (#1232, closes #1167, by @dex0shubham) +- **`ContextGraph`'s temporal-input normalizer is now a public API** (#1455, closes #1377) by @Saket7002 — `normalize_temporal_input` is exposed publicly so `context/erasure.py`'s `ErasureCoordinator` can call it directly instead of reaching across modules for a private helper. No behavior change. Regression coverage added for the public normalizer; full targeted run (`test_context_graph_retraction.py` + `test_erasure_coordinator.py`): 101 passed. +- **New acceptance tests pin known contract gaps between `VectorStore`'s facade and the Qdrant/Pinecone/Milvus/Weaviate backends, as strict `xfail`** (#1332) by @ZohaibHassan16 — existing vector-store tests all bypass `_init_backend_store` (the code path that actually constructs cloud backend adapters), either mocking backend internals directly or injecting a fake backend, which is how #1316 could be fully green while broken end to end: a Qdrant-backed `VectorStore` can't read (no connection/collection ever established) and can't write (`store_vectors()` doesn't dispatch to `QdrantStore.insert_vectors`). New `tests/vector_store/test_backend_facade_contract.py` constructs each backend through the real facade path and marks the two capability gaps `xfail(strict=True)` for Qdrant/Pinecone/Weaviate (Milvus already passes, pinned separately as a control) — a fix will flip these to unexpected passes and fail the suite until the marker is removed, making them acceptance criteria rather than assertions of the broken behavior itself. 13 new tests (6 pass, 7 xfail); no application code changed. +- **CI now reports required status checks correctly on docs-only PRs** (#1410) by @Sameer6305 — `ci.yml`/`security-scan.yml` still trigger on every PR including docs-only changes, but skip their expensive jobs for docs-only diffs while still reporting a check status, so required checks don't block on jobs that never ran; full build/security scans are preserved for source or mixed changes, and non-PR triggers are unaffected. +- **CI gains npm Dependabot coverage for `explorer/` and container image scanning** (#1286) by @KaifAhmad1 — `dependabot.yml` previously had no `npm` ecosystem entry for `explorer/`, which is why the `brace-expansion`/`nanoid` CVEs fixed in #1280 went undetected until a manual check; added, mirroring the existing `pip` entry's schedule/labels/reviewers. New `container-scan.yml` builds the Dockerfile image, scans it with Trivy (CRITICAL/HIGH to the Security tab as SARIF, `ignore-unfixed: true`), and generates an SPDX SBOM with Syft, running on push to main, weekly, and on manual dispatch. Trivy runs report-only for now (no `exit-code` gate) until the first CRITICAL/HIGH baseline is triaged. +- **Distribution and trust-signal infrastructure: reusable install action, a PyPI install matrix, and release-pipeline hardening** (#1266) by @KaifAhmad1 + - New `.github/actions/setup-semantica` composite action other repos can call to install and verify `semantica` in one step + - New `install-matrix.yml` verifies the *published* PyPI package installs and imports cleanly across Ubuntu/macOS/Windows and Python 3.9-3.12, on a weekly schedule and on every release, backing a new "pip install" README badge + - New `scorecard.yml` runs OpenSSF Scorecard analysis weekly and on push to main, backing a new README trust-signal badge + - `release.yml` gains a `twine check` gate before publish, catching a broken PyPI long-description render before it ships; the existing Trusted Publishing/OIDC + SLSA attestation signing flow is otherwise unchanged + - New `CITATION.cff` (enables GitHub's native "Cite this repository" button alongside the existing `docs/citation.md`) and `examples/ci/` copy-paste GitHub Actions/GitLab CI/CircleCI templates for downstream adopters + - New `GROWTH.md` tracks distribution-channel status with explicit guardrails against artificially inflating download/install metrics + - No application code changed; new workflow YAML validated with `yaml.safe_load` and new action pins verified against the GitHub API +- **Resynced `github/codeql-action` pin to current v4 SHA** (#1249) by @ZohaibHassan16 — the v4 tag's underlying SHA had changed, failing "Verify Action Pins" on every PR; all 8 refs across `codeql.yml` and `defender-for-devops.yml` updated and reverified (40/40 clean). + +### Fixed + +- **README's production deploy instructions pointed at an environment variable that exists nowhere in the codebase** (#1473, fixes #1429) by @v01dst — `README.md:1546` told deployers to set `SEMANTICA_SECRET_KEY`, but the Explorer auth code (`semantica/explorer/dependencies.py:30`) reads `SEMANTICA_API_KEY` (with `SEMANTICA_ALLOW_ANONYMOUS=true` as the opt-out), so a deploy following the README set a silently-ignored variable and then hit 503s or unintended anonymous mode. One-line docs fix; `grep SEMANTICA_SECRET_KEY README.md` shows 0 hits afterward +- **The Python 3.9 install matrix was still broken after the spaCy/thinc fix in #1329** (#1445, closes #1347) by @ZohaibHassan16 — `scikit-learn`, `requests`, `chardet`, `grpcio`, `pillow`, `click`, and `onnxruntime` all now ship minimum versions requiring Python 3.10+, so a plain no-extras install on 3.9 failed to resolve. Adds Python-version markers for each, following the existing spaCy/thinc pattern: 3.9 is capped at the latest compatible release per package, 3.10+ stays unconstrained. Verified with `uv pip compile --python-version 3.9` for Linux/Windows/macOS, plus 3.10 and 3.12 +- **Ontology property generation inferred framework bookkeeping fields as business datatype properties** (#1420, closes #1416) by @pkupt — `_extract_data_properties` only skipped `id`/`type`/`entity_type`/`text`/`label`/`confidence`, so structural fields `GraphBuilder` and `EntityMerger` attach to entity dicts (`properties`, `relationships`, `metadata`, `provenance`, `merged_from`, `merge_strategy`) were emitted as bogus datatype properties alongside real attributes. The skip set is now a single `_CONTROL_FIELDS` constant covering all of them; flat top-level business attributes are unaffected. New `tests/ontology/test_ontology_framework_fields.py` +- **`ErasureCoordinator(vector_store=False)` didn't actually stop all vector deletion — it only stopped the coordinator's own leg** (#1395, closes #1378) by @Harsh4r0ra — disabling the vector leg made the coordinator itself report `status="not_configured"`, but `AgentMemory.batch_delete()` → `delete_memory()` still ran its own best-effort vector-delete cascade internally, catching any failure and returning `True` regardless, so `receipt.complete` could read `True` while an embedding was still live. A `skip_vector` flag is now threaded from `ErasureCoordinator` into a new keyword-only `AgentMemory.batch_delete(skip_vector=...)` parameter whenever the vector leg is explicitly disabled. The existing test that had asserted the buggy behavior is rewritten, plus a new regression test pinning `delete_calls == 0` +- **MCP graph persistence and setup were broken across multiple surfaces** (#1394, closes #1134) by @Sameer6305 — the root MCP server loaded graphs with a non-existent method instead of `load_from_file()`, and mutations made through MCP tools weren't persisted back to `SEMANTICA_KG_PATH` on either server implementation. Fixed graph loading, wired persistence through for both MCP server implementations, corrected the MCP installation and Claude Code setup docs (including the `claude mcp add` invocation and documenting the required `PYTHONPATH`), and added end-to-end MCP stdio JSON-RPC regression coverage +- **CI's Safety-based security scan crashed intermittently instead of reporting real findings** (#1390, closes #1389) by @ZohaibHassan16 — the same crash pattern previously seen with `cuda-toolkit` recurred with `torchvision`, and identical runs against `requirements-ci.txt` could either succeed or crash, so `IGNORED_VULN_IDS` couldn't help — Safety crashed before it ever wrote a report. Replaces the Safety step in `security-scan.yml` with `pip-audit` (already used successfully in `security.yml` against the same dependencies) and removes `security.yml` entirely now that `security-scan.yml` covers everything it did, plus Bandit, Semgrep, and PR reporting on a broader trigger set. `IGNORED_VULN_IDS` is now empty since `pip-audit`'s OSV source doesn't carry either CVE Safety was flagging. Verified via YAML/embedded-JS syntax checks, report-handling tests against six report shapes, and `verify-action-pins.sh` passing with 47 action references (down from 49 after removing `security.yml`) +- **`verify-action-pins.sh` failed after `actions/deploy-pages`'s v5 tag moved** (#1387) by @ZohaibHassan16 — the tag advanced from v5.0.0 to v5.0.1 (backoff/jitter added to deployment polling, confirmed via the GitHub API); the pinned SHA in `docs.yml` is updated to match. Verified all 49 action references pass +- **CI's security scan failed on an unreachable, transitive `torchvision` CVE** (#1385, closes #1384) by @ZohaibHassan16 — `SFTY-20260723-60537` (CVE-2026-65918) is a GIF-decoder finding in `torchvision`, pulled in transitively via `safetensors`/`sentence-transformers` and never used directly (confirmed by grep across `semantica/`, `mcp/`, `integrations/`); fixed upstream in commit `4e05dc2` but not yet in any released `torchvision`. Added to `IGNORED_VULN_IDS`, matching the existing `cuda-toolkit` precedent +- **`ErasureReceipt.to_dict()` returned nested dicts shared by reference with the live receipt** (#1381, fixes #1376) by @BinarySpecter — `backend_result`'s nested dicts weren't copied, so a caller mutating the returned dict could corrupt the receipt's own internal state; the audit record it's meant to be is no longer safe to hand out. Fixed with a proper deep copy in `semantica/context/erasure.py`. `tests/context/test_erasure_coordinator.py`: 49 passed, 3 subtests +- **The `--ignore`-based Safety CVE suppression added in #1370 crashed CI on the very next run** (#1371) by @KaifAhmad1 — a correction to #1370: `--ignore` only crashes once Safety has to apply itself against a real match, and the push-triggered run on `main` immediately after #1370 merged hit the exact `'cuda-toolkit'` crash #1131/#1157 had already fixed, even though a plain scan (no `--ignore`) had run clean moments earlier on the same dependencies. The author notes their own pre-merge local testing was misleading — their local Safety database didn't surface the CVE at all, so `--ignore` never had a real match to crash against locally. Fix: drop `--ignore` entirely, run the plain scan proven not to crash, and filter the accepted vulnerability ID out of the JSON report in `jq` before both the count check and detail-printing. Also fixes a latent bug where `.vulnerabilities | length` silently returned `0` for a null/missing `vulnerabilities` key instead of erroring, which the existing Guard 2 comment had assumed already happened. Validated the jq filter against six synthetic report shapes rather than relying on a local Safety run +- **CI's security scan failed on a real, unfixable-upstream `cuda-toolkit` CVE with no released fix available** (#1370) by @KaifAhmad1 — `SFTY-20260120-40557` (CVE-2025-33228) is a hard `==13.0.3` pin from `torch==2.13.0`'s own wheel metadata (the latest available torch release), so no version bump can resolve it; the CVE itself is OS command injection in NVIDIA Nsight Systems' `gfx_hotspot` recipe, which Semantica never invokes and which isn't among the CUDA extras torch actually requests here. Added `--ignore SFTY-20260120-40557` to the `safety check` invocation, scoped to this one vulnerability ID with an inline comment explaining why and when to revisit. Verified locally against Safety 3.8.1 that the ignore only suppresses this ID and no others. (Superseded the following day by #1371, which found this `--ignore` itself reintroduced a Safety crash in live CI) +- **A malformed Safety report could be silently read as a clean scan** (#1366) by @T1mn — the Security Scan workflow had no check that `safety-report.json` actually contained a well-formed, array-valued `vulnerabilities` field before counting findings, so a present-but-malformed report risked passing as zero findings. Adds an independent fail-closed check that validates the field's shape and renders an explicit invalid-report warning instead of treating malformed data as clean; the existing `--file requirements-ci.txt` Safety scan and the separate `security.yml` pip-audit workflow are unchanged +- **The bundled Claude Code plugin failed to install entirely** (#1363, fixes #1350) by @7487 — `plugins/.claude-plugin/plugin.json` declared `"agents": "./agents"`, but unlike `skills`, Claude Code's plugin schema rejects a bare directory string for `agents` (`Validation errors: agents: Invalid input`) and requires an explicit array of `.md` file paths. Replaced with `["./agents/decision-advisor.md", "./agents/explainability.md", "./agents/kg-assistant.md"]`. New `tests/test_plugin_manifest.py` guards that `agents` stays a non-empty array of existing `.md` paths in sync with `plugins/agents/`. Verified with the official validator (Claude Code 2.1.231): validation now passes +- **Checkov's own suppressed findings kept reopening as brand-new GitHub code-scanning alerts on every rescan** (#1346) by @KaifAhmad1 — the same 4 Checkov k8s findings on `deploy/helm/knowledge-explorer` (namespace/seccomp) were already suppressed via working `checkov.io/skipN` annotations and correctly marked `SKIPPED` in Checkov's JSON output, but Checkov's SARIF exporter emits every evaluated check as an ordinary `level: warning` result regardless of skip status and never populates SARIF's own `suppressions` field — so GitHub had no way to know these were suppressed and opened new alert numbers across three separate scans. New `.github/scripts/filter_checkov_skipped.py` cross-references Checkov's JSON `skipped_checks` against the SARIF `results` (matched on check ID plus the last two path segments, since JSON and SARIF use different path roots) and drops already-suppressed results before the SARIF reaches GitHub. Verified locally against a real checkov 3.3.1 + helm 3.16.4 run: removed exactly the 4 known-suppressed results, left 2 genuinely real findings elsewhere in the repo untouched +- **A Scorecard Pinned-Dependencies alert flagged an install step for a directory that doesn't exist in the repo** (#1345) by @KaifAhmad1 — `benchmark.yml:51` ran `pip install -r benchmarks/requirements.txt`, but `benchmarks/` doesn't exist anywhere in the repository, so the step couldn't be hash-pinned and the job already failed on the very next real step (`benchmarks/benchmarks_runner.py`, also missing) — the line did nothing useful. Dropped it rather than leave it unpinned. Also closed directly via the API without a PR: #6099 (Dockerfile Pinned-Dependencies, dismissed won't-fix — installing our own git-tracked source with `--no-deps --no-build-isolation` has no third-party fetch to pin, and pip rejects `--hash`/`--require-hashes` on local directory targets) and #6112–#6115 (same suppressed-Checkov-alert root cause as #1346, dismissed as false positive) +- **`MilvusStore.get_collection()` attached to a mismatched collection and only failed later, far from the root cause** (#1344, closes #1331) by @pkupt — the method wrapped `Collection(name)` right after the `has_collection` guard with no schema check, so an INT64-pk or metadata-less collection attached successfully and only surfaced an error deep inside `get_vector`/`get_metadata`. A schema check now runs immediately after attach, before the store assigns `self.collection`, so a mismatch is caught early with an error naming the actual problem. 9 new focused tests in `tests/vector_store/test_milvus_get_collection.py` cover the matching case and each rejection case +- **The Docker build broke outright after #1338, failing Container Security Scan on the build step itself rather than just SBOM/Trivy** (#1341) by @KaifAhmad1 — `explorer-extra.txt` was compiled with `--python-version 3.11` but installed on the Dockerfile's actual `python:3.13-slim` interpreter; `librosa`'s `audioread` dependency needs `standard-aifc`/`standard-sunau` only under `python_version >= "3.13"` (Python 3.13 dropped `aifc`/`sunau` from stdlib), and a lockfile resolved for 3.11 carries no hashes for those packages at all, so `--require-hashes` failed outright once pip resolved against the real 3.13 environment. Split into `explorer-extra-py311.txt` (used by `ci.yml`, unchanged resolution) and a newly-compiled `explorer-extra-py313.txt` (used by the Dockerfile, including the `standard-aifc`/`standard-sunau`/`standard-chunk` hashes), with `.github/requirements/README.md` documenting why the two can't be recombined +- **The Neo4j persistence example in `docs/quickstart.md` raised `AttributeError` when followed as written** (#1340, fixes #1135) by @Sameer6305 — the example passed a raw `Neo4jStore` backend directly to `GraphBuilder(graph_store=store)`, but `GraphBuilder` expects the `GraphStore` facade and calls `add_nodes()`/`add_edges()`, which the raw backend doesn't expose (`'Neo4jStore' object has no attribute 'add_nodes'`). Updated the example to construct `GraphStore(backend="neo4j", ...)` instead. New regression test in `tests/kg/test_graph_builder_with_graph_store.py` covering `GraphBuilder` against the `GraphStore` facade +- **`pip install semantica` failed on Python 3.9 across all three OSes** (#1329) by @KaifAhmad1 — `spacy` had no upper bound, so pip resolved spacy 3.8.16 whose `thinc>=8.3.12` requirement has no cp39 wheels and no working sdist build path either. Caps `spacy<3.8.8` and adds `thinc<8.3.5` for `python_version < '3.10'` (py3.10+ stays unconstrained); verified with a dry-run resolve against manylinux/win_amd64/macosx_arm64, all landing on prebuilt wheels (spacy 3.8.7 + thinc 8.3.4). Also pins Docker base images by digest and remaining unpinned CI tool installs, and adds Sigstore signing so `dist/*.sigstore.json` ships alongside release artifacts (OpenSSF Scorecard Pinned-Dependencies/Signed-Releases hardening) +- **FAISS vector store silently lost `vector_ids`/`metadata` across save/load, so a reloaded index reported zero vectors and `semantica store migrate --from faiss` silently copied zero records** (#1314, closes #1272) by @AhmadBilalDSA — loading a saved index reinitialized `vector_ids = []` and `metadata = {}`, so `scan_vectors()` returned `[]` and `count()` returned `0` despite a valid binary index on disk. Metadata now persists to an atomic companion `.meta.json` file written alongside the index, restored exactly on reload, with a `RuntimeWarning` plus a logged warning when the binary index exists but its sidecar is missing. New end-to-end regression test verifying `scan_vectors()` matches the original records across fresh store instances +- **Registered ontologies opened the Ontology Editor to an empty canvas, and ontology deep links didn't land on the Editor at all** (#1278, closes #1274) by @taoche — the app shell ignored `ontologyTab`/`ontologyEntity` URL state, and even when the Editor did open, it loaded registry metadata but never fetched the selected ontology's schema nodes and structural edges. Adds `GET /api/ontology/graph?uri=...` returning the bounded schema subgraph, wires deep-link state into startup tab selection, and maps the response into React Flow nodes/edges with loading/error/selection handling. 40 backend tests plus 77 explorer graph-workspace tests pass +- **Explorer's Full Graph view rendered small, multi-component graphs as unlabeled dots with relationships suppressed** (#1277, closes #1275) by @taoche — coordinate-free graphs of any size got the same large-graph seed layout, ForceAtlas2 stabilization, and overview edge LOD, which crushes node spacing and hides ordinary edges on a small graph. Adds a deterministic, component-aware layout path for coordinate-free graphs of up to 48 nodes — skips force stabilization, keeps labels visible, preserves relationship edges — while larger graphs and graphs with existing coordinates are unaffected. 81 explorer tests pass +- **Explorer graph-loading failures showed only a generic `Fetch failed: ` message, discarding the server's actionable error detail** (#1260, closes #1256) by @wanglin1111111 — e.g. an unconfigured `SEMANTICA_API_KEY` returns a specific remediation string in the response body's `detail` field, but the UI overlay showed a generic "check that the backend is running" hint instead, sending users down the wrong troubleshooting path. `useLoadGraph.ts` now reads the JSON body on a non-OK response and appends `detail` to the thrown error, degrading gracefully when the body isn't JSON +- **`ConsoleProgressDisplay` wrote progress bars to `sys.stdout`, corrupting the JSON-RPC protocol on stdio MCP servers** (#1254, closes #1134) by @dex0shubham — stdio MCP servers frame newline-delimited JSON-RPC on stdout, so an interleaved progress bar could make a response body unparseable. Progress now defaults to `sys.stderr` (resolved per-write via a property so a later rebinding, e.g. pytest capture, is honored), with an optional `stream` override; the cp1252 emoji-capability probe now inspects the actual target stream instead of always stdout. 9 new tests in `tests/utils/test_progress_stream.py` +- **`SlidingWindowChunker` accepted a zero or negative `stride`, and a failed `chunk_with_overlap()` call could leave chunker state un-restored** (#1245, closes #1244) by @HsienW — the fixed-size chunking path depends on `stride` to advance the cursor, but an explicit non-positive value passed validation; a temporary overlap override used internally by `chunk_with_overlap()` could also derive a non-positive stride, and the original overlap/custom stride weren't guaranteed to be restored if chunking raised. Non-positive stride/overlap values are now rejected before chunking, and the temporary override is restored via `try`/`finally` on both success and failure. 13 new/updated tests +- **Explorer's temporal scrubber sent duplicate snapshot requests and could apply a stale response over a newer one** (#1241, closes #1128) by @ALDRIN121 — repeated `onTimeChange` calls at the same timestamp (timeline recreation, play ticks, drag events) each fired a fresh `/api/temporal/snapshot` request with no dedup — 13+ identical-`at` requests observed at ~500ms cadence — and under variable network latency an older position's response could land after a newer one's, leaving the active-node chip visibly lagging the scrubber. New `temporalSnapshotGuards.ts` dedupes in-flight requests per scrubber position, caches and re-applies snapshots on revisit, and applies a response only while the scrubber is still on that position; state resets when the graph summary changes. 16 new unit tests +- **Distinct property spellings normalizing to the same ontology name produced duplicate property definitions, and object/data properties could collide under one IRI** (#1231) by @T1mn — follow-up to #1170/#1171. Same-kind properties normalizing to the same name are now merged, preserving their domains and ranges; a normalized name shared across an object and a data property now raises a structured `ValidationError` instead of silently colliding +- **Class inference could emit duplicate ontology classes for source types that normalize to the same name (e.g. `Person`/`person`), silently misassigning properties to the first class** (#1230) by @T1mn — follow-up to #1171. The collision is now detected and rejected with a structured `ValidationError` before duplicate classes or misassigned properties are emitted. New regression test for the `Person`/`person` case +- **`OntologyGenerator.infer_properties`'s public entry point still fell back to `owl:Thing` when relationship endpoints were given by entity ID or alias**, even though the main generation pipeline had already been fixed (#1229) by @T1mn — follow-up to #1170. The endpoint-resolution logic is now extracted into a shared `relationship_utils.py` helper used by both `PropertyGenerator` and the public inference path, so the two can't drift again +- **`auto_generate_id=False` on the six decision-model dataclasses was unreachable dead code** (#1153, fixes #1152) by @cxzg007 — `Decision`, `DecisionContext`, `Policy`, `PolicyException`, `Precedent`, and `ApprovalChain` declared `auto_generate_id` only as a plain `__post_init__` parameter rather than a dataclass field or `InitVar`, so the generated `__init__` never forwarded it — it was always `True`, and the "require a caller-supplied id" validation branch could never run. Declared as `InitVar[bool] = True` on each dataclass, restoring the intended contract with no serialization change (`InitVar` isn't a real field, so `to_dict()`/`from_dict()` are unaffected). 38 tests pass in `tests/context/test_decision_models.py`; 108 downstream tests unaffected +- **Three functions used mutable list-literal default arguments**, a classic Python pitfall where the same list object persists and can accumulate mutations across calls (#1068) by @yzxcj797 — `GraphAnalyzer.analyze_temporal_evolution(metrics=[...])`, `HierarchicalChunker.__init__(levels=[...])`, and `split_hierarchical(levels=[...])` now default to `None` with a fresh list built in-body. New regression tests in `tests/kg/test_kg.py` and `tests/split/test_chunkers.py` +- **`AgentMemory.find_by_entity()` defaulted to `limit=10`, silently truncating results** (#1024) by @yzxcj797 — the erasure workflow added in #1018 (`ErasureCoordinator`) computing what references an entity from a truncated page could leave the untruncated remainder live after a supposedly-complete erasure. Default changed to `limit=None` (all matches), with explicit limits still supported for pagination. New regression tests in `tests/context/test_agent_memory_find_by_entity.py` +- **Explorer SHACL validation error messages didn't name the environment variable that controls the limit being hit** (#1437, closes #1430) by @pkupt — the Turtle-size, triple-count, and timeout limit-exceeded messages in `validate_shacl` now name the specific env var to change, and `docs/guides/shacl-validation.md` documents all four resource-limit variables with their defaults. Existing message-assertion tests extended to also check the env var name appears. +- **Explorer's `POST /api/export` only supported `json`/`csv`, while the MCP `export_graph` tool already resolved Turtle, N-Triples, RDF/XML, JSON-LD, and GraphML through the same exporters** (#1157, closes #1131) by @13g4d0 — the Explorer route now reaches the same `semantica.export` exporters the MCP tool uses (`RDFExporter.export_to_rdf`, `GraphMLExporter.export`) rather than reimplementing anything, with an alias table shared with (and tested against) `mcp/tools/export.py`'s `_FORMAT_ALIASES`, correct media types/extensions per format, a 422 message that now names the supported formats instead of just saying the requested one isn't, and a missing optional dependency now returning 503 instead of a misleading 422. Parquet export is explicitly left out — it writes a file/path rather than a response body, and deserves its own review. Tests parse each of the seven RDF spellings with `rdflib` rather than asserting on strings, plus a canary that the Explorer and MCP alias tables agree; `tests/explorer/test_explorer_api.py`: 110 passed. +- **`semantica ingest` reported "✓ Ingested" while writing nothing to a configured Neo4j backend** (#1465, closes #1351) by @evgenyponomarev — `ingest()`/`ingest_file()` never referenced a graph store at all, so `--store`/`GRAPH_STORE_DEFAULT_BACKEND` were accepted and silently discarded; the command now raises a clear error when a non-memory graph backend is configured, naming both this and the related `kg build` no-op (#1352) rather than recommending a workaround that fails the same way. `--output .json` writes the ingested result instead (via the existing `_write_result_output` helper), and `_json_default` now expands dataclasses (`FileObject`) and decodes `bytes` so the written file holds real content, not a Python repr. 3 new regression tests; full `tests/test_cli_commands.py`: 270 passed + +### Security + +- **Five HIGH-severity Trivy findings in the built container image** (#1334) by @KaifAhmad1 — `setuptools` 70.3.0 (CVE-2025-47273, path traversal; base-image-bundled and never touched by our own build) upgraded explicitly to 78.1.1. `msgpack` 1.1.2 (GHSA-6v7p-g79w-8964, OOB read/crash on Unpacker reuse) shipped because the Dockerfile's bare `pip install ".[explorer]"` re-resolved dependencies from scratch instead of reusing the audited, hash-pinned `requirements-ci.txt` (which already pins `msgpack==1.2.1`) — the image now installs against a constraints file derived from `requirements-ci.txt` so it matches what's actually been audited. `openssl`/`libssl3t64` (CVE-2026-14456, QUIC server DoS) has no packaged fix yet in Debian's `trixie-security`; an upgrade step is added so the next rebuild picks it up automatically, documented as non-exploitable here since the image only serves plain HTTP via uvicorn and never opens a QUIC listener +- **Two npm advisories in `explorer/package-lock.json` flagged by OpenSSF Scorecard, plus over-broad workflow token permissions** (#1280) by @KaifAhmad1 — `brace-expansion` (transitive via `minimatch`) 5.0.8→5.0.9 and `nanoid` (transitive via `postcss`) 3.3.16→3.3.18 close GHSA-rgw5-rvv9-x895 and GHSA-2v37-7h3g-55p8 (both unbounded/looping-input DoS); lockfile-only, both versions already satisfy their parents' declared ranges. Also narrows `security-events: write`/`actions: read` from workflow-level to job-level scope in `codeql.yml` and `defender-for-devops.yml`, matching least-privilege token-permission guidance +- **12 Dependabot alerts against `aiohttp`** (request smuggling, websocket/parser bugs, cookie/redirect and deserialization issues, one rated High), pinned transitively via `checkov` in `.github/requirements/checkov.txt` (#1342) by @KaifAhmad1 — root cause: `checkov==3.3.1` itself constrained `aiohttp<3.14.0`, excluding every patched release. Bumping to `checkov==3.3.16` relaxes that to `aiohttp<3.15.0`, letting `aiohttp` resolve to the patched `3.14.3` and clearing all 12 alerts at once. Two related alerts are documented as left open rather than fixed here: `asteval` (checkov 3.3.16 still hard-pins `asteval==1.0.6` with no compatible range yet) and `ecdsa` (`0.19.2` is already latest; no fix exists yet for the Minerva timing-attack advisory GHSA-wj6h-64fc-37mp, which upstream has declared out of scope) — both assessed as non-exploitable here since these are checkov's own transitive dependencies used only for local static IaC analysis, with no network-signing or cloud-auth code path exercised + +### Dependencies + +- Routine version bump fixing 2 disclosed advisories with no application-facing behavior change: `browserslist` (transitive dev dependency in `explorer/`) 4.28.2→4.28.8, closing GHSA-73wf-gq98-2v4g and GHSA-c83g-rgw3-j3cx (#1382) ## [0.6.7] - 2026-08-28 diff --git a/CITATION.cff b/CITATION.cff index bd5c72f4..e8cf3506 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -7,8 +7,8 @@ authors: repository-code: "https://github.com/semantica-agi/semantica" url: "https://getsemantica.ai" license: MIT -version: 0.6.7 -date-released: 2026-08-28 +version: 0.6.8 +date-released: 2026-09-05 keywords: - knowledge-graph - context-graph diff --git a/README.md b/README.md index 0bd57d19..c6f59dd8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ### Graph-Native Infrastructure for Context and Accountable AI Systems -#### *The Open Source Palantir for AI Agents* +#### *Developer-first, knowledge infrastructure for AI, alternative to expensive enterprise platforms.* > Ingest your enterprise data, extract what matters, build a Context Graph and knowledge graph (KG), and run graph analytics and causal reasoning over all of it, with full decision provenance baked in. Explainable, traceable, and trustworthy by design. @@ -1479,18 +1479,18 @@ updates the supplied runtime object; it does not add disk persistence. --- -## What's New in v0.6.7 +## What's New in v0.6.8 -**Feature release**, plus one SSRF hardening fix and a large batch of correctness fixes across the RDF/ontology export pipeline: +**Every release from here on is cryptographically signed** — the build now runs SLSA build-provenance attestation plus Sigstore signing, and `.sigstore.json` bundles ship alongside the wheel/sdist on every GitHub Release, closing the OpenSSF Scorecard Signed-Releases gap. Beyond that, this is a large fix-and-hardening release plus a batch of vector-store and LLM-provider additions: -- **First-class LangChain integration** (`semantica[langchain]`): a `BaseRetriever` and `VectorStore` over `HybridSearch`, plus graph/decision-query tools -- **SAP OData ingestor** (`semantica[ingest-sap]`): OAuth2/Basic-auth, SSRF-guarded ingestion for Business Partners and Sales Orders, following the existing Snowflake/Databricks connector pattern -- **`ContextGraph` gains deterministic, human-editable Markdown round-trip persistence** alongside the existing JSON API, and Explorer can validate and apply Markdown edits to individual graph nodes and AgentMemory items supplied by the hosting application -- **`reasoning` gains a structured Action layer**: rule-driven `Assert`/`Retract`/`Call`/`EmitEvent` actions with optional provenance, turning the reasoner into a production-rule system -- **`run_shacl_validation` is now a public, documented API**, and a dozen ontology/RDF export correctness fixes land: OWL property/class export, SHACL target-namespace resolution, one canonical confidence datatype across all four RDF formats, reachable OWL-Time reification, JSON-LD default-graph and content-derived document identity, and full metadata passthrough on every RDF serializer -- **Security**: Agno's `AgnoKnowledgeGraph.load_urls()` and OpenClaw's MCP tool now route outbound requests through the shared SSRF guard +- **Vector store gains real enumeration**: `scan_vectors()`/`iter_vectors()` land across FAISS, SQLiteVec, PgVector, Qdrant, Weaviate, and Milvus (each via the pagination primitive its API actually supports), making `semantica store migrate` functional between backends for the first time; Weaviate also gains `delete_vectors()` for `ErasureCoordinator` support +- **`semantica.llms` gains first-class `Anthropic`, `Gemini`, `Ollama`, `DeepSeek`, and `Novita` provider wrappers**, matching the existing `Groq`/`OpenAI` pattern +- **Ontology package gains a deterministic, CI-friendly quality gate** for ontologies and knowledge graphs, plus first-class Google ADK integration and a Salesforce ingestor +- **Explorer's read-only Markdown viewer becomes a full editor** for live `ContextGraph` nodes and host-supplied `AgentMemory` items +- **`ErasureCoordinator`** completes the erasure workflow `purge_node()` only started, so a purged entity no longer survives verbatim in `AgentMemory` or as an embedding +- **Security**: 12 Dependabot `aiohttp` alerts, 5 HIGH-severity Trivy container findings, and 2 npm advisories all resolved -Also fixes: `PipelineBuilder.set_parallelism()` now actually parallelizes independent pipeline steps, `flatten_dict()` no longer silently drops data on a key collision, `Config.get()` honors boolean environment overrides, and the MCP server's `export_graph` tool works again on every format. +Also fixes 35 correctness bugs (Python 3.9 install breakage, FAISS save/load metadata loss, `semantica ingest`'s silent no-op against a configured graph store, MCP persistence, Explorer graph rendering, ontology property-collision handling, and more) and a large batch of documentation corrections across the site. → [Full release notes](RELEASE_NOTES.md) · [Changelog](CHANGELOG.md) @@ -1543,7 +1543,7 @@ pip install semantica[watch] # Directory file watcher pip install semantica[explorer] # Knowledge Explorer dashboard ``` -For production deployments, use Docker or Kubernetes rather than a local `pip install`. Set `SEMANTICA_SECRET_KEY`, configure a persistent LPG graph store (Neo4j / FalkorDB / Apache AGE / AWS Neptune) and/or RDF triple store (Blazegraph / Apache Jena / Eclipse RDF4J), and point the vector store at a hosted backend (Qdrant / Pinecone). See [ARCHITECTURE.md](ARCHITECTURE.md) for the full deployment topology. +For production deployments, use Docker or Kubernetes rather than a local `pip install`. Set `SEMANTICA_API_KEY`, configure a persistent LPG graph store (Neo4j / FalkorDB / Apache AGE / AWS Neptune) and/or RDF triple store (Blazegraph / Apache Jena / Eclipse RDF4J), and point the vector store at a hosted backend (Qdrant / Pinecone). See [ARCHITECTURE.md](ARCHITECTURE.md) for the full deployment topology. ```bash # From source diff --git a/docs/architecture.md b/docs/architecture.md index cfe08134..e0f70fc5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -149,25 +149,25 @@ registry.register_plugin("my_plugin", MyPlugin, version="1.0.0") -Every component works standalone. `NERExtractor` runs without a graph store. `VectorStore` runs without decision tracking. The framework never forces a full stack instantiation: you pay only for what you import. +Every component works standalone. `NERExtractor` runs without a graph store. `VectorStore` runs without decision tracking. The framework never forces a full stack instantiation; you pay only for what you import. -Custom ingestors, extractors, validators, and exporters follow the same base class pattern. Register them via `PluginRegistry` and they participate in the full pipeline: provenance tracking, retry policies, and parallel execution included: with no changes to core code. +Custom ingestors, extractors, validators, and exporters follow the same base class pattern. Register them via `PluginRegistry` and they participate in the full pipeline (provenance tracking, retry policies, and parallel execution included) with no changes to core code. -Lineage tracking is built into graph construction at the lowest level. Every node and edge carries a `source_id` pointing back to the originating document, extraction method, and timestamp. There's no opt-in required: provenance is always on. +Lineage tracking is built into graph construction at the lowest level. Every node and edge carries a `source_id` pointing back to the originating document, extraction method, and timestamp. There is no opt-in required; provenance is always on. -Centralized `ConfigManager` with environment variable overrides. No magic defaults: all behavior is explicit and overridable. Suitable for multi-environment deployments where dev, staging, and production need different backends. +Centralized `ConfigManager` with environment variable overrides. No magic defaults; all behavior is explicit and overridable. Suitable for multi-environment deployments where dev, staging, and production need different backends. @@ -179,13 +179,13 @@ Centralized `ConfigManager` with environment variable overrides. No magic defaul | Characteristic | Mechanism | | :-------------- | :--------- | | **Parallel execution** | `Pipeline(workers=N)` with configurable workers per stage | -| **Delta processing** | Incremental graph updates: no full recompute on new data | +| **Delta processing** | Incremental graph updates (no full recompute on new data) | | **Streaming ingestion** | Process large corpora without loading everything into memory | | **Backend flexibility** | Swap in-memory NetworkX for Neo4j / FalkorDB with no API changes | | **Deduplication v2** | `blocking_v2`, `hybrid_v2`, `semantic_v2`: up to 7x faster than v1 | | **Indexed search** | Explorer search at 0.004ms on 118k nodes (v0.5.0) | -- [Modules](/modules) — Full module documentation with code examples. -- [Learning More](/learning-more) — Configuration reference, performance guide, and troubleshooting. -- [Pipeline Reference](/reference/pipeline) — Pipeline orchestration, workers, and retry policies. -- [Core Reference](/reference/core) — Framework lifecycle, plugin registry, and configuration. +- [Modules](/modules): full module documentation with code examples. +- [Learning More](/learning-more): configuration reference, performance guide, and troubleshooting. +- [Pipeline Reference](/reference/pipeline): pipeline orchestration, workers, and retry policies. +- [Core Reference](/reference/core): framework lifecycle, plugin registry, and configuration. diff --git a/docs/choose-your-module.md b/docs/choose-your-module.md index 7af66e35..a903135f 100644 --- a/docs/choose-your-module.md +++ b/docs/choose-your-module.md @@ -5,7 +5,7 @@ icon: "compass" --- - Every module works independently — import only what you need. This page maps developer goals to starting points. The [Module Reference](/modules) covers every module in depth. + Every module works independently: import only what you need. This page maps developer goals to starting points. The [Module Reference](/modules) covers every module in depth. ## Quick Reference @@ -75,7 +75,7 @@ Pick your goal to see the minimum imports and a working skeleton. sources = FileIngestor().ingest("report.pdf") parsed = DocumentParser().parse_document("report.pdf") - # No API key required — pattern-based extraction + # No API key required: pattern-based extraction entities = NERExtractor(method="pattern").extract(parsed) relationships = RelationExtractor(method="rule").extract(parsed, entities=entities) @@ -89,7 +89,7 @@ Pick your goal to see the minimum imports and a working skeleton. Pass `method="pattern"` to `NERExtractor` for zero-cost, zero-API-key extraction. Switch to `method="llm"` with any of the supported providers for higher recall. - **Next:** [Quickstart →](/quickstart) — full pipeline with visualization and export. + See the [Quickstart →](/quickstart) for a full pipeline with visualization and export. @@ -109,7 +109,7 @@ Pick your goal to see the minimum imports and a working skeleton. knowledge_graph=ContextGraph(advanced_analytics=True), ) - # Store facts — retrieval uses both vectors and graph structure + # Store facts: retrieval uses both vectors and graph structure context.store("Apple Inc. was co-founded by Steve Jobs in 1976 in Cupertino.") # GraphRAG query with multi-hop reasoning trace @@ -206,11 +206,11 @@ Pick your goal to see the minimum imports and a working skeleton. ```python from semantica.export import RDFExporter, ParquetExporter, LPGExporter, ArangoAQLExporter - # RDF — multiple serialization formats + # RDF: multiple serialization formats RDFExporter().export(graph, "graph.ttl", format="turtle") RDFExporter().export(graph, "graph.jsonld", format="jsonld") - # Parquet — for Spark, BigQuery, Databricks, Snowflake + # Parquet: for Spark, BigQuery, Databricks, Snowflake ParquetExporter().export(graph, "output/graph.parquet") # Neo4j / Memgraph via Cypher @@ -225,15 +225,15 @@ Pick your goal to see the minimum imports and a working skeleton. **Next:** [Export module reference →](/reference/export) - - Use Semantica from Claude Desktop, Cursor, VS Code, or any MCP-aware tool — no Python code required after setup. 15 tools available instantly. + + Use Semantica from Claude Desktop, Cursor, VS Code, or any MCP-aware tool; no Python code required after setup. 15 tools are available. - **Step 1 — Install:** + **Step 1: Install** ```bash pip install semantica ``` - **Step 2 — Add to your MCP client config:** + **Step 2: Add to your MCP client config** @@ -273,10 +273,10 @@ Pick your goal to see the minimum imports and a working skeleton. -## Still Unsure? +## Architecture Selection Guidance - + Use a **knowledge graph** (`kg`) when you need structured reasoning, multi-hop traversal, provenance, or compliance audit trails. Use a **vector store** (`vector_store`) when you need fast fuzzy similarity search over large text corpora and relationships between items don't matter. @@ -286,12 +286,12 @@ Pick your goal to see the minimum imports and a working skeleton. See also: [Core Concepts](/concepts) - + Start with the [Quickstart](/quickstart). It builds a complete pipeline (ingest → parse → extract → graph → visualize → export) with no API key required. - - Add `AgentContext`. It wraps your existing agent with memory, decision tracking, and precedent search — no changes to your LLM provider or agent framework needed. + + Add `AgentContext` to equip an existing agent with memory, decision tracking, and precedent search, with no changes to your LLM provider or agent framework required. ```python from semantica.context import AgentContext, ContextGraph @@ -307,7 +307,7 @@ Pick your goal to see the minimum imports and a working skeleton. [Context module reference →](/reference/context) - + | Layer | Module | Key class | | :---- | :------ | :--------- | | Ingestion | `ingest` | `FileIngestor` | @@ -322,6 +322,6 @@ Pick your goal to see the minimum imports and a working skeleton. --- -- [Quickstart](/quickstart) — Full pipeline in 5 minutes. -- [Module Reference](/modules) — Every module with examples and common chains. -- [API Reference](/reference/context) — Complete class and method documentation. +- [Quickstart](/quickstart): full pipeline in 5 minutes. +- [Module Reference](/modules): every module with examples and common chains. +- [API Reference](/reference/context): complete class and method documentation. diff --git a/docs/citation.md b/docs/citation.md index d3bb00b9..87b48c8f 100644 --- a/docs/citation.md +++ b/docs/citation.md @@ -43,10 +43,10 @@ icon: "quote-left" ## Share Your Research -Published research using Semantica? [Let us know](https://github.com/semantica-agi/semantica/issues): we may feature your work. +If you publish research using Semantica, [let us know](https://github.com/semantica-agi/semantica/issues) so we can feature your work. ## See Also -- [License](/project-license) — MIT License details. -- [Community](/community) — Connect with the Semantica community. +- [License](/project-license): MIT License details. +- [Community](/community): connect with the Semantica community. diff --git a/docs/cli-setup.md b/docs/cli-setup.md index 9a973a4d..c401eeec 100644 --- a/docs/cli-setup.md +++ b/docs/cli-setup.md @@ -18,7 +18,7 @@ After installation the following commands are available: | Command | Entry point | What it does | | :------- | :----------- | :------------ | | `semantica` | `semantica.cli:main` | General-purpose CLI for pipeline runs, extraction, and graph operations | -| `semantica-server` | `semantica.server:main` | FastAPI/uvicorn REST API server bound to `0.0.0.0:8000` | +| `semantica-server` | `semantica.server:main` | FastAPI/uvicorn REST API server bound to `127.0.0.1:8000` by default (set `SEMANTICA_HOST` to override) | | `semantica-worker` | `semantica.worker:main` | Background worker process entry point for Semantica deployments | | `semantica-explorer` | `semantica.explorer:main` | Interactive browser dashboard for knowledge graph exploration | | `semantica-mcp` | `semantica.mcp_server:main` | MCP server (stdio) for Claude Desktop, Cursor, Windsurf, and other MCP clients | @@ -49,11 +49,11 @@ python -c "import semantica; print(semantica.__version__)" ## When to Use Each Command -- **semantica** — The general-purpose CLI. Use it for one-off pipeline runs, entity extraction, and graph operations from a shell script or CI job. -- **semantica-server** — Starts the REST API server. Binds to `0.0.0.0:8000`. Use this when another service or application needs programmatic access to Semantica over HTTP. -- **semantica-worker** — Background task processor. Run alongside `semantica-server` when you need async pipeline execution outside the request cycle. Start the server first, then start one or more workers pointing at the same backend. -- **semantica-explorer** — Launches the browser dashboard. Requires `pip install semantica[explorer]`. Use this to explore a saved knowledge graph interactively. See [Explorer Setup](/explorer-setup). -- **semantica-mcp** — Runs the MCP server over stdio. Configure it in your MCP client's settings file to expose all 15 tools and 3 resources to Claude Desktop, Cursor, Windsurf, or any MCP-aware client. See [MCP Server](/reference/mcp_server). +- **semantica**: general-purpose CLI. Use it for one-off pipeline runs, entity extraction, and graph operations from a shell script or CI job. +- **semantica-server**: starts the REST API server. Binds to `127.0.0.1:8000` by default; set `SEMANTICA_HOST` to expose beyond localhost. Use this when another service or application needs programmatic access to Semantica over HTTP. +- **semantica-worker**: background task processor. Run alongside `semantica-server` when you need async pipeline execution outside the request cycle. Start the server first, then start one or more workers pointing at the same backend. +- **semantica-explorer**: launches the browser dashboard. Requires `pip install semantica[explorer]`. Use this to explore a saved knowledge graph interactively. See [Explorer Setup](/explorer-setup). +- **semantica-mcp**: runs the MCP server over stdio. Configure it in your MCP client's settings file to expose all 15 tools and 3 resources to Claude Desktop, Cursor, Windsurf, or any MCP-aware client. See [MCP Server](/reference/mcp_server). ## Usage Examples @@ -61,7 +61,7 @@ python -c "import semantica; print(semantica.__version__)" ```bash - # Starts FastAPI + uvicorn on 0.0.0.0:8000 + # Starts FastAPI + uvicorn on 127.0.0.1:8000 (set SEMANTICA_HOST to change) semantica-server ``` @@ -228,7 +228,7 @@ Install the [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/ ## Next Steps -- [Explorer Setup](/explorer-setup) — Build a graph, save it, and launch the browser dashboard. -- [MCP Server](/reference/mcp_server) — All 15 tools and 3 resources exposed over the MCP protocol. -- [Installation](/installation) — Virtual environments, optional extras, and platform-specific notes. -- [Quickstart](/quickstart) — End-to-end pipeline walkthrough with working code. +- [Explorer Setup](/explorer-setup): build a graph, save it, and launch the browser dashboard. +- [MCP Server](/reference/mcp_server): all 15 tools and 3 resources exposed over the MCP protocol. +- [Installation](/installation): virtual environments, optional extras, and platform-specific notes. +- [Quickstart](/quickstart): end-to-end pipeline walkthrough with working code. diff --git a/docs/community.md b/docs/community.md index 8c7d4e28..6d25fdf7 100644 --- a/docs/community.md +++ b/docs/community.md @@ -9,10 +9,10 @@ Semantica is built in the open, with contributions from researchers, engineers, ## Get Help -- [GitHub Issues](https://github.com/semantica-agi/semantica/issues) — File bug reports and feature requests with full context. -- [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions) — Ask questions, share ideas, and discuss design decisions. -- [Pull Requests](https://github.com/semantica-agi/semantica/pulls) — Browse open contributions and submit your own. -- [Security Issues](https://github.com/semantica-agi/semantica/security/advisories/new) — Report vulnerabilities privately: never in public issues. +- [GitHub Issues](https://github.com/semantica-agi/semantica/issues): file bug reports and feature requests with full context. +- [GitHub Discussions](https://github.com/semantica-agi/semantica/discussions): ask questions, share ideas, and discuss design decisions. +- [Pull Requests](https://github.com/semantica-agi/semantica/pulls): browse open contributions and submit your own. +- [Security Issues](https://github.com/semantica-agi/semantica/security/advisories/new): report vulnerabilities privately (never in public issues). ## Community Guidelines @@ -68,7 +68,7 @@ See the [Contributing Guide](/contributing-guide) for the full development workf ## See Also -- [Contributing Guide](/contributing-guide) — Step-by-step guide for submitting PRs and setting up your dev environment. -- [Community Projects](/community-projects) — Projects and integrations built by the community. -- [FAQ](/faq) — Common questions answered. -- [Governance](/governance) — How the project is run and decisions are made. +- [Contributing Guide](/contributing-guide): step-by-step guide for submitting PRs and setting up your dev environment. +- [Community Projects](/community-projects): projects and integrations built by the community. +- [FAQ](/faq): common questions answered. +- [Governance](/governance): how the project is run and decisions are made. diff --git a/docs/cookbook.md b/docs/cookbook.md index f073efe4..14a66e1f 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -18,43 +18,43 @@ icon: "flask" ## Featured Recipe -- **[Your First Knowledge Graph](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/08_Your_First_Knowledge_Graph.ipynb)** — Go from raw text to a queryable knowledge graph in 20 minutes. Topics: Extraction, Graph Construction, Visualization · *Beginner* +- **[Your First Knowledge Graph](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/08_Your_First_Knowledge_Graph.ipynb)**: go from raw text to a queryable knowledge graph in 20 minutes. Topics: Extraction, Graph Construction, Visualization · *Beginner* ## Core Tutorials Essential guides to master the Semantica framework. -- **[Welcome to Semantica](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/01_Welcome_to_Semantica.ipynb)** — Interactive introduction to the framework's core philosophy and all modules. Topics: Framework Overview, Architecture · *Beginner* -- **[Data Ingestion](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/02_Data_Ingestion.ipynb)** — Loading data from files, web, databases, streams, feeds, repositories, email, and MCP. Topics: FileIngestor, WebIngestor, DBIngestor · *Beginner* -- **[Document Parsing](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/03_Document_Parsing.ipynb)** — Extracting clean text from complex formats like PDF, DOCX, and HTML. Topics: OCR, PDF Parsing, Text Extraction · *Beginner* -- **[Data Normalization](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/04_Data_Normalization.ipynb)** — Pipelines for cleaning, normalizing, and preparing text. Topics: Text Cleaning, Unicode, Formatting · *Beginner* -- **[Entity Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/05_Entity_Extraction.ipynb)** — Using NER to identify people, organizations, and custom entities. Topics: NER, spaCy, LLM Extraction · *Beginner* -- **[Relation Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/06_Relation_Extraction.ipynb)** — Discovering and classifying relationships between entities. Topics: Relation Classification, Dependency Parsing · *Beginner* -- **[Embedding Generation](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/12_Embedding_Generation.ipynb)** — Creating and managing vector embeddings for semantic search. Topics: Embeddings, OpenAI, HuggingFace · *Intermediate* -- **[Vector Store](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/13_Vector_Store.ipynb)** — Setting up vector stores for similarity search and retrieval. *Intermediate* -- **[Graph Store](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/09_Graph_Store.ipynb)** — Persisting knowledge graphs in Neo4j or FalkorDB. Topics: Neo4j, Cypher, Persistence · *Intermediate* -- **[Ontology](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/14_Ontology.ipynb)** — Defining domain schemas and ontologies to structure your data. Topics: OWL, RDF, Schema Design · *Intermediate* -- **[Seed Data](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/25_Seed_Data.ipynb)** — Bootstrapping a knowledge graph from trusted CSV, JSON, database, and API sources before extraction runs. Topics: SeedDataManager, Foundation Graphs · *Intermediate* -- **[Semantic Layer Basics](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/26_Semantic_Layer_Basics.ipynb)** — Capstone tutorial that combines a knowledge graph, generated ontology, explicit mappings, ontology-aligned RDF, and a SPARQL query. Topics: Semantic Layer, Ontology Mapping, Oxigraph, SPARQL · *Intermediate* +- **[Welcome to Semantica](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/01_Welcome_to_Semantica.ipynb)**: interactive introduction to the framework's core philosophy and all modules. Topics: Framework Overview, Architecture · *Beginner* +- **[Data Ingestion](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/02_Data_Ingestion.ipynb)**: loading data from files, web, databases, streams, feeds, repositories, email, and MCP. Topics: FileIngestor, WebIngestor, DBIngestor · *Beginner* +- **[Document Parsing](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/03_Document_Parsing.ipynb)**: extracting clean text from complex formats like PDF, DOCX, and HTML. Topics: OCR, PDF Parsing, Text Extraction · *Beginner* +- **[Data Normalization](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/04_Data_Normalization.ipynb)**: pipelines for cleaning, normalizing, and preparing text. Topics: Text Cleaning, Unicode, Formatting · *Beginner* +- **[Entity Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/05_Entity_Extraction.ipynb)**: using NER to identify people, organizations, and custom entities. Topics: NER, spaCy, LLM Extraction · *Beginner* +- **[Relation Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/06_Relation_Extraction.ipynb)**: discovering and classifying relationships between entities. Topics: Relation Classification, Dependency Parsing · *Beginner* +- **[Embedding Generation](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/12_Embedding_Generation.ipynb)**: creating and managing vector embeddings for semantic search. Topics: Embeddings, OpenAI, HuggingFace · *Intermediate* +- **[Vector Store](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/13_Vector_Store.ipynb)**: setting up vector stores for similarity search and retrieval. *Intermediate* +- **[Graph Store](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/09_Graph_Store.ipynb)**: persisting knowledge graphs in Neo4j or FalkorDB. Topics: Neo4j, Cypher, Persistence · *Intermediate* +- **[Ontology](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/14_Ontology.ipynb)**: defining domain schemas and ontologies to structure your data. Topics: OWL, RDF, Schema Design · *Intermediate* +- **[Seed Data](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/25_Seed_Data.ipynb)**: bootstrapping a knowledge graph from trusted CSV, JSON, database, and API sources before extraction runs. Topics: SeedDataManager, Foundation Graphs · *Intermediate* +- **[Semantic Layer Basics](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/26_Semantic_Layer_Basics.ipynb)**: capstone tutorial that combines a knowledge graph, generated ontology, explicit mappings, ontology-aligned RDF, and a SPARQL query. Topics: Semantic Layer, Ontology Mapping, Oxigraph, SPARQL · *Intermediate* ## Advanced Concepts Deep dive into advanced features, customization, and complex workflows. -- **[Advanced Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/01_Advanced_Extraction.ipynb)** — Custom extractors, LLM-based extraction, and complex pattern matching. Topics: Custom Models, Regex, LLMs · *Advanced* -- **[Advanced Graph Analytics](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/02_Advanced_Graph_Analytics.ipynb)** — Centrality, community detection, and pathfinding algorithms. Topics: PageRank, Louvain, Shortest Path · *Advanced* -- **[Advanced Context Engineering](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/11_Advanced_Context_Engineering.ipynb)** — Production-grade memory system for AI agents using FAISS and Neo4j. Topics: Agent Memory, GraphRAG, Entity Injection · *Advanced* -- **[Complete Visualization Suite](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/03_Complete_Visualization_Suite.ipynb)** — Interactive, publication-ready visualizations of your graphs. Topics: PyVis, NetworkX, D3.js · *Intermediate* -- **[Conflict Resolution](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/17_Conflict_Detection_and_Resolution.ipynb)** — Strategies for handling contradictory information from multiple sources. Topics: Truth Discovery, Voting, Confidence · *Advanced* -- **[Multi-Format Export](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/05_Multi_Format_Export.ipynb)** — Exporting to RDF, OWL, JSON-LD, and NetworkX formats. Topics: Serialization, Interoperability · *Intermediate* -- **[Multi-Source Integration](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/06_Multi_Source_Data_Integration.ipynb)** — Merging data from disparate sources into a unified graph. Topics: Entity Resolution, Merging, Fusion · *Advanced* -- **[Reasoning and Inference](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/08_Reasoning_and_Inference.ipynb)** — Using logical reasoning to infer new knowledge from existing facts. Topics: Logic Rules, Inference Engines · *Advanced* -- **[Temporal Knowledge Graphs](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/10_Temporal_Knowledge_Graphs.ipynb)** — Modeling and querying data that changes over time. Topics: Time Series, Temporal Logic, Allen Algebra · *Advanced* -- **[Provenance Tracking](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/22_Provenance_Tracking.ipynb)** — Audit-grade, W3C PROV-O-aligned tracking of where every entity, relationship, and chunk came from. Topics: PROV-O, Lineage, Checksums, Invalidation · *Advanced* -- **[Reasoning Module](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/23_Reasoning.ipynb)** — Deriving new knowledge from existing facts with forward chaining, backward chaining, and Datalog strategies. Topics: Reasoner, Datalog, Explanations · *Advanced* -- **[Change Management](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/24_Change_Management.ipynb)** — Versioning, audit trails, and data-integrity checks for knowledge graphs and ontologies. Topics: ChangeLogEntry, Version Storage, Data Integrity · *Advanced* +- **[Advanced Extraction](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/01_Advanced_Extraction.ipynb)**: custom extractors, LLM-based extraction, and complex pattern matching. Topics: Custom Models, Regex, LLMs · *Advanced* +- **[Advanced Graph Analytics](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/02_Advanced_Graph_Analytics.ipynb)**: centrality, community detection, and pathfinding algorithms. Topics: PageRank, Louvain, Shortest Path · *Advanced* +- **[Advanced Context Engineering](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/11_Advanced_Context_Engineering.ipynb)**: persistent memory system for AI agents using FAISS and Neo4j. Topics: Agent Memory, GraphRAG, Entity Injection · *Advanced* +- **[Complete Visualization Suite](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/03_Complete_Visualization_Suite.ipynb)**: interactive network, analytics, and temporal visualizations for graphs. Topics: PyVis, NetworkX, D3.js · *Intermediate* +- **[Conflict Resolution](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/17_Conflict_Detection_and_Resolution.ipynb)**: strategies for handling contradictory information from multiple sources. Topics: Truth Discovery, Voting, Confidence · *Advanced* +- **[Multi-Format Export](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/05_Multi_Format_Export.ipynb)**: exporting to RDF, OWL, JSON-LD, and NetworkX formats. Topics: Serialization, Interoperability · *Intermediate* +- **[Multi-Source Integration](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/06_Multi_Source_Data_Integration.ipynb)**: merging data from disparate sources into a unified graph. Topics: Entity Resolution, Merging, Fusion · *Advanced* +- **[Reasoning and Inference](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/08_Reasoning_and_Inference.ipynb)**: using logical reasoning to infer new knowledge from existing facts. Topics: Logic Rules, Inference Engines · *Advanced* +- **[Temporal Knowledge Graphs](https://github.com/semantica-agi/semantica/blob/main/cookbook/advanced/10_Temporal_Knowledge_Graphs.ipynb)**: modeling and querying data that changes over time. Topics: Time Series, Temporal Logic, Allen Algebra · *Advanced* +- **[Provenance Tracking](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/22_Provenance_Tracking.ipynb)**: W3C PROV-O-aligned lineage tracking and checksum verification for entities, relationships, and chunks. Topics: PROV-O, Lineage, Checksums, Invalidation · *Advanced* +- **[Reasoning Module](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/23_Reasoning.ipynb)**: deriving new knowledge from existing facts with forward chaining, backward chaining, and Datalog strategies. Topics: Reasoner, Datalog, Explanations · *Advanced* +- **[Change Management](https://github.com/semantica-agi/semantica/blob/main/cookbook/introduction/24_Change_Management.ipynb)**: versioning, audit trails, and data-integrity checks for knowledge graphs and ontologies. Topics: ChangeLogEntry, Version Storage, Data Integrity · *Advanced* ## How to Run diff --git a/docs/explorer-setup.md b/docs/explorer-setup.md index 96221460..b21e2a3f 100644 --- a/docs/explorer-setup.md +++ b/docs/explorer-setup.md @@ -109,7 +109,7 @@ Explorer loads a graph from a JSON file on disk. You need to create that file fi - Already have a graph from a pipeline run? Skip straight to Step 2. The only requirement is that the file was saved with `ContextGraph.save_to_file()`. + Pipelines that already produced a saved graph can skip straight to Step 2, provided the file was saved with `ContextGraph.save_to_file()`. @@ -264,7 +264,7 @@ Once running, Explorer exposes a REST API and dashboard for: The full endpoint catalogue is documented in the Swagger UI at `/docs` and in the reference page below. -- [Explorer Reference](/reference/explorer) — Every REST endpoint, WebSocket events, analytics, and all supported flags. -- [CLI Setup](/cli-setup) — All five Semantica executables and when to use each one. -- [Context Module](/reference/context) — Full documentation for ContextGraph: build, query, save, and load. -- [Quickstart](/quickstart) — End-to-end pipeline: ingest → extract → build graph → export. +- [Explorer Reference](/reference/explorer): every REST endpoint, WebSocket events, analytics, and all supported flags. +- [CLI Setup](/cli-setup): all five Semantica executables and when to use each one. +- [Context Module](/reference/context): full documentation for ContextGraph (build, query, save, and load). +- [Quickstart](/quickstart): end-to-end pipeline (ingest → extract → build graph → export). diff --git a/docs/faq.md b/docs/faq.md index d3537518..c98fdf1c 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -17,7 +17,7 @@ icon: "circle-question" | API key required? | Optional: pattern extraction works with no keys | | Works with LangChain / LlamaIndex? | Yes: Semantica is a layer on top, not a replacement | | Production-ready? | Yes: 1,000+ tests, security fixes shipped in every release (see [CHANGELOG](https://github.com/semantica-agi/semantica/blob/main/CHANGELOG.md)) | -| Latest version? | **v0.6.7** (August 2026) | +| Latest version? | **v0.6.8** (September 2026) | | Local LLMs? | Yes: Ollama via LiteLLM, HuggingFaceLLM for air-gapped | @@ -27,7 +27,7 @@ icon: "circle-question" -Semantica is an open-source framework for building context graphs and decision intelligence layers for AI. It transforms unstructured data: documents, APIs, databases: into structured knowledge graphs with full provenance tracking, making AI systems explainable and auditable. +Semantica is an open-source framework for building context graphs and decision intelligence layers for AI. It transforms unstructured data (documents, APIs, databases) into structured knowledge graphs with full provenance tracking, making AI systems explainable and auditable. It's not a replacement for LangChain or LlamaIndex. It's the **accountability layer** that goes on top: recording decisions, tracing facts to sources, and making reasoning transparent. @@ -46,7 +46,7 @@ It's not a replacement for LangChain or LlamaIndex. It's the **accountability la -Most frameworks stop at retrieval or generation. Semantica adds an **accountability layer**: every decision is recorded, every fact links to a source, and every reasoning step is explainable. It's designed for environments where you need to audit *why* an AI reached a conclusion: not just what it said. +Most frameworks stop at retrieval or generation. Semantica adds an **accountability layer**: every decision is recorded, every fact links to a source, and every reasoning step is explainable. It's designed for environments where you need to audit *why* an AI reached a conclusion, not just what it said. Semantica works alongside these frameworks, not against them. @@ -54,11 +54,11 @@ Semantica works alongside these frameworks, not against them. -No. This is **system-level explainability, not foundation-model explainability**. Semantica does not expose, reconstruct, or explain what happens *inside* the LLM/foundation model — its internal reasoning or chain-of-thought stays opaque, as it does for any external system. +No. This is **system-level explainability, not foundation-model explainability**. Semantica does not expose, reconstruct, or explain what happens *inside* the LLM/foundation model. Its internal reasoning or chain-of-thought stays opaque, as it does for any external system. What Semantica explains is *outside* the model: what context and data were used, what decision was produced, the provenance behind it, the relevant relationships, the policies applied, and the resulting decision trail. -In short: Semantica explains and audits *what the AI system did* — not the foundation model's private internal reasoning. +In short, Semantica explains and audits *what the AI system did*, not the foundation model's private internal reasoning. @@ -70,9 +70,9 @@ Yes: MIT licensed, no vendor lock-in, no paywalled features. Some capabilities r -**v0.6.7**: released August 2026. +**v0.6.8**: released September 2026. -Highlights: first-class LangChain integration, SAP OData ingestor, human-editable Markdown round-trip persistence for `ContextGraph`, a structured Action layer for the reasoning engine, and a public `run_shacl_validation` entry point. The 0.6.x line also added first-class CrewAI support and the Semantica RDF vocabulary with deterministic IRIs. See the [CHANGELOG](https://github.com/semantica-agi/semantica/blob/main/CHANGELOG.md) for the full history. +Highlights: every release is now cryptographically signed (SLSA build provenance + Sigstore, closing the OpenSSF Scorecard Signed-Releases gap), real vector-store enumeration (`scan_vectors()`/`iter_vectors()`) across FAISS/SQLiteVec/PgVector/Qdrant/Weaviate/Milvus making `store migrate` functional, first-class Anthropic/Gemini/Ollama/DeepSeek/Novita LLM provider wrappers, a CI-friendly ontology quality gate, and 35 correctness fixes. The 0.6.x line also added first-class LangChain and CrewAI support and the Semantica RDF vocabulary with deterministic IRIs. See the [CHANGELOG](https://github.com/semantica-agi/semantica/blob/main/CHANGELOG.md) for the full history. ```bash pip install --upgrade semantica @@ -348,6 +348,6 @@ set PYTHONIOENCODING=utf-8 ## Support -- [Discord](https://discord.gg/sV34vps5hH) — Community chat and live support. -- [GitHub Issues](https://github.com/semantica-agi/semantica/issues) — Bug reports and feature requests. -- [Contributing](/contributing-guide) — Help improve Semantica. +- [Discord](https://discord.gg/sV34vps5hH): community chat and live support. +- [GitHub Issues](https://github.com/semantica-agi/semantica/issues): bug reports and feature requests. +- [Contributing](/contributing-guide): help improve Semantica. diff --git a/docs/getting-started.md b/docs/getting-started.md index 11e9295e..fd00501e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -42,7 +42,7 @@ icon: "rocket" Verify installation: ```python import semantica - print(semantica.__version__) # 0.6.7 + print(semantica.__version__) # 0.6.8 ``` diff --git a/docs/glossary.md b/docs/glossary.md index 8ef5454e..00e84627 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -23,16 +23,16 @@ A persistent, queryable graph of everything an agent knows, decides, and reasons A first-class object in Semantica: a recorded agent choice with category, scenario, reasoning, outcome, confidence score, causal chain, and source provenance. Stored and searchable via `context.record_decision()`. **Entity** -A distinct object or concept in the real world: a person, organization, location, event, or abstract concept. Entities are nodes in a knowledge graph, each with typed properties and a source provenance record. +A distinct object or concept in the real world (person, organization, location, event, or abstract concept). Entities are nodes in a knowledge graph, each with typed properties and a source provenance record. **Knowledge Graph (KG)** -A structured representation of knowledge using entities (nodes) and relationships (edges). Knowledge graphs enable reasoning, querying, semantic search, and traceable inference: unlike flat vector stores. +A structured representation of knowledge using entities (nodes) and relationships (edges). Knowledge graphs enable reasoning, querying, semantic search, and traceable inference, unlike flat vector stores. **Relationship** -A directed, typed connection between two entities: e.g., `works_for`, `located_in`, `founded_by`. Relationships carry confidence scores and provenance back to the source document. +A directed, typed connection between two entities (e.g., `works_for`, `located_in`, `founded_by`). Relationships carry confidence scores and provenance back to the source document. **Semantic** -Relating to meaning in language or logic. Semantic understanding captures context and intent: going beyond keyword matching to understand what text *means*. +Relating to meaning in language or logic. Semantic understanding captures context and intent, going beyond keyword matching to understand what text *means*. ## Data Processing @@ -41,19 +41,19 @@ Relating to meaning in language or logic. Semantic understanding captures contex Splitting large documents into smaller pieces while preserving semantic context. Semantica supports recursive, semantic boundary, entity-aware, relation-aware, sliding window, structural, and table-aware chunking strategies. **Ingestion** -Loading data from external sources: files, databases, APIs, streams: into the pipeline as a unified `SourceDocument`. The first stage in every Semantica pipeline. +Loading data from external sources (files, databases, APIs, streams) into the pipeline as a unified `SourceDocument`. The first stage in every Semantica pipeline. **Normalization** -Standardizing data into a consistent canonical form: converting dates to ISO format, canonicalizing entity names, fixing encoding issues, stripping noise. Ensures downstream extraction works on clean, consistent text. +Standardizing data into a consistent canonical form by converting dates to ISO format, canonicalizing entity names, fixing encoding issues, and stripping noise. Ensures downstream extraction works on clean, consistent text. **Parsing** -Extracting structured text, layout, and metadata from unstructured or semi-structured documents: PDFs, Word files, HTML, PPTX. `DoclingParser` additionally handles multi-column layouts, merged-cell tables, and OCR. +Extracting structured text, layout, and metadata from unstructured or semi-structured documents (PDFs, Word files, HTML, PPTX). `DoclingParser` additionally handles multi-column layouts, merged-cell tables, and OCR. ## Artificial Intelligence **Abductive Reasoning** -Inference to the most plausible explanation for observed facts. One of six reasoning engines in `semantica.reasoning`: returns the most likely hypothesis given available evidence. +Inference to the most plausible explanation for observed facts. One of six reasoning engines in `semantica.reasoning`, returning the most likely hypothesis given available evidence. **Datalog** A declarative logic programming language for knowledge base queries. Semantica's `DatalogEngine` supports recursive Horn clause rules with bottom-up semi-naive fixpoint semantics. Added in v0.4.0. @@ -62,7 +62,7 @@ A declarative logic programming language for knowledge base queries. Semantica's An advanced RAG approach that combines vector similarity search with knowledge graph traversal. Every LLM response is grounded in structured graph context, with each claim traceable to a source node. Eliminates hallucination without source attribution. **Inference** -Deriving new facts or conclusions from existing knowledge using logical rules: without the derived facts being explicitly present in the source data. +Deriving new facts or conclusions from existing knowledge using logical rules, without the derived facts being explicitly present in the source data. **LLM (Large Language Model)** An AI model trained on large text corpora, capable of understanding and generating natural language. Semantica integrates with 8+ LLM providers for entity extraction, relation extraction, and reasoning. @@ -74,7 +74,7 @@ A technique that enhances LLM outputs by retrieving relevant context from a know ## Knowledge Graph Components **Allen Interval Algebra** -A system of 13 relations for describing how two time intervals relate: before, after, meets, overlaps, during, starts, finishes, equals, and their inverses. Supported in `TemporalKnowledgeGraph` since v0.4.0. +A system of 13 relations for describing how two time intervals relate (before, after, meets, overlaps, during, starts, finishes, equals, and their inverses). Supported in `TemporalKnowledgeGraph` since v0.4.0. **BiTemporalFact** A fact with two independent time dimensions: *valid time* (when it was true in the world) and *transaction time* (when it was recorded in the system). Enables full audit trails for slowly changing data. @@ -86,43 +86,43 @@ A directed connection between two nodes in a graph, representing a typed relatio A vertex in a knowledge graph representing an entity or concept. Nodes carry typed properties, a confidence score, and provenance linking back to the source document. **Property** -An attribute or characteristic of an entity or relationship: name, date, URI, confidence score, source URL. +An attribute or characteristic of an entity or relationship, such as name, date, URI, confidence score, or source URL. **Temporal Graph** A knowledge graph where nodes and edges carry `valid_from` / `valid_until` time windows, enabling point-in-time queries and historical state reconstruction. **Triplet** -The atomic unit of knowledge: a `(subject, predicate, object)` triple: e.g., `(Apple_Inc, founded_by, Steve_Jobs)`. The building block of RDF and SPARQL-based storage. +The atomic unit of knowledge: a `(subject, predicate, object)` triple (e.g., `(Apple_Inc, founded_by, Steve_Jobs)`). The building block of RDF and SPARQL-based storage. ## Entity Recognition & Extraction **Coreference Resolution** -Determining when multiple expressions in text refer to the same entity: e.g., "Apple" and "the company" both referring to Apple Inc. Handled by `CoreferenceResolver` in `semantica.semantic_extract`. +Determining when multiple expressions in text refer to the same entity (e.g., "Apple" and "the company" both referring to Apple Inc.). Handled by `CoreferenceResolver` in `semantica.semantic_extract`. **Entity Resolution** Determining when two entity mentions across different documents refer to the same real-world entity. Also called entity linking or deduplication. Uses similarity scoring, blocking, and semantic embeddings. **Event Detection** -Identifying and classifying events in text: acquisitions, partnerships, product launches, regulatory decisions. Handled by `EventDetector` in `semantica.semantic_extract`. +Identifying and classifying events in text (acquisitions, partnerships, product launches, regulatory decisions). Handled by `EventDetector` in `semantica.semantic_extract`. **Named Entity Recognition (NER)** -Identifying and classifying named entities in text into predefined categories: persons, organizations, locations, dates, products, and custom types. Three modes: pattern-based, ML-based, and LLM-based. +Identifying and classifying named entities in text into predefined categories (persons, organizations, locations, dates, products, and custom types). Three modes: pattern-based, ML-based, and LLM-based. **Relationship Extraction** -Identifying and extracting typed semantic relationships between entities: e.g., `(Google, acquired, DeepMind)`: from raw text. +Identifying and extracting typed semantic relationships between entities (such as `(Google, acquired, DeepMind)`) from raw text. ## Ontology & Schema **Axiom** -A statement accepted as true in an ontology, used to define logical constraints: e.g., "every Person must have a name", "Organization can have at most one CEO at a time". +A statement accepted as true in an ontology, used to define logical constraints (e.g., "every Person must have a name", "Organization can have at most one CEO at a time"). **Class** -A category or type of entity in an ontology: `Person`, `Organization`, `Location`. Classes form a hierarchy and carry constraints validated by SHACL. +A category or type of entity in an ontology (`Person`, `Organization`, `Location`). Classes form a hierarchy and carry constraints validated by SHACL. **Ontology** -A formal specification of domain concepts, relationships, and constraints: typically expressed in OWL. Semantica can auto-generate ontologies from knowledge graphs or import existing OWL/RDF/Turtle files. +A formal specification of domain concepts, relationships, and constraints, typically expressed in OWL. Semantica can auto-generate ontologies from knowledge graphs or import existing OWL/RDF/Turtle files. **Ontology Hub** Semantica's v0.5.0 visual browser UI for the full ontology lifecycle: visual class editor, SHACL Studio, alignment authoring, health dashboard, and version-controlled diffs. @@ -140,13 +140,13 @@ A W3C standard for representing controlled vocabularies, taxonomies, and thesaur ## Storage & Retrieval **Embedding** -A dense numerical vector that represents text, images, or other data in a continuous semantic space. Entities with similar meaning produce vectors that are close together: enabling similarity search and semantic matching. +A dense numerical vector that represents text, images, or other data in a continuous semantic space. Entities with similar meaning produce vectors that are close together, enabling similarity search and semantic matching. **Graph Database** A database optimized for storing and querying graph-structured data using node and edge primitives. Semantica supports Neo4j, FalkorDB, Apache AGE, and Amazon Neptune. **Hybrid Search** -A retrieval strategy combining vector similarity search with keyword or metadata filtering: higher accuracy than either approach alone. +A retrieval strategy combining vector similarity search with keyword or metadata filtering, achieving higher accuracy than either approach alone. **Triplet Store** A database designed specifically for storing and querying RDF `(subject, predicate, object)` triples. Semantica supports embedded Oxigraph as well as Blazegraph, Apache Jena, and RDF4J. @@ -158,19 +158,19 @@ A database optimized for storing and searching high-dimensional embedding vector ## Graph Analytics **Centrality** -A measure of a node's importance in the graph. Common metrics: PageRank (link-based importance), betweenness centrality (bridge nodes), closeness centrality (average distance to all others). +A measure of a node's importance in the graph. Common metrics include PageRank (link-based importance), betweenness centrality (bridge nodes), and closeness centrality (average distance to all others). **Community Detection** -Identifying groups of densely connected nodes: clusters that share more internal links than external ones. Used for finding subject communities, fraud rings, and organizational clusters. +Identifying groups of densely connected nodes (clusters that share more internal links than external ones). Used for finding subject communities, fraud rings, and organizational clusters. **Distance Band** -A classification of a node's semantic proximity to a target: `near`, `mid`, or `far`, based on embedding distance thresholds. Part of Distance Intelligence (v0.5.0). +A classification of a node's semantic proximity to a target (`near`, `mid`, or `far`) based on embedding distance thresholds. Part of Distance Intelligence (v0.5.0). **Distance Intelligence** -Semantica's v0.5.0 feature for semantic neighborhood exploration: N×N distance matrices, ego-mode visualization centered on a single entity, and distance band classification across the graph. +Semantica's v0.5.0 feature for semantic neighborhood exploration, including N×N distance matrices, ego-mode visualization centered on a single entity, and distance band classification across the graph. **PageRank** -An algorithm measuring node importance based on the structure of incoming relationships: originally designed for web pages, applicable to any directed graph. +An algorithm measuring node importance based on the structure of incoming relationships; originally designed for web pages, but applicable to any directed graph. ## Query Languages & Standards @@ -194,13 +194,13 @@ The W3C query language for RDF data. Semantica's `SparqlReasoner` uses SPARQL fo Handling contradictory facts from multiple sources in the same knowledge graph. Semantica's `ConflictDetector` surfaces conflicts; resolution strategies include prefer-most-recent, prefer-most-reliable, majority-vote, and flag-for-review. **Data Provenance** -Complete information about the origin, history, and lineage of every fact: source document, extraction method, timestamp, confidence score. W3C PROV-O compliant in Semantica. +Complete information about the origin, history, and lineage of every fact (source document, extraction method, timestamp, confidence score). W3C PROV-O compliant in Semantica. **Deduplication** Identifying and merging duplicate entity records. Semantica v2 strategies (`blocking_v2`, `hybrid_v2`, `semantic_v2`) are up to 7x faster than v1. **W3C PROV-O** -The W3C provenance ontology standard. Semantica tracks lineage across all modules in PROV-O compliant format: suitable for HIPAA, SOX, GDPR, and FDA 21 CFR Part 11 compliance. +The W3C provenance ontology standard. Semantica tracks lineage across all modules in PROV-O compliant format, suitable for HIPAA, SOX, GDPR, and FDA 21 CFR Part 11 compliance. ## Security Terms @@ -214,7 +214,7 @@ A vulnerability in XML parsers that allows attackers to read arbitrary files or ## See Also -- [Core Concepts](/concepts) — Deeper explanation of key ideas with code examples. -- [Getting Started](/getting-started) — First working examples: no prior graph experience required. -- [Modules Guide](/modules) — All 27 modules explained with code and pipeline chains. -- [API Reference](/reference/context) — Complete technical reference for every class and method. +- [Core Concepts](/concepts): deeper explanation of key ideas with code examples. +- [Getting Started](/getting-started): first working examples with no prior graph experience required. +- [Modules Guide](/modules): all 27 modules explained with code and pipeline chains. +- [API Reference](/reference/context): complete technical reference for every class and method. diff --git a/docs/governance.md b/docs/governance.md index 61ab29dc..8fa78d0a 100644 --- a/docs/governance.md +++ b/docs/governance.md @@ -9,9 +9,9 @@ icon: "scale-balanced" ## Roles -- **Maintainers** — Semantica team: review and merge PRs, manage releases and code quality, set project direction and community standards. -- **Contributors** — Submit code, documentation, and bug reports. Help with issues and reviews. Recognized in [CONTRIBUTORS.md](https://github.com/semantica-agi/semantica/blob/main/CONTRIBUTORS.md). -- **Community Members** — Use Semantica, provide feedback, share use cases, and participate in GitHub Discussions and Discord. +- **Maintainers**: Semantica team. Review and merge PRs, manage releases and code quality, set project direction and community standards. +- **Contributors**: submit code, documentation, and bug reports. Help with issues and reviews. Recognized in [CONTRIBUTORS.md](https://github.com/semantica-agi/semantica/blob/main/CONTRIBUTORS.md). +- **Community Members**: use Semantica, provide feedback, share use cases, and participate in GitHub Discussions and Discord. ## Decision Process @@ -65,11 +65,11 @@ Semantica follows **Semantic Versioning** (`MAJOR.MINOR.PATCH`): ## Project Goals -- **Usability** — Easy to use and understand: sensible defaults, clear documentation, minimal ceremony. -- **Reliability** — Production-ready quality: tested across Python versions, platforms, and real-world workloads. -- **Performance** — Efficient and scalable: from single-machine notebooks to enterprise graph databases. -- **Extensibility** — Easy to extend with plugins and custom modules via the `PluginRegistry` pattern. -- **Community** — Welcoming and inclusive: all backgrounds and experience levels contribute and are recognized. +- **Usability**: easy to use and understand with sensible defaults, clear documentation, and minimal ceremony. +- **Reliability**: production-ready quality tested across Python versions, platforms, and real-world workloads. +- **Performance**: efficient and scalable from single-machine notebooks to enterprise graph databases. +- **Extensibility**: easy to extend with plugins and custom modules via the `PluginRegistry` pattern. +- **Community**: welcoming and inclusive. All backgrounds and experience levels contribute and are recognized. ## License @@ -79,5 +79,5 @@ MIT License: see [LICENSE](https://github.com/semantica-agi/semantica/blob/main/ ## See Also -- [Contributing](/contributing-guide) — How to submit changes. -- [Community](/community) — Community guidelines and channels. +- [Contributing](/contributing-guide): how to submit changes. +- [Community](/community): community guidelines and channels. diff --git a/docs/guides/decision-intelligence.md b/docs/guides/decision-intelligence.md index a915c84c..9053e778 100644 --- a/docs/guides/decision-intelligence.md +++ b/docs/guides/decision-intelligence.md @@ -102,9 +102,8 @@ The `Decision` dataclass that backs this node has the following fields — these from semantica.context import Decision from datetime import datetime -# Constructing a Decision explicitly (alternative to record_decision) d = Decision( - decision_id = "dec_001", # UUID — auto-generated if omitted via record_decision + decision_id = None, # required arg — None/"" auto-generates a UUID category = "threat_classification", scenario = "Unattributed C2 cluster", reasoning = "Infrastructure overlaps APT29 ASN", @@ -117,9 +116,29 @@ d = Decision( valid_until = "2025-09-30T23:59:59", # ISO datetime metadata = {"source_feed": "isac_partner_b"}, ) -graph.add_decision(d) ``` +To actually store a decision built this way, pass its fields to `ContextGraph.add_decision()` as keyword arguments — this is the alternative to `record_decision()` for cases where you want `valid_from`/`valid_until` or extra metadata fields alongside the required ones: + +```python +decision_id = graph.add_decision( + category = "threat_classification", + scenario = "Unattributed C2 cluster", + reasoning = "Infrastructure overlaps APT29 ASN", + outcome = "classified_as_apt29_cluster", + confidence = 0.88, # float 0.0–1.0 + decision_maker = "cti_pipeline_v2", + # optional fields: + valid_from = "2025-07-01T00:00:00", # ISO datetime + valid_until = "2025-09-30T23:59:59", # ISO datetime + source_feed = "isac_partner_b", # extra kwargs are stored as metadata +) +``` + + +Only pass keyword arguments to `add_decision()`, not a pre-built `Decision` object. `add_decision(Decision(...))` stores the node directly and skips the indexing step that `record_decision()` performs, so the decision becomes invisible to `find_precedents()`, `get_causal_chain()`, and `get_decision_insights()`, and `trace_decision_causality()` raises `ValueError` if you call it on one. The keyword-argument form above does not have this problem — it delegates to `record_decision()` internally. Note that, like `record_decision()`, it always generates its own `decision_id` (returned from the call); there is no way to force a specific ID. + + ## Searching Precedents Before Deciding Before making a significant call, the system should search past decisions for similar scenarios. This is how you prevent the same cluster being classified differently across two agent runs — the second agent finds the first agent's decision and uses it as a prior. @@ -137,7 +156,7 @@ for p in precedents: print(" Similarity: {:.3f}".format(p.metadata.get("similarity_score", 0))) ``` -Hybrid search blends two signals: semantic similarity over the `scenario` and `reasoning` text (weight 0.7), and structural graph proximity via Node2Vec embeddings (weight 0.3). The result is a ranked list of `Decision` objects — the most similar past decisions float to the top regardless of how differently they were phrased. +Hybrid search blends two signals: lexical overlap between the query and each decision's `scenario`, `reasoning`, and `entities` text (weight 0.7 — word-level Jaccard similarity, with a character-bigram fallback for CJK-style queries), and structural similarity based on how many other nodes each decision connects to in the graph (weight 0.3, only computed when the graph was built with `advanced_analytics=True`). The result is a ranked list of `Decision` objects, filtered to those scoring at least `similarity_threshold` (default 0.5) — because the match is lexical rather than embedding-based, precedents phrased very differently from the query may not surface even if they describe a similar scenario. ## Building a Causal Chain @@ -262,8 +281,13 @@ d = Decision( ) if engine.check_compliance(d, "cti_confidence_gate"): - graph.add_decision(d) - engine.record_policy_application(d.decision_id, "cti_confidence_gate", "1.0") + # Pass fields as kwargs, not the Decision object itself — see the + # warning above. add_decision() generates its own decision_id. + decision_id = graph.add_decision( + category=d.category, scenario=d.scenario, reasoning=d.reasoning, + outcome=d.outcome, confidence=d.confidence, decision_maker=d.decision_maker, + ) + engine.record_policy_application(decision_id, "cti_confidence_gate", "1.0") print("Decision recorded — policy compliant.") else: print("Decision blocked — confidence 0.62 below policy minimum 0.80.") @@ -590,7 +614,7 @@ if engine.check_compliance(d, "lending_policy_v3"): decision_maker=d.decision_maker, ) graph.add_causal_relationship(stress_id, loan_id, "INFLUENCED") - engine.record_policy_application(d.decision_id, "lending_policy_v3", "3.0") + engine.record_policy_application(loan_id, "lending_policy_v3", "3.0") print("Loan decision recorded — policy compliant.") # SR 11-7 explainability report diff --git a/docs/guides/shacl-validation.md b/docs/guides/shacl-validation.md index 8ee40f7e..df81df04 100644 --- a/docs/guides/shacl-validation.md +++ b/docs/guides/shacl-validation.md @@ -707,6 +707,22 @@ report_dict = report.to_dict() --- +## Resource limits + +Live SHACL validation in the Explorer enforces four resource limits, all configurable +through environment variables. When a limit trips, the error message names the +variable that controls it. + +| Environment variable | Default | What it bounds | +| --- | --- | --- | +| `SEMANTICA_MAX_SHACL_TURTLE_BYTES` | `262144` (256 KB) | Size of the submitted SHACL Turtle | +| `SEMANTICA_MAX_SHACL_TRIPLES` | `1000` | Triple count of the parsed shapes graph | +| `SEMANTICA_MAX_SHACL_TIMEOUT` | `15.0` | Validation timeout in seconds | +| `SEMANTICA_MAX_SHACL_CONCURRENCY` | `4` | Concurrent validations per process | + +The first three are surfaced in the validation error message when exceeded; the +concurrency limit applies as a semaphore and does not appear in responses. + ## Using SHACL validation as a CI/CD gate Call this function as a pre-publish gate; exit code 1 blocks the pipeline. diff --git a/docs/installation.md b/docs/installation.md index c562ae11..c8dd48a4 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -183,6 +183,6 @@ Install the [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/ ## Next Steps -- [Getting Started](/getting-started) — Understand what Semantica does before you build. -- [Build the Pipeline](/quickstart) — Follow the end-to-end workflow with code. -- [Browse Examples](/cookbook) — See notebook examples organized by use case. +- [Getting Started](/getting-started): understand what Semantica does before you build. +- [Build the Pipeline](/quickstart): follow the end-to-end workflow with code. +- [Browse Examples](/cookbook): see notebook examples organized by use case. diff --git a/docs/learning-more.md b/docs/learning-more.md index 7134e98f..6f74d88c 100644 --- a/docs/learning-more.md +++ b/docs/learning-more.md @@ -9,9 +9,9 @@ Whether you're running your first pipeline or deploying Semantica in production, ## Learning Paths -- **Beginner (1–2 hrs)** — New to Semantica and knowledge graphs. [Start with Installation →](/installation) -- **Intermediate (4–6 hrs)** — Comfortable with basics, building real applications. [Start with Modules →](/modules) -- **Advanced (8+ hrs)** — Enterprise deployments, customization, and extension. [Start with Architecture →](/architecture) +- **Beginner (1–2 hrs)**: new to Semantica and knowledge graphs. [Start with Installation →](/installation) +- **Intermediate (4–6 hrs)**: comfortable with basics, building real applications. [Start with Modules →](/modules) +- **Advanced (8+ hrs)**: enterprise deployments, customization, and extension. [Start with Architecture →](/architecture) @@ -116,7 +116,7 @@ pip install "semantica[gpu]" # GPU acceleration -Set your API key as an environment variable — never hardcode keys in source files: +Set your API key as an environment variable (never hardcode keys in source files): ```bash export OPENAI_API_KEY="sk-..." @@ -236,6 +236,6 @@ The `blocking_v2`, `hybrid_v2`, and `semantic_v2` strategies reduce O(n²) compa - **Graph exports**: encrypt sensitive exports at rest; use the v0.5.0 SSRF-safe `base_url` validation when configuring custom LLM gateways - **XML ingestion**: always use `XMLIngestor` (v0.5.0), which uses the XXE-safe lxml backend; never parse untrusted XML with the standard library parser -- [Cookbook](/cookbook) — Interactive Jupyter notebooks from beginner to advanced. -- [FAQ](/faq) — Common questions answered. -- [API Reference](/reference/core) — Complete technical documentation. +- [Cookbook](/cookbook): interactive Jupyter notebooks from beginner to advanced. +- [FAQ](/faq): common questions answered. +- [API Reference](/reference/core): complete technical documentation. diff --git a/docs/modules.md b/docs/modules.md index 4a41086a..9c47f1cc 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -5,23 +5,23 @@ icon: "puzzle-piece" --- - Looking for a quick reference? Jump to the [Module Index](#module-index) at the bottom. + Jump to the [Module Index](#module-index) for a quick reference. - Not sure which module to use? The [Choose the Right Module](/choose-your-module) guide maps 35+ developer goals to modules with code examples — start there if you're orienting for the first time. + The [Choose the Right Module](/choose-your-module) guide maps 35+ developer goals to modules with code examples; start there if you're orienting for the first time. Semantica is organized into **27 modules** across six logical layers. Each module is independently importable: you never pay for what you don't use. ## Architecture Overview -- **Input Layer** — Data ingestion and preparation. Modules: `ingest`, `parse`, `split`, `normalize` -- **Core Processing** — Intelligence and understanding. Modules: `semantic_extract`, `kg`, `ontology`, `reasoning` -- **Storage** — Persistent data storage. Modules: `embeddings`, `vector_store`, `graph_store`, `triplet_store` -- **Quality Assurance** — Data quality and consistency. Modules: `deduplication`, `conflicts` -- **Context & Memory** — Agent memory and decision tracking. Modules: `context`, `provenance`, `change_management` -- **Output & Orchestration** — Export, visualization, and workflows. Modules: `export`, `visualization`, `pipeline`, `explorer` +- **Input Layer**: data ingestion and preparation. Modules: `ingest`, `parse`, `split`, `normalize` +- **Core Processing**: intelligence and understanding. Modules: `semantic_extract`, `kg`, `ontology`, `reasoning` +- **Storage**: persistent data storage. Modules: `embeddings`, `vector_store`, `graph_store`, `triplet_store` +- **Quality Assurance**: data quality and consistency. Modules: `deduplication`, `conflicts` +- **Context & Memory**: agent memory and decision tracking. Modules: `context`, `provenance`, `change_management` +- **Output & Orchestration**: export, visualization, and workflows. Modules: `export`, `visualization`, `pipeline`, `explorer` ## Input Layer @@ -51,7 +51,7 @@ sources = parquet.ingest("data/events.parquet") xml = XMLIngestor() sources = xml.ingest("data/records/", schema_path="schema.xsd") -# Enterprise lakehouse/warehouse — Unity Catalog + Delta Lake, or a Snowflake warehouse +# Enterprise lakehouse/warehouse: Unity Catalog + Delta Lake, or a Snowflake warehouse databricks = DatabricksIngestor(host="...", token="...", http_path="...") customers = databricks.ingest_table("customers") ``` @@ -59,7 +59,7 @@ customers = databricks.ingest_table("customers") **Available ingestors:** `FileIngestor`, `WebIngestor`, `ParquetIngestor`, `XMLIngestor`, `RESTIngestor`, `PublicAPIIngestor`, `DBIngestor`, `DatabricksIngestor`, `SnowflakeIngestor`, `EmailIngestor`, `FeedIngestor`, `MCPIngestor`, `OntologyIngestor`, `RepoIngestor`, `StreamIngestor`, `ArrowIngestor`, `CloudStorageIngestor` - `DuckDBIngestor`, `ElasticIngestor`, `GDriveIngestor`, `HuggingFaceIngestor`, `MongoIngestor`, and `PandasIngestor` also ship but aren't re-exported from the top-level `semantica.ingest` namespace yet — import them directly, e.g. `from semantica.ingest.duckdb_ingestor import DuckDBIngestor`. + `DuckDBIngestor`, `ElasticIngestor`, `GDriveIngestor`, `HuggingFaceIngestor`, `MongoIngestor`, and `PandasIngestor` also ship but aren't re-exported from the top-level `semantica.ingest` namespace yet; import them directly, e.g. `from semantica.ingest.duckdb_ingestor import DuckDBIngestor`. ### Parse @@ -463,7 +463,7 @@ Exposes Semantica as an MCP stdio server for IDE and agent integrations. python -m semantica.mcp_server ``` -**Integrations:** Claude Desktop, VS Code, Cursor, Windsurf, Cline: 15 MCP tools exposed +**Integrations:** Claude Desktop, VS Code, Cursor, Windsurf, Cline. 15 MCP tools are exposed. ### Seed @@ -749,6 +749,6 @@ versioner.create_snapshot(kg, "2024-Q1", author="user@example.com", description= | [core](/reference/core) | Base classes & registry | `Semantica`, `ConfigManager`, `PluginRegistry`, `LifecycleManager` | | [utils](/reference/utils) | Shared utilities | `helpers`, `validators` | -- [Getting Started](/getting-started) — Your first knowledge graph in 5 minutes. -- [Cookbook](/cookbook) — 40+ domain notebooks with real-world examples. -- [API Reference](/reference/context) — Full technical documentation. +- [Getting Started](/getting-started): your first knowledge graph in 5 minutes. +- [Cookbook](/cookbook): 40+ domain notebooks with real-world examples. +- [API Reference](/reference/context): full technical documentation. diff --git a/docs/project-license.md b/docs/project-license.md index 284d2eb8..4a26885f 100644 --- a/docs/project-license.md +++ b/docs/project-license.md @@ -76,5 +76,5 @@ By contributing to Semantica, you agree that your contributions will be licensed ## See Also -- [Contributing](/contributing-guide) — How to contribute to the project. -- [Citation](/citation) — How to cite Semantica in research. +- [Contributing](/contributing-guide): how to contribute to the project. +- [Citation](/citation): how to cite Semantica in research. diff --git a/docs/quickstart.md b/docs/quickstart.md index af3784a3..541f2961 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -5,7 +5,7 @@ icon: "rocket" --- - **v0.6.7** — first-class LangChain integration, SAP OData ingestor, human-editable Markdown persistence for `ContextGraph`, and a structured Action layer for the reasoning engine. What's new → + **v0.6.8**: cryptographically signed releases (SLSA provenance + Sigstore), real vector-store enumeration across FAISS/Qdrant/Weaviate/Milvus, and first-class Anthropic/Gemini/Ollama/DeepSeek/Novita LLM provider wrappers. What's new → This guide walks you through the end-to-end pipeline for building your first knowledge graph. Start here after installation. An LLM API key is optional: pattern-based extraction works out of the box. @@ -35,7 +35,7 @@ Verify: ```bash python -c "import semantica; print(semantica.__version__)" -# 0.6.7 +# 0.6.8 ``` @@ -454,7 +454,7 @@ pip install --upgrade semantica ## Next Steps -- [Core Concepts](/concepts) — Knowledge graphs, ontologies, reasoning engines: the mental model behind Semantica. -- [Module Reference](/modules) — Every module explained with key classes and common chains. -- [API Reference](/reference/context) — Complete documentation for every module, class, and parameter. -- [Cookbook](/cookbook) — 40+ interactive Jupyter notebooks with real-world datasets. +- [Core Concepts](/concepts): knowledge graphs, ontologies, and reasoning engines (the mental model behind Semantica). +- [Module Reference](/modules): every module explained with key classes and common chains. +- [API Reference](/reference/context): complete documentation for every module, class, and parameter. +- [Cookbook](/cookbook): 40+ interactive Jupyter notebooks with real-world datasets. diff --git a/docs/reference/embeddings.md b/docs/reference/embeddings.md index e0bc5eb6..5658be56 100644 --- a/docs/reference/embeddings.md +++ b/docs/reference/embeddings.md @@ -1,6 +1,6 @@ --- title: "Embeddings Module" -description: "Text and graph embedding generation: FastEmbed, Sentence-Transformers, OpenAI, BGE: with pooling strategies and provider-agnostic API." +description: "Text and graph embedding generation (FastEmbed, Sentence-Transformers, OpenAI, BGE) with pooling strategies and a provider-agnostic API." icon: "vector-square" --- @@ -41,12 +41,12 @@ Semantica uses embeddings for: ## What You Get -- **EmbeddingGenerator** — Main entry point: provider-agnostic, handles batching automatically across all backends. -- **TextEmbedder** — Text-specific with automatic batching and progress tracking. Default method is FastEmbed. -- **GraphEmbeddingManager** — Node and edge embeddings for graph databases: Neo4j, NetworkX, FalkorDB. -- **VectorEmbeddingManager** — Prepare, normalize, and format embeddings for FAISS, Weaviate, Qdrant, and Milvus. -- **Provider Stores** — `OpenAIStore`, `BGEStore`, `FastEmbedStore`, and `ProviderStoreFactory`. -- **Pooling Strategies** — Mean, Max, CLS, Attention, and Hierarchical: control token-to-vector aggregation. +- **EmbeddingGenerator**: provider-agnostic main entry point that handles batching automatically across all backends. +- **TextEmbedder**: text-specific embedder with automatic batching and progress tracking. Default method is FastEmbed. +- **GraphEmbeddingManager**: node and edge embeddings for graph databases (Neo4j, NetworkX, FalkorDB). +- **VectorEmbeddingManager**: prepare, normalize, and format embeddings for FAISS, Weaviate, Qdrant, and Milvus. +- **Provider Stores**: `OpenAIStore`, `BGEStore`, `FastEmbedStore`, and `ProviderStoreFactory`. +- **Pooling Strategies**: Mean, Max, CLS, Attention, and Hierarchical control token-to-vector aggregation. ## Provider Setup @@ -71,7 +71,7 @@ Semantica uses embeddings for: - **FastEmbed ignores the `device` parameter.** FastEmbed uses ONNX Runtime and manages its own execution providers: passing `device="cuda"` has no effect. Switch to `method="sentence_transformers"` if you need explicit GPU control. + **FastEmbed ignores the `device` parameter.** FastEmbed uses ONNX Runtime and manages its own execution providers; passing `device="cuda"` has no effect. Switch to `method="sentence_transformers"` if you need explicit GPU control. @@ -153,7 +153,7 @@ providers = check_available_providers() ## Getting Started -`EmbeddingGenerator` is the fastest path to embeddings: the default method is FastEmbed (ONNX, no GPU needed): +`EmbeddingGenerator` is the fastest path to embeddings. The default method is FastEmbed (ONNX, no GPU needed): ```python from semantica.embeddings import EmbeddingGenerator @@ -173,7 +173,7 @@ print(f"Similarity: {score:.3f}") ``` - **Always use the same model for indexing and querying.** Vectors from different models are not comparable: they live in different vector spaces. Switching models requires re-embedding your entire corpus. + **Always use the same model for indexing and querying.** Vectors from different models are not comparable; they live in different vector spaces. Switching models requires re-embedding your entire corpus. To switch provider after construction: @@ -258,7 +258,7 @@ generator.set_text_model("sentence_transformers", "BAAI/bge-large-en-v1.5") similarity = generator.compare_embeddings(embeddings[0], embeddings[1]) ``` - **Best for:** CPU-only production, lowest latency without GPU. Default: works out of the box. + **Best for:** CPU-only production and lowest latency without GPU. The default works out of the box. ```python @@ -390,7 +390,7 @@ store = ProviderStoreFactory.create(provider="bge", model_name="BAAI/bge-large-e ## Pooling Strategies -Pooling aggregates a set of embeddings into a single vector: useful when you have multiple chunk embeddings to combine: +Pooling aggregates a set of embeddings into a single vector. Useful when you have multiple chunk embeddings to combine: @@ -401,7 +401,7 @@ Pooling aggregates a set of embeddings into a single vector: useful when you hav pooled = pooler.pool(token_embeddings) # shape: (hidden_dim,) ``` - **Best for:** retrieval, semantic search, and clustering: averages all contributions. + **Best for:** retrieval, semantic search, and clustering. Averages all contributions. ```python @@ -411,7 +411,7 @@ Pooling aggregates a set of embeddings into a single vector: useful when you hav pooled = pooler.pool(token_embeddings) ``` - **Best for:** capturing the presence of any feature: takes the max activation per dimension. + **Best for:** capturing the presence of any feature. Takes the max activation per dimension. ```python @@ -432,7 +432,7 @@ Pooling aggregates a set of embeddings into a single vector: useful when you hav pooled = pooler.pool(token_embeddings, chunk_size=10) ``` - **Best for:** long documents: chunk-level mean pooling, then global mean pooling across chunks. + **Best for:** long documents (chunk-level mean pooling, then global mean pooling across chunks). @@ -619,7 +619,7 @@ providers = check_available_providers() # → {"sentence_transformers": True, "fastembed": True, "openai": False} ``` -- [Vector Store](/reference/vector_store) — Store and search the generated embeddings. -- [Split](/reference/split) — Chunk text before embedding for better retrieval quality. -- [KG Module](/reference/kg) — Distance Intelligence uses graph embeddings for semantic neighbourhoods. -- [Deduplication](deduplication) — Semantic deduplication uses embedding distance for entity resolution. +- [Vector Store](/reference/vector_store): store and search the generated embeddings. +- [Split](/reference/split): chunk text before embedding for better retrieval quality. +- [KG Module](/reference/kg): Distance Intelligence uses graph embeddings for semantic neighbourhoods. +- [Deduplication](/reference/deduplication): semantic deduplication uses embedding distance for entity resolution. diff --git a/docs/reference/semantic_extract.md b/docs/reference/semantic_extract.md index 28cdaf5f..e8fbd9c0 100644 --- a/docs/reference/semantic_extract.md +++ b/docs/reference/semantic_extract.md @@ -191,6 +191,41 @@ trip = TripletExtractor(method=["llm", "pattern"]) entities = ner.extract(text) ``` +### NER Merge Strategies + +`NERExtractor` uses `merge_strategy="fallback"` by default, so a method list remains an ordered fallback chain. To run several methods together, choose one of the explicit strategies below: + +| Strategy | Behavior | +| :--- | :--- | +| `fallback` | Return the first non-empty method result. | +| `union` | Keep candidates from any method. Same-label boundary variants are aligned, while distinct labels remain available. | +| `consensus` | Require cross-method support for an offset-aligned candidate. `min_votes` defaults to `2`. | + +```python +from semantica.semantic_extract import NERExtractor + +ner = NERExtractor( + method=["spacy", "huggingface"], + merge_strategy="consensus", + min_votes=2, + min_agreement=0.75, # optional support-ratio requirement + method_weights={"spacy": 0.8, "huggingface": 1.0}, +) +entities = ner.extract(text) + +for entity in entities: + print(entity.metadata["supporting_methods"]) + print(entity.metadata["vote_count"], entity.metadata["agreement"]) +``` + +Consensus counts support against the configured eligible methods, not only methods that emitted a candidate. An empty or failed eligible method is therefore a non-supporting vote. Use `eligible_methods=[...]` to restrict the consensus denominator when the configured methods have different coverage, or use `merge_strategy="union"` for complementary rule extractors. `method_weights` only break an otherwise eligible exact-span cross-label tie; they never turn one method into multiple votes. + +Each merged entity includes `supporting_methods`, `vote_count`, `eligible_method_count`, `agreement`, and per-method `method_scores` in its metadata. Consensus treats compatible label aliases such as `PER`/`PERSON` and `ORGANIZATION`/`ORG` as the same vote. It resolves a cross-label conflict only when the final spans are identical, using method weight, vote count, confidence, and a stable label order; nested entities at different spans remain available. `ml` and `spacy` are one backend for both voting and weights, so their weights are interchangeable (conflicting values are rejected). Boundary candidates are matched one-to-one only when their span IoU is at least 0.5 with every existing vote in that candidate; equal-confidence variants prefer the longer span. If a provider omits offsets, Semantica resolves its entity text against whole-word document matches before merging. This keeps repeated mentions with the same text distinct and prevents one broad span from acting as a vote for multiple mentions. + +`ensemble_voting=True` is deprecated and maps to `merge_strategy="union"` during migration. Use `merge_strategy="consensus"` when method agreement is required. + +Unlike `fallback`, `union` and `consensus` never inject a pattern-derived entity after the configured methods return no candidates. An empty result is therefore meaningful in those strategies. + ## Quick Start diff --git a/docs_check.py b/docs_check.py index 86095764..7f0ede1f 100644 --- a/docs_check.py +++ b/docs_check.py @@ -236,7 +236,7 @@ def _() -> list[str]: cwd=DOCS, capture_output=True, text=True, - timeout=300, + timeout=600, ) # Clean up zip regardless of outcome zip_path = os.path.join(DOCS, "export_ci_check.zip") @@ -246,9 +246,12 @@ def _() -> list[str]: combined = (result.stdout or "") + (result.stderr or "") if result.returncode != 0: - # On Windows, npm cleanup raises EPERM on temp dirs — not a real - # export failure. Treat as a skip rather than a hard failure. - if sys.platform == "win32" and "EPERM" in combined and \ + # On Windows, npm post-command cleanup can fail with EPERM/EBUSY on + # temp dirs — not a real export failure. Treat as a skip rather + # than a hard failure, unless a real Mintlify error signature is + # present. + if sys.platform == "win32" and \ + ("EPERM" in combined or "EBUSY" in combined) and \ "could not be generated" not in combined: return [] # Windows temp-cleanup noise; real CI runs on Linux @@ -263,7 +266,7 @@ def _() -> list[str]: except FileNotFoundError: return ["npx not found — skipping Mintlify export check (Node.js required)"] except subprocess.TimeoutExpired: - return ["mintlify export timed out after 300 s"] + return ["mintlify export timed out after 600 s"] # ── Summary ─────────────────────────────────────────────────────────────────── diff --git a/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx b/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx index 433e0060..dd741c18 100644 --- a/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx +++ b/explorer/src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx @@ -1,9 +1,12 @@ import { useEffect, + useId, + useLayoutEffect, useMemo, useRef, useState, type CSSProperties, + type KeyboardEvent, } from "react"; import ReactMarkdown, { type Components } from "react-markdown"; import remarkGfm from "remark-gfm"; @@ -34,6 +37,26 @@ export interface MarkdownContentViewerProps { defaultMode?: "preview" | "source"; } +// Exported for unit-testing the roving-tabindex navigation logic without a DOM. +// Given the ordered list of tab modes and the currently focused mode, returns +// the mode that should receive focus for a given keyboard key. Returns null if +// the key is not a navigation key so callers can handle the default case. +// eslint-disable-next-line react-refresh/only-export-components +export function resolveTabNavigation( + current: "preview" | "source", + key: string, +): "preview" | "source" | null { + const order = ["preview", "source"] as const; + const idx = order.indexOf(current); + switch (key) { + case "ArrowRight": return order[(idx + 1) % order.length]; + case "ArrowLeft": return order[(idx - 1 + order.length) % order.length]; + case "Home": return order[0]; + case "End": return order[order.length - 1]; + default: return null; + } +} + export function MarkdownContentViewer({ content, resource, @@ -70,6 +93,64 @@ export function MarkdownContentViewer({ if (copied) setCopied(false); } + // useId (not hardcoded strings) so the ids stay unique if more than one viewer + // is ever mounted at once — the same pattern GraphWorkspace uses for its search + // combobox. Hardcoded ids would collide silently in that case. + const baseId = useId(); + const previewTabId = `${baseId}-tab-preview`; + const sourceTabId = `${baseId}-tab-source`; + const panelId = `${baseId}-panel`; + + // Roving tabindex: the tablist is one Tab stop and arrows move focus within it. + // Focus is tracked separately from selection because activation is manual (see + // handleTabKeyDown), so a tab can hold focus without being the selected one. + // + // focusedMode is stored in a ref rather than state so that moving focus with + // arrow keys does NOT trigger a React re-render. A re-render here is expensive: + // react-markdown@10 has no internal memoisation and calls processor.parse() + + // processor.runSync() unconditionally on every render — measured at 385ms for a + // 1000-row GFM table and 1.4s at 2000 rows (#1118). Using a ref means arrow-key + // navigation is free of Markdown re-parses while still keeping the DOM tabIndex + // attributes correct via direct mutation (the same pattern used by WAI-ARIA APG + // keyboard examples for roving tabindex). + // + // The JSX tabIndex props use activeMode (not the ref) to satisfy the + // react-hooks/refs lint rule that bars ref reads during render. JSX provides the + // correct value on initial render and after selectMode() calls (which always keep + // focusedModeRef.current === activeMode at React render boundaries). A + // useLayoutEffect (see below) corrects any JSX overwrite that occurs when focus + // and selection temporarily differ during arrow navigation. + const focusedModeRef = useRef<"preview" | "source">(defaultMode); + const previewTabRef = useRef(null); + const sourceTabRef = useRef(null); + + const focusTab = (mode: "preview" | "source") => { + focusedModeRef.current = mode; + // Imperatively update tabIndex on both buttons so the roving tabindex + // DOM state is correct without scheduling a React re-render. + if (previewTabRef.current) previewTabRef.current.tabIndex = mode === "preview" ? 0 : -1; + if (sourceTabRef.current) sourceTabRef.current.tabIndex = mode === "source" ? 0 : -1; + (mode === "preview" ? previewTabRef : sourceTabRef).current?.focus(); + }; + + const selectMode = (mode: "preview" | "source") => { + // Keep the ref in sync before setActiveMode so the upcoming re-render reads + // the correct focusedModeRef.current when evaluating JSX tabIndex props. + focusedModeRef.current = mode; + setActiveMode(mode); + }; + + // Manual activation (APG permits it, and here it is required): arrows move + // focus only, Enter/Space activates via the native button click. Automatic + // activation would re-run the full markdown parse on every arrow keypress — + // measured at 385ms for a 1000-row GFM table and 1.4s at 2000 rows (#1118). + const handleTabKeyDown = (event: KeyboardEvent) => { + const next = resolveTabNavigation(focusedModeRef.current, event.key); + if (next === null) return; + event.preventDefault(); + focusTab(next); + }; + const copyTimeoutRef = useRef(undefined); useEffect(() => { return () => { @@ -77,9 +158,44 @@ export function MarkdownContentViewer({ }; }, []); + // When the viewed resource/node changes, reset focusedModeRef to match the + // incoming defaultMode. The render-phase setActiveMode(defaultMode) above + // resets React state, but refs are not state and must be updated separately. + // useLayoutEffect fires synchronously before paint so the ref is correct before + // the no-deps tabIndex-correction effect (declared next) reads it. + // Using resourceKey as the dep means this runs exactly once per resource change, + // immediately after the render that detected the change. + useLayoutEffect(() => { + focusedModeRef.current = defaultMode; + }, [resourceKey, defaultMode]); + + // After every render, restore the DOM tabIndex to match focusedModeRef.current. + // This is necessary because the JSX tabIndex props derive from activeMode, which + // is correct for initial render and for renders triggered by selectMode(). However, + // when focus and selection differ (i.e. after arrow-key navigation, before Enter/Space), + // any unrelated re-render (copy-button click, parent update, etc.) will reconcile JSX + // tabIndex={activeMode === X} and overwrite the imperative tabIndex values set by + // focusTab(). useLayoutEffect fires synchronously after React's DOM mutations, before + // paint, so it corrects any such overwrite before the user sees it. It does not + // schedule another render — the two property writes are pure DOM mutations. + // No deps array: intentional. The correction must run after every render, not just mount. + // SSR-safe: useLayoutEffect is silently skipped on the server; the JSX tabIndex from + // activeMode provides the correct initial value (focusedModeRef.current === activeMode + // at mount). Strict Mode: runs twice on remount — both runs write the same values, + // no state mutation, no render triggered. + useLayoutEffect(() => { + if (previewTabRef.current) { + previewTabRef.current.tabIndex = focusedModeRef.current === "preview" ? 0 : -1; + } + if (sourceTabRef.current) { + sourceTabRef.current.tabIndex = focusedModeRef.current === "source" ? 0 : -1; + } + }); + const rawContent = editor.editing ? editor.session?.draft ?? "" : (typeof content === "string" ? content : ""); + const previewContent = useMemo(() => { if (!editor.editing) return rawContent; const lines = rawContent.split(/\r?\n/); @@ -87,7 +203,9 @@ export function MarkdownContentViewer({ const closingIndex = lines.findIndex((line, index) => index > 0 && line === "---"); return closingIndex < 0 ? rawContent : lines.slice(closingIndex + 1).join("\n").replace(/^\n/, ""); }, [editor.editing, rawContent]); + const hasContent = rawContent.trim().length > 0; + const renderedMarkdown = useMemo( () => ( @@ -96,6 +214,7 @@ export function MarkdownContentViewer({ ), [previewContent], ); + const handleCopy = async () => { if (!hasContent) return; try { @@ -110,33 +229,37 @@ export function MarkdownContentViewer({ const handleEdit = async () => { modeBeforeEditRef.current = activeMode; - setActiveMode("source"); + selectMode("source"); if (!await editor.beginEdit()) { - setActiveMode(modeBeforeEditRef.current); + selectMode(modeBeforeEditRef.current); } }; const handleCancel = () => { editor.discard(); - setActiveMode(modeBeforeEditRef.current); + selectMode(modeBeforeEditRef.current); }; const handleApply = async () => { if (await editor.save()) { - setActiveMode("preview"); + selectMode("preview"); } }; return (
-
+
) : null} + {/* Both tabs point aria-controls at this one panel: only the active view is + ever rendered inside (edit/preview/source/empty), so per-tab panel ids + would leave the inactive tab referencing an element not in the DOM. + Wrapping all branches — including empty state and editing textarea — + keeps every aria-controls reference resolvable at all times. + tabIndex=0 because the panel is a scroll container (viewerBodyStyle caps + its height), so keyboard users need to be able to focus and scroll it. + aria-busy signals to assistive tech that the content is loading/saving. */}
{activeMode === "source" && editing ? ( diff --git a/explorer/tests/markdownContentViewer.test.ts b/explorer/tests/markdownContentViewer.test.ts index 7bb544ea..e3bf7d7a 100644 --- a/explorer/tests/markdownContentViewer.test.ts +++ b/explorer/tests/markdownContentViewer.test.ts @@ -286,3 +286,335 @@ test("copy button always starts in un-copied state on initial render", () => { assert.equal(html.includes("Copy"), true, "Copy button must be present on initial render"); assert.equal(html.includes("Copied"), false, "Copied indicator must NOT be present on initial render"); }); + +// ─── #1117: complete ARIA tab/tabpanel relationship ───────────────────────── +// The tabs previously exposed role/aria-selected but never connected to the +// panel, so assistive tech could not tell which content the tabs controlled. +// These assertions read the rendered HTML, matching the aria-label precedent +// used by the GFM footnote tests above. + +/** Pull an attribute value out of the element carrying a given marker attribute. */ +function attrOf(html: string, elementMarker: string, attr: string): string | null { + const idx = html.indexOf(elementMarker); + if (idx === -1) return null; + const tagStart = html.lastIndexOf("<", idx); + const tag = html.slice(tagStart, html.indexOf(">", idx) + 1); + const m = tag.match(new RegExp(`${attr}="([^"]*)"`)); + return m ? m[1] : null; +} + +test("each tab is wired to the panel and the panel back to the active tab", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Node\n\nBody text.", + defaultMode: "preview", + })); + + const panelId = attrOf(html, 'role="tabpanel"', "id"); + assert.ok(panelId, "panel must carry an id"); + + // Both tabs must reference the panel that actually exists in the DOM. + const controls = [...html.matchAll(/aria-controls="([^"]*)"/g)].map((m) => m[1]); + assert.equal(controls.length, 2, "both tabs must declare aria-controls"); + for (const c of controls) { + assert.equal(c, panelId, "aria-controls must resolve to the rendered panel"); + } + + // The panel must be labelled by the *selected* tab. + const labelledBy = attrOf(html, 'role="tabpanel"', "aria-labelledby"); + const selectedTabId = attrOf(html, 'aria-selected="true"', "id"); + assert.ok(selectedTabId, "selected tab must carry an id"); + assert.equal(labelledBy, selectedTabId, "panel must be labelled by the selected tab"); +}); + +test("panel labelling follows the active tab in source mode", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Node\n\nBody text.", + defaultMode: "source", + })); + const labelledBy = attrOf(html, 'role="tabpanel"', "aria-labelledby"); + const selectedTabId = attrOf(html, 'aria-selected="true"', "id"); + // Assert both are present before comparing — otherwise null === null would + // make this pass against a component with no tab wiring at all. + assert.ok(labelledBy, "panel must declare aria-labelledby"); + assert.ok(selectedTabId, "selected tab must carry an id"); + assert.equal(labelledBy, selectedTabId); + assert.equal(selectedTabId.endsWith("-tab-source"), true, "source tab must be the selected one"); +}); + +// The empty state is a third render branch. If the panel only existed on the two +// content branches, aria-controls would dangle for empty nodes. +test("tabpanel is still rendered, and aria-controls still resolves, when empty", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { content: "" })); + assert.equal(html.includes("No content available for this node."), true); + const panelId = attrOf(html, 'role="tabpanel"', "id"); + assert.ok(panelId, "empty state must still render the tabpanel"); + const controls = [...html.matchAll(/aria-controls="([^"]*)"/g)].map((m) => m[1]); + assert.equal(controls.length, 2); + assert.deepEqual([...new Set(controls)], [panelId], "aria-controls must not dangle on the empty state"); +}); + +test("tablist is a single tab stop via roving tabindex", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Node", + defaultMode: "preview", + })); + const tabIndexes = [...html.matchAll(/role="tab"[^>]*/g)].map((m) => m[0].match(/tabindex="(-?\d+)"/)?.[1]); + assert.equal(tabIndexes.filter((t) => t === "0").length, 1, "exactly one tab may be reachable via Tab"); + assert.equal(tabIndexes.filter((t) => t === "-1").length, 1, "the other tab must be removed from tab order"); +}); + +test("ids are unique per instance so two mounted viewers cannot collide", () => { + const one = renderToString(React.createElement(MarkdownContentViewer, { content: "# A" })); + const two = renderToString(React.createElement( + "div", + null, + React.createElement(MarkdownContentViewer, { content: "# A" }), + React.createElement(MarkdownContentViewer, { content: "# B" }), + )); + assert.ok(attrOf(one, 'role="tabpanel"', "id")); + const panelIds = [...two.matchAll(/role="tabpanel" id="([^"]*)"/g)].map((m) => m[1]); + assert.equal(panelIds.length, 2, "both viewers must render a panel"); + assert.notEqual(panelIds[0], panelIds[1], "panel ids must differ between instances"); +}); + +import { resolveTabNavigation } from "../src/workspaces/GraphWorkspace/MarkdownContentViewer.tsx"; + +// ─── #1117 / Qodo: roving-tabindex navigation logic ───────────────────────── +// resolveTabNavigation is the pure function that drives handleTabKeyDown. +// Testing it directly gives us coverage of the navigation contract without +// needing a live DOM or synthetic keyboard events. + +// ── ArrowRight moves focus forward, wraps at end ──────────────────────────── +test("ArrowRight from preview moves focus to source without wrapping", () => { + assert.equal(resolveTabNavigation("preview", "ArrowRight"), "source"); +}); + +test("ArrowRight from source wraps back to preview", () => { + // With only two tabs the rightmost tab wraps to the first. + assert.equal(resolveTabNavigation("source", "ArrowRight"), "preview"); +}); + +// ── ArrowLeft moves focus backward, wraps at start ────────────────────────── +test("ArrowLeft from source moves focus to preview without wrapping", () => { + assert.equal(resolveTabNavigation("source", "ArrowLeft"), "preview"); +}); + +test("ArrowLeft from preview wraps back to source", () => { + // The leftmost tab wraps to the last. + assert.equal(resolveTabNavigation("preview", "ArrowLeft"), "source"); +}); + +// ── Home and End always resolve to the boundary tabs ──────────────────────── +test("Home always moves focus to the first tab (preview)", () => { + assert.equal(resolveTabNavigation("preview", "Home"), "preview", "Home on first tab stays at first"); + assert.equal(resolveTabNavigation("source", "Home"), "preview", "Home on last tab jumps to first"); +}); + +test("End always moves focus to the last tab (source)", () => { + assert.equal(resolveTabNavigation("source", "End"), "source", "End on last tab stays at last"); + assert.equal(resolveTabNavigation("preview", "End"), "source", "End on first tab jumps to last"); +}); + +// ── Non-navigation keys return null so the handler can bail out ───────────── +test("non-navigation keys return null so keydown handler does not move focus", () => { + for (const key of ["Enter", "Space", " ", "Tab", "Escape", "a", "F1"]) { + assert.equal( + resolveTabNavigation("preview", key), + null, + `key "${key}" must return null`, + ); + assert.equal( + resolveTabNavigation("source", key), + null, + `key "${key}" on source must return null`, + ); + } +}); + +// ── Arrow navigation does NOT change activeMode (manual activation) ────────── +// resolveTabNavigation only returns the target for focus movement. The caller +// (focusTab) imperatively updates tabIndex and moves DOM focus without calling +// setActiveMode. We verify the contract: resolveTabNavigation never returns a +// value that could be interpreted as "activate" — it just returns a tab identity. +// The absence of a setActiveMode call in focusTab is what enforces manual +// activation; these tests confirm the logic layer does not accidentally activate. +test("resolveTabNavigation return value is purely a focus target, never an activation signal", () => { + // A real activation calls setActiveMode. resolveTabNavigation just computes + // the next focused tab. If the caller only updates focusedModeRef + DOM tabIndex, + // activeMode remains unchanged. This test asserts the function's return contract. + const result = resolveTabNavigation("preview", "ArrowRight"); + assert.equal(typeof result, "string", "returns a string tab name when key is a navigation key"); + assert.notEqual(result, null, "non-null means 'move focus here'"); + // The returned value is a valid tab mode, not a command to switch content. + assert.ok(result === "preview" || result === "source"); +}); + +// ── Roving tabindex initial state for defaultMode='source' ────────────────── +// The existing 'tablist is a single tab stop' test only checks defaultMode='preview'. +// When the component starts in source mode the source tab must start at tabIndex 0. +test("roving tabindex initial state is correct when defaultMode is source", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Node", + defaultMode: "source", + })); + const tabIndexes = [...html.matchAll(/role="tab"[^>]*/g)].map( + (m) => m[0].match(/tabindex="(-?\d+)"/)?.[1], + ); + // There are exactly two tabs; one must be 0, the other -1. + assert.equal(tabIndexes.filter((t) => t === "0").length, 1, "exactly one tab is reachable via Tab"); + assert.equal(tabIndexes.filter((t) => t === "-1").length, 1, "the other tab is removed from tab order"); + + // The source tab specifically must hold tabIndex 0 (it is the focused/active one). + // We identify the source tab by its id suffix and verify its tabindex. + const sourceTabMatch = [...html.matchAll(/role="tab"[^>]*/g)].find((m) => + m[0].includes("-tab-source"), + ); + assert.ok(sourceTabMatch, "source tab must be present in rendered HTML"); + assert.equal( + sourceTabMatch[0].match(/tabindex="(-?\d+)"/)?.[1], + "0", + "source tab must have tabIndex 0 when defaultMode is source", + ); +}); + +// ── aria-labelledby correctness for each defaultMode ──────────────────────── +// These tests verify the static wiring; the existing tests cover preview and +// source modes, so these act as a consolidated regression check that both +// directions of the panel labelling contract hold after the refactor. +test("panel aria-labelledby matches the selected tab in preview mode after refactor", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Refactor check", + defaultMode: "preview", + })); + const labelledBy = attrOf(html, 'role="tabpanel"', "aria-labelledby"); + const selectedTabId = attrOf(html, 'aria-selected="true"', "id"); + assert.ok(labelledBy, "panel must carry aria-labelledby after refactor"); + assert.ok(selectedTabId, "a tab must be aria-selected=true after refactor"); + assert.equal(labelledBy, selectedTabId, "panel must be labelled by the selected tab"); + assert.ok(selectedTabId.endsWith("-tab-preview"), "preview tab must be selected"); +}); + +test("panel aria-labelledby matches the selected tab in source mode after refactor", () => { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Refactor check", + defaultMode: "source", + })); + const labelledBy = attrOf(html, 'role="tabpanel"', "aria-labelledby"); + const selectedTabId = attrOf(html, 'aria-selected="true"', "id"); + assert.ok(labelledBy, "panel must carry aria-labelledby after refactor"); + assert.ok(selectedTabId, "a tab must be aria-selected=true after refactor"); + assert.equal(labelledBy, selectedTabId, "panel must be labelled by the selected tab"); + assert.ok(selectedTabId.endsWith("-tab-source"), "source tab must be selected"); +}); + +// ── Qodo performance regression: focusedModeRef is a ref, not state ────────── +// The confirmed bug was: setFocusedMode (useState setter) caused a re-render +// on every arrow keypress, which triggered react-markdown's full parse+runSync +// cycle even though activeMode did not change. +// +// The fix uses useRef instead of useState for the focused-mode tracking. Refs +// do not schedule re-renders when mutated. We cannot directly count React +// renders inside renderToString (it runs synchronously, once). What we CAN +// verify is the structural invariant that makes the fix work: +// +// 1. The component renders identically for the same props on successive +// renderToString calls (no hidden state that would differ if focusedMode +// were state vs ref — both start at defaultMode on fresh mount). +// 2. The tabIndex JSX prop reads from focusedModeRef.current which equals +// defaultMode on initial render. This is the same output the old code +// produced, so no regression in SSR output. +// +// Full verification of "arrow key press does NOT trigger ReactMarkdown.parse()" +// requires a live DOM + render-count instrumentation. That test belongs in an +// interactive framework (Playwright component test or jsdom + Testing Library) +// which is not installed in this project. The structural guarantee provided by +// the ref-based implementation is documented here for that future test to pin. +test("successive renderToString calls produce identical tabIndex output (ref parity with state)", () => { + const props = { content: "# Perf node\n\n" + "row. ".repeat(200), defaultMode: "preview" as const }; + const first = renderToString(React.createElement(MarkdownContentViewer, props)); + const second = renderToString(React.createElement(MarkdownContentViewer, props)); + // Both renders start with a fresh ref initialised to defaultMode, so output + // must be byte-for-byte identical (modulo React's useId counter which advances + // per call — we compare structure, not the specific id values). + const extractTabIndexes = (html: string) => + [...html.matchAll(/role="tab"[^>]*/g)].map((m) => m[0].match(/tabindex="(-?\d+)"/)?.[1]); + assert.deepEqual( + extractTabIndexes(first), + extractTabIndexes(second), + "tabIndex values must be the same on every fresh mount with the same defaultMode", + ); + // Verify content is actually rendered (not an empty-state shortcut). + assert.ok(first.includes("Perf node"), "markdown content must be rendered"); +}); + +// ── Regression guard: tabIndex-reset-on-re-render (useLayoutEffect fix) ────── +// +// The adversarial review identified a concrete bug: after ArrowRight moves focus +// to Source while Preview remains selected (activeMode='preview'), any subsequent +// React re-render applied JSX tabIndex={activeMode === X} and overwrote the +// imperative tabIndex values set by focusTab(), reverting focus tracking to the +// selection state. +// +// Fix: useLayoutEffect(() => { ... }) with no deps array, which runs after every +// React render and restores focusedModeRef.current to the DOM before paint. +// +// WHY THIS CANNOT BE TESTED WITH renderToString: +// The fix is a client-side DOM mutation applied by useLayoutEffect. On the +// server, useLayoutEffect is silently skipped (React design: effects do not run +// during SSR). renderToString produces only the initial HTML, which correctly +// reflects activeMode === focusedModeRef.current at mount time. It cannot +// simulate: (a) a keydown event that calls focusTab(), (b) a subsequent +// state-update re-render, or (c) the useLayoutEffect correction after that +// render. The full sequence requires a live DOM with React hydrated and event +// dispatch — either jsdom + React Testing Library, or Playwright component +// tests. Neither is installed in this project. +// +// WHAT WE CAN VERIFY (SSR-compatible proxies): +// 1. The fix is mechanical: useLayoutEffect reads focusedModeRef.current and +// writes it unconditionally to the DOM. The only way it fails is if: +// (a) focusedModeRef.current is wrong — covered by the navigation logic tests. +// (b) useLayoutEffect is not called — impossible if it is in the component body +// unconditionally. +// (c) The ref assignment in focusTab() is skipped — covered by the imperative +// DOM update tests (focusTab sets the ref before calling .focus()). +// 2. We verify the structural guarantee: on initial render focusedModeRef.current +// equals defaultMode, so JSX and useLayoutEffect agree, and no visible change +// occurs. This is the only SSR-observable aspect of the fix. +// +// TRACKING: Add a jsdom/Playwright test for the full sequence as a follow-up. +// The specific scenario to pin: +// Preview selected → focusTab('source') → re-render (e.g. setCopied) → +// useLayoutEffect runs → sourceTab.tabIndex === 0 AND previewTab.tabIndex === -1. + +test("tabIndex regression (SSR proxy): initial focusedModeRef matches defaultMode so JSX and useLayoutEffect agree on mount", () => { + // On initial mount focusedModeRef.current = defaultMode and activeMode = defaultMode, + // so both the JSX tabIndex expression and the useLayoutEffect correction write + // identical values. There is no visible disagreement at first render. + // This confirms the static foundation the fix relies on. + for (const mode of ["preview", "source"] as const) { + const html = renderToString(React.createElement(MarkdownContentViewer, { + content: "# Node", + defaultMode: mode, + })); + const tabs = [...html.matchAll(/role="tab"[^>]*/g)]; + assert.equal(tabs.length, 2, `${mode}: both tab buttons must be present`); + + const focusedTab = tabs.find((m) => m[0].includes(`-tab-${mode}`)); + const otherTab = tabs.find((m) => !m[0].includes(`-tab-${mode}`)); + assert.ok(focusedTab, `${mode}: the ${mode} tab must be present`); + assert.ok(otherTab, `${mode}: the other tab must be present`); + + // The tab matching defaultMode must have tabIndex=0 (focused/active at mount). + assert.equal( + focusedTab[0].match(/tabindex="(-?\d+)"/)?.[1], + "0", + `${mode}: ${mode} tab must start as the single Tab stop`, + ); + // The other tab must have tabIndex=-1 (removed from tab order at mount). + assert.equal( + otherTab[0].match(/tabindex="(-?\d+)"/)?.[1], + "-1", + `${mode}: the other tab must be removed from tab order at mount`, + ); + } +}); diff --git a/integrations/google_adk/README.md b/integrations/google_adk/README.md new file mode 100644 index 00000000..48df72d6 --- /dev/null +++ b/integrations/google_adk/README.md @@ -0,0 +1,270 @@ +# Semantica Google ADK Integration + +Google ADK integration for [Semantica](https://github.com/semantica-agi/semantica). + +This integration provides: + +- Google ADK `FunctionTool` wrappers for Semantica's knowledge graph +- Decision recording and querying tools +- A graph-backed Google ADK `BaseSessionService` +- Shared `ContextGraph` state across ADK agents and sub-agents + +Google ADK is an optional dependency. + +## Installation + +Install Semantica with the Google ADK integration: + +```bash +pip install semantica[google-adk] +``` + +Or install Google ADK separately: + +```bash +pip install google-adk +``` + +## Knowledge Graph Tools + +Create a shared `ContextGraph` and expose it through ADK tools: + +```python +from google.adk.agents import Agent + +from semantica.context import ContextGraph +from integrations.google_adk import semantica_kg_tools + + +graph = ContextGraph() + +agent = Agent( + name="researcher", + model="gemini-2.0-flash", + tools=semantica_kg_tools(graph), +) +``` + +The tool factory provides: + +- `extract_entities` +- `extract_relations` +- `add_to_shared_graph` +- `query_shared_graph` + +The graph passed to `semantica_kg_tools()` is shared by all returned tools. + +## Decision Tools + +Decision intelligence can use the same graph: + +```python +from integrations.google_adk import semantica_decision_tools + +decision_tools = semantica_decision_tools(graph) + +agent = Agent( + name="decision_agent", + model="gemini-2.0-flash", + tools=decision_tools, +) +``` + +The returned tools provide: + +- `record_shared_decision` +- `query_shared_decisions` + +This allows decisions made by one agent to be queried later by another agent using the same `ContextGraph`. + +## Combining Knowledge and Decision Tools + +Both tool groups can be supplied to the same ADK agent: + +```python +from google.adk.agents import Agent + +from semantica.context import ContextGraph +from integrations.google_adk import ( + semantica_kg_tools, + semantica_decision_tools, +) + + +graph = ContextGraph() + +tools = ( + semantica_kg_tools(graph) + + semantica_decision_tools(graph) +) + +agent = Agent( + name="researcher", + model="gemini-2.0-flash", + tools=tools, +) +``` + +This gives the agent access to both the shared knowledge graph and decision history. + +## Graph-Backed Session Service + +`SemanticaSessionService` implements Google ADK's session service interface while storing session information in a Semantica `ContextGraph`. + +```python +from semantica.context import ContextGraph +from integrations.google_adk import SemanticaSessionService + + +graph = ContextGraph() + +session_service = SemanticaSessionService(graph) +``` + +The same graph can be shared with the KG and decision tools: + +```python +from google.adk.agents import Agent + +from semantica.context import ContextGraph +from integrations.google_adk import ( + SemanticaSessionService, + semantica_kg_tools, + semantica_decision_tools, +) + + +graph = ContextGraph() + +session_service = SemanticaSessionService(graph) + +tools = ( + semantica_kg_tools(graph) + + semantica_decision_tools(graph) +) + +agent = Agent( + name="researcher", + model="gemini-2.0-flash", + tools=tools, +) +``` + +Session information and tool-generated knowledge can therefore share the same graph-backed context store. + +## Optional Dependency + +Importing the integration does not require Google ADK to be installed: + +```python +from integrations.google_adk import ADK_AVAILABLE + +print(ADK_AVAILABLE) +``` + +If Google ADK is unavailable, attempting to construct ADK-specific tools or the session service raises an informative `ImportError`. + +## Shared ContextGraph + +A major purpose of this integration is allowing multiple ADK agents or sub-agents to share one Semantica graph: + +```text + ContextGraph + | + +--------------+--------------+ + | | | + Researcher Planner Reviewer + Agent Agent Agent + | | | + +--------------+--------------+ + | + Shared knowledge + + decisions + + session state +``` + +This makes information extracted during an earlier stage of an agent workflow available to later stages without requiring the information to be extracted again. + +## Example Workflow + +```python +from google.adk.agents import SequentialAgent, Agent + +from semantica.context import ContextGraph +from integrations.google_adk import ( + semantica_kg_tools, + semantica_decision_tools, +) + + +graph = ContextGraph() + +researcher = Agent( + name="researcher", + model="gemini-2.0-flash", + tools=semantica_kg_tools(graph), +) + +planner = Agent( + name="planner", + model="gemini-2.0-flash", + tools=( + semantica_kg_tools(graph) + + semantica_decision_tools(graph) + ), +) + +workflow = SequentialAgent( + name="research_workflow", + sub_agents=[ + researcher, + planner, + ], +) +``` + +The researcher can add entities and relationships to the graph. The planner can then query the same graph and record decisions against it. + +## API + +### `semantica_kg_tools(graph=None)` + +Returns Google ADK `FunctionTool` instances for Semantica knowledge graph operations. + +### `semantica_decision_tools(graph=None)` + +Returns Google ADK `FunctionTool` instances for recording and querying decisions. + +### `SemanticaSessionService(graph=None)` + +Creates a Google ADK-compatible session service backed by a Semantica `ContextGraph`. + +### `ADK_AVAILABLE` + +Boolean indicating whether Google ADK is installed. + +### `__version__` + +Version of the Semantica Google ADK integration. + +## Development + +Run the Google ADK integration tests with: + +```bash +pytest tests/integrations/google_adk -v +``` + +Tests that require Google ADK should use: + +```python +import pytest + +pytest.importorskip("google.adk") +``` + +This keeps the integration optional for environments that do not install Google ADK. + +## License + +This integration follows the license of the Semantica project. \ No newline at end of file diff --git a/integrations/google_adk/__init__.py b/integrations/google_adk/__init__.py new file mode 100644 index 00000000..5cc1a65b --- /dev/null +++ b/integrations/google_adk/__init__.py @@ -0,0 +1,50 @@ +""" +Google ADK integration for Semantica. + +Google ADK is an optional dependency. The integration can be imported +without google-adk installed, but ADK-specific functionality requires it. +""" + +from __future__ import annotations + +try: + import google.adk # noqa: F401 + + ADK_AVAILABLE = True +except ImportError: + ADK_AVAILABLE = False + + +from .kg_tools import ( + extract_entities, + extract_relations, + add_to_graph, + query_graph, + semantica_kg_tools, +) + +from .decision_tools import ( + record_decision, + query_decisions, + semantica_decision_tools, +) + +from .session_service import SemanticaSessionService + + +__version__ = "0.1.0" + + +__all__ = [ + "ADK_AVAILABLE", + "__version__", + "extract_entities", + "extract_relations", + "add_to_graph", + "query_graph", + "semantica_kg_tools", + "record_decision", + "query_decisions", + "semantica_decision_tools", + "SemanticaSessionService", +] \ No newline at end of file diff --git a/integrations/google_adk/_shared.py b/integrations/google_adk/_shared.py new file mode 100644 index 00000000..ab2876e8 --- /dev/null +++ b/integrations/google_adk/_shared.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import threading +from typing import Any, Dict + +# Shared between kg_tools.py and decision_tools.py so that a ContextGraph +# passed to both semantica_kg_tools() and semantica_decision_tools() (the +# combined-tools use case documented in the README) is locked and defaulted +# consistently across both tool sets rather than each module keeping its +# own independent registry. + +_graph_locks_guard = threading.Lock() +_graph_locks: Dict[int, threading.RLock] = {} + + +def graph_lock(graph: Any) -> threading.RLock: + """Return the mutation lock associated with a ContextGraph instance.""" + key = id(graph) + + with _graph_locks_guard: + lock = _graph_locks.get(key) + + if lock is None: + lock = threading.RLock() + _graph_locks[key] = lock + + return lock + + +_default_graph: Any = None +_default_graph_lock = threading.Lock() + + +def get_default_graph() -> Any: + """Create or return the cached process-local default ContextGraph.""" + global _default_graph + + if _default_graph is None: + with _default_graph_lock: + if _default_graph is None: + from semantica.context import ContextGraph + + _default_graph = ContextGraph() + + return _default_graph diff --git a/integrations/google_adk/decision_tools.py b/integrations/google_adk/decision_tools.py new file mode 100644 index 00000000..c296a626 --- /dev/null +++ b/integrations/google_adk/decision_tools.py @@ -0,0 +1,305 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Any, List, Optional +import uuid + +from ._shared import graph_lock as _graph_lock +from ._shared import get_default_graph as _get_default_graph + + +try: + from google.adk.tools import FunctionTool + + ADK_AVAILABLE = True +except ImportError: + FunctionTool = None + ADK_AVAILABLE = False + + +def _get_decision_models() -> Any: + """Import Semantica decision models lazily.""" + from semantica.context.decision_models import Decision + + return Decision + + +def _get_decision_recorder(graph: Any) -> Any: + """Create a DecisionRecorder backed by the supplied graph.""" + from semantica.context import DecisionRecorder + + return DecisionRecorder(graph_store=graph) + + +def _decision_to_dict(decision: Any) -> dict: + """Convert a Semantica Decision model into a serializable dictionary.""" + if hasattr(decision, "model_dump"): + return decision.model_dump() + + if hasattr(decision, "dict"): + return decision.dict() + + if isinstance(decision, dict): + return decision + + return { + key: value + for key, value in vars(decision).items() + if not key.startswith("_") + } + + +def record_decision( + category: str, + scenario: str, + reasoning: str, + outcome: str, + confidence: float = 1.0, + decision_maker: str = "agent", + entities: Optional[List[str]] = None, + source_documents: Optional[List[str]] = None, +) -> dict: + """ + Record a decision using Semantica's DecisionRecorder. + + Args: + category: Decision category such as "research", "planning", or + "approval". + scenario: Situation in which the decision was made. + reasoning: Explanation for the decision. + outcome: Result or selected action. + confidence: Confidence score between 0 and 1. + decision_maker: Agent, user, or system responsible for the decision. + entities: Optional entity IDs related to the decision. + source_documents: Optional source document IDs supporting the decision. + + Returns: + Dictionary containing the recorded decision ID and decision metadata. + """ + return _record_decision( + category=category, + scenario=scenario, + reasoning=reasoning, + outcome=outcome, + confidence=confidence, + decision_maker=decision_maker, + entities=entities or [], + source_documents=source_documents or [], + graph=_get_default_graph(), + ) + + +def _record_decision( + category: str, + scenario: str, + reasoning: str, + outcome: str, + confidence: float, + decision_maker: str, + entities: List[str], + source_documents: List[str], + graph: Any, +) -> dict: + """Internal implementation of decision recording.""" + try: + confidence = max(0.0, min(1.0, float(confidence))) + + Decision = _get_decision_models() + + decision = Decision( + decision_id=str(uuid.uuid4()), + category=category, + scenario=scenario, + reasoning=reasoning, + outcome=outcome, + confidence=confidence, + decision_maker=decision_maker, + timestamp=datetime.now(), + ) + + recorder = _get_decision_recorder(graph) + + with _graph_lock(graph): + decision_id = recorder.record_decision( + decision=decision, + entities=entities, + source_documents=source_documents, + ) + + return { + "decision_id": decision_id, + "category": category, + "scenario": scenario, + "outcome": outcome, + "confidence": confidence, + "decision_maker": decision_maker, + } + + except Exception as exc: + return { + "decision_id": "", + "error": str(exc), + } + + +def query_decisions(query: str) -> dict: + """ + Query previously recorded decisions by keyword. + """ + return _query_decisions(query, _get_default_graph()) + +def _query_decisions(query: str, graph: Any) -> dict: + """Internal decision query implementation.""" + if not isinstance(query, str): + return { + "query": query, + "decisions": [], + "count": 0, + "error": "query must be a string", + } + + query = query.strip() + + if not query: + return { + "query": query, + "decisions": [], + "count": 0, + } + + try: + query_lower = query.lower() + decisions = [] + seen = set() + + for node in graph.find_nodes() or []: + if not isinstance(node, dict): + continue + + node_type = node.get("type") + + if str(node_type).lower() != "decision": + continue + + node_id = str(node.get("id") or "") + + if not node_id or node_id in seen: + continue + + metadata = node.get("metadata") or {} + + category = metadata.get("category", "") + scenario = metadata.get("scenario", "") + reasoning = metadata.get("reasoning", "") + outcome = metadata.get("outcome", "") + decision_maker = metadata.get("decision_maker", "") + + searchable = " ".join( + str(value or "") + for value in ( + node_id, + category, + scenario, + reasoning, + outcome, + decision_maker, + ) + ).lower() + + if query_lower not in searchable: + continue + + seen.add(node_id) + + decisions.append( + { + "decision_id": node_id, + "category": str(category or ""), + "scenario": str(scenario or ""), + "reasoning": str(reasoning or "")[:1000], + "outcome": str(outcome or ""), + "decision_maker": str( + decision_maker or "" + ), + } + ) + + return { + "query": query, + "decisions": decisions, + "count": len(decisions), + } + + except Exception as exc: + return { + "query": query, + "decisions": [], + "count": 0, + "error": str(exc), + } + +def semantica_decision_tools( + graph: Optional[Any] = None, +) -> List[Any]: + """ + Return Google ADK FunctionTools bound to a shared ContextGraph. + + Args: + graph: + Optional ContextGraph shared by the ADK agent and other + Semantica tools. + + Returns: + ADK FunctionTools for recording and querying decisions. + + Raises: + ImportError: + If google-adk is not installed. + """ + if not ADK_AVAILABLE or FunctionTool is None: + raise ImportError( + "Google ADK is required for semantica_decision_tools(). " + "Install it with: pip install semantica[google-adk]" + ) + + shared_graph = graph if graph is not None else _get_default_graph() + + def record_shared_decision( + category: str, + scenario: str, + reasoning: str, + outcome: str, + confidence: float = 1.0, + decision_maker: str = "agent", + entities: Optional[List[str]] = None, + source_documents: Optional[List[str]] = None, + ) -> dict: + """Record a decision in the shared Semantica knowledge graph.""" + return _record_decision( + category=category, + scenario=scenario, + reasoning=reasoning, + outcome=outcome, + confidence=confidence, + decision_maker=decision_maker, + entities=entities or [], + source_documents=source_documents or [], + graph=shared_graph, + ) + + def query_shared_decisions(query: str) -> dict: + """Query decisions stored in the shared Semantica knowledge graph.""" + return _query_decisions(query, shared_graph) + + return [ + FunctionTool(record_shared_decision), + FunctionTool(query_shared_decisions), + ] + + +__all__ = [ + "ADK_AVAILABLE", + "record_decision", + "query_decisions", + "semantica_decision_tools", +] \ No newline at end of file diff --git a/integrations/google_adk/kg_tools.py b/integrations/google_adk/kg_tools.py new file mode 100644 index 00000000..d125d9c4 --- /dev/null +++ b/integrations/google_adk/kg_tools.py @@ -0,0 +1,659 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Optional + +from ._shared import graph_lock as _graph_lock +from ._shared import get_default_graph as _get_default_graph + +try: + from google.adk.tools import FunctionTool + + ADK_AVAILABLE = True +except ImportError: + FunctionTool = None # type: ignore + ADK_AVAILABLE = False + + +def _get_ner_extractor() -> Any: + """Create Semantica default NER extractor.""" + from semantica.semantic_extract import NERExtractor + + return NERExtractor() + + +def _get_relation_extractor() -> Any: + """Create Semantica default relation extractor.""" + from semantica.semantic_extract import RelationExtractor + + return RelationExtractor() + + +def _first_string(obj: Any, attributes: tuple[str, ...]) -> str: + """Return the first non-empty string from an object or dictionary.""" + if obj is None: + return "" + + if isinstance(obj, dict): + for attribute in attributes: + value = obj.get(attribute) + + if isinstance(value, str) and value.strip(): + return value.strip() + + return "" + + for attribute in attributes: + value = getattr(obj, attribute, None) + + if isinstance(value, str) and value.strip(): + return value.strip() + + return "" + + +def _entity_name(entity: Any) -> str: + """Return a best-effort name for an extracted entity.""" + return _first_string( + entity, + ( + "name", + "text", + "label", + "node_id", + "id", + ), + ) + + +def _entity_type(entity: Any) -> str: + """Return a best-effort type for an extracted entity.""" + return ( + _first_string( + entity, + ( + "type", + "label", + ), + ) + or "Entity" + ) + + +def _entity_confidence(entity: Any) -> float: + """Normalize an entity confidence value.""" + try: + confidence = ( + entity.get("confidence") + if isinstance(entity, dict) + else getattr(entity, "confidence", None) + ) + + if confidence is None: + return 1.0 + + return round(float(confidence), 4) + + except (TypeError, ValueError): + return 1.0 + + +def _relation_source(relation: Any) -> str: + """Return the source entity of an extracted relation.""" + source = _first_string( + relation, + ( + "source", + "source_id", + ), + ) + + if source: + return source + + if isinstance(relation, dict): + return _entity_name(relation.get("subject")) + + return _entity_name(getattr(relation, "subject", None)) + + +def _relation_target(relation: Any) -> str: + """Return the target entity of an extracted relation.""" + target = _first_string( + relation, + ( + "target", + "target_id", + ), + ) + + if target: + return target + + if isinstance(relation, dict): + return _entity_name(relation.get("object")) + + return _entity_name(getattr(relation, "object", None)) + + +def _relation_type(relation: Any) -> str: + """Return the relation predicate/type.""" + return ( + _first_string( + relation, + ( + "type", + "relation", + "predicate", + ), + ) + or "related_to" + ) + + +def _json_safe(value: Any) -> Any: + """ + Convert common Semantica objects into values suitable for ADK tool output. + + ADK tools should return values that can be serialized into the tool + response sent back to the model. + """ + if value is None or isinstance(value, (str, int, float, bool)): + return value + + if isinstance(value, dict): + return { + str(key): _json_safe(item) + for key, item in value.items() + } + + if isinstance(value, (list, tuple, set)): + return [_json_safe(item) for item in value] + + if hasattr(value, "to_dict"): + try: + return _json_safe(value.to_dict()) + except Exception: + pass + + if hasattr(value, "model_dump"): + try: + return _json_safe(value.model_dump()) + except Exception: + pass + + return str(value) + + +def extract_entities(text: str) -> dict: + """Extract named entities from text using Semantica's NER pipeline.""" + if not isinstance(text, str): + return { + "entities": [], + "count": 0, + "error": "text must be a string", + } + + try: + extractor = _get_ner_extractor() + raw_entities = extractor.extract_entities(text) or [] + + entities: List[Dict[str, Any]] = [] + + for entity in raw_entities: + name = _entity_name(entity) + + if not name: + continue + + entities.append( + { + "name": name, + "type": _entity_type(entity), + "confidence": _entity_confidence(entity), + } + ) + + return { + "entities": entities, + "count": len(entities), + } + + except Exception as exc: + return { + "entities": [], + "count": 0, + "error": str(exc), + } + + +def extract_relations(text: str) -> dict: + """Extract relationships between entities from text using Semantica.""" + if not isinstance(text, str): + return { + "relations": [], + "count": 0, + "error": "text must be a string", + } + + try: + ner_extractor = _get_ner_extractor() + entities = ner_extractor.extract_entities(text) + + relation_extractor = _get_relation_extractor() + raw_relations = relation_extractor.extract_relations(text, entities=entities) or [] + + relations: List[Dict[str, Any]] = [] + + for relation in raw_relations: + source = _relation_source(relation) + target = _relation_target(relation) + + if not source or not target: + continue + + relations.append( + { + "source": source, + "relation": _relation_type(relation), + "target": target, + "confidence": _entity_confidence(relation), + } + ) + + return { + "relations": relations, + "count": len(relations), + } + + except Exception as exc: + return { + "relations": [], + "count": 0, + "error": str(exc), + } + + +def add_to_graph(text: str) -> dict: + """ + Extract entities and relationships from text and add them to a ContextGraph. + + This standalone function uses a process-local default graph. For a shared + graph across ADK agents, use ``semantica_kg_tools(graph=shared_graph)``. + """ + return _add_to_graph(text, _get_default_graph()) + + +def query_graph(query: str) -> dict: + """ + Query the shared Semantica knowledge graph by keyword. + + This standalone function uses a process-local default graph. For a shared + graph, use ``semantica_kg_tools(graph=shared_graph)``. + """ + return _query_graph(query, _get_default_graph()) + + +def _add_to_graph(text: str, graph: Any) -> dict: + """Internal graph mutation implementation.""" + if not isinstance(text, str): + return { + "nodes_added": 0, + "edges_added": 0, + "error": "text must be a string", + } + + try: + ner_extractor = _get_ner_extractor() + relation_extractor = _get_relation_extractor() + + nodes_added = 0 + edges_added = 0 + + with _graph_lock(graph): + existing_nodes = set() + + for node in graph.find_nodes() or []: + if isinstance(node, dict): + node_id = node.get("id") or node.get("node_id") + else: + node_id = getattr( + node, + "id", + getattr(node, "node_id", None), + ) + + if node_id: + existing_nodes.add(str(node_id)) + + existing_edges = set() + + for edge in graph.find_edges() or []: + if isinstance(edge, dict): + source = edge.get("source") or edge.get("source_id") + target = edge.get("target") or edge.get("target_id") + edge_type = edge.get("type") or edge.get("edge_type") + else: + source = getattr( + edge, + "source_id", + getattr(edge, "source", None), + ) + target = getattr( + edge, + "target_id", + getattr(edge, "target", None), + ) + edge_type = getattr( + edge, + "edge_type", + getattr(edge, "type", None), + ) + + if source and target: + existing_edges.add( + ( + str(source), + str(edge_type or "related_to"), + str(target), + ) + ) + + raw_entities = ner_extractor.extract_entities(text) or [] + + entities: List[Any] = [] + seen_entities = set() + + for entity in raw_entities: + name = _entity_name(entity) + entity_type = _entity_type(entity) + + if not name or name in seen_entities: + continue + + seen_entities.add(name) + entities.append(entity) + + if name in existing_nodes: + continue + + try: + added = graph.add_node( + node_id=name, + node_type=entity_type, + ) + + if added: + nodes_added += 1 + existing_nodes.add(name) + + except Exception: + # Do not fail the entire tool because one node could not + # be inserted. + continue + + raw_relations = relation_extractor.extract_relations( + text, + entities=entities, + ) or [] + + for relation in raw_relations: + source = _relation_source(relation) + target = _relation_target(relation) + relation_type = _relation_type(relation) + + if not source or not target: + continue + + edge_key = ( + source, + relation_type, + target, + ) + + if edge_key in existing_edges: + continue + + try: + added = graph.add_edge( + source_id=source, + target_id=target, + edge_type=relation_type, + ) + + if added: + edges_added += 1 + existing_edges.add(edge_key) + + except Exception: + continue + + return { + "nodes_added": nodes_added, + "edges_added": edges_added, + } + + except Exception as exc: + return { + "nodes_added": 0, + "edges_added": 0, + "error": str(exc), + } + + +def _query_graph(query: str, graph: Any) -> dict: + """Internal graph query implementation.""" + if not isinstance(query, str): + return { + "query": query, + "results": [], + "count": 0, + "error": "query must be a string", + } + + query = query.strip() + + if not query: + return { + "query": query, + "results": [], + "count": 0, + } + + try: + results: List[Dict[str, Any]] = [] + seen = set() + + # Prefer ContextGraph.query() when available because it can provide + # richer semantic/structural results. + query_method = getattr(graph, "query", None) + + if callable(query_method): + try: + matches = query_method(query) or [] + + for match in matches: + if not isinstance(match, dict): + continue + + node = match.get("node") or {} + + if not isinstance(node, dict): + node = _json_safe(node) + + node_id = ( + node.get("id") + or node.get("node_id") + or match.get("id") + ) + + if not node_id: + continue + + node_id = str(node_id) + + if node_id in seen: + continue + + seen.add(node_id) + + results.append( + { + "id": node_id, + "type": ( + node.get("type") + or node.get("node_type") + or "" + ), + "content": str( + match.get("content") + or node.get("content") + or ( + node.get("properties") or {} + ).get("content", "") + )[:500], + "score": round( + float(match.get("score") or 0.0), + 4, + ), + } + ) + + except Exception: + # Fall back to deterministic keyword search below. + pass + + # Deterministic fallback/search enrichment. + query_lower = query.lower() + + for node in graph.find_nodes() or []: + if isinstance(node, dict): + node_id = ( + node.get("id") + or node.get("node_id") + or "" + ) + node_type = ( + node.get("type") + or node.get("node_type") + or "" + ) + + properties = node.get("properties") or {} + + content = ( + node.get("content") + or properties.get("content") + or "" + ) + + else: + node_id = getattr( + node, + "id", + getattr(node, "node_id", ""), + ) + node_type = getattr( + node, + "node_type", + getattr(node, "type", ""), + ) + content = getattr(node, "content", "") + + node_id = str(node_id or "") + node_type = str(node_type or "") + content = str(content or "") + + if not node_id or node_id in seen: + continue + + haystack = " ".join( + ( + node_id, + node_type, + content, + ) + ).lower() + + if query_lower in haystack: + seen.add(node_id) + + results.append( + { + "id": node_id, + "type": node_type, + "content": content[:500], + "score": 1.0, + } + ) + + return { + "query": query, + "results": results, + "count": len(results), + } + + except Exception as exc: + return { + "query": query, + "results": [], + "count": 0, + "error": str(exc), + } + + +def semantica_kg_tools( + graph: Optional[Any] = None, +) -> List[Any]: + """ + Return Google ADK FunctionTools bound to a shared ContextGraph instance. + + Args: + graph: + Optional Semantica ContextGraph. When supplied, all returned tools + operate on this same graph instance. + + Returns: + A list containing FunctionTools for: + - extract_entities + - extract_relations + - add_to_graph + - query_graph + + Raises: + ImportError: + If google-adk is not installed. + """ + if not ADK_AVAILABLE or FunctionTool is None: + raise ImportError( + "Google ADK is required for semantica_kg_tools(). " + "Install it with: pip install semantica[google-adk]" + ) + + shared_graph = graph if graph is not None else _get_default_graph() + + def add_to_shared_graph(text: str) -> dict: + """Extract entities and relationships from text and add them to the shared Semantica graph.""" + return _add_to_graph(text, shared_graph) + + def query_shared_graph(query: str) -> dict: + """Query the shared Semantica knowledge graph by keyword.""" + return _query_graph(query, shared_graph) + + # FunctionTool derives the tool name/schema from the wrapped callable and + # its docstring, which is exactly the ADK convention we want. + return [ + FunctionTool(extract_entities), + FunctionTool(extract_relations), + FunctionTool(add_to_shared_graph), + FunctionTool(query_shared_graph), + ] + + +__all__ = [ + "ADK_AVAILABLE", + "extract_entities", + "extract_relations", + "add_to_graph", + "query_graph", + "semantica_kg_tools", +] \ No newline at end of file diff --git a/integrations/google_adk/session_service.py b/integrations/google_adk/session_service.py new file mode 100644 index 00000000..f5a0a02d --- /dev/null +++ b/integrations/google_adk/session_service.py @@ -0,0 +1,646 @@ +""" +Semantica-backed Google ADK session service. + +Session metadata, state, and event history are represented as nodes in a +Semantica ContextGraph instead of being kept only in ADK's in-memory store. + +Google ADK is an optional dependency. +""" + +from __future__ import annotations + +import asyncio +import copy +import inspect +import threading +import urllib.parse +import uuid +from datetime import datetime +from typing import Any, List, Optional + +try: + from google.adk.events import Event + from google.adk.sessions import BaseSessionService, Session + try: + from google.adk.sessions import ListSessionsResponse + except ImportError: + # Not every google-adk release re-exports ListSessionsResponse from + # the sessions package __init__; it always lives in + # base_session_service. + from google.adk.sessions.base_session_service import ListSessionsResponse + try: + from google.adk.sessions import GetSessionConfig + except ImportError: + from google.adk.sessions.base_session_service import GetSessionConfig + ADK_AVAILABLE = True +except (ImportError, ModuleNotFoundError): + ADK_AVAILABLE = False + BaseSessionService = object + Session = Any + Event = Any + ListSessionsResponse = Any + GetSessionConfig = Any + + +class SemanticaSessionService(BaseSessionService): + """ + Google ADK SessionService backed by a Semantica ContextGraph. + + Graph structure: + + ADKSession + | + +-- HAS_EVENT --> ADKEvent + + Session metadata and state are stored in the ContextGraph node metadata. + """ + + def __init__(self, graph: Optional[Any] = None) -> None: + if not ADK_AVAILABLE: + raise ImportError( + "Google ADK is required for SemanticaSessionService. " + "Install it with: pip install semantica[google-adk]" + ) + + super().__init__() + + if graph is None: + from semantica.context import ContextGraph + + graph = ContextGraph() + + self.graph = graph + self._lock = threading.RLock() + + # Graph helpers + + @staticmethod + def _node_id(app_name: str, user_id: str, session_id: str) -> str: + """Return the internal ContextGraph node ID for a session. + + Each component is percent-encoded before joining so a ':' inside + app_name/user_id/session_id can never be mistaken for the + separator: without this, distinct identities such as + (app_name="tenant:A", user_id="alice") and + (app_name="tenant", user_id="A:alice") would collide on the same + node ID. + """ + parts = ( + urllib.parse.quote(part, safe="") + for part in (app_name, user_id, session_id) + ) + return "adk-session:" + ":".join(parts) + + @staticmethod + def _event_node_id(event: Any) -> str: + """Return the internal ContextGraph node ID for an event.""" + event_id = getattr(event, "id", None) + + if event_id: + return f"adk-event:{event_id}" + + return f"adk-event:{uuid.uuid4()}" + + @staticmethod + def _safe_dict(value: Any) -> dict: + """Convert common Python/Pydantic objects into a dictionary.""" + if value is None: + return {} + + if isinstance(value, dict): + return copy.deepcopy(value) + + if hasattr(value, "model_dump"): + try: + return copy.deepcopy(value.model_dump()) + except Exception: + pass + + if hasattr(value, "dict"): + try: + return copy.deepcopy(value.dict()) + except Exception: + pass + + try: + return { + key: copy.deepcopy(item) + for key, item in vars(value).items() + if not key.startswith("_") + } + except Exception: + return {} + + @staticmethod + def _node_properties(node: Any) -> dict: + """ + Extract application properties from a ContextGraph node. + """ + if isinstance(node, dict): + metadata = node.get("metadata") + + if isinstance(metadata, dict): + return copy.deepcopy(metadata) + + properties = node.get("properties") + + if isinstance(properties, dict): + return copy.deepcopy(properties) + + return {} + + metadata = getattr(node, "metadata", None) + + if isinstance(metadata, dict): + return copy.deepcopy(metadata) + + properties = getattr(node, "properties", None) + + if isinstance(properties, dict): + return copy.deepcopy(properties) + + return {} + + def _find_session_node( + self, + app_name: str, + user_id: str, + session_id: str, + ) -> Optional[Any]: + """Find a session node by its logical ADK session ID.""" + expected_node_id = self._node_id(app_name, user_id, session_id) + + for node in self.graph.find_nodes() or []: + if not isinstance(node, dict): + continue + + # Fast path: ContextGraph node ID. + if str(node.get("id")) == expected_node_id: + return node + + # Fallback: logical ID stored in metadata. + metadata = node.get("metadata") + + if ( + isinstance(metadata, dict) + and str(metadata.get("session_id")) == str(session_id) + and str(metadata.get("app_name")) == str(app_name) + and str(metadata.get("user_id")) == str(user_id) + ): + return node + + return None + + def _find_node_by_id( + self, + node_id: str, + ) -> Optional[Any]: + """Find a ContextGraph node by graph node ID.""" + for node in self.graph.find_nodes() or []: + if isinstance(node, dict) and str(node.get("id")) == str(node_id): + return node + + return None + + # ------------------------------------------------------------------ + # Event helpers + # ------------------------------------------------------------------ + + @staticmethod + def _serialize_event(event: Any) -> dict: + """Serialize an ADK Event into ContextGraph metadata.""" + data = SemanticaSessionService._safe_dict(event) + + for field in ( + "id", + "invocation_id", + "author", + "timestamp", + "partial", + "turn_complete", + "branch", + ): + if field not in data and hasattr(event, field): + value = getattr(event, field) + + if isinstance(value, datetime): + value = value.isoformat() + + data[field] = copy.deepcopy(value) + + return data + + def _event_nodes( + self, + app_name: str, + user_id: str, + session_id: str, + ) -> List[Any]: + """Return all event nodes connected to a session.""" + session_node_id = self._node_id(app_name, user_id, session_id) + + events: List[Any] = [] + + for edge in self.graph.find_edges() or []: + if not isinstance(edge, dict): + continue + + if edge.get("source") != session_node_id: + continue + + if edge.get("type") != "HAS_EVENT": + continue + + target = edge.get("target") + + if target is None: + continue + + node = self._find_node_by_id(str(target)) + + if node is not None: + events.append(node) + + return events + + @staticmethod + def _event_timestamp(node: Any) -> str: + """Return a sortable timestamp for an event node.""" + properties = SemanticaSessionService._node_properties(node) + timestamp = properties.get("timestamp") + + if timestamp is None: + return "" + + return str(timestamp) + + def _event_from_node( + self, + node: Any, + ) -> Any: + """ + Reconstruct an ADK Event from its stored metadata. + """ + properties = self._node_properties(node) + + graph_node_id = node.get("id") if isinstance(node, dict) else None + event_id = properties.get("id") + + if not event_id and graph_node_id: + graph_node_id = str(graph_node_id) + if graph_node_id.startswith("adk-event:"): + event_id = graph_node_id[len("adk-event:"):] + + if event_id: + properties["id"] = event_id + + # ContextGraph-specific values should never become Event fields. + properties.pop("session_id", None) + properties.pop("app_name", None) + properties.pop("user_id", None) + + try: + return Event(**properties) + except Exception: + return properties + + # ------------------------------------------------------------------ + # Session helpers + # ------------------------------------------------------------------ + + @staticmethod + def _session_kwargs( + app_name: str, + user_id: str, + session_id: str, + state: Optional[dict], + events: Optional[List[Any]], + ) -> dict: + """Build kwargs for the ADK Session model.""" + return { + "app_name": app_name, + "user_id": user_id, + "id": session_id, + "state": copy.deepcopy(state or {}), + "events": list(events or []), + } + + def _session_from_node( + self, + node: Any, + ) -> Session: + """Reconstruct an ADK Session from a ContextGraph node.""" + properties = self._node_properties(node) + + session_id = str(properties.get("session_id") or "") + app_name = str(properties.get("app_name") or "") + user_id = str(properties.get("user_id") or "") + + # Fallback to the graph node ID. + if not session_id: + graph_node_id = node.get("id") if isinstance(node, dict) else None + + if graph_node_id: + graph_node_id = str(graph_node_id) + if graph_node_id.startswith("adk-session:"): + # Each component is percent-encoded by _node_id(), so + # splitting on ':' after the prefix always yields + # exactly 3 parts regardless of what characters the + # original app_name/user_id/session_id contained. + parts = graph_node_id[len("adk-session:"):].split(":") + if len(parts) == 3: + decoded = [urllib.parse.unquote(part) for part in parts] + app_name = app_name or decoded[0] + user_id = user_id or decoded[1] + session_id = decoded[2] + else: + session_id = graph_node_id[len("adk-session:"):] + else: + session_id = graph_node_id + + state = properties.get("state") or {} + + if not isinstance(state, dict): + state = {} + + event_nodes = self._event_nodes(app_name, user_id, session_id) + event_nodes.sort(key=self._event_timestamp) + + events = [self._event_from_node(node) for node in event_nodes] + + return Session( + **self._session_kwargs( + app_name=app_name, + user_id=user_id, + session_id=session_id, + state=state, + events=events, + ) + ) + + # ------------------------------------------------------------------ + # ADK SessionService implementation + # ------------------------------------------------------------------ + + async def create_session( + self, + *, + app_name: str, + user_id: str, + state: Optional[dict[str, Any]] = None, + session_id: Optional[str] = None, + ) -> Session: + """Create and persist an ADK session.""" + return await asyncio.to_thread( + self._create_session_sync, app_name, user_id, state, session_id + ) + + def _create_session_sync( + self, + app_name: str, + user_id: str, + state: Optional[dict[str, Any]], + session_id: Optional[str], + ) -> Session: + with self._lock: + session_id = session_id or str(uuid.uuid4()) + + if self._find_session_node(app_name, user_id, session_id) is not None: + raise ValueError(f"Session already exists: {session_id}") + + self.graph.add_node( + node_id=self._node_id(app_name, user_id, session_id), + node_type="ADKSession", + app_name=app_name, + user_id=user_id, + session_id=session_id, + state=copy.deepcopy(state or {}), + created_at=datetime.now().isoformat(), + updated_at=datetime.now().isoformat(), + ) + + return Session( + **self._session_kwargs( + app_name=app_name, + user_id=user_id, + session_id=session_id, + state=state, + events=[], + ) + ) + + async def get_session( + self, + *, + app_name: str, + user_id: str, + session_id: str, + config: Optional[GetSessionConfig] = None, + ) -> Optional[Session]: + """Retrieve an ADK session from ContextGraph.""" + return await asyncio.to_thread( + self._get_session_sync, app_name, user_id, session_id, config + ) + + def _get_session_sync( + self, + app_name: str, + user_id: str, + session_id: str, + config: Optional[GetSessionConfig], + ) -> Optional[Session]: + with self._lock: + node = self._find_session_node(app_name, user_id, session_id) + + if node is None: + return None + + properties = self._node_properties(node) + if properties.get("app_name") != app_name: + return None + if properties.get("user_id") != user_id: + return None + + session = self._session_from_node(node) + + # Bound the returned event history the same way ADK's own + # InMemorySessionService does, outside the lock since it only + # trims the already-built Session object. + if config: + if config.num_recent_events: + session.events = session.events[-config.num_recent_events:] + if config.after_timestamp: + i = len(session.events) - 1 + while i >= 0: + if session.events[i].timestamp < config.after_timestamp: + break + i -= 1 + if i >= 0: + session.events = session.events[i + 1:] + + return session + + async def append_event( + self, + session: Session, + event: Event, + ) -> Event: + """Persist an ADK event and associate it with a session.""" + # ADK's own base implementation is a no-op for partial/streaming + # events (it returns before touching session.events or state), so + # a graph-backed session must not persist them either -- otherwise + # every intermediate chunk of a streamed response becomes a + # permanent event node. + if getattr(event, "partial", False): + return event + + await asyncio.to_thread(self._append_event_sync, session, event) + return event + + def _append_event_sync(self, session: Session, event: Event) -> None: + with self._lock: + session_id = str(session.id) + app_name = str(session.app_name) + user_id = str(session.user_id) + + session_node = self._find_session_node(app_name, user_id, session_id) + + if session_node is None: + raise ValueError(f"Session does not exist: {session_id}") + + # Verify cross-tenant security + properties = self._node_properties(session_node) + if properties.get("app_name") != app_name or properties.get("user_id") != user_id: + raise ValueError("Cross-tenant session write denied: app_name or user_id mismatch.") + + # Apply ADK in-memory event and state delta semantics. This + # runs inside asyncio.to_thread's worker thread, which has no + # event loop of its own, so a coroutine base implementation is + # driven with a private event loop scoped to this one call. + base_append = getattr(super(), "append_event", None) + if base_append is not None: + if inspect.iscoroutinefunction(base_append): + asyncio.run(base_append(session, event)) + else: + base_append(session, event) + else: + if hasattr(session, "events"): + session.events.append(event) + + event_node_id = self._event_node_id(event) + event_data = self._serialize_event(event) + + self.graph.add_node( + node_id=event_node_id, + node_type="ADKEvent", + session_id=session_id, + **event_data, + ) + + self.graph.add_edge( + source_id=self._node_id(app_name, user_id, session_id), + target_id=event_node_id, + edge_type="HAS_EVENT", + ) + + # ContextGraph's supported mutation API is add_node_attribute(). + self.graph.add_node_attribute( + self._node_id(app_name, user_id, session_id), + { + "state": self._safe_dict(getattr(session, "state", {})), + "updated_at": (datetime.now().isoformat()), + }, + ) + + async def delete_session( + self, + *, + app_name: str, + user_id: str, + session_id: str, + ) -> None: + """Delete a session and all of its graph-backed events.""" + await asyncio.to_thread( + self._delete_session_sync, app_name, user_id, session_id + ) + + def _delete_session_sync( + self, + app_name: str, + user_id: str, + session_id: str, + ) -> None: + with self._lock: + session_node = self._find_session_node(app_name, user_id, session_id) + if session_node is None: + return + + properties = self._node_properties(session_node) + + if properties.get("app_name") != app_name: + return + if properties.get("user_id") != user_id: + return + + session_node_id = self._node_id(app_name, user_id, session_id) + event_node_ids = [] + + for edge in self.graph.find_edges() or []: + if not isinstance(edge, dict): + continue + + if ( + edge.get("source") == session_node_id + and edge.get("type") == "HAS_EVENT" + and edge.get("target") + ): + event_node_ids.append(str(edge["target"])) + + for event_node_id in event_node_ids: + self.graph.purge_node(event_node_id) + + self.graph.purge_node(session_node_id) + + async def list_sessions( + self, + *, + app_name: str, + user_id: Optional[str] = None, + ) -> ListSessionsResponse: + """List sessions for an app, optionally scoped to one user.""" + return await asyncio.to_thread(self._list_sessions_sync, app_name, user_id) + + def _list_sessions_sync( + self, + app_name: str, + user_id: Optional[str], + ) -> ListSessionsResponse: + with self._lock: + sessions: List[Session] = [] + + for node in self.graph.find_nodes(node_type="ADKSession") or []: + if not isinstance(node, dict): + continue + + properties = self._node_properties(node) + + if properties.get("app_name") != app_name: + continue + if user_id is not None and properties.get("user_id") != user_id: + continue + if not properties.get("session_id"): + continue + + sessions.append(self._session_from_node(node)) + + # Return the wrapped ListSessionsResponse + if ListSessionsResponse is not Any and ListSessionsResponse is not object: + return ListSessionsResponse(sessions=sessions) + + return sessions + + +__all__ = [ + "ADK_AVAILABLE", + "SemanticaSessionService", +] \ No newline at end of file diff --git a/mcp/__main__.py b/mcp/__main__.py deleted file mode 100644 index f219b7d3..00000000 --- a/mcp/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Entry point: python -m mcp.server""" -from mcp.server import main - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml index 295e7b92..154eaa76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "semantica" -version = "0.6.7" +version = "0.6.8" description = "Graph-Native Infrastructure for Context and Accountable AI Systems: context graphs, decision intelligence, full provenance tracking, and explainable reasoning engines — every AI decision traceable, every output auditable." readme = "README.md" license = { text = "MIT" } @@ -192,7 +192,7 @@ tripletstore-oxigraph = ["pyoxigraph>=0.5.0"] # ---- Vector Store Backends ---- vectorstore-qdrant = ["qdrant-client>=1.0.0"] vectorstore-weaviate = ["weaviate-client>=4.0.0"] -vectorstore-pinecone = ["pinecone-client>=3.0.0"] +vectorstore-pinecone = ["pinecone>=3.0.0"] vectorstore-milvus = ["pymilvus>=2.0.0"] vectorstore-pgvector = ["psycopg[binary,pool]>=3.0.0", "pgvector>=0.2.0"] vectorstore-sqlite = ["sqlite-vec>=0.1.1"] @@ -246,6 +246,7 @@ agno = ["agno>=1.0.0"] # duplicate the prebuilt tooling users can install separately. crewai = ["crewai>=0.80.0"] langchain = ["langchain-core>=0.3.0"] +google-adk = ["google-adk>=1.27.0; python_version >= '3.10'"] # ---- File Watching ---- watch = ["watchdog>=6.0.0"] @@ -276,6 +277,7 @@ dev = [ # Explorer Dashboard explorer = [ "fastapi>=0.109.2", + "starlette>=0.53.0", "uvicorn[standard]>=0.22.0", "websockets>=15.0.1", "python-multipart>=0.0.7", @@ -293,7 +295,7 @@ explorer-lite = [ # dependency-audit/security gates. Install it explicitly via ``semantica[crewai]``. all = [ "semantica[dev,viz,infra,cloud,monitoring,watch,llm-all,models-huggingface,split-all,graph-all,tripletstore-oxigraph,vectorstore-all,parse-docling,ingest-parquet,ingest-arrow,shacl,explorer]", - "semantica[dev,viz,infra,cloud,monitoring,watch,llm-all,models-huggingface,split-all,graph-all,tripletstore-oxigraph,vectorstore-all,parse-docling,ingest-parquet,ingest-arrow,shacl,agno,langchain]" + "semantica[dev,viz,infra,cloud,monitoring,watch,llm-all,models-huggingface,split-all,graph-all,tripletstore-oxigraph,vectorstore-all,parse-docling,ingest-parquet,ingest-arrow,shacl,agno,langchain,google-adk]" ] # ---------------- ENTRYPOINTS ---------------- @@ -307,7 +309,7 @@ semantica-mcp = "semantica.mcp_server:main" # ---------------- TOOLING ---------------- [tool.setuptools.packages.find] where = ["."] -include = ["semantica*", "integrations*"] +include = ["semantica*", "integrations*", "semantica_mcp*"] [tool.setuptools.package-data] # Explicit patterns are more reliable than **/* across setuptools versions. diff --git a/requirements-ci.txt b/requirements-ci.txt index 559c42c1..c7f98f32 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,18 +1,16 @@ # This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml --python-version 3.11 --extra all --generate-hashes -o requirements-ci.txt +# uv pip compile pyproject.toml --python-version 3.11 --python-platform linux --extra all --generate-hashes -o requirements-ci.txt accelerate==1.14.0 \ --hash=sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d \ --hash=sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6 - # via - # docling-ibm-models - # docling-slim -agno==2.9.0 \ - --hash=sha256:7777674b3931b341fad4fcf02a61b185a08588c509101348facf87feb2144c0c \ - --hash=sha256:7d9c134703e3c2798023cd57dcb9caa8e1174f6914813f9b130becfc3521a46f + # via docling-slim +agno==3.0.5 \ + --hash=sha256:14daedb4548d7c189eaf9922a722c790c506ff26bd6fd39d842111567a522f1d \ + --hash=sha256:52425d2e731b09badfdf64a50cb6fd7b580111ec1788e4a072de475578ad77a5 # via semantica (pyproject.toml) -agnoctl==0.1.3 \ - --hash=sha256:6fce1d2482b1f2e0a3d14b0a7c12fbd49d8df4f0bf0a4fd9fd91753cbff5efdc \ - --hash=sha256:94e1570cf2673ace2d7fa347b51c5fb2416d6f993a0a24b4fca71b04b15e72dd +agnoctl==0.2.0 \ + --hash=sha256:eee03a1febcd11d80b524aac2da527ffa134d88d92c19a529b1862fa6f965d69 \ + --hash=sha256:fcc5f150e1aab3e128815c79a28e03bad4446d46864f5c39d434985e89ea214f # via agno aiohappyeyeballs==2.7.1 \ --hash=sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d \ @@ -139,12 +137,17 @@ aiohttp==3.14.3 \ --hash=sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24 \ --hash=sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5 # via + # google-adk # instructor # litellm aiosignal==1.4.0 \ --hash=sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e \ --hash=sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7 # via aiohttp +aiosqlite==0.22.1 \ + --hash=sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650 \ + --hash=sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb + # via google-adk amqp==5.3.1 \ --hash=sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2 \ --hash=sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432 @@ -159,9 +162,9 @@ annotated-types==0.8.0 \ --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 # via pydantic -anthropic==0.122.0 \ - --hash=sha256:45ec906452ffae6b5f7f0c53d01f50bfb7e4ce878d7ae8e4309d13171e557e67 \ - --hash=sha256:ffec56ae96657c8d19fa575ec96f140f380c353a07ab7d61b92eb18ee6536601 +anthropic==1.3.0 \ + --hash=sha256:6873492a77ede8849a161ab1bc78bc9a1e492a006d0b5bb4c57ac77845df838a \ + --hash=sha256:e7e7dbebf9f3c84a23954ab989378af6ae10a4d1804c81e9fea4b5ced695ce75 # via semantica (pyproject.toml) antlr4-python3-runtime==4.9.3 \ --hash=sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b @@ -174,7 +177,9 @@ anyio==4.14.2 \ # google-genai # groq # httpx + # httpx2 # jupyter-server + # langsmith # openai # starlette # watchfiles @@ -182,33 +187,48 @@ argon2-cffi==25.1.0 \ --hash=sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1 \ --hash=sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 # via jupyter-server -argon2-cffi-bindings==25.1.0 \ - --hash=sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99 \ - --hash=sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6 \ - --hash=sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d \ - --hash=sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44 \ - --hash=sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a \ - --hash=sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f \ - --hash=sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2 \ - --hash=sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690 \ - --hash=sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584 \ - --hash=sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e \ - --hash=sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0 \ - --hash=sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f \ - --hash=sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623 \ - --hash=sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b \ - --hash=sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44 \ - --hash=sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98 \ - --hash=sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500 \ - --hash=sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94 \ - --hash=sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6 \ - --hash=sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d \ - --hash=sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85 \ - --hash=sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92 \ - --hash=sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d \ - --hash=sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a \ - --hash=sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520 \ - --hash=sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb +argon2-cffi-bindings==26.1.0 \ + --hash=sha256:061a6919145bbf282ebf1f9c59d3135d4833c25313c8595c0d68cf7712ddfce2 \ + --hash=sha256:0cc40f7b4050bb93eb67de95d2d759322fc7ce4930b9d645581ecf4913ec651e \ + --hash=sha256:151dfaad9de753f4af2a7854e707e4784f2acc434340ade64239c5b104b2d605 \ + --hash=sha256:19423e5d7ac1cc354baab59eaabf18db2ec04ef6593b5abe5a34f323c4a8f87a \ + --hash=sha256:19b562b1de4b9052ef1214a2821c44b6e6f22945daa102c32ae4eff929d8b6d8 \ + --hash=sha256:1a0a29ed86960e44eaace7e081bdfab4f08b012fd96ec8edba71e2ad020939e4 \ + --hash=sha256:1af817e84578ef8b7295ad17de0f9896e4c8520dbf2233c7aa5aa3d487256fc4 \ + --hash=sha256:1b0bcac4d490a237e18cf91f57352920c29f77f2fa39efd0813fb81298bf17ba \ + --hash=sha256:1d98e33bd8bd67d7206c124e200bf2229c4cfa8c9c19f7b44a897f0fc71837eb \ + --hash=sha256:21ca0396fe5ec995dd54431c32698189666f9224810acfa752e50d2bd94d9df2 \ + --hash=sha256:224865cbbcb7a2bd1356741dff12b0134df726b6d44bb7b500df8e303cbd9e81 \ + --hash=sha256:242bb0cda2ae3650764fc194593d9ea45fc9e72729acd89778c7cfe184cec2a5 \ + --hash=sha256:27f1821903e2ceadcb88ec2b45ef190897b7682449c772f4d9b53e42c520cf29 \ + --hash=sha256:28524438cd3e723f25412f63d4fd516ff5bae9ae5aa56acbe2a1404398a0cf31 \ + --hash=sha256:2b741888c93147444fdfc851abd81cc207f37f7f7da42062a00deb3888e57da8 \ + --hash=sha256:2c36ff87b5dfaa477d0bd51e9d7f6abdae7c8955d2983c97419085d842154b3e \ + --hash=sha256:34b7d9c24a4165a2c61cc8ae11d44d48c9ce2830fb536cb7914e11fdd9962728 \ + --hash=sha256:49d525938467d52c923a890153c99087c9d5a937d1f6b585dbdba34ec82e397a \ + --hash=sha256:4f84cdd868978d7b7350a566c254042d44216d9e37f241f3a6d3b1dfebeede35 \ + --hash=sha256:62ff20cd130c956c7c9144d5fe35228f98b51c579b2439e988b27ef93e16c02a \ + --hash=sha256:63505c71542a44b68b1e38060450fb006404170da375feb31af153e7f9c6205d \ + --hash=sha256:6376d4b3aca039375ca8bf92f770da0ec424a1ce3a37077a8d3c557411aa56ca \ + --hash=sha256:6a4e68eed961a8de6928d1c17ff3dc2a547e0e923c17f8f1cd79fb7bc9502f98 \ + --hash=sha256:6ab674f668d5962a3a4136ae0812519b0f1586874263723a32181d60d64137e1 \ + --hash=sha256:7014ab7e6f5d8511af92544667a0346ea6dfc314ea9a7cad1dba9fdb5c9a6e33 \ + --hash=sha256:76ae29acace5d33355344612844d588e19deaaba4639d8bb01601e4b1418ef36 \ + --hash=sha256:78de2d65e0b9ea7ce9d1b1c3e87297b2d7305a02c266ee2a2d6910daddd7ee69 \ + --hash=sha256:9bacedc04b0402837586a17f0919e3dfdd95291f441f1f56bd80ec274c2840a1 \ + --hash=sha256:a86c069c91a747a2c4e5c51473590aeb48172fff9b2130d23729a42d98665ecb \ + --hash=sha256:ac82fc756a446b6ccd7139ce70efa9d8bbe541e7ad579a12dcb52764b7175c5f \ + --hash=sha256:af11ac37a7c53dc16cb7950a6190851b0870fe218b6c60c0bb7ac355234e3083 \ + --hash=sha256:b70225b5fd1e0d2ef4f7fd30d24658454535f0924dff0caca5dc08efbbbadfbb \ + --hash=sha256:c49e853a3bef9dd10329f31f702e7fa9b5c58229ff9c2ff6d069efaf09177c08 \ + --hash=sha256:ccaf0a46cbb380f1fd102a874e32aa629fd3cb0c0e94f4943fa1f6d5edc5dac6 \ + --hash=sha256:d157ddfab1e8b21f2f1dedda9c09645d98b5ed0b667b0626be600a345d426440 \ + --hash=sha256:d88e5f7e60f28ae0b0cc6b2f16c43e87cd642a196a86f85e0d8bb6fe016fc16d \ + --hash=sha256:db0fcd827ca61622a01b220aadfbece01939acf53888f2cb98cd93e9b1e2c97e \ + --hash=sha256:df612391feca41c44d20118f3b88d1b86419465cd1f5496859f715ca60ec2210 \ + --hash=sha256:f0c3103fcff20183e593459cfea6e012281c0e76ae3ed8b5565ad1b92eac3990 \ + --hash=sha256:f9c4420a7a864fe1b86ce35befc95b8e39fb852493b81cf798671ddc265de638 \ + --hash=sha256:ffff613aaa9ce6236766e2fc6dc560bb5abde7a2e2416e3db1f9ae395a2b4dd4 # via argon2-cffi arrow==1.4.0 \ --hash=sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205 \ @@ -290,24 +310,25 @@ attrs==26.1.0 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via # aiohttp - # jsonlines # jsonschema # referencing audioread==3.1.0 \ --hash=sha256:1c4ab2f2972764c896a8ac61ac53e261c8d29f0c6ccd652f84e18f08a4cab190 \ --hash=sha256:b30d1df6c5d3de5dcef0fb0e256f6ea17bdcf5f979408df0297d8a408e2971b4 # via librosa -authlib==1.7.2 \ - --hash=sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231 \ - --hash=sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f - # via weaviate-client +authlib==1.8.0 \ + --hash=sha256:88aebbd9af6757e14e912d5dc007ae1dc1f3e27e3b2152ce7c552ee2c3b3c121 \ + --hash=sha256:f3ecd5f1da737262fb53bf1a4d95c4ea1ad9dd509316587a255c99ab1838a4f0 + # via + # google-adk + # weaviate-client azure-core==1.41.0 \ --hash=sha256:522b4011e8180b1a3dcd2024396a4e7fe9ac37fb8597db47163d230b5efe892d \ --hash=sha256:f46ff5dfcd230f25cf1c19e8a34b8dc08a337b2503e268bb600a16c00db8ad5a # via azure-storage-blob -azure-storage-blob==12.30.0 \ - --hash=sha256:2cd74d4d5731e5eb6b8d5c5056ee115a5e88f8fdf22517b739836fda685018be \ - --hash=sha256:d415ac50b67a8da6b3ae7e9f1014b1b55cd7aafa0b8d4ca9b380568dc7360423 +azure-storage-blob==12.30.1 \ + --hash=sha256:7a24f978c51d56a0375beebffcbe8453e59ae390d2695705848edc75083e4184 \ + --hash=sha256:7dc09c37f4f58508e20532b4b4c178f4763f41b01e0b9063835b994fd9d2a7b3 # via semantica (pyproject.toml) babel==2.18.0 \ --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d \ @@ -399,19 +420,21 @@ blis==1.3.3 \ --hash=sha256:f20f7ad69aaffd1ce14fe77de557b6df9b61e0c9e582f75a843715d836b5c8af \ --hash=sha256:f36c0ca84a05ee5d3dbaa38056c4423c1fc29948b17a7923dd2fed8967375d74 # via thinc -boto3==1.43.69 \ - --hash=sha256:4eb494d05b2bd08a7eee61b8ac4c34745c99e9bbce435c91f8d15d372dd8c2db \ - --hash=sha256:76297a0b415849c63575ae08a4f1661b2dc8ee0100f104b86f98aa69b47fa2c7 - # via semantica (pyproject.toml) -botocore==1.43.73 \ - --hash=sha256:068433028e011ccbeab1dd7c46b1090c24e378397693c66e67ca571176498daa \ - --hash=sha256:0fa1e63c24b3531be3e1bc1687a88b3be9e63a430153f24edd93efc162bb1c51 +boto3==1.43.85 \ + --hash=sha256:113b6e1aa3f5722f90c01fc63968c269a9b1fd03ac2594fe16c56a66e6331c5f \ + --hash=sha256:f11bdaca18e59f53ec0529f4d6203dd1f0bb7ff165e51559d62fd863024abc9b + # via + # semantica (pyproject.toml) + # litellm +botocore==1.43.85 \ + --hash=sha256:685510e5f4c0f321806c815a60f121a176c0969665f053c4a336209cbe62b1d5 \ + --hash=sha256:8fc0a3c56078c629320b021edadf7a45d289eea21a4988ada6a02277e5bbbdc0 # via # boto3 # s3transfer -cachetools==7.1.7 \ - --hash=sha256:a3e2a00b14d8f8a6b70c1dae7b4685e7ad3bc965c5b42124a2d6ce895da6cf50 \ - --hash=sha256:ef98ef375ad188819ef2f9b3645e3987f4b8c5b7550e436ad998c2de78296df0 +cachetools==7.1.8 \ + --hash=sha256:1221d547a0b24b7f26fa891d40d488b5258beab9aebd8ed68c729be3af849c43 \ + --hash=sha256:a81e3844acaa7355b6567f97bd67a94a14ec3a9bc2cbbdae45b9592cc036775b # via pymilvus catalogue==2.0.10 \ --hash=sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15 \ @@ -431,7 +454,6 @@ certifi==2026.7.22 \ # docling-slim # httpcore # httpx - # pinecone-client # pulsar-client # requests cffi==2.1.1 \ @@ -543,43 +565,53 @@ cfgv==3.5.0 \ --hash=sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 \ --hash=sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132 # via pre-commit -chardet==7.5.1 \ - --hash=sha256:0df08f2b2f6ac04b3e7f9e8ad1b1559c2e8497338ff9dfa1e0922335ff9dfe8d \ - --hash=sha256:126b2a65141ed8a460c721d19f487c7b6fd12542aa761ce449f543296d0dd71e \ - --hash=sha256:1cd58589a52211901c5ac57016feffbe8a7e7e6328f5bf0b03ce44043e221e99 \ - --hash=sha256:26160da949c66f0cca280101d85e6e4fddca53bfe465a1b69ceb3c9998295cc0 \ - --hash=sha256:2c99dea9eea1bdc6cc20dcb3555581234899d58a4147163c7d03d8fca11402b0 \ - --hash=sha256:36843a0e9e3196142e317806d5ca29ab0bb714de2315897124a018438cc535a3 \ - --hash=sha256:3eb37b2c0aa67bfb1112aa90bfdd95cd3b4006fe051a2ea4872c3c6ba9cf855b \ - --hash=sha256:44214df32ff7c87fe82d7f1f21c7fe95c04769869e523f944413e668e001f52c \ - --hash=sha256:469f164a608ccee4a8a2c0c2b4328470df9b07443e8269714f8e8a51f6fdf4c4 \ - --hash=sha256:46d10bbb7ba7ba345694fe0276a61290d4cc25d3624c03282311dbc58c1d49b4 \ - --hash=sha256:4d30a84ec52c37532ad7978329a41224c454959b22503b8f8ed4df763e6c3ed2 \ - --hash=sha256:54bae16fc5b7ea39956ee737dd09b5f5438deafa9f565ac27c882992c3965b88 \ - --hash=sha256:5953d8236049aed0411908cfeaeeec03984ab2f109f980f0ec70fbe6938c8f9e \ - --hash=sha256:59598a8e15769ebe62fd0c153a5e4347a7a126cc7b376a724ff63ad80b890506 \ - --hash=sha256:61312fd3ff363c3ec549548250630a02fd3123c360dd492bf1fce0d28e915b87 \ - --hash=sha256:6df2e255413c5f277067d9af7444ab1e9719126335efbefae24d8370aedf35c5 \ - --hash=sha256:6eefafa763b7099c3c0a86c343097d69b766b3fe5705edba9400bae26450af1f \ - --hash=sha256:71f152d66e7bd1faad615897d34765243cb567ad6aef07bf0d7c0cdd69bf6cce \ - --hash=sha256:8a001a8f030625b705d9a4e68116e573462bd38192cc6c1bfa318b45606747ac \ - --hash=sha256:951ccab3a037a563079f4d448e82bbfee5f2715239440f732ffb0ac9f251dedb \ - --hash=sha256:9c378ccd8c0fab30171ed7c54d501f72c4294d9b98c71ea1ff7852aa9ccac399 \ - --hash=sha256:a198d47eaa28e1ba458f11ab636f0677f34c5d1ce7e909bec6ca2f346c21e78c \ - --hash=sha256:a599a836fbd41ff5a2a0c20e211da13ba8cc14dcba1e4bcfad8a7bcad64a2ff6 \ - --hash=sha256:a6b20b42a9e6048d557aec9903df33239c299b4643c6553203127c8f92f47e78 \ - --hash=sha256:a77d6d2d61f39b40423bd0abaee32175739cec9f11edf9e7236a5341d0e05c99 \ - --hash=sha256:b1c049906b95db7b12fd674f661f75285baf925eae98aa52a4a09305fc786855 \ - --hash=sha256:b72b9b95c636d170d9a6284d99be9fd93ca08bb2221385ff1a5b69da98ec4f76 \ - --hash=sha256:ba7e9b6c15b4fcdf07ae675e5116dee610425f9ad6955c9bdb6bf99aed2e555d \ - --hash=sha256:bbf6948b7a5b85af5e435993c4e5fdb092d0d8f38c00d68c96e2640bafce5ec2 \ - --hash=sha256:c461fc9746912d19ab77efc1912b7f9a364b7fca1d787e1215207d5f6f68685d \ - --hash=sha256:d06a8bacc8b6a26e900c3bd825601f9a8c02e063e687e960cf77363a9a397a0b \ - --hash=sha256:e6faa6b18c7af2fca8d4cbb51fa035fa47f8f4b68547ae927a1c0be34dfc96fa \ - --hash=sha256:e72489029c1f6e4be6138dd045a4e52bffaba5d5da0398df585bfcf8b239e324 \ - --hash=sha256:ecbe0e0a9fff7825fc48650ef297ede49c71a7abc411a0638416207a70bf78c0 \ - --hash=sha256:f22396ad419f1e78594057e200aa7253be56840f5b04d07b86ccecd99e09c068 \ - --hash=sha256:fad6fbc154113e3b17bb757c34b21477e4b6d69fdd4ce51ff2b3f29a42f08b5b +chardet==7.6.0 \ + --hash=sha256:089e3bb81a0a07e94f15461ded9f9ee66d349615b1a9fd557d4de1003e2fc12e \ + --hash=sha256:0ad9bc6dab4f338673353fa3f0dc96122f559aaf746087408106e2fcbf132fe8 \ + --hash=sha256:0bdb6f03107b7ace3f44e0edd91aa24456ee558787df265cc19daf45785b31c7 \ + --hash=sha256:0c44a32da32cc8b23d6b20d98ace15ec7600950e4955d1bf5ab1f849b0187fdb \ + --hash=sha256:0f304de7041afaec0195ad6464937cd112392002e9d72ed15d55f20a9abd3a13 \ + --hash=sha256:167d7ba3ee08b654e36d7b43ebd9a36606c9a12e2fabdb361757a095ca3b7e3d \ + --hash=sha256:19fea52164e6e00f2a21ed418f42e4b0162a09199274c86d07ad3efd661317c4 \ + --hash=sha256:249993b88ac7a58cad2781acea8f379152a28a719c9b401d614898c63a8c83da \ + --hash=sha256:271ab71ec1be61dbbce0436de0848895c03eae051c379e937a39573d9ce403d9 \ + --hash=sha256:284136186ff90735f901ed0a1c6d41e7af67c666841cc0eceb58482a21b7056c \ + --hash=sha256:2b5d31f9b7f793e15e81cca877e7ccd72bffffa2a3443a9d47be9dfee84fad69 \ + --hash=sha256:2cf0adaca8b1c4bacfade9d0a1e4f8f70b1bb122833d6f07ab90e3adc84eb13a \ + --hash=sha256:360260d074d8712ac1e9048fcafb0fdde246f9d0b12555748ad0017c5ecee43d \ + --hash=sha256:406936df1328a3284fef366eaa2bfd1cccd0ef1b10cb99781dd5b022ea644b84 \ + --hash=sha256:4076d795897ce45239825956a1334e134322ecc4bfe84dbb12acd5390de0fbc1 \ + --hash=sha256:43ea433e43a23c55e8e17f3fad1e07f5cfe5450c73124b95b0d849c21ad379ee \ + --hash=sha256:459e2b1c98f9a86a4698112aa42dffa802bbbff883c1ff144071f87224125862 \ + --hash=sha256:4b81d3f7d7914442d5f7d515b8c6d79cee6b794bc208971fb6902f176671166a \ + --hash=sha256:55a4c31adc7c7e83ad412f2f66b6b7358d0d4fe67505e7f58e18f68f75d341bb \ + --hash=sha256:57e6846cc13ce1ff59979f4ec9da770c57e12aa99046073f632de5a51d9a6f20 \ + --hash=sha256:5e9b31b9ae93872d66439b046a1e08c2ea99791f3c254dce1e2633e395c5587c \ + --hash=sha256:61238d5945b36af9a2ad13494f8969b7deb3c3b4abe223e54670c064e73f5328 \ + --hash=sha256:6424512f576fa7e88b7431d38a42d57552c8f717465a975fc42e497cd280d833 \ + --hash=sha256:75d6c3a4d2046d49e83d2d2206eb073a1f390743e856d90c1bbc19949b26acf4 \ + --hash=sha256:7b586cab9e9072dddd89bc2bd27ee72808d0c84ec73695fe6ec0f3c46b057c65 \ + --hash=sha256:7bbc8a9652c7f859c593847f220c1d264f25749369abb1a267b404ee8cceb209 \ + --hash=sha256:83512a475a2f3886166aa0bca1bbb39343a4eb3186dd5532127d6f2591d09118 \ + --hash=sha256:8900f6c7cf6b015b17a51767cc6144689059ba1cdceaa383d29eb037ac28579e \ + --hash=sha256:93d9df6089ded42ed1fe9f57e272c0b74bd0464d45c0c7d50f09f26f31105c3c \ + --hash=sha256:a12023d48d0e207791c01161d03cb3c0d85c6a15f345eb9d3d56063a63d1e40f \ + --hash=sha256:a4f0a368ad04d5def08bdfaa17c7e15e71552f93923dc2aa9b2f7d9dee02fbb6 \ + --hash=sha256:aa03322e07ac08d520ec50bb50c73143d0892d1adc067d4c5e58f4ef4b2363a8 \ + --hash=sha256:b3b4c96c4df93899b3c8b9e8159e06b1f55c66d7ca384d91481108e251a06eb0 \ + --hash=sha256:b73f277c1ac09c4f8076c4214b816c7aa78a0a2f0cb7156742f4303f856bedc3 \ + --hash=sha256:c54b6a8d3b219560fa5cf4c28df932c37471afe047afdc152067104e741f38c1 \ + --hash=sha256:c6061adf247ab5dda173b67010e13904c6071717660c7c8077fb50aca362b264 \ + --hash=sha256:cbaca8f563a9de07ab1a53157dba93802e54c26afe3339892afcc7c59ea4ef1b \ + --hash=sha256:cedbc584789eb2edfde20fd03669972a833ce6019e60014ae613f9bfc440e8e3 \ + --hash=sha256:cf6d08c2373b7772a558d141f9e8cee53fe1d222341bac612e4d558b04995f73 \ + --hash=sha256:d5dc835e40e0e09c2c3eab43731a8b5127834f42786dda09ba2f4b699ccd527a \ + --hash=sha256:d6030886e7da2740bf299b6a8cc75b4dcc2c90db0ca8fe0a6e4fd0bfd071dabd \ + --hash=sha256:da86fc1b40ff5996fbb5e4c2d2dca770eac2c893cef157dacc050b8b4d929846 \ + --hash=sha256:dde4080fb6bb8db96e8c44893771bcc0d235f4c22cdddb194a765a65e3a72ba7 \ + --hash=sha256:f14f46ef1977e41ce1f4814ca6984cea7f8b6baf8cbc6626ef7bf3d13cf7ea13 \ + --hash=sha256:f2ec3c78cc6b54bf8e091ec4ee885473078b5d7ef18ab1b01c86ae1e98bf88f7 \ + --hash=sha256:fc1e1571321baf8927582fe34363ad7f02279f11c8c2839c14b4c76894148db6 # via semantica (pyproject.toml) charset-normalizer==3.5.1 \ --hash=sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45 \ @@ -755,9 +787,9 @@ charset-normalizer==3.5.1 \ --hash=sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0 \ --hash=sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f # via requests -click==8.4.2 \ - --hash=sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6 \ - --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76 +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 # via # semantica (pyproject.toml) # black @@ -765,6 +797,7 @@ click==8.4.2 \ # click-didyoumean # click-plugins # click-repl + # google-adk # huggingface-hub # litellm # python-oxmsg @@ -782,10 +815,14 @@ click-repl==0.3.0 \ --hash=sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9 \ --hash=sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812 # via celery -cloudpathlib==0.24.0 \ - --hash=sha256:b1c51e2d2ec7dc4fed6538991f4aea849d6cf11a7e6b9069f86e461aa1f9b5b4 \ - --hash=sha256:c521a984e77b47e656fe78e20a7e3e260e0ab45fc69e33ac01094227c979e34a +cloudpathlib==0.25.0 \ + --hash=sha256:63612e17778c5e3a51b472def8d785d0aaaf347486d6b6786dc7be627556d4c6 \ + --hash=sha256:8faef3ed3a0dd71d134e8617b4fdc5ce56a12a6b485c080cfe80106e5f1d1f5d # via weasel +cloudpickle==3.1.2 \ + --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \ + --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + # via joblib colorlog==6.12.0 \ --hash=sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f \ --hash=sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e @@ -885,176 +922,176 @@ contourpy==1.3.3 \ --hash=sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9 \ --hash=sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a # via matplotlib -coverage==7.15.4 \ - --hash=sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8 \ - --hash=sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c \ - --hash=sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624 \ - --hash=sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00 \ - --hash=sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982 \ - --hash=sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429 \ - --hash=sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4 \ - --hash=sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f \ - --hash=sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88 \ - --hash=sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa \ - --hash=sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753 \ - --hash=sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f \ - --hash=sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52 \ - --hash=sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3 \ - --hash=sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7 \ - --hash=sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c \ - --hash=sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26 \ - --hash=sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b \ - --hash=sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9 \ - --hash=sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b \ - --hash=sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4 \ - --hash=sha256:317db01a2cb02552fd67e2b1cca77a4b528a2a277176c5e0bf2cecbb639d3f54 \ - --hash=sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d \ - --hash=sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c \ - --hash=sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e \ - --hash=sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97 \ - --hash=sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b \ - --hash=sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e \ - --hash=sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5 \ - --hash=sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e \ - --hash=sha256:3fc2130bf37df31852a8384f12601563a45a0024bccc6624f38355cba7a8d360 \ - --hash=sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba \ - --hash=sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e \ - --hash=sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7 \ - --hash=sha256:425920379052ff1fe465268f3361d35804a241bbdd5a1b592c8cb60df4c52325 \ - --hash=sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2 \ - --hash=sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839 \ - --hash=sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b \ - --hash=sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d \ - --hash=sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768 \ - --hash=sha256:4dff9daa47d83120c3ec38ce921214242944a832aa04e903e50b5b7ebac8972d \ - --hash=sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645 \ - --hash=sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36 \ - --hash=sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082 \ - --hash=sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734 \ - --hash=sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b \ - --hash=sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc \ - --hash=sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f \ - --hash=sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7 \ - --hash=sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278 \ - --hash=sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e \ - --hash=sha256:69bb2400abef928e365ea7d4d9925169ada78ed2295546780002d4b65de3df88 \ - --hash=sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd \ - --hash=sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf \ - --hash=sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f \ - --hash=sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6 \ - --hash=sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7 \ - --hash=sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8 \ - --hash=sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54 \ - --hash=sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf \ - --hash=sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5 \ - --hash=sha256:81661f82d302484e3119e7c80c519c02fa9bcc2a6b339baf67d67bc89c580f04 \ - --hash=sha256:83cf06cdd687677742caff1a9134833b7a8b75f111519d2cb0e0ba1b9a851e15 \ - --hash=sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2 \ - --hash=sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26 \ - --hash=sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f \ - --hash=sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78 \ - --hash=sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a \ - --hash=sha256:8ee3838dcb656602c3b51e16aed9bfb0822f8d8d6d1c5966d32ec8c104be8e20 \ - --hash=sha256:8fa4de68e2a752468ff14b4e15db7def689a71be759e826a31ccecbef69c5fd0 \ - --hash=sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303 \ - --hash=sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84 \ - --hash=sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff \ - --hash=sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b \ - --hash=sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8 \ - --hash=sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017 \ - --hash=sha256:a093fd37229918976f602aa07aa59e0973cde82186f220c8e197f721f5be0ce4 \ - --hash=sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc \ - --hash=sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7 \ - --hash=sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce \ - --hash=sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d \ - --hash=sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57 \ - --hash=sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72 \ - --hash=sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719 \ - --hash=sha256:b24e078eabcd6a9caa8b0713f9bc1eeb310bcc960a29d45a3b4fcd4b16d5b11d \ - --hash=sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490 \ - --hash=sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809 \ - --hash=sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4 \ - --hash=sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800 \ - --hash=sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc \ - --hash=sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c \ - --hash=sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921 \ - --hash=sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d \ - --hash=sha256:cb476b2e828ecb71cb6b6a928d23fd20a7ddb501188022dae1c37499149cc338 \ - --hash=sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85 \ - --hash=sha256:cfe20cc8cf8821d4fe54f89106cbf06aa27f37b5bbe3535568065a81539b4150 \ - --hash=sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced \ - --hash=sha256:d0be6daac4cce6b8c8dc65886bae1b082ddbca4da8e5cbb5e15166acf253e264 \ - --hash=sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c \ - --hash=sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931 \ - --hash=sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb \ - --hash=sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571 \ - --hash=sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22 \ - --hash=sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c \ - --hash=sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25 \ - --hash=sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67 \ - --hash=sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce \ - --hash=sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d \ - --hash=sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a \ - --hash=sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9 \ - --hash=sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839 \ - --hash=sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890 \ - --hash=sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a \ - --hash=sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425 \ - --hash=sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25 \ - --hash=sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a \ - --hash=sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc \ - --hash=sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d \ - --hash=sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac \ - --hash=sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed \ - --hash=sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f +coverage==7.16.0 \ + --hash=sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06 \ + --hash=sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11 \ + --hash=sha256:050a291b3cfe5e0df5999ef2fa5a7aff6e2db329f069d47eb63f02bde2e7e96b \ + --hash=sha256:058631257350b31784ed43ceb808298b6f074edf4ebca4c7ce5082e6bf873a61 \ + --hash=sha256:0598aadae641f30a0796b75b45c0b9c5de8619bd5cfb251bb0cc254e86e6dd13 \ + --hash=sha256:06f20145a9eb5bf1fd1dde3c0bc2af2e7c22135ab07ca6284d6ada7cc3904c4e \ + --hash=sha256:077f0964087883176ff6ab9b074694cae29f8c708273b13ca62c183c6ed716cd \ + --hash=sha256:0bb04ee77e557d7476471969d35fbbfb5fc8a4152e9409aa5811780c36d9b23e \ + --hash=sha256:0ccc37c00e1a5d30840902c54557e104d04aead872cedf6d2281c8725a467e06 \ + --hash=sha256:0fca700cae4635656668ba6e2b66a85aac9f2622d7b2bcf82e844c409eaa1313 \ + --hash=sha256:136988df5bc5a48795d9c42c75c4bbda5d9a78e750a080c1233010edff93a1af \ + --hash=sha256:1420370276f1694b663207b8245c3628aafb9624fe3cebf313a13d860e55ee67 \ + --hash=sha256:151855767480be14db595cbc2040f6a4db965cdfeebd354d79b0256742b029e0 \ + --hash=sha256:1545c52ce756b8a97007f439a220297f1cd72a2cbbcdffccdf1c1f70e74f9a42 \ + --hash=sha256:17fc3628f99812fec24f40092af34c1c73274d331babab3d1d768a75de650cf7 \ + --hash=sha256:181c2906b9b3759955c1c33c51fbb91c754fbd0b82ea49e2c81061f5a052082c \ + --hash=sha256:183613f664718b340589d7f005c7e92b4b601cffd20a8a4117cfda3e983b080f \ + --hash=sha256:190ffa0f5af966254c249fb3aeaca2cef389785e3e287fd577d39e134d20f8a3 \ + --hash=sha256:1a03e78f53e4d2ab13adac19958a89322d1829913e5623d642627bf60b35da21 \ + --hash=sha256:1c2c45ee1853668f0ea1a0ddff396421c9dc5ad25a56bfb94a895970c2d8e7c2 \ + --hash=sha256:1c5a43cc0ef101637ae920a9eed24cf0549ef815621eae68b3ad577ec5a7ad2f \ + --hash=sha256:1f81cb1554c3712e41649ed5dc98656b50b958e4da12f0f5adb681ce3db92831 \ + --hash=sha256:22d8802827404be32f5a4d6ddc037f6fa0074b7d06702c0224cb598def8b665d \ + --hash=sha256:245f7de6d023a5bba375dbec9f2e0869bfa26ac0cc639bbb7b4c814884000b73 \ + --hash=sha256:26e7de0cb87960c6c9b5cad760068dab767b2b49a3b9376e1992c1e2691a015e \ + --hash=sha256:27461af9f3ed7d2cf2411eb083784f87055ebf42211789ae3a216c48609bc743 \ + --hash=sha256:289f2ed4d56eebf029b649e7dfc3c1153b111962a75e294cdd8e4a1598a04cc3 \ + --hash=sha256:2c3ff6580f2dfc5bec34717b85b2e6cf5ec993b721e7bb58a794babd525a8178 \ + --hash=sha256:2ddaa9e2af4760a329d80008b7a3b4762fbb0dbcb169199360f9a5179c32f2dc \ + --hash=sha256:30f5aee6d1d517abcdfd4f9cad027969ff79a1440a22da263f9514e31b5b66e9 \ + --hash=sha256:32c56b5b47c50635081445ac404dd08c2d591b9c837c22570aa9e182c3b42cd4 \ + --hash=sha256:34d8686bce035c8465b318a8c2890e69ba14a00801a27f4eb6bdc97c23944d87 \ + --hash=sha256:35a9676bf86097f790113ebd9fb67681804ef54d40941d2f10ba68c02239e575 \ + --hash=sha256:360967a6fd77794c167529eec2d16ff8e38216110619d23acc3fd466a1648bee \ + --hash=sha256:36aed4951aedf04cbe9465e76f8e71219980a52b73d07afe69746cba6ba7b97a \ + --hash=sha256:38b8e1e73750b8965d1154ed733f5303acd4e24ee2d5ee872bb1bfab744a31ce \ + --hash=sha256:3e8037e8213adf882e9d7eedd2c5c557933ab0b9632c42d98fe98ec9bcdb4025 \ + --hash=sha256:4080ad6bad9f14690e6b2104f5e8d137ccc65a4b5427a36662090637d4bd16d5 \ + --hash=sha256:4212cec9b42fd9929e70b462732fefd8b13406371871c82f3c14397499d6550b \ + --hash=sha256:47d5e1fc0b321c8308a2aacee0497c435b08acaa629b7059798fdf6fc3006352 \ + --hash=sha256:496277c8d7beed695e02c7be53516a0152e4caef8738a0feab6a638546cce449 \ + --hash=sha256:49fa72ead28c8216f8916398a4f3c4669acb30a061822810ee20a727a1be2897 \ + --hash=sha256:4b1d09cb5d8dc2c7164450f5217e6f0717497de9c588806a0780d352abef904a \ + --hash=sha256:4c1f16d5555a195295d0dc9c902612270e3dfed6a11f3bf7bc470b7b6a79ed3c \ + --hash=sha256:4fcb5f07a9b7083bfb715115d27ce263ba2b5b89dddeee536b295ba0e3c2c627 \ + --hash=sha256:507596cee23e9968b1934fe86d799b76166541af0a293930918b1b48a5c84bd2 \ + --hash=sha256:51e7d0e311d2fba3915f971236cbdd4ad821fc7a23988221c0b33c964b0eba22 \ + --hash=sha256:5205baea687133613dced668a3d0168ea1479349615bfc255849a7944988c889 \ + --hash=sha256:54b7fba6a74d010de34319a0419d5b65af8c00f539ad0b6f39fc6f342ab99697 \ + --hash=sha256:55957d350452017f523b9b03ffac078f9a214e23c04a3d0a674569203550c719 \ + --hash=sha256:577c2ac8c0036f6f8edd3a7783a9e67302b17771d1abf0fd2ed246e3158be51b \ + --hash=sha256:584896fb8b650e999e24ef57e9513e482c12f8e15a73ee9d4584e23c99465867 \ + --hash=sha256:5dad64d9c17cb1983adef07998e6e2e1cf870a156f1ea80f81ce1970f4c545ce \ + --hash=sha256:64f0611ee05364fc85cc3e5bc371804117a76fd337720e6017332fc7c534257a \ + --hash=sha256:69474d81f198774c9d2937599ca5da04c9e1c5de5032da23c607ce4960ce360e \ + --hash=sha256:6ad3bbad240ab937512156bc944fdee63ac4dd34a7558a3094548fd4c1150c02 \ + --hash=sha256:6c60cde430c0e7e3be612973af39b4cff90ec2e2defe7b2b701daea3a0ffff04 \ + --hash=sha256:6e2854b62601c89a63814ad5def3b90d99c6724cc4cb977f75b725e5fca4b1e3 \ + --hash=sha256:6e701938ec9081d3e400a0c9a9a8ae0f7ca44214741daeac4454b1c6ef6dbd19 \ + --hash=sha256:6fde65e0ea945920265dfe4a2108fc45eee2e2ea3d9c3073af6373ff9836aa71 \ + --hash=sha256:719a3feb6220dd32ed932d4c3676d17fb8739e2643b29c0e7c3af400ff80ac44 \ + --hash=sha256:72a0795cc6d34acc2b03dfeabdc82b61b72087f2737018b56ac92c1cf5446c54 \ + --hash=sha256:770d4244c423dcafb5c31db393f429fe952b1bba23bbff7cc3886f8133769ba5 \ + --hash=sha256:78103e79f9378cb0e43ddaa728629a373c070df903c5dfa98b63ba2cfb4e8c42 \ + --hash=sha256:785b114356c99c0dd5b3f57b9696cfd57b7704f4c53847df8dc88c6cc0d9bcb6 \ + --hash=sha256:78f8b56261d608be102c62edd3a60b66bcd0b581f3f86fdcabaf8b8d95adc950 \ + --hash=sha256:7cae7715afa51dd7c9c42e6603bb46daf424c3449fdf06519cc658aa8d46e2e4 \ + --hash=sha256:80cf547379ad6b1878fd03b033b51188beab4b41824c96e7839e014a4cb947be \ + --hash=sha256:80d7d5d744a041f08637df743ac086204ec5acbcd8432a42b00b49e607358024 \ + --hash=sha256:81d63b68b26304e3668edb103311c17fe13c2ed1c7fe973309819f27bf61c5b8 \ + --hash=sha256:857fceba6ff4b507ee0ad98798a33d544a8473df0c542bf04251ee4ed5ee6292 \ + --hash=sha256:87771ecf986cff55e87413238cd5e4f54d949c2074bd6fc1657d26a56314ee24 \ + --hash=sha256:916cf8d25c1ce148f7eceb1d45afc9724841200110adc4e53250391852debd91 \ + --hash=sha256:92cbc2bf4f7f67c79f1d3ca4fe8c50faddf48e852a3d07eaaf02dc014889832f \ + --hash=sha256:9421dde689e68d9fd2b6cd7d8c4498e79b5431467b6298517e3f3e60fdbe80a7 \ + --hash=sha256:949eae7e0f562b1518355aaef4b03523e49a6d3fea12aa3542d9e36c863f8267 \ + --hash=sha256:97051c4903689b1afedc2a354d6118223051e03588078b53048603bda9014577 \ + --hash=sha256:984e5430fc6f858385009e92549955157d79335b1f3e13e1031e0f89d1284261 \ + --hash=sha256:9b83f6ac575530783771c8dcf05284f7c8b5b12f1e7cb226d63445aac4497a3a \ + --hash=sha256:9c0690994b84a15a53bdd39e0b2fdb539b22533820623eb86ba75b93760c645b \ + --hash=sha256:a336b1e2990a64f5c356a9b8380fb9c029d56c832b801255250c44d603271bfd \ + --hash=sha256:a3cd34b9025d62180ce2b5dae8a985bfa6cb8c05ecd57fd34ffc1ff751b5a74d \ + --hash=sha256:a739bf08cdca0fad51b73322e4fade0102dd87794e278450b5ee87ef827954db \ + --hash=sha256:a89d07e48d9baead9a15599923a02f62c6df6c3d85aa84ef34be3c9fd6aeb91f \ + --hash=sha256:acadbf2f2a18d7f9c7f119ac798c00c540d7c79c93abd71ed648c87891303633 \ + --hash=sha256:b1374099dd1ad0d31fbb6c95d00a56a3c5e85fb3343dca14fc12f78323a2b42a \ + --hash=sha256:b2af58ecdcec37fe633d4865fccbc8c00d8aa3b31c099bcacb2720c9a0be6ab9 \ + --hash=sha256:b37ad5cbb77776f446e1b55b461eec2eef5c3e7130c72dc0e1447c3a9da2d199 \ + --hash=sha256:b670bd5fa93d9b6855b2837217b45a90863118e2de5e9e033aebd46d07cd08d3 \ + --hash=sha256:b7dbbbf6551eb94618e7bc76ab61cc2740a5b3d13294171bd6adb36e12346c3c \ + --hash=sha256:bbf08d951abaa1ce89e28c998361d56b952413846b459cd017f116ad4c9adbfa \ + --hash=sha256:c1bcfe470a796fbea6234accd81d258a31574dc0b7bf569e16be757572c4de17 \ + --hash=sha256:c5297028c8df849a61b29129cadfe682f90b5b396f528eb319a57d7678eefdad \ + --hash=sha256:c5612cc20ca76abc883e50269af47c1494b42958bb63dbb9aa79729a1ab5f7d3 \ + --hash=sha256:c5feffce90c3d602e149de1c477578efc34dee5f069f9764cc15808ce01ee15c \ + --hash=sha256:c72c9b201dc0e8c2c8821d49858fd865010d08181bf877d2320971b6464ebfd5 \ + --hash=sha256:c76a9b50a344261fe4a9bd20c322b48d3913cc48e8c37f78c21a596008296e68 \ + --hash=sha256:c94ef980f7b94d9dab9dac076d44ca706654cd51bad19734e029084adf528c8e \ + --hash=sha256:cb953835dbfa6d641ac3943e0986bc680f8abbdc2985af15b46c54985347146a \ + --hash=sha256:cc12e5e32acdd62fe5895939695579560639853219288519685c75b7e968d63a \ + --hash=sha256:cce4dc8528453128c6fae523b15f3887fbea1d4d7c9eb9639d3d4fdcbe570c73 \ + --hash=sha256:cd1e85abed2d2499c16664137ac802356316f92b4e2bf3c150bdf0c45f5dd9ae \ + --hash=sha256:ce2ba5e9f1842fe09165825abfb3bc6b527c71a27bc2eb3a10f2284ced64506d \ + --hash=sha256:d1c77c3579ac42798f8b7eed6d3dd258debacca32c8753fc8a1f6eaf1db644f5 \ + --hash=sha256:d568a8adcec0eda42ec23e5e65dfb8c184fc255120f9e99b484f7c869d923fb9 \ + --hash=sha256:d9a218d3f9c7d6916684ed5ba94f620661117a730e733cd6ef5e87accc5872eb \ + --hash=sha256:dcd3dafcdd78305d27c59a1006b53a4990acb89e68d8fbe0992f4f83503c827f \ + --hash=sha256:de24c62bf798940a14674a47489a81b79915ec4134f556d5199830e065225dd0 \ + --hash=sha256:e40e323711b485592354069b1c027ef879cc2d11657eac09a6e5ad0b49ab7406 \ + --hash=sha256:e6b2b9599e7513b0a9c5bf0357f9f8deaa4c2c821025b0693d420e6602748981 \ + --hash=sha256:e9883a2f8206ce3af59117dc278e5d043fea06912bca3f199816129e5e2de354 \ + --hash=sha256:ebaf39dd13f8af65fe5f0316b81046228ef4d91d3c3766192b418753649896d6 \ + --hash=sha256:ed35097438dfa980c1ec75bc83edf8acbe7a374d7007e571957a257fbd0e2fb3 \ + --hash=sha256:edc2be98e6c55ccc5ff7832bb64f023a4b03dba39dfa84b850046cf08a8249b0 \ + --hash=sha256:f093faf23df888518d273be6da65f0ec5a25b5d8b670231e4d87de07361042e7 \ + --hash=sha256:f6c9c21a8bf0d19788f3c5f3e020c90317a0a63ef60521b376003801e21250fb \ + --hash=sha256:f98d438add63546745e5e847192e3e9ab897ed6f2ca96f8281e2f5a15958ae62 \ + --hash=sha256:f99d12f8234c00b88b8077fedf288b25c77f746de312053b7db90fa756ecbdb3 \ + --hash=sha256:fa4ff0b3dd52208d2b30903022d5087f82000507b504753dfeee83e4f32d6883 \ + --hash=sha256:fddd26ed9a2527a7e23f7e4c1fd0734c4a5b45f77b261da1c536b20a7d2e6f0c \ + --hash=sha256:fe5aa402d02318db2f41e471320b2ecca6085b8f595a034c037085732e49c04a # via pytest-cov -cryptography==50.0.0 \ - --hash=sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03 \ - --hash=sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7 \ - --hash=sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437 \ - --hash=sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987 \ - --hash=sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025 \ - --hash=sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037 \ - --hash=sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269 \ - --hash=sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105 \ - --hash=sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc \ - --hash=sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95 \ - --hash=sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b \ - --hash=sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47 \ - --hash=sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c \ - --hash=sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41 \ - --hash=sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c \ - --hash=sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d \ - --hash=sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7 \ - --hash=sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c \ - --hash=sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708 \ - --hash=sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef \ - --hash=sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f \ - --hash=sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f \ - --hash=sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a \ - --hash=sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f \ - --hash=sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a \ - --hash=sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a \ - --hash=sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e \ - --hash=sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3 \ - --hash=sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d \ - --hash=sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3 \ - --hash=sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f \ - --hash=sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae \ - --hash=sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30 \ - --hash=sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9 \ - --hash=sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9 \ - --hash=sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07 \ - --hash=sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba \ - --hash=sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3 \ - --hash=sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f \ - --hash=sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533 \ - --hash=sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5 \ - --hash=sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11 \ - --hash=sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9 \ - --hash=sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f \ - --hash=sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169 \ - --hash=sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645 +cryptography==50.0.1 \ + --hash=sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71 \ + --hash=sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23 \ + --hash=sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6 \ + --hash=sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e \ + --hash=sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361 \ + --hash=sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054 \ + --hash=sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f \ + --hash=sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6 \ + --hash=sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49 \ + --hash=sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5 \ + --hash=sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149 \ + --hash=sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88 \ + --hash=sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad \ + --hash=sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a \ + --hash=sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f \ + --hash=sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2 \ + --hash=sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20 \ + --hash=sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45 \ + --hash=sha256:5fe939deeb161024a6be98229c953b6591fef1f41214497a78fe793a244c017f \ + --hash=sha256:693c99b49bd37d0d096e4334c10232c77248c415b98d35236094cdf96d57258b \ + --hash=sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527 \ + --hash=sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3 \ + --hash=sha256:804728ce710890870f3aaa344b2e161172d258d768ac139d02cfd9092d0d94e6 \ + --hash=sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367 \ + --hash=sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0 \ + --hash=sha256:9cb3cb952cf5a8abd50c782a98a89d71699715e802fe349704b47f2425b42a94 \ + --hash=sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239 \ + --hash=sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b \ + --hash=sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a \ + --hash=sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9 \ + --hash=sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5 \ + --hash=sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc \ + --hash=sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648 \ + --hash=sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986 \ + --hash=sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959 \ + --hash=sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0 \ + --hash=sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17 \ + --hash=sha256:d63ae8f6481fec907ac0f588eee8a90aefde112c633131fe540e5711ddbb5a4e \ + --hash=sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733 \ + --hash=sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f \ + --hash=sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8 \ + --hash=sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf \ + --hash=sha256:fb4b9672d389c738b175c4166e78310f8a70358886aacd9173ee03a85ffdc671 \ + --hash=sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80 \ + --hash=sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558 \ + --hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef # via # authlib # azure-storage-blob @@ -1080,8 +1117,8 @@ cuda-bindings==13.3.1 \ --hash=sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a \ --hash=sha256:efd4c814d311ec08c981f6dded1dbe7d4b371067ee4f6c14cccec4bde9590f80 # via torch -cuda-pathfinder==1.6.0 \ - --hash=sha256:1503af579d8379c24bdd65528379bc57039b0455be9f5f9686cf8e473a1fce51 +cuda-pathfinder==1.8.1 \ + --hash=sha256:ae0137ff9e56ea97499bcbf54f5f2778ec25f3266715ac86da192a795af982a8 # via cuda-bindings cuda-toolkit==13.0.3.0 \ --hash=sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f @@ -1160,13 +1197,13 @@ cymem==2.0.13 \ # preshed # spacy # thinc -d3blocks==1.8.1 \ - --hash=sha256:2c4834bc8496547b1a7144ac3e6f89281d5450bd286ccb05fa635f036f1f9ead \ - --hash=sha256:510d2a443f6c9bf47e75739357a69d0eb9be2436f2f68e3801dd9d85a801cadb +d3blocks==1.8.6 \ + --hash=sha256:0aa8966bfc714281d1d447e85c48c28dd03d343e691f9e5d1f3cdc359dfb4c1a \ + --hash=sha256:a08d5a112f10fb38e5df1aca3aaf543e1ef457095bca1974ed9a5a690d8432c7 # via semantica (pyproject.toml) -d3graph==3.0.1 \ - --hash=sha256:345254d65e50d0de3a1a099c6bc99139a9fba59179b8c6b2212b80af6b8d2775 \ - --hash=sha256:91598fa901c6a1b01b634ae4974b2a220731441f669b6d66a2ca066c1fe05ee6 +d3graph==3.1.3 \ + --hash=sha256:9063b4a1dd4671ee6eb0bba53b90bd9bf42c50036bda3600709c62d40b24b102 \ + --hash=sha256:a488e07b3b840daa50193468235d8706e793931e1739e9598f666c1d7b5d48a2 # via d3blocks datazets==1.1.4 \ --hash=sha256:8c11d3a8d2ee2d49ef6d1f1512bdd33f7c7c592aa48267fe6e44ed7726e16b72 \ @@ -1238,60 +1275,59 @@ distro==1.9.0 \ --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 # via - # anthropic # google-genai # groq + # langsmith # openai doclang==0.7.3 \ --hash=sha256:9440c4ca9f7e061a7b8d33bdf15b1029be69a4c13cd8952dd6ce541884e4c685 \ --hash=sha256:ca50615357e46ebf9597bb9065b9112367103ec24bd539f8ae12649224cf50b0 # via docling-core -docling==2.119.0 \ - --hash=sha256:04b9aef29b9b94fc6e56fcd440824337d132a84074c7ba20b9cf055f220e72fb \ - --hash=sha256:3ff6f1dbe9f53ac034aca03c39fae374b381dc1eda4ad85d27a458fc13fbc917 +docling==2.124.0 \ + --hash=sha256:822474679fc3f14081db8a5aa985057de3ec889390fc24534a8c269225af0b48 \ + --hash=sha256:bff775aea776429e3bc1d20972dc173e80cd748b1202ebadc4c3ab64c156cdb5 # via semantica (pyproject.toml) -docling-core==2.91.0 \ - --hash=sha256:4949a5dd77ae1daf4153c095897d3bdde1c870f2bbe401bf94d8834bef867998 \ - --hash=sha256:dc40fe76524a2700f869265015a9ef86027888e73b5652f324b3b5c52a2df240 +docling-core==2.93.0 \ + --hash=sha256:0bdf6f42b0c83ca4dcd3916e6d1fbcfdc87168d93f192e00a1d2b0c55d5357d4 \ + --hash=sha256:5c0b9df0d4f5a969928968484b4736a4201b61d41f5132ce9fbcba00a50be5e8 # via - # docling-ibm-models # docling-parse # docling-slim -docling-ibm-models==3.14.0 \ - --hash=sha256:795d39cd0f7b1e14a702e681b0ef0f9bd31deaedddb4e2686ad577296ecb8fc9 \ - --hash=sha256:def964e3d524f66c7321ef9d48d4021278f14319f01d3f78058cd2324f641e22 +docling-ibm-models==4.0.1 \ + --hash=sha256:05f70f2b6c0f26ab93598afdb686cc8568720bda3006f99c6eb29e7e414545a4 \ + --hash=sha256:47d490d0e45a56c48a4064970339547ba9d759129353a209879d81977c6fb80a # via docling-slim -docling-parse==7.12.1 \ - --hash=sha256:0fcc7234daf1db53e1c3d8063083d850620c3e44d2975be1c01fe0bbcd86b3d1 \ - --hash=sha256:187cb186bed683e6cc7f00ffc010f2bf4f541cb87964194a8bf101b558b8455e \ - --hash=sha256:24b29a5ca40071b3d5ea7494c22a5552f4d9d29bf93bdfc1925986d922feff5f \ - --hash=sha256:25c8759b19292ad8cf31e1b133980c30f0fd8800b510c02c56a3af5dbb989232 \ - --hash=sha256:32e4c806e63f5e6dcf63643d3472b241e7786653bad82321c95b937b69942a5e \ - --hash=sha256:33fc4afd8b46a2c760a8ca4558b4d744d6c9d207318581f3234fec3df91ca85f \ - --hash=sha256:344c7f1da55be7fc992045fb5ff07b411e589b1da62a8f943842ac7ac986245a \ - --hash=sha256:447ced8771f7053f9f834efec363d578d92598119b95e83bed2551c185a7355f \ - --hash=sha256:4f73b41fda9c4fa6beb6c3d685071dd28a2c1f00e69f0133fbc15bda8c0f7ab7 \ - --hash=sha256:55d3189fd36a5afcab884680e51c9131fd28359f2af32e718dd34a7432d62af3 \ - --hash=sha256:5e38953b7e023b6456c8e989d2e92a212489d7b412616c1b6d7c9b62953ee564 \ - --hash=sha256:61a2f203a8e4ce5cd27fba2b6de4474eb11b5e6e1e17116e0dd349376d234d6d \ - --hash=sha256:6488966275412d599aa2fcd0920cea38bb20ac937aeb5bd135ea5354c6dcb5b6 \ - --hash=sha256:8a3debe619e2303442139472ec39a21cdb4714b27d026cf0c34233411779c8c4 \ - --hash=sha256:8e5ee53b6dfe6806b7c3a0d84d564530c5be9dfe0cb3823014f4654d92f345ee \ - --hash=sha256:a3c91f46776ba2aa3c31667747f4f39e91f8c1b58e391663c5e44d7bb416a3f6 \ - --hash=sha256:b3a740f373f00c87555cdbbf996b726df1e99a9de5d95a24484f71d1f7394077 \ - --hash=sha256:c73d965106e07f4af76f137ff1ad4ed67c7fc89cc745f96c214377bfde2e9baf \ - --hash=sha256:cc40315ddda283efc8abe5e62472464c1ff9f55f38ed65fc6f7e42c7ff425062 \ - --hash=sha256:cfddd1f3a3631e87d4c965b4ffd0dc95e249b046af0a6b8916e129285fb0e80c \ - --hash=sha256:d5c015e19e9be88ce001115a1913bf6addf4c2931c3556e9cf2a10b596bbe46d \ - --hash=sha256:dd75ec4d62f42db4a56b1cce6c0f6d3a8605feb364027f95b22dae7465f23ab0 \ - --hash=sha256:e1fef05edfc00ef3133fa38b8b169e76d1897ac71f550577971751aa2cb8fe5d \ - --hash=sha256:fb9426f173fadece791c0007b98b94ba5ee8c71721748005fa992b5d2a8cd0ad \ - --hash=sha256:ff5ac71bc2a702cbc7bd4fd7c2fcbf6af483c982af4bae36fb727a9db169a849 \ - --hash=sha256:fff030c85eb85fc6589bdabc64bdc58a0c82cd6e0870ff9cf55114be525f46f5 +docling-parse==7.16.0 \ + --hash=sha256:12a1b9d145cc44d94a7b80e4ef62ea732b00cbd7803b1dac0d803893a82c100c \ + --hash=sha256:1e2bebe14d3ce0b6e7f256f2e4bbc7f2405dc9ddc91bbc5031dcba07bf97d9b8 \ + --hash=sha256:1ed3fd24a26a2b00357cc199001e2817d70163447b456ac0c66efe1823e38404 \ + --hash=sha256:2eec0297713e85076a9856ba455b546ec108b7d1d0c485a4a0260e820e722ca9 \ + --hash=sha256:4a80901f294579681c1f51601892b1a3068a4599986bc3a92321305b78daca59 \ + --hash=sha256:4dad04e693ec3d120885249a097dc61b0823f01b0a99c52696828b9eb6649e0b \ + --hash=sha256:63f161f3cfcaefef919f60ad7dba805c2eda3daac1022fbdbf3368e51ea10520 \ + --hash=sha256:6ab9b9deefd347166d670f894d27f697bf2d213a51e526516dc9ff44e0c19b5a \ + --hash=sha256:7b400182bc6de50cb7705eb7d944fe5dfe747023a5b8557adcd643a5c014880d \ + --hash=sha256:870e781f7b7d1403d1168bf860c19fd2ab5ddb42335c60a1927673fa8fc615e2 \ + --hash=sha256:8d383646bd8f11969b1e45c0107ead107354e4ff20661943642091e92cffcc99 \ + --hash=sha256:8e63af02e5bcea71a407776e4f8d944f5db4ee840cb3abb9c4ecaae6768e0efb \ + --hash=sha256:96756c2205aaa958ee2930e7ded33b756c5ff663724f8aa7dce5cd241296cf1b \ + --hash=sha256:9c11e3c7cb509fa27de277795211bd80369a2e682ed0e3d7170e12df13ff6d05 \ + --hash=sha256:ab71b6b9d1085b08ca2a3529356ae3122e56a8be693a32d37e996ae88c6fe866 \ + --hash=sha256:b2c2bf844d567f729aa2a48f22b3c4f5d7e5a7db6b6b3a9ec07b45cf31d4ce3e \ + --hash=sha256:b4dc06844b775014bbf9a8f2d830d6700a47b2ec13c62a4ee61a2c9382fe4dca \ + --hash=sha256:c3b4d67da75631a28252bf574023e4a45fdd422603ea07cf73a8e70074547436 \ + --hash=sha256:c9d5994bdcb8b12d157871ca2fd1e399afeac4528a2343af7d82e87dfca35e82 \ + --hash=sha256:cb173b447806ff3a4af3c06936e9585dc201b3ecbb958aebde37a2725471675f \ + --hash=sha256:d81008264fec675bd75ddbb79ec20823dd638f3ab31d889e08991d769cc592ab \ + --hash=sha256:da0483decd77d9a967c54e751107c36a3c3bae186e989f81339dff5037f0a7fe \ + --hash=sha256:e6104ae8e881ddb6ed78275c662cf857dbfbb2af4853189cc1d4ba57f2a1ae1f \ + --hash=sha256:e71584a38d8bfaaf7947557b51359a260266b348507b0682814fa39f33cb2f0b \ + --hash=sha256:ea261b705ffd1cac97b0ca2eab656b2fe4434ff4876be38eddf0b2c9bb582133 \ + --hash=sha256:f1abfe050b670bcb25c7bcefc5d876bc3561949c1bff177e8a8a3d8d6f217314 # via docling-slim -docling-slim==2.119.0 \ - --hash=sha256:7b4ee3891e536403f07b6ab702bc757d6c3e4fe146475656d97cf911ac96db8f \ - --hash=sha256:ca385bc6b0ca99a4f0bba07feaa4c0fc4d5fa685a22184baa79de4bd87598323 +docling-slim==2.124.0 \ + --hash=sha256:2c7c667394d3eae9080bc15143ed6f1d39a4838090d9e3f29be212606e6676bd \ + --hash=sha256:f38f0fb4d325d189cd2bae4ff53c236582191b211e39ec0c565a5d619ac700b3 # via docling docstring-parser==0.18.0 \ --hash=sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015 \ @@ -1321,25 +1357,27 @@ faiss-cpu==1.15.0 \ --hash=sha256:e0fe7278f3784b7d205ae715a115801cafb75f6e55db6b0fbe83c4ff379f003f \ --hash=sha256:ec9b29aae29e428c085c2d49dbb02e4673cdea75db418d420f9e60e0b4184498 # via semantica (pyproject.toml) -faker==40.36.0 \ - --hash=sha256:754048c76c03afa7de83eee8f4bcee3cf668cbb7d995f54a4e9678db7f110308 \ - --hash=sha256:82b9497d9cfe017048075bcf969298a74b1b6e39f5e4dad1211085d1133f7b62 +faker==40.37.0 \ + --hash=sha256:a92dff7f310e61fb544c61720e15edb2e7448bc33d15a321a99e9ab7b94abf54 \ + --hash=sha256:ddbafa55c94d5b69c08ced3a7f202614204a02e07ba6548c729b8d18acc0b490 # via polyfactory -falkordb==1.6.2 \ - --hash=sha256:73dbbd9df61c56f45cf2fe8b9028888ad4892712790ce4e40d7ceabe118609ae \ - --hash=sha256:b17571ebf4d65dbd3588e8c470b16f0ddfea820e0a15ed73af0dd1a4728b480d +falkordb==1.7.1 \ + --hash=sha256:09dd89dfb668c6fe7741c0ec67fcdb5c7a4b009e87f065a644199170f4fa5766 \ + --hash=sha256:0e62d535edd5abf7b6d35e2493fba71118734313436ff803fcd5d1b20e9e2194 # via semantica (pyproject.toml) fastapi==0.141.1 \ --hash=sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3 \ --hash=sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1 - # via semantica (pyproject.toml) + # via + # semantica (pyproject.toml) + # google-adk fastembed==0.8.0 \ --hash=sha256:40bee672657574a1009e35ec50030a55f2b426842cb011845379817641bbbbd0 \ --hash=sha256:75966edfa8b006ee78514c726bd7f6a50721dadc89305279052be9db72fd53e8 # via semantica (pyproject.toml) -fastjsonschema==2.22.1 \ - --hash=sha256:0b83d1ce8d7845b959dcb20e1a5c3c8883b6541d9c52ab02cce5166b75ec805f \ - --hash=sha256:cf377ff5c9a6f4f3125fb35f75a2c5767bd824ffbcf62c209a93cd48d1453999 +fastjsonschema==2.22.2 \ + --hash=sha256:0fb3915616adac85ccfdd737d26be1089845d2019819505b42d39888458f74d4 \ + --hash=sha256:72064e12356a7d6ef02165be2946b9abadbdf238536e07eb587e3dbaa33099cf # via nbformat fastuuid==0.14.0 \ --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ @@ -1421,9 +1459,9 @@ fastuuid==0.14.0 \ --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d # via litellm -filelock==3.32.2 \ - --hash=sha256:87dd94cf281e586d135fa51132b8e3d9a598b316e90377a288663c9321036c82 \ - --hash=sha256:c33351e1f49cae33414acbc6d56784e6ecee82514ec90795da1161fc4836b5b8 +filelock==3.32.5 \ + --hash=sha256:142cd9fa77a872c5e78c62329a0d15278fadc686eb89e760017968961a4fd6b2 \ + --hash=sha256:f6a6a28f743f9b95ce19db5abe0f376f75eb56517dff21e1a4751e2657d3e83d # via # huggingface-hub # python-discovery @@ -1440,58 +1478,78 @@ flake8==7.3.0 \ flatbuffers==25.12.19 \ --hash=sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4 # via onnxruntime -fonttools==4.63.0 \ - --hash=sha256:032038247a96c1690f9f31e377c389383c902531b085aa4e4dabd6f57f870e69 \ - --hash=sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c \ - --hash=sha256:0c18358a155d75034911c5ee397a5b44cd19dd325dbb8b35fb60bf421d6a72ac \ - --hash=sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096 \ - --hash=sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d \ - --hash=sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68 \ - --hash=sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616 \ - --hash=sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78 \ - --hash=sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f \ - --hash=sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b \ - --hash=sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b \ - --hash=sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02 \ - --hash=sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d \ - --hash=sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f \ - --hash=sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8 \ - --hash=sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272 \ - --hash=sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49 \ - --hash=sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419 \ - --hash=sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001 \ - --hash=sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03 \ - --hash=sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196 \ - --hash=sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9 \ - --hash=sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e \ - --hash=sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5 \ - --hash=sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007 \ - --hash=sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380 \ - --hash=sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8 \ - --hash=sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27 \ - --hash=sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40 \ - --hash=sha256:a8b33a82979e0a6a34ff435cc81317be1f95ec1ebb7a3a2d1c8a6a54f02ae44e \ - --hash=sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0 \ - --hash=sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263 \ - --hash=sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb \ - --hash=sha256:b1cd75a03ad8cb5bc40c90bfde68c0c47de423aa19e5c0f362b43520645eea94 \ - --hash=sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b \ - --hash=sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6 \ - --hash=sha256:c0425b277a59cff3d80ca42162a8de360f318438a2ac83570842a678d826d579 \ - --hash=sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4 \ - --hash=sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59 \ - --hash=sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0 \ - --hash=sha256:cb014d58140a38135f16064c74c652ed57aa0b75cbf8bb59cac821f7edb5334e \ - --hash=sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be \ - --hash=sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd \ - --hash=sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18 \ - --hash=sha256:d7e5c9973aa04c95650c96e5f5ad865fbf42d62079163ecfab1e01cbc2504c22 \ - --hash=sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0 \ - --hash=sha256:e3297a6a4059b4acc3a1e9a8b04741f240a80044eef08ebd32e8b5bcdddce75b \ - --hash=sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b \ - --hash=sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af \ - --hash=sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745 +fonttools==4.64.0 \ + --hash=sha256:043f6c572bf236f2a76e762c25f841daea11e8fc03e78088d7be66e0c5b4e4c0 \ + --hash=sha256:06b6409b868494556a831ae33b2d9a090476c37516b38d70f45a9720b460d423 \ + --hash=sha256:08f172961e11f4eb4f80f2f20049e09b0ea8e044fa6d456fed8346eb8588f360 \ + --hash=sha256:09657817b75575822bcd6098ef0ebf0386f34430839ee53109e70fd40a7f6539 \ + --hash=sha256:1c3661324f3f0fa4539a32288a3e0711a5f3ccf020036e760bb558ae9811a16f \ + --hash=sha256:1e4e84b47839d35be24dbf476845a34f2ccf99707b66df125c1c414d3e86d25d \ + --hash=sha256:236e59bc7e2a63557a4d7b013f9cb9e28d9aebc45bc09f85e545e6bf091db626 \ + --hash=sha256:2524a26f8fdb9051b0d778d052f5d238285ca9f91a7dc004514c7d6cf38d35f4 \ + --hash=sha256:2730946ca8f12c356bd98eb9b2b095c8e761ed05bed5afb0d5b380cebe4f6370 \ + --hash=sha256:2c42237b7e8c6813643e57d3efed3be094d4c06339dc2166b626e2cc5c12ee93 \ + --hash=sha256:3200180abc69639483cf54a17cca2e13c31ede5f665979ea0a9c829d093f372f \ + --hash=sha256:398b14f89ca950b288bd290875f07e4e10685644fa4ac668546fb107b1ada4d4 \ + --hash=sha256:45e3ecc3888f1637094fd75cd8fc727f3a4b06d1ddf89181126c071e244fd2a5 \ + --hash=sha256:4691a122b8c1d0d82d6e7510ce59d5c42146518240274b53e912e255573924f7 \ + --hash=sha256:498f02ea92c9ca18c0f9c581ea93184a9d56c25b0af14189b0767adaf34235d8 \ + --hash=sha256:4a05783ff54ce4c7a28f18e5772efdf63c219374bd9ffc55452182e1cef8be60 \ + --hash=sha256:507c553cdb5abe2e951b5368423849fe29911a828c2135319c3e500e3bf25b32 \ + --hash=sha256:50e52b6f479ddb1fe32423c2ec860811f36584cf6eabf279fb9a4f98b859a8b4 \ + --hash=sha256:53eee22af5b5a305c1ee2652955ed46b148e881456fcec1e7f0eb27f642f6bb4 \ + --hash=sha256:5af87d1a6d247d7467ee082ae977a5443b2c45f8cd4d59375b6daa38d523c2de \ + --hash=sha256:5b90ad6637237b636d15c9ae8b7c4a7a1c194f33def378677e468c13fd4542f8 \ + --hash=sha256:5bfdaada437e7730c17d366bd7bb8c4a16639963ddbfc1b2f302a68a17a290e7 \ + --hash=sha256:66a83f93579fb3493e458c4449d1d566a7b2a1c7b19915cd0fa3c9b8b5a8540b \ + --hash=sha256:6786bed88581e19bc4f28ea7a64ad531e8f54acf50327fddca942688824a60bd \ + --hash=sha256:6946c033a144086d5b98c976b72f476b70c93fbbedf914eee0e886f073a4e9fa \ + --hash=sha256:6eae4376adb104c2acfa76fd9ea0cb12b572ca1d70eceac709871f638ff76e93 \ + --hash=sha256:6f1ce9ef9a1b13098efdc2e43a2ed96d9851bbde7b31c652a87552c4efe9b422 \ + --hash=sha256:70fd99e5a09fb77f14b29d70879a4fce9529b2d2948b14c96708e0a61e001b98 \ + --hash=sha256:730eed859508cb7b0775ebe6bb39f18901f168eb989d8ee23a4fe082700e1e3f \ + --hash=sha256:769fb64412ca237547ca73f111a64252d9e32c9d938bed51ed537bc9146a8f54 \ + --hash=sha256:7d7995b906666037d7114c20a5566a372902747452af7d5bd4cd6bca8f1a2550 \ + --hash=sha256:801fd04899d72eab34f02ab78d0451525621b3bd589da9d2d480dfffe951b643 \ + --hash=sha256:8252f20108e557532f91d7d6dd9af87c16ed6fa930f65516aa480fa2cfed3363 \ + --hash=sha256:83cc48d1411d2ff388dab99973dca81172cc9ceae9c9799da9548d494cfb38cb \ + --hash=sha256:89356c0793b474af7e49ec90d39fb2363e2341516a90460e38231df5ebe8acd5 \ + --hash=sha256:8dd18fdff0ac9759b8d67a714730abee07b2312e3656c20ba5affb0107094762 \ + --hash=sha256:917fd520bb60809d83c14d43cfe48d5ad2516abaf2c073d65a431800dade2d29 \ + --hash=sha256:9443eefff58aad558608f352092e1be6d278980e8c3b4e8621fcbfda97818500 \ + --hash=sha256:9ecb2b206b5b2386f6968721a0770226b66bdd54adc4279bfff3ddf62873eed8 \ + --hash=sha256:a0afa8bac675445dc0e2ba2891ecbedd9be89cb437afa94c823e0290cc2c4bc5 \ + --hash=sha256:a3238a693e806a3158375c6403b8f6f71d86eb9c149b60c97f26dfd560c98ac8 \ + --hash=sha256:a515f664cad988f2295056833a59f62220bc3e46afdaffe389a29060f6712355 \ + --hash=sha256:a8c631303bb1fd7be3067c47536a30ff1fcb4846d6008c112bc52a03f7cd6965 \ + --hash=sha256:b2763e452b025ee8e990f0462e76052de9bb094ebc21d296f62c6dfe958886b4 \ + --hash=sha256:b4a7af455ffed980925bc0ebf5b8d6239e6c3e797d9d755b6db192fb3080d614 \ + --hash=sha256:be084d19a3ac0c8b2aba696680642d703118d3b1f18cf83f5b7dbaf0ffc62ab6 \ + --hash=sha256:c3c1fb656063a2f762db5378ea8d38ad5f7836b4f3fb8c4652270ded43df2935 \ + --hash=sha256:c60be0aed97a32c6ba8cee21f0d0477136e495451bd97910f589ac892db120d4 \ + --hash=sha256:cf67f96dc0bfe9607f5f2b734cedfbe2f6f995231adee4ccefa12872044d452d \ + --hash=sha256:d16102cbcd4615b09c64e6022733faccc93200785f1ab0d4493afb8b0261edde \ + --hash=sha256:d30c966bea2deffa19c738c81776f7182da5ccabd97e666bae4f3d6ba87341d9 \ + --hash=sha256:d652592c71683941b768306fa1c7c6ce1bb9b072505043feafe86305d71030b7 \ + --hash=sha256:da4c9bdeaf6b06c12d13d0addfc8ef15aa9695d26574a6dc10751258bef72f30 \ + --hash=sha256:dac25768be4c03a990c359f408cb7e8958ed0e93061e495b3642ce7909761205 \ + --hash=sha256:dc96150f99e05a317cb1f042b92c4cf8bc93cdb1f9f85717322e202ecdf2e505 \ + --hash=sha256:de8acaa5f4160f537a3cf41b031171d51004b9f4aebfa6c194f18dffa9533d03 \ + --hash=sha256:e412767d1c9765cf1b82f7b00f1686c6ca5809ebb77af363b3f9f2325a465c01 \ + --hash=sha256:e4812f71c39d77ec5041348dafa400532adf7bf8f1fffa9aa6495fce5876d7b8 \ + --hash=sha256:e63b63b8b5fdb8e29318dff2b15c5f852be46e972775b466f75b848f6eed4502 \ + --hash=sha256:e662f874ab2c7da9861584db44a13573e0936df087215f63013138f6e5eba083 \ + --hash=sha256:e7b34209eef39462563c05ea9dcf51c272a2ded56f5753da925e66bca3baa484 \ + --hash=sha256:ecb2e59a7bc692fee64dda6010deb66222335693b30046f15cccf81233aa715f \ + --hash=sha256:f521d79d6acda4923b264805541696f452079db0952a5bb96f9ff742f50629ec \ + --hash=sha256:f8669ce37851b597d3435b91fefa51139e58d506ca449ca0e5bb68c63b8b6d2b \ + --hash=sha256:fa75c7970bc6bca340cc6e20f20f069201bfcb50094c31a536fd99724d1d01ca \ + --hash=sha256:ff7aff4637fbf71394df139c63ccfe08a47aa4252d2f91224ddb3335c716c925 # via matplotlib +formulaic==1.2.2 \ + --hash=sha256:0f84ff49e3fc9dc0e68ab08a0a9427874021aa6c558e66b44dc634a35739b09b \ + --hash=sha256:c99e8f11ff7d327eaecaf63855ca69b7fa0da100ad6c0041ef80912fbac667e6 + # via statsmodels fqdn==1.5.1 \ --hash=sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f \ --hash=sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014 @@ -1668,9 +1726,13 @@ gitdb==4.0.12 \ --hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \ --hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf # via gitpython -gitpython==3.1.59 \ - --hash=sha256:0a1475cfdc38a5bfba1a3e9a4a9da52a39749ecec322b772915c019f94e5b7e4 \ - --hash=sha256:67a82f537384578643624c8b2c531938a9b82be431663e575dcf638526631d4c +gitpython==3.1.61 \ + --hash=sha256:8ab28c9da863cdd9e7d7694ec46cf3e6c9a12d8a30a1acd3447aec11975d530c \ + --hash=sha256:f51c24d8c0f733a195447385f5774a5dfe8767f5acfd7994a33755644c6ecc95 + # via semantica (pyproject.toml) +google-adk==2.7.0 \ + --hash=sha256:283e16dc5ffab7684e17098f7282470339e88a3d16ecfa62b4025680672c906b \ + --hash=sha256:6517c0383d9449484b6398a5b60c6408dbd4de369e957eebc3b9d8d0c90736e9 # via semantica (pyproject.toml) google-api-core==2.34.0 \ --hash=sha256:98a779fe72de956eb1c9c2f47ff4c4432a668ece1a002ec38bed07ec2698ae59 \ @@ -1678,21 +1740,22 @@ google-api-core==2.34.0 \ # via # google-cloud-core # google-cloud-storage -google-auth==2.56.3 \ - --hash=sha256:40e229fc901f0a305b553050e5fce562d509bee0435be053abfa91582b51b90c \ - --hash=sha256:8ec438808f813ad034535000261eed1067475d229d05bbf4216e78c3f2362e53 +google-auth==2.57.0 \ + --hash=sha256:180dafe015cfb62193bea26b677500fab5b9fd51a1e825ebf3ad9b182047ae59 \ + --hash=sha256:9b4f96d6a1feb5f7201231f47cfb3de08d8f176f8a61f9e461555116e95a8789 # via + # google-adk # google-api-core # google-cloud-core # google-cloud-storage # google-genai -google-cloud-core==2.6.1 \ - --hash=sha256:1e044b131f2ae097b92312fa195164b0aeb6dc6a88e00231e1210516314c420c \ - --hash=sha256:2682a8a4474a32f56292fb4bca7fa7e4fb0b4af958f6abfe4bca8d195747fd45 +google-cloud-core==2.7.0 \ + --hash=sha256:874aaf89765db87a9b911b7a2ca7c5068554868eed9e75c7766affe342a2913d \ + --hash=sha256:c18a250904cfdda021eb3ae8b8238c9f9ca272a4cbbfb5cba946b3fe3022eed1 # via google-cloud-storage -google-cloud-storage==3.13.1 \ - --hash=sha256:98208de6c21e85cecd3eb44551894efff33d98365500e178867d4305854a770a \ - --hash=sha256:a80bf8cac2794808aa61c50c5f769ecbbe2d10331bacd0d69d30e59b14b346b2 +google-cloud-storage==2.19.0 \ + --hash=sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba \ + --hash=sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2 # via semantica (pyproject.toml) google-crc32c==1.8.0 \ --hash=sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8 \ @@ -1731,87 +1794,91 @@ google-crc32c==1.8.0 \ # via # google-cloud-storage # google-resumable-media -google-genai==2.19.0 \ - --hash=sha256:36e0326dd886b52ef765be4c46042732b46b21f637abbe060e3db7c3de23974c \ - --hash=sha256:d8f4126643793a7de230c396bcd142d21c948c8bb57507580e152549a7a41d9d - # via semantica (pyproject.toml) -google-resumable-media==2.10.1 \ - --hash=sha256:224975032ddb73f7ed9e2f0f4cc08ed1b06874c52d48cc8533e3eb72980b21a0 \ - --hash=sha256:4e2cbc704207ddc09f23b1f18e8ef4a4ccbfe0f1768b370e5c969704adbd0a1c +google-genai==2.22.0 \ + --hash=sha256:9fa3b5d9ddb635005d8ab2d6206fb2b3d7204b66965bbce7de13ecd1a866ebcd \ + --hash=sha256:c514001c45470cc0a942440ae1b8215445d12bfb6c373aac94637127e1f74ec6 + # via + # semantica (pyproject.toml) + # google-adk +google-resumable-media==2.10.2 \ + --hash=sha256:1de441703cd298d75a419bfdc0066e9fc7b0a1de630df96eea8ce8f5c759358c \ + --hash=sha256:e3cedc827a4ea41e216582d74346f1fb9fceb625a8c3c53912f2ca1d663334d7 # via google-cloud-storage -googleapis-common-protos==1.75.1 \ - --hash=sha256:28a1934bcd33b9c9da66ac301a0a4227e3367f095a17d0375cb98f0a09d93b79 \ - --hash=sha256:d3042c6c5a2d4e67113104d6b6818b59b6bd92a197f2a91508e801fe815cf071 +googleapis-common-protos==1.75.2 \ + --hash=sha256:6b83302f554ea93a0f48409c7fc2050f954bcbcddb7e3a9c76d4a823cb22920e \ + --hash=sha256:8829a3d1e4508c5b7b9a6b9525f7fccff611f8531644579a76466c29295d4bb2 # via google-api-core graphviz==0.21 \ --hash=sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78 \ --hash=sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42 + # via + # semantica (pyproject.toml) + # google-adk +groq==1.7.0 \ + --hash=sha256:cb1518f823423d4e52445859eb7d2918a927794cec12f8d5e50d9161e3690fc8 \ + --hash=sha256:d582dbb3f071b92ca339baba83af9f57ba6f46a51c34b04565d7cb9badb2785b # via semantica (pyproject.toml) -groq==1.6.0 \ - --hash=sha256:c4237ecf0053ba85fd926bb31cb4b178996310474b4618867994e3e40d8e3c02 \ - --hash=sha256:fa16db582455db324adcff1b5908519474736872e28cd5c8fa2b8bef6860e12a - # via semantica (pyproject.toml) -grpcio==1.83.0 \ - --hash=sha256:009667eaf3dcd5224c713589cdc98e7ca4ed0ff0b61132c6b276e930eb83a2df \ - --hash=sha256:10b3fa0475eb572c9a81a6fe37fa16a9c500c0c91cfc148cac15692b7e3c2867 \ - --hash=sha256:1aa567f8c3f19850ffd5d2858c9a8ea7c80f0db6c01186b71eb31e923ec984f5 \ - --hash=sha256:1c699bbb20f143c8f2bff219de578aa2dc1f919399d67dc702b038b986ee62df \ - --hash=sha256:28f6c35ac8fcf10e4594f138e468f194360089dde40d126a7033e863fc479930 \ - --hash=sha256:2b5e75c34842cd9c1b95285ca395c6a569664b81e3ffa6b714125922942abaaf \ - --hash=sha256:2bb48cb5e6dd005ca12b89ce4b6ac0b48ff3112c747542ee7986ef611a8ca6d9 \ - --hash=sha256:32e11c37f5285b0c6fa3042c05fe06903696689749833fc64e67dec71b9bbe33 \ - --hash=sha256:33898e6a28e4ae598f1577cb1c4fec2a15c033d0ec52b9b45a09610dd045b9da \ - --hash=sha256:35a5b1c192496b6c25956eebfa963468935612206fd2543ac3ce981e6a5e0f03 \ - --hash=sha256:3f351629f6ae16ecc0ec3553e586a6763ffd9f6114044286d0cbec3e09241bfa \ - --hash=sha256:4772402f43517b4824980be4b3b2274a81eec0004a70009473c31b340d43e223 \ - --hash=sha256:4e3eedfc92b6b9f2960115e7e620cf0cbf80bb7849a51ce3820dc54dfd88b6b9 \ - --hash=sha256:4fcaa7c45c45b4a89e2867d1f1785d9481a788399d915e341ed2eb49aeef9dd4 \ - --hash=sha256:5882c1a721b50ce0123ee5e839e1ab059ad72a7ade76cdf2d5bd833b56791acf \ - --hash=sha256:5f20a988480b0f28207f057f7f7ae1313393c3cef0adcfeae8248f9947eaf881 \ - --hash=sha256:61007cd08640abc5c54547ee32505474c482cd733a53cb87551ea81faa6350af \ - --hash=sha256:62003babc444a606dcd1f009cd16391ce23669ae4ad6ec267a873da7937a69f5 \ - --hash=sha256:6662f3b1e07cc7493d437351860dc867bddc6a93c83ecf33bbfdaf0c217ab2d0 \ - --hash=sha256:6755ed67cc3e454d51ae9f6e1915b80d3942fa4de956ef48dacd45ab7f40b727 \ - --hash=sha256:6b6c666a1d5613ff360c9e90f44665e3a88b25a815209ddbc0917eec281931cb \ - --hash=sha256:6be5c807b717be3dd649446f021301fd7907e376318675d2147823071034112a \ - --hash=sha256:6e01ecd9d8ef280abe1365138a4dc318f9a5287f4cb1b41d07816f796653f735 \ - --hash=sha256:6fb8a1dd0c6f0f931e69e9d0dc6d1c406ed2a44fa963414eafba07b7fb685d16 \ - --hash=sha256:7416952ca770477990257206276999056f8316d79196f2f25942393e58a20b49 \ - --hash=sha256:74fe6f9e8a35c7dbf32255ee154d15e3e5338a81ed39173d079d594d2e544cd1 \ - --hash=sha256:7674587248fbbb2ac6e4eecf83a8a0f3d91a928f941de571acfd3a2f007fbc24 \ - --hash=sha256:7936f2a56cf04f6514705c0fedf400971de01b6aa1719327e4718f410a765e2b \ - --hash=sha256:7bd82671b39065ba18cd536e9cd45b27ff649053f81ddd2c6a966d595067080f \ - --hash=sha256:8f6c395e493d20c39b29392ca200e9aaeb78d0bc2f04db0c0a7da7ddc939aa57 \ - --hash=sha256:8fe04f1050a59f875601eb55d42b4f66946fe89817f967e34db1462ccd07dadf \ - --hash=sha256:8ff0b8767ddd62704e0d9571c1890af08d84a3a689ebba1807e62519d0b3277f \ - --hash=sha256:a21cb4eeeba124443f399be2e8b624943cde864dcbe588cb42e5c483a52a906c \ - --hash=sha256:aa074041231f03959cb097dd5517b0677b8ea49215bae01d5710a7b69dd59969 \ - --hash=sha256:aeb339838db07600481ef869507279b75326c75eac6d10f7afa62a0da1d2bcdd \ - --hash=sha256:b0a0be840e51b6b7ee9df9269770faf77bdf4b771053c257c21d12bad607714c \ - --hash=sha256:bb669918fd88936b15599caff4160a77ab74bdeb25f2231f6e45b61282d6107b \ - --hash=sha256:bc60215b5cb9fc8ca72942c498b551ac2305bd08f6ef8d4e3f0d21b64fbecd61 \ - --hash=sha256:c19b454d3d3f28db81f2c7c4dbaee96e7f6fd149721733ffe79d6bc530f17404 \ - --hash=sha256:c6444666317338e903093c7c756e6cc88eee59f798cb8dd41e87725bf54e1617 \ - --hash=sha256:c834e86d8fd2f03d7e4db49a027f7c5b89c5b88eed305543a5295bd6fee61e40 \ - --hash=sha256:cb056f6e171c42639a50460b2929c82241fda51f71cf3dcdd68090fe45095a45 \ - --hash=sha256:cb2906c61db4f9c64cc360054b5df70eeb81846228e9e56a4944bd415a63dadc \ - --hash=sha256:d05ff664100d429335b93c91b8b34ddf9e94a112205e7fa06dede309e44a4e4c \ - --hash=sha256:ee94a4016fdf8699fb1fd8a38652475ff677f1c72074cee44deeeb9a7e95e745 \ - --hash=sha256:f1c3e5689d4b90987b1d72022bcfe866a9a3dc66197484cf856d96b6150e7f45 \ - --hash=sha256:f47d62808b4c0a97b78bff88a6d4ca283a2a492b9a04a87d814af95ca3b9c19c \ - --hash=sha256:f4cee5fc86e84a0cf7ad1574b454c3320e087c07f55b7df5dc0ac6a873fb90c0 \ - --hash=sha256:f5e822a7e7d03282f6ad225e710493c48b9057a353358344a5f7c42b2b37618d \ - --hash=sha256:f5f410d7c2903eabb34789dfd6342eef04af1ad459943936b7e09a9f5bd417b9 \ - --hash=sha256:fba099b716e73512d61b97f71ea3c31a72abb36904036e316bf4dd148ca8dcc8 +grpcio==1.83.1 \ + --hash=sha256:0468b627f2987c9a77f7580030207cbd85457ffe52998beff4f0b5c38c58a72c \ + --hash=sha256:05ba265193fbd9f63355311ec7567bba32a72aeb8e9fd7b3443e4fcad87b0750 \ + --hash=sha256:0d07661944477517b12a239e18720c8d9038f80a62f2c56260fae80327f43d2a \ + --hash=sha256:0f736f8359cf7cb8d0914a290999765a4342b0c35f01adc6e3ba24598f9d62b7 \ + --hash=sha256:145b0050d24eb38accd9dc7ae09a3c09b8e7330159f3cfb46b1dba8711d50c42 \ + --hash=sha256:16138031a47b771860a16a975b53087f4fd5bbdbb2c03a188c5d90ad65d2bdae \ + --hash=sha256:179368d9361854616ce6f397d4716e07480129652752fcbcfc5a7260455ad6f2 \ + --hash=sha256:1fea1ae4795d4790579995a4dd5e20e7494d358e29a340e8368dab9723264328 \ + --hash=sha256:20d944d967843f8183f9f23d5916388362e5f8eeeae855bbe4354d906dc9f31b \ + --hash=sha256:2110059146fb0ea216e1ffddb29377b5cc2fd412a5b0a92e102616bd5edf18c2 \ + --hash=sha256:215cec07d11176507387bda4bf2751816e880f9bff8dc1ca524bfbb8ed8f2fad \ + --hash=sha256:2a141f7bfc1601a0942405a8af6334ab21ba1dd0fa49b8427686df7beebd374d \ + --hash=sha256:2e57af456385491a76e13c4aada8c8f43a8e47051e06ea97a9dbe2a49654e6db \ + --hash=sha256:34f1841fc6d1d76f8a2d74177eafa2d1ec7d7e039633488c9fcc1b375a1fc165 \ + --hash=sha256:47e6934ad38779271e2e7cc5f78a63a407cf3d98114c65c1fdbcd3f5a716f29b \ + --hash=sha256:4910b62f7d12197160bfb7de06d876d64dd12d43483e8292f98f49ca09b628d9 \ + --hash=sha256:4e7c1468cf37cca17ab18bc8072901eed8daeb81685589ccd07988e5a750ee67 \ + --hash=sha256:547645f02499c972f3edec9be4db9997f1d03df307c1c199772342ed6d8b3c6d \ + --hash=sha256:55656318d5dd387077396dffb929171ca3966e24bfead9a6c5dba9f889062cb4 \ + --hash=sha256:583bf2e8255040a4a312f9572dfe62a05271437b149550e1a536d5c47d2d1e8a \ + --hash=sha256:5acd14c6ddf047de62cbf8745b11103ea91abbf57d1b8edd5395ccd9fcd13abb \ + --hash=sha256:5ccc26715fd4defca5e129e280dd883b1737b65045ec50ffe22ce42104089519 \ + --hash=sha256:5cce1d9fe2887239f054dc9c314597e04f33d2e6bd3150a91c4946d7e5be5d98 \ + --hash=sha256:623c87c6d4a1cb30d82c4e896f95477050f2e01b4a1f8cf91ff2b1abdf89c457 \ + --hash=sha256:65c5a7210911ffe0f67b1cdc5308f9854b6d1f1b345e3e49ab7cac1ba50fa346 \ + --hash=sha256:72578aa07a4008f17521ef52debcc3acfd1e2c5426243bc3ffb56a38bfe610b7 \ + --hash=sha256:7b94174cbca93316888f805efbeb08f1c020f7b7493d2d50cc4f6b64ebb7e8bd \ + --hash=sha256:7d43e3bd2b7d749c2dbd41c2cc83d550c3343d299a19acbbba9e37ad8c11fa8e \ + --hash=sha256:81bbf35a46bf8cad2dfbb2eccc19c711befb58b288acb534bbcd0d74283202a6 \ + --hash=sha256:8b3c87ca908296bf125f841d3e1a2225a2b39aaa8ed7a57e7ccde465ee519bab \ + --hash=sha256:8d228e253b77865efcbdd7b5894ca882c9e0ea98c02b7d20582e61ded8dfd4b5 \ + --hash=sha256:907a5e5afb31f7a46376afc1a1edddd7afa00a74bbbc5b78979bbc34479581f6 \ + --hash=sha256:947d945f52e8ecf3cafd2bb7113502a16ccfda3e12c854443094de32d83ad432 \ + --hash=sha256:9cee6fcbf2eb57c4b49451787bfa87be8efc1ca02a0b327dd4b54d44502e362b \ + --hash=sha256:9daf5acf4fc9d5f5627229969c2580a91e511779d76e4ccdeb9f4770f05d8bc2 \ + --hash=sha256:9e703effe3ae779925c82ac24fdb82cf4105e1096810151ed9501c5f34546b9c \ + --hash=sha256:a2aea8bd6e0a34f12cbaddb7bb70bec836818789fa5c7ab7572c6b745396a2d4 \ + --hash=sha256:a4a87dc86b0393257a11eb11e911c4c3456cbacd1c1ab9e9441060d9a3ad126b \ + --hash=sha256:a6a282e81530cead60bbd752cc04950a57f224379e9821495d6a35bd5ce9b1f4 \ + --hash=sha256:abce7d43ec29cd39230fa8339de1a07643b55adc412a454850fbd875349950ff \ + --hash=sha256:b59eaaeeb03dde0a2708095fb50f1afa94f11dc1b459bb7790b53bfb8cf95153 \ + --hash=sha256:b74f2a1d9ab1dfa3e263ef33d581613679b78d0884babf11671af26e45570ead \ + --hash=sha256:b7ace1f740b36fcd451a1bb96f71ee7650e60b308822baeb66a023965bc27f4b \ + --hash=sha256:c0f3f20c90e72a171917ae65706500b096a1c3eb5f162c3ce702a2e25635f132 \ + --hash=sha256:c12e1fc59c6dc26d10d9144453ddc6cbfe4cd4c31e874ed2d0132f88e685eb8b \ + --hash=sha256:c7e9e19413d43077d5a5c77b02ff82610209088e8f98da929347bc03d4c848d1 \ + --hash=sha256:d0dda8af248f6971555e1d4425f64864ce4e7369c5f8ef57c3e82a9bef77e22f \ + --hash=sha256:e256f95a40e3b0183a98556fb7164d24b97eeb353123ccabfcba94712b35ee2a \ + --hash=sha256:e572da3e247b28a98f46636d33c756e81ffb0f5def96c231ba45332333060595 \ + --hash=sha256:e844cdb25c3c93c7572e0a37137c12305efea493be4eb65801b3ee93f180c186 \ + --hash=sha256:f732feb060ef57c1a040c24cee072ba9fab99bd0a7d2c916ef3f1c4d84b98974 # via # semantica (pyproject.toml) # grpcio-health-checking # pymilvus # qdrant-client # weaviate-client -grpcio-health-checking==1.83.0 \ - --hash=sha256:7d8b47a5bfbc699d4aee0fc7a27f5d0265eb23a6a64db5ac2d8b749a0f8a9911 \ - --hash=sha256:ad6bc4d5a1103ad704d25ccd82def300dfa27996b185cab3e4cceeef2c6867d4 +grpcio-health-checking==1.81.1 \ + --hash=sha256:cbc6a4171825ec64389de2f062d296ba129a5c27eefd0dd55fa837909184bdf9 \ + --hash=sha256:ecc61480e25058a4a04e11e4ab6900ad7439b32e60a8ce4ece7d9f219221c85d # via weaviate-client h11==0.16.0 \ --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ @@ -1819,6 +1886,7 @@ h11==0.16.0 \ # via # agno # httpcore + # httpcore2 # uvicorn h2==4.4.1 \ --hash=sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6 \ @@ -1874,6 +1942,10 @@ httpcore==1.0.9 \ --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 # via httpx +httpcore2==2.12.0 \ + --hash=sha256:7e04258ce01013d7d615e5b910a3b27fac937d7a95038227e79652b4ba3b4ceb \ + --hash=sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648 + # via httpx2 httptools==0.8.0 \ --hash=sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683 \ --hash=sha256:0ea897f0c729581ebf72131a438a7932d9b14efef72d75ada966700cac3caaeb \ @@ -1933,21 +2005,29 @@ httpx==0.28.1 \ # semantica (pyproject.toml) # agno # agnoctl - # anthropic # docling-slim + # google-adk # google-genai # groq # huggingface-hub # jupyterlab + # langchain-core # litellm # ollama # openai + # pinecone # qdrant-client # weasel # weaviate-client -huggingface-hub==1.27.0 \ - --hash=sha256:7df6827c2f956c60fbaa64646e979e566db76f619dd0a9729dfb8c5a3eb4f68d \ - --hash=sha256:c1fed40ea82a6b41b477f5243546549b792ae0a93abcea608cff66089bf8f8df +httpx2==2.12.0 \ + --hash=sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf \ + --hash=sha256:cc8b6eecb8661c146b8f89a60e97456ee086e91a784ed31ac450c3a9e613dd36 + # via + # anthropic + # langsmith +huggingface-hub==1.29.0 \ + --hash=sha256:6ebb385a581435325cf6d5c5b233d5d4bc91175834d99fd65dae14379b36e9ad \ + --hash=sha256:b00f7782afc14db4bc6572763810a635bdfbab8623d957bfb553bd18e03852cd # via # accelerate # docling-ibm-models @@ -1964,12 +2044,13 @@ identify==2.6.19 \ --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a \ --hash=sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842 # via pre-commit -idna==3.18 \ - --hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2 \ - --hash=sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848 +idna==3.19 \ + --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ + --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 # via # anyio # httpx + # httpx2 # jsonschema # requests # yarl @@ -1983,10 +2064,14 @@ iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 # via pytest -instructor==1.15.4 \ - --hash=sha256:00e0ecda80fd9746fb6d082d3f9641e193adb1d8849f0775f91519a82aeff968 \ - --hash=sha256:ea2280c3678d0f6891c4d826104f95624b680e69877113a6345b1d7c9027ba0f +instructor==1.16.0 \ + --hash=sha256:313ac2ef4f13a4828889a24d65ad3ed63d794506759bd3412c4b71eda197db33 \ + --hash=sha256:5176def4060e17650e70851e1461fe649e0470de80ce782f245ae444d53b6429 # via semantica (pyproject.toml) +interface-meta==2.0.1 \ + --hash=sha256:902bd9a95a12f195f15753a1080075d4eca7a2cb934fac7ac03c9e362b50796a \ + --hash=sha256:f38016bef9a4429b6d0792d809be7b65e9781820c674bf7f463999086b6e6323 + # via formulaic ipykernel==7.3.0 \ --hash=sha256:897eb64da762549ef610698fca5e9675195ec6ac8ec7f19d81ce1ca20c876057 \ --hash=sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09 @@ -1995,9 +2080,9 @@ ipykernel==7.3.0 \ # jupyter # jupyter-console # jupyterlab -ipython==9.16.1 \ - --hash=sha256:4acae635506f6d352d94c4899a19d5f85f8bc4d230932342dca556fdab1c69b4 \ - --hash=sha256:5a3d1f9a47ff216d6cf9cf863124f6a2c1a198d1354c546a4d24a370a283b64c +ipython==9.17.1 \ + --hash=sha256:6d1645743cfd1a07eb695d85aa2b5fa66721f8cbae9431d4049f7084bbf06509 \ + --hash=sha256:8919be8c27f20a6f4423145028063f6637b42a03ce57665bb12015ee1f073529 # via # ipykernel # ipywidgets @@ -2007,9 +2092,9 @@ ipython-pygments-lexers==1.1.1 \ --hash=sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81 \ --hash=sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c # via ipython -ipywidgets==8.1.8 \ - --hash=sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668 \ - --hash=sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e +ipywidgets==8.1.9 \ + --hash=sha256:bcccba38a6ec3253f7a39c943cea5b9ad01999ce071396171adbc51c6a6a8613 \ + --hash=sha256:f2b8cbcaae10252b809fbe4d7470db75c09b769a32cbf816d20e5ca6d3c5a79d # via # semantica (pyproject.toml) # jupyter @@ -2027,9 +2112,44 @@ isoduration==20.11.0 \ --hash=sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9 \ --hash=sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042 # via jsonschema -isort==8.0.1 \ - --hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \ - --hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75 +isort==9.0.1 \ + --hash=sha256:182918b730772292d33564a6ac5b201ca2bb79a8ad2ac77e7681ecc0f19a8f84 \ + --hash=sha256:1878b5165b0db434c0c62373a81a111e1afffb373f20e57bd2020ebdbaa36808 \ + --hash=sha256:1b8d6c836fb83232f5f4c1c037d332caf743bb24dca63167bad9174ae13e150e \ + --hash=sha256:2057236a764f31c78dac78f7343057621fcc2fd40461ce61061f34fd09066f46 \ + --hash=sha256:23d3b6657763f9be1b15bb9664b016abfce34849d6215a46a42af7945d4acd68 \ + --hash=sha256:2f41e40246742970db0227a2afb2d7da872bddd888826cf182c0916993fadb43 \ + --hash=sha256:2fb33e0c0f9f87821acf6d82c83f0a0c7e54680fdf3fe4131409d2b95901f00a \ + --hash=sha256:3727eb33a9759649346481cf2a9287d656a170c31ed7c105856f9c6f5b539756 \ + --hash=sha256:466b0c3f156a21c10edefba697e641666bc26ffb0122bf08b42caa3d464c20aa \ + --hash=sha256:5022b332ac91ccb39dc28bb206d5ae96ae7f8d45e710b072cb039b2fcda6602a \ + --hash=sha256:5832683294dd61c59d00cd043a68d42f6ecd7dc7d04b73ac777f7f90a534d6ae \ + --hash=sha256:5aac7263b7a7f9f647f94fb6df2761ff5b60a7168eb492ff39dd30443207fa19 \ + --hash=sha256:5e72a7063570f1d740f0284c7ae5739dc34c6a2d9f1049b13027a5bdadb56682 \ + --hash=sha256:67680927f739d4b48d67d8b7430faa92c95b02fb6075ca0351c6446214f6c7bb \ + --hash=sha256:7281cdf538f682b8d75fa44bcdad1b299036bbc440855f7d61412b3b85d5727d \ + --hash=sha256:771d5b7385292a0b2106229b792b8750954bbaf231e0475b1f53f1dd43e00936 \ + --hash=sha256:77f4b984ab3badbbf2363c849b92465e0f69e8fc54d1a932c87532a559269397 \ + --hash=sha256:7a75d4c21d8b93345a2743b96cc75c6f085aa89ddbaadd6edd5e9765be12ab77 \ + --hash=sha256:7ea5f505b152fedd2b990b39d8b76108a48b355da874025aad4982e8ceeb0f3d \ + --hash=sha256:825c05d2d63a1b9c608c352503c10b6411a3c6e12bcacc97b306774ee379786f \ + --hash=sha256:873cf1b6371d41e2a74d57d7c0176d311822f0415441abf8251ad074c9fe4a66 \ + --hash=sha256:89ebbcdbdd9d66cc14909bbac36acb9db29f37325606113c9f270242f8a1f896 \ + --hash=sha256:8f490acc182253d07071cc8255b57a281855e2e027b929a89eaa7c797f7b213e \ + --hash=sha256:930879e4cfab3264f1d7346abeec10726b5382dc4be9f4251c25ec7fa057926b \ + --hash=sha256:98d48ad47f705ac7f046cfaab0a11320ed0b903243ccb850347229414a364d28 \ + --hash=sha256:99b7bc28b1f05f7e3267629043a99c6c479a750df3689327a10324e396827f94 \ + --hash=sha256:9dd4664ad009552bc4c9f464bd31190d0f04132412ee4d9392145fdf58d92127 \ + --hash=sha256:ba23db109e3e93ef1999f7209a651214994cd807801addd16ac485982eb4edd7 \ + --hash=sha256:c08b2989a16a46e97af652266ee8af617eb5b1bfa3195cc921cc0dc66b485d10 \ + --hash=sha256:c2525606f62742fc4ed9f8ca89043b9522ac3e6f9c9892e6cb16f4870d937f38 \ + --hash=sha256:c3ce022ccedf63aa5fc77bd0e926b8561a1476c9709d7cedf63abd7967772aac \ + --hash=sha256:cc9814ce2ee42c17007d822455e4db55e32e589808ecfc2665d51c848d0bb30a \ + --hash=sha256:cdf765657edb2bcccbb1b20d26e710acbcb27379c0a407c6cb376e5619059a7b \ + --hash=sha256:e3a2697ebcb54b51af4833de44447dbf31ddf081c5f163772092d21c0267483b \ + --hash=sha256:f6877ed17054eae153d686270678b11c1f6cb79433a1c07453140cccbaf7cc1d \ + --hash=sha256:fb7d55156a1f766a2b097165524f07be61ececa41a71ca33d24a00777f79a829 \ + --hash=sha256:fd326823ddbe338357ba1823b7f96481d4421d54c83ebd43c92f1b51314a24ae # via semantica (pyproject.toml) jedi==0.20.0 \ --hash=sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67 \ @@ -2170,27 +2290,23 @@ jmespath==1.1.0 \ # via # boto3 # botocore -joblib==1.5.3 \ - --hash=sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713 \ - --hash=sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3 +joblib==1.6.0 \ + --hash=sha256:2ccc96785b12046c08fd6d55839c12857831b54a3c1673ffadd2f04bfc4eda03 \ + --hash=sha256:3dbbf9f6e4b592a2357b854608e980fe6390d131d7a82f011a377ef2ebef7aba # via # distfit # hdbscan # librosa # pynndescent # scikit-learn -joserfc==1.7.4 \ - --hash=sha256:32d46c2cd5e3203c13e87a6c61333cab310b1ba80cd54b4c4f386a848a122463 \ - --hash=sha256:b3bc561672ae541b17a9237053b48a03dacddd92d68047b3ecdfb4b5714a88ed +joserfc==1.7.5 \ + --hash=sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159 \ + --hash=sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea # via authlib json5==0.15.0 \ --hash=sha256:56636a30c0e8a4665fe2179c0212f32eae3796dea89ea6f649b9436ecdb39618 \ --hash=sha256:7424d1f1eb1d56da6e3d70643f53619862b4ce81440bdb8ecfd6f875e5ba4a71 # via jupyterlab-server -jsonlines==4.0.0 \ - --hash=sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74 \ - --hash=sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55 - # via docling-ibm-models jsonpatch==1.33 \ --hash=sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade \ --hash=sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c @@ -2202,7 +2318,9 @@ jsonpickle==4.1.2 \ jsonpointer==3.1.1 \ --hash=sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900 \ --hash=sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca - # via jsonschema + # via + # jsonpatch + # jsonschema jsonref==1.1.0 \ --hash=sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552 \ --hash=sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9 @@ -2212,6 +2330,7 @@ jsonschema==4.26.0 \ --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce # via # docling-core + # google-adk # jupyter-events # jupyterlab-server # litellm @@ -2230,9 +2349,9 @@ jupyter-builder==1.2.2 \ # via # jupyterlab # notebook -jupyter-client==8.9.1 \ - --hash=sha256:0b7a295bc46e8751e9adae84781f726c851c1d911bd793edc4a3bde942e3da81 \ - --hash=sha256:a58f730dd9e728ba16ba1d62ebccf7ffe1ebbdbce4e95cfae941b7321ae1f4fa +jupyter-client==8.10.0 \ + --hash=sha256:5f73f24f22fa25192cfff6b23c051932a2473a797b05734aff495b392103e14e \ + --hash=sha256:9f7116294dca55f1785be880057d44544db9b1567718d92cb33c58886afb9497 # via # ipykernel # jupyter-console @@ -2263,9 +2382,9 @@ jupyter-lsp==2.3.1 \ --hash=sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81 \ --hash=sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6 # via jupyterlab -jupyter-server==2.20.0 \ - --hash=sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14 \ - --hash=sha256:c3b67c93c471e947c18b5026f04f21614218adb706df8f48227d3ee8e0a7cdcc +jupyter-server==2.21.0 \ + --hash=sha256:2ae2e5ce5e97268553e25aebe040197455673d925b5fc7995477153318160cf7 \ + --hash=sha256:70d9a1883f57d3576ea17f4ce061ec1a7aad7ef388d00428cfb7f5e4f0022271 # via # jupyter-lsp # jupyterlab @@ -2292,148 +2411,172 @@ jupyterlab-server==2.28.0 \ # via # jupyterlab # notebook -jupyterlab-widgets==3.0.16 \ - --hash=sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0 \ - --hash=sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8 +jupyterlab-widgets==3.0.17 \ + --hash=sha256:40ac1e9955acf116c4d995d9bfa082d86ad9ec6d91c4f134827cf5e0a5eb75e0 \ + --hash=sha256:6e61fe21ca8a66039180a5cc52a433e07279d2fee79c8be963e00d55193f17a8 # via ipywidgets -kafka-python==3.0.10 \ - --hash=sha256:06950fed5e705ec5207458ca6ae43b6c0ae1de4146ddbf7877f42a893830100c \ - --hash=sha256:9b2597f194009dcab8f7207ce9a15d59cb5d30bf5aa742fea69dffbd2866b2d1 +kafka-python==3.0.11 \ + --hash=sha256:9d10cab4e11e02545d82c7e5af5702da5aa46dd4eccd11ad92a50bf6dbbecd14 \ + --hash=sha256:a003d927e79c801d6cfd1e59ceaaf78807351e75cdb5b8ee9ce4262586f9780f # via semantica (pyproject.toml) -kiwisolver==1.5.0 \ - --hash=sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9 \ - --hash=sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679 \ - --hash=sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0 \ - --hash=sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8 \ - --hash=sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276 \ - --hash=sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96 \ - --hash=sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e \ - --hash=sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac \ - --hash=sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f \ - --hash=sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a \ - --hash=sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15 \ - --hash=sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7 \ - --hash=sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368 \ - --hash=sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02 \ - --hash=sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9 \ - --hash=sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681 \ - --hash=sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57 \ - --hash=sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27 \ - --hash=sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4 \ - --hash=sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920 \ - --hash=sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374 \ - --hash=sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3 \ - --hash=sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa \ - --hash=sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23 \ - --hash=sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859 \ - --hash=sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb \ - --hash=sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d \ - --hash=sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc \ - --hash=sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581 \ - --hash=sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c \ - --hash=sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099 \ - --hash=sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05 \ - --hash=sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9 \ - --hash=sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd \ - --hash=sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc \ - --hash=sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796 \ - --hash=sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303 \ - --hash=sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca \ - --hash=sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314 \ - --hash=sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489 \ - --hash=sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57 \ - --hash=sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1 \ - --hash=sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797 \ - --hash=sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021 \ - --hash=sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db \ - --hash=sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22 \ - --hash=sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028 \ - --hash=sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083 \ - --hash=sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65 \ - --hash=sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588 \ - --hash=sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0 \ - --hash=sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a \ - --hash=sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1 \ - --hash=sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c \ - --hash=sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac \ - --hash=sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476 \ - --hash=sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53 \ - --hash=sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3 \ - --hash=sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4 \ - --hash=sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615 \ - --hash=sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb \ - --hash=sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18 \ - --hash=sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b \ - --hash=sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1 \ - --hash=sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2 \ - --hash=sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c \ - --hash=sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac \ - --hash=sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d \ - --hash=sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf \ - --hash=sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2 \ - --hash=sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f \ - --hash=sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f \ - --hash=sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4 \ - --hash=sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9 \ - --hash=sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e \ - --hash=sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737 \ - --hash=sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b \ - --hash=sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed \ - --hash=sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3 \ - --hash=sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7 \ - --hash=sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08 \ - --hash=sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e \ - --hash=sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902 \ - --hash=sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd \ - --hash=sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6 \ - --hash=sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310 \ - --hash=sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537 \ - --hash=sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554 \ - --hash=sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e \ - --hash=sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87 \ - --hash=sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a \ - --hash=sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c \ - --hash=sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79 \ - --hash=sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e \ - --hash=sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16 \ - --hash=sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1 \ - --hash=sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875 \ - --hash=sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd \ - --hash=sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0 \ - --hash=sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9 \ - --hash=sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646 \ - --hash=sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657 \ - --hash=sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4 \ - --hash=sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232 \ - --hash=sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819 \ - --hash=sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384 \ - --hash=sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309 \ - --hash=sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede \ - --hash=sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2 \ - --hash=sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203 \ - --hash=sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7 \ - --hash=sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df \ - --hash=sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c \ - --hash=sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167 \ - --hash=sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3 \ - --hash=sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09 \ - --hash=sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398 +kiwisolver==1.5.1 \ + --hash=sha256:007a5553dfc4f4e8d184f588a0200e2cd4b63a59cc8796df3c39909e679dc7a0 \ + --hash=sha256:0324cd2567259b7a095f6cf18a52b0ffc6f3de9e69528ff1bc0e7a37bd43ff1a \ + --hash=sha256:0627b9bceb9c3cdcf12b8a18655eedfed2692b038df27423383c120d0b7dc2d6 \ + --hash=sha256:06a6917674de9e0fe3f66f5430787f59a9f2ddb64af9b714eaec547e29ef5c19 \ + --hash=sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0 \ + --hash=sha256:0a4faea5c6db201c6a21391d2ac926ea97acf7dacdbc3c417189e1adb1a00837 \ + --hash=sha256:0ba9527afc80ae3d7814ed98b6572d02bf85eaf48065678342c5f0c6dab7a8c7 \ + --hash=sha256:0d8924877ce22e17326a99a418c3c82037da078df3c6a260b13eca677444e6e7 \ + --hash=sha256:0ebdef3eae5336568147c39a55be6a2036ffde53faa9ca2d978989ae7c2da12c \ + --hash=sha256:1209042a623ddfda5497e4066c7b77651dde8e1d3a9dd97599dc7e97f3b9b78c \ + --hash=sha256:16895f553ee6620a827d2da56b871f835fb70b9216cca5d188e885caf6e3bd23 \ + --hash=sha256:17851e5dad4484be0cbccbde3b15331deae036de9aebd45eed964487802b172f \ + --hash=sha256:1798e83840c3f627246104c4d8a9639c60fa068adf9ce92b61791781fa8a68c1 \ + --hash=sha256:18170a77ddfecf40ec60d0928268dc95880c881864e015a8f34094ed18b9b9ad \ + --hash=sha256:186884a58486651e3c217b6acea0a53eaa9498fdd472057c46f2f0fb5c25aad5 \ + --hash=sha256:18a0cfb124546a4c2e6087c5f3029c7f44b37c85b142e0ced71f73a7599ac208 \ + --hash=sha256:1983f0974a750a6f6556f368ba11105d1d8369c735b944747c9f12ae5aea7aae \ + --hash=sha256:1a7587dc335f2c0f5bd577fd0540bd16c66006bdb60f759a1059f025e6c4f071 \ + --hash=sha256:1acc7e5b7ef05e9da8bb70cd6c7c4513090213d2e1ad9720f599f0bf6c52aec5 \ + --hash=sha256:1d852545c4d0e35a72728d072cbaa59e2fa7dd84bdf01e068d670dd0ceb58eb6 \ + --hash=sha256:1ed0f5e49d0ceff8b72190824d9e59c062fbbc02c231b853112c78474b3f5ec2 \ + --hash=sha256:1fff05e239575b1481b6ed1a782f6fad616efbf1f0b1f44e6e85c4dfe426e483 \ + --hash=sha256:21e46b23a2da695c364124817bc01d970effd5483147f8d66a6a7167e3f6b851 \ + --hash=sha256:22d5e5aaad6be121f2515765e3b1c444352cb8eb4c86510801db8f2e50757316 \ + --hash=sha256:2551cf9917af48ee7c4b29cc82320489508cf96fd26a51f6fc124de661cd44c7 \ + --hash=sha256:255605693a483db7bd5c79f60437f7bf658f7f520d61aa42722e32257c941951 \ + --hash=sha256:26e8268480be5061d509e29669d59103c067a26377a56491630ece11762e3858 \ + --hash=sha256:27add358abe374ebaa3b8763ef380bc99051b5a4b18d94878366a9e4f59efef0 \ + --hash=sha256:2ae70bc59790d2af72a3f76f24b272403e135070340281108b447cb77ea70819 \ + --hash=sha256:2e10ae1bba1899188b33557c10d73affcc12033edd18adddb57d209039976a4c \ + --hash=sha256:3221f78211074f561c44ca42eac0619828171bec15a2c4cf6f7747d07df76e8e \ + --hash=sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38 \ + --hash=sha256:34ec467940442c9943016fb2d4c81d1ba84351eeca2f1a78f8bc87f1ba0d414c \ + --hash=sha256:37f801b5d7cc0e5a548921308e059fd2b057bb42972b591cfa3049f95423c4ed \ + --hash=sha256:38f6e0deb4d0a4615efe0c4efc5990b06ae450ab50a0b321c0b078b6d238c083 \ + --hash=sha256:3c24cd69455e1b00ddf770c13b6e2c33e07d6dc3f2d34add0bf9277c5c6bbd46 \ + --hash=sha256:3cc210010fd2f438a3ed430b45f1b501fd13a8618bf984dc2c5ce5b69b78752e \ + --hash=sha256:3fa5855898f6d3d01b72ccd48a2d65cbdee301251603fefe34e2025bddba219c \ + --hash=sha256:416ba7ff9f233b7036689bb5a3783537e838ad483f63558d2a800f75afe738b1 \ + --hash=sha256:431dc224a1a92a5c8f582d96e505196a3b5997a7271076678da2dfde67b77e9a \ + --hash=sha256:43844c1a7ad6d723d5b5b4c4fc7f5bd399c40e288120d16257c7c9e8765c6e85 \ + --hash=sha256:44b8faef94f1857e77fa0238f3390ff1ac51d2ea20a487e2e452a59fd2b5f5ca \ + --hash=sha256:470d420f98d368d6f010633a20659b544c5fdfa5329e6b70219f2ef08fd4a7ef \ + --hash=sha256:482676e5bd48d70ac99d9fc78863469845421e01184fa83f1f9366dc49f7e974 \ + --hash=sha256:4d4ca09bf13cff792b1884f64b98ee6c2467930d632233be25c56b442d99f10e \ + --hash=sha256:5025e36fb4fb275cef0a4e30dbb11cb4ae61d1c83deb90189cb5d7e4cafd6b55 \ + --hash=sha256:509735237ae0d849e8a843551d423d2500d2e0a9ac1611a145658b29c0fb9f85 \ + --hash=sha256:534f02c1abb31ed6dbd3515545285c330b2f12d00fdb1fdb71658b9ca5a13a6a \ + --hash=sha256:5978c3340f16a35c30f8ab2fa7bcf559973c55f1a5ef6970e1f621acf3c4db13 \ + --hash=sha256:5b973887ff782cfd6b67c9904ad8ca542e0bc5e4961503408b423b5a688b4d38 \ + --hash=sha256:5c490db2168a508088f59140dd392556a54b8bd1048fc6383c8baff13c359673 \ + --hash=sha256:5d142e352eb13facc7dd047489aebdff6ba78576c239f1ea04931979caaf0567 \ + --hash=sha256:5daa1f19e097050b9c4d9a78fcc9263cb96c9dfae08037ddc1b7c4ad1889f2a2 \ + --hash=sha256:61e9a64c7635095a6bfe483e2ff055d437c59bd45f3617a228b37277f0185d62 \ + --hash=sha256:63fb7294b768f444eb4b068965f2662f28c2fd4161e23bd60fcf3ff27b74c046 \ + --hash=sha256:685929988b208a911f1285e2f8ed54210b0d681a3dc0f03e00d599d291986e7e \ + --hash=sha256:6a797a1cefc8b9c93170db580337e1fe3d011ad18b1299943231279406342048 \ + --hash=sha256:6b92f60017dda7d877fdc546438b5e28f31c523264f49cf5a48c1d0ce1a0dfbc \ + --hash=sha256:70ed9a45c7484d2b30cdacf60d220f494a1763b9fec1ad03285c6553fa0889f2 \ + --hash=sha256:719a35fa1156db3640555f95ebb94f60a444e64d1c69626b0edef5df78eba225 \ + --hash=sha256:74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d \ + --hash=sha256:74ea337e0ec3f6f342a36a4f1b5cd94dd9affddcd28ba9aae2905af932ee8c6b \ + --hash=sha256:75d9b1cf8258462dbdc1eeda718c96ea7f079324c09067f6daabfcf37712b7fe \ + --hash=sha256:77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3 \ + --hash=sha256:7824b5e8bdbf0bccb4ccd37bbb115849a1dc45437fb4de8351385ed07c437ee0 \ + --hash=sha256:7d38b0c279c3032e8c9cc013b405c6df8e1668dbf15465779aa7f15f61201812 \ + --hash=sha256:7e9c01d3dd7ceba4d1d436cc021d40d592466e40b9bc7f5d83dc4e98a5c9cd8c \ + --hash=sha256:7fd82debf43c6acd0a94359d232f6bb516ee13f269a7993736a9ac9f988bb5d9 \ + --hash=sha256:824c3d763a05ea9e9003610145186b0e9848c7584a5575c79bac5a8e7cd80bad \ + --hash=sha256:828f75af2b0080c8a972e75f649ab46af008e92c6104a57a759157200b835b75 \ + --hash=sha256:83f78128fa28705fa85d01c59771c72fe81c11bd0e6155edbb9f818983a7d761 \ + --hash=sha256:876bbfd276473d3daffe30e8c975df4ed9429967b41a6cb362dbb5155b6f13ad \ + --hash=sha256:886fc26012f0e8b5f69d1cfe6d711f6b11f194621539bf8e6bb1c25c5dc82724 \ + --hash=sha256:8a34616dc2521cc8dc1d7d081734da63539f021ac0450ce950908340c6e7aa2f \ + --hash=sha256:8a708a47ade1fe19e8371d5da076bac0dd4b0a5a7985ad6c637f7f7e361b6baa \ + --hash=sha256:8af9b142ad719ae3a911ebf616bc4b78b32bbab84d6a40d3ad2f129670509957 \ + --hash=sha256:8bf4df63592c2a66b4f8edc5df2544998c288aa02f96ce0acd880cd1de8c8127 \ + --hash=sha256:8de6f2a4ce7e7bd27d23dd94abf0ccafe0e0e5cc9c764b0577191f2c25f08f26 \ + --hash=sha256:8f8fddb8e323bd6eee4e54e69a39243beab22689070f4c66b472c4cc88bb89d8 \ + --hash=sha256:8fca690b00c4c48f6c2a547b0160ed511357093a4e4c9b47e0fadf3128066d89 \ + --hash=sha256:9506e892bcc3b409831d363c6f53e5985e1c8d1f6f6b0256d00358684ff85378 \ + --hash=sha256:958254518717542d02d0688d0d20cbf771da5e415e6f49543f92481c850a4540 \ + --hash=sha256:95a02752aa032eef4aed01cda6d9b687c669bd0396bf4519eef8bba22a286720 \ + --hash=sha256:96c30002424670b5e1e46495c2b8cbffef39cf77c1d79e76462029d50339785b \ + --hash=sha256:98b208a7cc42c803445ef551d6753cc42a5ea13e9cab1ee66cd8b9cb70195330 \ + --hash=sha256:9b3092d8992a1d69b7a59c3e39f35e1b9be327a17f68a7c35fc17329e337d6f2 \ + --hash=sha256:9e51c119992ea8820706871c30a4642ec76de20ae82f9b50b9a45517d8e9f810 \ + --hash=sha256:a5716a33bfabb2c6ce27b6cf03253467b3804f83e215f4d202685cf93c6c9874 \ + --hash=sha256:a5a00665d1a0e26763a7338d7e911d4598fbc1d50dd0d6b7919b7dc6c5d6569f \ + --hash=sha256:a5ca5aebae78a0bc13c1943af4af615d4966c5b650b05d5aa83b50e427196fee \ + --hash=sha256:a7b85b2cc6ea45e5f7e8c9a30bc9fabd47cda09106cbb4b967335c3e6c43b69d \ + --hash=sha256:a83ee7107df13abe42a54a6654670eef9bb39425cf2e27f65e0007465e1286ab \ + --hash=sha256:aa7d00b1700966d2917e54d278aba86897890ca9276dd8b76cf6446b6c181b92 \ + --hash=sha256:ab620eb663952455271ac37f9aaad86b73c969c02f11f53cea405b38e96a4300 \ + --hash=sha256:ad8b9671348d7c8716715652ae11f85ed0eb99e265a2df2ca490577d69860b2c \ + --hash=sha256:aefe930d113798330e9462f7874542977869c0613cba3262e2de3a8d5dee8f3a \ + --hash=sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444 \ + --hash=sha256:b390aec180a7c054919c04898835e1c77bced23ea8383eb2c570213bf25d1a86 \ + --hash=sha256:b3d78f7bb2b9d9a30345be1474b9aaa8685430b54afb51ba3639b5c6c11e9ed6 \ + --hash=sha256:b5664603a253efd3a75716d793d1d3a6a82723b61dc6db767b2460bbbeec4c0f \ + --hash=sha256:b69602970994a2ed8bbfa78c2f0394a7435226c6040489702d9f0a0ad0c07052 \ + --hash=sha256:b6ae6a0328f0bc035741820fdeecdcd67bf4694eee03972e843663107122f450 \ + --hash=sha256:bad20d4c69c851c982a1e3606f4c293edfd5a87885786c50082412240c4b1ffd \ + --hash=sha256:bb7c99f0673c03017a3ee01e54a5c2617a05468b11eabe513b0080e063ed95b1 \ + --hash=sha256:bebb89489b279b2f5661bbbb2abcc87bcd4a46607bb4a5c966f04f1db6b8df9a \ + --hash=sha256:bfd1de989b3330420e29de39352f5c049905c9e3ee67233a50d550e3d652c148 \ + --hash=sha256:c2306e8bb53601979fcb3fa09cc65e031876d9ae01eff2fcbcd7a84ef94d5bc1 \ + --hash=sha256:c3a4e41e3096bf1f0f1b76e2ffd6d828d6547f574f702d59bdbef7acfa59db9c \ + --hash=sha256:c6834b92dd2428e2dd85ef3d85f723d3c12f20aaf43a2ddd4f944ca25d833408 \ + --hash=sha256:c90d3022d8a94778939cda8638c6c8da8fa757b8958dad7ec868ce29c87681b8 \ + --hash=sha256:ca307d6c259e5c98d3cb9ade55342b47a6839762caf2536f3d7b46ee660cc82e \ + --hash=sha256:ca7f6fe0f37ca978a1e5eb7a3a68e6413f417e78e838324947ffd420202b198b \ + --hash=sha256:cb6fae641357ed2f6e533c0d3c6504a4a5703621a50c89459e46051d56b61140 \ + --hash=sha256:cdaeeb6c350106df6bf9d873395973e5f066a9713200b72cd64f55d0a3eafab6 \ + --hash=sha256:cea20da04494e662b83c872683bf4ff2345206043d036315ed0e924b652e7294 \ + --hash=sha256:cea90547bfd93807e0013a004dc76552be44fad3bc1cc2b38610a9e889ed098f \ + --hash=sha256:d09037ca068d784ebc4aec290ef952ca27ac15dd9c0b5801a88c6e1096b83e6b \ + --hash=sha256:d27c2123977cb9269c30a49ba45f03a4323017ef693e19db4ec9dbe1299a3002 \ + --hash=sha256:d50de98e8d807dc31822fff96f50293163a62418eb65487a21b42713d72ed0b7 \ + --hash=sha256:d66a64dd5dec136040ec2ae94aa026a912ee60fdd45bc28d3db30037fd809e88 \ + --hash=sha256:d79308fa689fac89cbcfbd4dbfc80b5f95c54c5a7fd4d194be221f9d33d026e6 \ + --hash=sha256:da3275833be0edbaf4830fae08bae3dc7219f40ce0c37eaa6c25825957e06612 \ + --hash=sha256:dc1a26b8e53395a01c2c611e58602fa47461f136fba7cd5542e6db6d64be1839 \ + --hash=sha256:dc23390afe9f4ef9ac3bcc72a03a56eebbde03f4c571a32cb38f859cff9a6524 \ + --hash=sha256:e05c2f7925f1d88778e53cb44f14e0223204a3bdd09a41664750363acfb1f2ef \ + --hash=sha256:e12dfea7f5fc2a34a9080efbf79c4c44eb380ec5b9c6fea09407e08f0d1e941d \ + --hash=sha256:e4e4523d6f336708d732516e6cfca7796cf3d96c9474eb5aecf6165f2f1fefc3 \ + --hash=sha256:e4e49f7e1a4e7191bdf9dc67a974db714501b1fc52c24324103d06a86abd5c08 \ + --hash=sha256:e68e151428b5384f766cd25739bf77c7e4a3dc93b5ded7a12118d9fbfdf78ab6 \ + --hash=sha256:e8e4d953faaded9ec7ede36824e9814082d22d4c7b1eafbfa079ecba8cd0d076 \ + --hash=sha256:ee9df1f0d77b9c6e94f4ac0fec533fbddd5ea3a327807f18d7b069ae019ded80 \ + --hash=sha256:f0a887b6565bbfe80efde2b7f6e8890d7d9bbdb11bdb17028a3690c32fe0621f \ + --hash=sha256:f0f4a42db92d6ec7677ab9d12830a2a8ec145a9c6d15db2b593466bc875c78d7 \ + --hash=sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a \ + --hash=sha256:f1d56ec54d257d05e0b50f5780d967540cd07beeaf9e5f645b26d50cce79f4d8 \ + --hash=sha256:f4167e87b397f273dc2356fcf1eaf50a6bac51e6105f45103ef7129c8efb0255 \ + --hash=sha256:f76fc85bd054c806960f917ec0f329e24e436f1712267d90588e4c39890caa63 \ + --hash=sha256:f942903fde7363d1d879057ec5de01310efda2597161784d752fa9953a01a71a \ + --hash=sha256:f9b1c4900736e489a812c529100de4b8fb617d4db075e931e213c57424b83d9b \ + --hash=sha256:fc271a6f0a2126958f4090e5507b9da5848927dae331f8f763bd4aa642b3d2cd \ + --hash=sha256:febcce10f2bcdbb80b4ea919238a6a4ac13dbc4c7cadbe8d5d75c3682f8b5404 # via matplotlib kombu==5.6.2 \ --hash=sha256:8060497058066c6f5aed7c26d7cd0d3b574990b09de842a8c5aaed0b92cc5a55 \ --hash=sha256:efcfc559da324d41d61ca311b0c64965ea35b4c55cc04ee36e55386145dace93 # via celery -langchain-core==1.5.6 \ - --hash=sha256:b5f73bd9688c457b31ec73657a0ad56948f889fae27acee79286e9c285632ee6 \ - --hash=sha256:d6cf37bf695ecc22cddeb8461a684e353190b2ce430d99eb22bc11c0c7c00ea5 +langchain-core==1.6.1 \ + --hash=sha256:1b156cb395aac4f009a8a1b38a574c7d948fe2d5f74c96e0d8a5017b4149e04f \ + --hash=sha256:954a84132a5cb0435d27b910e336347b6744ecc18fbeef1e2de7029a0959841a # via semantica (pyproject.toml) -langchain-protocol==0.0.18 \ - --hash=sha256:70b53a86fbf9cedc863555effe44da192ab02d556ddbf2cf95b8873adcf41b5a \ - --hash=sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6 +langchain-protocol==0.0.19 \ + --hash=sha256:4cdf879a492a35980fd859ae792d3c65458ccaae504e183c9a10d7eac1f0720f \ + --hash=sha256:79d90a1425122ac87e8052e2ec054fbd09c3edbf341bdfb6397112a495c7bf8c # via langchain-core -langsmith==0.11.0 \ - --hash=sha256:7339f90e6fd9a1a009445b5084a7a0e56a8b6f17305ee5d7e8c5e7582217854f \ - --hash=sha256:e87a3929915936c066b3fa3283ec3f3f0013e2ef7f98a443a7fbe3fab8e784a3 +langsmith==0.12.1 \ + --hash=sha256:34574d4411947f62825e8368fd172cad2505ddfb73bf5d8ed7c0e7a41e4c4879 \ + --hash=sha256:8916c1a8daa4282511f311f569fd5cb2f0aba8d89a4d1761620ed37b57f72c00 # via langchain-core lark==1.3.1 \ --hash=sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905 \ @@ -2590,15 +2733,15 @@ librt==0.15.0 \ --hash=sha256:fc1ed11c4ad0b91af24def2050f2840ea4567828e3dd058fbe608d982f6e5465 \ --hash=sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d # via mypy -litellm==1.96.2 \ - --hash=sha256:0168e49cffecb0b45a0d044ecb7e5f7f6fc9e14d974b50f54edfa9d8e0db9c1d \ - --hash=sha256:0aa667c6fc58b20ff04fe5efaac5cf48b1e224bac32dbf22ced2e5ee695bc4ba \ - --hash=sha256:1a42d68b903f6b605bd7744e9700b16a725f897c5edcc448e491cec14cd5a9db \ - --hash=sha256:1f304b8854e385946469d2dcf8ef88e24357120671caeaddea11b085b0302308 \ - --hash=sha256:76e9c72cb6757bb7c35cd23e3e4fc028910795910ae92491076bd50f7b61f205 \ - --hash=sha256:80d477ae092b05ce023b5084542cb3cb75999b52b1dd2e4d834fb348effc9399 \ - --hash=sha256:8c21a57a0f3507176492a4f65361a4302af1a0723c97e96961c7ed0e96934832 \ - --hash=sha256:e5d96d16b37a043e482a134a71b0c1df4e700603fc14df22e4bd27dc9e38f5c4 +litellm==1.99.0 \ + --hash=sha256:1c45097e426fed2ae7fbd38b5404c3addeb203d0e1148c0a59848aabd5fe83c6 \ + --hash=sha256:5617804e838499bce8fecb41ad9bc984b7977361e557666fed0fef0c4623ce62 \ + --hash=sha256:594bf4b6ff6b79c6aa3c3b78c0e939d4afd12687e076ba3fb608d38a5aa7f9c6 \ + --hash=sha256:71109c323164b4b6776ff259876523e6e883a465aa1413dd51a1bda8e92efc5f \ + --hash=sha256:a43e8716da8beed04480e91b4233ff2f1ab1fedd84cad332dbc526b54a9229ca \ + --hash=sha256:e2b383070656fdbec4bc44602edaaed2a21e99ceee4ea0a4650c8cb381e67b59 \ + --hash=sha256:e42f94731665b68e263481efd79f7629e9b97eed7e10c57dc37a890eca058227 \ + --hash=sha256:e461b7ce53af990e5287cf7ae30d82c956b56dcce886f63ef39a76e678f82a3b # via semantica (pyproject.toml) llvmlite==0.49.0 \ --hash=sha256:00f16db782f4a13c78c5804aedc434e46794a77e89999a168f9401106270e50a \ @@ -2819,9 +2962,9 @@ lxml==6.1.2 \ # doclang # python-docx # python-pptx -mail-parser==4.6.1 \ - --hash=sha256:7381853eccb551c83090c90f3ef16e433e8c9b320acbe27145a13c0d2fca480f \ - --hash=sha256:f688394e26e3f838fee33039bda5b8ea1876622c81ff0c5f0db5a817379ff62c +mail-parser==4.6.4 \ + --hash=sha256:1929109b7934ee061c1533a897b18c57fd0adb3548b79b5e28d901819bade872 \ + --hash=sha256:bc6e437b3afe38091893e7b6ea49c7f2188ad7616abdd4fa3059e85f1b69efce # via docling-slim markdown-it-py==4.2.0 \ --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \ @@ -2996,114 +3139,151 @@ mistune==3.3.4 \ --hash=sha256:58b5c96d6fcb61190dfe5fae498d2b2065f99cf61e9649418fd54cf1ada86dfe \ --hash=sha256:ee015381e955e370962968befe1d729ab60fafb6a715ac6751763fbce38c8d4a # via nbconvert -mmh3==5.2.1 \ - --hash=sha256:022aa1a528604e6c83d0a7705fdef0b5355d897a9e0fa3a8d26709ceaa06965d \ - --hash=sha256:0634581290e6714c068f4aa24020acf7880927d1f0084fa753d9799ae9610082 \ - --hash=sha256:08043f7cb1fb9467c3fbbbaea7896986e7fbc81f4d3fd9289a73d9110ab6207a \ - --hash=sha256:0a3984146e414684a6be2862d84fcb1035f4984851cb81b26d933bab6119bf00 \ - --hash=sha256:0bbc17250b10d3466875a40a52520a6bac3c02334ca709207648abd3c223ed5c \ - --hash=sha256:0cc21533878e5586b80d74c281d7f8da7932bc8ace50b8d5f6dbf7e3935f63f1 \ - --hash=sha256:0d0b7e803191db5f714d264044e06189c8ccd3219e936cc184f07106bd17fd7b \ - --hash=sha256:113f78e7463a36dbbcea05bfe688efd7fa759d0f0c56e73c974d60dcfec3dfcc \ - --hash=sha256:169e0d178cb59314456ab30772429a802b25d13227088085b0d49b9fe1533104 \ - --hash=sha256:17fbb47f0885ace8327ce1235d0416dc86a211dcd8cc1e703f41523be32cfec8 \ - --hash=sha256:19bbd3b841174ae6ed588536ab5e1b1fe83d046e668602c20266547298d939a9 \ - --hash=sha256:1d9f9a3ce559a5267014b04b82956993270f63ec91765e13e9fd73daf2d2738e \ - --hash=sha256:1e4ecee40ba19e6975e1120829796770325841c2f153c0e9aecca927194c6a2a \ - --hash=sha256:22b0f9971ec4e07e8223f2beebe96a6cfc779d940b6f27d26604040dd74d3a44 \ - --hash=sha256:26fb5b9c3946bf7f1daed7b37e0c03898a6f062149127570f8ede346390a0825 \ - --hash=sha256:2778fed822d7db23ac5008b181441af0c869455b2e7d001f4019636ac31b6fe4 \ - --hash=sha256:28cfab66577000b9505a0d068c731aee7ca85cd26d4d63881fab17857e0fe1fb \ - --hash=sha256:29bc3973676ae334412efdd367fcd11d036b7be3efc1ce2407ef8676dabfeb82 \ - --hash=sha256:2bd9f19f7f1fcebd74e830f4af0f28adad4975d40d80620be19ffb2b2af56c9f \ - --hash=sha256:2d5d542bf2abd0fd0361e8017d03f7cb5786214ceb4a40eef1539d6585d93386 \ - --hash=sha256:30e4d2084df019880d55f6f7bea35328d9b464ebee090baa372c096dc77556fb \ - --hash=sha256:3619473a0e0d329fd4aec8075628f8f616be2da41605300696206d6f36920c3d \ - --hash=sha256:368625fb01666655985391dbad3860dc0ba7c0d6b9125819f3121ee7292b4ac8 \ - --hash=sha256:3737303ca9ea0f7cb83028781148fcda4f1dac7821db0c47672971dabcf63593 \ - --hash=sha256:3a9fed49c6ce4ed7e73f13182760c65c816da006debe67f37635580dfb0fae00 \ - --hash=sha256:3c38d142c706201db5b2345166eeef1e7740e3e2422b470b8ba5c8727a9b4c7a \ - --hash=sha256:3cb61db880ec11e984348227b333259994c2c85caa775eb7875decb3768db890 \ - --hash=sha256:3d74a03fb57757ece25aa4b3c1c60157a1cece37a020542785f942e2f827eed5 \ - --hash=sha256:3f796b535008708846044c43302719c6956f39ca2d93f2edda5319e79a29efbb \ - --hash=sha256:41105377f6282e8297f182e393a79cfffd521dde37ace52b106373bdcd9ca5cb \ - --hash=sha256:41aac7002a749f08727cb91babff1daf8deac317c0b1f317adc69be0e6c375d1 \ - --hash=sha256:44983e45310ee5b9f73397350251cdf6e63a466406a105f1d16cb5baa659270b \ - --hash=sha256:4cbbde66f1183db040daede83dd86c06d663c5bb2af6de1142b7c8c37923dd74 \ - --hash=sha256:4eda76074cfca2787c8cf1bec603eaebdddd8b061ad5502f85cddae998d54f00 \ - --hash=sha256:4fc6cd65dc4d2fdb2625e288939a3566e36127a84811a4913f02f3d5931da52d \ - --hash=sha256:50885073e2909251d4718634a191c49ae5f527e5e1736d738e365c3e8be8f22b \ - --hash=sha256:5174a697ce042fa77c407e05efe41e03aa56dae9ec67388055820fb48cf4c3ba \ - --hash=sha256:54b64fb2433bc71488e7a449603bf8bd31fbcf9cb56fbe1eb6d459e90b86c37b \ - --hash=sha256:54fe8518abe06a4c3852754bfd498b30cc58e667f376c513eac89a244ce781a4 \ - --hash=sha256:55dbbd8ffbc40d1697d5e2d0375b08599dae8746b0b08dea05eee4ce81648fac \ - --hash=sha256:57b52603e89355ff318025dd55158f6e71396c0f1f609d548e9ea9c94cc6ce0a \ - --hash=sha256:58370d05d033ee97224c81263af123dea3d931025030fd34b61227a768a8858a \ - --hash=sha256:5d87a3584093e1a89987e3d36d82c98d9621b2cb944e22a420aa1401e096758f \ - --hash=sha256:623f938f6a039536cc02b7582a07a080f13fdfd48f87e63201d92d7e34d09a18 \ - --hash=sha256:62815d2c67f2dd1be76a253d88af4e1da19aeaa1820146dec52cf8bee2958b16 \ - --hash=sha256:6290289fa5fb4c70fd7f72016e03633d60388185483ff3b162912c81205ae2cf \ - --hash=sha256:67e41a497bac88cc1de96eeba56eeb933c39d54bc227352f8455aa87c4ca4000 \ - --hash=sha256:6c85c38a279ca9295a69b9b088a2e48aa49737bb1b34e6a9dc6297c110e8d912 \ - --hash=sha256:6f01f044112d43a20be2f13a11683666d87151542ad627fe41a18b9791d2802f \ - --hash=sha256:707151644085dd0f20fe4f4b573d28e5130c4aaa5f587e95b60989c5926653b5 \ - --hash=sha256:723b2681ed4cc07d3401bbea9c201ad4f2a4ca6ba8cddaff6789f715dd2b391e \ - --hash=sha256:72d1cc63bcc91e14933f77d51b3df899d6a07d184ec515ea7f56bff659e124d7 \ - --hash=sha256:7374d6e3ef72afe49697ecd683f3da12f4fc06af2d75433d0580c6746d2fa025 \ - --hash=sha256:7501e9be34cb21e72fcfe672aafd0eee65c16ba2afa9dcb5500a587d3a0580f0 \ - --hash=sha256:76219cd1eefb9bf4af7856e3ae563d15158efa145c0aab01e9933051a1954045 \ - --hash=sha256:7aec798c2b01aaa65a55f1124f3405804184373abb318a3091325aece235f67c \ - --hash=sha256:7be6dfb49e48fd0a7d91ff758a2b51336f1cd21f9d44b20f6801f072bd080cdd \ - --hash=sha256:7e4e1f580033335c6f76d1e0d6b56baf009d1a64d6a4816347e4271ba951f46d \ - --hash=sha256:7e8ec5f606e0809426d2440e0683509fb605a8820a21ebd120dcdba61b74ef7f \ - --hash=sha256:7f196cd7910d71e9d9860da0ff7a77f64d22c1ad931f1dd18559a06e03109fc0 \ - --hash=sha256:82f3802bfc4751f420d591c5c864de538b71cea117fce67e4595c2afede08a15 \ - --hash=sha256:85ffc9920ffc39c5eee1e3ac9100c913a0973996fbad5111f939bbda49204bb7 \ - --hash=sha256:8e6c219e375f6341d0959af814296372d265a8ca1af63825f65e2e87c618f006 \ - --hash=sha256:8f767ba0911602ddef289404e33835a61168314ebd3c729833db2ed685824211 \ - --hash=sha256:8ff038d52ef6aa0f309feeba00c5095c9118d0abf787e8e8454d6048db2037fc \ - --hash=sha256:915e7a2418f10bd1151b1953df06d896db9783c9cfdb9a8ee1f9b3a4331ab503 \ - --hash=sha256:92883836caf50d5255be03d988d75bc93e3f86ba247b7ca137347c323f731deb \ - --hash=sha256:960b1b3efa39872ac8b6cc3a556edd6fb90ed74f08c9c45e028f1005b26aa55d \ - --hash=sha256:9aeaf53eaa075dd63e81512522fd180097312fb2c9f476333309184285c49ce0 \ - --hash=sha256:9d8089d853c7963a8ce87fff93e2a67075c0bc08684a08ea6ad13577c38ffc38 \ - --hash=sha256:a4130d0b9ce5fad6af07421b1aecc7e079519f70d6c05729ab871794eded8617 \ - --hash=sha256:a482ac121de6973897c92c2f31defc6bafb11c83825109275cffce54bb64933f \ - --hash=sha256:add7ac388d1e0bf57259afbcf9ed05621a3bf11ce5ee337e7536f1e1aaf056b0 \ - --hash=sha256:b1f12bd684887a0a5d55e6363ca87056f361e45451105012d329b86ec19dbe0b \ - --hash=sha256:b3f99e1756fc48ad507b95e5d86f2fb21b3d495012ff13e6592ebac14033f166 \ - --hash=sha256:b4cce60d0223074803c9dbe0721ad3fa51dafe7d462fee4b656a1aa01ee07518 \ - --hash=sha256:baeb47635cb33375dee4924cd93d7f5dcaa786c740b08423b0209b824a1ee728 \ - --hash=sha256:bbea5b775f0ac84945191fb83f845a6fd9a21a03ea7f2e187defac7e401616ad \ - --hash=sha256:bbfcb95d9a744e6e2827dfc66ad10e1020e0cac255eb7f85652832d5a264c2fc \ - --hash=sha256:bd6e7d363aa93bd3421b30b6af97064daf47bc96005bddba67c5ffbc6df426b8 \ - --hash=sha256:be77c402d5e882b6fbacfd90823f13da8e0a69658405a39a569c6b58fdb17b03 \ - --hash=sha256:c302245fd6c33d96bd169c7ccf2513c20f4c1e417c07ce9dce107c8bc3f8411f \ - --hash=sha256:c88653877aeb514c089d1b3d473451677b8b9a6d1497dbddf1ae7934518b06d2 \ - --hash=sha256:cae6383181f1e345317742d2ddd88f9e7d2682fa4c9432e3a74e47d92dce0229 \ - --hash=sha256:cd471ede0d802dd936b6fab28188302b2d497f68436025857ca72cd3810423fe \ - --hash=sha256:d106493a60dcb4aef35a0fac85105e150a11cf8bc2b0d388f5a33272d756c966 \ - --hash=sha256:d30b650595fdbe32366b94cb14f30bb2b625e512bd4e1df00611f99dc5c27fd4 \ - --hash=sha256:d51fde50a77f81330523562e3c2734ffdca9c4c9e9d355478117905e1cfe16c6 \ - --hash=sha256:d57dea657357230cc780e13920d7fa7db059d58fe721c80020f94476da4ca0a1 \ - --hash=sha256:d771f085fcdf4035786adfb1d8db026df1eb4b41dac1c3d070d1e49512843227 \ - --hash=sha256:dae0f0bd7d30c0ad61b9a504e8e272cb8391eed3f1587edf933f4f6b33437450 \ - --hash=sha256:db0562c5f71d18596dcd45e854cf2eeba27d7543e1a3acdafb7eef728f7fe85d \ - --hash=sha256:dfd51b4c56b673dfbc43d7d27ef857dd91124801e2806c69bb45585ce0fa019b \ - --hash=sha256:e080c0637aea036f35507e803a4778f119a9b436617694ae1c5c366805f1e997 \ - --hash=sha256:e48d4dbe0f88e53081da605ae68644e5182752803bbc2beb228cca7f1c4454d6 \ - --hash=sha256:e8b4b5580280b9265af3e0409974fb79c64cf7523632d03fbf11df18f8b0181e \ - --hash=sha256:e8b5378de2b139c3a830f0209c1e91f7705919a4b3e563a10955104f5097a70a \ - --hash=sha256:e904f2417f0d6f6d514f3f8b836416c360f306ddaee1f84de8eef1e722d212e5 \ - --hash=sha256:eee884572b06bbe8a2b54f424dbd996139442cf83c76478e1ec162512e0dd2c7 \ - --hash=sha256:f1fbb0a99125b1287c6d9747f937dc66621426836d1a2d50d05aecfc81911b57 \ - --hash=sha256:f40a95186a72fa0b67d15fef0f157bfcda00b4f59c8a07cbe5530d41ac35d105 \ - --hash=sha256:f6e0bfe77d238308839699944164b96a2eeccaf55f2af400f54dc20669d8d5f2 \ - --hash=sha256:f963eafc0a77a6c0562397da004f5876a9bcf7265a7bcc3205e29636bc4a1312 \ - --hash=sha256:fb9d44c25244e11c8be3f12c938ca8ba8404620ef8092245d2093c6ab3df260f \ - --hash=sha256:fc78739b5ec6e4fb02301984a3d442a91406e7700efbe305071e7fd1c78278f2 \ - --hash=sha256:fceef7fe67c81e1585198215e42ad3fdba3a25644beda8fbdaf85f4d7b93175a \ - --hash=sha256:fd96476f04db5ceba1cfa0f21228f67c1f7402296f0e73fee3513aa680ad237b +mmh3==5.3.0 \ + --hash=sha256:00105934e7d52f80b4364282918c37e2cc3cf9868ef4052016cbc39d8711c3f4 \ + --hash=sha256:002414b951d980072a90dc25e84fe41570e399503c9db46a45edf61c7f0bd3bf \ + --hash=sha256:045b44b299a658f02dfb43db4037437c66290f6d00984992030648b8ffc230d0 \ + --hash=sha256:060888503ab547f4700e93d13f45ee0d7633f5196028529c1e98c27d5b31520d \ + --hash=sha256:0619b41fc524c0e1bda7b29e47a9cdb4746be3ebd9413798f82c024894283d47 \ + --hash=sha256:0a301b316198758aabbfa3ee565e221b757645d9f94b8a9d2889bfefabbb77dd \ + --hash=sha256:0a6bd95c410ec9500d9515a4fe522e24452f71df38de47395f99aebc085a5d5a \ + --hash=sha256:0d8deb73a11a7b41bb831bc1c40fb9a70d9993d96dbfe82c0d3c6fb3acbe14ea \ + --hash=sha256:0e23bb59643dd36cfc1b5b6f32ca494dcb798f46281f2fa4561d34e7de777a24 \ + --hash=sha256:10508e24e01f6b52c91577e22c4466d703c2c696d34fac1a9048e0da837a1a1c \ + --hash=sha256:1232c5667aaa2f1f77096d94dc0019ceb7ace106e9066b66d28773c4226364b0 \ + --hash=sha256:1617be1ce93f97513bbf23cafa28a6ab1d20b0928de760054a4c603a89bdab7c \ + --hash=sha256:166e3c09c56e980bfef1ee4fe4eded81d544615359eb92fb7ccda8c96f5d9538 \ + --hash=sha256:1b51409765cbec88396b7a47d87220f9a34112c74a8d0db3ca2551941f023c74 \ + --hash=sha256:1d5cd932e6e44762e393570299d50c719c91f3610e33da4bc82578bcdd039c29 \ + --hash=sha256:1d5de36640293374673ec6813b7a23d8a9621bbd87f079c6ec4c5e8585cb1f64 \ + --hash=sha256:1e2cc33121171b1e4657146bfdaf77eb135787465f298d9f5ed04c14771c35d2 \ + --hash=sha256:222ea0a485e23bdcb29e28d15b8b01ebe34e8720bad4b5f92b645ed86e3fc715 \ + --hash=sha256:22cf74b87b11fbe732ce6e23699d59b53208114232515975ce486d1e6b5248f8 \ + --hash=sha256:231fa0e3e9f9d02f46b19c2fd5bba4d61db4415f06f450b9f0bcb82a69be6f48 \ + --hash=sha256:235135d482a4176b9cf42920bc1f47f46daf746f5124921e3bf1932b55b6a327 \ + --hash=sha256:23bafb94a90f3ce2044b8a7a36c6c98ac6830ac8df1b3c0b65faa27a1bcbc49e \ + --hash=sha256:24eb191910181f4f41ffa2f323d63ebe725ddb0ac461baaa2274658cbe6b4c7e \ + --hash=sha256:25743ca117f8fdb707952c55fd86a03457e99036c9bcd22e6d5f9dbd0d80dc7a \ + --hash=sha256:2585bef3623efaceb1cf4734c27bbba81b9bcb4ea5b8bf53823ca75b388933c9 \ + --hash=sha256:27706a3a4700d34718bd1eaf89719a13bbaa269606048dd00173997562c19c92 \ + --hash=sha256:28de4e0a140f86bea109a6d91de9f1e9a94c9d88e6ec9d4d3c78924e4f8c0a42 \ + --hash=sha256:2b4cd2fcf1b517872530d9ef1a2de2ef9b86e5a0f8927539ea0b68337618244e \ + --hash=sha256:2bc13752e7d80c7ea09d09368b78fca0d52f461c5c16abee5e4f78218f3d346a \ + --hash=sha256:2d028a0e523d0c3e478544fc8350c024a0e0d73b549dd6013af4119a87589de2 \ + --hash=sha256:2d58d876bfeb587cd95c9e00e31cd7d2737cb31e6ca83dbcba445667c4403ac3 \ + --hash=sha256:2ff91eadc4301f12ca6b46eaef21937348ed35a4789803fd92df5a2c044fdb3f \ + --hash=sha256:302e5b527b36a875abb611911142e92abac7580faec51318440149ab13b8e936 \ + --hash=sha256:381e2f183ebf25ac91c73e5d1b44b1676b287062fc9732d63bd05fb35bacb2b1 \ + --hash=sha256:397b26f58cb062233abe36b6cdcb99f08204f529d2384e0f3ed07901dca6a2c0 \ + --hash=sha256:3bfc16e0bca99ecc91d4c9fdc708c432fac2eba3797e78577f4d280031456f37 \ + --hash=sha256:3fb0d4918b7d827ac804069849fde03d516628cbbf7bffe0b957ba6f1440cca6 \ + --hash=sha256:408d37be08e12a154b482dcb300781d3dd154abab8e002ab32ecde6aa6a325bc \ + --hash=sha256:422defa81ff5c114213033cb1ce45f3904427ec9dbfa0442a0f4656191f75f5b \ + --hash=sha256:430ed4de594d0084d9b7956b05075a9054d290a3a0d7b370553a9096a4fd429f \ + --hash=sha256:44ca9d01e9761208de5ab845ce86096d3e730b35acc0f90cedaf58d3dfa4e325 \ + --hash=sha256:453571409c955837178bedab51d5514aeda9ee4854dcde9e9134765f22da4380 \ + --hash=sha256:4770ee0d719be9edc3849a231fc29fde75bfab6cf234b79733ed35cb7cf901f7 \ + --hash=sha256:4785dec2f768b74c0735c9f0441b2392111a1467c463cae737bad30ac976b524 \ + --hash=sha256:48ae6e629a63cdadc29264a66edbaac9f18dd77feed973880c65393820867426 \ + --hash=sha256:49e91971bcc5f6170b451a18bf2c7c50072826ffce7a365d097b32ee01789add \ + --hash=sha256:4a19b00097fcc8e3008bb006cd6bfaf0544e9fa2abc4cc77fbad57971a37dcc0 \ + --hash=sha256:4b660543eee66d5f07408fc1cdd0d017416f0f5cd1725def314cca3f67b0cac5 \ + --hash=sha256:50958a182c7e189fa1408d9a28d9a37afbb3d72107cf8c6fb69b07b13506c246 \ + --hash=sha256:55347832dc6d49731283717087551839fa46a7997929403c96599b1bc93124f1 \ + --hash=sha256:592e561f01e670699126207ac05b0cfa068af110adf95c7a8317615cdd9345d8 \ + --hash=sha256:598350a6adefb5799c800fd00bbeacbc115ee560e2fd7b35f703608c1037a2ed \ + --hash=sha256:5c0772d6bba19a5601d24b3c6ce6627484fd5a3fd1d402913e1578b1447d51a0 \ + --hash=sha256:5e7373e6834e4bdf2c24dbb1a0c6dd834bb5a189efb65723ebb58a8f3e76204b \ + --hash=sha256:60d00bbddaec09e8d3259a0d8805e16b96b2709f14d9c486e29568ffe6f55993 \ + --hash=sha256:61104a5fc2923d5670f30e3ecbcb12ed70f49a17dfd757b32bc965eb90b53988 \ + --hash=sha256:6576400e7a748ec5c7ea72f38d626939876dd1756f4a0ccf552b8646dcf6f3e9 \ + --hash=sha256:67be681c7024df71c5d38c976b8d38c5998a17058533fc4eca6599b730d73b3a \ + --hash=sha256:6be479b31ba2f4f86f886060cff1e640facc7f22d266d960b0ca41a9bf2690ef \ + --hash=sha256:6e30838399bf3ff46a97a90a9997ed6960abce22352b33f7469d0f54b4294064 \ + --hash=sha256:6f60209149e1011b4a3f3119ad2404b06507fe077755bdfcd4ea0090f64cb399 \ + --hash=sha256:6ff08246d6c6dcdd6306a7d806eb75867cea6238eeb88778fc9c577d8e9fe2d9 \ + --hash=sha256:70cd0d092d872b17cf1cd66c394a0537c291c97a539bd884fd0c2bdbf989cccb \ + --hash=sha256:762a03f0726850677f7f1e34a105d9121b4e11a0c20c28c56fc4b33a809c0e64 \ + --hash=sha256:76601efc7bfe44014d7bf7d6ba9c75bbb0b426c93ce0f9216cd9ec5d384c224b \ + --hash=sha256:767c6c0cf3f67c3e4e246ae7e1cc9ce7755f174f994aa3111c8357f16a587161 \ + --hash=sha256:7f9cb34c661454f73432112a81ac522ebe69500feeb8d77f744f6bd3e8b2f2ba \ + --hash=sha256:811f0ef99cc178becef108563505b9e5aa69a406028136874edf6513d3568f8b \ + --hash=sha256:816db9f5ca3da2ecc9c74481c5b0474cc4368cb94e77f40bff8493abeeac81f0 \ + --hash=sha256:81b2682cdcaef93e1bc110ee4d626d8117c50c0427e0e9b8120e7c00dca90474 \ + --hash=sha256:83537149cfeff3d960758a2e5db9894f6207d83750858b4e1ec249849d864edb \ + --hash=sha256:83d93abf6a68d54b4e2c4c041ffcffeb94b1c9ab3171443fda3f5f19024be517 \ + --hash=sha256:8404f75bd1f23c2a806d8e61c95845969f9e256a91d8cdc07fdecde9a39b88b2 \ + --hash=sha256:844f16c472352afa499d3a2ff0316cc812204bda42540af4768ac80a98b56277 \ + --hash=sha256:86238bb78ff65c9fc1e6b371b78f271e23c5d61898222c201122209dc8eadc76 \ + --hash=sha256:88a79efc9949837126358be48d3a73628b615d0f9e4a81b2f819cf324b6fedd2 \ + --hash=sha256:8962a67c314f1da82957aee5b940698aaffff13e41b3298baa59d30cbddb23e2 \ + --hash=sha256:8d58d74be01b0eb74a14cfe768495c57f3a7a7c573a9db3c984b5f2c759a0fe5 \ + --hash=sha256:9063635665d5e62601d1f089526155ad2aba3c85e2050e439c774919447155aa \ + --hash=sha256:90ac257fd93bcd915672262b5f93f606400c1cc29e869bb2ff806038e7234a94 \ + --hash=sha256:93203131f4da1824dec6ffad11ff8afa03f0690f0075efa901ad7517a261a359 \ + --hash=sha256:9438a4436d33b4c71b57f20cb982f791703c868f8c497c998ec33de3b9c4ce0b \ + --hash=sha256:95832419b87b882bec9dcd7d041d74887ba7745b3659c14be1ae1db5cfa35cad \ + --hash=sha256:97206d90a8c68079525f9c4f64e94699927ceb01569b4a4da1c798c758abcf47 \ + --hash=sha256:98db40c6ef8bbeb028e0424736a6bef3b1d8d0a02399236eb00db0dd0b7ca957 \ + --hash=sha256:99a5b0a908beb01b0e134b7b085d0ea6bfb7ed28ba3ed0737365aa2ce9bda0e4 \ + --hash=sha256:9a1841be8752f46cbf58a4825f3789ea66ed9c1190be5d81327509cf757b6f5a \ + --hash=sha256:9e03132ea81849b5732122d3d2f39e46a327a461eb3d678f1e74747c9b7ef154 \ + --hash=sha256:a0eee1c2cafed49facb1fd300f684401ba5bd557d6d7dfa9bd0c13c5b0904f0c \ + --hash=sha256:a2b77b3e6a9d817822407b32c514205b44ceeb8ab197bee09de19e5f1e04ce18 \ + --hash=sha256:a2c36e6d747dcf42f5afc49b107c65c7e2be5dd8e80209244bc50c6bf2bfb81c \ + --hash=sha256:a404419c65611d015eb03b204d3558635d97fcef149d0e8ed0ef818f7262f76b \ + --hash=sha256:a4fa195374868fc65fe0ddefacf080c191f7405b36c8ee4ff7ce6c749b52dff9 \ + --hash=sha256:a5ada8e81fecfca0d934520be81117e5fdccbce7ae43612ae8aeb6c40dfd69ff \ + --hash=sha256:a78928eb29e96e8386eebc6c4271648e3918502a24296da7020607d74d33e622 \ + --hash=sha256:a8389010a2721e568fbe40ba94e7d8f931beabd59b95af2b2ab8238fd40a6b3e \ + --hash=sha256:ad12368a31ddcbc3aed132ffee572917026315936a7df5433b9a9e8a5a9b0c33 \ + --hash=sha256:ad5e6b56000d4b1b82a380c664982371939dc8f728fcbb73d017edc035247dcb \ + --hash=sha256:ae30d67c5f41be6c70d28557c0a3285787dccbde46a3c84a97d01996cbf43e7c \ + --hash=sha256:af550cb399a702b1f277c375461fcd9533f46b435bba1e7847b4b729c6a316aa \ + --hash=sha256:b1829bfe98d1f6e7bd79646b78e73dcef92c5aa32aaa622b9e07bf39df98c9b5 \ + --hash=sha256:b6b7397804e9299bd0c01ea426245fa3d730d3e9c31f583f51aa87bed399c481 \ + --hash=sha256:b77364386164808efb1d7ea1305b14bb04fc6907aab242df3f6c04f5f0c1b371 \ + --hash=sha256:b8e62fec0ca6a14d8e47cde788be90237b089ef9cf46628dafcd5bb1bd64a219 \ + --hash=sha256:b93c17989d23a08104acc7e1912250b2eec4d24da9983d3c5f8ca9cda8d62a02 \ + --hash=sha256:bc6018e95814fd64ea8357c6e9e5608b97b1f33962c76cff60efd52f76b18a40 \ + --hash=sha256:bd044cff158529364124210044fec4f1b0a13219fb6e8b9e393384458bb753fc \ + --hash=sha256:be154e808cce4259aec43e3097599d8986f39963cf300fba1b414eb1c3137e1d \ + --hash=sha256:bee76669a5b588cd806aa619ea9eb8f0c8a00e6991001d830e07cc69258962a9 \ + --hash=sha256:c1fc62fc688e884fb6300a536297a09c089c43f9eb8228790bafa66c28e28900 \ + --hash=sha256:c1fe81cd71d35fd2770dc206a4bd818bd73b515810314e1573b9db8c9a0284db \ + --hash=sha256:c260e150268c47a51b5dc7c2d907ca2a18ecc9a1dabebd62da427342fa6923b6 \ + --hash=sha256:c541249b3b9b5a023a9b6c9cc29e16777926e513c97fad3e2c44956e30d5fc7d \ + --hash=sha256:c6fbc4e3017fb99e639abdb58a6a31e14bcbd270562805a0b80a102f8a4f3024 \ + --hash=sha256:ca4b9402ca8ad16ac8a76aa740c6d8c33a783c684b2f9320685f806ea88306ca \ + --hash=sha256:ceee3a95661cd91eaa7b42d303e1d3c7d0d302674cefe74022b574523af218d2 \ + --hash=sha256:d04df504ac8c214eb5e0c9ef640ebe97c539c4a32a94603e9c7aea595aeba688 \ + --hash=sha256:d0eaf2ff43d5940112cb863c6a09d7d942b6317c006362c555d4c523eab6367a \ + --hash=sha256:d1a9424ee53ac4600568f02683254237a3400af2f683060e7217f744d0833885 \ + --hash=sha256:d1cda1816c79bf9f7b0f7ced1b48bda64c8157a8532e1318727b4157062a5a67 \ + --hash=sha256:d4cc2cb5f117da6460c14c65cbc0d1cf0976af3e56de6cd627cc36019f323e15 \ + --hash=sha256:d54b2f5dcd78b99893d0f6f265749886ae4d316fc54ab352444e581972555210 \ + --hash=sha256:d5babb2a32f79826644c75ebe1e449ddf9035f38cfe4c820e2e3aa61adb0dd08 \ + --hash=sha256:d6f6a61516c4caa5413bd71e635039976224ccc56e82e3eb4a1df3eb5e370a28 \ + --hash=sha256:d7eec1b09bde3a9b6e2102717a587b9c9a96c360a1ef478b5668414619cac606 \ + --hash=sha256:d95ee6696aa5b7283f4a27b67eb7db1c4fb5bb7a9117205d29ebaaa7f6294d7b \ + --hash=sha256:da4ad7a0d4c589069c46101dcb55ee304616293bcf614f4c445b3ecc961fa836 \ + --hash=sha256:db98041fc5d22f1cf66b38a361cf07a15321c0f2956b6134b007733de8befe2a \ + --hash=sha256:def4d23969c4e944a9b01ad3187893edda1cf185d5913224420a72d1959245ff \ + --hash=sha256:e0c195d2a2cf60c5f32928415d44d430129899b4c215d710f93179a056cf560b \ + --hash=sha256:e2f439ffd4fd7d64b77f6a287d4605700bad26fe12bb1b63b4ee45211344e2fc \ + --hash=sha256:e4e32e99c3f56f4e4766bd86f0d14f32590098240bce76df2452a8caecf7cdac \ + --hash=sha256:e5133cc123cbbb69b585bb0b0166bf03c035787892a8b365238dd060ce02f8b1 \ + --hash=sha256:e7adbd0f38ead7310e1e7428f254d450857645efc761c937a7d71100cce7a3a4 \ + --hash=sha256:e87e2c89016a83a6d7b8ff4e688ee6da843c5bf46bd1dcbdd36b181639575350 \ + --hash=sha256:e90bf1e025fee24edbba0b1459624d46ef9208d3d479cd13fae799d26f1609b4 \ + --hash=sha256:ecd0172b50350cae19e8dbb07789e11234099de8ea44db0a981467a98c165170 \ + --hash=sha256:ef9fe783b932927da8070f5b2913ce412e42c80bf17fd523042325ee3a44f756 \ + --hash=sha256:f0c7a36ccb66bfc8fcfa7a9722614b959231e325f0e08862c6ea70a7283a6520 \ + --hash=sha256:f401a82d80c53d88605b82a80623edd95d922732d2c513c1c5f8e4b5e10c2913 \ + --hash=sha256:f61f2850b318c043961662f6cdd08e69b05f1d25d0e321782a3995d39f811548 \ + --hash=sha256:fa216ac716e7c99e4dc4b039c6219a31cd381cc0588ca45cf66f36011613f3ed \ + --hash=sha256:fcd32858eb0df02dd0210523f12e1dabbae1a8d2d74b58ba40aabf2ca75ef872 \ + --hash=sha256:fe9b3b53b0688e9e5d7358e934e87c2da5ed34d997d0ef1ec403024ea760215b # via fastembed mpire==2.10.2 \ --hash=sha256:d627707f7a8d02aa4c7f7d59de399dec5290945ddf7fbd36cbb1d6ebb37a51fb \ @@ -3113,74 +3293,147 @@ mpmath==1.3.0 \ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c # via sympy -msgpack==1.2.1 \ - --hash=sha256:01e2dd6c9b19d333a00282330cc8a73d38d8dabc306dc5b42cd668c3ac82e833 \ - --hash=sha256:020e881a764b20d8d7ca1a54fc01b8175519d108e3c3f194fddc200bda95951a \ - --hash=sha256:04c721c2c7448767e9e3f2520a475663d8ee0f09c31890f6d2bd70fd636a9647 \ - --hash=sha256:05f340e47e7e47d2da8db9b53e1bb1d294369e9ef45a747441309f6650b8351d \ - --hash=sha256:0a70e3cf2804a300d921bb0940426e35f4e489a23adfb77a808892241db0a064 \ - --hash=sha256:0adcf06ffde0777c0e1a9b771a2b1c4226ba1bbf748c8efcc02fcdeca3299107 \ - --hash=sha256:0c0d9802354507bcba62af19c17918e3eb437cc25e6f50657d511b5856a77aac \ - --hash=sha256:0e2bf9280bceb5efca998435904b5d3e9fdbcc11d90dc9df30aec7973252b720 \ - --hash=sha256:1233ee2dd0cefba127583de50ea654677277047d238303521db35def3d7b2e7c \ - --hash=sha256:146ee4e9ce80b365c6d4c47073da9da7bcec473e58194ceee5dd7620ace77e06 \ - --hash=sha256:1548006a91aa93c5da81f3bdcebc1a0d10cea2d25969754fbe848da622b2b895 \ - --hash=sha256:196300e7e5d6e74d50f1607ab9c06c4a1484c383cd22defd727902591f7e8dde \ - --hash=sha256:1dabedcd0f23559f3596428c6589c1cd8c6eaed3a0d720795b07b0225d769203 \ - --hash=sha256:20466cca18c49c7292a8984bc15d65857b171e7264bdcb5f96baf8be238791fc \ - --hash=sha256:298872ecf9e61950f1c6af4ca969b859ee91783bb920ef6e6172697d0c8aad74 \ - --hash=sha256:29a3f6e9667868429d8240dfd063ea5ffdc1321c13d783aa23827a38de0dcb22 \ - --hash=sha256:2eda0b7ebb1283a98d3e4492ac933c8af6aff59fd3df1c3ed024f536af4b1dc8 \ - --hash=sha256:2ef59c659f289eddf8aa6623823f19fa2f40a4029266889eac7a2505dd210c35 \ - --hash=sha256:2ff164c1b0bcb740b073b99e945234d0212852fa378e44a208c425379140dbeb \ - --hash=sha256:33f14fba63278b714efe6ad07e50ea5f03d91537aa6a1c5f1ceca4cf44013ca9 \ - --hash=sha256:350cb813d0af6e65d2f7ef0d729f7ff5be5a8bce03665892f43e5883d4ecc1b8 \ - --hash=sha256:4202c74688ca06591f78cb18988228bd4cca2cc75d57b60008372892d2f1e6e6 \ - --hash=sha256:4227224aaec8f7fbcbfbd4272319347b2bb4030366502600f8c45588c5187b07 \ - --hash=sha256:491cc39455ca765fad51fb451bf2915eb2cf41192ab5801ce8d67c1d614fe056 \ - --hash=sha256:575957e79cd51903a4e8495a242442949641e08f1efd5197b43bebd3ea7682b4 \ - --hash=sha256:5ad5467fc3f68b5468e06c5f788d712e9f8ffc8b0cd1bcb160c105c1ee92dae7 \ - --hash=sha256:5bb9c386f0a329c035ddbab4b72d1028bf9627add8dda41070288563d57ed1b1 \ - --hash=sha256:5c24aa15d5963051e1a5c62b12c50cd705992502b5ec1f3bece6046f33c9fc24 \ - --hash=sha256:5f6277e5f783c36786a145e0247fc189a03f35f84b251646e53592d2bc12b355 \ - --hash=sha256:60926b75d00c8e816ef98f3034f484a8bc64242d66839cef4cf7e503142316a0 \ - --hash=sha256:633727297ed063441fd1cda2288865487f33ad14eeb8831afb5f0c396a62cfce \ - --hash=sha256:67f6dd22fa72a93752643f07889796d62739a13415ee630169a8ce764f86cf9f \ - --hash=sha256:6d09badf350af2be9d189184e04e64cf54ad93569ab3d96fca58bd3e84aad707 \ - --hash=sha256:6ee967f7c7e1df2890c671ff2ee51a28ded0efc95da3e507176dee881ce36c66 \ - --hash=sha256:74847557e28ce71bd3c438a447ca90e4b507e997ddbdef8a12a7b283b86c156b \ - --hash=sha256:779197a6513bab3c3632265e3d0f7cb3227e62510841a6f34f1eaa37efbb345e \ - --hash=sha256:787c9bebb5833e8f6fc8abca3c0597683d8d87f56a8842b6b89c75a5f3176e2d \ - --hash=sha256:7d31c0ac0c640f877804c67cb2bc9f4e23dc2db97e96c2e67fa27d38283b41f8 \ - --hash=sha256:810b916696c86ef0deb3b74588480224df4c1b071136c34183e4a2a4284d7ac7 \ - --hash=sha256:83efa1c898e0fc5380fc0cabbf75164c52e3b5cbb45973710d75821928380c73 \ - --hash=sha256:85f57e960d877f2977f6430896191b04a21f8901b3b4baf2e4604329f4db5402 \ - --hash=sha256:8b267ce94efb76fbd1b3373511420074ee3187f0f7811bf394531de13294735a \ - --hash=sha256:8c2ed1e48cc0f460bf3c7780e7137ff21a4e18433451916f2442c1b21036cd7d \ - --hash=sha256:8c7b398c56ff125feae96c2737abfec5595f1fa0aa186df60c56040b8accb95c \ - --hash=sha256:8d00f177ca88a77c1cf848d204a38f249751650b601cb6532acc68805d8a8273 \ - --hash=sha256:8ff92d7feeaf5bc26c51495b69e2f99ed97ab79346fb6555f44be7dd2ac6503b \ - --hash=sha256:91054a783328e0ea7954b8771095705c8d2243b814743fbaadf14552c9c52c5d \ - --hash=sha256:98b58bdb89c46190e4609bb36abe17c6d4105ad13f9c5f8f6f64d320f8ced3fb \ - --hash=sha256:a28d076ca7c82b9c8728ad90b7147489449557038bed50e4241eb832395169b4 \ - --hash=sha256:aa6c4be5d1c02a42b066ca6ddb71adf36432868fdcdb6ee87e634e86e0674190 \ - --hash=sha256:aded5bdf32609dc7987a49bbbd15a8ef096193f96dd8bbeb791de729e650acf5 \ - --hash=sha256:afc5febcd4c99effbc02b528e49d6fd0760b2b7d48c05239e345a5fa6e743d9a \ - --hash=sha256:b50b727bd652bdc37d950336c848ef20ec54a4cafc38dce19b1cd86ad625d0f7 \ - --hash=sha256:c1c79a604a2969a868a78b6ebd27a887e00c624f14f66b3038e0590cb23332d1 \ - --hash=sha256:ca0dacff965c47afdc3749a8469d7302a8f801d6a28758d55120d75e66ce6889 \ - --hash=sha256:d3567748a5107cb40cdf66a275430c2f87c07777698f4bfd25c35f44d533258c \ - --hash=sha256:dc871b997a9370d855b7394465f2f350e847a5b806dd38dcc9c989e7d87da155 \ - --hash=sha256:dd3bfe82d53edfe4b7fc9a7ec9761e23a7a5b1dac22264505af428253c29ed24 \ - --hash=sha256:e3dc2feb0876209d9c38aa56cb1de169bd6c4348f1aa48271f241226590993e6 \ - --hash=sha256:e4f1d0f8f98ade9634e01fb704a408f9336c0a8f1117b369f5db83dc7551d8b1 \ - --hash=sha256:ec0e675d59150a6269ddc9139087c722292664a37d071a849c05c473350f1f2d \ - --hash=sha256:ee1d9ed27d0497b848923746cf762ed2e7db24f4be7eec8e5cbe8c766aa707b7 \ - --hash=sha256:f02cf17a6ca1abe29b5f980644f7551f94d71f2011509b26d8625ce038f0df64 \ - --hash=sha256:f12038a35fabd52e56a3547bab42401af49a45caa6dd00b34c44de235bc93ee2 \ - --hash=sha256:f310233ef7fb9c14e201c93639fe5f5260b005f56f0b29048e999c30935596cc \ - --hash=sha256:f9389552ecf4784886345ead0647e4edc96bee37cbab05b75540f542f766c48c +msgpack==1.2.2 \ + --hash=sha256:06d95f61de7afe4f4ff908a6feebfcb070d0582ac87c9cf3cedf8551cf634516 \ + --hash=sha256:0708afbf6a9587f0bfe479a9825c141d14d91e2f6a5c8103cf28bc96f4edb5d9 \ + --hash=sha256:0883a1578168929fd1640fbbc4614773f1a130e419a8a817dc2918d9af1b651c \ + --hash=sha256:0a652ceeededf71d3fa40c303a02a149d42338d310162367b91c539d4bd6e0a3 \ + --hash=sha256:0dd9173c5ebaf5ecc5ca86e7ae1db92934e1d57b856f3dd90698941431f4fd77 \ + --hash=sha256:0e3315de5a4b2920ccef48d96b4448025e064a10d0f5a250f6584477d839c8d4 \ + --hash=sha256:0e91332144f69bc3018c91232fac26da580ef748fb8eaddd7914d4458001cc4f \ + --hash=sha256:0fbc1bed8a535389b41882cfae66376e248cd1680eaa94fd83193c73e1d24986 \ + --hash=sha256:11e8c421e117d1c36728b423d0402555cccbf0c6f53e288f0e75b6b12100d70f \ + --hash=sha256:1510f24612d4b983dff6935d9273e02c320cfd525727fbcb58836a75f589fdbc \ + --hash=sha256:1814f92306ae7862908e9ece7cfd90e0dc87ded3e89b6ae7ffdd1175d6376fdc \ + --hash=sha256:1e8cdd1f3e7cc52c751092a9bf740e81e6919ab109cd376ae2d965dad0bbae34 \ + --hash=sha256:1f3af0baafd184436501004828bb3df64eeb2fc49dfe9d89abcf604956094563 \ + --hash=sha256:1f6b6f8deb07d49090e1808c6ef9cb7d23ca17bef3aa6ed3e5e03df16606e60c \ + --hash=sha256:226a62ffe99fe54c5c61d910ec64c3449b7766c3280bd286bf6c94838dde239a \ + --hash=sha256:29cc2d5291711a52956a79a51f41c732329df39ad727c886bd8f0b5b9237a808 \ + --hash=sha256:336525cc2688e43ea77dfb1a4ce012c8cde561835913801dbfcfdcf4111d8abb \ + --hash=sha256:34e83e345194a2a51d8bd447dea9de2104f91e75b247f4735f14f04529f0746b \ + --hash=sha256:352ed831042549cca8be23780e1fe7c9177e65ff02bf183509c4b4d33f671782 \ + --hash=sha256:3e915d390d7068b257ca8b62f3fc59fad135c8631d1017ab03b0b924b07c5367 \ + --hash=sha256:419a45c67a5c04213172a14b1864657e014665b77d7081b107a51707923dd39e \ + --hash=sha256:42fd9260416885b4815caca5bdd14dfd5dda6cdade732d6c09104ef8f6228761 \ + --hash=sha256:46ec851571d8f1b6e29794ebb9dd36f785008da6d14f57c702e60781d6caf648 \ + --hash=sha256:4710d881d8fb047deed2485707409116722af2b992d3fefd73c7667c4e350839 \ + --hash=sha256:4955accbd87f27beebef5f3ecc27503aa74cb016fb4f640868e749fd93194a35 \ + --hash=sha256:4a4348705be86e029d04e741cf9ed0dfe03e942d7d3b92e838fa80d3aa2c3ebc \ + --hash=sha256:4b554d8164ebb526892194f71dcd96ef1fefe0c250087498785d3ffc04a80be3 \ + --hash=sha256:4d9a562aec0a92fe536da2e533d313b3d2a6b929157b1dec7ff623446dc0a8ab \ + --hash=sha256:51dd39d23cfdea0400ed3ff2d29d1e83bd951d3aea79dc89be5b701a09edfe23 \ + --hash=sha256:53679573c75cce5f82359e0bd4e6a97809a6b9a9b7a48fd1ba592f4a82cddc84 \ + --hash=sha256:55faa6f8395e23b848c535ad5dcb96b3462f37f5e7f4ac500d500434f7345da7 \ + --hash=sha256:58ce37a4a54577115922385d37201d9a44d66d0167dfbbf4770a2e9bf8ea7ba3 \ + --hash=sha256:59d5b93efa45fd09f620d0c9ba81cde339a2c9937af3eea42ee9653094ce6640 \ + --hash=sha256:6195257a107bf25872ef84aab7295078271eea3ac6413f0506b631f6c9586ed5 \ + --hash=sha256:652d1bf13d01bac8fd569def0fe76745e55bcda01e30aa6332d5947ea3788839 \ + --hash=sha256:682804bf31e43d46e51a9a33bd575b51e839d715ce6bd5612c055f7b28ad637b \ + --hash=sha256:68df2947921d449f6dcfeafd86cb2cdde13327a8b447534bbe4ee5aaf32a5695 \ + --hash=sha256:6f53285f20d592ed309ee19e509cc4c77a3bda1db02ad67e8a0949bb227a5a6d \ + --hash=sha256:73b0e05c32c3cfc3cd84994908e57430c0ebc6813abf905d3f18ff115d54df3f \ + --hash=sha256:77c2e018417dc1d66f235e383877ee885b60ade9d29e494dd581e08af2cb1923 \ + --hash=sha256:7826f16edc763e768404f55605ef85dfcf5857e729c1ed29e0d7c180be4fe6d8 \ + --hash=sha256:7afa5431f6f3487c584187ca6c8e2a34e9b106529893b3e720eabb068f6ac970 \ + --hash=sha256:7d095df2627e5dd59ac7b0c5ad627a671c76e6020171e03cbe4621a61f0562c3 \ + --hash=sha256:7fe374ba76eb0ecca13a1703daa8fa85825a6ddddbb52d4c1a732fa524194683 \ + --hash=sha256:82b1bdf293267afaadcc608b125e7fc6576bb0785a60c4fa7d07c7ab76ed76ec \ + --hash=sha256:86f173a584f72f6164801f31866d22a581f60c991572cf922aed9ab8eb422b77 \ + --hash=sha256:8b1415d02e9bf722672af8a90f90813265a0cd0b14163187261e54a5592bc949 \ + --hash=sha256:8b2a281b556f120a43e591ea39915741b7ad54d4727b9c4350a0a11692252533 \ + --hash=sha256:8c6321a414f8b4a8dc43976b2fa8349156434ca9adedd9a187b796f7e1d3d3fc \ + --hash=sha256:8dc4487097571f7311188c3eca2a3e86cd1f1db4c37c7a017bcc3fd38486cbfe \ + --hash=sha256:90986cc9aab9d7d1d8f38bcbf65d3f7ac83bdd90c35765db7d691b4829698cba \ + --hash=sha256:9352e6cdb510a7b1a5d3ccaccec730e82e50cf3484a3af7bdaab19e23b9589ff \ + --hash=sha256:935b1cfad9b908b0fa845010f4271df4c2f04e1cd26e3f18acd61a45f93c9e36 \ + --hash=sha256:9b659d77f8726fa5e7038967dda6b68d53cf34472c094cfa5b845454713b90d5 \ + --hash=sha256:9bd3d1557c3fe1a095068210708a03e3e4795973392af6f4047060e70abd9a6c \ + --hash=sha256:9bf452ff4d4981f25a18e9476e002bcc9263e7928024aa4d7148e25f7be3f929 \ + --hash=sha256:9d7fb25b4442fae0cb2590272d06ab4f6caa526ee36a994edb81e946b874813e \ + --hash=sha256:9db1ba1c1e6a84245a9dd866265b56b8a1e9461549cc72ed296d8cbfbd32961b \ + --hash=sha256:9eb0b0e602064527a045ea28c4f174ed69383587e29cebe28947e3b84106eb2a \ + --hash=sha256:9fd7f32e2f0fb334e7ecc5adb5cf0458785bd3a9d9d86f950e1715f101cebce5 \ + --hash=sha256:a378e12ccc06d76efde115caf4073b7e5ff3cc18291d1341f9e65fb882e3f754 \ + --hash=sha256:a4161eee7799863aee237c35c90427861f7b994416dd81ae829f560b0a81bdcd \ + --hash=sha256:a9b4cf3685a135666d27d0d7a73fece74e2fad01d9b508fded89e843512f0e90 \ + --hash=sha256:aa1120c653b76d8eafa50423b5eba06b5c9737f8692c74fa3afe03e84b8978ea \ + --hash=sha256:b07c03f0da7e5279170df7745ddc732d526c8a198208936ec1a95c11ed2b2d5f \ + --hash=sha256:b13b59e66f107cca1ba708dd5307179870ca1b15b19fcee7ccf722e5308d9212 \ + --hash=sha256:b542ffc0a5c531eedc40419f291f1bd659aa8d4223408a5b51c88a2796083fd3 \ + --hash=sha256:b5c696ae7cd7166b3657261adb855b461ff31f07823fdbae9de8bf80adfccc21 \ + --hash=sha256:b68614fba0570349833b7dd999ff0aed4e5cc8d9eb6e3a7d4527be33c65e33d3 \ + --hash=sha256:b8dd6c71d20c28d2d0eb0c51e7cccf3584afde3b1364f6629596186c9025bd54 \ + --hash=sha256:b9b0c1f2aa7b0026b4bd50718100e8b04175e4f36e160aa852502377b5e572e7 \ + --hash=sha256:c522420d78db2431887d45b518e304d86e27b9ad0b30f24e3806a6ad5d8bdbfc \ + --hash=sha256:ccfd880988f8438d1c91c77d7edc58e70f4d2012e999167bc154c64c6f06ea6b \ + --hash=sha256:cdb6cc6e1127d15879c47a8b3270716243da82d3e7feab1f5946872c75b3d60f \ + --hash=sha256:cf66fb38703e61a486b01b56d43bb1f50698fbe99b6bd90feba10f24fab60b3b \ + --hash=sha256:d13d07efbf655f9ae7a2352b630c52727b359005b21ba08a507585c9ac8c0896 \ + --hash=sha256:d242f3c4ccf55b056e6cf901720dccde58f1df117898f2bbf3bcd6e38ec7c248 \ + --hash=sha256:d24b38a825bcca41bb956de50eb98451ef291304a8607fad99e619043d3e79b9 \ + --hash=sha256:d3c247d457ae9079974c7ce3c665396754a6d2baff7eaa51332212a8a5a3f13b \ + --hash=sha256:d886baa46b2532135e7320067e6a44edb09ba5883a6096b0f9c044533984b8a8 \ + --hash=sha256:e05a94a0442de86818a30281c6cc2cb9cc7aa148386fd3541c4d4774b73cb3a9 \ + --hash=sha256:e1b99ad34613d5f8477fa5cf99bc4eaeaf27965588007c102370cd9a78fe9de5 \ + --hash=sha256:e2eb7ea0ac3911a7aac9d8aaa36d40f216d99455b3274cd3fac38181bcd910cf \ + --hash=sha256:e497ee34e8a3342bbde51b27c22d8db05a651df3361dd3daef5b3ab0d66f3e04 \ + --hash=sha256:f11e09f10210a91c169e39c7a5a1f9090eaa73ad75555fafad5023c3053c47ba \ + --hash=sha256:f466049b8e1ec0854287bbe9a074316826fe0e08dcf707245f98b1ae49e92650 \ + --hash=sha256:f80361592c13d7226b4379c8941529b63fe1a9d0e05d2de8f3306b70e522b53f \ + --hash=sha256:ffdd2f4950daf7815490f23087963e3420175b9609520b7ff5df64d351159c22 # via librosa +msgspec==0.21.1 \ + --hash=sha256:0d03867786e5d7ba25d666df4b11320c27170f4aeafcb8e3a8b0a50a4fb742ca \ + --hash=sha256:0d1009f6715f5bff3b54d4ff5c7428ad96197e0534e1645b8e9b955890c84664 \ + --hash=sha256:0d2cc73df6058d811a126ac3a8ad63a4dfa210c82f9cf5a004802eaf4712de90 \ + --hash=sha256:15f523d51c00ebad412213bfe9f06f0a50ec2b93e0c19e824a2d267cabb48ea2 \ + --hash=sha256:1bf17cbd7b28a5dffc7e764c654eed8ccde5e0f1de7970628608304640d4ce4e \ + --hash=sha256:21995e74b5c598c2e004110ad66ec7f1b8c20bf2bcf3b2de8fd9a3094422d3ff \ + --hash=sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c \ + --hash=sha256:344c7cd0eaed1fb81d7959f99100ef71ec9b536881a376f11b9a6c4803365697 \ + --hash=sha256:38fe93e86b61328fe544cb7fd871fad5a27c8734bfda90f65e5dbe288ae50f61 \ + --hash=sha256:3cb779ea0c35bc807ff941d415875c1f69ca0be91a2e907ab99a171811d86a9a \ + --hash=sha256:3d6b9dc50948eaf65df54d2fd0ff66e6d8c32f116037209ee861810eb9b676cb \ + --hash=sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898 \ + --hash=sha256:4692b7c1609155708c4418f88e92f63c13fdf08aa095c84bae82bad75b53389b \ + --hash=sha256:48943e278b3854c2f89f955ddc6f9f430d3f0784b16e47d10604ee0463cd21f5 \ + --hash=sha256:49880fd20fdbcfe1b793f07dd83f12572bab679c9800352c8b2240289aa46a06 \ + --hash=sha256:4e47390360583ba3d5c6cb44cf0a9f61b0a06a899d3c2c00627cedebb2e2884b \ + --hash=sha256:5102c7e9b3acff82178449b85006d96310e690291bb1ea0142f1b24bcb8aabcb \ + --hash=sha256:52c5e21930942302394429c5a582ce7e6b62c7f983b3760834c2ce107e0dd6df \ + --hash=sha256:5666b1b560b97b6ec2eb3fca8a502298ebac56e13bbca1f88523538ce83d01ea \ + --hash=sha256:5d2d4116ebe3035a78d9ec76e99a9d64e5fa6d44fe61a9c5de7fd1acf54bcc69 \ + --hash=sha256:5f8e9dfcd98419cf7568808470c4317a3fb30bef0e3715b568730a2b272a20d7 \ + --hash=sha256:6129f0cca52992e898fd5344187f7c8127b63d810b2fd73e36fca73b4c6475ee \ + --hash=sha256:628aaa35c74950a8c59da330d7e98917e1c7188f983745782027748ee4ca573e \ + --hash=sha256:68604db36b3b4dd9bf160e436e12798a4738848144cea1aca1cb984011eb160f \ + --hash=sha256:6badc03b9725352219cca017bfe71c61f2fbd0fb5982b410ac17c97c213deb30 \ + --hash=sha256:72d9cd03241b8b2edb2e12dcc66c500fa480d8cbd71a8bac105809d468882064 \ + --hash=sha256:740fbf1c9d59992ca3537d6fbe9ebbf9eaf726a65fbf31448e0ecbc710697a63 \ + --hash=sha256:764173717a01743f007e9f74520ed281f24672c604514f7d76c1c3a10e8edb66 \ + --hash=sha256:846758412e9518252b2ac9bffd6f0e54d9ff614f5f9488df7749f81ff5c80920 \ + --hash=sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a \ + --hash=sha256:92d89dfad13bd1ea640dc3e37e724ed380da1030b272bdf5ecafb983c3ad7c75 \ + --hash=sha256:a9aa659ebb0101b1cbc31461212b87e341d961f0ab0772aaf068a99e001ec4aa \ + --hash=sha256:abbb39d65681fa24ed394e01af3d59d869068324f900c61d06062b7fb9980f2f \ + --hash=sha256:ae0162e22849a5e91eaad907766525107523b0daea3df267a9fcb5ba4e0936ae \ + --hash=sha256:b504b6e7f7a22a24b27232b73034421692147865162daaec9f3bf62439007c87 \ + --hash=sha256:c6faffe5bb644ec884052679af4dfd776d4b5ca90e4a7ec7e7e319e4e6b93a6e \ + --hash=sha256:d3124010b3815451494c85ff345e693cb9fe5889cfcbbef39ed8622e0e72319c \ + --hash=sha256:d4248cf0b6129b7d230eacd493c17cc2d4f3989f3bb7f633a928a85b7dcfa251 \ + --hash=sha256:d4ab834a054c6f0cbeef6df9e7e1b33d5f1bc7b86dea1d2fd7cad003873e783d \ + --hash=sha256:d8b8578e4c83b14ceea4cef0d0b747e31d9330fe4b03b2b2ad4063866a178f93 \ + --hash=sha256:dd677e3001fdfed9186de72eab434da2976303cd5eb9550921d3d0c3e3e168ce \ + --hash=sha256:ed2ab278200e743a1d2610a4e0c8fc74f6cecb8548544cdec43f927bd9265238 \ + --hash=sha256:ee9e3f11fa94603f7d673bf795cfa31b549c4a2c723bc39b45beb1e7f5a3fb99 \ + --hash=sha256:ef3ec2296248d1f8b9231acb051b6d471dfde8f21819e86c9adaaa9f42918521 \ + --hash=sha256:f041a2279f31e3a53319005e4d60ba77c085cfcbe394cdc7ce803c2d01fe9449 \ + --hash=sha256:f60800e6299b798142dc40b0644da77ceac5ea0568be58228417eae14135c847 \ + --hash=sha256:f667b90b37fad734a91671abd68e0d7f4d066862771b87e91c53996dcb7a9027 \ + --hash=sha256:f7b27d1a8ead2b6f5b0c4f2d07b8be1ccfcc041c8a0e704781edebe3ae13c484 \ + --hash=sha256:fab48eb45fdbfbdb2c0edfec00ffc53b6b6085beefc6b50b61e01659f9f8757f + # via pinecone multidict==6.7.1 \ --hash=sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0 \ --hash=sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9 \ @@ -3419,63 +3672,72 @@ murmurhash==1.0.15 \ # preshed # spacy # thinc -mypy==2.3.0 \ - --hash=sha256:04e617030eca5221909c8b7d8d7fd1c637948199aa2100b2ad9813feb07e1491 \ - --hash=sha256:094af99f92638aa92852326188b85a89e50f4a472f44827c03362228482f0762 \ - --hash=sha256:09abd66d8685e73f8f7d17b847c3e104d9a7b164a8706ea87d6c96a3d45816d5 \ - --hash=sha256:0b025a93cffb9781d231f232be07a17912f35f10a313c24f301c81e842870654 \ - --hash=sha256:13b1b16e2fa39f3b2e33fb1c468abc7a69369fa2e886b4b87b5afc81472325cd \ - --hash=sha256:1c6c6bf687b17f90dbfcad95b960d32eaa0154c00da45f03ab50bf8952e047fe \ - --hash=sha256:1fa8d916ac3b705af733c4c1e6c9ebe38fd0d52beb15b105c3e8355b55e6ecdc \ - --hash=sha256:28e1e2af8cd8fff551fd30f2fe4b03fb76764ac8b1ba6c6a1bd00ad32b412db3 \ - --hash=sha256:2d53fc67b9d28a43c6199077f49fea0f05839e36cf6158500331c9549225e5a5 \ - --hash=sha256:3419d00717afbc5265b50dd14b1278f29ea4884dd398ab67873489ac093fd329 \ - --hash=sha256:37fa4de896a84e2dc9200d91e614c22563b43d1a266789d4bbac7b22ebe6192b \ - --hash=sha256:3961a4a34b05f7c74b0f05aa51fbfe99a2d1e126038df40318d15c8f558b7ef3 \ - --hash=sha256:3dd0bed92c4bdec57c42505b96416fb9e6a5aa7be84d2809bcd5f2ecec2860d7 \ - --hash=sha256:3e77244df3843048c3f927182916730e40c124cbaa43905c1fb86cb382aa0805 \ - --hash=sha256:4359424140d985192c778c1ce2c114a10c1ca58a381ed79cfa70d37df94b299f \ - --hash=sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e \ - --hash=sha256:56c184d2c20ca6b6378d58d1960270a767f41f5e44acbbd27f05effef4f4e1d7 \ - --hash=sha256:5e91adad1ca81742ac7ef9893959911df867752206b37135185e88dfb3c89494 \ - --hash=sha256:691fdc37132b1ae628d834f672e74de83462d9fb4aff621835767fb43a8dd373 \ - --hash=sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88 \ - --hash=sha256:6f99ec626e3c3a2f7c0b22c5b90ddb5dabb1c18729c971e9bdaca1f1766d2cee \ - --hash=sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2 \ - --hash=sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757 \ - --hash=sha256:75cbb4b9ef04a0c84a957f07abc4504fbf64b8dcc145675101f2d3a78a4b1d6a \ - --hash=sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b \ - --hash=sha256:85c5385b93012ffa3b31479ab579aef5415f4f3a32c6cf1ae07a984d2a0ff461 \ - --hash=sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97 \ - --hash=sha256:944c665d984157cb96a679dfb7a4a81dd1d36b24b9c284b699514e6e626b82d4 \ - --hash=sha256:9559ab18a9c9957dfa3004ab57cd4bac5f26a724329a9584e583367f0c2e1117 \ - --hash=sha256:982e3d53dd23d0a4cef67dd66791fdbede0cf38f9eb617bf47663554c51e1e36 \ - --hash=sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac \ - --hash=sha256:adebc76aab4f3495a88b41d48aa4aff0c03f2822501da76625afcca5975f19e5 \ - --hash=sha256:aec15d465d477558fd842757b487849007311cf3897849cdda0e3162ac0ac556 \ - --hash=sha256:b1942b9314d4c784b8ea1dbab4972603290e5dd5630f06675f13aec97526bc4c \ - --hash=sha256:b352b7e49f5e6576009e8df730e1ff4f915cb565b851b396d2ffe2f5a6f5da88 \ - --hash=sha256:b5cd2f027a972a4a5f2278a11fac9747f5f81a53a30b714d74950b6807e55568 \ - --hash=sha256:be51653d7669d7d7955d613b8d0bb57d5b652eaf71a873ddf65ac87254dd2595 \ - --hash=sha256:cfca8ee88544090f86b6dcce05ec55d66eb48a762412ac2507810ba4bd793b6f \ - --hash=sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1 \ - --hash=sha256:de121747278144fc9ae7caa2e978cf5df12aebc82933182f5b3b86081a30baef \ - --hash=sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6 \ - --hash=sha256:ea317b060ce83e26050f8f9e4d7d6bf44ed7597c8ff9990bccffbb9d1d8522db \ - --hash=sha256:f1b3a98dfd21058bc759bb3337d5d1f61d0fdf9f3cf9c00f4291790fb5427bff \ - --hash=sha256:f4ed18f111bfe2d599bca7468e7f9251042c1c2118f762c8de2766a56d773c60 \ - --hash=sha256:fbc00cee7bdbb9291979ddc9d08034a29dfcda4932628c9bbc28c1edd589df0c +mypy==2.3.1 \ + --hash=sha256:114dff494000f18bd10d5d95d84b8567b26da60279ecbe838131841df20e635d \ + --hash=sha256:18162b128c3f9c703cd35f5537446900b0d21a2549aa7a95d21380d2ef643fb0 \ + --hash=sha256:192abaedf75da1bc0b1cef104927e70ec49c1ef0031cc4825c7ee10a438ed24d \ + --hash=sha256:1c80fbc405ed8020f5ff3802dc18cf060197bcdd3fbdd6a26ef2fd34dfdd5226 \ + --hash=sha256:2166b29228835e1f88ff411e96639e6ca3c7fdde84b62ec211f70f86b4051167 \ + --hash=sha256:2329c0501293d4e1f33bc15d04d6304d65a1cdda967ee93a05c1e681a3923133 \ + --hash=sha256:2a0ba2e57847849fb0d1fcdabb32786d223095ed8bc121dfe322bcdb3d9c46bc \ + --hash=sha256:30c0477d4aab7b7f39c8397dc877f2c96b9fe5588ec379f372c56eb63d599f63 \ + --hash=sha256:375d7013876a8233b2d05be185bfa09f689696cd999ce8b1cfe6acac5c80e8a3 \ + --hash=sha256:3c80cd23d85368bdd9f37d5231dfd97d35bcbf5bf41af96ef3a9b078ad1957f9 \ + --hash=sha256:3f7e865dd51f235f60a2dbcd8728a1c095f5ca28f095d48a725b84cd935735c4 \ + --hash=sha256:47a8a7a0a7f6f6e63995c0ac36fa0c07b127413fdc81f0439b7f3dccafd33561 \ + --hash=sha256:47c1b1207258513a9d93495f69c8be9de73916186f0e52703e8c461b7a623419 \ + --hash=sha256:4956f34d145e145562a0a0bf367f642bbc85c04ec2baf47ae015947c3169a85d \ + --hash=sha256:4c5095a327483591c94e0c8d3ef9e50d4ab1369b541eae007c1f23bc2a41f6bb \ + --hash=sha256:5159ae60f5dbc3a498af5ba8365505808ac8031bc63f9e00304ad545d40bdd9b \ + --hash=sha256:52eaf3a155f35cf80b40220288c861eb45f14a2340c1f6cbfbdb0feff32879d1 \ + --hash=sha256:57a936373fc690c43a8cd7e7e12a35148e4ec5aa7698ad7fc0a9f918bdc5be41 \ + --hash=sha256:586b3612214cceabb3c0f588c97e7d1e535393f06a60e912e994f6b3ace97523 \ + --hash=sha256:667196b352f4cf304ded4c10f90cfc179263a1acfb3cdcfa984bdfd340d498bc \ + --hash=sha256:6941ab3619377bc3f32ca02876b07d27f216f5201604b664d3937ea0fdd23bb4 \ + --hash=sha256:6ed5c7e3419083268e5c9258bd1c1ef91af44a9e89374dbcaf37b775716e72eb \ + --hash=sha256:6f041a6de52c9217ca125e78ba0a335cb7fd98a1c0580978e49ab2b126f70b57 \ + --hash=sha256:71af9c8a894e862b58e92abb08e53b05a384a1e5e5d6dc7cda59126211a53d82 \ + --hash=sha256:77ad9529e67dca28e511f5cd5671436584ce91f6d3bac159a353158187b986ac \ + --hash=sha256:83d36c2924df7426333abe7faf4724a7e1aab0d9fd41625e81b4683034b80c13 \ + --hash=sha256:84081f538ce27375045c02e3d7f81bd11d853400621ae245d87ce7b6c420ec74 \ + --hash=sha256:851833db876e7b650f93719c74b7879a08e338979c96054fdfc3bfd90a486355 \ + --hash=sha256:858fc57d3d91fa728e33e7ad71def60fc6272694607b306cd3292db53ae39080 \ + --hash=sha256:8ad80807dc3ab8ea978b1b2b6e4a657194ace1d4ef03e0e731aff1abd517da29 \ + --hash=sha256:8e036f06b41630f4c8a1d48f9ac6aa26acc65f8be089973f5519da643318f03f \ + --hash=sha256:94f04929f1c44c35fb0061e912087edaf504acede963a4a7d00680bd089d8531 \ + --hash=sha256:9b4eacbee8a69836c06eff6d0dd4e134a07c2b047755b30c08625fe214f322c6 \ + --hash=sha256:a32bbbb940af990d3be0b8af321c7b6815bb1b3b48142fe7459b9cc5f58959ff \ + --hash=sha256:b091a455111214cb5c9d54a57b9618e9a49f9fe2a42e4e1ac86e9d104ed96ce8 \ + --hash=sha256:b9c53e395c12cad2c6d4b67d5da7c6057638a132d85c08b73646b18f802a0045 \ + --hash=sha256:bb26deed807bdb0457cf3e3f1cd7c4a1cf9d66864eaf1b4a61e06805d4c6b1f9 \ + --hash=sha256:bbfe022634a2a195406bd469e888d2eaf193b02ba7e607391cd7640374aaae3b \ + --hash=sha256:bf678dffd16efcda2c15cbd30e9ecc0081388e29ea23687a88e686ed92638dc3 \ + --hash=sha256:c8637731bb5eee3671eb2c3200827aa3564ed8a9309ecee4d1afe77e6d031bdb \ + --hash=sha256:cfb12e360242d23d91f5e978d94f58ea66acf5804c4fb6f2f794a20d4cb1b595 \ + --hash=sha256:d00d769056bde2f4e69c175071eba45cfb44fa1ed92bdfbfe64a93e0543b0cf0 \ + --hash=sha256:df12e20c9efd614738c71b390007ecd0181125afc4ccafca04d78a1d2eed2c01 \ + --hash=sha256:e099200a1b1b1223a4951f0a90cbff1b8c91b250ba599dab1f7217a628144d90 \ + --hash=sha256:e598c8c66401d26b150872154a286e6d484cf2789c3bb28a7556806298423021 \ + --hash=sha256:e5f1c50bb05b64e2026b52867e8d21106f01313c744a2c4ecc34c90d12e8d6e2 \ + --hash=sha256:e9144ac16fde007096f9563eb2041b4433c2d705c4218edeb79e7e9d01035ee6 \ + --hash=sha256:eda22fd4efa9dcd39331d1dede9b5b8b8a7fd69af07592e778433da98610d29e \ + --hash=sha256:ef0c6335cda9d807f8193d8ff6204a72bc909fa9882aacbca14f43cdb7188306 \ + --hash=sha256:f12fdb70459d0060dea40b29e52163a961b156106d68d57882a6a9f648983a53 \ + --hash=sha256:f5d716048611e85ca9eefb2e1baa5d73ede389b5820ded260ea27c757d667af8 \ + --hash=sha256:ff715e45b2231a8e85de1d163d1b42791e4d7aab8f5145f85fee1b710b735aff # via semantica (pyproject.toml) mypy-extensions==1.1.0 \ --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ --hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558 # via # black + # isort # mypy -narwhals==2.24.0 \ - --hash=sha256:42fdedf44e5b2ca7505630d45b4ac3058f38d8485cba9fe1652ca23152df7489 \ - --hash=sha256:b5c0f684ccd9d7475b564111e319a4964abcf2baf79d3cf6b1003d06ac9b828d +narwhals==2.25.0 \ + --hash=sha256:1f0f403e8c7e4463cde9bfe78b12fdd809e3ae3dda6d9b2f802934fb9c7a6a8f \ + --hash=sha256:62c036c810662bf7820b7737077176313bc59350eeeefb808510f388c743e4b2 # via + # formulaic # plotly # scikit-learn nbclient==0.11.0 \ @@ -3488,16 +3750,16 @@ nbconvert==7.17.1 \ # via # jupyter # jupyter-server -nbformat==5.11.0 \ - --hash=sha256:7dbaed4a69cae28c2b4d44ab7430a6af4544fb89455023f6f21550be757b60c8 \ - --hash=sha256:f70a17f591a9ccd1c601d5e61a4b20972703926df0ba42458ce14bf575766bb6 +nbformat==5.11.1 \ + --hash=sha256:32d4521c68c6e7d5b29c76defaeed9f42ea733142b9b19f88277ce10390b9c4d \ + --hash=sha256:cc6698fa75f4fab8755ead786317815f13a6fee3b53311c0abb1a8b51d52f7ec # via # jupyter-server # nbclient # nbconvert -neo4j==6.2.0 \ - --hash=sha256:b87abdd13a5cc2e3bd51026926c2f20ac38fa3febe98c340520dce19e97388d0 \ - --hash=sha256:e1e246b65b572bd8ea97f9e0e721b7d40a5ce53e53d0007c29aef63e4f9124d9 +neo4j==6.3.0 \ + --hash=sha256:d0d3986c37ad174a549a39dfec6d22a00a061ebcb0267f1a5caf856589838e73 \ + --hash=sha256:d243f9c8adf882ae7205a76eb2419b0a632d5f3c89383d4a0044d737a2223991 # via semantica (pyproject.toml) nest-asyncio2==1.7.2 \ --hash=sha256:1921d70b92cc4612c374928d081552efb59b83d91b2b789d935c665fa01729a8 \ @@ -3645,6 +3907,7 @@ numpy==2.4.6 \ # docling-slim # faiss-cpu # fastembed + # formulaic # gensim # hdbscan # ismember @@ -3768,31 +4031,31 @@ omegaconf==2.3.1 \ --hash=sha256:3d701d14e9a8828f1edd28bb70b725908b34277cdd72cf7d6a83f94dadc6b6a0 \ --hash=sha256:e5e7de64aeebeddaf8e6d3f7a783b32ac2a01c0fbd9c878012caecb891a1f42a # via rapidocr -onnxruntime==1.28.0 \ - --hash=sha256:07fb3cbe990d6bf0ab3c22bfbbfb0e314151266046ea6edb4a07f556b4258c5f \ - --hash=sha256:0a83bdb70d143cede762b677789bf2a7acca54b3fb82565601d5c30695aa933c \ - --hash=sha256:0d650aeee29368414367b65529e90afe4bf1bab76254789063b8b2f7ea3013c8 \ - --hash=sha256:0faf85fb447a663c9cdadc39bd6b19bdf7bedded6699e45731b9b36c46fd993d \ - --hash=sha256:1a1a19175464665c9b8d50bc916f216cc0b569110045b7bbca8f9f290b186f58 \ - --hash=sha256:26ff0fdd06efb6c155bae95387a09db1a2be89c7a03e4d0bffd5a171cc2826da \ - --hash=sha256:31410f544674f534c2f27348af52ef81682ca9c8719154bf4d48f0ef23823b1e \ - --hash=sha256:4e81a23df16e7acb9d51b06d30cc098e49315ef9180f97bc2221d167b4b04d9c \ - --hash=sha256:4f6e92367ddce1e4d33cf295024f40192be6c6171a09208f515ba169ced06c8e \ - --hash=sha256:54fa221d669282bd8f582708ce4c96010a7e9fb0661f9006b37fe2fedafb73fe \ - --hash=sha256:6afdc83f1317c136e92fc29f5ee9f058de59d87c0b22cee3fdbfbaa0ccc2098a \ - --hash=sha256:8adff67a3f28257b37cfe945a7e952e4122666aa8c91a0380862e9fd4c2ed19f \ - --hash=sha256:8d66f9ceb29909c70839e4e4fb3435c7b490050d8f162bd5f3aba4ca01ee517f \ - --hash=sha256:a166b78ee04f3a37fa1ef82034b6a3ce96d9684e582d4d30b296de83e9998bb5 \ - --hash=sha256:ac301f53b1930402fc46c368e268acfed02f3207272aaff05070d7e09f96f031 \ - --hash=sha256:bc2565e487b4896fb988d6383577d875d958e071fc5f6c3550bd5d02ae98264b \ - --hash=sha256:c35064f9b3c43c81c5d5d282091401d0f1ff22796d93ccade4ea2ece5e137ab8 \ - --hash=sha256:cfab507abe09d6ffeb817eee07944d452fdc0b00fdcef34cab4db10a45e378c7 \ - --hash=sha256:e02feeb0165c5f13b4cc954738078d59b90128516ac12b671ee24a530242bf02 \ - --hash=sha256:e562d6e36a749f6764481c0ddb0f2af3d0b5a3c164291361d08803c557f369af \ - --hash=sha256:f2a3b9e30ce880d4ca54999cb313569e36da4f62eefe25f87be18f43e9a3a4d5 \ - --hash=sha256:f5c5daabd28aad610f83fdcf32acec8fb57e6adc6c6a39fe2a3c755db957b410 \ - --hash=sha256:f649dd6f6452d12a8059888aa489fe519e062e18793dac72b9efa0f9fdb64135 \ - --hash=sha256:f7f022a1103cae591c75fc4565589a515f2ddd14a6ac8e8a05812dfeda142e28 +onnxruntime==1.29.0 \ + --hash=sha256:07c5907474dec4a2792fd7626b753dc66707808385a6d9eecf993db0066a9d0f \ + --hash=sha256:0d4f427afac434b0070fe992b540ddf20a7aff2265f760f314d91331935b6b98 \ + --hash=sha256:11264bb58f7b7cf6af835ab10d36838d73680580820fd6f51d90124a1ca8f449 \ + --hash=sha256:16925ef8497e2c07e4b5ae15b504079b3ab3f65e22c58efd10dde0f3caea969a \ + --hash=sha256:1ea91cef3b971506e51ae9c37c16d027774ec64994a524ec1bdfb027d68a9832 \ + --hash=sha256:2945e1f82f81f27e88decea88c7861f45baea23818950d467bf3909aa303119e \ + --hash=sha256:2b80d8c7ec2cc7438e4da3760b88c24568cba72c9ace96d668800a6c79419acb \ + --hash=sha256:3a3814c041251d6a77fdf513fb282056538ee826d2f1178a0df3c549d3fff6ba \ + --hash=sha256:4a3129ae56e70d2618ff773920166916310370a7e3cacb60b9e0e8910092725f \ + --hash=sha256:4acf2b4948b7ede87221ca6332344b8facdc8059d6ac751a7d367d04532b02dd \ + --hash=sha256:4b940b0d777590c7e20bf298f5c16af1ea6ad1b400a1c822a6be192f64f4d954 \ + --hash=sha256:4eae472cf7dc3107dec1bb53cd6d142d1964616d08aae48654cd4254b2363c4b \ + --hash=sha256:533f8370ce124304e5cb08ab961836cf755631e3dd77adc5f3bbdab70c2b7d99 \ + --hash=sha256:6c0c37b92f67ed68dd36221ce0403e1d9bd4f7efce724439978a2597848530e5 \ + --hash=sha256:85f8e8406c52658735fe5c7fbfd3ebaa1ed340768324f6252e4274e374580a23 \ + --hash=sha256:939e5d65f332e6d399774b2bd0d3559fd8fa629c1e77833db29d968d2384f23d \ + --hash=sha256:be0f8ed688cfb1d4d5765a137193b7bfab0c8ea214eed99260b380bb525a3a7f \ + --hash=sha256:c1ad3f437153fe77f9d01a08fbaac0beb030e09b8a80ace1603bcf69b6c95481 \ + --hash=sha256:d2fb19e848f7c33ed8d3182b52504aaa11c5e8da438bbb47296f85b133cbcf6b \ + --hash=sha256:d67673c5367727860922c5262d724472f1b5539fb7ccf4c81a638f9b71719803 \ + --hash=sha256:dc61a79cb39afd66ab3f01fd2c23591a7f01de89c1668e1fb6315067fc279164 \ + --hash=sha256:e2128f31f449e922c62dbe5d8b6b7b079f0bcaf2d56a102fa203cb6e5bb5ab19 \ + --hash=sha256:e417ef8628dcce310d2d53023e750ea298ec14d4341ae6dc3a572bfd9bc7fa97 \ + --hash=sha256:e74b278af1d949876f5d91d1268fd6c680e79f2bac194967394eaba9fdf69e7e # via # semantica (pyproject.toml) # fastembed @@ -3827,6 +4090,7 @@ opentelemetry-api==1.43.0 \ --hash=sha256:20acf45e9b21851926835292e4045d290acade1edd2ff3de86d2f069687ba1fd # via # semantica (pyproject.toml) + # google-adk # opentelemetry-instrumentation # opentelemetry-sdk # opentelemetry-semantic-conventions @@ -3837,7 +4101,9 @@ opentelemetry-instrumentation==0.64b0 \ opentelemetry-sdk==1.43.0 \ --hash=sha256:d1323a547c1ce69d6a069a17a44b7da82bb8b332051ecb074041f87642c86823 \ --hash=sha256:d8187c81c162df9913e4003dd6485f7390d9a24fc17026ec7387b8b8218b08e9 - # via semantica (pyproject.toml) + # via + # semantica (pyproject.toml) + # google-adk opentelemetry-semantic-conventions==0.64b0 \ --hash=sha256:72f76fb2d1582d9d033dd1fcd84532e961e6ff3d90d24ba6fabc72975a83864c \ --hash=sha256:ea77e85e354b8f604ddbe5f3d9135216f982fa4d77e5859ac30f6d8a50505aa6 @@ -3845,82 +4111,76 @@ opentelemetry-semantic-conventions==0.64b0 \ # semantica (pyproject.toml) # opentelemetry-instrumentation # opentelemetry-sdk -orjson==3.11.9 \ - --hash=sha256:011382e2a60fda9d46f1cdee31068cfc52ffe952b587d683ec0463002802a0f4 \ - --hash=sha256:03db380e3780fa0015ed776a90f20e8e20bb11dde13b216ce19e5718e3dfba62 \ - --hash=sha256:051b102c93b4f634e89f3866b07b9a9a98915ada541f4ec30f177067b2694979 \ - --hash=sha256:08f4d8ebb44925c794e535b2bebc507cebf32209df81de22ae285fb0d8d66de0 \ - --hash=sha256:0b34789fa0da61cf7bef0546b09c738fb195331e017e477096d129e9105ab03d \ - --hash=sha256:0e4eed3b200023042814d2fc8a5d2e880f13b52e1ed2485e83da4f3962f7dc1a \ - --hash=sha256:115ab5f5f4a0f203cc2a5f0fb09aee503a3f771aa08392949ab5ca230c4fbdbd \ - --hash=sha256:135869ef917b8704ea0a94e01620e0c05021c15c52036e4663baffe75e72f8ce \ - --hash=sha256:147302878da387104b66bb4a8b0227d1d487e976ce41a8501916161072ed87b1 \ - --hash=sha256:14ed654580c1ed2bc217352ec82f91b047aef82951aa71c7f64e0dcb03c0e180 \ - --hash=sha256:16969c9d369c98eb084889c6e4d2d39b77c7eb38ceccf8da2a9fff62ae908980 \ - --hash=sha256:19b72ed11572a2ee51a67a903afbe5af504f84ed6f529c0fe44b0ab3fb5cc697 \ - --hash=sha256:231742b4a11dad8d5380a435962c57e91b7c37b79be858f4ef1c0df1a259897e \ - --hash=sha256:25e4aed0312d292c09f61af25bba34e0b2c88546041472b09088c39a4d828af1 \ - --hash=sha256:26a473dbb4162108b27901492546f83c76fdcea3d0eadff00ae7a07e18dcce09 \ - --hash=sha256:277fefe9d76ee17eb14debf399e3533d4d63b5f677a4d3719eb763536af1f4bd \ - --hash=sha256:2d057a602cdd19a0ad680417527c45b6961a095081c0f46fe0e03e304aac6470 \ - --hash=sha256:32ef5f4283a3be81913947d19608eacb7c6608026851123790cd9cc8982af34b \ - --hash=sha256:33d7d766701847dc6729846362dc27895d2f2d2251264f9d10e7cb9878194877 \ - --hash=sha256:34fd2317602587321faab75ab76c623a0117e80841a6413654f04e47f339a8fb \ - --hash=sha256:3513550321f8c8c811a7c3297b8a630e82dc08e4c10216d07703c997776236cd \ - --hash=sha256:380cdce7ba24989af81d0a7013d0aaec5d0e2a21734c0e2681b1bc4f141957fe \ - --hash=sha256:3a81d52442a7c99b3662333235b3adf96a1715864658b35bb797212be7bddb97 \ - --hash=sha256:3ebca4179031ee716ed076ffadc29428e900512f6fccee8614c9983157fcf19c \ - --hash=sha256:48ee05097750de0ff69ed5b7bbcf0732182fd57a24043dcc2a1da780a5ead3a5 \ - --hash=sha256:4bab1b2d6141fe7b32ae71dac905666ece4f94936efbfb13d55bb7739a3a6021 \ - --hash=sha256:4d4e98d6f3b8afed8bc8cd9718ec0cdf46661826beefb53fe8eafb37f2bf0362 \ - --hash=sha256:4d7fde5501b944f83b3e665e1b31343ff6e154b15560a16b7130ea1e594a4206 \ - --hash=sha256:4da3c38a2083ca4aaf9c2a36776cce3e9328e6647b10d118948f3cfb4913ffe4 \ - --hash=sha256:4e39364e726a8fff737309aff059ff67d8a8c8d5b677be7bb49a8b3e84b7e218 \ - --hash=sha256:4fd66214623f1b17501df9f0543bef0b833979ab5b6ded1e1d123222866aa8c9 \ - --hash=sha256:4fef17e1f8722c11587a6ef18e35902450221da0028e65dbaaa543619e68e48f \ - --hash=sha256:53b50b0e14084b8f7e29c5ce84c5af0f1160169b30d8a6914231d97d2fe297d4 \ - --hash=sha256:57ea77fb70a448ce87d18fca050193202a3da5e54598f6501ca5476fb66cfe02 \ - --hash=sha256:59e403b1cc5a676da8eaf31f6254801b7341b3e29efa85f92b48d272637e77be \ - --hash=sha256:5b192c6cf397e4455b11523c5cf2b18ed084c1bbd61b6c0926344d2129481972 \ - --hash=sha256:5f63aaf97afd9f6dec5b1a68e1b8da12bfccb4cb9a9a65c3e0b6c847849e7586 \ - --hash=sha256:63e0efbc991250c0b3143488fa57d95affcabbfc63c99c48d625dd37779aafe2 \ - --hash=sha256:6cc7923789694fd58f001cbcac7e47abc13af4d560ebbfcf3b41a8b1a0748124 \ - --hash=sha256:71e63adb0e1f1ed5d9e168f50a91ceb93ae6420731d222dc7da5c69409aa47aa \ - --hash=sha256:71f3db16e69b667b132e0f305a833d5497da302d801508cbb051ed9a9819da47 \ - --hash=sha256:844417969855fc7a41be124aafe83dc424592a7f77cd4501900c67307122b92c \ - --hash=sha256:8697ab6a080a5c46edaad50e2bc5bd8c7ca5c66442d24104fa44ec74910a8244 \ - --hash=sha256:87e4d4ab280b0c87424d47695bec2182caf8cfc17879ea78dab76680194abc13 \ - --hash=sha256:8aff7da9952a5ad1cef8e68017724d96c7b9a66e99e91d6252e1b133d67a7b10 \ - --hash=sha256:8ecc30f10465fa1e0ce13fd01d9e22c316e5053a719a8d915d4545a09a5ff677 \ - --hash=sha256:97d0d932803c1b164fde11cb542a9efcb1e0f63b184537cca65887147906ff48 \ - --hash=sha256:97db4c94a7db398a5bd636273324f0b3fd58b350bbbac8bb380ceb825a9b40f4 \ - --hash=sha256:9af678d6488357948f1f84c6cd1c1d397c014e1ae2f98ae082a44eb48f602624 \ - --hash=sha256:9ef6fe90aadef185c7b128859f40beb24720b4ecea95379fc9000931179c3a49 \ - --hash=sha256:9f78cf8fec5bd627f4082b8dfeac7871b43d7f3274904492a43dab39f18a19a0 \ - --hash=sha256:a028425d1b440c5d92a6be1e1a020739dfe67ea87d96c6dbe828c1b30041728b \ - --hash=sha256:a6082706765a95a6680d812e1daf1c0cfe8adec7831b3ff3b625693f3b461b1c \ - --hash=sha256:a8f5f8bc7ce7d59f08d9f99fa510c06496164a24cb5f3d34537dbd9ca30132e2 \ - --hash=sha256:aaea64f3f467d22e70eeed68bdccb3bc4f83f650446c4a03c59f2cba28a108db \ - --hash=sha256:ace6c58523302d3b97b6ac5c38a5298a54b473762b6be82726b4265c41029f92 \ - --hash=sha256:b3afcf569c15577a9fe64627292daa3e6b3a70f4fb77a5df246a87ec21681b94 \ - --hash=sha256:b6ef1979adc4bc243523f1a2ba91418030a8e29b0a99cbe7e0e2d6807d4dce6e \ - --hash=sha256:be4fa4f0af7fa18951f7ab3fc2148e223af211bf03f59e1c6034ec3f97f21d61 \ - --hash=sha256:c2d3dc759490128c5c1711a53eeaa8ee1d437fd0038ffd2b6008abf46db3f882 \ - --hash=sha256:c5d001196b89fa9cf0a4ab79766cd835b991a166e4b621ba95089edc50c429ff \ - --hash=sha256:cce9127885941bd28f080cecf1f1d288336b7e0d812c345b08be88b572796254 \ - --hash=sha256:cde1a448023ba7d5bb4c01c5afb48894380b5e4956e0627266526587ef4e535f \ - --hash=sha256:d4087e5c0209a0a8efe4de3303c234b9c44d1174161dcd851e8eea07c7560b32 \ - --hash=sha256:d8ea516b3726d190e1b4297e6f4e7a8650347ae053868a18163b4dd3641d1fff \ - --hash=sha256:e30ab17845bb9fa54ccf67fa4f9f5282652d54faa6d17452f47d0f369d038673 \ - --hash=sha256:e5c9b8f28e726e97d97696c826bc7bea5d71cecd63576dba92924a32c1961291 \ - --hash=sha256:ea407d4ccf5891d667d045fecae97a7a1e5e87b3b97f97ae1803c2e741130be0 \ - --hash=sha256:ea5c46eb2d3af39e806b986f4b09d5c2706a1f5afde3cbf7544ce6616127173c \ - --hash=sha256:eebdbdeef0094e4f5aefa20dcd4eb2368ab5e7a3b4edea27f1e7b2892e009cf9 \ - --hash=sha256:f01c4818b3fc9b0da8e096722a84318071eaa118df35f6ed2344da0e73a5444f \ - --hash=sha256:f36b7f32c7c0db4a719f1fc5824db4a9c6f8bd1a354debb91faf26ebf3a4c71e \ - --hash=sha256:f5d89a2ed90731df3be64bab0aa44f78bff39fdc9d71c291f4a8023aa46425b7 \ - --hash=sha256:ffe02797b5e9f3a9d8292ddcd289b474ad13e81ad83cd1891a240811f1d2cb81 - # via pymilvus +orjson==3.12.0 \ + --hash=sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a \ + --hash=sha256:01efac2074fffb4cb1ea3fab7861e9d0f2a26913854a972f5ac760525dbdaf6e \ + --hash=sha256:03091c8a64db4be38746597ceea68f33c238e27acd9bfe99fb59420224ae7a55 \ + --hash=sha256:08231552159be266a7269555bd9f7c016aee7d9ad6dab06eb58796c5ccb7101c \ + --hash=sha256:0b1ac5bf6609b2716c7954011c5fef6254922df029f45d032ee4ebf5d363cbed \ + --hash=sha256:103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11 \ + --hash=sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b \ + --hash=sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54 \ + --hash=sha256:18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387 \ + --hash=sha256:1c680706fc8396d95e7c4c1f9482563f552137aef91b57237a3ad5aaf64629df \ + --hash=sha256:2b7bcefb9f40fa242fa6b06377232c048e655747790829609168c01162f60578 \ + --hash=sha256:2bb3ce43203936072dd8b4917b01d3aecfc02329bfb42510cb7cfb24708adc9c \ + --hash=sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83 \ + --hash=sha256:2eb5c56e534127b2b8fa38d2363c8b1b8190367ee0d1d16c041517d880843b94 \ + --hash=sha256:31ed278a36304390adc3eec5d7f6fd593a7c3e99e5a06cd07866396c4b1b4710 \ + --hash=sha256:33efefcf5d88eaf400b47e2eba02f91f319bb9951be61ca500b7d536d3f2079d \ + --hash=sha256:3bb17a06f9bd15237b3216c044209fe92597379124018cfc196fbb846cde64df \ + --hash=sha256:3dbce9b6b3074b31a5d5dd322a9c4e5b16f206091ece4194c2e36952847a105e \ + --hash=sha256:40f92192227505acca4e2533ce565f8e6b9535f7d0d09b0968452f18b7376b38 \ + --hash=sha256:477ecaf6b9f88f873341b91fcc736119ca81b5e002a9f7f308ff5b4f2ce2a70e \ + --hash=sha256:50fae885cb073eac7556353ff3df93312b0d5137b0a5056b2bb63f97ed9a93c7 \ + --hash=sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873 \ + --hash=sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f \ + --hash=sha256:58c58e1de0006ffb580368d6793c36c7b0b021db066479cf281bf5061e732328 \ + --hash=sha256:5a0fdbc216388f653d3752ff310e710f59253bd4ed6a2bfb3f4f06b84714bbd8 \ + --hash=sha256:61318b6de893c7a9d9f3e5ecbadccbfc26a7eb417ccc7bbf0771de3b4d72f868 \ + --hash=sha256:644d005bc82f917337a95ce270c9f6f92f9834c2bed7b1477572f8db00784222 \ + --hash=sha256:6a2a79c89984dc719817d388c8709e0efc2a2795a934eaa746b4882eb6045adc \ + --hash=sha256:6a31348d7dfa64cd9c78bd1f510ff44c48fe64d71094e6b90e364dba3b55949e \ + --hash=sha256:747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796 \ + --hash=sha256:784106539f4b9d4b930e0b4eb8d45168507dae001945e71b4675a367f1e5e806 \ + --hash=sha256:7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98 \ + --hash=sha256:83445adc40cba26d6d621185a45128ce455b766af368cad2ab64b970603a7978 \ + --hash=sha256:859fc4196855890150bb08e649b30d2c93b249b3e3edd0d3bb2231abf8aa8adc \ + --hash=sha256:8c3bb86dd10f39b3fbf434b7d5dc7cac77d6fc8ac572ae30a10731ede2c4b647 \ + --hash=sha256:8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3 \ + --hash=sha256:8e386b0bc0ddd7cd2056f884b5a0af33592bd01ac66a7ca4b42a65a7e7774a13 \ + --hash=sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7 \ + --hash=sha256:9a36ec60f1796f9a3f13e3b98390295e17a1c7c10155b448d264098bf9ee5900 \ + --hash=sha256:9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5 \ + --hash=sha256:9e6fee342a48760e854d743e7a81534d8e2925a6f46e09f750cf56b50fd1de5d \ + --hash=sha256:a15f9a891bce5f5cc5d210e3ad8614d4d1b489a56448c099d6d2a7168b2d954a \ + --hash=sha256:a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10 \ + --hash=sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5 \ + --hash=sha256:a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e \ + --hash=sha256:a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92 \ + --hash=sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0 \ + --hash=sha256:ad0422b92d5195443a39f80c3bcf731cc2e00f153bd32063a47b73b057bd0f03 \ + --hash=sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d \ + --hash=sha256:b85931be5b6763c31283805c9bdaae1ca03ad9f6f12a15f1cbf6745b907932c2 \ + --hash=sha256:b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998 \ + --hash=sha256:bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344 \ + --hash=sha256:bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241 \ + --hash=sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e \ + --hash=sha256:c6b11be792c3d2c6a4be2af4ebf97a68d0bf5f580aca6e86a418a354f6cc846a \ + --hash=sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5 \ + --hash=sha256:d39f3f5c3927e2dc0913fe5bbc1a2f6b1b9d1bba1de6358340d0ad0d0c00ca92 \ + --hash=sha256:d8e78d3d93705e3d27cc17cdb209e44d7a8ea203010cac6ce9c7ffc1ae1996f1 \ + --hash=sha256:dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e \ + --hash=sha256:e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8 \ + --hash=sha256:e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef \ + --hash=sha256:ed4ca42bd55955aa34deedcfdfd0e0c31abf51143aae158ae2bc3520b626e517 \ + --hash=sha256:f06dd838d1e07d9b1de0932ec0485ec92c4d5f5d1ad4817a656268c3e88be1e1 \ + --hash=sha256:f3c0683136acdc29afdf88a5bc2f7d3d0e34087788d1d63c0144b805a87a196f \ + --hash=sha256:fb2539159dfe8d371914f354360fa50e4a577cc89222a3828b9650a5e5040252 + # via + # langsmith + # pinecone + # pymilvus overrides==7.7.0 \ --hash=sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a \ --hash=sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49 @@ -3940,6 +4200,7 @@ packaging==26.3 \ # deprecation # distfit # faiss-cpu + # google-adk # huggingface-hub # ipykernel # jupyter-events @@ -3947,11 +4208,14 @@ packaging==26.3 \ # jupyterlab # jupyterlab-server # kombu + # langchain-core + # langsmith # lazy-loader # matplotlib # nbconvert # onnxruntime # opentelemetry-instrumentation + # patsy # plotly # pooch # pyshacl @@ -4012,6 +4276,7 @@ pandas==3.0.5 \ # datazets # distfit # docling-core + # formulaic # pymilvus # seaborn # statsmodels @@ -4029,9 +4294,9 @@ pathspec==1.1.1 \ # via # black # mypy -patsy==1.0.2 \ - --hash=sha256:37bfddbc58fcf0362febb5f54f10743f8b21dd2aa73dec7e7ef59d1b02ae668a \ - --hash=sha256:cdc995455f6233e90e22de72c37fcadb344e7586fb83f06696f54d92f8ce74c0 +patsy==1.0.3 \ + --hash=sha256:79ebf4c93ff4d296e58a9d5be2b2ee31bd49d737cf11d70ffbd8a44b2de42e65 \ + --hash=sha256:d3dbebe8fd5f46e29912d030b63c6268647b59bf788a99e2af28a30234cf357c # via statsmodels pexpect==4.9.0 \ --hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \ @@ -4144,25 +4409,27 @@ pillow==12.3.0 \ # python-pptx # rapidocr # torchvision -pinecone-client==6.0.0 \ - --hash=sha256:d81a9e73cae441e4ab6dfc9c1d8b51c9895dae2488cda64f3e21b9dfc10c8d94 \ - --hash=sha256:f224fc999205e4858c4737c40922bdf42d178b361c8859bc486ec00d45b359a9 +pinecone==9.1.0 \ + --hash=sha256:461632bb07919da32b943100b8a047c74be53a6aa15c8b7679bff7a0f834c939 \ + --hash=sha256:6c3a6dfa577dc11aed3197e1b221e65522603e9e1f6bd27a1b504a0909b3559f \ + --hash=sha256:d3871bd3f39cb430ae8470158dc9c5dcffbac5ae31d144d9a7c3b351ac51755f \ + --hash=sha256:d53fe6f4978ab0642eb2d3a0ee3b2576ccfeebaa11e0690b18e67dac4e057047 \ + --hash=sha256:e930ba819f5b7e20aac688d04c840a8b6fbc6d12630d71303bb2130881a9d169 \ + --hash=sha256:fc71ec431108de2df1a1978d3a24ac16f74ba3d8f3265c3760f969386e8742b8 \ + --hash=sha256:fe6aeaf6515e9021984755ebc162f643c79d98056059aab2e765962a7538818c \ + --hash=sha256:ffae8fb7cbb4056b920586629f15b08107350be4802a5637d10b31e2ad841f9c # via semantica (pyproject.toml) -pinecone-plugin-interface==0.0.7 \ - --hash=sha256:875857ad9c9fc8bbc074dbe780d187a2afd21f5bfe0f3b08601924a61ef1bba8 \ - --hash=sha256:b8e6675e41847333aa13923cc44daa3f85676d7157324682dc1640588a982846 - # via pinecone-client -platformdirs==4.11.2 \ - --hash=sha256:3a2ae5fca3520a01ab1be8b45613537f52ddf5b5f6f53d88233892dfbf0cd82d \ - --hash=sha256:7f89089b6ea71bda7962953edcf784b2e2d9d285b40ad88be2bb75c6e9d82ab4 +platformdirs==4.11.7 \ + --hash=sha256:4f41487eeeeeb07f3a6625e61d9bc0ae6809f92d3386dbd74392fbb76108104d \ + --hash=sha256:8a02cb259042c79d1cd0450facc2fe6dc9d303ae7901afbe33bf8ea0b188cef6 # via # black # jupyter-core # pooch # virtualenv -plotly==6.9.0 \ - --hash=sha256:36bebe2f1bb13884774fe61689c329071446f6ce4a8927fb1f0d6fb24f581236 \ - --hash=sha256:967ad33e8c704fed051800d11d985eb206a9c795c14206b30a6f463ed9c67d0d +plotly==7.0.0 \ + --hash=sha256:08b21f1244a97e7a1a699833c4bb2678475aa108b3f1989886ed0b038ebfd849 \ + --hash=sha256:78cbf7bd06d1b05bb3b8ec1b709864695229b55151b6f7530fbf55517ead6fdd # via # semantica (pyproject.toml) # bertopic @@ -4181,9 +4448,9 @@ pooch==1.9.0 \ --hash=sha256:de46729579b9857ffd3e741987a2f6d5e0e03219892c167c6578c0091fb511ed \ --hash=sha256:f265597baa9f760d25ceb29d0beb8186c243d6607b0f60b83ecf14078dbc703b # via librosa -portalocker==2.7.0 \ - --hash=sha256:032e81d534a88ec1736d03f780ba073f047a06c478b06e2937486f334e955c51 \ - --hash=sha256:a07c5b4f3985c3cf4798369631fb7011adb498e2a46d8440efc75a8f29a0f983 +portalocker==3.2.0 \ + --hash=sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac \ + --hash=sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968 # via qdrant-client pre-commit==4.6.2 \ --hash=sha256:8f5d7bfb021ecdbcd9d49d89847082dd24172ccde534390081a679ad046e2441 \ @@ -4267,6 +4534,7 @@ prompt-toolkit==3.0.53 \ # click-repl # ipython # jupyter-console + # questionary propcache==0.5.2 \ --hash=sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427 \ --hash=sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5 \ @@ -4392,19 +4660,21 @@ propcache==0.5.2 \ # via # aiohttp # yarl -proto-plus==1.28.3 \ - --hash=sha256:5f91b30dafa6bb38d432c5557a6ee1d35ffd40b4b1e0e3ca27260448560b91d9 \ - --hash=sha256:dc76880b8ee951cca002098574376cf71e055f9f16d9ba6570fb8a06f726d281 +proto-plus==1.28.4 \ + --hash=sha256:4b01341272f8a348db3f003b6143109f83ab43091019d5181b3fcdf500ab32aa \ + --hash=sha256:5ff7ecad828e032a491fcb86947801768e32237f99dd049b649965b892ae9a63 # via google-api-core -protobuf==7.35.1 \ - --hash=sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799 \ - --hash=sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87 \ - --hash=sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6 \ - --hash=sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30 \ - --hash=sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9 \ - --hash=sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4 \ - --hash=sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4 \ - --hash=sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a +protobuf==6.33.6 \ + --hash=sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326 \ + --hash=sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901 \ + --hash=sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3 \ + --hash=sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a \ + --hash=sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135 \ + --hash=sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e \ + --hash=sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3 \ + --hash=sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2 \ + --hash=sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593 \ + --hash=sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf # via # semantica (pyproject.toml) # google-api-core @@ -4440,66 +4710,66 @@ psutil==7.2.2 \ # accelerate # ipykernel # ipython -psycopg==3.3.4 \ - --hash=sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a \ - --hash=sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc +psycopg==3.3.5 \ + --hash=sha256:ce5aa5cdb4f9379f00f487590e5890bfa7df9a164648c969ffa628505e21af4e \ + --hash=sha256:d0a3d9ccf5788af054cbd745278cb02401b5c312aeaafbf2c6144460aec47da4 # via semantica (pyproject.toml) -psycopg-binary==3.3.4 \ - --hash=sha256:018fbed325936da502feb546642c982dcc4b9ffdea32dfef78dbf3b7f7ad4070 \ - --hash=sha256:0579252a1202cd73e4da137a1426e2dae993ae44e757605344282af3a082848c \ - --hash=sha256:136f199a407b5348b9b857c504aff60c77622a28482e7195839ce1b51238c4cc \ - --hash=sha256:13a7f380824c35896dcac7fe0f61440f7ca49d6dc73f3c13a9a4471e6a3b302e \ - --hash=sha256:17a21953a9e5ff3a16dab692625a3676e2f101db5e40072f39dbee2250194d68 \ - --hash=sha256:1dc1f79fd16bb1f3f4421417a514607539f17804d95c7ed617265369d1981cae \ - --hash=sha256:1fbaa292a3c8bb61b45df1ad3da1908ccee7cb889db9425e3557d9e34e2a4829 \ - --hash=sha256:22cdbf5f91ef7bb91fe0c5757e1962d3127a8010256eefd9c61fcaf441802097 \ - --hash=sha256:26df2717e59c0473e4465a97dfb1b7afebaa479277870fd5784d1436470db47c \ - --hash=sha256:276904e3452d6a23d474ef9a21eee19f20eed3d53ddd2576af033827e0ba0992 \ - --hash=sha256:28b7398fdd19db3232c884fb24550bdfe951221f510e195e233299e4c9b78f97 \ - --hash=sha256:2c09aad7051326e7603c14e50636db9c01f78272dc54b3accff03d46370461e6 \ - --hash=sha256:32a6fbf8481e3a370d0d72b860d35948a693cb01281da217f7b2f307636e591a \ - --hash=sha256:41f2ec0fea529832982bcb6c9415de3c86264ebe562b77a467c0fbcd7efbba8d \ - --hash=sha256:46893c26858be12cc49ca4226ed6a60b4bfccadd946b3bebb783a60b38788228 \ - --hash=sha256:47c656a8a7ba6eb0cff1801a4caaa9c8bdc12d03080e273aff1c8ac39971a77e \ - --hash=sha256:494ca54901be8cf9eb7e02c25b731f2317c378efa44f43e8f9bd0e1184ae7be4 \ - --hash=sha256:514404ed543efd620c85602b747df2a23cf1241b4067199e1a66f2d2757aaa41 \ - --hash=sha256:574ea21a9651958f1535c5a1c649c7409e9168bcbffa29a3f2f961f58b322949 \ - --hash=sha256:580ae30a5f95ccd90008ec697d3ed6a4a2047a516407ad904283fa42086936e9 \ - --hash=sha256:5ab28a2a7649df3b72e6b674b4c190e448e8e77cf496a65bd846472048de2089 \ - --hash=sha256:5c4ab71be17bdca30cb34c34c4e1496e2f5d6f20c199c12bad226070b22ef9bf \ - --hash=sha256:612a627d733f695b1de1f9b4bd511c15f999a5d8b915d444bbd7dd71cf3370da \ - --hash=sha256:6402a9d8146cf4b3974ded3fd28a971e83dc6a0333eb7822524a3aa20b546578 \ - --hash=sha256:6b9016b1714da4dd5ecaaa75b82098aa5a0b87854ce9b092e21c27c4ae23e014 \ - --hash=sha256:71e55ccbdfae79a2ed9c6369c3008a3025817ff9d7e27b32a2d84e2a4267e66e \ - --hash=sha256:7465bfe6087d2d5b42d4c53b9b11ca9f218e477317a4a162a10e3c19e984ba8e \ - --hash=sha256:75a9067e236f9b9ae3535b66fe99bddb33d39c0de10112e49b9ab11eee53dc31 \ - --hash=sha256:773d573e11f437ce0bdb95b7c18dc58390494f96d43f8b45b9760436114f7652 \ - --hash=sha256:77df19583501ea288eaf15ac0fe7ad01e6d8091a91d5c41df5c718f307d8e31b \ - --hash=sha256:7f7668f30b9dd5163197e5cbf4e0efd54e00f0a859cc566ce56cfc31f4054839 \ - --hash=sha256:8c0056529e68dbe9184cd4019a1f3d8f3a4ead2f6fc7a5afcf27d3314edd1277 \ - --hash=sha256:94596f9e7633ee3f6440711d43bb70aa31cc0a46a900ab8b4201a366ace5c9e7 \ - --hash=sha256:ab8cca8ef8fb1ccf5b048ae5bd78ba55b9e4b5d472e3ce5ca39ff4d2a9c249e4 \ - --hash=sha256:ad3bc94054876155549fdaedf4a46d1ec69d39a5bcee377148afe498e84c4b8e \ - --hash=sha256:b56b603ebcea8aa10b46228b8410ba7f13e7c2ee54389d4d9be0927fd8ce2a70 \ - --hash=sha256:b6f5a29e9c775b9f12a1a717aa7a2c80f9e1db6f27ba44a5b59c80ac61d2ffcf \ - --hash=sha256:b7bfff1ca23732b488cbca3076fc11bc98d520ee122514fdb17a8e20d3338f5a \ - --hash=sha256:bdef84570ebbce1d42b4e7ea952d21c414c5f118ad02fee00c5625f35e134429 \ - --hash=sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8 \ - --hash=sha256:c677c4ad433cb7150c8cd304a0769ae3bcfbe5ea0676eb53faa7b1443b16d0d3 \ - --hash=sha256:cf7f73a4a792bc5db58a4b385d8a1467e8d468f7548702fb0ed1e9b7501b1c13 \ - --hash=sha256:cffc3408d77a27973f33e5d909b624cce683db5fc25964b02fe0aae7886c1007 \ - --hash=sha256:d7b4d40c153fa352ab3cca530f3a0baedf7621b2ebcbd7f084009522c21788fc \ - --hash=sha256:dbfdb9b6cc79f31104a7b162a2b921b765fcc62af6c00540a167a8de47e4ed38 \ - --hash=sha256:df1d567fc430f6df15c9fcf67d87685fc49bdb325adc0db5af1adfb2f44eb5c9 \ - --hash=sha256:e2631da29253a98bd496e6c4813b24e09a4fe3fb2a9e88513305d6f8747cce95 \ - --hash=sha256:e7510c37550f91a187e3660a8cc50d4b760f8c3b8b2f89ebc5698cd2c7f2c85d \ - --hash=sha256:eb05ee1c2b817d27c537333224c9e83c7afb86fe7296ba970990068baf819b16 \ - --hash=sha256:eb4eed2079c01a4850bf467deacfab56d356d4225040170af03dc9958321242d \ - --hash=sha256:ee17a2cf4943cde261adfad1bbc5bf38d6b3776d7afff74c7cabcbeaeb08c260 \ - --hash=sha256:f80e3f2b5331dbbf0901bcb658056c03eeb2c1ef31d774afb0d61598b242e744 \ - --hash=sha256:f9b1c2533af01cd7648378599f82b0b8ae32f293296e6eec5753a625bc97ef28 \ - --hash=sha256:fa1cbc10768a796c96d3243656016bf4e337c81c71097270bb7b0ad6210d9765 \ - --hash=sha256:fbd1d4ed566895ad2d3bf4ddfd8bae90026930ddf29df3b9d91d32c8c47866a7 +psycopg-binary==3.3.5 \ + --hash=sha256:0249c3e960cdee686000eb77169fb6590105c05bacc37e057ccdffdcd8e6ebde \ + --hash=sha256:04f64b39830887c2c737b522cbfd6ad215d65e67ebfff674aa4cf21c02af487b \ + --hash=sha256:06de14ac978a2d53e864069fb5487075c6e3cfb0740f1bfd7017bc8b9942067f \ + --hash=sha256:0d8a4b7ae47f3381e2ded89891d2455b809f4afb7e5b58086844abb8cfa420ea \ + --hash=sha256:1344fd57a19737554670e67aecabd4fb37cd7937f2925840009645d641117e5a \ + --hash=sha256:14f432430fd9e1a9e7d9ab2fe14956c77f5d074ebdc556a1ad04e9a1bd3fca04 \ + --hash=sha256:14fdfd65a96ecbd8b586d14546105641f4a6ac7cbe335c786830ea4de94bbe60 \ + --hash=sha256:19e5bf9872dbd164c220567fd385ba2309c7d9df1541f78343510c6b0f36a1b7 \ + --hash=sha256:1ef2e498be47800f6202b9a2304c22646325ca6d54001b7c785bcfdb24a1e8ab \ + --hash=sha256:2111f880add40fb03c60556069ad68e884a0908a74d2debafc603caf93b73552 \ + --hash=sha256:25105f9b46bdf2a30fcb67f56976ed66f6855941ae16bc024192609b917d493c \ + --hash=sha256:2719fe19a4da752c4110cc767716d0a5bdb760d1153d89018bb7c9c61717bde5 \ + --hash=sha256:358748fc4c8ccdc0e2bdf55420494930e19c3ade586ea9c3a6de3dad1f897311 \ + --hash=sha256:35885e333020fc152d27bea1a494bef13b2e68f6fd92b6229015e93539152008 \ + --hash=sha256:39e70c8e3b5fad70e2970ea4cc502bf3b612018f128aa1c670f1fa78b9774543 \ + --hash=sha256:40505676b1526b9ea387dace034040a8c8b0bcf984cd6bd4720a2ab15e813586 \ + --hash=sha256:40f8b132c7243ef5f503f0b6f986bf16d38a51b0df1c6ba2577743f128be03e3 \ + --hash=sha256:479b96fd78149cfa10369dc53fbfb89ee729be13146b584a23dbc7e164c0cf1e \ + --hash=sha256:4901e5b9a31c230211a1871263b6594373bacd770b14ad9b14d349716cb69cbe \ + --hash=sha256:553b5443cbc94fdb9b0e31b62acdf615e0780982d6b13752a15eb3d6c0dfd0d2 \ + --hash=sha256:5698ab5941a4d138c30fef858588e651fe7d583280cd6e41832825ad9e747750 \ + --hash=sha256:5816472e3bb05615f33a741e0835043d1f4bf9709ff30d2f4aed71815cfc6b5e \ + --hash=sha256:5b981d25fc2dd13fa7328e40703ea8a03f3e9d855ec946431e96a23206d3b9fd \ + --hash=sha256:682a17a57415c3ca1731eec018ed031f012ffcb81ba74806eb219cb396065672 \ + --hash=sha256:6e85d50b87257fb117675a19ee59daa7bf9a57f6431500adf7059df799232ef4 \ + --hash=sha256:7b443f943abfe35aa5a776630cea27c9348aa66659286cee0b99084332252080 \ + --hash=sha256:88e01aa2e938a45655a8a5213fc3a44ba78cb4cab8a569b3e0bcb3d1d0eaba16 \ + --hash=sha256:893ce86a4b997f6ca1261a7826db2506727332a6ff66646fa7f024b39b5e630e \ + --hash=sha256:8dbd694f3741dd4ac5bc60b70e17f7841aefb3f0f38cef4d2756de270e03af43 \ + --hash=sha256:972cc28e943746e71ede254a4dfd1fdfcdd6dcadd6f375703849859f09377f24 \ + --hash=sha256:98a388509306e5e08a4203253ac52846bc1b034e5cbd0ae6da1211593cc28594 \ + --hash=sha256:9c071bf78e5c2e6efa40bc9089a954d7b41221347a72f35c6bf2d8c96e632f75 \ + --hash=sha256:a5e45e4bb68656253ce5c7a344c0a425c293581eba954d7fd7c2e4b2dc9f3038 \ + --hash=sha256:ab39e2794b95af61a2ff69e33e5ab6ac5df36e9ffea9a3b18e38b2aaca8c5ad5 \ + --hash=sha256:ae67072db949d0c094b747a8ec52ad0fa3c42b27842a5f746f3613d54dde3fba \ + --hash=sha256:af5084124fb2fd16557073822519dfe8c389636a16adef661a4c0c3918733171 \ + --hash=sha256:ba466011569297114449df9d523438e1adeedf3e4f31ffb78e897ec3fef3076b \ + --hash=sha256:c065531e8c1815276f50dbfa283e3a7f022671414cdda6fa9a16794dd53b28f9 \ + --hash=sha256:c09775c549b40b274206e1b043c5e5b5af39666e85c98382a30bd05d23ab677b \ + --hash=sha256:c0cac998b9b1e82dec853d2e53b3d34d56a525cf231f9441a636cfd5992929a9 \ + --hash=sha256:c6bd84e4cf67930f26f015dec33f615472b9c5871d46408efe112dbc1bc021de \ + --hash=sha256:ca8af7c0454cdce235d4aedcb5528857468f1490202d25e24fc7af40e176d563 \ + --hash=sha256:cb3b3bffebfe07110730626e76238161124f35ac87b748d663316a28d22f58b0 \ + --hash=sha256:cd0faa2475ab254ad1b507430131cf7f7f0be927ffdc03c32ad3b33d2ef63f42 \ + --hash=sha256:cf0e5e63ee86098299c673992053d556c489ba9ae6aca6cb6e24d16a8e0b09e6 \ + --hash=sha256:d06da67e9c687c6a6fdac9da4b17cbeb296ddd59bd01f6416ed4294bc57c5faf \ + --hash=sha256:d2a61e8147902771df7efe14062a3c8736347850d0d8befcf048235752504f2e \ + --hash=sha256:d8b66353b20e79bf7ac0a80f03ae97f522ccbbf909d687eec62f112e56c0276d \ + --hash=sha256:df209e64674a34b41662c67fdc8b4e0ffd77d2136393790691d086a09f9a6cab \ + --hash=sha256:df9853b832b7b916e02ef68e0d5403a7dab2d5c1ddfe94f22b1b155eb862622f \ + --hash=sha256:e5becd311f9af8d180bad372f51fb2252fd02cb2073056e2b170c9274f95fe7f \ + --hash=sha256:f45d77e398542ce0937d9fa3cd9d84e9c5fc6b34c50a66404ae840bada312750 \ + --hash=sha256:f7e1e45aad410e20de45df2b159df68ff6c8dbf47a3501f806c4489b27f4ad2b \ + --hash=sha256:fd5b047c9fd887b767d063845413e405f5de8ce1dc7a9d0da0637b77b836b469 \ + --hash=sha256:fdbeb38c9b7ca8fa57a7bda3802bedb62f4494ad3dd46c7dd36dc3f77fd5093f # via psycopg psycopg-pool==3.3.1 \ --hash=sha256:2af5b432941c4c9ad5c87b3fa410aec910ec8f7c122855897983a06c45f2e4b5 \ @@ -4772,21 +5042,23 @@ pycparser==3.0 \ --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 # via cffi -pydantic==2.13.4 \ - --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba \ - --hash=sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6 +pydantic==2.13.5 \ + --hash=sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73 \ + --hash=sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08 # via # semantica (pyproject.toml) # agno # anthropic # docling-core - # docling-ibm-models # docling-parse # docling-slim # fastapi + # google-adk # google-genai # groq # instructor + # langchain-core + # langsmith # litellm # ollama # openai @@ -4796,127 +5068,127 @@ pydantic==2.13.4 \ # thinc # weasel # weaviate-client -pydantic-core==2.46.4 \ - --hash=sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0 \ - --hash=sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262 \ - --hash=sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda \ - --hash=sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0 \ - --hash=sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e \ - --hash=sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b \ - --hash=sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594 \ - --hash=sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29 \ - --hash=sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2 \ - --hash=sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c \ - --hash=sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d \ - --hash=sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398 \ - --hash=sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d \ - --hash=sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3 \ - --hash=sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f \ - --hash=sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb \ - --hash=sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7 \ - --hash=sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5 \ - --hash=sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9 \ - --hash=sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462 \ - --hash=sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4 \ - --hash=sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b \ - --hash=sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d \ - --hash=sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df \ - --hash=sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2 \ - --hash=sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0 \ - --hash=sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519 \ - --hash=sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd \ - --hash=sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7 \ - --hash=sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac \ - --hash=sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6 \ - --hash=sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565 \ - --hash=sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898 \ - --hash=sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb \ - --hash=sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928 \ - --hash=sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6 \ - --hash=sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3 \ - --hash=sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a \ - --hash=sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596 \ - --hash=sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987 \ - --hash=sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e \ - --hash=sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d \ - --hash=sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712 \ - --hash=sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008 \ - --hash=sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd \ - --hash=sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1 \ - --hash=sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be \ - --hash=sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea \ - --hash=sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292 \ - --hash=sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33 \ - --hash=sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3 \ - --hash=sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4 \ - --hash=sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b \ - --hash=sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826 \ - --hash=sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac \ - --hash=sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7 \ - --hash=sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d \ - --hash=sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf \ - --hash=sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4 \ - --hash=sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc \ - --hash=sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15 \ - --hash=sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3 \ - --hash=sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b \ - --hash=sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914 \ - --hash=sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04 \ - --hash=sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c \ - --hash=sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b \ - --hash=sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9 \ - --hash=sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce \ - --hash=sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4 \ - --hash=sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a \ - --hash=sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f \ - --hash=sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424 \ - --hash=sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894 \ - --hash=sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9 \ - --hash=sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76 \ - --hash=sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201 \ - --hash=sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb \ - --hash=sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109 \ - --hash=sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4 \ - --hash=sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848 \ - --hash=sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526 \ - --hash=sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0 \ - --hash=sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01 \ - --hash=sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458 \ - --hash=sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e \ - --hash=sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba \ - --hash=sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a \ - --hash=sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39 \ - --hash=sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c \ - --hash=sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000 \ - --hash=sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b \ - --hash=sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf \ - --hash=sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4 \ - --hash=sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd \ - --hash=sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28 \ - --hash=sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9 \ - --hash=sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30 \ - --hash=sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983 \ - --hash=sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1 \ - --hash=sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76 \ - --hash=sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5 \ - --hash=sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4 \ - --hash=sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7 \ - --hash=sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c \ - --hash=sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066 \ - --hash=sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3 \ - --hash=sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02 \ - --hash=sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89 \ - --hash=sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50 \ - --hash=sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76 \ - --hash=sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49 \ - --hash=sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b \ - --hash=sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d \ - --hash=sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7 \ - --hash=sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4 \ - --hash=sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c \ - --hash=sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e \ - --hash=sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff \ - --hash=sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae +pydantic-core==2.46.5 \ + --hash=sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942 \ + --hash=sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821 \ + --hash=sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5 \ + --hash=sha256:0bddb4020d8f04175865ccd17eff3040874fc11fb593f424edb452653b4b947c \ + --hash=sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184 \ + --hash=sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2 \ + --hash=sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc \ + --hash=sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5 \ + --hash=sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0 \ + --hash=sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931 \ + --hash=sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e \ + --hash=sha256:1e449def1945a462c464331254e5a44fca7c3b4f9aedf59ec2f50f8066dd8e25 \ + --hash=sha256:1e5aad1220a1192c42341c8fd4a8686657e73ab2a920c970bdc4de334fe3193d \ + --hash=sha256:200aa3dc9f8d54f0754f43247c0bad0999fdcfbfd2488384dd44f37279271fe6 \ + --hash=sha256:2471fd51c61c610e1dcf7de44d7299283661654d11264ab4802b303368d69c47 \ + --hash=sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038 \ + --hash=sha256:28a6a556cd3b6066bea827857f9d9cce027c96f776e512f544a581f9e42161f8 \ + --hash=sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b \ + --hash=sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a \ + --hash=sha256:2d330aaba8621b1edcec8ae2c4050f63b84ccf6d98723a8f212e9684713abf0e \ + --hash=sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074 \ + --hash=sha256:337639ba62a11acde6ef3aeb08c8ea755f8ef1fe5e513356c0f36a2b0d7568b0 \ + --hash=sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433 \ + --hash=sha256:356c8368cbc321050b169595683a2e1d63413b1e0e2868b330af9fc14c616d3f \ + --hash=sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034 \ + --hash=sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21 \ + --hash=sha256:3a3e26b6a8274211bddee2d0e4d0d42778f17a34510f49d2ec44b58abfc41736 \ + --hash=sha256:3aa166e99c4f2985407fb8714aebede877ecb5455cf321b606adca926d30d5a0 \ + --hash=sha256:3d2652072b2d774947ba5cf78a9e59644ac62ee572daf6dd2e1dfe905e15b2b7 \ + --hash=sha256:40375c2d05acec10323e45dfe2077ac44bc74659008614af5069034e2cfc781c \ + --hash=sha256:413a717a410d0c817ef5b786a059415550b3794e1d0c2abffd9efb93a3d9f7b4 \ + --hash=sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c \ + --hash=sha256:49776eab08766a08dfff7012f8b422dcd7e25e43b316eedf0477c24fcfa84b7c \ + --hash=sha256:4d44cf99ddebf875f9b68cc267aa684c99b7b44fe63ee1cac4ec163807290069 \ + --hash=sha256:4dedce55295becb61921e386b99d4f2706045306e7fa52249a33004c837379fb \ + --hash=sha256:4f8507560a9284e1370bb048ed4282012fbef4e8d109875b95e884d228552061 \ + --hash=sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29 \ + --hash=sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3 \ + --hash=sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa \ + --hash=sha256:53feb344243bb9510a9dec7bf3cf1b64d88a98af5dc7872a5160465f8b198c8e \ + --hash=sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38 \ + --hash=sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f \ + --hash=sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b \ + --hash=sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8 \ + --hash=sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e \ + --hash=sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c \ + --hash=sha256:5f93c5fe914d75fbec9a49209b00da5f08e9e467d69da2b1510c81940cfd10be \ + --hash=sha256:657b40d6240c0a7b6a64b30f22d1e3aa631c7e846c621b0c0f6d1d75e2e15ea6 \ + --hash=sha256:6d30e1a4f138b8951063e9a394752a9179b51da288ffa507b1e659222f4c1793 \ + --hash=sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1 \ + --hash=sha256:701b2e04b560eeb4bddf7a25ab8ca476176e34fdbd9a0e18196f0d12d4685f0b \ + --hash=sha256:771cf63ae0b1b50dd22e5f3e3549fab5f3f4ff1635d352a9e1a97fe01c7b2e64 \ + --hash=sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f \ + --hash=sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761 \ + --hash=sha256:7b0fc826b16c55e561e5d2a0c5c77b051ba1d92808118c4e4b5390f5e0cf191d \ + --hash=sha256:7c6be839a5a8312626b32029a415644a0846b420bc8b52b95b28cd92da162168 \ + --hash=sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869 \ + --hash=sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111 \ + --hash=sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5 \ + --hash=sha256:850a08d167dde16db8702c274f320c7be9d7da6f6dff2b58b18f9e815bd94f5b \ + --hash=sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7 \ + --hash=sha256:892a881d5f68c2b9ea304b7a6c2c60d9343df578a311b0f86b94bc8f1ffe8129 \ + --hash=sha256:895395f8918627b04efb1ad2a4cf605387143300ba03304cd1dfa6d03f5e095e \ + --hash=sha256:8b10e3e8fd7ddc2bd915848a2768e44c15b22936f1cc54c462ad1164deb02655 \ + --hash=sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c \ + --hash=sha256:8feeac04b5794e513e710af2f9c87d49f31a6dc47967bb264a1fed61a8989bec \ + --hash=sha256:9432f3598db432cb51c5b37fdbf29a60fcccc79e30d37a05022776a6bc4ab689 \ + --hash=sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f \ + --hash=sha256:978e7b97d4824b5be09c69fb70507cbde3b0323fc147332ca40a94d9a6a0ebbf \ + --hash=sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea \ + --hash=sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9 \ + --hash=sha256:9b68938dd5b0c783d88ff8e2dcc69451b5eb936fe212d516b21b9d5567f6d464 \ + --hash=sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519 \ + --hash=sha256:9f47b8a949e60f027f0aa0a6f6c7b7e9c55cbf4380d10b344e282fa4e7ab1e1b \ + --hash=sha256:a1dee1b804ff4d11c663636cf15d2ea47e9f79cd56c033fb1cbf08924842a48f \ + --hash=sha256:a2468d93d181667a7abd66e1b64bb9f76f361b0fef8faddf687456453576f5ee \ + --hash=sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a \ + --hash=sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e \ + --hash=sha256:a445486499897b88a7d6c310c88ed64dd37b1b59bfd7ae9107490bbb362f47d6 \ + --hash=sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2 \ + --hash=sha256:ab4b66edffb32d9e951efb3814bd104b8367a7501b81b955cacb5726d897389f \ + --hash=sha256:aca6c767f552b21b10f774aeac128e828eafb796adfa1b666a18bf6321453c3a \ + --hash=sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f \ + --hash=sha256:b10ec717381bdbfafef34607824db4c91de69ff085e4fca3b2af91b4fa17e68a \ + --hash=sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47 \ + --hash=sha256:b6acfb46a814762367fb7ba0828b0a17d441b92ce249a0e007474c9072662dda \ + --hash=sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0 \ + --hash=sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4 \ + --hash=sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d \ + --hash=sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3 \ + --hash=sha256:c14ad3bdc85ee7f318742c457ca3968a92126d144b15721c759033bfb06296c2 \ + --hash=sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355 \ + --hash=sha256:c3471e5c4a949c26ec00a77f01df59096aa9495877de76fd60a980f8ee6be461 \ + --hash=sha256:c583b927a8838dab890706a6fa7573fbb8b70e24000ef9f7238e2d6f6435a5ed \ + --hash=sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f \ + --hash=sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5 \ + --hash=sha256:cd5214352ae68f3b5e9af7768bdc5253695ee069675db3480518420b3be881f2 \ + --hash=sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829 \ + --hash=sha256:cdc8b74ecc48c0cb1e9607a05ec4e9e88db60a19ffcc9a1d5f9088ede40c8dc0 \ + --hash=sha256:d0a24b40877af2de4950252be9d21eaf7fb07660f3c2cae1f56c6b599ada5266 \ + --hash=sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575 \ + --hash=sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290 \ + --hash=sha256:d625a186a65201c23a9e3b8ed9c47e90a026e03256608cc91851c6709096844f \ + --hash=sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62 \ + --hash=sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f \ + --hash=sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a \ + --hash=sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7 \ + --hash=sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed \ + --hash=sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f \ + --hash=sha256:eb7d8d0e5886a89a55d2eef490e272fa965a9d57c6b29a5b5088a7997ec2cad1 \ + --hash=sha256:ecb42011e12ee19cafbc312887cbf3546959fe02fbad44f272d4be5baa997615 \ + --hash=sha256:ef3fbbf161dc9351a2fe0422e51b129f9e97e42385bd0320b309c15f7d287dd8 \ + --hash=sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3 \ + --hash=sha256:f077d0b97ab11fa7dcc633fca53515f290bca8a8a633e966d5b6d1879d9ed01a \ + --hash=sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff \ + --hash=sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084 \ + --hash=sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0 \ + --hash=sha256:fc5d783bd4a2387e97b8a2d5ec781cfb92b3d893bf82370548e99db5915935d3 \ + --hash=sha256:fc8515076c11f3cfdf4fb142dcca0fe384b1230a3b5415458ac84f3e0903ec13 \ + --hash=sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9 # via # instructor # pydantic @@ -4932,9 +5204,9 @@ pyflakes==3.4.0 \ --hash=sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58 \ --hash=sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f # via flake8 -pygments==2.20.0 \ - --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ - --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c # via # ipython # ipython-pygments-lexers @@ -4955,42 +5227,42 @@ pynndescent==0.6.0 \ --hash=sha256:7ffde0fb5b400741e055a9f7d377e3702e02250616834231f6c209e39aac24f5 \ --hash=sha256:dc8c74844e4c7f5cbd1e0cd6909da86fdc789e6ff4997336e344779c3d5538ef # via umap-learn -pyoxigraph==0.5.9 \ - --hash=sha256:09071f6c08b9489723dec96e2d96f64a15b9be2d165d3bbf67d40712884ba7a3 \ - --hash=sha256:276ca12ca2cc20b123812af78a489d35f871f1ee5579d9f98d6c65f038fcd9ee \ - --hash=sha256:379bef7f8fc38f638f358b1e12bc5bdc908a0e7d47157f4399bf103c727a66da \ - --hash=sha256:3bd1925a8185320bcb8c549cccafbb423cc3957513890e6f420b8e355055052a \ - --hash=sha256:4558d430bbad6e6b4ba98e0e89a2e28402211069d38e6e9b00083ae2d9d2d175 \ - --hash=sha256:4f8ff48b873157ab38e2595a56d6d2008471a45853f5fffc645658c6f69c07db \ - --hash=sha256:56b78aab5a5688ede88404372574785ba74e8d82b2cd1c0b0623a03b7069967f \ - --hash=sha256:57f3619c7860f4c95ddab077e4a3dedb7ca4cf191bd81096db835264d414ec5b \ - --hash=sha256:5a8a1b2debadb5fe79f8b89cbe1193e9c0e6fc1cf0c9431b6be706234beeabbe \ - --hash=sha256:5c9f93db5e14a03ac1e3934cece3fb6f7c0a9f4bde33082c72c788c12bf65ba4 \ - --hash=sha256:68f8daf082ea4bf9583abd10b64e23cd2c4a3285338a5ec24254181d45e63083 \ - --hash=sha256:6ab699861035163e89bc512ce20aa6e91b654e4d33114c9f5facab08f0fe3d7e \ - --hash=sha256:70ac4792acee8c86f795b0db785b467afbb02daf58e2beb6e6ef3c3f43f4c222 \ - --hash=sha256:70ffb46ae49f52b18a49c3fb63d906ae9c189de4fe4dbf5453279bda4d27af4e \ - --hash=sha256:71dba053e5efc0002fbd4ace3119b9d3aec8a6c5b164ed7409a2429bf71171b8 \ - --hash=sha256:79caf78136a8312e506beb607910cc5a93662a05a173aa9b560ce9d08801384f \ - --hash=sha256:8b998bc479a54a8905cdeaad621d0f7fed212abf9f1cbededfde4c51fc8e3bb8 \ - --hash=sha256:917d976dcb813d613d0ddd7da1c9dec6ad02ee815015f393c703cf0804946653 \ - --hash=sha256:94c2a8b52c1ed6e445a235a4f89cd460eea936f399d28df5e9927826bf52f032 \ - --hash=sha256:95347d64417299f91128ccfee486dcb14d2c6674a9b9a62e5c6978b651a2ccf2 \ - --hash=sha256:afe19bd1835a7245caad06cc9bb1a5c861882dc074fdfa24ba2626e3bbf9866a \ - --hash=sha256:b829233ea4445ccd1032d02e9189432a77e16888a79313498aa501b8731dc925 \ - --hash=sha256:b8884b0ce3ccbac99ebc2c995614a13dc5f5d86b0adb847f36aeb1c713d12946 \ - --hash=sha256:baffb41d914b761b06cde61eeb0a35dd5f0fa4808f71ae9902fdd1179e70e553 \ - --hash=sha256:bcac65148bddcd0ae24ee1bf20a2e89cc225a926b9e9996eb64dcce60400d1a3 \ - --hash=sha256:c711156407663e2182e4ea07c959f8e471f1b6ecaee1f00ce3accca7a53d9917 \ - --hash=sha256:d04806073905f448a48811b217115e71224be7f1d4075d1f5f5ec07a016f42ae \ - --hash=sha256:dd3a801b56c383cf4b078bd51cc1b86498b1a1f6e3e2f56406a00c3239fc97ca \ - --hash=sha256:e9ca7cd7666336fcbafd9a2ec7d598dd859b7bd2ca7b0838a0f7b92dd3828c28 \ - --hash=sha256:eee3db30ecb6836fdc05ddcbc6aa79ed521afcbfa707a8561b7e5891c4fb4ff8 \ - --hash=sha256:efd3d03bd2a36f9b0bdf3ce70d76ce5278c481fe961d14c2bb6efcac10f57ae2 \ - --hash=sha256:f39a6175a80a55c837981d4d68f42380071bb1d45af124de488fcc8a61a81af3 \ - --hash=sha256:f619aac7199b2ba91cade2fe69f64b4c73abb1e6b33735b0ff7205a753e609cd \ - --hash=sha256:f9154bea122c0bab11eda7604b27ceb424ab8ba1637250503008b8c6632ea405 \ - --hash=sha256:fe2bea0f41f5284b6dad99ea718d7ff03600068cdf8736b63a9e6cd05f056b19 +pyoxigraph==0.5.10 \ + --hash=sha256:06bea7135073e7fe07e160c7a4c8a9d58aede60da1564fe61199e8767280b907 \ + --hash=sha256:0881a9386b069e84c88f63e5d307c1d27f05e18c1627563d22f5fce3e2552602 \ + --hash=sha256:12999d88f9285500a8542692ae525c10a7c2ca67cb629eb6cb0a73dd7ec70824 \ + --hash=sha256:197aad1665a8a15069c9a1ec2f810aba489bbc3f26dd0ac4e9592d58e374201d \ + --hash=sha256:1e3aa6621c7ac6e008dc1fb79391a774352e963f3f3bffebf26349fc97d45a01 \ + --hash=sha256:203959ce9688e31a0156d45b7f605ebb9dd911cbfe4b499bf09980562dabf599 \ + --hash=sha256:23b1f241fed46e6c9bcdef805a960577e9686806bce0e5ba3de30d26db3ce7c3 \ + --hash=sha256:327bed1b7c824a15b035ab0b954cb68e4dc78cbffdc986fd0de0b84fdd3965bb \ + --hash=sha256:3bfd27f6b27f0143383a056f0bbabf2ddaf2ae7bc9735bfc55aa806ac3875b40 \ + --hash=sha256:3c35cd02f3c1917679c3f2fb8d57775d85599e52f0cfc7d45428faede6e577d9 \ + --hash=sha256:3db4e45eca735ad5f3b6cb9b2037720d7aeca8c878f9e984da8b437595386d34 \ + --hash=sha256:49f998e963453abd22c09b69523ef205cc6883b4658788c88e68aa131f438fb1 \ + --hash=sha256:4dc9df8e398200fedef7eef830da24056e0c08654b8b58a9b092b1ee6c92ab4f \ + --hash=sha256:5bb618eada8f7a75554ae2900f0f54d630cd5b8bc069e3f98109641dfa306399 \ + --hash=sha256:63b9d6987b07d8b2adb19f1be1adb3607761443a75272db0d15849c1ef2bd625 \ + --hash=sha256:6479ccf81c9d23fa6ddd68075ed602b9b5eff4bf72b37df9bab8260bae872ad3 \ + --hash=sha256:71d419aa84b0ce314ec8c084f89291bdf7e14d5cc6e36500f7209a9d352e74e9 \ + --hash=sha256:82464ab731d2f9860db2d14660fee16d944d3809b159e17903fcb23f7bf4d0f7 \ + --hash=sha256:83904657b98cc4d4138d695710b776b2b4b5cf5bbaec370e4f51b6190458597c \ + --hash=sha256:83bda3907b7d4379c445a4402feff663ae29c891b5133e5c658c44c1afedc8cd \ + --hash=sha256:85b62a1364b882416a92692c05e532f5e61285225a6be61bba9243d93ae09080 \ + --hash=sha256:994ce3da97883f81dce60bb299032909cc88f1bff6ad34ee7af401fa578e2771 \ + --hash=sha256:9fff9a904f6f54442979691feed5fc843dc0d0cf5a412b9c001fff7a1d43aa7e \ + --hash=sha256:a4552cbcd5e379ee7990eef651301074f1ff7ba5c3f69b3186013a89c91af83e \ + --hash=sha256:a4cf205a29b34d2ac327702d5e6de131bfc277299dcf84476c7cfba1310f67f3 \ + --hash=sha256:a5c062e6c8d661206d8e43dfb1ed7f4628ebd659e7da2ae07e9be5666817b855 \ + --hash=sha256:a773f21d4276778331b2920622b93a6ee86631a9411a32a641de43832882e376 \ + --hash=sha256:be8a88654210ae8598e3c40c3b5891e0dcc09f348808c15fc437bb51124b5bd1 \ + --hash=sha256:cb5c0945ca392ef464a12d2a3def3765e651329af64311b30c5890ac56116081 \ + --hash=sha256:d03f13e360134547328ceab5c35e9ed0392225e7017655d51700453f0920da3d \ + --hash=sha256:d9e13df7b9eb49ac4a4729ef7b838a7089ec4e500713dec2f44a0fe5b00098c3 \ + --hash=sha256:e9fdb6757d80b47c523471cf5469d48f741ccdaade8339d38206499ba42e9180 \ + --hash=sha256:edb24d1e168e3566b1aa081e5a5b55dcaabc690712d8b8da73d0e3092395ada9 \ + --hash=sha256:f268f6b56ec91395493ab2b4ce76809f7cbd5a0642815fee49258cfe651efd48 \ + --hash=sha256:fc65cca4cebfa2698e0aacd6f6fa25590ec9a5b1091946f277789831ab8b5e4d # via semantica (pyproject.toml) pyparsing==3.3.2 \ --hash=sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d \ @@ -4998,29 +5270,29 @@ pyparsing==3.3.2 \ # via # matplotlib # rdflib -pypdfium2==5.12.1 \ - --hash=sha256:05bab9b1ba2de7fc299ae2af25cb9c8a0543bc8bb893e879fe8c9ba8310e9ce4 \ - --hash=sha256:05bfa20a08a96584253bbe38b60e13f81a037eac31c5579e607ec1480ad25dbf \ - --hash=sha256:07eeebb2784f4cd38d386b924235df43217a397442796673296bb6efbdaad1d0 \ - --hash=sha256:236dbdc88aa54f14b27937ccb2ebe3dcf08c10dbb8652f432ea982dc9af39732 \ - --hash=sha256:4648f0905441bcb141687ca2263bbf38a1aa056b943eef06019f91cff3e1da4a \ - --hash=sha256:5c3e6cbe43581af79526184643920ab03a9401a0c79f2226bea9d4d1e3d34008 \ - --hash=sha256:5f257bb40fa44ce9ba18d2c919777dbd3f16bf22548b1d68fd56c7c92f1de530 \ - --hash=sha256:66a9ed40d70a5d728cd42148fecb9d7a0917c6161d6bb67c844093a4ed1df089 \ - --hash=sha256:6eabf028ad8e7bc7811c9acf3a72718c180569b624b844d2c6cc974609784275 \ - --hash=sha256:715ae16b34ea1d64884d58800155179ba700e9ea65a2f583b020666acd2bfb12 \ - --hash=sha256:7857cfa6642ec5a09db12ff8f5cf6b6494585b5e3a605399fddc4fb862837b63 \ - --hash=sha256:847378a5ab41332998b2621b21bab2e96dc8c3eff36a08bce26695b964163983 \ - --hash=sha256:9609be73a6701a68f29dffe0335f7a2e4b3ba581542ed65d35d49f761a4600ca \ - --hash=sha256:974082344172da76a5c3c0782eaedfe6069dbe88db77d8c671ef36b61e9b14e2 \ - --hash=sha256:9c8856ce7dd77a7827476c7d75afe1197d6cd505f5cb4167b6aacf661f3f8ea5 \ - --hash=sha256:9f059f7bdbdf4352eb83691071096940d769d6ae5930b8734237fdb1bd78fbc2 \ - --hash=sha256:afc0b7e0c975a429abc75875209ce17b66d749f6ac5cbe8ba72470e83901e304 \ - --hash=sha256:bdff622181fab64f32328591c9c8287cdc745c9a1f2afc26ca3feba39e3e6645 \ - --hash=sha256:d0e0648fb2e28f50efcd1ec0a5a18ced9f4d66b2c227fae9b603f0a883b2d13f \ - --hash=sha256:d4ee061e566a6422b660cdddaaa799a2d1cbf2f016921bcaf24d61426d01d942 \ - --hash=sha256:e10cbf41b21233ec5e20adfc170cf60edd77abead86a97dc708fff55a8a886c7 \ - --hash=sha256:e5358d2ce4ebc5c899aab1df9ca5d215357244e9168aa443225d3c1e649c7eac +pypdfium2==5.13.0 \ + --hash=sha256:07f58e91b8c45ca144a1ff3008faf3c73ef8a5e9fb32988831788363288228cd \ + --hash=sha256:2abedfb5c70992b19c780ed58d7f7b929e8ce8ee52c9140158f44317c90ec6c7 \ + --hash=sha256:2ed32ff685f8e05e637c990bedbf5fca66727bf27718d8bc33eeab21ce0630d1 \ + --hash=sha256:3826e521e895648983cb9ee6b934d4bf51552600043984f84e9c2b3b14b696f3 \ + --hash=sha256:46b2f5be9e7ae941ee4216e3d20b66f9dc3d81944a3d57756272de5275204709 \ + --hash=sha256:47dcca2a8d507b5fd24f94c3c9d48fb379430f097bc20f01beff6c963ffbcedb \ + --hash=sha256:554a0b23376460af1410e3c915906895e2dac67a086b9e6ccde0643a795d3b0d \ + --hash=sha256:5c029d7163a91f264eafab51fb442a84a33efd9fd83d5a06c0136a7857a3cc8d \ + --hash=sha256:7ca2d8e31bd8d0d40c496416b7d8bea423388669ffd494929f50e8c3a82326b8 \ + --hash=sha256:81df25c1ab4c13ff773102d3cbea1967511d079123b067fc077bd0c4d57d91d8 \ + --hash=sha256:882f4bbd4b17a335b43603169a14cde9341de12b238acd5c39e690cbca7c4293 \ + --hash=sha256:9c777edba28d1d5fd15435ed3a78ee2fdb93dd069be37cb53b559bc122793770 \ + --hash=sha256:9ee8c2bb2e68b396ab4a763215ac100dacb6b96d0da5bebeb239a021aecc3a7e \ + --hash=sha256:ada81c36483cd61d07e32bc7814620ee96256b4f421b913f566861bf91800248 \ + --hash=sha256:b90b0a5ac310bb34db8eb848e58fcab4e201e124e3cf3cb1ccb7b85293e034af \ + --hash=sha256:bcd81394fe101405e026eedb3e40bef84635c1e5d974dd6036420eb6937753c6 \ + --hash=sha256:be2dccbde0ce7efe334ecd8f348df4308db360756ede4f0821d82dfc9a58caa8 \ + --hash=sha256:d33ee7077db67478b75efe4b5ea9610fb96c5416a0bc4949227f0f59c34dfcd9 \ + --hash=sha256:d66a32d89fa5b4a2715810171239eb194df4aba604727483ab760512f3c6a851 \ + --hash=sha256:d96929bde3bd64c771ab3558ca1ffd7704cc4d872ab92cd9f8f8b8a20f7f36b8 \ + --hash=sha256:d96beb7f379e6c76d874ca93fcd182ac3168dd499056407070f9927fb1061b8e \ + --hash=sha256:da5c7b74eebf40b5c1fbe1de01aa1edc8827a79fb1efd999616bc20dcaf77ba4 # via docling-slim pypickle==2.0.2 \ --hash=sha256:d3307127314465fe3dc8f0162e11777d5e8284f3a29dc48b0f770d364a85d998 \ @@ -5056,10 +5328,9 @@ python-dateutil==2.9.0.post0 \ # jupyter-client # matplotlib # pandas - # pinecone-client -python-discovery==1.5.2 \ - --hash=sha256:3e338c2d0f15dfaeea57493f4c2c6caebe0e998ea815c30ae8bf8ee21f1112d3 \ - --hash=sha256:45fd4f20a4e3f9b7bf2e0817870bc8e3b320a19658da177af800768c82dbf354 +python-discovery==1.6.0 \ + --hash=sha256:6393b4eae1be8b2182670635e7baff89ac21cb9f8e86fd1ff40c7b1144febb4c \ + --hash=sha256:d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a # via virtualenv python-docx==1.2.0 \ --hash=sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7 \ @@ -5067,19 +5338,20 @@ python-docx==1.2.0 \ # via # semantica (pyproject.toml) # docling-slim -python-dotenv==1.2.2 \ - --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ - --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 +python-dotenv==1.2.3 \ + --hash=sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9 \ + --hash=sha256:a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35 # via # semantica (pyproject.toml) # docling-slim + # google-adk # litellm # pydantic-settings # pymilvus # uvicorn -python-json-logger==4.1.0 \ - --hash=sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2 \ - --hash=sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195 +python-json-logger==4.2.0 \ + --hash=sha256:158a52126fcd6869e09574d2b66272666f3dc8f468c62637ef9a1fa883719cb9 \ + --hash=sha256:e371ebe22ec01e289850102091a2b1f6fc9e655c7f1f5f29073936756c290afa # via jupyter-events python-louvain==0.16 \ --hash=sha256:b7ba2df5002fd28d3ee789a49532baad11fe648e4f2117cf0798e7520a1da56b @@ -5089,7 +5361,9 @@ python-louvain==0.16 \ python-multipart==0.0.32 \ --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \ --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 - # via semantica (pyproject.toml) + # via + # semantica (pyproject.toml) + # google-adk python-oxmsg==0.0.2 \ --hash=sha256:22be29b14c46016bcd05e34abddfd8e05ee82082f53b82753d115da3fc7d0355 \ --hash=sha256:a6aff4deb1b5975d44d49dab1d9384089ffeec819e19c6940bc7ffbc84775fad @@ -5228,106 +5502,102 @@ pyyaml==6.0.3 \ # accelerate # agno # docling-core + # google-adk # huggingface-hub # jupyter-events + # langchain-core # omegaconf # pre-commit # rapidocr # transformers # uvicorn -pyzmq==27.1.0 \ - --hash=sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d \ - --hash=sha256:01f9437501886d3a1dd4b02ef59fb8cc384fa718ce066d52f175ee49dd5b7ed8 \ - --hash=sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e \ - --hash=sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba \ - --hash=sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581 \ - --hash=sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05 \ - --hash=sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386 \ - --hash=sha256:0c996ded912812a2fcd7ab6574f4ad3edc27cb6510349431e4930d4196ade7db \ - --hash=sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28 \ - --hash=sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e \ - --hash=sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea \ - --hash=sha256:18339186c0ed0ce5835f2656cdfb32203125917711af64da64dbaa3d949e5a1b \ - --hash=sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066 \ - --hash=sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97 \ - --hash=sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0 \ - --hash=sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113 \ - --hash=sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92 \ - --hash=sha256:1f8426a01b1c4098a750973c37131cf585f61c7911d735f729935a0c701b68d3 \ - --hash=sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86 \ - --hash=sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd \ - --hash=sha256:346e9ba4198177a07e7706050f35d733e08c1c1f8ceacd5eb6389d653579ffbc \ - --hash=sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233 \ - --hash=sha256:3970778e74cb7f85934d2b926b9900e92bfe597e62267d7499acc39c9c28e345 \ - --hash=sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31 \ - --hash=sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74 \ - --hash=sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc \ - --hash=sha256:49d3980544447f6bd2968b6ac913ab963a49dcaa2d4a2990041f16057b04c429 \ - --hash=sha256:4a19387a3dddcc762bfd2f570d14e2395b2c9701329b266f83dd87a2b3cbd381 \ - --hash=sha256:4c618fbcd069e3a29dcd221739cacde52edcc681f041907867e0f5cc7e85f172 \ - --hash=sha256:50081a4e98472ba9f5a02850014b4c9b629da6710f8f14f3b15897c666a28f1b \ - --hash=sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556 \ - --hash=sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4 \ - --hash=sha256:510869f9df36ab97f89f4cff9d002a89ac554c7ac9cadd87d444aa4cf66abd27 \ - --hash=sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c \ - --hash=sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd \ - --hash=sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e \ - --hash=sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526 \ - --hash=sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e \ - --hash=sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f \ - --hash=sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128 \ - --hash=sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96 \ - --hash=sha256:722ea791aa233ac0a819fc2c475e1292c76930b31f1d828cb61073e2fe5e208f \ - --hash=sha256:726b6a502f2e34c6d2ada5e702929586d3ac948a4dbbb7fed9854ec8c0466027 \ - --hash=sha256:753d56fba8f70962cd8295fb3edb40b9b16deaa882dd2b5a3a2039f9ff7625aa \ - --hash=sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f \ - --hash=sha256:7be883ff3d722e6085ee3f4afc057a50f7f2e0c72d289fd54df5706b4e3d3a50 \ - --hash=sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c \ - --hash=sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2 \ - --hash=sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146 \ - --hash=sha256:849ca054d81aa1c175c49484afaaa5db0622092b5eccb2055f9f3bb8f703782d \ - --hash=sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97 \ - --hash=sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5 \ - --hash=sha256:9541c444cfe1b1c0156c5c86ece2bb926c7079a18e7b47b0b1b3b1b875e5d098 \ - --hash=sha256:96c71c32fff75957db6ae33cd961439f386505c6e6b377370af9b24a1ef9eafb \ - --hash=sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32 \ - --hash=sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62 \ - --hash=sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf \ - --hash=sha256:a1aa0ee920fb3825d6c825ae3f6c508403b905b698b6460408ebd5bb04bbb312 \ - --hash=sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda \ - --hash=sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540 \ - --hash=sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604 \ - --hash=sha256:ad68808a61cbfbbae7ba26d6233f2a4aa3b221de379ce9ee468aa7a83b9c36b0 \ - --hash=sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db \ - --hash=sha256:b1267823d72d1e40701dcba7edc45fd17f71be1285557b7fe668887150a14b78 \ - --hash=sha256:b2e592db3a93128daf567de9650a2f3859017b3f7a66bc4ed6e4779d6034976f \ - --hash=sha256:b721c05d932e5ad9ff9344f708c96b9e1a485418c6618d765fca95d4daacfbef \ - --hash=sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2 \ - --hash=sha256:bd67e7c8f4654bef471c0b1ca6614af0b5202a790723a58b79d9584dc8022a78 \ - --hash=sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b \ - --hash=sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f \ - --hash=sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6 \ - --hash=sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39 \ - --hash=sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f \ - --hash=sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355 \ - --hash=sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a \ - --hash=sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a \ - --hash=sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856 \ - --hash=sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9 \ - --hash=sha256:da96ecdcf7d3919c3be2de91a8c513c186f6762aa6cf7c01087ed74fad7f0968 \ - --hash=sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7 \ - --hash=sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1 \ - --hash=sha256:df7cd397ece96cf20a76fae705d40efbab217d217897a5053267cd88a700c266 \ - --hash=sha256:e2687c2d230e8d8584fbea433c24382edfeda0c60627aca3446aa5e58d5d1831 \ - --hash=sha256:e30a74a39b93e2e1591b58eb1acef4902be27c957a8720b0e368f579b82dc22f \ - --hash=sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7 \ - --hash=sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394 \ - --hash=sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07 \ - --hash=sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496 \ - --hash=sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90 \ - --hash=sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271 \ - --hash=sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6 \ - --hash=sha256:ff8d114d14ac671d88c89b9224c63d6c4e5a613fe8acd5594ce53d752a3aafe9 +pyzmq==27.2.0 \ + --hash=sha256:00e73942ef12cecbc7951c4a9104bb8ffaed742abb13af2da6833d90dd368cef \ + --hash=sha256:010db74a1dd67c7cd8b8b30916355735db7d633a070510bb34e41ab679ab2c0e \ + --hash=sha256:0e1af01858d6dc0c09cea57f9cb1ddf4601f04897b6bb1efc3a2038123c87d79 \ + --hash=sha256:0f4bd6743e8bf854c3bfce892dd6578a514aabf128e37a4b2eafcf01856f7e44 \ + --hash=sha256:1132805970045adb9f5f05dd57040978286a8e21a5475f2c2ddf1bc983b9a2c7 \ + --hash=sha256:1ecbdd131b9669f62d3a45afee5527c7ae9f141e4301267f21714c90bd21725f \ + --hash=sha256:1f8079d0521fe94bbb401fe9407578b28f3701627c8be2c9f7e0c5b77dcb0109 \ + --hash=sha256:211350c3ccd4746bc5a85e8fe961bad1f7f2f274f67cf1f785fad7f96f562eea \ + --hash=sha256:288cc790da0e3064a14a38ddc56ba169dada8c8af4cb86518db2bcbd380eedbb \ + --hash=sha256:2c218c6ab8bc447ba62054b581fd30209689d199c6ecb253f79615ca74a38e12 \ + --hash=sha256:3146385b94a760236c5eceff468a66a296a716ca98a2e0f9217b1518118466b1 \ + --hash=sha256:348d6fd3e4b81ae4580622ea8c2ea60224e84b2ac1b3be4482e6edc7de06e7a3 \ + --hash=sha256:376981d106598beb70be384f44d8f589832fd0051d184d38d10043da3cc3b080 \ + --hash=sha256:39755dc4a923021bd0677990ffdbc21cff0e1ee1cf07fe3817acea153ef4cb67 \ + --hash=sha256:3ab6eb88590e510ab16715c32dbba12000da9bee989fdadd9ee19a234c492eb7 \ + --hash=sha256:3d45189c0c3c99f817b7fefff0d32eeef684cf33e1e3c0fc4281515357c54702 \ + --hash=sha256:3ee556ed1cf836f96de9d5e545563116426d4a94f21b8041fdc79408eff18ebb \ + --hash=sha256:3ee8dd7031d5e23f632e0e7eee67183ca7d2536e0de35dc1e5d69f3471a791e8 \ + --hash=sha256:40124779c3a56ad5d91902df1ff89159cb414b6c1a0ee697abcc66cf5e6db62d \ + --hash=sha256:40d96cb7a8f6a43aa9617c00215c2b73e1b5e4a1d6cbc9f5860ed7ac682599f0 \ + --hash=sha256:44f261eca7dfb9904ea2b56428f59ab693bbe2715c0413a701f17b067ebf877c \ + --hash=sha256:468139ddb2e494d06e586bd3a6835077e8b3764560c8db552fe685c5867fc24e \ + --hash=sha256:480dba27b145373b5e103890f17969d891bc9e86746d6b8b29dd70b0d4addc62 \ + --hash=sha256:4ebc7889b31bc11c72e9f17ba3ebb0a8b0911cce413f41b498e55383a94819a3 \ + --hash=sha256:507c0b33f95502723d325487e8e50c2cdd3b37444143f05423a3861327f69bf7 \ + --hash=sha256:54d4259d1bfae24ecdb5ca79f7acc2eac6c286a02d6a0ae617797cb45f0726d3 \ + --hash=sha256:56b48fa9d478a3af7254f397697a62f5ad3e1bb677e200b2701f0c290d97e5af \ + --hash=sha256:591c8de5851c5ea372194469fe97587b97c3b641e9a70f31bb3474acbfde0241 \ + --hash=sha256:650c6cd7cb39a069e7048261efe66fce8bf2e0052c831a7a099b7a0f2ea860d7 \ + --hash=sha256:679b5b1dde326a921ea2c9ec1f9ea3115bfe1b4735779bbc6eb0473a0ed93f71 \ + --hash=sha256:6eb63cc61ab93b01b9afc887a160255e2fbe703fdbacfe5feaef87214f51bd6c \ + --hash=sha256:714f8cbd66c7e405338d668f79d2fe83fe923defe348e843be998603cf92eeff \ + --hash=sha256:722f0a6940be1a483c81029a271d950e04dc2ff113a42e21b3d2b7a0d8e59638 \ + --hash=sha256:76afba06ae698f2b8fe4fb34b32c760a650f168c2e622f370f2c528035b7f650 \ + --hash=sha256:770a37f28ddfbe1d2c40a2e3ce37e5fd10831daa6ae9634105aa8a5d23507b00 \ + --hash=sha256:7e2579c5de82ddf4544d723c1bc8b44c3b806d157acc9fb2a2d18e10ef28e202 \ + --hash=sha256:82a09aa67871d4f2fcafd47bf670fb93210b232a7c2d4b8a54676314edf04033 \ + --hash=sha256:88c0fac061bac269076edeb3a209acefc96cd6167c239daf1c2b404ac48d7012 \ + --hash=sha256:8a5c04ad2e368142aea52d1abdf6631cb2534864e3c16ab78268ab957060b2a6 \ + --hash=sha256:8b86e04f55af0f4d8cd8ecf14c0b8b81ebc8fd66fa20126b753514628ecadc7e \ + --hash=sha256:917d601e9540098f580d2723d0ce6402cdb6f02bc8dc2de74e0dca6e13bffd1b \ + --hash=sha256:9216132843d139a123f243c07fe70f7487dce5041093dd77040f9adb5dc91872 \ + --hash=sha256:94242bd4de6af7e74665e14a88630bccd615057f6acfaf08a3a432551d604645 \ + --hash=sha256:95369ed6626afcfe2ac89832fb1b917c077fbeb905fbbe5d918349ce0222b89b \ + --hash=sha256:95f52b877149b06bbdeec2e8ea6230aad14950bbfbcfa16e7eb88951f07d6b28 \ + --hash=sha256:97d4c6622f129b514a4f5939af1b5f434c97f47085d9311b5f7f36e24b3bd447 \ + --hash=sha256:9846e881620dd62566ca76a53e384c3f37490faf4b9240aebc7498810dfca853 \ + --hash=sha256:9ab72ee77b313d0658447204c8201f9b315146e923b48c56ea7dbd005d464a91 \ + --hash=sha256:a070a9cdad1f8f8a85ea153afcc4654f11b10895d14c0acabe10f1df0e0892ea \ + --hash=sha256:a0ee3c49be2aa15abd12cbbd14d4ea2892f872c688e1e487af39ec1972ed549d \ + --hash=sha256:a7c1144dc61777938e932a2c9011b980b89fd8ff3733033b34c44c299187a6e1 \ + --hash=sha256:a843094b4d3d633bc3623e47a2ff50742d6af02bc1f7606aa2e67e971e21878d \ + --hash=sha256:ac126d48cf18aa955daabef43bf0009ff76ad4deee437d09ecf15388214b5beb \ + --hash=sha256:ae6ebbc0bfe5a21ce21e32ba567bf73df2d93888109c65acbd42506cf9395759 \ + --hash=sha256:b26f2d0493b79ce3c3112c8a12649418915582ba4707b8ed9f44febf2be71f42 \ + --hash=sha256:b398c5fe102b41e1559f7ffdae760aabd5f432d73b047b4ae0eac4e01cb594d2 \ + --hash=sha256:b8d5f66e4a8246cf77f7b8f7902af64f00553368fa0373c89d99b78f0ad79394 \ + --hash=sha256:baa2ce3485145653194d6c8c5beedd1e9f0bf46a0919c9fa2fe2204fc35b74d9 \ + --hash=sha256:bad4813f270592cedf56977e31ac1fc374fb0f6f67ea5134a5e37c19cb429a8e \ + --hash=sha256:bf0b6e4ce1bb089751c504c5493d6b0557eabd02dd21b76e9086cf964234b103 \ + --hash=sha256:c218b816220d05acf6ab1bafca58926d95cbcc5fec5024724666030466308f0c \ + --hash=sha256:c5129a8fe43ecc49b99eb75616603d483a3c2fcaef504988fafe8ea392aea98b \ + --hash=sha256:c551b9e2f86dc625fcb1a032c0d68042678caf96a8dd7c28796766b673bd5b52 \ + --hash=sha256:c7cfb75caa83f5153c687e9d2107f64b5ef0ef0d6edd260d3ff920baaaa69101 \ + --hash=sha256:c9322f9c87b0935870516c2876e1e29497fdc50439c785ece63e3fbbab06c821 \ + --hash=sha256:d1526b42a2e725b84ed226f37becedc250c6347594e5ed304e4e9aff68c9aec3 \ + --hash=sha256:d1bc1d380a91d954ed5fc9f12915dba014eed0978d2de05ee7ca688bdaac144a \ + --hash=sha256:d41ebb260b69329b7d4a2936d44c872c86dd785355b51366c8b14e07ed7e9373 \ + --hash=sha256:d61910b52be5b2cd8b248dbcbe3a1b0275556a7d99fb613fc43323b546e273b8 \ + --hash=sha256:d61a0169ba05ab7ebc48dc793f092df12f789bf378dac8321ccd966fd93d94e8 \ + --hash=sha256:d64da42cae09e6b0c61368b4cc8ca80f23ce3af17584d08053f3dc957433d5ed \ + --hash=sha256:d9527e3dbaef1edaeeb2446fa7379446814a43ade8adc7c4a5ebe69437815ddd \ + --hash=sha256:dcc99ca132b667a4ed750afd42db4ea73288f18425a9b2e3c0af095665c491f5 \ + --hash=sha256:dde5e291548ca0f397623b5e523db5c90172b32aa4fd3ba464a79ea31a580b43 \ + --hash=sha256:dea74fd65f1fc5f7fe167916a473ebe6ed6174e5e5d9de11ea6583661be6cf43 \ + --hash=sha256:dfcd024eade5870b25f890c4df0ba9421ed8167d8d3d82334237512c1158dada \ + --hash=sha256:e0fa0bc6b1a184aee59b32efcd1b7f0e6d5b8f9387799e4c16a4cb66a86747d6 \ + --hash=sha256:e1ed46048d1920cabc96d952a0d5cfe4127ad8db572c335aae4e3c57b9278d7f \ + --hash=sha256:ec8a318dfc27c7d946651b3d9e8025d5734f30c168a822195601827207bac09b \ + --hash=sha256:edce90a1e588ec63adbf612cc0ad582de4169cd216c7ae53c15f42a2ee902f35 \ + --hash=sha256:f52f08101907609cc08db6a1f9f2a7a9afd54e9b2ca16178c9c38e99fb593cef \ + --hash=sha256:f5c6d8744d10b5e1eadd90a7c58f8546acf6bf680ee463f7e6ada09ad6c9f802 \ + --hash=sha256:f707bcf2c1d007d14d70531d4dd7b41060881c73efa845580bf6faaf9ea24d42 \ + --hash=sha256:fba8afcf265c6e9fbe1594cb045d4765c6c9a7d607653a8196067ef23566b843 \ + --hash=sha256:fdaaa4ea3242f6ad298eb5177eb042aea5c73c30e76d20caee7b15af20d24ec2 \ + --hash=sha256:ff60f0f7ccfda0e303ac43bec7096007b7cdf2c41b3739d1ec667febe67acab3 # via # ipykernel # jupyter-client @@ -5337,6 +5607,10 @@ qdrant-client==1.19.0 \ --hash=sha256:13602a2b3478a95ecdf42f97b93d7f703b63a3361cd912a04495a33a5ac14121 \ --hash=sha256:365395a04b0a26c309b25b7d8b1c99ef2071ec9a2b74bc8a5fd3b7a3642fe963 # via semantica (pyproject.toml) +questionary==2.1.1 \ + --hash=sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d \ + --hash=sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59 + # via agnoctl rapidocr==3.9.2 \ --hash=sha256:04d6b8d151f823d930bd91910555f57bea897c0c44fa6794267b94cf9c1ef9a0 # via docling-slim @@ -5360,122 +5634,139 @@ referencing==0.37.0 \ # jsonschema # jsonschema-specifications # jupyter-events -regex==2026.7.19 \ - --hash=sha256:062f8cb7a9739c4835d22bd96f370c59aba89f257adcfa53be3cc209e08d3ae0 \ - --hash=sha256:064f1760a5a4ade65c5419be23e782f29147528e8a66e0c42dd4cedb8d4e9fc6 \ - --hash=sha256:09523a592938aa9f587fb74467c63ff0cf88fc3df14c82ab0f0517dcf76aaa62 \ - --hash=sha256:09d3007fc76249a83cdd33de160d50e6cb77f54e09d8fa9e7148e10607ce24af \ - --hash=sha256:09f3e5287f94f17b709dc9a9e70865855feee835c861613be144218ce4ca82cc \ - --hash=sha256:0c41c63992bf1874cebb6e7f56fd7d3c007924659a604ae3d90e427d40d4fd13 \ - --hash=sha256:0e9554c8785eac5cffe6300f69a91f58ba72bc88a5f8d661235ad7c6aa5b8ccd \ - --hash=sha256:1123ef4211d763ee771d47916a1596e2f4915794f7aabdc1adcb20e4249a6951 \ - --hash=sha256:15b364b9b98d6d2fe1a85034c23a3180ff913f46caddc3895f6fd65186255ccc \ - --hash=sha256:1649eb39fcc9ea80c4d2f110fde2b8ab2aef3877b98f02ab9b14e961f418c511 \ - --hash=sha256:17ed5692f6acc4183e98331101a5f9e4f64d72fe58b753da4d444a2c77d05b12 \ - --hash=sha256:199535629f25caf89698039af3d1ad5fcae7f933e2112c73f1cdf49165c99518 \ - --hash=sha256:1c398716054621aa300b3d411f467dda903806c5da0df6945ab73982b8d115db \ - --hash=sha256:1d3372064506b94dd2c67c845f2db8062e9e9ba84d04e33cb96d7d33c11fe1ae \ - --hash=sha256:1d58561843f0ff7dc78b4c28b5e2dc388f3eff94ebc8a232a3adba961fc00009 \ - --hash=sha256:1d793a7988e04fcb1e2e135567443d82173225d657419ec09414a9b5a145b986 \ - --hash=sha256:1ebac3474b8589fce2f9b225b650afd61448f7c73a5d0255a10cc6366471aed1 \ - --hash=sha256:20568e182eb82d39a6bf7cff3fd58566f14c75c6f74b2c8c96537eecf9010e3a \ - --hash=sha256:22a992de9a0d91bda927bf02b94351d737a0302905432c88a53de7c4b9ce62e2 \ - --hash=sha256:2955907b7157a6660f27079edf7e0229e9c9c5325c77a2ef6a890cba91efa6f0 \ - --hash=sha256:2c4e61e2e1be56f63ec3cc618aa9e0de81ef6f43d177205451840022e24f5b78 \ - --hash=sha256:2cc3460cedf7579948486eab03bc9ad7089df4d7281c0f47f4afe03e8d13f02d \ - --hash=sha256:2ce9e679f776649746729b6c86382da519ef649c8e34cc41df0d2e5e0f6c36d4 \ - --hash=sha256:2ef7eeb108c47ce7bcc9513e51bcb1bf57e8f483d52fce68a8642e3527141ae0 \ - --hash=sha256:3080a7fd38ef049bd489e01c970c97dd84ff446a885b0f1f6b26d9b1ad13ce11 \ - --hash=sha256:343a4504e3fb688c47cad451221ca5d4814f42b1e16c0065bde9cbf7f473bd52 \ - --hash=sha256:36aacfb15faaff3ced55afbf35ec72f50d4aee22082c4f7fe0573a33e2fca92e \ - --hash=sha256:3d3143f159261b1ce5b24c261c590e5913370c3200c5e9ebbb92b5aa5e111902 \ - --hash=sha256:40b34dd88658e4fedd2fddbf0275ac970d00614b731357f425722a3ed1983d11 \ - --hash=sha256:4458124d71339f505bf1fb94f69fd1bb8fa9d2481eebfef27c10ef4f2b9e12f6 \ - --hash=sha256:4896db1f4ce0576765b8272aa922df324e0f5b9bb2c3d03044ff32a7234a9aba \ - --hash=sha256:4a0530bb1b8c1c985e7e2122e2b4d3aedd8a3c21c6bfddae6767c4405668b56e \ - --hash=sha256:4aa5435cdb3eb6f55fe98a171b05e3fbcd95fadaa4aa32acf62afd9b0cfdbcac \ - --hash=sha256:4c3501bfa814ab07b5580741f9bf78dfdfe146a04057f82df9e2402d2a975939 \ - --hash=sha256:4e5413bd5f13d3a4e3539ca98f70f75e7fca92518dd7f117f030ebedd10b60cb \ - --hash=sha256:4e6883a021db30511d9fb8cfb0f222ce1f2c369f7d4d8b0448f449a93ba0bdfc \ - --hash=sha256:52579c60a6078be70a0e49c81d6e56d677f34cd439af281a0083b8c7bc75c095 \ - --hash=sha256:555497390743af1a65045fa4527782d10ff5b88970359412baa4a1e628fe393b \ - --hash=sha256:56ad4d9f77df871a99e25c37091052a02528ec0eb059de928ee33956b854b45b \ - --hash=sha256:571fde9741eb0ccde23dd4e0c1d50fbae910e901fa7e629faf39b2dda740d220 \ - --hash=sha256:572fc57b0009c735ee56c175ea021b637a15551a312f56734277f923d6fd0f6c \ - --hash=sha256:59787bd5f8c70aa339084e961d2996b53fbdeab4d5393bba5c1fe1fc32e02bae \ - --hash=sha256:5a2721c8720e2cb3c209925dfb9200199b4b07361c9e01d321719404b21458b3 \ - --hash=sha256:5cc26a66e212fa5d6c6170c3a40d99d888db3020c6fdab1523250d4341382e44 \ - --hash=sha256:5ebee1ee89c39c953baac6924fcde08c5bb427c4057510862f9d7c7bdb3d8665 \ - --hash=sha256:60be8693a1dadc210bbcbc0db3e26da5f7d01d1d5a3da594e99b4fa42df404f5 \ - --hash=sha256:618a0aed532be87294c4477b0481f3aa0f1520f4014a4374dd4cf789b4cd2c97 \ - --hash=sha256:61bb1bd45520aacd56dd80943bd34991fb5350afdd1f36f2282230fd5154a218 \ - --hash=sha256:6383cd2ed53a646c659ba1fe65727db76437fdaa069e697a0b44a51d5843d864 \ - --hash=sha256:64729333167c2dcaaa56a331d40ee097bd9c5617ffd51dabb09eaddafb1b532e \ - --hash=sha256:64b6ca7391a1395c2638dd5c7456d67bea44fc6c5e8e92c5dc8aa6a8f23292b4 \ - --hash=sha256:65dcd28d3eba2ab7c2fd906485cc301392b47cc2234790d27d4e4814e02cdfda \ - --hash=sha256:65fa6cb38ed5e9c3637e68e544f598b39c3b86b808ed0627a67b68320384b459 \ - --hash=sha256:66bd62c59a5427746e8c44becae1d9b99d22fb13f30f492083dfb9ad7c45cc18 \ - --hash=sha256:6e44c0e7c5664be20aee92085153150c0a7967310a73a43c0f832b7cd35d0dd3 \ - --hash=sha256:6f8c6e7a1cfa3dc9d0ee2de0e65e834537fa29992cc3976ffec914afc35c5dd5 \ - --hash=sha256:7322ec6cc9fba9d49ab888bb82d67ac5625627aa168f0165139b17018df3fb8a \ - --hash=sha256:73b133a9e6fb512858e7f065e96f1180aa46646bc74a83aea62f1d314f3dd035 \ - --hash=sha256:73f272fba87b8ccfe70a137d02a54af386f6d27aa509fbffdd978f5947aae1aa \ - --hash=sha256:7e77b324909c1617cbb4c668677e2c6ae13f44d7c1de0d4f15f2e3c10f3315b5 \ - --hash=sha256:80115dd39481fd3a4b4080220799dbcacb921a844de4b827264ececacbe17c78 \ - --hash=sha256:87ccab0db8d5f4fbb0272642113c1adb2ffc698c16d3a0944580222331fa7a20 \ - --hash=sha256:89dfee3319f5ae3f75ebd5c2445a809bb320252ba5529ffdafea4ef25d79cf1a \ - --hash=sha256:8ac59a0900474a52b7c04af8196affc22bd9842acb0950df12f7b813e983609a \ - --hash=sha256:8cae6fd77a5b72dae505084b1a2ee0360139faf72fedbab667cd7cc65aae7a6a \ - --hash=sha256:8d3469c91dd92ee41b7c95280edbd975ef1ba9195086686623a1c6e8935ce965 \ - --hash=sha256:90c633e7e8d6bf4e992b8b36ce69e018f834b641dd6de8cea6d78c06ffa119c5 \ - --hash=sha256:93db40c8de0815baab96a06e08a984bac71f989d13bab789e382158c5d426797 \ - --hash=sha256:9724e6cb5e478cd7d8cabf027826178739cb18cf0e117d0e32814d479fa02276 \ - --hash=sha256:98c6ac18480fcdb33f35439183f1d2e79760ab41930309c6d951cb1f8e46694c \ - --hash=sha256:9a15e785f244f3e07847b984ce8773fc3da10a9f3c131cc49a4c5b4d672b4547 \ - --hash=sha256:9b60d7814174f059e5de4ab98271cc5ba9259cfea55273a81544dceea32dc8d9 \ - --hash=sha256:9be2a6647740dd3cca6acb24e87f03d7632cd280dbce9bbe40c26353a215a45d \ - --hash=sha256:9c7472192ebfad53a6be7c4a8bfb2d64b81c0e93a1fc8c57e1dd0b638297b5d1 \ - --hash=sha256:9dce8ec9695f531a1b8a6f314fd4b393adcccf2ea861db480cdf97a301d01a68 \ - --hash=sha256:9e50d748a32da622f256e8d505867f5d3c43a837c6a9f0efb149655fadd1042a \ - --hash=sha256:a81758ed242b861b72e778ba34d41366441a2e10b16b472784c88da2dea7e2dd \ - --hash=sha256:ac777001cdfc28b72477d93c8564bb7583081ea8fb45cdca3d568e0a4f87183c \ - --hash=sha256:b2b506b1788df5fecd270a10d5e70a95fe77b87ea2b370a318043f6f5f817ee6 \ - --hash=sha256:b2ea4a3e8357be8849e833beeae757ac3c7a6b3fc055c03c808a53c91ad30d82 \ - --hash=sha256:bf1516fe58fc104f39b2d1dbe2d5e27d0cd45c4be2e42ba6ee0cc763701ec3c7 \ - --hash=sha256:c0d702548d89d572b2929879bc883bb7a4c4709efafe4512cadee56c55c9bd15 \ - --hash=sha256:c10b82c2634df08dfb13b1f04e38fe310d086ee092f4f69c0c8da234251e556e \ - --hash=sha256:c42572142ed0b9d5d261ba727157c426510da78e20828b66bbb855098b8a4e38 \ - --hash=sha256:c4585c3e64b4f9e583b4d2683f18f5d5d872b3d71dcf24594b74ecc23602fa96 \ - --hash=sha256:c639ea314df70a7b2811e8020448c75af8c9445f5a60f8a4ced81c306a9380c2 \ - --hash=sha256:c670fe7be5b6020b76bc6e8d2196074657e1327595bca93a389e1a76ab130ad8 \ - --hash=sha256:cc1b2440423a851fad781309dd87843868f4f66a6bcd1ddb9225cf4ec2c84732 \ - --hash=sha256:cd3584591ea4429026cdb931b054342c2bcf189b44ff367f8d5c15bc092a2966 \ - --hash=sha256:d15df07081d91b76ff20d43f94592ee110330152d617b730fdbe5ef9fb680053 \ - --hash=sha256:d19662dbedbe783d323196312d38f5ba53cf56296378252171985da6899887d3 \ - --hash=sha256:d24ecb4f5e009ea0bd275ee37ad9953b32005e2e5e60f8bbae16da0dbbf0d3a0 \ - --hash=sha256:d446c6ac40bb6e05025ccee55b84d80fe9bf8e93010ffc4bb9484f13d498835f \ - --hash=sha256:d51ffd3427640fa2da6ade574ceba932f210ad095f65fcc450a2b0a0d454868e \ - --hash=sha256:d6ce43a0269d68cee79a7d1ade7def53c20f8f2a047b92d7b5d5bcc73ae88327 \ - --hash=sha256:d721e53758b2cca74990185eb0671dd466d7a388a1a45d0c6f4c13cef41a68ac \ - --hash=sha256:d7da47a0f248977f08e2cb659ff3c17ddc13a4d39b3a7baa0a81bf5b415430f6 \ - --hash=sha256:db47b561c9afd884baa1f96f797c9ca369872c4b65912bc691cfa99e68340af2 \ - --hash=sha256:dbe6493fbd27321b1d1f2dd4f5c7e5bd4d8b1d7cab7f32fd67db3d0b2ed8248a \ - --hash=sha256:dbece16025afda5e3031af0c4059207e61dcf73ef13af844964f57f387d1c435 \ - --hash=sha256:ddd67571c10869f65a5d7dde536d1e066e306cc90de57d7de4d5f34802428bb5 \ - --hash=sha256:de9208bb427130c82a5dbfd104f92c8876fc9559278c880b3002755bbbe9c83d \ - --hash=sha256:e30d40268a28d54ce0437031750497004c22602b8e3ab891f759b795a003b312 \ - --hash=sha256:e8b0abe7d870f53ca5143895fef7d1041a0c831a140d3dc2c760dd7ba25d4a8b \ - --hash=sha256:f035d9dc1d25eff9d361456572231c7d27b5ccd473ca7dc0adfce732bd006d40 \ - --hash=sha256:f04b9f56b0e0614c0126be12c2c2d9f8850c1e57af302bd0a63bed379d4af974 \ - --hash=sha256:f0fa4fa9c3632d708742baf2282f2055c11d888a790362670a403cbf48a2c404 \ - --hash=sha256:f2e7f8e2ab6c2922be02c7ec45185aa5bd771e2e57b95455ee343a44d8130dff \ - --hash=sha256:f8f6fa298bb4f7f58a33334406218ba74716e68feddf5e4e54cd5d8082705abf \ - --hash=sha256:fbf300e2070bb35038660b3be1be4b91b0024edb41517e6996320b49b92b4175 \ - --hash=sha256:fce7760bf283405b2c7999cab3da4e72f7deca6396013115e3f7a955db9760da \ - --hash=sha256:fcee38cd8e5089d6d4f048ba1233b3ad76e5954f545382180889112ff5cb712d \ - --hash=sha256:fe31f28c94402043161876a258a9c6f757cb485905c7614ce8d6cd40e6b7bdc1 \ - --hash=sha256:ffd8893ccc1c2fce6e0d6ca402d716fe1b29db70c7132609a05955e31b2aa8f2 +regex==2026.9.3 \ + --hash=sha256:01bdce6a372efd5ae3d8560cedbc691be259a50206564bbaf04008b2937721ab \ + --hash=sha256:0540de6e7917f89acaf9771bdcd6fa7e505c67416d3b283e52ad4ab25399c6d6 \ + --hash=sha256:05e9f7d16b42686fb38b1702071a7359469ba89e9d516e2ba5228e077dcac524 \ + --hash=sha256:0b1ba3aaaf5776de473ee16625ac60ac195abb0343afb273575a8201d99be089 \ + --hash=sha256:0edd12c8201222f58817689dc61fe44893f3f2f2aee530b211dfa92af84df9cc \ + --hash=sha256:0ee4721472e00e96b3cceec545c9867f91815f628f6ea304ae6cea93a7e4e7ae \ + --hash=sha256:0efb99024ad5ba9198ffa816156b3319c3164b5a8d1e35940d92fdc9158b8d9f \ + --hash=sha256:107319f437fc382e1e445d0abf8923db07f76e5fb3245ac5b09604e5dc8d4f63 \ + --hash=sha256:11f9b104fa7b23f736b50fe1980a1422eb4641865e82614a25413deabccae575 \ + --hash=sha256:14bf4a88833c12a990dbf5cca77eb293401d60878be7d566a2d5096fb0216c27 \ + --hash=sha256:14f8969a92ed847f78e8eed81c6a0384a9e8058a56eb659f83ddb59879c2e4e3 \ + --hash=sha256:185c1ae881856208dda05708b6c908aff76878e59c998c8548d365c1bbcaf1bd \ + --hash=sha256:1ba1dbbb93c5c5629c1861763aec5bfa9f05ad24ef450694130e25029ce7bc36 \ + --hash=sha256:1d9148e47cfa1a067138867996b1d5d825de0132fed8dac3c92eebaaf312d280 \ + --hash=sha256:1f22e0d21ae7016c77175c139a7fca465b988efc1280df4816c79752068d9e2e \ + --hash=sha256:1f975a75dae06e88665e4a709873d936a7e8f9445e3d354b75de0e735d28cf71 \ + --hash=sha256:233662cf8cfdfe3c0e58aa8f7bbefc579b5be0ac34546f123c159804179e8687 \ + --hash=sha256:24b4bbb65ff2c4e8c552c93c342bf5ffa0ad162d963d96bac7c1883c20e1b34e \ + --hash=sha256:27f0809798071f56fb1bc536bb93714a95e8ed2ec0dfd869f095deebb30fd11a \ + --hash=sha256:294ef8fb58a45f912513692380f366ca191940de5b3b3de5308ce2e8500d8ea4 \ + --hash=sha256:2a1269856278ae8bc78342bf20191c1f10638d2c22df72364d2fa02d70d49f35 \ + --hash=sha256:2a36e654181ecb996241bae256d28f26b03d019d6ef65c61dcf44396ab07c548 \ + --hash=sha256:2c71da070224baf426850d9eab23ac797d9859b1841dbda43012c01b69697803 \ + --hash=sha256:2cbc83154c8b0201ada07bc5d6e37106df6325f2fda60d73228e2e8da7433cae \ + --hash=sha256:2d25e41851e41539898116ac760fcc856e2c1047a843a336b19e2c8e4a43ad16 \ + --hash=sha256:2ef9a284ec658d48ec57edfba0944d1582532601472f695b799ba08d37fd6544 \ + --hash=sha256:3077ace9bf59f8513c8471a817a5af63699987dc024f535c1eac3447a4d70211 \ + --hash=sha256:307dbd844f678de48ea74cdc909b007729c50e6be7f182bdf6a1df6732374c69 \ + --hash=sha256:32a32d2664e602b20e4b9c11234cb32f2e985323e453b2b396ea4ae686d82052 \ + --hash=sha256:3367d5eefae493ac2a1586ec11cc8213c3305528ed3f8e19d5c3871cc01da6ce \ + --hash=sha256:33c2860b73ea342c0a42bee9ebe3b3a0de3d68c580c4dcb52241cf4b6663731b \ + --hash=sha256:33d3a772ff62c882a5d1402045e17c0199f33b9b173139071c4203e7e0292420 \ + --hash=sha256:356fc21b4c313decb3214a4306bb5bd0623dce4a8d03d0d5ada6fb0b6a5b93b5 \ + --hash=sha256:35d48ce3dee087b63b15cd0a7a3110d0a76c29edbe1f2ad0520b8c4adb7cb596 \ + --hash=sha256:39d8d5490ba4b8f26ef2aef72b775b1e7fc1a5ebaf28d11e637eb27b0b6a3048 \ + --hash=sha256:3cc1a82779315f7b2a4642d5028b39057838cd4c0415744d4e53c8b512352141 \ + --hash=sha256:3d75065d9f6ed1afb2a41588def408cf442cee65b3651cbd7e86650146127bb4 \ + --hash=sha256:3de4eab8c763393b75bbb26f81934ab2cc8794f48f79e90622e3ab7ea57f3d14 \ + --hash=sha256:445623b1337e971ccc571d3642aeb3f2fec77e60b6ee193dd7688168471d1846 \ + --hash=sha256:48e84252a16234ee860206a738f9a5084f830a5d0a1370a418d3af4e917f5e08 \ + --hash=sha256:490a81770599b17b8594227674872dfb215af05f2be0065a32a7c70175fd9e23 \ + --hash=sha256:4a9cd8485a6729387c889c88c24d5eace39dc0c0c9ddb9003f9c81751f654b69 \ + --hash=sha256:4ac52b95a938789fcfcfbf6faf647b45d7be940f0c51f06991c1353db54c67aa \ + --hash=sha256:4f39485bb02dae23e14cdbad086ab0e468756775bc5c64bb69e7cb756ebc1dcd \ + --hash=sha256:5133884ec10c9d6bcf7fed4ceb98fb3a6acbb6c2ba1bab6c4b6700d6c39c7595 \ + --hash=sha256:55493b5b6cb6c4ec9a3c310d4ef947dbd34326ee4eecd4249e18e00d84f14be5 \ + --hash=sha256:56d8659c65166641d8f1b5efccc391c62c8a899eff4d528b981cc62b7b402a4b \ + --hash=sha256:57f405fcb82e2b78df88f04f8aa49ce513ce23bfea073b581597bd52545e9b3f \ + --hash=sha256:5db80d0b1c8238940b5957dd66b5c818ea40a221f6652fb717c027a562d09c77 \ + --hash=sha256:5dd356a646fe549d42b766cb9075b54eccd3f20604d87a3eff25f1430bba5b2e \ + --hash=sha256:5e49096c90364317897b8d9fe97e2f86063dc0fc0bcd352017ea6fdd703ff25d \ + --hash=sha256:5e674cecb61cb160be392da07fd8a71509ef927f437fbf3215432692ed385151 \ + --hash=sha256:5fecba510f6b8f9cf1dfd103d785a61da47221cf09d4cec10946d1950daf1a17 \ + --hash=sha256:610371fe95c7e8e824ad8762248836cabbb5a4ab8befb982cb7dc8df773075d3 \ + --hash=sha256:61a9da95a836e7d300945891265bbeb6510a860863f6b01ed6397e6239a31253 \ + --hash=sha256:635482cd183a1856da75a39c473a2e222697b7927f1955f586db83fc8a5da17c \ + --hash=sha256:63fa79eab192623acb169de1dfe8e733598c4047d06f7712347d1bb810a5ad20 \ + --hash=sha256:647983d2609be6155c748249e770ab7e75e15e386cbf15469569f3eaf165bbb7 \ + --hash=sha256:665207e41bacd435db001099eeab44103197c2c1a729d73ade74688a905ed4ce \ + --hash=sha256:690ab9d06cd689b79aab84afa0984a1bc85ea4b93b0a42b015f3ff5e2f5b08be \ + --hash=sha256:6b5dc780377e35be6b0cf6fec7fb4a45cadb1e834bc0ef2ce596cc015290ef69 \ + --hash=sha256:6c0f60b05cc708e6cdf68dbca86b7a36d99695962db0189bb1b3884ca3b28e90 \ + --hash=sha256:6c997a1703401089bc02d731e360127428fb4ecdf6524268e8975882ff02528b \ + --hash=sha256:6d416ed4058ea38f69884f3db523b0414c0094d262dde63694aa254e32a433e6 \ + --hash=sha256:6f198b622a3ccf02eeab00de71f6b2f45b1b50b1979aa56b25178c963e475950 \ + --hash=sha256:6f64c66b3b13758b4f8f56f17972cd0ce5d0033d19d7332ed32e2dbdbce94dec \ + --hash=sha256:6fe39780de6916ecb1c664eda81802e6310fd9d4a07dccb13a86b63918e00e65 \ + --hash=sha256:6ff5a98456194621f757d6226f88035c00ebbeab0a9095dc9b5a9ec5cc94b50b \ + --hash=sha256:70082b2a8f099b8bf660b553b22a4b8ffd34fcc09ef154fc6b9c7108518fc124 \ + --hash=sha256:72df83ee0eb89b070e28d1260786d13485b98a8b80228c7439d303dd9aac9970 \ + --hash=sha256:7a7ddc9a8ca1795166a1ca80364b8ce74187fc210e112d3fb048b711b934f36c \ + --hash=sha256:7ab7ad8b557d1f21e8b4a97a2c1a2ac82cf10c92d6a875c90f038ca9ed85dc5b \ + --hash=sha256:7bb75921a4885d30d9e881d7a595ce50407a8a82933e15451ad7e0d89d1a5944 \ + --hash=sha256:7d2eed2e4d231278a2ccab3f4bfa2c1e39855f336475f7756a281d767d2b1753 \ + --hash=sha256:82c27460ff2ea683159a204db2f9ce0f9549dec7070aa0809fbc36226bac1816 \ + --hash=sha256:837c1859913798d8bebcd98d4a037e113f8d79e81733009bf590e449769eecb3 \ + --hash=sha256:862c29f7e7927e71391df6700079b1dd7c2aeb75115dc46e37004a092f8f16b7 \ + --hash=sha256:8c8a63d55cdf3c716225a2f8741a1df7a52b5fa98ac7530165c9cc9b32feabcc \ + --hash=sha256:90aa2a7f9cc1cd2e8082db61618a2ed0f4197eef52266a6420e88277f184e328 \ + --hash=sha256:92e06a5ecabd6e8352da58b9201ae241a1e7382c1419defa9d2f64ad4ea6abf2 \ + --hash=sha256:98620c9c4c22568ad70f57b80527c780b6f8fd26e36507bf8e2273262a228275 \ + --hash=sha256:9887e9455398a1517294dec14e23ed9a178c8dd909f2788e971b66623d3f7c16 \ + --hash=sha256:99034ec353c973e2c89555866083491b9a2dbe81f2fbe15fb0f2b68506232f01 \ + --hash=sha256:994fa00a9b0d14c6e6926ff5ade98d4d83676a5eeab6f87718170e481396d380 \ + --hash=sha256:99896cc18fb421be93e337d6bf2c1686ba330bc2d5c0ef581c842f0639a5e886 \ + --hash=sha256:9c407afee6ea4caa313e815814e76d8447fcd32f81e3331d7937c2d33ab80c1b \ + --hash=sha256:9ce34fc5a6f6c9b2ba4a8060009d989e4f55630e3f9c3bf5962f42dcc31355ef \ + --hash=sha256:9e1c602c55dc8ec05cc7e0a8e31f3ad3d4644dbcb7ac39114105c9bd0384371f \ + --hash=sha256:9e61478a8a06e6456ff2e66b9ac18f7f28d76a75fa5fda0c5198a4de07b8dcb8 \ + --hash=sha256:a87ab35e92d40b53c5373af36625794e422f83ec56122f0a63871892856d721c \ + --hash=sha256:aabd43208e335f4c3f0b56de3464b066dd425983a58f6eeb5738bcd7465403db \ + --hash=sha256:ad2027883344e70ddddff02259411f80faf47d5e779eb0e39cb95ee546fa628c \ + --hash=sha256:ae9f7055e7357b2873866a3d77d0136a251ca2ae2dde3d8cdb819425d717c177 \ + --hash=sha256:af06c9099df15ee44fda3fdfa002bfe37de02901c2b3a5ef350853861ef3b4b5 \ + --hash=sha256:b5f85bffdfe17da7dfff78eb32b261c27f3cd64c060033645079493f4cebc8e9 \ + --hash=sha256:b7b7e6be82fd6d5256adabb82253c5c307de981cc20c0ce4cff0cbe6de88529b \ + --hash=sha256:b9190d4901d7786af9ab0ec46172e27cf7d72cdba2b82ee38eb40aadd3239a6e \ + --hash=sha256:bb2d4ad7f9bac398a7a19f07bd09fd5b2c1e4eeab316065aa84a305f62fc5361 \ + --hash=sha256:bda6af6fb4d5fe9532620e4f72d3c7efcdf7472ead9037b184c0a060f0e7c71f \ + --hash=sha256:c07eaf30bc072b179acc8ac50519a2a81f03f88a220750c6d83dadbdc33fb1de \ + --hash=sha256:c0ea77435b1d5a27cccf27f8762f50e73fd2d94e8e412a8e5cdedb650b36d5fc \ + --hash=sha256:c1fa3f84cee5211a3e574ba76ac9596df8c8d16a855f31a25681d23eadcc6c16 \ + --hash=sha256:c90f34f1b1905d7d6c42b25b6ffb4e5066e6c95c7715169e3332b1760614d092 \ + --hash=sha256:cb6374a84f11a6b25e63aa69ee2d015286048c1efee93c4a3b5b8df62da79ff8 \ + --hash=sha256:cc5d0f82cf05beb6c0d463398173a09357ed4f4a631f7641cef6f6480a05bc57 \ + --hash=sha256:cce8e5243d95068f595155663e3e18b1938b16cf716dce6cf2491ebc08b98d96 \ + --hash=sha256:cda393bb35828e3993fcaf39c8ede78b16614954eb15fe77336d5d141be40f76 \ + --hash=sha256:ce6505846d29f860966e0e9cfadaad1041a7d8ce7dc4af39940fcb1877dec29a \ + --hash=sha256:d10a442c6450ebd35aa89392b8e4b0459ba474df63fc5e6828573d0a31a627ec \ + --hash=sha256:d1d52739de118acf82bfbaf7046955ead4fb613d24ba4187be565cd91ff9a64e \ + --hash=sha256:d36e9097a1bc6eebe8858216ccd2326f561662de4e50c27533452491c1cf1685 \ + --hash=sha256:d514026ca1c473cc14440e4d7bdf6721c642455b647a74c8143c0f22da358c28 \ + --hash=sha256:d539a51be176e874ed66029b2df8cb8e1123a3e6420d52a690de6effded4f20d \ + --hash=sha256:d7b3a8a4bbd83ad8b29758f5d24bab10a3f2de87970db36f1e3651c733353136 \ + --hash=sha256:d963442186918577ad83e3a8c5564eeeba90e2da233ce59f6eeccbb7b5cf771e \ + --hash=sha256:db6538d733047f9ce4b74ee29c77643a1f99e4ca36e273495da93fbeedd2f03f \ + --hash=sha256:e27003c0a93a5aa541c260bd8ba8b917a2af4c378eb684daa9f1565f5e363181 \ + --hash=sha256:e3037d02425863ce9501afbaa04ba967162810004bacde39a53ea9a5b740eb32 \ + --hash=sha256:e337dceb936f333775cf51d49f6badb8cd3d2a6b27e8cb443a6c5861fbf3c1f9 \ + --hash=sha256:e33dfc13c02d9c4e55bcf3f3b2eb448537823a6f6f30bf737b2974b63a530bc9 \ + --hash=sha256:e7663a6803a47255c32cc7e17ae3ccfe02dba5b0849f87729651c0263a129d20 \ + --hash=sha256:ecc27adda0d1e1bc39793b41fdd562d2f4bc4dcee6ee0e3c733d519c332183b2 \ + --hash=sha256:edfd2b0cad175780f8668fd6f66486354b8770e4057e44038daaa4a066f8ddff \ + --hash=sha256:f02091b425bbcc2d8481913855c744baa4dd73e334814337b201d837e9040ef7 \ + --hash=sha256:f4381151a29a7b9307842ff444609c4b9a402775fbe9afb3ec3e34ec0396bbae \ + --hash=sha256:f568bdc17b7ebb3a323ee8920468d2ed74af84da911a00d9585ac887bac73b88 \ + --hash=sha256:f584dd93ef6ddb10ba028e247fe1fc0ba52ed70ca4d596bb8d52bf4304c147ec \ + --hash=sha256:f5e8a0ce681ddabf6a35d7b817d74a94ab237ae7233a5b97118db0b4e524473f \ + --hash=sha256:f93c60d8c522b4ecea35dd6c58cc42f251ecb12882a5d67d4bd8d12137fbd05b \ + --hash=sha256:fbaf76379bf2a72e534bbb1276d45e63a80d8cade17953ed79a350d6426262fb # via + # instructor # tiktoken # transformers requests==2.34.2 \ @@ -5488,15 +5779,18 @@ requests==2.34.2 \ # datazets # docling-slim # fastembed + # google-adk # google-api-core # google-auth # google-cloud-storage # google-genai # instructor # jupyterlab-server + # langsmith # pooch # pymilvus # rapidocr + # requests-toolbelt # spacy # tiktoken requests-toolbelt==1.0.0 \ @@ -5659,9 +5953,7 @@ rtree==1.4.1 \ --hash=sha256:efa8c4496e31e9ad58ff6c7df89abceac7022d906cb64a3e18e4fceae6b77f65 \ --hash=sha256:efe125f416fd27150197ab8521158662943a40f87acab8028a1aac4ad667a489 \ --hash=sha256:f155bc8d6bac9dcd383481dee8c130947a4866db1d16cb6dff442329a038a0dc - # via - # docling-ibm-models - # docling-slim + # via docling-slim s3transfer==0.19.2 \ --hash=sha256:ba0309fd86be3c27dbf78cdd813c13c5e1df16e5874b99d2535ebbdfb9892993 \ --hash=sha256:d8168eccca828cbb2cd573675333f3bddd254313a9c42494b84c76b539e8ba25 @@ -5799,6 +6091,7 @@ scipy==1.17.1 \ # colourmap # distfit # docling-slim + # formulaic # gensim # hdbscan # librosa @@ -5822,9 +6115,9 @@ send2trash==2.1.0 \ --hash=sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c \ --hash=sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459 # via jupyter-server -sentence-transformers==5.7.0 \ - --hash=sha256:b78141da3d8137e70d965866e2ca43190b9266f3d4d8752e250ded75e7136730 \ - --hash=sha256:fd8c8fc35e6323631dff9f3760969ebf7980dc3cfda0ab1354bc6a774cc0e5d8 +sentence-transformers==6.0.1 \ + --hash=sha256:1c3b8d9403f87ad0c879638554f36cc85744f38a6a70d150fd7e964ca0e9935b \ + --hash=sha256:b8888d72c707ba33c63aa30845850702dd5acadf1dd0d051436380bcebe4fd0f # via # semantica (pyproject.toml) # bertopic @@ -5922,6 +6215,7 @@ sniffio==1.3.1 \ # anthropic # google-genai # groq + # langsmith # openai soundfile==0.14.0 \ --hash=sha256:0a6ae43c50c71b4e020cc55382925cb89451c1ed1a0c3d0f5d802da269226849 \ @@ -5966,37 +6260,47 @@ soxr==1.1.0 \ --hash=sha256:f4977323ef9c3aa3c2a26ff5fe0191c84b8fd759daf7afb1f25a91a55ad8b730 \ --hash=sha256:feebcba99ac99adb8009d46c8f4c1956b8c167576b0ae8a6fb47502e9a6f78e7 # via librosa -spacy==3.8.15 \ - --hash=sha256:0262f86956e751ace6e47e530cf9841d66f2354d8cd91cfeeb39eee4c5f2962f \ - --hash=sha256:1c8a27500b409b472a743c2b0b2af2dff42f4287ace36f2b776e4592d65ab493 \ - --hash=sha256:2cbd33b0801ed0fed71454cdfaefbabb18b1b854471f7683c5df2f78be00e8cc \ - --hash=sha256:35060be85c952df84e9095713ba05f8515473e127ad0c6b43cd22a1b42d4cdbc \ - --hash=sha256:37f370f579c1bb56aa767e6d585672133f37e89c1181ebbd251fd946777f0ef5 \ - --hash=sha256:39ac175bbf8a8381c41b8e0abbdede3ff29d1f29f3cac42644f5719671e20884 \ - --hash=sha256:4031de613e8ba666392fef107a06b5144270f0f64b6a61df37faf6a329d61b5a \ - --hash=sha256:59c1ad50c8d0afe8397a06e28f39f3cebced0cdf53b14607dfca44c9822d7650 \ - --hash=sha256:6b27d0abf1138644837536705578dc1ea48a693796b283a49202e1dbde0a3b02 \ - --hash=sha256:7863c35506ec6f7e3fc13836330a4badd723514b9dc067ed79a8b0c593b824b4 \ - --hash=sha256:81ca434f0a08062fe5d5fd05858196b3c007c755d8b54e2019c071949a93eefe \ - --hash=sha256:8397a6e76b85d7a1d42b2654ed0bbad053e9100b80f41efcf608a8cf02df76f6 \ - --hash=sha256:9ad71e9ce6c4e1b984a91bc82b2e4e08df23581f5cc670bbacf29a07a9822d5a \ - --hash=sha256:9b22d269dfa6aa3c6a000e576b261bee46c277afaf915a3fe1e0a81ad227ee7e \ - --hash=sha256:aaa70356876c152f0235ff5bc4869f7a45133392ff73aa881113376f7eec0caa \ - --hash=sha256:b4527b7824e8228f2abed18774b224de7aad9b900d47ff66483abdf16d0c8a5e \ - --hash=sha256:c8b187654941e417c4cc0378ed7860bf6aad7bbbc370b925994a9cebeb8ca615 \ - --hash=sha256:c9279132fee6e131b295f5336b8f9e46c16e5ec430e6c58a56fc9ef134cc1f5a \ - --hash=sha256:c9683078efb96dee8b1a751bc0bfa9271a6604b7257547849b4254f285ce77d9 \ - --hash=sha256:cb0782680cf930e9ab984a73b2078c981343f77c5c407773c704cbf8c7df13c0 \ - --hash=sha256:ce66d75279ee84b749eea058ff3ea79a31adf0ff53f887943dced258ceb6ce2a \ - --hash=sha256:d41166f5f763ff3a3e085d4314b6cf34cf1cd2bf0845aaafa1acc266e9a46ef7 \ - --hash=sha256:d5806db3034618ef426e360dd8519283dc08f5be1690ce5bc3bb2c628f86215f \ - --hash=sha256:e1aaf79b42c0e8c5dd4801b474c22a61fc68edb347fd3a6e09d5cba7e1aed5db \ - --hash=sha256:e7ca79280762f1de0a7a5c1ce8ccfe1f54b772e8761ea7d9408e535427592749 \ - --hash=sha256:f04bc083600ff688fe500a736dd4d8ac3d06527f7808c2d7932d4174d8f3b751 \ - --hash=sha256:f1c0f054365fdfd95cfe96acbb4c5c1dbefb787905e4e63013eda54d3c815050 \ - --hash=sha256:f5714a76826756cea2257d35524233ebe49c6f70b6510b224e46036a4a341a79 \ - --hash=sha256:fa9df68fc8887c0a6440b84d1d307980e594d99b45f19a37d733e58caa9a6682 \ - --hash=sha256:ff1a616862d6c07a9e7ae13b911005f89c064ce884ec31784f8032adb3f86829 +spacy==3.8.16 \ + --hash=sha256:024ce6408ea00c7f8c6387a6de65bb67aad40c51c9d63705303c5cb9a8ef51b0 \ + --hash=sha256:04fd0206c9f33a0542a40049211528742b5492ef5f971d06b151b9cc49b9237b \ + --hash=sha256:111d817b32755d869e5ed6cc258b55c50c6687f47b78f6ebb2c14b1ce5ee707c \ + --hash=sha256:15908539b375bd8e3c627a076dac793b8fd790c5945f3a696837dd70776a4fc0 \ + --hash=sha256:2810fd2ce41f6a8dde62642dad0d11fd9db7cb6a1cd40b1c8b70a01586e6ff9f \ + --hash=sha256:32fe82bfbe6711a4687e427c8eec44af7cd5e09322a252b35bc8166aa84b9025 \ + --hash=sha256:39304dd9800065c09fa440983ac75cf444469b159959171c0e0d761b3e854d62 \ + --hash=sha256:397a80c4d09a6237eebaaee00a2e5f8732ff4cc165f94679b899f30179d38ea8 \ + --hash=sha256:44a641085abbe3a09ea56a89f2e50b5f51aea6cf69213b70305fb48e341b883b \ + --hash=sha256:49fe8d6a6cf343777caf59a3d4ba76f81cee0dfc1abe78be571cdc2db0d77672 \ + --hash=sha256:4e91c4f9a320d964a27ffae1b62e37bb6e8c391aa837890a082d82bd9a89ed43 \ + --hash=sha256:5991c334e71c23b798c25e0d403295dde4d2d1fb58c2e075450b964db03c05ea \ + --hash=sha256:5e32a51b115674d3f42c6cde696c583dc1594f5bff6b430de4aba4c753d47f93 \ + --hash=sha256:6a1523ce0a1358936fa1abdc3a43f2ec558d4166ac5c2f246b33fd88e3f238aa \ + --hash=sha256:6c51eac85344784ca7b184f0c3f7da0fca47c354d63e05e733d90cae35a2ecc4 \ + --hash=sha256:770cc0581fc06c0723cb1488dc1d1da0570801168da786674626f342d903ed37 \ + --hash=sha256:81fe468596678c7bf717650b12352c8e4d174c72d050dcc01c8ee3c6c781ccad \ + --hash=sha256:86227a0a0d3dfee3f3dcc15f73c1387b586d77b075348afc250ffafea88ffcec \ + --hash=sha256:8681eb07f6b0e48fb3ba4a5f7f2582c0fc6df991d240b431b628131029c4ade3 \ + --hash=sha256:8a8a2bf3eb3486a0992176b77ac1d38ca9c669623941fdb8d3dddacb44dfd28e \ + --hash=sha256:8d0f63c3124d0a34a37e9b519d004cee1269744be07f91f843902e6c9f3e557a \ + --hash=sha256:97bb04bd81a3690c45dfd62d4bd19584aa544bb955c8b497fb488256d10ac54c \ + --hash=sha256:9e89bebe168ec8714b21f0225950f0525d0ef87109cfcb11e5d18ebb1f4e658a \ + --hash=sha256:a237491463e351755f0167546f6a821d42971c5275a219a62d468f19f654138b \ + --hash=sha256:a2c46c35467d963a62dc0407c99d3a18562d4c85ee887a57e4a07dde020f1a38 \ + --hash=sha256:a3d19da23637cc396b42d22fc33852680f675d8ddcb847d3e2a0d094712d1794 \ + --hash=sha256:b022ebde7465334c0631f74e0cd21dff257ee5f80ca68918c56fd64681b49463 \ + --hash=sha256:b741266d901222dde979a802d5e9f3cf3d9bf77a15be3137b387f62905a74d57 \ + --hash=sha256:bc4e59799dcb0eb4823e5946515d3ca0d503ff78ef2502172d4b59ee0fc567ec \ + --hash=sha256:cb07d4b8255be6b6ff3a34ada93173176d0937d2023b35302a34eed3364681c0 \ + --hash=sha256:cc5a850ba2ce371ac13893ef4153a7f3cf0d7fee8ca4ddea21fac2d2628d7ea0 \ + --hash=sha256:cc7e449aec9a313bc037ef5ea45fb0ac99135d92dace8421d68414d16be39543 \ + --hash=sha256:ccd74917536fa82896f31c66db5f230301ec2662b7ad3c0d8c3eb790b0fc6121 \ + --hash=sha256:ce120d4077050352f344b987354be3e3fddb207436b537cf91a891837657ce2c \ + --hash=sha256:dc17227717aa254b63c90161d8de4ec672ca5bd8e5c92effba2a8510498ee355 \ + --hash=sha256:e045765035e9760f38637101f41a7c89d3b69659dcc70e716bb4011a95969ac9 \ + --hash=sha256:e67052bebdeba53847d3d09058f814708d587c4fb2216c65239964a341da2280 \ + --hash=sha256:f08555e5204da7d9c3f8440d9d88dc3011edcfc1fd21689069091da1973c883b \ + --hash=sha256:f42f257404e749d9048d3b3b97004692210057d38e03c2f156817258bf6daf2b \ + --hash=sha256:f765cb6cbef82b5d98c46936a1385e87fb05919433fbc6b953e3c093ac30f8ef # via semantica (pyproject.toml) spacy-legacy==3.0.12 \ --hash=sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f \ @@ -6082,44 +6386,66 @@ stack-data==0.6.3 \ starlette==1.6.0 \ --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c \ --hash=sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b - # via fastapi -statsmodels==0.14.6 \ - --hash=sha256:00781869991f8f02ad3610da6627fd26ebe262210287beb59761982a8fa88cae \ - --hash=sha256:0444e88557df735eda7db330806fe09d51c9f888bb1f5906cb3a61fb1a3ed4a8 \ - --hash=sha256:06eec42d682fdb09fe5d70a05930857efb141754ec5a5056a03304c1b5e32fd9 \ - --hash=sha256:0f52ef0f0b63b8fd11e1ef1c2a1e73a410720b8715c9a83a26d733b6815597fe \ - --hash=sha256:109012088b3e370080846ab053c76d125268631410142daad2f8c10770e8e8d9 \ - --hash=sha256:151b73e29f01fe619dbce7f66d61a356e9d1fe5e906529b78807df9189c37721 \ - --hash=sha256:19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d \ - --hash=sha256:26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c \ - --hash=sha256:2738a00fca51196f5a7d44b06970ace6b8b30289839e4808d656f8a98e35faa7 \ - --hash=sha256:3414e40c073d725007a6603a18247ab7af3467e1af4a5e5a24e4c27bc26673b4 \ - --hash=sha256:341fa68a7403e10a95c7b6e41134b0da3a7b835ecff1eb266294408535a06eb6 \ - --hash=sha256:3bef39f8587754f2d644b2e831e102fa08ace9a5a1af4b583b122e6fd3e083ab \ - --hash=sha256:47ee7af083623d2091954fa71c7549b8443168f41b7c5dce66510274c50fd73e \ - --hash=sha256:4d0c1b0f9f6915619e2a0d3853e5763d4d66876892ad352e7d7b93a737556978 \ - --hash=sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a \ - --hash=sha256:6ad5c2810fc6c684254a7792bf1cbaf1606cdee2a253f8bd259c43135d87cfb4 \ - --hash=sha256:730f3297b26749b216a06e4327fe0be59b8d05f7d594fb6caff4287b69654589 \ - --hash=sha256:73f305fbf31607b35ce919fae636ab8b80d175328ed38fdc6f354e813b86ee37 \ - --hash=sha256:8021271a79f35b842c02a1794465a651a9d06ec2080f76ebc3b7adce77d08233 \ - --hash=sha256:81e7dcc5e9587f2567e52deaff5220b175bf2f648951549eae5fc9383b62bc37 \ - --hash=sha256:89ee7d595f5939cc20bf946faedcb5137d975f03ae080f300ebb4398f16a5bd4 \ - --hash=sha256:9e0fc891d6358bf376cc0ae1fee10a650478172ae9ba359daba1785fc496cd1a \ - --hash=sha256:9e8d2e519852adb1b420e018f5ac6e6684b2b877478adf7fda2cfdb58f5acb5d \ - --hash=sha256:a3764ba8195c9baf0925a96da0743ff218067a269f01d155ca3558deed2658ca \ - --hash=sha256:a518d3f9889ef920116f9fa56d0338069e110f823926356946dae83bc9e33e19 \ - --hash=sha256:aa60d82e29fcd0a736e86feb63a11d2380322d77a9369a54be8b0965a3985f71 \ - --hash=sha256:b328eafa86a2a67303fdb1d25677d15b70cd2a5229aabec7670ec5ea840f1375 \ - --hash=sha256:b5eb07acd115aa6208b4058211138393a7e6c2cf12b6f213ede10f658f6a714f \ - --hash=sha256:bdf1dfe2a3ca56f5529118baf33a13efed2783c528f4a36409b46bbd2d9d48eb \ - --hash=sha256:d8c00a42863e4f4733ac9d078bbfad816249c01451740e6f5053ecc7db6d6368 \ - --hash=sha256:e443e7077a6e2d3faeea72f5a92c9f12c63722686eb80bb40a0f04e4a7e267ad \ - --hash=sha256:e83a9abe653835da3b37fb6ae04b45480c1de11b3134bd40b09717192a1456ea \ - --hash=sha256:e93bd5d220f3cb6fc5fc1bffd5b094966cab8ee99f6c57c02e95710513d6ac3f \ - --hash=sha256:f1c08befa85e93acc992b72a390ddb7bd876190f1360e61d10cf43833463bc9c \ - --hash=sha256:f4ff0649a2df674c7ffb6fa1a06bffdb82a6adf09a48e90e000a15a6aaa734b0 \ - --hash=sha256:fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5 + # via + # semantica (pyproject.toml) + # fastapi + # google-adk +statsmodels==0.15.0 \ + --hash=sha256:04e65ee05475aeceb9cb954cf96c0f9361b9cdfd497b8aae47afbab0c7c786dd \ + --hash=sha256:081adf7e5f2da63f63cbd90491447de5d4c4c2921f19814f235bcf3e736edd3c \ + --hash=sha256:09fe34ac80e147cd76cb702ba672559ee3c5dd3ba15c25a232922b46e5ffe70e \ + --hash=sha256:0a42d58efadd47352f805fcf7eeb000f3399fe9cda260055a81105a922443bdd \ + --hash=sha256:0ca5d27711f171ee478d547d5a8c5424d1820a1b73c2a3d6bf63f71ad4525f71 \ + --hash=sha256:0cb6228713fe47046b606daee63ed4473a7c246fa97cf0a6d6f20d3b0c96c051 \ + --hash=sha256:0e108365c5062ba1cf568a183cfc8da551ab8b2e84dbb00e1e87a6a714dd4cb3 \ + --hash=sha256:0ffe08752f420f12a0b03e8f7d8f3a45417615309b1f37f10561f9a53f044737 \ + --hash=sha256:103a83723f998897fbb5d5d0ff728cbe3755a90d9b1f22406bf58f305faff2e7 \ + --hash=sha256:1f1443cc1ff7683f3d7a9054e6dccd92040fbd4324616a0b6dbb8af1ce7889fb \ + --hash=sha256:24b9917a09bbe0227047e512a40652c4b9117c2cfbf19c4a5b9971a8944a8606 \ + --hash=sha256:27e9467fae83ff327c9d16ae5ea7e771bcdd66a682f49aa324eeefea5584f2d5 \ + --hash=sha256:333d0c70f269ff98b647f157d2ae7fbcf0617e6f9a5b1817fc6609766502292c \ + --hash=sha256:38038823fbe86f11e09433b4a0f4af5f102327c3137a1604b002ce3cf722a31f \ + --hash=sha256:3e5f870037ca154a4d177aeac68704a784b24cf2c45d86b046fc28e37a2a5584 \ + --hash=sha256:40b2737456e75d96866943e33017a1dffc9166025ee33b7eb373fbe8c7a85e09 \ + --hash=sha256:4f90dc4989bd82837c80059fdce132976d4e5a80100119d16e1b00537eb55867 \ + --hash=sha256:511a4f9cadbf91a690bfa863bb3e25dce583d8140046886fdf35fbfabfbce401 \ + --hash=sha256:5538a734dd28efd450bb38a5d5fc94d13ee709bc497e3e4c0a24b3803e3eac39 \ + --hash=sha256:583eb2a46f7c7a5c34a26715c8b6d92199bfb4f04623db5fff9902a3c7c832d3 \ + --hash=sha256:5891ad077b36d6401e44967739df3d2cdd380a0e6e9e46dadec7e3d3a828af55 \ + --hash=sha256:5a7ab98634d47048c6b7c168b3f8f71369e256d7314bc386168526f34c7699d7 \ + --hash=sha256:5d257fe58d0772bc46a557880ca78e2a8e07fec7bfd9d11074aef8e33e1aecbc \ + --hash=sha256:5e1214312d6362cebe0b5c53b4f478d9072559709000369dfc528211cb993e8a \ + --hash=sha256:6197b4cca69fd7bc23ab9430abf1c479764cfe74767467f51964a633dfade7a5 \ + --hash=sha256:65e64afc2655a486ea44a5ffe4e2b05ee3abbcb10ea61d1c02d3e9e519338d9b \ + --hash=sha256:733887a09ff878a2e29504eda684488b26bd134ba3539ac58d2136a58914c65a \ + --hash=sha256:74c0b63448f03020cc27a8ee698fbcf2ba91e61a9f9eee7978c0c4ed33a9b17f \ + --hash=sha256:7b5561f454d6b629bb45cb505df6fdb498f960d0de111bd81f215fa47b15a72d \ + --hash=sha256:7ef37437f740620a8da03437a29b1fc21bd9a70eeb231cd0d223cc4d766adb78 \ + --hash=sha256:80b946d7c922f46f045f25e50daf13439f79c05e403ba50081cad509892c8b26 \ + --hash=sha256:88dbe345ecf317862481b3c86305f42133082e5b366a3a8cf240a6caac404daf \ + --hash=sha256:8e8deb1e3d4ab89fec2a52815f1ec924920c4e0cdb307421a4e47cfed87c03ff \ + --hash=sha256:90df413d7e09474f7e7cbf8807440c1bf76298cdd9daabf9141f76d7627c9151 \ + --hash=sha256:90e75e726be2c9fc1d1d4c43337503a35864bdf5b4693773a08c6ee92d04a153 \ + --hash=sha256:972c33d9fe6bf907b853ba0eccb2f7cf3da90fdddbee002b8478bcaccf637e2a \ + --hash=sha256:a45026596c71afa18543ec2af40b895609c181fc3649433c1cded5ce23ec5cd4 \ + --hash=sha256:ac9bce461d0c8f529f8ed331a469eaa5dd7c3213c06b62623a2e5438a6383093 \ + --hash=sha256:af80f9658c48792947fc754a026ffbebca61a8b483253781973f604a67cf1f8c \ + --hash=sha256:b67886b66d9c7ca118526accedb5c6de7ffd74c015dc0492ea0b1690192b65da \ + --hash=sha256:ba89eb3ccb1c9c1e400d5081157305495b09350b3a65f2d66d3424ad1d3c4209 \ + --hash=sha256:bcaa1e4499697c4e023e5fa6fb04459a34e4f4a449c45ae441992e933bde24db \ + --hash=sha256:c3c1138b4d0e5b0c2387b17dafd2a0fd137c67bd869f70d4ee56feecc918cb0d \ + --hash=sha256:c6e20788141df134b83ffb43b8cccf135a876188f84124835c86f011f138adee \ + --hash=sha256:c6f2bd48915cc9b3b95c3c7f6ac363b6f8aaa38172962ba5f7f6885f0dfe57f2 \ + --hash=sha256:cc37bb71f4b1e8c9b07e9ce0d32e01fbabe231d323484d35b58e93683a5123e1 \ + --hash=sha256:cf897abce5616e2763ab7d7a7fa914677df87a649971f877b83a358c98f6e291 \ + --hash=sha256:d37d574412a47a71561c0b28c5f876137a86f26fa91e889a879950e22b4e9b4f \ + --hash=sha256:d48ba61db0d13d3033330e6bd1f864dfa9b48c0b952b3b0f7570206b35d552e5 \ + --hash=sha256:dd184e911a1a8c52e5c1c27b43ccd10c24faec08053271ca1a35276cae0a3ada \ + --hash=sha256:dde0657d18e322eedb2540a84c0582e97afd0f78c370b45eebb0d4ac228a7a83 \ + --hash=sha256:eb7fc1147cd8d17132190db7a8e63cd76a06145dec502816d8f3c20619923f2b \ + --hash=sha256:ecf84918dcd410ab34d3d85d34e0b16ebb10319c9fc77dbf9a1f1a49324c59f8 \ + --hash=sha256:edd803f4606061af8c7a83e2326ea294cbfa802a908dfbda4a88347301fb6820 \ + --hash=sha256:edee066ac9b171d95c3de925225331a21de1deb5b6452f252bc853e44566e72b # via distfit structlog==26.1.0 \ --hash=sha256:e081a26d6c373e6d201eca24eede26d8ffab07f88f477822e679183428d3d91e \ @@ -6137,8 +6463,10 @@ tenacity==9.1.4 \ --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a # via + # google-adk # google-genai # instructor + # langchain-core terminado==0.18.1 \ --hash=sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0 \ --hash=sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e @@ -6191,64 +6519,71 @@ threadpoolctl==3.6.0 \ --hash=sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb \ --hash=sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e # via scikit-learn -tiktoken==0.13.0 \ - --hash=sha256:059c8ecf554eb5b41e6e054ba467b871b03277d267dee7244380aca4359747d4 \ - --hash=sha256:115c4f26ffa11caac8b54eea35c2ad38c612c20a48d35dd15d70a02ac6f51f58 \ - --hash=sha256:125bc05005e747f993a83dc67934249932d6e4209854452cd4c0b1d53fba3ba2 \ - --hash=sha256:165cf1820ea4a354985c2490a5205d4cc74661c934aca79dd0368232fff94e0f \ - --hash=sha256:2a3b536c55802fe42f4b4644d2be4f04bf788506b48de0a0a658cb58f8bce232 \ - --hash=sha256:2b920b35805cd64585a37c3dc7ce65fba4d2d36016be01e1d7942482ca29093a \ - --hash=sha256:2c397ddda233208345b01bd30f2fca79ff730e55731d0108a603f9bc57f6af3b \ - --hash=sha256:303f7d91b4fce3baddbcde05c139091d4caa5026ac7214c1dc7ff7a71ee429ff \ - --hash=sha256:32ac870a806cfb260a02d0cb70426aef02e038297f8ad50df5040bb5af360791 \ - --hash=sha256:32e0c12305105002c047b3bb1070b0dd9a73b0cb3b2856a8972b810e7a4f5881 \ - --hash=sha256:35e1ea1e0631c04f551297284a1ab7e1f65a3c55a9a48728d5e0f66b4527c04a \ - --hash=sha256:36217497eaffc158607a3b26f065300db2aefd43b115263f3b9688ce38146173 \ - --hash=sha256:3f277ebea5edd7b8bf03c6f9431e1d67d517530115572b2dc1d465326e8f88c7 \ - --hash=sha256:43cee3e5400573b2046fbf092cc7a5bc30164f9e4c95ce20714da929df48737a \ - --hash=sha256:44733b99bfd72b590cd0936b1c01b3b4dd73122db2d544bc1ceeb18a7678c910 \ - --hash=sha256:472527e9132952f2fbf77cd290658bacf003d4d5a3fabc18e5fbd407cbae4d9b \ - --hash=sha256:477c9a38e20d0ed248090509acf1e839ad3967a4f00b4b0f958210049f656dee \ - --hash=sha256:47b1df8d73390a24f94980c75158cdd5c56d256f16d55f30cb49c230caba9ba4 \ - --hash=sha256:493af3aa28a4aaf2e3d2600a2ee717252c9bf5ab38fff94eb5a02db5ab77e5ad \ - --hash=sha256:4d9980f11429ed2d737c463bb1fb78cf330caa026adf002f714aced7849a687b \ - --hash=sha256:4e2f67d27c9626cdd25fe33d9313c5cdb3d8d82da646b68d6eb8e7e9c20e6448 \ - --hash=sha256:51384448aa508e4df84c0f7c1dc3211c7f7b8096325660ee5fc82f3e11b381ce \ - --hash=sha256:5ba5fd62507a932d1241346179e3b39bc7bf7408f03c272652d93b3bedf5db24 \ - --hash=sha256:5cb65b60b9408563676d874a3a4ee573370066f0dc4e29d84e82e989c6517424 \ - --hash=sha256:5d48843bee149630eb735a99e1f4a85b47308d21868ea63163f6e87768d3cfed \ - --hash=sha256:5df5d1507bd245f1ccad4a074698240021239e455eb0bb4ced4e3d7181872154 \ - --hash=sha256:5e6358911cab4adee6712da27d65573496a4f68cf8a2b5fca6a4ad10fc5748cf \ - --hash=sha256:6644c9c2b5cf3916f5a3641d7d12fdb3f006a7b3d9ff6acdaec44e29ab1ff91e \ - --hash=sha256:6b1615f0ff71953d19729ceb18865429c185b0a23c5353f1bbca34a394bf60f7 \ - --hash=sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec \ - --hash=sha256:6eb4a5bfbc6426938026b1a334e898ac53541360d62d8c689870160cc80abd67 \ - --hash=sha256:75ab9bc99fa020a4c283424590ecd7f3afd70c1c281cb3fa3192a6c3af9f9615 \ - --hash=sha256:7ab10f4a21c2999846940113f6dbd72e0fa06a24119feddd74cc47e85818e06d \ - --hash=sha256:7bfe1849caa65d1e1d9871817170ec497bbb7984e182012e1bdce72f66608cdb \ - --hash=sha256:7d40c6c5aab171dcd6eb8455bc567bde404bb9def60cdb8c1299cc782b242bb9 \ - --hash=sha256:7de52e3f566d19b3b11bd37eea552c6c305ad74081f736882bd44d148ed4c48d \ - --hash=sha256:85b78cc3a2c3d48723ca751fa981f1fedccd54194ca0471b957364353a898b07 \ - --hash=sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41 \ - --hash=sha256:8fe806a50664e83a6ffd56cbd1e4f5dcc6cd32a3e7538f70dc38b1a271384545 \ - --hash=sha256:91c180fe255bd5a86d8316210d2833a1d4d33d026cd86a67812f4773743c8d26 \ - --hash=sha256:95097e4f89b06403976e498abf61a0ee73a7497e73fb599cb211d8197a054d91 \ - --hash=sha256:975cbd78d085d75d26b59660e262736dcaed1e35f8f142cd6291025c01d25486 \ - --hash=sha256:9b842981fa91accdffd48ff6408a977b7a91c3fbda55d353c3c68114d5c9d69e \ - --hash=sha256:9b8858b29804b3a0add25ce9e62fb00f89f621dc754d75d03ca419d17e8ddf67 \ - --hash=sha256:a116178fa7e1b4065bff05214360373a65cac22f965be7b3f73d00a0dbfe7649 \ - --hash=sha256:a2937ad042d49d50eac6e1ba07c5661d4bd3942a5b1e0c0d08475c4df83676e1 \ - --hash=sha256:b8ac2d6420ff05841a89ba5205c6d45f56c4f6843454f3c884b7eb1a2a8dddb2 \ - --hash=sha256:b967dfb9d0adf9a631953b1b40717684f04478270fc51bbccdd2f838d67a2f00 \ - --hash=sha256:c9435714c3a84c2319499de9a300c0e604449dd0799ff246458b3bb6a7f433c1 \ - --hash=sha256:ca8b310bd93b3772cb1b7922d915446864860f562bdfe4825c63a0aed3fb28cd \ - --hash=sha256:cb99cb5127449f58d0a2d5f5ccfb390d8dbdfd919c221246caaee29d8725ed51 \ - --hash=sha256:d108bc2d470fc53c8ecd24f2c0fd2b5f98c33e87cdb6aa2e9b8c5dced703d273 \ - --hash=sha256:da86f8c96ac1c235d7a3b3eebff1eacfdbcfb8ad792706943268d4d2938fbafe \ - --hash=sha256:e28157350f7ebf35008dd8e9e0fdb621f976e4230c881099c85e8cf07eaa50e2 \ - --hash=sha256:eaaaef47c2406277181d2086484c317bf7fc433e2d5d03ff94f56b0dcec87471 \ - --hash=sha256:ed5a30027cb4d8c7ca8b273d4766f3db3cf58fad9e9f3b1a68a351ffb54873d5 \ - --hash=sha256:fc1c44cd37b43fc46bae593129164f4f281e82ea116b57a85aa81bda57eafc94 +tiktoken==0.14.0 \ + --hash=sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3 \ + --hash=sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14 \ + --hash=sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890 \ + --hash=sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78 \ + --hash=sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3 \ + --hash=sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232 \ + --hash=sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e \ + --hash=sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7 \ + --hash=sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695 \ + --hash=sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea \ + --hash=sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f \ + --hash=sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06 \ + --hash=sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874 \ + --hash=sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef \ + --hash=sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d \ + --hash=sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771 \ + --hash=sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae \ + --hash=sha256:2ec16eb585332c55d022d86354e209ddf27326b1ea3477585ab248e7776d3b1f \ + --hash=sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a \ + --hash=sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010 \ + --hash=sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91 \ + --hash=sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f \ + --hash=sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6 \ + --hash=sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632 \ + --hash=sha256:447ada49af4898b5e992f0b5799d2f3af385921102c211947ce3fe960dd919da \ + --hash=sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33 \ + --hash=sha256:50a7e5646cbac2a8f7c3e8c0934ffda1a4357ee9c44b652434b23c3ed54d0900 \ + --hash=sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9 \ + --hash=sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4 \ + --hash=sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438 \ + --hash=sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871 \ + --hash=sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1 \ + --hash=sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d \ + --hash=sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0 \ + --hash=sha256:7b7acbb7a4b8383707bce22ad3c162006478c27b56368acd3e1fcb1658a80425 \ + --hash=sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6 \ + --hash=sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa \ + --hash=sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89 \ + --hash=sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36 \ + --hash=sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1 \ + --hash=sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1 \ + --hash=sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c \ + --hash=sha256:a140e83317fef02faeeb78d9a8efac623887f2feaf0055c55dcdb2b17f0226ad \ + --hash=sha256:aa428a559d5fd02ae619aacaace86c7474a1f2702d2c01fc828908dd60f20f7a \ + --hash=sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482 \ + --hash=sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79 \ + --hash=sha256:c3093001ddce822b4587e6e94bf6de36a5f97b3f31de1c9fc8d4fda144c59ff4 \ + --hash=sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c \ + --hash=sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da \ + --hash=sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58 \ + --hash=sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94 \ + --hash=sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948 \ + --hash=sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5 \ + --hash=sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4 \ + --hash=sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450 \ + --hash=sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037 \ + --hash=sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42 \ + --hash=sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49 \ + --hash=sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f \ + --hash=sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098 \ + --hash=sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b \ + --hash=sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c \ + --hash=sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513 \ + --hash=sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e # via # semantica (pyproject.toml) # litellm @@ -6256,31 +6591,24 @@ tinycss2==1.5.1 \ --hash=sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661 \ --hash=sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957 # via bleach -tokenizers==0.22.2 \ - --hash=sha256:143b999bdc46d10febb15cbffb4207ddd1f410e2c755857b5a0797961bbdc113 \ - --hash=sha256:1a62ba2c5faa2dd175aaeed7b15abf18d20266189fb3406c5d0550dd34dd5f37 \ - --hash=sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e \ - --hash=sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001 \ - --hash=sha256:1e50f8554d504f617d9e9d6e4c2c2884a12b388a97c5c77f0bc6cf4cd032feee \ - --hash=sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7 \ - --hash=sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd \ - --hash=sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4 \ - --hash=sha256:319f659ee992222f04e58f84cbf407cfa66a65fe3a8de44e8ad2bc53e7d99012 \ - --hash=sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67 \ - --hash=sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a \ - --hash=sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5 \ - --hash=sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917 \ - --hash=sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c \ - --hash=sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195 \ - --hash=sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4 \ - --hash=sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a \ - --hash=sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc \ - --hash=sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92 \ - --hash=sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5 \ - --hash=sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48 \ - --hash=sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b \ - --hash=sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c \ - --hash=sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5 +tokenizers==0.23.1 \ + --hash=sha256:120468fb4c24faf0543c835a4fabafa4deb3f20a035c9b6e83d0b553a97615d4 \ + --hash=sha256:1974288a609c343774f1b897c8b482c791ab17b75ab5c8c2b1737565c1d82288 \ + --hash=sha256:1bf13402aff9bc533c89cb849ec3b412dc3fbeacc9744840e423d7bf3f7dc0e3 \ + --hash=sha256:1feeeadf865a7915adc25445dea30e9933e593c31bb96c277cee36de227c8bfa \ + --hash=sha256:5075b405006415ea148a992d093699c66eb01952bf59f4d5727089a98bda45a4 \ + --hash=sha256:53b09e85775d5187941e7bab30e941b4134ab4a7dd8c68e783d231fb7ca27c51 \ + --hash=sha256:56f3a77de629917652f876294dc9fe6bad4a0c43bc229dc72e59bb23a0f4729a \ + --hash=sha256:93120a930b919416da7cd10a2f606ac9919cc69cacae7980fa2140e277660948 \ + --hash=sha256:9d10a6d957ef01896dc274e890eee27d41bd0e74ef31e60616f0fc311345184e \ + --hash=sha256:a26197957d8e4425dfba746315f3c425ea00cfa8367c5fbc4ec73447893dcea9 \ + --hash=sha256:ae848657742035523fdf261773630cb819a26995fcd3d9ecae0c1daf6e5a4959 \ + --hash=sha256:e03d6ffcbe0d56ee9c1ccd070e70a13fa750727c0277e138152acbc0252c2224 \ + --hash=sha256:e0948bbb1ac1d7cdfc9fb6d62c596e3b7550036ad60ecd654a66ad273326324e \ + --hash=sha256:e3d8f40ea6268047de7046906326abed5134f27d4e8447b23763afe5808c8a96 \ + --hash=sha256:e7bfaf995c1bdbbd21d13539decb6650967013759318627d85daeb7881af16b7 \ + --hash=sha256:ea5a0ce170074329faaa8ea3f6400ecde604b6678192688533af80980daae71a \ + --hash=sha256:f836ca703b89ae07919a309f9651f7a88fd5a33d5f718ba5ad0870ec0256bad6 # via # semantica (pyproject.toml) # fastembed @@ -6426,7 +6754,7 @@ tqdm==4.70.0 \ # semantica (pyproject.toml) # bertopic # d3blocks - # docling-ibm-models + # d3graph # docling-slim # fastembed # huggingface-hub @@ -6456,9 +6784,9 @@ traitlets==5.16.1 \ # nbclient # nbconvert # nbformat -transformers==5.15.0 \ - --hash=sha256:bbf98f57b2ddd7c4ecbccfa2c0069017aa6fd01cc204bd50cbc0eeadcf2a13b8 \ - --hash=sha256:d7f007736f67749ae9490c4f8cb5d30b452ae2d68c8675e50ba8d63ea7feb107 +transformers==5.16.1 \ + --hash=sha256:17b0eac726ddc55e84ac58946063e0c6d37fd000c456b581f050ea0f4e822869 \ + --hash=sha256:2f2d5b98a5ad3718713653734298fa620754ed683702a635ebb587df3ed29c7e # via # semantica (pyproject.toml) # docling-core @@ -6564,6 +6892,12 @@ triton==3.7.1 \ --hash=sha256:ee89fbf782ec2ad50391dd1cf26cbea4f4467154c37f4773026da8fc31c0f58e \ --hash=sha256:fe4ea396a06171f1f1f58cbd39c70b09294398f7dd7c620939bab54ad6f934fa # via torch +truststore==0.10.4 \ + --hash=sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301 \ + --hash=sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981 + # via + # httpcore2 + # httpx2 typer==0.26.8 \ --hash=sha256:3512ca79ac5c11113414b36e80281b872884477722440691c89d1112e321a49c \ --hash=sha256:c244a6bd558886fe3f8780efb6bdd28bb9aff005a94eedebaa5cb32926fe2f7e @@ -6590,20 +6924,25 @@ typing-extensions==4.16.0 \ # beautifulsoup4 # docling-core # fastapi + # formulaic + # google-adk # google-genai # groq # grpcio + # httpx2 # huggingface-hub # ipython # jupyter-client # jupyterlab + # langchain-core + # langchain-protocol + # langsmith # librosa # mypy # openai # opentelemetry-api # opentelemetry-sdk # opentelemetry-semantic-conventions - # pinecone-client # polyfactory # psycopg # psycopg-pool @@ -6635,7 +6974,9 @@ tzdata==2026.3 \ tzlocal==5.4.4 \ --hash=sha256:8dbb8660838688a7b6ba4fed31d18dedf842afb4d47ca050d6d891c2c15f3be4 \ --hash=sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15 - # via celery + # via + # celery + # google-adk umap-learn==0.5.12 \ --hash=sha256:6aff02ecac5f2aad9f3c65ee518d7ae93e1a985ae38721fdcffceee4232c33c7 \ --hash=sha256:f2a85d2a2adcb52b541bed9b27a23ca169b56bb1b23283abeebfb8dfb8a42fe5 @@ -6651,7 +6992,6 @@ urllib3==2.7.0 \ --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 # via # botocore - # pinecone-client # qdrant-client # requests uuid-utils==0.17.0 \ @@ -6752,10 +7092,12 @@ uuid-utils==0.17.0 \ # via # langchain-core # langsmith -uvicorn==0.52.1 \ - --hash=sha256:112ec661814189acbccd3f7b86460147cc065fc92c0821afa78918780e4354dd \ - --hash=sha256:e4403f9d93188cf9d1088e9f40e3acd12630e2df8675316704379a7fc20fff6a - # via semantica (pyproject.toml) +uvicorn==0.52.4 \ + --hash=sha256:73acfee47a0b133c5de13d219492d62d8a31e935f4fe6e41a232451a15379f86 \ + --hash=sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1 + # via + # semantica (pyproject.toml) + # google-adk uvloop==0.22.1 \ --hash=sha256:017bd46f9e7b78e81606329d07141d3da446f8798c6baeec124260e22c262772 \ --hash=sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e \ @@ -6818,9 +7160,9 @@ vine==5.1.0 \ # amqp # celery # kombu -virtualenv==21.7.4 \ - --hash=sha256:376ec93cd6aab3044fa395d7db226db38043b7b5748948044b2a87168525e843 \ - --hash=sha256:c9d960c95fa458171e58222a5ccab7465298e4b6559977865e627c4719f1e825 +virtualenv==21.7.8 \ + --hash=sha256:1dc49c790072a9072cb1803f9bd62aa69cd583077cada32390f75505cdc64c9b \ + --hash=sha256:3040eb3cbf5d32b10ffd57d167e6a162237ad82ba7d8cf1400a1efed593d85ac # via pre-commit wasabi==1.1.3 \ --hash=sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878 \ @@ -6860,7 +7202,9 @@ watchdog==6.0.0 \ --hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \ --hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \ --hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2 - # via semantica (pyproject.toml) + # via + # semantica (pyproject.toml) + # google-adk watchfiles==1.2.0 \ --hash=sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9 \ --hash=sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98 \ @@ -6970,9 +7314,9 @@ watchfiles==1.2.0 \ --hash=sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08 \ --hash=sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4 # via uvicorn -wcwidth==0.8.2 \ - --hash=sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda \ - --hash=sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85 +wcwidth==0.8.3 \ + --hash=sha256:d128512515fbf4612e0ff21fd6380399210318b7b54a9af59dff8454cf9730eb \ + --hash=sha256:d5b73dba6158a595ec9370350e7f2637bcac8d6c5e4fde34f30fcffb6103a5e4 # via # prettytable # prompt-toolkit @@ -6988,227 +7332,201 @@ webcolors==25.10.0 \ --hash=sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d \ --hash=sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf # via jsonschema -webencodings==0.5.1 \ - --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ - --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 +webencodings==0.6.1 \ + --hash=sha256:565f9ad031c702dae404e27a099e3e09186a3ab1b9520f06d215502b651fd910 \ + --hash=sha256:7fab6269c8bf237c657876b52058ccb182e861518d1c695c1a9aaa8c1c105d5b # via # bleach # tinycss2 -websocket-client==1.9.0 \ - --hash=sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98 \ - --hash=sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef +websocket-client==1.9.2 \ + --hash=sha256:0fcb57545848be86992e128218fd96dd87a6769ffdb1a968dff79632b85604d0 \ + --hash=sha256:e1a673830a9c7bfa47b1cd3d5e4178f4c9651d80a4eab02c9c23a1c3ec6250ce # via jupyter-server -websockets==16.1.1 \ - --hash=sha256:01fbdcbac298efe19360b94bc0039c8f746f0220ba570f327577bfee81059175 \ - --hash=sha256:024193f8551a2b0eafbdd160911012c4e6c228c28430c84433253299a9e42d6a \ - --hash=sha256:04fd29a0e2fe9414a95b00e92c67ae51bf900c50c0f8a4b2dafdad621f49ea1d \ - --hash=sha256:056ae37939ed7e9974f364f5864e76e49182622d8f9751ac1903c0d09b013985 \ - --hash=sha256:0f62863e8a00a6d33c3d6566ec0b89f23787b747ffe0c3bc71ec0e76b82c94b1 \ - --hash=sha256:0ffd3031ea8bda8d61762e84220186105ba3b748b3c8da2ae4f7816fac03e573 \ - --hash=sha256:1214e673c404684b9bf7154f5cf43b45025b1a6160fac3a9e438e9c1a97e22cb \ - --hash=sha256:125f22dbefaf1554fea66fc83851490edb284ce4f501d37ffed2752f418332d9 \ - --hash=sha256:130937b167a52af203c8d58e78d67705874e82759862e3b9671a452fec4abc87 \ - --hash=sha256:1427fb4cf0d72f66333e2cacc3ff5f575bf2d7008166ce991a4a470b21d51a22 \ - --hash=sha256:195c978b065fa40910582464f99d6b15c8b314c68e0546549a55ed83f4735328 \ - --hash=sha256:1d27fa8462ad6a1cb36206a3d0640b2333340def181fae11ed7f9adeaa5c0747 \ - --hash=sha256:1db4de4a0e95673f7545d393c49eeb0c2f18ac1ef93073218c79d5cdb2ee75ab \ - --hash=sha256:1f79c89b5eb034d1722938a891916582f8f7f503f58ca22518a63c3f2cd18499 \ - --hash=sha256:23253dd5bcae3f9aaee0a1d30967a8dbd52e5d3cff93a2e5b84df57b77d4750d \ - --hash=sha256:249116b4a76063d930a46391ad56e135c286e4562a18309029fc2c73f4ed4c62 \ - --hash=sha256:29dfa8114c4a620c69591c5973860f768eac29d3fd6904f37f34266cb219c512 \ - --hash=sha256:2a606d9c24035242a3e256e9d5b77ed9cd6bccfcb7cf993e5ca3c0f6f68fb6a7 \ - --hash=sha256:2a636ff1e7a5c4edf71ef0e79adae7f25dba93b4fcbe3dc958733477ffeb0eaf \ - --hash=sha256:2bb5d041a8307d2e18782e7ce777f6fdb1e8c2f5d09291484b18c294b789d9aa \ - --hash=sha256:2e28e602bb13da44fbe518c1781a88e3b9d4c3d48d02c9bad83e546164336f57 \ - --hash=sha256:30bbe120437b5648a77d3519b7024ea09530e0b5b18d3698c5a0ae536fe0cc2e \ - --hash=sha256:34420aaa64440ebd51ac72ca8a45ef4626429438c9b02e633ae412ed43f925d3 \ - --hash=sha256:38565aca3e01ea8734e578fb2118dade0ecb0250533f29e22b8d1a7a196cf4d0 \ - --hash=sha256:387e8e4aa5df2f90b198fa3cad3478822a89cf905b6a6d6c97dc3664689640cc \ - --hash=sha256:39f2a024af5c345ffe8fcf1ee18c049c024c94df393bb09b044a6917c77bde43 \ - --hash=sha256:3df13f73af9b3b38ab1195eb299ecb67a4330c911c97ae04043ff74085728abe \ - --hash=sha256:414e596c75f74e0994084694189d7dc9229fb278e33064d6784b73ffbba3ca31 \ - --hash=sha256:41c8e77f17294c0ac18008a7309b99b34ee72247ef10b6dff4c3f8b5ac29896b \ - --hash=sha256:42290eb6db4ccaca7012656738214f8514082fb6fa40cdeb61bb9a471b52e383 \ - --hash=sha256:42f599f4d48c7e1a3338fdaac3acd075be3b3cf02d4b274f3bf2767aedd3d217 \ - --hash=sha256:43e3a9fdd7cbf7ba6040c31fae0faf84ca1474fef777c4e37912f1540f854499 \ - --hash=sha256:443aefe96b7fdb132e2a70806cca1f2af49bb3f28e47abcd7c2e9dcf4d8fa1b8 \ - --hash=sha256:46dcaa042cd1de6c59e7d9269fa63ff7572b6df40510600b678f0826b3c7af51 \ - --hash=sha256:496af849a472b531f758dbd4d61338f5000538cb1a7b3d20d9d32a264517f509 \ - --hash=sha256:49ae99bdfcae803a885c926bf14f886196e84925395bb3f568fef5c0f0979d7d \ - --hash=sha256:4b57693728576d84ede0a77987ab16881b783d2cd9f1dc180a8fbbc3f79c4428 \ - --hash=sha256:4e3b680b1e0a27457e727a0d572fd81dffa87b6dbf8b228ab57da64f7d85aead \ - --hash=sha256:4e8d01cc3bcae7bbf8167f944aeafefed590fae5693552bba9794a9df68371cc \ - --hash=sha256:5283810d2646741a0d8da2aa733d6aefa0545809afccb2a5d105a26bc45125f1 \ - --hash=sha256:53260c8930da5771cec89439bff99c20c8cb03ddb9588b980697355a83cd4bd3 \ - --hash=sha256:536676848fc5961aca9d20389951f59169508f765637a172403dc5434d722fa0 \ - --hash=sha256:54509b8e92fee4453e152b7558ddef37ce9705a044922f2095a6105e3f80c96f \ - --hash=sha256:56cd5fc4f10a9ea8aa0804bddb7b42506cf9e136046f3b4c27de8fec9e2ecba5 \ - --hash=sha256:5bfd1ac19b1b9986a9c95a82d5e23a391ebb09e12c34d7be6094b86efcc35731 \ - --hash=sha256:5c31aa7e39ee3e8a358573257f1c0bb5c52430d1b637030dd9c8cc2c282926be \ - --hash=sha256:5e3b7d601f6f84156b08cc4a5e541c2b50ad7b36cfc302b657a12477c904a5df \ - --hash=sha256:61922544a0587a13fd3f53e4c0e5e606510c7b0d9d22c8444e5fae22a06b38cb \ - --hash=sha256:6456ff333092d509127d75a638cb411afae8ff17f092635015d1902efec8a293 \ - --hash=sha256:69159730a823dde3ea8d08783e8d47ef135a6d7e8d44eb127e32b321c9db8e3e \ - --hash=sha256:69e52d175a0a7d1e13b4b67ad41c560b7d98e8c6f6126eb0bda496c784faf8c7 \ - --hash=sha256:6aaface73b9c71974c6497366d8b9628357f6c9749e09c4ea3610176c63f2ae3 \ - --hash=sha256:6abbd3e82c731c8e531714466acd5d87b5e88ac3243465337ba71d68e23ae7e3 \ - --hash=sha256:6ff9417c0ada4d0f7d212f928303e5579bdf3ace4c802fa4afabb30995da58c3 \ - --hash=sha256:7421fad442de870a8cbf2287d1cad7e706ece0dbfeba5e911df132cbdc1cb56a \ - --hash=sha256:7883388947767080f094950b342b30d35a2a06b849cd967c422fa0db72b40ea9 \ - --hash=sha256:79eace538c6a97e96d0d03d4f9d314f9677f5ed85a8a984992ffd90b13cb8a56 \ - --hash=sha256:7b1b19636af86a3c7995d4d028dbe376f39b4bf31541146f9c123582a6c94562 \ - --hash=sha256:7dfcad78ea1492ee3a9ec765cb7f51bbc17d477107aaf6b22abf7b2558d1c5a0 \ - --hash=sha256:8087e82f842609734c9b5a1330464f8e94e346ba0e18c832c08bafa4b0d63c15 \ - --hash=sha256:820fb8450edddae3812fd58cbc08e2bf22812cb248ecb5f06dbb82119a56e869 \ - --hash=sha256:8483c2096363120eea8b07c06ae7304d520f686665fffd4811fad423930a65d7 \ - --hash=sha256:84a2cef8deffbd9ab8ee0ea546a2a6a7030c28f44e6cdd4547dbfeb489eb8999 \ - --hash=sha256:86d7f0f8bdb25d2c632b72527325e4776430fd5bc61b9118de4e2b8ddb5f5b01 \ - --hash=sha256:8fe0b50da2d84535fb4f7b4bfa951280f97ce3d558a0443b541166d609e67b57 \ - --hash=sha256:90001d893bc368e302ef168d82130b4e4fdd27b85fa094682df9b667c2d48838 \ - --hash=sha256:9246a0d063cfcbcc85f2359dd6876d681213f4790832272aa16641b4ed5d64d4 \ - --hash=sha256:92b820d345f7a3fc7b8163949ee92df910f290c3fc517b3d5301c78065adafe1 \ - --hash=sha256:952303a7318d4cbe1011400839bb2051c9f84fa0a35923267f5daba34b15d458 \ - --hash=sha256:97fd3a0e8b53efa41970ac1dff3d8cf0d2884cadeb4caaf95db7ad1526926ee3 \ - --hash=sha256:9c1c5705e314449e3308872fe084b8571ce078ee4fc55a98a769bdefe5917392 \ - --hash=sha256:9c9f23004a3d40e89c01a7955d186a6cc83418d93b749701944ce2de3e95a1f3 \ - --hash=sha256:9f63bcef7f4b02b06b35fc01c93b96c43b5e88e1e8868676caacf493d5a31f3a \ - --hash=sha256:a0eadbbf2c30f01efa58e1f110eb6fa293261f6b0b1aa38f7f48707107690af9 \ - --hash=sha256:a28fcbc9b6baf54a2e23f8655f308e4ccc6afdd7266f8fe7954f320dcda0f785 \ - --hash=sha256:a6a61aff018180c9c50b7b0da33bfd29d378af3497429c95006c589a23a11648 \ - --hash=sha256:aabe464bfd13bd25f4821faf111da6fefdc389f870265a53105580e45b0a2e49 \ - --hash=sha256:ab59169ace05dcb49a1d4118f0bde139557adf45091bd85747e36bf5de984dd1 \ - --hash=sha256:b436f6ec4fc3a6b4237c84d3f83170ed2b40bb584222f0ac47a0c8a5921980c7 \ - --hash=sha256:b6b9dadbef0cccd9f4c4ee96b08898afa73e26803bbe0f6aeb5bb12b0074206d \ - --hash=sha256:b852788aa51764e2d8e4cf5493d559326bcae5e38d16ba25ffa322b034df272a \ - --hash=sha256:bae954c382e013d5ea5b190d2830526bfa45ad121c326da0049b8c769f185db6 \ - --hash=sha256:bcce07e23e5769375158f5efdcdafa8d5cd014b93c6683865b840ed65b96f231 \ - --hash=sha256:cc97814dfb786a83b6e2dc2e79351e1b83e6d715647d6887fcabd83026417a00 \ - --hash=sha256:cd2ca96a082a36964aca83e992f72abeb61b7306c1a6cba4c7d06a7b93750cac \ - --hash=sha256:cfb70b4eb56cac4da0a83588f3ad50d46beb0690391082f3d4e2d488c70b68ea \ - --hash=sha256:d0fcf657e9f13ff4b177960ab2200237b12994232dfb6df16f1cfe1d4339f93c \ - --hash=sha256:d14bfb217eb4701e850f1525c9d29d79c44794cdf1c299ead25f39f8c78dea81 \ - --hash=sha256:d57685547e0060cc6fd90ee6a28405d6bd395e525545f13c8d7cd99c78afd79f \ - --hash=sha256:d6bec75c290fe484a8ba4cacdf838501e17c06ecfbbf31eede81a9e431bd7751 \ - --hash=sha256:d9531d9cbeac99af6f038fb1bc351403531f7d634a2c2e10e2f7c854c6ed5b68 \ - --hash=sha256:da4ca1a9d72f9030b3146b8d7022719a9f3d478f61efe6f7dd51d243f61c51b2 \ - --hash=sha256:dab9eb87869da2d6ed3af3f3adf28414baae6ec9d4df355ffc18889132f3436c \ - --hash=sha256:db234eda965dcce15df96bb9709f587cd87d4d52aaf0e80e2f34ec04c7670c57 \ - --hash=sha256:dc0fad4933f427acd5b1cec210f3ea6dce7089e1724e4b9ec6ef47c6c04d1b3b \ - --hash=sha256:dc385593a42e31cd6fb60c19f0ecb015b386603818fc2c6c274fb42bd2bb4165 \ - --hash=sha256:dcc04fedf83effaeb9cce98abc9469bb1b42ef85f03e01c8c1f4438ef7555737 \ - --hash=sha256:e047dc87ef7ca50f4d309bf775ad4a71711c58556d75d7bd0604b2317f43e94b \ - --hash=sha256:e09f753a169951eb4f28c2c774f71069304f66e7277e0f5a2892423599cfa854 \ - --hash=sha256:ed5bb271084b46530ee2ddc0410537a9961152c5ccba2fc98c5276d992ccba87 \ - --hash=sha256:f0aa4aad3b1b69ad3fd85a0fd0952ec64331c762bd77ec51cc814170873890b2 \ - --hash=sha256:f17dbe07eb3ea7f99e4df9b7e0efefe80fbf30d37a8cc4d561a0aed310bc8847 \ - --hash=sha256:f2769a0344a09e9ccf5b3cce538bc75a51b53eff3275d3896310c8552049195d \ - --hash=sha256:f55f0b01956a094c8587146d9558c91937e78789c333860ffaf35931a6e5dbc4 \ - --hash=sha256:f5d497865f05bb222cab7016c6034542e84e5f29f49c6fd3f4939cda7197b5b8 \ - --hash=sha256:f70541f3104339f59f830522d94ebadb1bf47426287381623443d8bb1cdbf33d \ - --hash=sha256:fb9a0a6dc3d1b3986cb88091b6899f0396651e0f74e2c9766ab8d6ffc3842e29 \ - --hash=sha256:fce6c48559c86d1ac3632ccb1bebc7d5442fbe79bd9bb0e40379ee54be2a4051 \ - --hash=sha256:fd46fff7eb62c24804d234f0051c7a8ea81285ad63e0337d3dcf33ca82aee58a +websockets==15.0.1 \ + --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ + --hash=sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9 \ + --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ + --hash=sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3 \ + --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ + --hash=sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e \ + --hash=sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1 \ + --hash=sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256 \ + --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ + --hash=sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880 \ + --hash=sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123 \ + --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ + --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ + --hash=sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed \ + --hash=sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41 \ + --hash=sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411 \ + --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ + --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ + --hash=sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c \ + --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ + --hash=sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb \ + --hash=sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e \ + --hash=sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee \ + --hash=sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f \ + --hash=sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf \ + --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ + --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ + --hash=sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a \ + --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ + --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ + --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ + --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ + --hash=sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d \ + --hash=sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5 \ + --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ + --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ + --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ + --hash=sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9 \ + --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ + --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ + --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ + --hash=sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475 \ + --hash=sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940 \ + --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ + --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ + --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ + --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ + --hash=sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b \ + --hash=sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a \ + --hash=sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054 \ + --hash=sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb \ + --hash=sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205 \ + --hash=sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04 \ + --hash=sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4 \ + --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ + --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ + --hash=sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122 \ + --hash=sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b \ + --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ + --hash=sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770 \ + --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ + --hash=sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b \ + --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ + --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ + --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ + --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ + --hash=sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9 \ + --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ + --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 # via # semantica (pyproject.toml) # docling-slim + # google-adk # google-genai + # langsmith # uvicorn -widgetsnbextension==4.0.15 \ - --hash=sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366 \ - --hash=sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9 +widgetsnbextension==4.0.16 \ + --hash=sha256:a31a8774885b96fe825462f5d6496166f0c7cae111195b6465c801d230eb5a4e \ + --hash=sha256:adeea0ae78f0856ee4945f413299801b82a0a01416303301f39a704282a37b73 # via ipywidgets -wrapt==2.3.0 \ - --hash=sha256:0a45ffae742ce91a16e11cb6c7cd71e7f9994f3cbd283b962ab093f5c6dcf525 \ - --hash=sha256:0bb2797048db0956348cb3058c33bc4184614f13231389cfbccc16a5d32780a7 \ - --hash=sha256:0d3fb71e65b001adfc42684522eeccd9c21d8ba679945abc993439567b66e59f \ - --hash=sha256:0db083387d6e75ec0be8173ecbf0e811cf60bae1cc75a815feb104167ea10d4d \ - --hash=sha256:10461884b3014fbfc8eb7d09a93c5f246363e6711d9d881f95eb8c27fdef049f \ - --hash=sha256:1236fa25173ca964c97422470482e9011b9e3c7ed0d75798b40b3da3b0e0e760 \ - --hash=sha256:141ed6211286a9660d8d6702de598b43f0934b4f0eda16393f100a80f501d945 \ - --hash=sha256:1598becd30f8f2777d18564064eb4f4dbe1ab0e05a8f09786d0ef505ac782bf3 \ - --hash=sha256:195b1842b4122fb54e3cd3dd5b2b4aa49302a5a61da901df0481f5c97aedde84 \ - --hash=sha256:1d6159c9b2fefec02314e1332dbbbfaf960e369dfd26bcf7f8b258b5732065b3 \ - --hash=sha256:22cc5c0a717bd4da87018ae0bffd4c19c6fb679d3ff357216ba566ab26c76cab \ - --hash=sha256:242b60c21e30866e6a2fa606c612b47c553fa60c0eaeeeb7797fb842ac0ce609 \ - --hash=sha256:24da48596326ef8e448cfa837b454f638713d3531262375f00e5a9681682fc07 \ - --hash=sha256:261f53870cd4fb2bf38f9f972c56c728fd224cb7c65721307de59d9e7e6741ae \ - --hash=sha256:2935d5454b3f179a29b12cf390ee47246740ba2c3a7545b1b46ba31a5f2a4a0b \ - --hash=sha256:2e49885a62ec4ee854d1b9e6371fda6afd219917225752abf729a3f36d4df9a5 \ - --hash=sha256:379f670f45b7bb8993edd9f6fc36c6cc65edb81cffa0b504be34acb0303fff0a \ - --hash=sha256:3873c3c5ca9f4ef91f693602eca19d1f1e7c410338df82a4ff11d826b5896a8f \ - --hash=sha256:392158c9a7f2ab1b8699418bfc0fe6f83548788c418b27d7bf2019ad3405cebb \ - --hash=sha256:39febbee6d77301d31da6996b152ce52452da7c7ef72aba10c2fa976dff9c295 \ - --hash=sha256:3d1c2c1b808600d2ea808e6360910a60ed5f409a4011655e10f9164ba0a414a6 \ - --hash=sha256:3da470536bf9645143323dd41b32db55c6f4304ad382094c1a1da8a92061e10d \ - --hash=sha256:418f54bb09d1762db02c7009b4051149893af3153a87f92d70356703c11eea02 \ - --hash=sha256:42869085687f0aefd57c0f636c3f9354f8ffb321a8ba9cb52d19beb796e561c5 \ - --hash=sha256:45c9279b373d15649dfa2c2077cb3408ea1a6d3125afbdab9d6b809a66f68e14 \ - --hash=sha256:4fa0df3bff4e7ce45759f33fd39335fe2f60477bb9ecf7b8aa41e7d07ee36a23 \ - --hash=sha256:50f416b74d092bb9f41b424e90dd457f365f7ba4b11de62a23679769a21bd85c \ - --hash=sha256:51a7a4181c1295774812271fbcd7c909df372bc25579d4ed9eb875caaf0ae86f \ - --hash=sha256:54ca1d5573f69b5fe1d74f1f65799c68015e82f685efec9fd8cfa40a094c44d0 \ - --hash=sha256:5ab559e1b2551d23d54db2a0001c6d73bad022a254639561c5f6c382a9d6c2fe \ - --hash=sha256:5ba1e5e08ddc46130e9682b2c249f2d1dd39bda9106ed4bd401b7519f18f41bd \ - --hash=sha256:5d221a6e6ddd302b8397433184e96b59f259f50024b854db1c411a881586b6b8 \ - --hash=sha256:6208f302f110295d64b22a7ac96500c791bf492dce4366e622e4912b077c9687 \ - --hash=sha256:626b69db2021aa01671ec7bbc9740e558522bd44c18cf2ce69bf3d666a014109 \ - --hash=sha256:628f3ba8ec793a5b10a6cd8c6c6b7b55eb552abd1f3bd301336acb74c7a82dfe \ - --hash=sha256:629d73378082c00a8173031f9fb30a3ac6abbc894a5bfdfae71fabc60642d501 \ - --hash=sha256:646d20d413ffcd1b0a2f700076e2d0252d872dcb7754860a73e45a59ea883614 \ - --hash=sha256:67bfe2485f50368c3fcd2275fc1fd100e350d601e0058921a7c82678a465aeab \ - --hash=sha256:681a2d0eefd721998f90642762b8e75c2159ec531b20ad5e437245ea7b06a107 \ - --hash=sha256:69e477046f2237ef0bc6547544ee73008dc764ca26eff44f09e976d221b34d5d \ - --hash=sha256:6db604ef0c67bdb2042ecdfd7b7f037cf09733557ca42360d1018285634f7b98 \ - --hash=sha256:729126e667da34d251b8ebf8a45ef0c5ddadc21542b3d6e1abf4259ece6508df \ - --hash=sha256:73d0b10b64620a2cf4bc3d31775c4d9527e309a5549e4379e3bf71e8d2dc193e \ - --hash=sha256:7ebb274aba688b043429eb1500ff8a76ce0cb8ac0812ca3e301f06247b8722b3 \ - --hash=sha256:8159ec0b0cb7608175eb150de94c19e34f4d47ac655f5ca9baf45df6b688ffd3 \ - --hash=sha256:816877aa749253149f9ecfd2635d4d948ecfa338e1a0311d187b1acb1bb8a3eb \ - --hash=sha256:85de890ff968196e92dd1ae73a9fb8970495e7650a457b1c9ef0ac3dd550bce2 \ - --hash=sha256:8f8a1c6472675956cece9a8f403f43c3594f1681319eed2dd56f60877397c636 \ - --hash=sha256:9045917809c63fdf7abe3a2ceaed3d670b8ee4500ddd9291192d30aeb34467c5 \ - --hash=sha256:932dced0a7b2950ed58a3325536a1dcb7b58e7330af54e8552d2e566b5328b99 \ - --hash=sha256:93513bec052c6cd987f9f580c3df068c8bc4ebae6543736be3ca7ec5959cafcd \ - --hash=sha256:9790ea25190a4e0fe4cdf4eeb868e9d75f8a024a70a5b6bf9c348a3a2b72e731 \ - --hash=sha256:9f5d2aec29dfc76c37e23897dee92766a3fd4f3bff3ae7fc9c6b4bf37d8c1360 \ - --hash=sha256:a65e8db2b4e90c2e7ade931086351c98ef420bf7a94ee08c95ac8a3cbbc43579 \ - --hash=sha256:a6b5984cd65dd639546f0eb4b8eacf1c31cb2fe9fb5c27bffe240987cdb2cf84 \ - --hash=sha256:a6e19531ae33c508cea7d84a7edfda01fa86e51b8d1a93a77712c55e6e469152 \ - --hash=sha256:abc71504669d126d91f89fc0e388c6295d8fbd2439be884f175133fda8aa403c \ - --hash=sha256:ac870cc97b73bb00ac353329e9559a4bebc47c4c86792ed9b23b58c15b6ad838 \ - --hash=sha256:ad71df7a04dd3497e9302e81f4a7c91bd401ea0e15a9df9029527900f94bee43 \ - --hash=sha256:b1e5aa486e269b00ed35e64771c7d0ab8096cfd2643405ca8cd60ebedc099a51 \ - --hash=sha256:b4fc96b159af0a3e0faa72475a69d66292bea72a5bed1e1aca1bffbddc3cb2b0 \ - --hash=sha256:b767a9566f165dd14decf8f4194c6bb0ce3a8420cec213824e05a99400c9260a \ - --hash=sha256:bff9a671bc00709cab5a7f745c592b5671873449db0ee2a569af994f16b29a4d \ - --hash=sha256:c3b476ae63b4a3b4da681aafcb25ff3542d289fbda8b5da7caf76aaffafafdbb \ - --hash=sha256:c4bded758ad6f03b965830944a2f0bc5b2eb3767fe5a7310134315d1a6610e98 \ - --hash=sha256:c8388ba7faf5dbf9ee106bb70d66f257629b1bd98091123e19e8a4553a319199 \ - --hash=sha256:c8858d8ff9822a081e3cc49ae1b3b22f0f789c14001cdac8f94564010d9c9d66 \ - --hash=sha256:c88abcf53daef80e01a75c7530e727fa6e2c1888fe83e3dcdba4c96216a1f5c7 \ - --hash=sha256:cc2cea812e5cb179a796b766747e7d3b21088760d8deb95676d482b8c8e6fa7d \ - --hash=sha256:cd3a2edf0427013736b8127955cec62608c56e53ea47e82812ea32059cda407f \ - --hash=sha256:cdc021cb0b62471d6aac7f2bd92f3b4658073775f9ee7fcd325c511129e7bcc8 \ - --hash=sha256:ce9f398f868d2b3b27aa2ea4de79645ef9077aeeac8dfc2814b0d542c6a2b87f \ - --hash=sha256:d0077f3d65541925fa83002f967b22ad6550d24813ac64cb905f717194128d9c \ - --hash=sha256:d0f7284f88f4833705132d06d3b425a43095c2cbd07c58166aac3ab646ba12a4 \ - --hash=sha256:d2cc64539da63e39ffb9c7ede849b6e8ddaaf7b3876b5cfb04efd85a5f3f4eb6 \ - --hash=sha256:d8c7ed08477429752b8c44991f40ad7838b18332a160698740a6bfbc10d998a2 \ - --hash=sha256:df4ce31150bcd5d9f36f816aac3010ab4f4bf8672ac1d3b0ac7d539ec61c7c02 \ - --hash=sha256:e045ff75d7d94900fc32896ed93c45ce2d2cac28c9dead582ff9a5a49d446e35 \ - --hash=sha256:e2e692bc0d63f881cf7006730a56bd4e0c2fab5dc318466942805d692b166276 \ - --hash=sha256:e31734c5077f29f892b2565eee5106d610278151ad49fc6a9d69a647cd5730e2 \ - --hash=sha256:e3b9eaa742ae7a0aaaaad4ca4b69469d757af2d6e6663ef1dadc47adec0aeb41 \ - --hash=sha256:e3f3d7ec0a51fbfe00d3aef047641ff2c58b25565b4717fc1f90e050be01cba8 \ - --hash=sha256:e5301c35cf75655eb33498f2bd6ae8703ca19940e3167dc9cdf740c712a39c60 \ - --hash=sha256:ea52a0d0f08c584943d5764be0e84efa912c8da23c23e1e285ff2f5641c18fcc \ - --hash=sha256:ed635a9ca4f3a5a2b900c10c69e823373bc00ebc114b459383596d3487da3570 \ - --hash=sha256:fb8e2e6704a1e0b1b989546c69e2688371ef4a07fa5f61bde3eb6211186f5ac1 \ - --hash=sha256:fc648a335d7e01adb3640b25f02fd0ea05886cf04d0af7f4ee902bc7b5e466e8 \ - --hash=sha256:fc82c2ccc8e234c844f5303d9f2984b346dcdd53e94823ce8420d2c75b4b9023 \ - --hash=sha256:fd1f2f557dd3491fe75905e578f4db967393d40d1a8f468edc4d40ac7f2d5944 \ - --hash=sha256:fd85b0aa88efdb189d6ae2f35f4526943a8f091c38599c9c31478241c819e6a1 +wrapt==2.4.0 \ + --hash=sha256:0191d717dfbb8e519e7bfd4775e5b9bd57e359b3a09ab5db1ea47f6025b4d845 \ + --hash=sha256:0536f5d85ff6a157ebe7e0fe08c5479943742cf1ce59569075a66159efcbc495 \ + --hash=sha256:07daab5babb7edaf89413f5c8bd638474540fb2643b5dfb685bdc0680c96803a \ + --hash=sha256:08d8378c4514ac8dcc0ace76044cf87a873e6a52b5e6109834c8fb9037f4441b \ + --hash=sha256:09064c7be688c38c3ff125ce86bc26b69b5d78dd56062c3ddd9c814b2a25f1e1 \ + --hash=sha256:0972cd025f4c86fa2d8abd953d9f875779935343af58b4ce019ff89573fc65bd \ + --hash=sha256:0b8851a54b137eec9a8480d73cc1a309613e6a465d6f157300f1eb6b5b7c0505 \ + --hash=sha256:0eca69c9e93518240abe8801fb9b2726116a6e48172e4564c2651a2e14521747 \ + --hash=sha256:11ccb5f3de2047ef91408464abdc04682e40e7d7bc9614885d2abcaa7e2ef149 \ + --hash=sha256:15bb88c0a6c6312244917bb0a094368746fefb92663209363e16f20972a57b34 \ + --hash=sha256:1656de3835f760781c9b974bce07d8c04edb9c9ad7ad67264aee69cd68a1db09 \ + --hash=sha256:174f3576dacf55c8a7c21719d4b7c8088efb991888db5728cfc891b80b28853f \ + --hash=sha256:18aabd9301d06026f5900538051773d6f87f65ae02cdc60de482df978513dc0a \ + --hash=sha256:28f5de1526831b8f173889a436e289fe181ede8c66c9feb669d1aca8fd602eaf \ + --hash=sha256:2a9f1a2f75bb95257cc5744e255e10a5a86e923f328b40ad3dbf9d8d03430013 \ + --hash=sha256:2dc0f6412aaf5fc7e6a3abf119b7c671dbd026303daccac20112c046a48b68b9 \ + --hash=sha256:325c24cfddb46f93c931cf37fa3a9929ac94e70a5627efccd51283f9fd69c6db \ + --hash=sha256:328eb2d978ca3a6ae25f8d8fe560bf8f4bc9778b5932e7b142664eef05b92e8f \ + --hash=sha256:332d9bad7e9b718974bb2a576504c4956f45b4a0fcd7b3bb7827279167550464 \ + --hash=sha256:3367a5212212c9393e0d3ca6ae029b3a8fa40c5896e4a985d43fe8a4b8322f0d \ + --hash=sha256:36b56a4fba13b34ed8ff307557325fff215de0a58b5dbaef2c50e4d8aa39dbd1 \ + --hash=sha256:37ba372e9ae71ec43e165b5db05f52e71f7c07dafb9d6a254ef7128112dce751 \ + --hash=sha256:39cd68df4dff79f5336f9c745c06259d204bcb42d504040c9c91eac9e2abb39c \ + --hash=sha256:3a69161cae7f0dca44c89c1d14146b4a0508a0c3cad98b3f2db1f4e9016c94ba \ + --hash=sha256:3d5e5eb76fb87e62752af751d2dcd9d1cd986b12037d2e1363d109ba716029e8 \ + --hash=sha256:413e757dce7a43fcda8bb8441994b1127492ffac6a5803af777d44516df8c6e2 \ + --hash=sha256:430fde1a116df3ceb5c29035de1da6609b70e680d9b8ce3ee624422f3fe0978c \ + --hash=sha256:4597d19904b4aa97331d8bb651ac626d9397727e717942cf11bd7699ff97aa45 \ + --hash=sha256:49bb5a572469e0e18163a8ec2aa972135a0929899ecbe627665f274506e1b5b4 \ + --hash=sha256:4a1d591386ec4aa99780f672232868620f4f3b63401e2ceb529762580ef8c54e \ + --hash=sha256:4f8ddff4bbb75916be36da5169b8b9d475b59a1bd24acdb7551bb2c71be9aaac \ + --hash=sha256:52f01626f1d2bc54585954cd8b4931f81003b0ac8dad61c741f43014bc9a0f0b \ + --hash=sha256:5b8fff692f74782de89ba9d7b526a7cc398569b6a988ddc848159cc033c86237 \ + --hash=sha256:5c5c4c728cd22a36e4b8bb5df4a7d3bccaa865d27725b36eeb3b6f18fb2e1bc2 \ + --hash=sha256:5cc2e7c7b6032e11a2b367a9baadaf0c5241feff2d8205260d87f1aa6dbdf84b \ + --hash=sha256:5f041ed6a4d571010944bd6cfad9072db463e1851877b6d3227467a44af37456 \ + --hash=sha256:5f3bdfc35c83b562fcaebc0f24593045e5ed9f3b633adafd35222718a0ec38fa \ + --hash=sha256:635cc171ddfd72edff10e295a02daa65edaa1c0ba619ad11eeed15cd2258c5df \ + --hash=sha256:637fd6a18bb668a0c27b4767dcbc2fa93119c90da735bd2669fdde2d7b59fab3 \ + --hash=sha256:63b94f401d7ae3a9a3027472fd3a3ff38afd2ed293b2f0b3b84a6d133a9f99a3 \ + --hash=sha256:6436e2bda993a3eb69a1b317fc831c8ebcafb5704c390859ebd49f81218c4bbb \ + --hash=sha256:643e45aa88698c8aae938c50e61940985d4ab9e53ea666d3e8e4eb86a4820d0f \ + --hash=sha256:648d1d4f94e8a0a1656675c755f40d2f0ee5fe92c449ab45326f4ecc2738cbe8 \ + --hash=sha256:66e7512c0d324cc37bba1def2be1fc365cbb685d3aa393a8f6f4d2d00202881d \ + --hash=sha256:694005fdc3002ade0f21641408c588028abde03c85961f3ba7727d8bead3ed6b \ + --hash=sha256:6a48bc764deb585d3b8862544ca12df417109984e018767b4ac9aa46bbb55ccd \ + --hash=sha256:6b3e082d43f592fcd381aee46354a11ce887a813ce5bbcedd9766fd681723c09 \ + --hash=sha256:6b9b32d5e4f0a179cef5075cc79b79d6d3482c44c434c12969e48c6719e06d95 \ + --hash=sha256:6d57264c9dfcf37d2bf0b0fbec68d0f6184fc5617267619ada04d03e8b0231f3 \ + --hash=sha256:7082fc1f94b020ac275870c4af71b09cff22876fe6e9c4c0ad01ea21d217b288 \ + --hash=sha256:717dd7ef439863933664951f89902b7e0ee3652293543cb9917c2e16bbde1949 \ + --hash=sha256:72826910a1cf5a081234720fd43011304b899acfee219af49148155b4d795533 \ + --hash=sha256:75529a2fb569a671cf162f762c1b576f569f571b55ec7f3481258ca842ba507f \ + --hash=sha256:75944792cf6b99262d649d55710bf5901f7013fbb212c7a1d736b97a20517607 \ + --hash=sha256:788e473d1a6786d29d577b1e2bd95e214c09cdafde84907c522c31069c9acfac \ + --hash=sha256:7a057d376d994da6bd1bbf955ecfda699aa7353826f98847f5605e1801abdfd4 \ + --hash=sha256:7a27d9653e0f88aa06598954337a545fc3f75bc811df897157a8614846d18d9c \ + --hash=sha256:7d28f8f35a02d49f75f57fa4e755db4ba33f65841c0de64cd65b253916f5bf06 \ + --hash=sha256:7de5b8d94417e55c02be50cc226e0ae1209bbc73813bf691dff3979c94438115 \ + --hash=sha256:811a36628d8b76724b980d508d576e5c5ecae1073b6ec4b4eb21646921906fe6 \ + --hash=sha256:85ed3c67fd39e8d9a36c224758cb6f2f4eb277d07ea677930caa0008c18ec002 \ + --hash=sha256:8763ad01e3725b7751a4575f38bbcc19c0aa0822fec91c5c5bd21ce3ce7e1d2b \ + --hash=sha256:8828369b7d3e93c547cc8ad931b5a57b4e8d174035c82762fb1091e7d05ac9f5 \ + --hash=sha256:8b464316489fb2fca0669ea0f8f07290054a0f26fc72982d3e4cf95469628ba9 \ + --hash=sha256:9125c6dbe8b88c00dd8ef4fc1e55757e8eb4720b6b2b2cc610a45bd32bd28c57 \ + --hash=sha256:93180c2199784dd6a1075b33f9ed636bd0966821edbece6b3d5379b1c4f0bb7d \ + --hash=sha256:932a8892265df7b71257c30e5752635bc1f06a8c4e264024ff031bdf9bb10918 \ + --hash=sha256:947bd4b3438167b3638bf5477cb83a068a586ffb6d331ac427f39839c2b93b3c \ + --hash=sha256:9905bceb7b2833559518574ad6259d2ec9ffd111a0aa330ca685db74478e1ae3 \ + --hash=sha256:9c884240e7415d3a384e70a15ceea0e884cc9289bcc254afd6412d4e7cf99c47 \ + --hash=sha256:a1117c63a39ba4d1b884e658089e512412d5174217ea1b4fe570977e42a5b129 \ + --hash=sha256:a112a1bfdd2621e4344cb0a32dbaab80636b32dac1b055d03fbb2a67d806d1db \ + --hash=sha256:a67ec80d15ac199d4a9a04a33f3039a1c219c9bf1c07b1b0422497613f167fb9 \ + --hash=sha256:a9ca1cdb3f7facb4990c7739ea5afbaceeb6728d066feedde03a4cfe83b29b03 \ + --hash=sha256:abc347e92f9202c8ac1d5c1626a800fd5e56e13433f0651b26dddda5b421ac79 \ + --hash=sha256:b1737f46b1e4a81eb93500a7f2854319e1c7a86e8863fb050b7b4daadd5a4178 \ + --hash=sha256:b33df90f3d1e5b1c8811830b11a3e718b4f3a2823b748fa9be1688cb82b193f1 \ + --hash=sha256:be535bdfbedda84cb8ebc6a80955dfd03d46840c13470486bd038f089e38b172 \ + --hash=sha256:c246aaed719dcdb62eeb7b8d9306a6237777226ef3baad35919c4ae134c91ce7 \ + --hash=sha256:c4b42c92df24a986da7e2b5b44fb142504d858c54a276f9f366983ca4482dfda \ + --hash=sha256:c4fca1e63af6675af3df7cdfcd5a0c878b5e655c7e48611ced9dc8d62183a11d \ + --hash=sha256:d294576fddac636589e4deccfe782e8f429da10f167c1985c4d51071de3672b7 \ + --hash=sha256:d5f45bead708e2c0014be5e98531ce7202916b098a208c7be83c6ceb0a2559fa \ + --hash=sha256:d7c496a966d70f8caf215faddc00de9931a5bf652664221b785a73b20229a696 \ + --hash=sha256:d7dbbdbfdacb85c2d962fa52db791c77943fd777d600d74c95af2d53b32f5a94 \ + --hash=sha256:d8e6e1e5dc684dfce7c33fc8b67a08ba2af94f3a45cfc70d5c1d6a839d2caf97 \ + --hash=sha256:db1285071ea09a7767fac608e7b5c7b03c09833b06186875a359905fbc659d29 \ + --hash=sha256:e084558fbd112d2e1e34b0f5c71e45a3405bdad51a17150368a959bcf6697964 \ + --hash=sha256:e52c6a5be3284719e53b629ccfa565c146e604e861de35e861c94f7622806eb5 \ + --hash=sha256:e78c947e18fadfd690c9420c30a96d221feeb93fc8f1cc00509b370ac16c3114 \ + --hash=sha256:e8df31a126a0a247c1aa379e30873839de03912dea09ca360c680f3625d815df \ + --hash=sha256:e9e7e94472f0e3f1447caf27e1939eb384d0e87972a35a05f5c2e0968e9c01af \ + --hash=sha256:e9f8017443595870aa31f46125553a5c55ce95a26a267b96261baee6ba566d83 \ + --hash=sha256:ef4e2d6e399ce6eecc80179a6b9ef6544f121288f95fc132bc36c9d9503903af \ + --hash=sha256:ef9797bf7c6f9ad9d294538c4f9a64ef3dbbadb63590a9a067393fd49ba28b0f \ + --hash=sha256:efd9a4be6785295e471f71efdf5682bd11d5b822b9665e6e1b4844917cf2f7ac \ + --hash=sha256:f1e9e088094f4895f84ab043e7d59401df137d663efbf1e80c82144882960830 \ + --hash=sha256:f43af38a642c3d6062e9740d8f5cc0feb5dbe0da516702df892147393b8cb14d \ + --hash=sha256:f53837b56ca834f381d300621f8c9525b9da517331ce0f4b805ed08f63bedcd7 \ + --hash=sha256:f7fed45dbadf5d98a52bfff9624d3cca00affeb9543d493c9632b7a53cdd35c9 \ + --hash=sha256:fc1b2cebd6d8db9b4ac0adc817c08b4901922e85604ae2a69aecb5217b2c09d8 # via + # formulaic # opentelemetry-instrumentation # smart-open xlsxwriter==3.2.9 \ diff --git a/semantica/__init__.py b/semantica/__init__.py index e58c358e..cdbcb815 100644 --- a/semantica/__init__.py +++ b/semantica/__init__.py @@ -10,7 +10,7 @@ Main exports: - Config: Configuration management """ -__version__ = "0.6.7" +__version__ = "0.6.8" __author__ = "Semantica Contributors" __license__ = "MIT" diff --git a/semantica/cli.py b/semantica/cli.py index d05a00f1..f920e520 100644 --- a/semantica/cli.py +++ b/semantica/cli.py @@ -917,11 +917,11 @@ def doctor(cli_ctx: CLIContext, local_json: bool, deep_embeddings: bool) -> None for lbl, st, note, hint in checks]) return - tbl = Table(box=_TABLE_BOX, show_edge=False, padding=(0, 2)) - tbl.add_column("Check", style=_KEY, no_wrap=True, min_width=16) - tbl.add_column("Status", no_wrap=True, min_width=6) - tbl.add_column("Note", style=_DIM) - tbl.add_column("Hint", style=_DIM) + tbl = Table(box=_TABLE_BOX, show_edge=False, padding=(0, 2), expand=True) + tbl.add_column("Check", style=_KEY, no_wrap=True, min_width=34) + tbl.add_column("Status", no_wrap=True, min_width=4) + tbl.add_column("Note", style=_DIM, min_width=15, ratio=2, overflow="fold") + tbl.add_column("Hint", style=_DIM, min_width=20, ratio=3, overflow="fold") icons = {"ok": f"[{_SUCCESS}] ✓[/{_SUCCESS}]", "warn": f"[{_WARN_STY}] ⚠[/{_WARN_STY}]", @@ -1155,6 +1155,57 @@ def _get_graph_store(cli_ctx: CLIContext) -> Any: return GraphStore(backend=backend, **graph_db) +def _load_rule_definitions(path: str) -> List[str]: + """Load reasoning rule definitions from a YAML or plain-text rules file. + + YAML files may hold a list of rule strings or a mapping with a ``rules`` + list; anything else (e.g. Datalog) is read as one rule per non-comment + line. The strings are handed to ``Reasoner.add_rule()`` untouched. + """ + text = Path(path).read_text(encoding="utf-8") + try: + data = yaml.safe_load(text) + except yaml.YAMLError: + data = None + if isinstance(data, dict): + rules_value = data.get("rules") + if rules_value is None and "rules" not in data: + raise click.ClickException( + f"Rules file '{path}' is a YAML mapping but has no 'rules' key. " + "Expected either a YAML list or a mapping with a 'rules' list." + ) + data = rules_value + if isinstance(data, list): + return [str(item) for item in data] + return [line.strip() for line in text.splitlines() + if line.strip() and not line.lstrip().startswith("#")] + + +def _graph_store_facts(cli_ctx: CLIContext) -> List[str]: + """Read the configured graph store into Reasoner fact strings. + + Follows the same conventions ``Reasoner.add_fact()`` applies to + KG-style dicts: nodes become ``Label(name)`` and relationships become + ``TYPE(source, target)``, with internal node ids resolved to names. + """ + gs = _get_graph_store(cli_ctx) + nodes = gs.get_nodes(limit=sys.maxsize) + relationships = gs.get_relationships(limit=sys.maxsize) + names: Dict[Any, Any] = {} + facts: List[str] = [] + for node in nodes: + props = node.get("properties") or {} + name = props.get("name") or props.get("id") or node.get("id") + names[node.get("id")] = name + for label in node.get("labels") or ["Entity"]: + facts.append(f"{label}({name})") + for rel in relationships: + source = names.get(rel.get("start_node_id"), rel.get("start_node_id")) + target = names.get(rel.get("end_node_id"), rel.get("end_node_id")) + facts.append(f"{rel.get('type', 'RELATED_TO')}({source}, {target})") + return facts + + # ─── Output helpers ────────────────────────────────────────────────────────── @@ -1415,6 +1466,31 @@ _INGEST_TYPES = [ ] _INGEST_FORMATS = ["pdf", "docx", "csv", "excel", "html", "json", "parquet", "xml", "rdf"] +_GRAPH_STORE_ENV_BACKEND_HINTS = { + "GRAPH_STORE_NEO4J_URI": "neo4j", + "GRAPH_STORE_FALKORDB_HOST": "falkordb", + "GRAPH_STORE_NEPTUNE_ENDPOINT": "neptune", + "GRAPH_STORE_AGE_CONNECTION_STRING": "age", +} + + +def _configured_ingest_graph_backend( + cli_ctx: CLIContext, store_override: Optional[str] +) -> Optional[str]: + graph_db = dict(cli_ctx.config.to_dict().get("graph_db", {})) + backend = store_override or cli_ctx.store_backend or graph_db.get("backend") + if backend: + return str(backend) + + env_backend = os.environ.get("GRAPH_STORE_DEFAULT_BACKEND") + if env_backend: + return env_backend + + for env_var, hinted_backend in _GRAPH_STORE_ENV_BACKEND_HINTS.items(): + if os.environ.get(env_var): + return hinted_backend + + return None @main.command() @@ -1427,8 +1503,13 @@ _INGEST_FORMATS = ["pdf", "docx", "csv", "excel", "html", "json", "parquet", "xm @click.option("--watch", is_flag=True, default=False, help="Re-ingest on file changes.") @click.option("--batch-size", default=500, type=int, show_default=True) @click.option("--store", "store_override", default=None, - help="Target graph backend: neo4j falkordb age neptune") -@click.option("--output", default=None, type=click.Path(), help="Write to file instead of graph store.") + help="Target graph backend: neo4j falkordb age neptune. " + "Not yet implemented — ingest cannot persist to a graph " + "store, so this only determines whether the command " + "refuses to report false success; use --output instead.") +@click.option("--output", default=None, type=click.Path(), + help="Write ingested content to a .json/.jsonl/.csv file " + "instead of the (unimplemented) graph store.") @click.option("--dry-run", "local_dry", is_flag=True, default=False) @click.option("--json", "local_json", is_flag=True, default=False) @click.pass_obj @@ -1452,6 +1533,19 @@ def ingest( _dry(cli_ctx, "ingest", json_out=_is_json(cli_ctx, local_json), source=source, type=ingestor_type, format=fmt) return + graph_backend = _configured_ingest_graph_backend(cli_ctx, store_override) + if graph_backend and graph_backend.lower() != "memory" and not output: + raise click.ClickException( + f"A graph backend is configured ({graph_backend}), but " + "semantica ingest does not write to graph stores yet — no CLI " + "command currently does (tracked in issues #1351, #1352). " + "Pass --output .json to save the ingested content " + "instead, or build a GraphStore/GraphBuilder directly in " + "Python." + ) + # NOTE: --store/GRAPH_STORE_DEFAULT_BACKEND are read only to decide + # whether to raise the error above — nothing downstream of this point + # writes to a graph store, so neither is forwarded as an ingest kwarg. kwargs: Dict[str, Any] = {"batch_size": batch_size} if ingestor_type: kwargs["source_type"] = ingestor_type @@ -1461,10 +1555,6 @@ def ingest( kwargs["recursive"] = True if watch: kwargs["watch"] = True - if store_override or cli_ctx.store_backend: - kwargs["store"] = store_override or cli_ctx.store_backend - if output: - kwargs["output"] = output try: from .ingest import ingest as _ingest label = Path(source).name if Path(source).exists() else source @@ -1478,7 +1568,10 @@ def ingest( result = _ingest(source, **kwargs) except ImportError as exc: raise click.ClickException(f"Ingest module not available: {exc}") from exc - if _is_json(cli_ctx, local_json): + if output: + _write_result_output(Path(output), result) + _ok(cli_ctx, f"Wrote {output}") + elif _is_json(cli_ctx, local_json): _jecho(result if isinstance(result, dict) else {"status": "ok"}) else: _ok(cli_ctx, f"Ingested: {source}") @@ -1735,9 +1828,19 @@ def embed(ctx: click.Context) -> None: def _json_default(obj) -> object: """JSON serialiser that converts NumPy scalars/arrays to native Python types. + Also expands dataclasses (e.g. ``FileObject`` from ``ingest``) to plain + dicts and decodes ``bytes`` as UTF-8 text where possible, so a domain + object round-trips through ``--output`` as data instead of a repr string. Falls back to ``str()`` for everything else so the writer never crashes on - unexpected types (e.g. ``datetime``, custom domain objects). + unexpected types (e.g. ``datetime``). """ + if is_dataclass(obj) and not isinstance(obj, type): + return asdict(obj) + if isinstance(obj, bytes): + try: + return obj.decode("utf-8") + except UnicodeDecodeError: + return obj.hex() try: import numpy as np # local import — only needed when result contains numpy if isinstance(obj, np.ndarray): @@ -2160,17 +2263,43 @@ def reason_run(cli_ctx: CLIContext, engine: str, rules: Optional[str], cli_ctx = _require_ctx(cli_ctx) def _action() -> None: + # Only the forward-chaining production-rule engines run through + # Reasoner.infer_facts(); the other engines take different inputs + # (SPARQL/Datalog queries, observations, premises) and are not wired + # to this command yet. Fail honestly instead of silently + # forward-chaining under another engine's name. + if engine not in ("rete", "forward-chain"): + hint = (" Use 'semantica reason query' for SPARQL/Datalog queries." + if engine in ("sparql", "datalog") else "") + raise click.ClickException( + f"Engine '{engine}' is not wired to 'reason run' yet; " + f"supported engines: rete, forward-chain.{hint}") try: from .reasoning import Reasoner + # Reasoner has no run() method (#1354); dispatch to its real + # API: facts from the configured graph store + rules from the + # optional --rules file into infer_facts(). r = Reasoner(engine=engine, config=cli_ctx.config.to_dict()) + rule_defs = _load_rule_definitions(rules) if rules else None + facts = _graph_store_facts(cli_ctx) + + def _infer() -> Dict[str, Any]: + inferred = r.infer_facts(facts, rule_defs) + return { + "engine": engine, + "facts": len(facts), + "inferred_count": len(inferred), + "inferred_facts": inferred, + } + if cli_ctx.quiet or cli_ctx.json_output: - result = r.run(rules_file=rules) + result = _infer() else: with console.status( f"[{_DIM}]Running {engine} reasoning engine…[/{_DIM}]", spinner="dots", ): - result = r.run(rules_file=rules) + result = _infer() except ImportError as exc: raise click.ClickException(f"Reasoning module not available: {exc}") from exc if _is_json(cli_ctx, local_json): @@ -3661,14 +3790,17 @@ def store_connect(cli_ctx: CLIContext, backend: str, uri: Optional[str], local_j def _action() -> None: try: - from .graph_store import get_graph_store_method - store_cls = get_graph_store_method(backend) + # get_graph_store_method(task, method_name) is the method + # registry, not a backend factory (#1354); build the store + # through GraphStore, which resolves the backend by name. + from .graph_store import GraphStore cfg = dict(cli_ctx.config.to_dict().get("graph_db", {})) + cfg.pop("backend", None) if uri: cfg["uri"] = uri - # Attempt instantiation as the minimal connectivity probe; backends - # that require a live connection will fail here if unreachable. - store_instance = store_cls(config=cfg) + # Instantiation only wires the backend; the probe below performs + # the live connectivity check and raises if unreachable. + store_instance = GraphStore(backend=backend, **cfg) for probe in ("health_check", "ping", "connect"): fn = getattr(store_instance, probe, None) if callable(fn): @@ -4571,7 +4703,7 @@ def mcp_start(cli_ctx: CLIContext, transport: str, port: int) -> None: def _action() -> None: import subprocess as sp - cmd = [sys.executable, "-m", "mcp.server"] + cmd = [sys.executable, "-m", "semantica_mcp.mcp.server"] if transport == "http": cmd += ["--port", str(port)] proc = sp.Popen(cmd) @@ -4614,7 +4746,7 @@ def mcp_list_tools(cli_ctx: CLIContext, local_json: bool) -> None: def _action() -> None: try: - from mcp.tools import __all__ as tools + from semantica_mcp.mcp.tools import __all__ as tools except ImportError: tools = [ "extract_entities", "extract_relations", "build_graph", @@ -4655,7 +4787,7 @@ def mcp_call(cli_ctx: CLIContext, tool_name: str, args: str, local_json: bool) - except json.JSONDecodeError as exc: raise click.ClickException(f"Invalid JSON in --args: {exc}") from exc try: - from mcp.session import MCPSession + from semantica_mcp.mcp.session import MCPSession session = MCPSession(config=cli_ctx.config.to_dict()) result = session.call_tool(tool_name, **tool_args) except ImportError as exc: diff --git a/semantica/context/context_graph.py b/semantica/context/context_graph.py index e3f369fa..1bb2ec84 100644 --- a/semantica/context/context_graph.py +++ b/semantica/context/context_graph.py @@ -249,6 +249,42 @@ def _normalize_temporal_input(value: Optional[Union[str, int, float, datetime]]) raise ValueError("Temporal values must be datetime, epoch seconds, ISO strings, or None") +def normalize_temporal_input( + value: Optional[Union[str, int, float, datetime]] +) -> Optional[str]: + """Normalize a temporal value to a tz-naive UTC ISO-8601 string. + + This is the public surface of the normalization logic used throughout + :class:`ContextGraph` for retraction, purge, and decision timestamps. + Exposing it lets sibling modules (e.g. :mod:`erasure`) share the same + normalization without importing the private ``_normalize_temporal_input``. + + Args: + value: Any of the following: + + * ``None`` — returned as-is (no timestamp). + * :class:`~datetime.datetime` — converted to UTC if tz-aware, + then serialized as a tz-naive ISO string + (e.g. ``"2026-01-01T07:00:00"``). + * :class:`int` or :class:`float` — interpreted as a POSIX epoch + seconds value, converted to UTC, serialized as above. + * :class:`str` — must be a valid ISO-8601 datetime string; + offset-aware values (including ``Z``) are converted to UTC + before serialization. Year-only (``"2026"``) and date-only + (``"2026-01-15"``) shorthand forms are also accepted. + + Returns: + A tz-naive UTC ISO-8601 string (e.g. ``"2026-01-01T12:00:00"``), + or ``None`` when *value* is ``None``. + + Raises: + ValueError: If *value* is a string that cannot be parsed as an + ISO-8601 datetime, or if *value* is a type that is not + supported (e.g. a :class:`~datetime.date` object). + """ + return _normalize_temporal_input(value) + + def _closing_valid_until(current: Optional[str], at_iso: str) -> str: """Return the earlier of an existing end bound and a retraction time. @@ -4755,14 +4791,18 @@ class ContextGraph: # Find potential causes (decisions that influenced this one) via # shared entities/timestamps - additive heuristic, skipping anything - # already covered by an explicit relationship above. - potential_causes = [] + # already covered by an explicit relationship above. Deduplicate by + # decision id (dict preserves insertion order): a decision sharing + # several entities with the current one is one potential cause, + # not one per shared entity, otherwise the trace reports the same + # "influences" chain once per overlapping entity. + potential_causes = {} for entity in current_decision["entities"]: for other_decision_id in self._entity_index.get(entity, set()): if other_decision_id != current_id and other_decision_id not in explicit_cause_ids: other_decision = self._decisions[other_decision_id] if other_decision["timestamp"] < current_decision["timestamp"]: - potential_causes.append(other_decision_id) + potential_causes[other_decision_id] = None for cause_id in potential_causes: cause_dec = self._decisions.get(cause_id, {}) diff --git a/semantica/context/erasure.py b/semantica/context/erasure.py index 04f7fa72..f1720177 100644 --- a/semantica/context/erasure.py +++ b/semantica/context/erasure.py @@ -34,13 +34,14 @@ Example: 'unsupported' """ +import copy import inspect from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union from ..utils.logging import get_logger -from .context_graph import _normalize_temporal_input +from .context_graph import normalize_temporal_input __all__ = [ "ErasureCoordinator", @@ -117,13 +118,21 @@ class ErasureReceipt: ] def to_dict(self) -> Dict[str, Any]: - """Serialize the receipt, deep-copying the per-store results.""" + """Serialize the receipt, deep-copying the per-store results. + + Each store result is copied recursively so the returned payload shares + no mutable objects with the live receipt: mutating + ``payload["stores"][name][...]`` (including nested dicts such as a + vector backend's ``backend_result``) cannot corrupt the audit record. + """ return { "entity_id": self.entity_id, "reason": self.reason, "erased_at": self.erased_at, "complete": self.complete, - "stores": {name: dict(result) for name, result in self.stores.items()}, + "stores": { + name: copy.deepcopy(result) for name, result in self.stores.items() + }, } @@ -593,7 +602,7 @@ def _normalize_timestamp(at: Optional[Union[str, int, float, datetime]]) -> str: default path gets one timestamp for both records instead of two ``now()`` calls separated by the length of the cascade. """ - return _normalize_temporal_input( + return normalize_temporal_input( at if at is not None else datetime.now(timezone.utc) ) diff --git a/semantica/explorer/routes/ontology.py b/semantica/explorer/routes/ontology.py index e723d166..66fb6efb 100644 --- a/semantica/explorer/routes/ontology.py +++ b/semantica/explorer/routes/ontology.py @@ -35,6 +35,10 @@ router = APIRouter(prefix="/api/ontology", tags=["ontology"]) _MAX_FETCH_BYTES = 20 * 1024 * 1024 # 20 MB _MAX_ANALYSIS_NODES = 5_000 # cap for health/suggest/shacl node scans to avoid OOM _MAX_ENTITIES_PER_SIDE = 500 # per-ontology cap for the O(n²) pairwise suggestion loop +_GRAPH_TOO_LARGE_DETAIL = ( + "Ontology editor graph exceeds the maximum size " + f"({_MAX_ANALYSIS_NODES} nodes or edges)." +) class GraphTruncationError(Exception): @@ -72,6 +76,20 @@ _ONTOLOGY_TYPES = frozenset({ }) | _SCHEME_TYPES _SEARCHABLE_TYPES = _CLASS_TYPES | _PROPERTY_TYPES | _INDIVIDUAL_TYPES | _CONCEPT_TYPES | _SCHEME_TYPES +_SCHEMA_NODE_TYPES = _CLASS_TYPES | _PROPERTY_TYPES | _CONCEPT_TYPES | _ONTOLOGY_TYPES +_STRUCTURE_EDGE_TYPES = frozenset({ + "rdf:type", + "rdfs:subClassOf", + "rdfs:domain", + "rdfs:range", + "owl:disjointWith", + "owl:equivalentClass", + "owl:equivalentProperty", + "owl:inverseOf", + "skos:broader", + "skos:narrower", + "skos:related", +}) _URI_PREFIX_MAP = { "http://www.w3.org/2002/07/owl#": "owl:", @@ -1821,6 +1839,64 @@ async def search_entities( return results +def _known_ontology_uris( + session: GraphSession, registry: Dict[str, OntologyEntry] +) -> set[str]: + known = set(registry) + for node_type in _ONTOLOGY_TYPES: + for node in session.iter_nodes(node_type=node_type): + node_id = str(node.get("id", "")) + if node_id: + known.add(node_id) + return known + + +def _collect_core_nodes( + session: GraphSession, uri: str, known_ontology_uris: set[str] +) -> Dict[str, Dict[str, Any]]: + """Stream schema nodes, keeping only the ones this ontology owns. + + Filtering as each node arrives makes _MAX_ANALYSIS_NODES bound the work and + not merely the response: foreign nodes are discarded instead of materialized, + and the scan stops once the owned ones pass the cap. The ownership filter has + to stay ahead of that check — thousands of *other* ontologies' nodes must + never make this one too large to open. Requesting pages instead would bound + nothing: paginate_nodes normalizes the whole matching set on every call. + """ + core_nodes_by_id: Dict[str, Dict[str, Any]] = {} + for node_type in _SCHEMA_NODE_TYPES: + for node in session.iter_nodes(node_type=node_type): + node_id = str(node.get("id", "")) + if not node_id or not _node_belongs_to_ontology( + node, uri, known_ontology_uris + ): + continue + core_nodes_by_id[node_id] = node + if len(core_nodes_by_id) > _MAX_ANALYSIS_NODES: + raise GraphTruncationError(_GRAPH_TOO_LARGE_DETAIL) + return core_nodes_by_id + + +def _select_structure_edges( + session: GraphSession, core_node_ids: set[str] +) -> List[Dict[str, Any]]: + """Stream structural edges, keeping only those leaving a core node. + + The requested ontology may reference outward (e.g. rdfs:range to an external + vocabulary), but an unrelated ontology's property pointing at a core class + must not leak inward. + """ + selected_edges: List[Dict[str, Any]] = [] + for edge_type in _STRUCTURE_EDGE_TYPES: + for edge in session.iter_edges(edge_type=edge_type): + if str(edge.get("source", "")) not in core_node_ids: + continue + selected_edges.append(edge) + if len(selected_edges) > _MAX_ANALYSIS_NODES: + raise GraphTruncationError(_GRAPH_TOO_LARGE_DETAIL) + return selected_edges + + @router.get("/graph", response_model=OntologyGraphResponse) async def get_ontology_graph( request: Request, @@ -1828,88 +1904,39 @@ async def get_ontology_graph( session: GraphSession = Depends(get_session), ): """Return the editable schema subgraph for one registered ontology.""" - registry = _get_registry(request) - ontology_nodes: List[Dict[str, Any]] = [] - for node_type in _ONTOLOGY_TYPES: - nodes, _ = await asyncio.to_thread( - session.get_nodes, node_type=node_type, skip=0, limit=2**63 - 1 - ) - ontology_nodes.extend(nodes) - known_ontology_uris = set(registry) | { - str(node.get("id", "")) for node in ontology_nodes if node.get("id") - } + known_ontology_uris = await asyncio.to_thread( + _known_ontology_uris, session, _get_registry(request) + ) if uri not in known_ontology_uris: raise HTTPException(status_code=404, detail="Ontology not found in registry.") - schema_types = _CLASS_TYPES | _PROPERTY_TYPES | _CONCEPT_TYPES | _ONTOLOGY_TYPES - candidates_by_id: Dict[str, Dict[str, Any]] = {} - for node_type in schema_types: - nodes, _ = await asyncio.to_thread( - session.get_nodes, node_type=node_type, skip=0, limit=2**63 - 1 + try: + core_nodes_by_id = await asyncio.to_thread( + _collect_core_nodes, session, uri, known_ontology_uris ) - candidates_by_id.update( - (str(node.get("id", "")), node) for node in nodes if node.get("id") + if not core_nodes_by_id: + raise HTTPException(status_code=404, detail="Ontology graph not found.") + core_node_ids = set(core_nodes_by_id) + selected_edges = await asyncio.to_thread( + _select_structure_edges, session, core_node_ids ) + except GraphTruncationError as exc: + raise HTTPException(status_code=413, detail=str(exc)) from exc - core_node_ids = { - str(node.get("id", "")) - for node in candidates_by_id.values() - if _node_belongs_to_ontology(node, uri, known_ontology_uris) - } - if not core_node_ids: - raise HTTPException(status_code=404, detail="Ontology graph not found.") + # Invariant: the helpers raise the moment their accumulation passes + # _MAX_ANALYSIS_NODES, so core_nodes_by_id and selected_edges are both + # within the cap here; a post-filter re-check would be unreachable. + external_node_ids = { + node_id + for edge in selected_edges + for node_id in (str(edge.get("source", "")), str(edge.get("target", ""))) + } - core_node_ids + external_nodes = await asyncio.gather( + *(asyncio.to_thread(session.get_node, node_id) for node_id in external_node_ids) + ) - structure_edge_types = { - "rdf:type", - "rdfs:subClassOf", - "rdfs:domain", - "rdfs:range", - "owl:disjointWith", - "owl:equivalentClass", - "owl:equivalentProperty", - "owl:inverseOf", - "skos:broader", - "skos:narrower", - "skos:related", - } - selected_edges: List[Dict[str, Any]] = [] - for edge_type in structure_edge_types: - edges, _ = await asyncio.to_thread( - session.get_edges, - edge_type=edge_type, - skip=0, - limit=2**63 - 1, - ) - # Keep only edges whose source is a core node: the requested ontology - # may reference outward (e.g. rdfs:range to an external vocabulary), - # but an unrelated ontology's property pointing at a core class must - # not leak inward. - selected_edges.extend( - edge for edge in edges - if str(edge.get("source", "")) in core_node_ids - ) - if ( - len(core_node_ids) > _MAX_ANALYSIS_NODES - or len(selected_edges) > _MAX_ANALYSIS_NODES - ): - raise HTTPException( - status_code=413, - detail=( - "Ontology editor graph exceeds the maximum size " - f"({_MAX_ANALYSIS_NODES} nodes or edges)." - ), - ) - - selected_node_ids = set(core_node_ids) - for edge in selected_edges: - selected_node_ids.add(str(edge.get("source", ""))) - selected_node_ids.add(str(edge.get("target", ""))) - - selected_nodes = [candidates_by_id[node_id] for node_id in core_node_ids] - for node_id in selected_node_ids - core_node_ids: - external = await asyncio.to_thread(session.get_node, node_id) - if external is not None: - selected_nodes.append(external) + selected_nodes = list(core_nodes_by_id.values()) + selected_nodes.extend(node for node in external_nodes if node is not None) selected_nodes.sort(key=lambda node: str(node.get("id", ""))) selected_edges.sort( key=lambda edge: ( @@ -2877,7 +2904,8 @@ async def validate_shacl( status="error", message=( f"SHACL Turtle size ({len(_shacl_bytes)} bytes) " - f"exceeds maximum allowed size ({_MAX_SHACL_TURTLE_BYTES} bytes)." + f"exceeds maximum allowed size ({_MAX_SHACL_TURTLE_BYTES} bytes); " + f"set SEMANTICA_MAX_SHACL_TURTLE_BYTES to raise the limit." ), violations=[], ) @@ -2894,7 +2922,8 @@ async def validate_shacl( status="error", message=( f"SHACL graph triple count ({len(g)}) " - f"exceeds maximum allowed limit ({_MAX_SHACL_TRIPLES})." + f"exceeds maximum allowed limit ({_MAX_SHACL_TRIPLES}); " + f"set SEMANTICA_MAX_SHACL_TRIPLES to raise the limit." ), violations=[], ) @@ -2945,7 +2974,8 @@ async def validate_shacl( conforms=False, status="error", message=( - f"SHACL validation timed out after {_MAX_SHACL_TIMEOUT_SECONDS} seconds." + f"SHACL validation timed out after {_MAX_SHACL_TIMEOUT_SECONDS} seconds; " + f"set SEMANTICA_MAX_SHACL_TIMEOUT to raise the timeout." ), violations=[], ) diff --git a/semantica/explorer/session.py b/semantica/explorer/session.py index abdc1711..1ed937fb 100644 --- a/semantica/explorer/session.py +++ b/semantica/explorer/session.py @@ -9,7 +9,7 @@ import threading import time import uuid from datetime import UTC, datetime -from typing import Any, Dict, Iterable, List, Optional +from typing import Any, Dict, Iterable, Iterator, List, Optional from ..context.context_graph import ContextGraph, _resolve_edge_identity from .search_index import GraphSearchIndex @@ -375,6 +375,52 @@ class GraphSession: ) return page, total + def iter_nodes(self, node_type: Optional[str] = None) -> Iterator[Dict[str, Any]]: + """Yield matching nodes one at a time, in the same order as ``paginate_nodes``. + + ``paginate_nodes`` normalizes and holds the entire matching set before it + slices out a page, so a caller that filters the result down itself cannot + bound its cost by asking for smaller pages — it would re-pay that full + cost per page. Streaming lets such a caller retain only what it selects + and stop scanning as soon as it has enough. + + Only the id list is snapshotted under the lock; nodes are read one at a + time, so a concurrent mutation can be observed mid-iteration and ids that + disappear are skipped. ``paginate_nodes`` is the atomic alternative. + """ + with self._lock: + source_ids = ( + self.graph.node_type_index.get(node_type, set()) + if node_type + else self.graph.nodes.keys() + ) + node_ids = sorted( + (node_id for node_id in source_ids if node_id is not None), + key=lambda value: str(value), + ) + for node_id in node_ids: + with self._lock: + raw = self.graph.find_node(node_id) + if raw is None: + continue + yield self.normalize_node(raw) + + def iter_edges(self, edge_type: Optional[str] = None) -> Iterator[Dict[str, Any]]: + """Yield matching edges one at a time, in raw graph order. + + Same rationale as ``iter_nodes``. Edge normalization derives an identity + hash per edge, which ``paginate_edges`` pays for every matching edge (and + then sorts) before paging; a filtering caller only needs it for the edges + it keeps. Callers that need a stable order sort the subset they select. + """ + with self._lock: + raw_edges = self.graph.find_edges(edge_type=edge_type) + for edge in raw_edges: + normalized = self.normalize_edge(edge) + if not normalized["source"] or not normalized["target"]: + continue + yield normalized + def get_raw_counts(self) -> tuple[int, int]: """O(1) node/edge counts from the raw collections, with no per-item normalization. diff --git a/semantica/semantic_extract/ner_extractor.py b/semantica/semantic_extract/ner_extractor.py index 89df3e1e..242a0f9f 100644 --- a/semantica/semantic_extract/ner_extractor.py +++ b/semantica/semantic_extract/ner_extractor.py @@ -37,7 +37,7 @@ Key Features: * LLM-based: Large language model extraction - Fallback chain support: Try methods in order until one succeeds - Robust Fallbacks: Prevents empty results via ML -> Pattern -> Last Resort chain - - Ensemble voting: Combine results from multiple methods + - Explicit merge strategies: fallback, union, and consensus - Post-processing: Entity boundary validation - Multiple entity type support (PERSON, ORG, GPE, DATE, etc.) - Confidence scoring and filtering @@ -62,15 +62,20 @@ Example Usage: >>> extractor = NERExtractor(method="huggingface", huggingface_model="dslim/bert-base-NER") >>> entities = extractor.extract_entities("Apple Inc. was founded in 1976.") >>> - >>> # Using fallback chain - >>> extractor = NERExtractor(method=["llm", "ml", "pattern"], ensemble_voting=True) + >>> # Require agreement between multiple extraction methods + >>> extractor = NERExtractor( + ... method=["llm", "ml"], merge_strategy="consensus", min_votes=2 + ... ) >>> entities = extractor.extract_entities("Apple Inc. was founded in 1976.") Author: Semantica Contributors License: MIT """ -from typing import Any, Dict, List, Optional, Tuple, Union +import math +import re +import warnings +from typing import Any, Dict, List, Optional, Sequence, Tuple, Union from ..utils.exceptions import ProcessingError from ..utils.helpers import safe_import @@ -84,6 +89,31 @@ spacy, SPACY_AVAILABLE = safe_import("spacy") class NERExtractor: """Named Entity Recognition extractor.""" + _VALID_MERGE_STRATEGIES = {"fallback", "union", "consensus"} + _MERGE_OPTION_KEYS = ( + "merge_strategy", + "min_votes", + "min_agreement", + "method_weights", + "eligible_methods", + ) + _MIN_SPAN_IOU = 0.5 + _LABEL_ALIASES = { + "PER": "PERSON", + "PERSON": "PERSON", + "ORGANIZATION": "ORG", + "ORG": "ORG", + "LOCATION": "GPE", + "LOC": "GPE", + "GPE": "GPE", + "TIME": "DATE", + "DATE": "DATE", + "CURRENCY": "MONEY", + "MONEY": "MONEY", + "PERCENTAGE": "PERCENT", + "PERCENT": "PERCENT", + } + def __init__( self, method: Union[str, List[str]] = "ml", @@ -115,9 +145,16 @@ class NERExtractor: third-party servers (Qwen, LLaMA gateways, etc.) that do not implement the full function-calling protocol still return correctly structured results. - - device: Device for HuggingFace models ("cuda" or "cpu") - - min_confidence: Minimum confidence threshold - - ensemble_voting: Enable ensemble voting (default: False) + - device: Device for HuggingFace models ("cuda" or "cpu") + - min_confidence: Minimum confidence threshold + - merge_strategy: "fallback" (default), "union", or "consensus" + - min_votes: Required supporting methods for consensus (default: 2) + - min_agreement: Optional minimum support ratio for consensus + - method_weights: Optional method weights for exact-span + cross-label tie-breaking + - eligible_methods: Optional subset of configured methods to count + as consensus voters + - ensemble_voting: Deprecated alias for merge_strategy="union" - post_process: Enable post-processing (default: False) """ self.logger = get_logger("ner_extractor") @@ -133,6 +170,15 @@ class NERExtractor: self.language = config.get("language", "en") self.min_confidence = config.get("min_confidence", 0.5) self.ensemble_voting = config.get("ensemble_voting", False) + self.merge_strategy = self._resolve_merge_strategy(config) + self.min_votes = self._validate_min_votes(config.get("min_votes", 2)) + self.min_agreement = self._validate_min_agreement( + config.get("min_agreement") + ) + self.method_weights = self._validate_method_weights( + config.get("method_weights") + ) + self.eligible_methods = config.get("eligible_methods") self.post_process = config.get("post_process", False) self.progress_tracker = get_progress_tracker() # Ensure progress tracker is enabled @@ -164,6 +210,240 @@ class NERExtractor: exc_info=True, ) + def _resolve_merge_strategy(self, config: Dict[str, Any]) -> str: + """Resolve the explicit merge strategy and the deprecated legacy flag.""" + configured_strategy = config.get("merge_strategy") + if configured_strategy is None: + if self.ensemble_voting: + warnings.warn( + "ensemble_voting is deprecated because it historically " + "performed a union, not voting. Use merge_strategy='union' " + "or merge_strategy='consensus' explicitly.", + DeprecationWarning, + stacklevel=3, + ) + return "union" + return "fallback" + + strategy = self._validate_merge_strategy(configured_strategy) + if self.ensemble_voting: + warnings.warn( + "ensemble_voting is deprecated and ignored when merge_strategy " + "is provided.", + DeprecationWarning, + stacklevel=3, + ) + return strategy + + @classmethod + def _validate_merge_strategy(cls, strategy: Any) -> str: + """Return a normalized merge strategy or raise a useful configuration error.""" + if not isinstance(strategy, str): + raise ValueError( + "merge_strategy must be one of: fallback, union, consensus" + ) + + normalized = strategy.lower() + if normalized not in cls._VALID_MERGE_STRATEGIES: + raise ValueError( + "merge_strategy must be one of: fallback, union, consensus" + ) + return normalized + + @staticmethod + def _validate_min_votes(min_votes: Any) -> int: + """Validate the number of method votes required for consensus.""" + if isinstance(min_votes, bool) or not isinstance(min_votes, int): + raise ValueError("min_votes must be a positive integer") + if min_votes < 1: + raise ValueError("min_votes must be a positive integer") + return min_votes + + @staticmethod + def _validate_min_agreement(min_agreement: Any) -> Optional[float]: + """Validate an optional consensus support ratio.""" + if min_agreement is None: + return None + + try: + normalized = float(min_agreement) + except (TypeError, ValueError): + raise ValueError("min_agreement must be a number between 0 and 1") + + if not math.isfinite(normalized) or not 0.0 <= normalized <= 1.0: + raise ValueError("min_agreement must be a number between 0 and 1") + return normalized + + @classmethod + def _validate_method_weights(cls, method_weights: Any) -> Dict[str, float]: + """Validate optional positive method weights used for deterministic ties.""" + if method_weights is None: + return {} + if not isinstance(method_weights, dict): + raise ValueError("method_weights must be a mapping of method names to weights") + + normalized = {} + for method_name, weight in method_weights.items(): + if not isinstance(method_name, str): + raise ValueError("method_weights keys must be method names") + try: + numeric_weight = float(weight) + except (TypeError, ValueError): + raise ValueError("method_weights values must be positive numbers") + if not math.isfinite(numeric_weight) or numeric_weight <= 0: + raise ValueError("method_weights values must be positive numbers") + identity = cls._method_identity(method_name) + existing_weight = normalized.get(identity) + if existing_weight is not None and existing_weight != numeric_weight: + raise ValueError( + "method_weights assigns conflicting values to aliases for " + f"backend '{identity}'" + ) + normalized[identity] = numeric_weight + return normalized + + @staticmethod + def _method_identity(method_name: str) -> str: + """Normalize aliases that share one extraction backend for vote counting.""" + normalized = method_name.lower() + return "ml" if normalized in {"ml", "spacy"} else method_name + + def _resolve_eligible_methods( + self, + methods: Sequence[str], + configured_methods: Any = None, + ) -> List[str]: + """Resolve the configured method names that are eligible consensus voters.""" + available = [] + seen = set() + for method_name in methods: + identity = self._method_identity(method_name) + if identity not in seen: + available.append((identity, method_name)) + seen.add(identity) + + configured = ( + self.eligible_methods + if configured_methods is None + else configured_methods + ) + if configured is None: + return [method_name for _, method_name in available] + if isinstance(configured, str): + configured = [configured] + + try: + configured = list(configured) + except TypeError: + raise ValueError("eligible_methods must be a sequence of method names") + + requested_identities = set() + for method_name in configured: + if not isinstance(method_name, str): + raise ValueError("eligible_methods must be a sequence of method names") + requested_identities.add(self._method_identity(method_name)) + + available_identities = {identity for identity, _ in available} + unknown_methods = [ + method_name + for method_name in configured + if self._method_identity(method_name) not in available_identities + ] + if unknown_methods: + raise ValueError( + "eligible_methods contains methods not configured for extraction: " + + ", ".join(unknown_methods) + ) + + return [ + method_name + for identity, method_name in available + if identity in requested_identities + ] + + def _align_entities_to_text( + self, entities: List[Entity], text: str + ) -> List[Entity]: + """Resolve missing offsets before span-based methods are merged. + + Some providers, notably typed LLM extraction, can return text and + labels without offsets. For a single method that is harmless, but a + span-based merge needs document locations. Missing spans are therefore + aligned by a deterministic, per-label text search. Valid provider + offsets are preserved; candidates that cannot be aligned are excluded + because union and consensus cannot safely merge them. + """ + next_offsets = {} + occupied_offsets = {} + aligned = [] + + for entity in entities: + needle = entity.text + if not isinstance(needle, str) or not needle: + continue + + key = (needle.casefold(), self._canonical_label(entity.label)) + start_char = entity.start_char + end_char = entity.end_char + has_valid_span = ( + isinstance(start_char, int) + and isinstance(end_char, int) + and 0 <= start_char < end_char <= len(text) + and text[start_char:end_char].casefold() == needle.casefold() + ) + if has_valid_span: + aligned.append(entity) + next_offsets[key] = max(next_offsets.get(key, 0), end_char) + occupied_offsets.setdefault(key, set()).add((start_char, end_char)) + continue + + prior_offset = next_offsets.get(key, 0) + hinted_start = start_char if isinstance(start_char, int) else 0 + search_start = max(prior_offset, min(max(hinted_start, 0), len(text))) + occupied = occupied_offsets.setdefault(key, set()) + match = None + match_offset = 0 + left_boundary = ( + r"(? Union[List[Entity], List[List[Entity]]]: """ Alias for extract_entities. @@ -347,11 +627,39 @@ class NERExtractor: ) return [] - # Use method from options if provided, otherwise use instance method - methods = options.get("method", self.method) - if isinstance(methods, str): - methods = [methods] - methods = self._filter_unusable_methods(methods) + # Use method from options if provided, otherwise use instance method. + # Keep the requested list separate from the executable list: in + # consensus mode, a configured method with no result is still an + # eligible non-supporting vote. + requested_methods = options.get("method", self.method) + if isinstance(requested_methods, str): + requested_methods = [requested_methods] + + merge_strategy = self._validate_merge_strategy( + options.get("merge_strategy", self.merge_strategy) + ) + if merge_strategy == "consensus": + eligible_methods = self._resolve_eligible_methods( + requested_methods, + options.get("eligible_methods", self.eligible_methods), + ) + else: + # eligible_methods is a consensus-only setting. Union should + # retain every configured method's complementary output. + eligible_methods = self._resolve_eligible_methods( + requested_methods, requested_methods + ) + methods = self._filter_unusable_methods(requested_methods) + + min_votes = self._validate_min_votes( + options.get("min_votes", self.min_votes) + ) + min_agreement = self._validate_min_agreement( + options.get("min_agreement", self.min_agreement) + ) + method_weights = self._validate_method_weights( + options.get("method_weights", self.method_weights) + ) min_confidence = options.get("min_confidence", self.min_confidence) entity_types = options.get("entity_types", self.entity_types) @@ -361,7 +669,9 @@ class NERExtractor: if entity_types: all_options["entity_types"] = entity_types - # Try each method in order (fallback chain) + # Try each method in order. Fallback returns the first non-empty + # result; union and consensus keep empty method results so their + # denominators retain configured method provenance. all_entities = [] for method_name in methods: try: @@ -373,6 +683,8 @@ class NERExtractor: # Prepare method-specific options method_options = all_options.copy() + for merge_option in self._MERGE_OPTION_KEYS: + method_options.pop(merge_option, None) if method_name == "huggingface": # Prioritize runtime options over config/defaults method_options["model"] = ( @@ -400,6 +712,8 @@ class NERExtractor: method_options["api_key"] = api_key entities = method_func(text, **method_options) + if merge_strategy != "fallback": + entities = self._align_entities_to_text(entities, text) # Apply weighted scoring if entity_types are provided if entity_types: @@ -418,15 +732,14 @@ class NERExtractor: # Filter by confidence filtered = [e for e in entities if e.confidence >= min_confidence] - if filtered: - all_entities.append((method_name, filtered)) - - # If not using ensemble, return first successful result - if not self.ensemble_voting: + if merge_strategy == "fallback": + if filtered: # Ensure default metadata for e in filtered: - if e.metadata is None: e.metadata = {} - if "batch_index" not in e.metadata: e.metadata["batch_index"] = 0 + if e.metadata is None: + e.metadata = {} + if "batch_index" not in e.metadata: + e.metadata["batch_index"] = 0 self.progress_tracker.stop_tracking( tracking_id, @@ -434,6 +747,8 @@ class NERExtractor: message=f"Extracted {len(filtered)} entities using {method_name}", ) return filtered + else: + all_entities.append((method_name, filtered)) except Exception as e: self.logger.warning( @@ -441,15 +756,23 @@ class NERExtractor: ) continue - # Ensemble voting if enabled - if self.ensemble_voting and len(all_entities) > 1: + if merge_strategy == "consensus": entities = self._vote_entities( - [entities for _, entities in all_entities] + all_entities, + eligible_methods=eligible_methods, + min_votes=min_votes, + min_agreement=min_agreement, + method_weights=method_weights, + ) + elif merge_strategy == "union": + entities = self._union_entities( + all_entities, + eligible_methods=eligible_methods, + method_weights=method_weights, ) - elif all_entities: - entities = all_entities[0][1] # Use first successful method else: - # Fallback to pattern-based extraction if all models fail + # Only the explicit fallback strategy may introduce its own + # pattern candidates after every configured method fails. entities = self._extract_fallback(text) # Post-processing if enabled @@ -488,30 +811,466 @@ class NERExtractor: return filtered def _vote_entities( - self, results: List[List[Entity]], threshold: float = 0.5 + self, + results: Sequence[Union[List[Entity], Tuple[str, List[Entity]]]], + threshold: Optional[float] = None, + *, + eligible_methods: Optional[Sequence[str]] = None, + min_votes: Optional[int] = None, + min_agreement: Optional[float] = None, + method_weights: Optional[Dict[str, float]] = None, ) -> List[Entity]: - """Vote on entities across methods.""" - entity_counts = {} - total_methods = len(results) + """Merge method results using span-aligned cross-method consensus. - for entities in results: + ``results`` accepts the historical ``List[List[Entity]]`` shape as + well as ``(method_name, entities)`` pairs. The latter retains method + provenance, while anonymous historical inputs receive stable generated + names. ``threshold`` remains a compatibility alias for + ``min_agreement``; confidence is never used as a substitute for votes. + """ + resolved_min_votes = self._validate_min_votes( + self.min_votes if min_votes is None else min_votes + ) + if min_agreement is None: + min_agreement = threshold if threshold is not None else self.min_agreement + resolved_min_agreement = self._validate_min_agreement(min_agreement) + resolved_method_weights = self._validate_method_weights( + self.method_weights if method_weights is None else method_weights + ) + + return self._merge_method_results( + results, + merge_strategy="consensus", + eligible_methods=eligible_methods, + min_votes=resolved_min_votes, + min_agreement=resolved_min_agreement, + method_weights=resolved_method_weights, + ) + + def _union_entities( + self, + results: Sequence[Union[List[Entity], Tuple[str, List[Entity]]]], + *, + eligible_methods: Optional[Sequence[str]] = None, + method_weights: Optional[Dict[str, float]] = None, + ) -> List[Entity]: + """Merge all method results while retaining single-method candidates.""" + resolved_method_weights = self._validate_method_weights( + self.method_weights if method_weights is None else method_weights + ) + return self._merge_method_results( + results, + merge_strategy="union", + eligible_methods=eligible_methods, + min_votes=1, + min_agreement=None, + method_weights=resolved_method_weights, + ) + + def _merge_method_results( + self, + results: Sequence[Union[List[Entity], Tuple[str, List[Entity]]]], + *, + merge_strategy: str, + eligible_methods: Optional[Sequence[str]], + min_votes: int, + min_agreement: Optional[float], + method_weights: Dict[str, float], + ) -> List[Entity]: + """Align overlapping mentions and merge them with a named strategy.""" + method_results = self._normalize_method_results(results) + eligible_methods = self._normalize_eligible_method_names( + eligible_methods, method_results + ) + if not eligible_methods: + return [] + + eligible_identities = { + self._method_identity(method_name) for method_name in eligible_methods + } + clusters = self._cluster_entities(method_results, eligible_identities) + merged = [] + + for cluster in clusters: + entity = self._build_merged_entity( + cluster, + eligible_methods=eligible_methods, + merge_strategy=merge_strategy, + min_votes=min_votes, + min_agreement=min_agreement, + ) + if entity is None: + continue + + merged.append(entity) + + if merge_strategy == "consensus": + merged = self._resolve_consensus_label_conflicts( + merged, method_weights + ) + + return sorted( + merged, + key=lambda entity: ( + entity.start_char, + entity.end_char, + entity.label, + entity.text.casefold(), + ), + ) + + def _normalize_method_results( + self, + results: Sequence[Union[List[Entity], Tuple[str, List[Entity]]]], + ) -> List[Tuple[str, List[Entity]]]: + """Coalesce alias methods so one backend cannot cast two votes.""" + normalized = {} + for index, result in enumerate(results): + if ( + isinstance(result, tuple) + and len(result) == 2 + and isinstance(result[0], str) + ): + method_name, entities = result + else: + method_name, entities = f"method_{index + 1}", result + + identity = self._method_identity(method_name) + if identity not in normalized: + normalized[identity] = {"name": method_name, "entities": []} + if entities: + normalized[identity]["entities"].extend(entities) + + return [ + (data["name"], data["entities"]) + for data in normalized.values() + ] + + def _normalize_eligible_method_names( + self, + eligible_methods: Optional[Sequence[str]], + method_results: Sequence[Tuple[str, List[Entity]]], + ) -> List[str]: + """Keep configured failed methods in the consensus denominator.""" + if eligible_methods is None: + eligible_methods = [method_name for method_name, _ in method_results] + + known_names = { + self._method_identity(method_name): method_name + for method_name, _ in method_results + } + normalized = [] + seen = set() + for method_name in eligible_methods: + identity = self._method_identity(method_name) + if identity in seen: + continue + normalized.append(known_names.get(identity, method_name)) + seen.add(identity) + return normalized + + def _cluster_entities( + self, + method_results: Sequence[Tuple[str, List[Entity]]], + eligible_identities: set, + ) -> List[List[Tuple[str, Entity]]]: + """Align same-label mentions with deterministic one-to-one matching. + + Each method is matched to existing candidates as a batch, ordered by + descending span IoU. This prevents an early, weaker boundary variant + from consuming a method's only vote before its exact match is seen. + Different labels stay separate here and are reconciled only after + each label's independent support has been counted. + """ + clusters_by_label = {} + ordered_results = sorted( + method_results, + key=lambda result: ( + self._method_identity(result[0]), + result[0], + ), + ) + + for method_name, entities in ordered_results: + method_identity = self._method_identity(method_name) + if method_identity not in eligible_identities: + continue + + unique_entities = {} for entity in entities: - key = (entity.text.lower(), entity.label) - if key not in entity_counts: - entity_counts[key] = {"entity": entity, "score": 0.0, "count": 0} - entity_counts[key]["score"] += entity.confidence - entity_counts[key]["count"] += 1 + label = self._canonical_label(entity.label) + key = (label, entity.start_char, entity.end_char) + existing = unique_entities.get(key) + if existing is None or self._entity_order_key( + entity + ) < self._entity_order_key(existing): + unique_entities[key] = entity - # Return entities that meet threshold - voted = [] - for key, data in entity_counts.items(): - avg_score = data["score"] / data["count"] - if avg_score >= threshold: - entity = data["entity"] - entity.confidence = avg_score - voted.append(entity) + entities_by_label = {} + for entity in unique_entities.values(): + label = self._canonical_label(entity.label) + entities_by_label.setdefault(label, []).append(entity) - return voted + for label in sorted(entities_by_label): + candidates = sorted( + entities_by_label[label], key=self._entity_order_key + ) + label_clusters = clusters_by_label.setdefault(label, []) + edges = [] + for candidate_index, candidate in enumerate(candidates): + for cluster_index, cluster in enumerate(label_clusters): + if any( + self._method_identity(cluster_method) == method_identity + for cluster_method, _ in cluster + ): + continue + # A cluster represents one consensus mention, so a + # candidate must overlap *every* vote already in it. + # Using a best-pair score here would let A~B and B~C + # turn into a false A/B/C consensus when A !~ C. + scores = [ + self._span_iou(candidate, clustered_entity) + for _, clustered_entity in cluster + ] + score = min(scores) + if score >= self._MIN_SPAN_IOU: + edges.append((score, candidate_index, cluster_index)) + + matched_candidates = set() + matched_clusters = set() + for _, candidate_index, cluster_index in sorted( + edges, + key=lambda item: ( + -item[0], + self._entity_order_key(candidates[item[1]]), + item[2], + ), + ): + if ( + candidate_index in matched_candidates + or cluster_index in matched_clusters + ): + continue + label_clusters[cluster_index].append( + (method_name, candidates[candidate_index]) + ) + matched_candidates.add(candidate_index) + matched_clusters.add(cluster_index) + + for candidate_index, candidate in enumerate(candidates): + if candidate_index not in matched_candidates: + label_clusters.append([(method_name, candidate)]) + + return [ + cluster + for label in sorted(clusters_by_label) + for cluster in clusters_by_label[label] + ] + + def _resolve_consensus_label_conflicts( + self, + entities: Sequence[Entity], + method_weights: Dict[str, float], + ) -> List[Entity]: + """Choose one deterministic label when candidates share one span. + + Cross-label candidates only conflict when their final document spans + are identical. Nested entities at different spans remain distinct. + """ + resolved = {} + + def conflict_order_key(entity: Entity) -> Tuple[Any, ...]: + metadata = entity.metadata or {} + support_weight = sum( + self._method_weight(method_name, method_weights) + for method_name in metadata.get("supporting_methods", []) + ) + confidence = self._numeric_confidence(entity.confidence) + confidence_key = -confidence if confidence is not None else float("inf") + return ( + -support_weight, + -metadata.get("vote_count", 0), + confidence_key, + entity.label, + entity.text.casefold(), + ) + + for entity in entities: + key = (entity.start_char, entity.end_char) + existing = resolved.get(key) + if existing is None or conflict_order_key(entity) < conflict_order_key( + existing + ): + resolved[key] = entity + + return list(resolved.values()) + + @staticmethod + def _span_iou(first: Entity, second: Entity) -> float: + """Return overlap-over-union for two document spans.""" + intersection = max( + 0, + min(first.end_char, second.end_char) + - max(first.start_char, second.start_char), + ) + if not intersection: + return 0.0 + union = max(first.end_char, second.end_char) - min( + first.start_char, second.start_char + ) + return intersection / union if union else 0.0 + + @classmethod + def _canonical_label(cls, label: str) -> str: + """Normalize common NER aliases and BIO prefixes before label voting.""" + normalized = str(label).strip().upper() + if "-" in normalized: + prefix, remainder = normalized.split("-", 1) + if prefix in {"B", "I", "L", "U", "E", "S"}: + normalized = remainder + return cls._LABEL_ALIASES.get(normalized, normalized) + + @staticmethod + def _numeric_confidence(confidence: Any) -> Optional[float]: + """Convert a usable confidence score without treating missing scores as zero.""" + if confidence is None: + return None + try: + normalized = float(confidence) + except (TypeError, ValueError): + return None + return normalized if math.isfinite(normalized) else None + + @classmethod + def _entity_order_key(cls, entity: Entity) -> Tuple[Any, ...]: + """Provide a deterministic winner for boundary and confidence variants.""" + confidence = cls._numeric_confidence(entity.confidence) + confidence_key = -confidence if confidence is not None else float("inf") + return ( + confidence_key, + -(entity.end_char - entity.start_char), + entity.start_char, + entity.end_char, + entity.text.casefold(), + entity.label.casefold(), + ) + + def _method_weight( + self, + method_name: str, + method_weights: Dict[str, float], + ) -> float: + """Read a weight using the canonical backend name.""" + identity = self._method_identity(method_name) + return method_weights.get(identity, 1.0) + + def _build_merged_entity( + self, + cluster: Sequence[Tuple[str, Entity]], + *, + eligible_methods: Sequence[str], + merge_strategy: str, + min_votes: int, + min_agreement: Optional[float], + ) -> Optional[Entity]: + """Resolve one same-label, offset-aligned candidate.""" + selected_by_method = {} + for method_name, entity in cluster: + identity = self._method_identity(method_name) + existing = selected_by_method.get(identity) + if existing is None or ( + self._entity_order_key(entity) + < self._entity_order_key(existing[1]) + ): + selected_by_method[identity] = (method_name, entity) + + eligible_records = [] + seen = set() + for method_name in eligible_methods: + identity = self._method_identity(method_name) + if identity not in seen: + eligible_records.append((identity, method_name)) + seen.add(identity) + if not eligible_records: + return None + + if not selected_by_method: + return None + + supporting_entries = [ + (identity, method_name, entity) + for identity, (method_name, entity) in selected_by_method.items() + ] + vote_count = len(supporting_entries) + agreement = vote_count / len(eligible_records) + if merge_strategy == "consensus" and ( + vote_count < min_votes + or (min_agreement is not None and agreement < min_agreement) + ): + return None + + representative = min( + (entity for _, _, entity in supporting_entries), key=self._entity_order_key + ) + canonical_label = self._canonical_label(representative.label) + + supporting_by_identity = { + identity: (method_name, entity) + for identity, method_name, entity in supporting_entries + } + supporting_methods = [ + method_name + for identity, method_name in eligible_records + if identity in supporting_by_identity + ] + method_scores = { + method_name: ( + self._numeric_confidence(supporting_by_identity[identity][1].confidence) + if identity in supporting_by_identity + else None + ) + for identity, method_name in eligible_records + } + + confidence_scores = [] + for identity, method_name in eligible_records: + if identity not in supporting_by_identity: + continue + score = self._numeric_confidence( + supporting_by_identity[identity][1].confidence + ) + if score is not None: + confidence_scores.append(score) + + if confidence_scores: + confidence = sum(confidence_scores) / len(confidence_scores) + else: + confidence = representative.confidence + + metadata = dict(representative.metadata or {}) + metadata.update( + { + "merge_strategy": merge_strategy, + "supporting_methods": supporting_methods, + "vote_count": len(supporting_methods), + "eligible_method_count": len(eligible_records), + "agreement": agreement, + "method_scores": method_scores, + } + ) + + return Entity( + text=representative.text, + label=( + canonical_label + if merge_strategy == "consensus" + else representative.label + ), + start_char=representative.start_char, + end_char=representative.end_char, + confidence=confidence, + metadata=metadata, + ) def _post_process_entities(self, entities: List[Entity], text: str) -> List[Entity]: """Post-process entities for refinement.""" diff --git a/semantica/vector_store/pinecone_store.py b/semantica/vector_store/pinecone_store.py index 6df7ad85..43bd3616 100644 --- a/semantica/vector_store/pinecone_store.py +++ b/semantica/vector_store/pinecone_store.py @@ -374,7 +374,7 @@ class PineconeStore: # Check Pinecone availability if not PINECONE_AVAILABLE: self.logger.warning( - "Pinecone not available. Install with: pip install pinecone-client" + "Pinecone not available. Install with: pip install pinecone>=3.0.0 or pip install semantica[vectorstore-pinecone]" ) def connect(self, **kwargs) -> bool: @@ -389,7 +389,7 @@ class PineconeStore: """ if not PINECONE_AVAILABLE: raise ProcessingError( - "Pinecone is not available. Install it with: pip install pinecone-client" + "Pinecone is not available. Install it with: pip install pinecone>=3.0.0 or pip install semantica[vectorstore-pinecone]" ) api_key = kwargs.get("api_key") or self.api_key diff --git a/semantica_mcp/__init__.py b/semantica_mcp/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mcp/README.md b/semantica_mcp/mcp/README.md similarity index 100% rename from mcp/README.md rename to semantica_mcp/mcp/README.md diff --git a/mcp/__init__.py b/semantica_mcp/mcp/__init__.py similarity index 100% rename from mcp/__init__.py rename to semantica_mcp/mcp/__init__.py diff --git a/semantica_mcp/mcp/__main__.py b/semantica_mcp/mcp/__main__.py new file mode 100644 index 00000000..4b9ed44d --- /dev/null +++ b/semantica_mcp/mcp/__main__.py @@ -0,0 +1,5 @@ +"""Entry point: python -m semantica_mcp.mcp""" +from .server import main + +if __name__ == "__main__": + main() diff --git a/mcp/resources/__init__.py b/semantica_mcp/mcp/resources/__init__.py similarity index 100% rename from mcp/resources/__init__.py rename to semantica_mcp/mcp/resources/__init__.py diff --git a/mcp/resources/registry.py b/semantica_mcp/mcp/resources/registry.py similarity index 98% rename from mcp/resources/registry.py rename to semantica_mcp/mcp/resources/registry.py index 0f1d5ac4..ea73f4b1 100644 --- a/mcp/resources/registry.py +++ b/semantica_mcp/mcp/resources/registry.py @@ -10,8 +10,8 @@ from __future__ import annotations import json import logging -from mcp import __version__ -from mcp.session import get_graph +from .. import __version__ +from ..session import get_graph log = logging.getLogger("semantica.mcp.resources") diff --git a/mcp/schemas.py b/semantica_mcp/mcp/schemas.py similarity index 100% rename from mcp/schemas.py rename to semantica_mcp/mcp/schemas.py diff --git a/mcp/server.py b/semantica_mcp/mcp/server.py similarity index 96% rename from mcp/server.py rename to semantica_mcp/mcp/server.py index bf84dd24..d9484df2 100644 --- a/mcp/server.py +++ b/semantica_mcp/mcp/server.py @@ -6,8 +6,8 @@ Implements the Model Context Protocol so any MCP-compatible AI tool can interact with the Semantica knowledge graph. Run: - python -m mcp # via __main__.py - python -m mcp.server # direct + python -m semantica_mcp.mcp # via __main__.py + python -m semantica_mcp.mcp.server # direct """ from __future__ import annotations @@ -17,9 +17,9 @@ import logging import sys from typing import Any -from mcp import __version__ -from mcp.resources import RESOURCE_DEFINITIONS, handle_resource_read -from mcp.tools import TOOL_DEFINITIONS +from . import __version__ +from .resources import RESOURCE_DEFINITIONS, handle_resource_read +from .tools import TOOL_DEFINITIONS log = logging.getLogger("semantica.mcp.server") diff --git a/mcp/session.py b/semantica_mcp/mcp/session.py similarity index 100% rename from mcp/session.py rename to semantica_mcp/mcp/session.py diff --git a/mcp/tools/__init__.py b/semantica_mcp/mcp/tools/__init__.py similarity index 100% rename from mcp/tools/__init__.py rename to semantica_mcp/mcp/tools/__init__.py diff --git a/mcp/tools/decisions.py b/semantica_mcp/mcp/tools/decisions.py similarity index 99% rename from mcp/tools/decisions.py rename to semantica_mcp/mcp/tools/decisions.py index ca73ca69..c3154974 100644 --- a/mcp/tools/decisions.py +++ b/semantica_mcp/mcp/tools/decisions.py @@ -7,14 +7,14 @@ from __future__ import annotations import logging import os -from mcp.schemas import ( +from ..schemas import ( ANALYZE_DECISION_IMPACT, FIND_PRECEDENTS, GET_CAUSAL_CHAIN, QUERY_DECISIONS, RECORD_DECISION, ) -from mcp.session import get_graph, is_persistence_safe +from ..session import get_graph, is_persistence_safe log = logging.getLogger("semantica.mcp.tools.decisions") diff --git a/mcp/tools/export.py b/semantica_mcp/mcp/tools/export.py similarity index 98% rename from mcp/tools/export.py rename to semantica_mcp/mcp/tools/export.py index df39162b..f0ea9d2a 100644 --- a/mcp/tools/export.py +++ b/semantica_mcp/mcp/tools/export.py @@ -6,8 +6,8 @@ from __future__ import annotations import logging -from mcp.schemas import EXPORT_GRAPH, GET_PROVENANCE -from mcp.session import get_graph +from ..schemas import EXPORT_GRAPH, GET_PROVENANCE +from ..session import get_graph log = logging.getLogger("semantica.mcp.tools.export") diff --git a/mcp/tools/extraction.py b/semantica_mcp/mcp/tools/extraction.py similarity index 98% rename from mcp/tools/extraction.py rename to semantica_mcp/mcp/tools/extraction.py index 7ccd385b..f47b71cc 100644 --- a/mcp/tools/extraction.py +++ b/semantica_mcp/mcp/tools/extraction.py @@ -7,7 +7,7 @@ from __future__ import annotations import logging from typing import Any -from mcp.schemas import EXTRACT_ALL, EXTRACT_ENTITIES, EXTRACT_RELATIONS +from ..schemas import EXTRACT_ALL, EXTRACT_ENTITIES, EXTRACT_RELATIONS log = logging.getLogger("semantica.mcp.tools.extraction") diff --git a/mcp/tools/graph.py b/semantica_mcp/mcp/tools/graph.py similarity index 98% rename from mcp/tools/graph.py rename to semantica_mcp/mcp/tools/graph.py index 65afdc3c..1c105bf5 100644 --- a/mcp/tools/graph.py +++ b/semantica_mcp/mcp/tools/graph.py @@ -7,8 +7,8 @@ from __future__ import annotations import logging import os -from mcp.schemas import ADD_ENTITY, ADD_RELATIONSHIP, EMPTY, GET_ANALYTICS, SEARCH_GRAPH -from mcp.session import get_graph, is_persistence_safe +from ..schemas import ADD_ENTITY, ADD_RELATIONSHIP, EMPTY, GET_ANALYTICS, SEARCH_GRAPH +from ..session import get_graph, is_persistence_safe log = logging.getLogger("semantica.mcp.tools.graph") diff --git a/mcp/tools/reasoning.py b/semantica_mcp/mcp/tools/reasoning.py similarity index 97% rename from mcp/tools/reasoning.py rename to semantica_mcp/mcp/tools/reasoning.py index 98e888eb..dd64aa01 100644 --- a/mcp/tools/reasoning.py +++ b/semantica_mcp/mcp/tools/reasoning.py @@ -6,7 +6,7 @@ from __future__ import annotations import logging -from mcp.schemas import ABDUCTIVE_REASONING, RUN_REASONING +from ..schemas import ABDUCTIVE_REASONING, RUN_REASONING log = logging.getLogger("semantica.mcp.tools.reasoning") diff --git a/tests/context/test_context_graph_retraction.py b/tests/context/test_context_graph_retraction.py index fa0fc158..5d9e2fa8 100644 --- a/tests/context/test_context_graph_retraction.py +++ b/tests/context/test_context_graph_retraction.py @@ -15,15 +15,18 @@ than a mock callback, since the behaviour under test is precisely that these operations reach the existing mutation-recording path. """ +import ast +import inspect import json import os import tempfile import threading import unittest -from datetime import datetime +from datetime import datetime, timedelta, timezone from semantica.change_management import TemporalVersionManager from semantica.context import ContextEdge, ContextGraph +from semantica.context.context_graph import normalize_temporal_input BEFORE = "2025-06-01T00:00:00Z" BETWEEN = "2025-09-01T00:00:00Z" @@ -31,6 +34,283 @@ CUTOFF = "2026-01-01T00:00:00Z" AFTER = "2026-06-01T00:00:00Z" +class TestTemporalNormalization(unittest.TestCase): + """Regression tests for the public normalize_temporal_input API (issue #1377). + + The private ``_normalize_temporal_input`` was previously imported directly + by ``erasure.py``, creating fragile cross-module coupling. The public + wrapper must behave identically for every supported input type so that + context-graph timestamps and erasure-receipt timestamps always agree. + """ + + # ------------------------------------------------------------------ + # Basic output contract + # ------------------------------------------------------------------ + + def test_none_returns_none(self): + self.assertIsNone(normalize_temporal_input(None)) + + def test_naive_datetime(self): + """Naive datetime is serialized directly without tz conversion.""" + self.assertEqual( + normalize_temporal_input(datetime(2026, 1, 1, 12, 0, 0)), + "2026-01-01T12:00:00", + ) + + def test_aware_datetime_utc_strips_timezone(self): + """UTC-aware datetime is stripped of tzinfo before serialization.""" + value = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + self.assertEqual(normalize_temporal_input(value), "2026-01-01T12:00:00") + + def test_aware_datetime_positive_offset_converted_to_utc(self): + """+05:00 aware datetime is shifted to UTC before serializing.""" + value = datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=5))) + self.assertEqual(normalize_temporal_input(value), "2026-01-01T07:00:00") + + def test_aware_datetime_negative_offset_converted_to_utc(self): + """-08:00 aware datetime is shifted forward to UTC.""" + value = datetime(2026, 1, 1, 0, 0, 0, tzinfo=timezone(timedelta(hours=-8))) + self.assertEqual(normalize_temporal_input(value), "2026-01-01T08:00:00") + + def test_epoch_int_zero(self): + """Integer 0 maps to the Unix epoch in UTC.""" + self.assertEqual(normalize_temporal_input(0), "1970-01-01T00:00:00") + + def test_epoch_int_positive(self): + """A known epoch value round-trips correctly.""" + # 2026-01-01T00:00:00 UTC = 1767225600 + self.assertEqual(normalize_temporal_input(1767225600), "2026-01-01T00:00:00") + + def test_epoch_float_preserves_sub_second(self): + """Float epoch retains sub-second precision in the ISO string.""" + result = normalize_temporal_input(0.5) + self.assertTrue(result.startswith("1970-01-01T00:00:00")) + self.assertIn("5", result) # sub-second component present + + def test_iso_string_with_z_suffix(self): + """'Z' suffix is treated as UTC and the result is tz-naive.""" + self.assertEqual( + normalize_temporal_input("2026-01-01T12:00:00Z"), + "2026-01-01T12:00:00", + ) + + def test_iso_string_with_positive_offset(self): + """'+05:00' offset string is converted to UTC.""" + self.assertEqual( + normalize_temporal_input("2026-01-01T12:00:00+05:00"), + "2026-01-01T07:00:00", + ) + + def test_iso_string_naive(self): + """Naive ISO string is returned unchanged (treated as UTC).""" + self.assertEqual( + normalize_temporal_input("2026-01-01T12:00:00"), + "2026-01-01T12:00:00", + ) + + def test_year_only_string(self): + """Year-only shorthand expands to Jan 1 midnight.""" + self.assertEqual(normalize_temporal_input("2026"), "2026-01-01T00:00:00") + + def test_date_only_string(self): + """Date-only string expands to midnight of that date.""" + self.assertEqual( + normalize_temporal_input("2026-03-15"), + "2026-03-15T00:00:00", + ) + + # ------------------------------------------------------------------ + # Error cases + # ------------------------------------------------------------------ + + def test_invalid_string_raises_value_error(self): + """An unparseable string must raise ValueError, not silently produce None.""" + with self.assertRaises(ValueError): + normalize_temporal_input("not-a-date") + + def test_unsupported_type_raises_value_error(self): + """A date object (not datetime) is not a supported type.""" + from datetime import date + + with self.assertRaises(ValueError): + normalize_temporal_input(date(2026, 1, 1)) # type: ignore[arg-type] + + def test_error_message_contains_the_bad_value(self): + """The ValueError for an invalid string names the bad input.""" + with self.assertRaises(ValueError, msg="not-a-timestamp") as ctx: + normalize_temporal_input("not-a-timestamp") + self.assertIn("not-a-timestamp", str(ctx.exception)) + + # ------------------------------------------------------------------ + # Return-type contract + # ------------------------------------------------------------------ + + def test_always_returns_str_or_none(self): + """Every non-None input must produce a str, never another type.""" + inputs = [ + datetime(2026, 1, 1), + datetime(2026, 1, 1, tzinfo=timezone.utc), + 0, + 1767225600, + 0.5, + "2026-01-01T00:00:00Z", + "2026", + ] + for value in inputs: + with self.subTest(value=value): + result = normalize_temporal_input(value) + self.assertIsInstance(result, str) + + def test_output_is_always_tz_naive(self): + """The returned ISO string must never carry a UTC offset or 'Z'.""" + aware_inputs = [ + datetime(2026, 1, 1, tzinfo=timezone.utc), + datetime(2026, 1, 1, tzinfo=timezone(timedelta(hours=3))), + "2026-01-01T00:00:00Z", + "2026-01-01T00:00:00+05:00", + ] + for value in aware_inputs: + with self.subTest(value=value): + result = normalize_temporal_input(value) + self.assertNotIn("Z", result) + self.assertNotIn("+", result) + self.assertNotIn("-0", result[-6:]) # no trailing UTC offset + + +class TestTemporalNormalizationImportGuard(unittest.TestCase): + """Structural regression tests that pin the fix for issue #1377. + + The *point* of #1377 is that ``erasure.py`` must not import + ``_normalize_temporal_input`` directly from ``context_graph``. These + tests catch a future reversion without relying on behavioral differences + (there are none — the wrapper is transparent). + """ + + def test_erasure_does_not_import_private_normalizer(self): + """``erasure.py`` source must not contain ``_normalize_temporal_input`` + as a name in any import statement. + + This test parses the AST rather than inspecting the live module so + that it catches the import even if the name is shadowed at runtime. + """ + import semantica.context.erasure as _erasure_module + + src = inspect.getsource(_erasure_module) + tree = ast.parse(src) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom): + for alias in node.names: + self.assertNotEqual( + alias.name, + "_normalize_temporal_input", + "erasure.py imports the private _normalize_temporal_input; " + "it must use the public normalize_temporal_input instead " + "(issue #1377)", + ) + + def test_erasure_imports_public_normalizer(self): + """``erasure.py`` must explicitly import ``normalize_temporal_input``.""" + import semantica.context.erasure as _erasure_module + + src = inspect.getsource(_erasure_module) + tree = ast.parse(src) + imported_names = [] + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom): + for alias in node.names: + imported_names.append(alias.name) + self.assertIn( + "normalize_temporal_input", + imported_names, + "erasure.py does not import normalize_temporal_input from context_graph", + ) + + def test_normalize_temporal_input_is_importable_as_public_api(self): + """``normalize_temporal_input`` must be importable without underscore prefix.""" + # Would raise ImportError if the name were removed or renamed. + from semantica.context.context_graph import normalize_temporal_input as fn + + self.assertTrue(callable(fn)) + + def test_private_normalizer_not_leaked_into_erasure_namespace(self): + """The private ``_normalize_temporal_input`` must not be reachable + through the ``erasure`` module's namespace.""" + import semantica.context.erasure as _erasure_module + + self.assertFalse( + hasattr(_erasure_module, "_normalize_temporal_input"), + "erasure module exposes _normalize_temporal_input in its namespace; " + "it should only hold normalize_temporal_input", + ) + + +class TestTemporalNormalizationCrossModuleConsistency(unittest.TestCase): + """Verify that erasure.py and context_graph.py normalize identically. + + This is the core correctness requirement of issue #1377: the receipt's + ``erased_at`` and the graph tombstone's ``purged_at`` must always carry + the same string because they are produced by the same normalization path. + If the two modules ever diverged (e.g. because erasure reimplemented + normalization independently), these tests would catch it. + """ + + def _graph(self): + g = ContextGraph(advanced_analytics=False) + g.add_node("alice", "person") + return g + + def _assert_receipt_and_tombstone_agree(self, at_value, label=""): + from semantica.context.erasure import ErasureCoordinator + + g = self._graph() + receipt = ErasureCoordinator(graph=g).erase_entity("alice", at=at_value) + tombstone = g.get_tombstone("alice", "node") + self.assertEqual( + receipt.erased_at, + tombstone["purged_at"], + f"receipt.erased_at != tombstone.purged_at for input {label!r}", + ) + # Also verify both agree with what normalize_temporal_input produces + # directly, so the public function is the single source of truth. + expected = normalize_temporal_input(at_value) + self.assertEqual(receipt.erased_at, expected) + + def test_iso_z_string_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + "2026-01-01T12:00:00Z", "ISO-Z string" + ) + + def test_iso_offset_string_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + "2026-01-01T12:00:00+05:00", "ISO +05:00 string" + ) + + def test_iso_naive_string_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + "2026-01-01T12:00:00", "ISO naive string" + ) + + def test_epoch_int_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree(1767225600, "epoch int") + + def test_aware_utc_datetime_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone.utc), + "aware UTC datetime", + ) + + def test_aware_offset_datetime_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + datetime(2026, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=5))), + "aware +5h datetime", + ) + + def test_naive_datetime_receipt_and_tombstone_agree(self): + self._assert_receipt_and_tombstone_agree( + datetime(2026, 1, 1, 12, 0, 0), "naive datetime" + ) + + def _graph(): """alice --works_at--> acme, plus an unrelated bob.""" graph = ContextGraph(advanced_analytics=False) diff --git a/tests/context/test_decision_causal_edge_regression.py b/tests/context/test_decision_causal_edge_regression.py index eae7eae3..3bfa1591 100644 --- a/tests/context/test_decision_causal_edge_regression.py +++ b/tests/context/test_decision_causal_edge_regression.py @@ -452,3 +452,42 @@ def test_find_precedents_sees_lowercase_precedent_edge(): precedents = graph.find_precedents(later) assert [d.decision_id for d in precedents] == [precedent] + + +def test_heuristic_cause_reported_once_per_shared_entity_pair(): + """A potential cause found through the shared-entity heuristic must be + reported once, not once per shared entity. + + ``trace_decision_causality()`` collects ``potential_causes`` by looping + over every entity of the current decision, so a decision sharing two + entities with an earlier one (e.g. the same customer and the same + property) used to be appended twice and produced two identical + "influences" chains. + """ + graph = ContextGraph(advanced_analytics=True) + cause = graph.record_decision( + category="lending", scenario="earlier review", reasoning="r", + outcome="approved", confidence=0.9, + entities=["customer_123", "property_456"], + ) + effect = graph.record_decision( + category="risk", scenario="later review", reasoning="r", + outcome="flagged", confidence=0.9, + entities=["customer_123", "property_456"], + ) + # Pin timestamps so the earlier/later ordering is deterministic. + graph._decisions[cause]["timestamp"] = 100.0 + graph._decisions[effect]["timestamp"] = 200.0 + + chains = graph.trace_decision_chain(effect) + + influence_hops = [ + (hop["from"], hop["to"]) + for chain in chains + for hop in chain["hops"] + if hop["type"] == "influences" + ] + assert influence_hops, "shared-entity heuristic must find the earlier decision" + assert influence_hops.count((cause, effect)) == 1, ( + "the same (cause, effect) pair must be reported once, not once per shared entity" + ) diff --git a/tests/context/test_erasure_coordinator.py b/tests/context/test_erasure_coordinator.py index e4dcb21a..7bb6158a 100644 --- a/tests/context/test_erasure_coordinator.py +++ b/tests/context/test_erasure_coordinator.py @@ -399,6 +399,121 @@ class TestReceipt(unittest.TestCase): self.assertEqual(receipt.stores["graph"]["status"], STATUS_ERASED) + def test_to_dict_deep_copies_nested_store_results(self): + """A nested dict in a store result is not shared with the live receipt. + + ``backend_result`` from a vector backend is a dict of its own, so a + shallow per-store copy leaves it referenced by both the payload and the + receipt -- sanitizing the payload for a user-facing response would + silently corrupt the audit record. + """ + receipt = ErasureReceipt( + entity_id="customer-4471", + stores={ + "vectors": { + "status": STATUS_ERASED, + "backend": "qdrant", + "backend_result": {"status": "completed"}, + }, + }, + ) + + payload = receipt.to_dict() + payload["stores"]["vectors"]["backend_result"]["status"] = "redacted" + + self.assertEqual( + receipt.stores["vectors"]["backend_result"]["status"], "completed" + ) + + def test_to_dict_store_results_are_distinct_objects(self): + """The per-store dict and any nested dict in the payload must not be + the same objects as the ones in the live receipt. + + A mutation test proves *isolation* only when the copy actually + happened; identity checks prove *that* a copy was made. + """ + receipt = ErasureReceipt( + entity_id="customer-4471", + stores={ + "vectors": { + "status": STATUS_ERASED, + "backend": "qdrant", + # Realistic Qdrant-shaped backend_result with rendered enum + "backend_result": {"status": "UpdateStatus.COMPLETED", "points": 1}, + "vector_ids": 2, + "via": "delete_vectors", + }, + "graph": {"status": STATUS_ERASED, "nodes": 1, "edges": 3}, + }, + ) + + payload = receipt.to_dict() + + # The stores container itself is a new dict. + self.assertIsNot(payload["stores"], receipt.stores) + + # Each per-store result dict is a new object. + self.assertIsNot( + payload["stores"]["vectors"], receipt.stores["vectors"] + ) + self.assertIsNot( + payload["stores"]["graph"], receipt.stores["graph"] + ) + + # The nested backend_result dict is also a new object. + self.assertIsNot( + payload["stores"]["vectors"]["backend_result"], + receipt.stores["vectors"]["backend_result"], + ) + + # Values are equal (correct copy), not just distinct references. + self.assertEqual( + payload["stores"]["vectors"]["backend_result"], + {"status": "UpdateStatus.COMPLETED", "points": 1}, + ) + self.assertEqual(payload["stores"]["graph"], {"status": STATUS_ERASED, "nodes": 1, "edges": 3}) + + def test_to_dict_isolation_across_multiple_stores(self): + """Mutations to any store in the payload must not affect any other + store in either the payload or the live receipt. + + This catches a hypothetical implementation that shares a single deep + copy across all stores rather than copying each independently. + """ + receipt = ErasureReceipt( + entity_id="e1", + stores={ + "vectors": { + "status": STATUS_UNSUPPORTED, + "backend": "faiss", + "vector_ids": 1, + "detail": "backend exposes no delete()", + }, + "memory": {"status": STATUS_ERASED, "items": 4}, + "graph": { + "status": STATUS_ERASED, + "nodes": 1, + "edges": 2, + }, + }, + ) + + payload = receipt.to_dict() + + # Mutate every store in the payload. + payload["stores"]["vectors"]["status"] = "tampered" + payload["stores"]["memory"]["items"] = 0 + payload["stores"]["graph"]["nodes"] = 99 + + # None of the live receipt's stores are affected. + self.assertEqual(receipt.stores["vectors"]["status"], STATUS_UNSUPPORTED) + self.assertEqual(receipt.stores["memory"]["items"], 4) + self.assertEqual(receipt.stores["graph"]["nodes"], 1) + + # The other stores in the payload are also unaffected (no aliasing). + self.assertEqual(payload["stores"]["memory"]["items"], 0) # our mutation + self.assertEqual(receipt.stores["memory"]["items"], 4) # unchanged + def test_receipt_and_tombstone_agree_on_when_the_erasure_happened(self): graph = _graph() receipt = ErasureCoordinator(graph=graph).erase_entity( diff --git a/tests/explorer/test_explorer_api.py b/tests/explorer/test_explorer_api.py index b0de2e18..0c5cdcf1 100644 --- a/tests/explorer/test_explorer_api.py +++ b/tests/explorer/test_explorer_api.py @@ -768,7 +768,7 @@ class TestImportExport: Canary: if either alias table drifts such that an alias becomes unsupported, this test will catch it.""" - from mcp.tools.export import _FORMAT_ALIASES as MCP_ALIASES + from semantica_mcp.mcp.tools.export import _FORMAT_ALIASES as MCP_ALIASES from semantica.explorer.routes.export_import import _RDF_FORMATS # Verify all MCP aliases are present in Explorer diff --git a/tests/explorer/test_ontology_subissue3.py b/tests/explorer/test_ontology_subissue3.py index b24d596a..6294c944 100644 --- a/tests/explorer/test_ontology_subissue3.py +++ b/tests/explorer/test_ontology_subissue3.py @@ -13,6 +13,7 @@ pytest.importorskip("fastapi") from semantica.explorer.app import create_app # noqa: E402 from semantica.explorer.routes.ontology import ( # noqa: E402 + _MAX_ANALYSIS_NODES, OntologyEntry, _convert_ontology_to_graph, _node_belongs_to_ontology, @@ -310,6 +311,66 @@ def test_ontology_graph_ignores_unrelated_data_when_enforcing_size_limit(client) } +def test_ontology_graph_rejects_oversized_core_and_stops_scanning(client, monkeypatch): + graph = client.app.state.session.graph + for index in range(5_001): + graph.add_node( + f"http://example.org/onto-a#Bulk{index:05d}", + node_type="owl:Class", + content="Bulk", + scheme_uri="http://example.org/onto-a", + ) + for index in range(3_000): + graph.add_node( + f"urn:unrelated:{index}", + node_type="owl:Class", + content="Unrelated", + scheme_uri="http://example.org/onto-b", + ) + + streamed = 0 + original_iter_nodes = GraphSession.iter_nodes + + def counting_iter_nodes(self, node_type=None): + nonlocal streamed + for node in original_iter_nodes(self, node_type=node_type): + streamed += 1 + yield node + + monkeypatch.setattr(GraphSession, "iter_nodes", counting_iter_nodes) + + response = client.get( + "/api/ontology/graph", + params={"uri": "http://example.org/onto-a"}, + ) + + assert response.status_code == 413 + assert str(_MAX_ANALYSIS_NODES) in response.json()["detail"] + # The graph holds 8,001 owl:Class nodes and onto-a's own sort first, so a + # scan that abandons at the cap sees far fewer than the whole type. + assert streamed < 6_000 + + +def test_ontology_graph_hydrates_external_edge_targets_in_sorted_order(client): + graph = client.app.state.session.graph + external = "http://external.example/Thing" + also_external = "http://external.example/Aardvark" + graph.add_node(external, node_type="owl:Class", content="External Thing") + graph.add_node(also_external, node_type="owl:Class", content="External Aardvark") + graph.add_edge("http://example.org/onto-a#name", external, edge_type="rdfs:range") + graph.add_edge("http://example.org/onto-a#name", also_external, edge_type="rdfs:range") + + response = client.get( + "/api/ontology/graph", + params={"uri": "http://example.org/onto-a"}, + ) + + assert response.status_code == 200 + node_ids = [node["id"] for node in response.json()["nodes"]] + assert {external, also_external} <= set(node_ids) + assert node_ids == sorted(node_ids) + + def test_shacl_generate_and_shapes(client): response = client.post( "/api/ontology/shacl/generate", @@ -735,6 +796,7 @@ onto:PersonShape a sh:NodeShape ; payload = response.json() assert payload["status"] == "error" assert "exceeds maximum allowed size" in payload["message"] + assert "SEMANTICA_MAX_SHACL_TURTLE_BYTES" in payload["message"] def test_validate_shacl_rejects_too_many_triples(client): @@ -757,6 +819,7 @@ onto:PersonShape a sh:NodeShape ; payload = response.json() assert payload["status"] == "error" assert "exceeds maximum allowed limit" in payload["message"] + assert "SEMANTICA_MAX_SHACL_TRIPLES" in payload["message"] def test_validate_shacl_handles_timeout(client): @@ -786,6 +849,7 @@ onto:PersonShape a sh:NodeShape ; payload = response.json() assert payload["status"] == "error" assert "timed out" in payload["message"] + assert "SEMANTICA_MAX_SHACL_TIMEOUT" in payload["message"] def test_validate_shacl_returns_unavailable_for_truncated_graph(client): diff --git a/tests/integrations/google_adk/test_decision_tools.py b/tests/integrations/google_adk/test_decision_tools.py new file mode 100644 index 00000000..3e19fce2 --- /dev/null +++ b/tests/integrations/google_adk/test_decision_tools.py @@ -0,0 +1,142 @@ +import pytest + +import sys +import importlib +from unittest.mock import patch + +@pytest.fixture(autouse=True) +def require_adk(request): + """Skip tests if ADK is missing, unless testing missing dependency behavior.""" + if "missing_adk" not in request.node.name: + pytest.importorskip("google.adk") + + +from integrations.google_adk.decision_tools import ( + ADK_AVAILABLE, + query_decisions, + record_decision, + semantica_decision_tools, +) + + +def test_adk_available(): + assert ADK_AVAILABLE is True + + +def test_record_decision_returns_dict(): + result = record_decision( + category="testing", + scenario="Google ADK integration test", + reasoning="Testing decision recording through the ADK adapter.", + outcome="Integration works", + confidence=0.9, + decision_maker="test-agent", + ) + + assert isinstance(result, dict) + assert "decision_id" in result + assert result["decision_id"] + assert result["category"] == "testing" + assert result["outcome"] == "Integration works" + assert result["confidence"] == 0.9 + + +def test_record_decision_clamps_confidence(): + result = record_decision( + category="testing", + scenario="Confidence test", + reasoning="Testing confidence normalization.", + outcome="Done", + confidence=2.0, + ) + + assert result["confidence"] == 1.0 + + +def test_record_decision_rejects_invalid_input_gracefully(): + result = record_decision( + category=None, + scenario="Invalid category test", + reasoning="Testing error handling.", + outcome="Done", + ) + + assert isinstance(result, dict) + assert "decision_id" in result + + +def test_query_decisions_returns_dict(): + result = query_decisions("testing") + + assert isinstance(result, dict) + assert "query" in result + assert "decisions" in result + assert "count" in result + assert isinstance(result["decisions"], list) + + +def test_record_then_query_decision(): + result = record_decision( + category="adk-integration", + scenario="Shared graph test", + reasoning="Verify that decisions can be queried after recording.", + outcome="Shared graph works", + confidence=0.95, + decision_maker="test-agent", + ) + + assert result["decision_id"] + + query_result = query_decisions("Shared graph test") + + assert isinstance(query_result, dict) + assert query_result["count"] >= 1 + + decision_ids = { + decision["decision_id"] + for decision in query_result["decisions"] + } + + assert result["decision_id"] in decision_ids + + +def test_semantica_decision_tools_returns_function_tools(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_decision_tools(graph) + + assert isinstance(tools, list) + assert len(tools) == 2 + + for tool in tools: + assert tool is not None + + +def test_semantica_decision_tools_names(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_decision_tools(graph) + + names = { + getattr(tool, "name", None) + for tool in tools + } + + assert "record_shared_decision" in names + assert "query_shared_decisions" in names + + +def test_missing_adk_graceful_failure(monkeypatch): + import pytest + import integrations.google_adk.decision_tools as decision_module + + # Safely mock the flag to False just for this test + monkeypatch.setattr(decision_module, "ADK_AVAILABLE", False) + + assert decision_module.ADK_AVAILABLE is False + with pytest.raises(ImportError, match="Google ADK is required"): + decision_module.semantica_decision_tools(None) \ No newline at end of file diff --git a/tests/integrations/google_adk/test_init.py b/tests/integrations/google_adk/test_init.py new file mode 100644 index 00000000..bd25ebde --- /dev/null +++ b/tests/integrations/google_adk/test_init.py @@ -0,0 +1,81 @@ +import pytest + +import sys +import importlib +from unittest.mock import patch + +@pytest.fixture(autouse=True) +def require_adk(request): + """Skip tests if ADK is missing, unless testing missing dependency behavior.""" + if "missing_adk" not in request.node.name: + pytest.importorskip("google.adk") + + +from integrations.google_adk import ( + ADK_AVAILABLE, + SemanticaSessionService, + __version__, + semantica_decision_tools, + semantica_kg_tools, +) + + +def test_adk_available(): + assert ADK_AVAILABLE is True + + +def test_version_exists(): + assert isinstance(__version__, str) + assert __version__ + + +def test_public_exports(): + from integrations import google_adk + + assert hasattr(google_adk, "semantica_kg_tools") + assert hasattr(google_adk, "semantica_decision_tools") + assert hasattr(google_adk, "SemanticaSessionService") + assert hasattr(google_adk, "ADK_AVAILABLE") + assert hasattr(google_adk, "__version__") + + +def test_kg_tools_export(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_kg_tools(graph) + + assert isinstance(tools, list) + assert len(tools) == 4 + + +def test_decision_tools_export(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_decision_tools(graph) + + assert isinstance(tools, list) + assert len(tools) == 2 + + +def test_session_service_export(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + service = SemanticaSessionService(graph) + + assert service is not None + assert service.graph is graph + + +def test_missing_adk_graceful_failure(monkeypatch): + import integrations.google_adk as init_module + + # Safely mock the flag to False just for this test + monkeypatch.setattr(init_module, "ADK_AVAILABLE", False) + + assert init_module.ADK_AVAILABLE is False \ No newline at end of file diff --git a/tests/integrations/google_adk/test_kg_tools.py b/tests/integrations/google_adk/test_kg_tools.py new file mode 100644 index 00000000..a1617015 --- /dev/null +++ b/tests/integrations/google_adk/test_kg_tools.py @@ -0,0 +1,122 @@ +import pytest +import sys +import importlib +from unittest.mock import patch + +@pytest.fixture(autouse=True) +def require_adk(request): + """Skip tests if ADK is missing, unless testing missing dependency behavior.""" + if "missing_adk" not in request.node.name: + pytest.importorskip("google.adk") + +from integrations.google_adk.kg_tools import ( + ADK_AVAILABLE, + extract_entities, + extract_relations, + semantica_kg_tools, +) + + +def test_adk_available(): + assert ADK_AVAILABLE is True + + +def test_extract_entities_returns_dict(): + result = extract_entities( + "Google was founded by Larry Page and Sergey Brin." + ) + + assert isinstance(result, dict) + assert "entities" in result + assert "count" in result + assert isinstance(result["entities"], list) + + +def test_extract_relations_returns_dict(): + result=extract_relations('Alice works at Acme Corp. Bob founded Acme Corp. ') + + assert isinstance(result, dict) + assert "relations" in result + assert "count" in result + assert isinstance(result["relations"], list) + assert "error" not in result,f"Extraction failed with the error: {result.get('error')}" + assert result["count"]>0, " We Expected at least one relation to be extracted" + assert len(result["relations"]) > 0, "Relation list should not ne empty" + + +def test_add_to_graph_surfaces_the_real_relation_extractor_error(): + """A genuine bug in the relation extractor must surface its own message, + not a second, unrelated TypeError from a compatibility shim that can + never succeed.""" + from unittest.mock import MagicMock + + from integrations.google_adk.kg_tools import _add_to_graph + from semantica.context import ContextGraph + + fake_extractor = MagicMock() + fake_extractor.extract_relations.side_effect = TypeError("boom: bad entities shape") + + with patch( + "integrations.google_adk.kg_tools._get_relation_extractor", + return_value=fake_extractor, + ): + result = _add_to_graph("Alice works at Acme Corp.", ContextGraph()) + + assert "error" in result + assert "boom: bad entities shape" in result["error"] + + +def test_semantica_kg_tools_returns_function_tools(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_kg_tools(graph) + + assert isinstance(tools, list) + assert len(tools) == 4 + + for tool in tools: + assert tool is not None + + +def test_semantica_kg_tools_share_graph(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + tools = semantica_kg_tools(graph) + + assert len(tools) == 4 + + # FunctionTool should wrap our closures/functions. + names = { + getattr(tool, "name", None) + for tool in tools + } + + assert "extract_entities" in names + assert "extract_relations" in names + assert "add_to_shared_graph" in names + assert "query_shared_graph" in names + + +def test_extract_entities_invalid_input(): + result = extract_entities(None) + + assert isinstance(result, dict) + assert result["entities"] == [] + assert result["count"] == 0 + assert "error" in result + + +def test_missing_adk_graceful_failure(monkeypatch): + import pytest + import integrations.google_adk.kg_tools as kg_module + + # Safely mock the flag to False just for this test + monkeypatch.setattr(kg_module, "ADK_AVAILABLE", False) + + assert kg_module.ADK_AVAILABLE is False + with pytest.raises(ImportError, match="Google ADK is required"): + kg_module.semantica_kg_tools(None) \ No newline at end of file diff --git a/tests/integrations/google_adk/test_session_service.py b/tests/integrations/google_adk/test_session_service.py new file mode 100644 index 00000000..e2212171 --- /dev/null +++ b/tests/integrations/google_adk/test_session_service.py @@ -0,0 +1,576 @@ +import asyncio + +import pytest + + +import sys +import importlib +from unittest.mock import patch + +@pytest.fixture(autouse=True) +def require_adk(request): + """Skip tests if ADK is missing, unless testing missing dependency behavior.""" + if "missing_adk" not in request.node.name: + pytest.importorskip("google.adk") + + +from integrations.google_adk.session_service import ( + ADK_AVAILABLE, + SemanticaSessionService, +) + + +def test_adk_available(): + assert ADK_AVAILABLE is True + + +def test_create_session(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + assert session is not None + assert session.app_name == "test-app" + assert session.user_id == "test-user" + assert session.id + assert session.state == {} + assert session.events == [] + + +def test_create_session_with_state(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + state = { + "topic": "knowledge graphs", + "step": 1, + } + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + state=state, + ) + ) + + assert session.state == state + + +def test_get_session(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + created = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + state={"foo": "bar"}, + ) + ) + + loaded = asyncio.run( + service.get_session( + app_name="test-app", + user_id="test-user", + session_id=created.id, + ) + ) + + assert loaded is not None + assert loaded.id == created.id + assert loaded.app_name == "test-app" + assert loaded.user_id == "test-user" + assert loaded.state == {"foo": "bar"} + + +def test_get_session_honors_num_recent_events_config(): + from google.adk.events import Event + from google.adk.sessions.base_session_service import GetSessionConfig + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session(app_name="test-app", user_id="test-user") + ) + + for i in range(5): + asyncio.run( + service.append_event( + session, + Event(author="agent", invocation_id=f"inv-{i}"), + ) + ) + + full = asyncio.run( + service.get_session( + app_name="test-app", user_id="test-user", session_id=session.id + ) + ) + assert len(full.events) == 5 + + bounded = asyncio.run( + service.get_session( + app_name="test-app", + user_id="test-user", + session_id=session.id, + config=GetSessionConfig(num_recent_events=2), + ) + ) + assert [e.invocation_id for e in bounded.events] == ["inv-3", "inv-4"] + + +def test_get_missing_session(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.get_session( + app_name="test-app", + user_id="test-user", + session_id="does-not-exist", + ) + ) + + assert session is None + + +def test_create_duplicate_session_fails(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + session_id="fixed-session", + ) + ) + + with pytest.raises(ValueError): + asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + session_id="fixed-session", + ) + ) + + +def test_colon_in_identity_does_not_collide_with_a_different_tenant(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + # app_name="tenant:A", user_id="alice" and app_name="tenant", + # user_id="A:alice" would join to the identical raw string + # "adk-session:tenant:A:alice:s1" if the components were not escaped + # before joining. Both must be creatable as distinct sessions. + session1 = asyncio.run( + service.create_session( + app_name="tenant:A", + user_id="alice", + session_id="s1", + ) + ) + session2 = asyncio.run( + service.create_session( + app_name="tenant", + user_id="A:alice", + session_id="s1", + ) + ) + + fetched1 = asyncio.run( + service.get_session(app_name="tenant:A", user_id="alice", session_id="s1") + ) + fetched2 = asyncio.run( + service.get_session(app_name="tenant", user_id="A:alice", session_id="s1") + ) + + assert fetched1 is not None + assert fetched2 is not None + assert fetched1.app_name == "tenant:A" + assert fetched1.user_id == "alice" + assert fetched2.app_name == "tenant" + assert fetched2.user_id == "A:alice" + assert session1.id == session2.id == "s1" + + +def test_append_event(): + from google.adk.events import Event + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + event = Event( + author="test-agent", + invocation_id="invocation-1", + ) + + returned = asyncio.run( + service.append_event( + session, + event, + ) + ) + + assert returned is event + + +def test_append_event_does_not_persist_partial_events(): + """ADK's own base append_event is a no-op for partial/streaming events; + a graph-backed session must not persist them either.""" + from google.adk.events import Event + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + partial_event = Event(author="test-agent", invocation_id="chunk-1", partial=True) + final_event = Event(author="test-agent", invocation_id="chunk-1", partial=False) + + asyncio.run(service.append_event(session, partial_event)) + asyncio.run(service.append_event(session, final_event)) + + fetched = asyncio.run( + service.get_session( + app_name="test-app", + user_id="test-user", + session_id=session.id, + ) + ) + + assert len(fetched.events) == 1 + + +def test_append_event_persists(): + from google.adk.events import Event + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + event = Event( + author="test-agent", + invocation_id="invocation-1", + ) + + asyncio.run( + service.append_event( + session, + event, + ) + ) + + loaded = asyncio.run( + service.get_session( + app_name="test-app", + user_id="test-user", + session_id=session.id, + ) + ) + + assert loaded is not None + assert len(loaded.events) == 1 + assert loaded.events[0].author == "test-agent" + assert loaded.events[0].invocation_id == "invocation-1" + + +def test_list_sessions(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session1 = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + session2 = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + asyncio.run( + service.create_session( + app_name="other-app", + user_id="test-user", + ) + ) + + response = asyncio.run( + service.list_sessions( + app_name="test-app", + user_id="test-user", + ) + ) + + session_ids = { + session.id + for session in response.sessions + } + + assert session1.id in session_ids + assert session2.id in session_ids + assert len(response.sessions) == 2 + + +def test_list_sessions_returns_list_sessions_response(): + from google.adk.sessions.base_session_service import ListSessionsResponse + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + asyncio.run(service.create_session(app_name="test-app", user_id="test-user")) + + response = asyncio.run( + service.list_sessions(app_name="test-app", user_id="test-user") + ) + + assert isinstance(response, ListSessionsResponse) + assert isinstance(response.sessions, list) + + +def test_list_sessions_without_user_id_returns_all_users(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session1 = asyncio.run( + service.create_session(app_name="test-app", user_id="user-1") + ) + session2 = asyncio.run( + service.create_session(app_name="test-app", user_id="user-2") + ) + asyncio.run(service.create_session(app_name="other-app", user_id="user-1")) + + response = asyncio.run(service.list_sessions(app_name="test-app")) + + session_ids = {session.id for session in response.sessions} + assert session1.id in session_ids + assert session2.id in session_ids + assert len(response.sessions) == 2 + + +def test_session_state_is_persisted_in_graph(): + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + state={ + "research_topic": "AI agents", + }, + ) + ) + + nodes = graph.find_nodes() + + session_nodes = [ + node + for node in nodes + if isinstance(node, dict) + and node.get("type") == "ADKSession" + ] + + assert len(session_nodes) == 1 + + node = session_nodes[0] + + # ContextGraph.find_nodes() stores custom node attributes in metadata. + assert node["metadata"]["session_id"] == session.id + assert node["metadata"]["app_name"] == "test-app" + assert node["metadata"]["user_id"] == "test-user" + assert node["metadata"]["state"] == { + "research_topic": "AI agents", + } + + +def test_session_events_are_stored_as_graph_nodes(): + from google.adk.events import Event + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + event = Event( + author="researcher", + invocation_id="invocation-123", + ) + + asyncio.run( + service.append_event( + session, + event, + ) + ) + + nodes = graph.find_nodes() + + event_nodes = [ + node + for node in nodes + if isinstance(node, dict) + and node.get("type") == "ADKEvent" + ] + + assert len(event_nodes) == 1 + + event_node = event_nodes[0] + + # ContextGraph.find_nodes() stores custom node attributes in metadata. + assert event_node["metadata"]["session_id"] == session.id + assert event_node["metadata"]["author"] == "researcher" + assert event_node["metadata"]["invocation_id"] == "invocation-123" + + +def test_session_and_event_are_connected(): + from google.adk.events import Event + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + session = asyncio.run( + service.create_session( + app_name="test-app", + user_id="test-user", + ) + ) + + event = Event( + author="researcher", + invocation_id="invocation-456", + ) + + asyncio.run( + service.append_event( + session, + event, + ) + ) + + edges = graph.find_edges() + + has_event_edges = [ + edge + for edge in edges + if isinstance(edge, dict) + and edge.get("type") == "HAS_EVENT" + ] + + assert len(has_event_edges) == 1 + + edge = has_event_edges[0] + + assert edge["source"] == f"adk-session:{session.app_name}:{session.user_id}:{session.id}" + + +def test_slow_graph_scan_does_not_block_the_event_loop(): + """A synchronous, CPU-bound graph scan inside a session-service call + must not block other coroutines on the same event loop.""" + import time + from semantica.context import ContextGraph + + graph = ContextGraph() + service = SemanticaSessionService(graph) + + original_find_nodes = graph.find_nodes + + def _slow_find_nodes(*args, **kwargs): + time.sleep(0.3) + return original_find_nodes(*args, **kwargs) + + graph.find_nodes = _slow_find_nodes + + async def _run(): + heartbeats = 0 + + async def _heartbeat(): + nonlocal heartbeats + while True: + await asyncio.sleep(0.01) + heartbeats += 1 + + heartbeat_task = asyncio.create_task(_heartbeat()) + await service.list_sessions(app_name="test-app") + heartbeat_task.cancel() + return heartbeats + + heartbeats = asyncio.run(_run()) + + # If list_sessions blocked the event loop for the 0.3s sleep, the + # heartbeat coroutine would never have gotten a chance to run. + assert heartbeats > 0 + + +def test_missing_adk_graceful_failure(monkeypatch): + import pytest + import integrations.google_adk.session_service as session_module + + # Safely mock the flag to False just for this test + monkeypatch.setattr(session_module, "ADK_AVAILABLE", False) + + assert session_module.ADK_AVAILABLE is False + with pytest.raises(ImportError, match="Google ADK is required"): + session_module.SemanticaSessionService() \ No newline at end of file diff --git a/tests/integrations/google_adk/test_shared_graph.py b/tests/integrations/google_adk/test_shared_graph.py new file mode 100644 index 00000000..584a5dbb --- /dev/null +++ b/tests/integrations/google_adk/test_shared_graph.py @@ -0,0 +1,201 @@ +import asyncio + +import pytest + +import sys +import importlib +from unittest.mock import patch + +@pytest.fixture(autouse=True) +def require_adk(request): + """Skip tests if ADK is missing, unless testing missing dependency behavior.""" + if "missing_adk" not in request.node.name: + pytest.importorskip("google.adk") + + +from google.adk.events import Event + +from integrations.google_adk import ( + SemanticaSessionService, + semantica_decision_tools, + semantica_kg_tools, +) + + +def test_all_integrations_share_same_graph(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + kg_tools = semantica_kg_tools(graph) + decision_tools = semantica_decision_tools(graph) + session_service = SemanticaSessionService(graph) + + assert session_service.graph is graph + + # FunctionTool closures capture the supplied shared graph. + assert len(kg_tools) == 4 + assert len(decision_tools) == 2 + + +def test_kg_tools_and_decision_tools_lock_the_same_graph(): + """kg_tools and decision_tools must serialize writes against each other + when handed the same graph, not just within their own module.""" + from integrations.google_adk.decision_tools import _graph_lock as decision_graph_lock + from integrations.google_adk.kg_tools import _graph_lock as kg_graph_lock + from semantica.context import ContextGraph + + graph = ContextGraph() + + assert kg_graph_lock(graph) is decision_graph_lock(graph) + + +def test_shared_graph_session_and_decision_state(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + session_service = SemanticaSessionService(graph) + + # Record a decision using the same graph. + from integrations.google_adk.decision_tools import _record_decision + + decision_result = _record_decision( + category="shared-context", + scenario="Multi-agent workflow", + reasoning="Verify shared graph state.", + outcome="Shared graph works", + confidence=0.95, + decision_maker="test-agent", + entities=[], + source_documents=[], + graph=graph, + ) + + assert decision_result["decision_id"] + + # Create a session using the same graph. + session = asyncio.run( + session_service.create_session( + app_name="shared-app", + user_id="shared-user", + state={ + "decision_id": decision_result["decision_id"], + }, + ) + ) + + assert session.id + + loaded = asyncio.run( + session_service.get_session( + app_name="shared-app", + user_id="shared-user", + session_id=session.id, + ) + ) + + assert loaded is not None + assert loaded.state["decision_id"] == ( + decision_result["decision_id"] + ) + + +def test_shared_graph_event_and_knowledge_nodes(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + session_service = SemanticaSessionService(graph) + + session = asyncio.run( + session_service.create_session( + app_name="shared-app", + user_id="shared-user", + ) + ) + + event = Event( + author="researcher", + invocation_id="shared-invocation", + ) + + asyncio.run( + session_service.append_event( + session, + event, + ) + ) + + nodes = graph.find_nodes() + + session_nodes = [ + node + for node in nodes + if isinstance(node, dict) + and node.get("type") == "ADKSession" + ] + + event_nodes = [ + node + for node in nodes + if isinstance(node, dict) + and node.get("type") == "ADKEvent" + ] + + assert len(session_nodes) == 1 + assert len(event_nodes) == 1 + + assert ( + session_nodes[0]["metadata"]["session_id"] + == session.id + ) + + assert ( + event_nodes[0]["metadata"]["session_id"] + == session.id + ) + + +def test_shared_graph_supports_multiple_sessions(): + from semantica.context import ContextGraph + + graph = ContextGraph() + + service = SemanticaSessionService(graph) + + session1 = asyncio.run( + service.create_session( + app_name="multi-agent", + user_id="user-1", + ) + ) + + session2 = asyncio.run( + service.create_session( + app_name="multi-agent", + user_id="user-2", + ) + ) + + assert session1.id != session2.id + + response1 = asyncio.run( + service.list_sessions( + app_name="multi-agent", + user_id="user-1", + ) + ) + + response2 = asyncio.run( + service.list_sessions( + app_name="multi-agent", + user_id="user-2", + ) + ) + + assert len(response1.sessions) == 1 + assert len(response2.sessions) == 1 + + assert response1.sessions[0].id == session1.id + assert response2.sessions[0].id == session2.id \ No newline at end of file diff --git a/tests/semantic_extract/test_ner_merge_strategies.py b/tests/semantic_extract/test_ner_merge_strategies.py new file mode 100644 index 00000000..cd00257c --- /dev/null +++ b/tests/semantic_extract/test_ner_merge_strategies.py @@ -0,0 +1,763 @@ +"""Regression coverage for NER method merge strategies.""" + +from unittest.mock import patch + +import pytest + +from semantica.semantic_extract.ner_extractor import NERExtractor +from semantica.semantic_extract.types import Entity + + +def entity(text, label, start, end, confidence=0.9): + """Create a compact entity fixture with realistic offsets.""" + return Entity(text, label, start, end, confidence=confidence) + + +def test_consensus_rejects_single_method_candidate_with_empty_peer(): + """An empty eligible method must remain in the consensus denominator.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [("first", [entity("Apple", "ORG", 0, 5)]), ("second", [])], + eligible_methods=["first", "second"], + ) + + assert result == [] + + +def test_consensus_aligns_compatible_labels_and_exposes_provenance(): + """Compatible labels vote together and preserve auditable method evidence.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [ + ("first", [entity("Apple", "ORGANIZATION", 0, 5, 0.8)]), + ("second", [entity("Apple", "ORG", 0, 5, 0.9)]), + ], + eligible_methods=["first", "second"], + ) + + assert len(result) == 1 + merged = result[0] + assert merged.label == "ORG" + assert merged.confidence == pytest.approx(0.85) + assert merged.metadata["supporting_methods"] == ["first", "second"] + assert merged.metadata["vote_count"] == 2 + assert merged.metadata["eligible_method_count"] == 2 + assert merged.metadata["agreement"] == 1.0 + assert merged.metadata["method_scores"] == {"first": 0.8, "second": 0.9} + + +def test_consensus_keeps_repeated_mentions_at_distinct_offsets(): + """Matching text must not collapse separate document mentions.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + first_mentions = [ + entity("Apple", "ORG", 0, 5), + entity("Apple", "ORG", 10, 15), + ] + second_mentions = [ + entity("Apple", "ORG", 0, 5), + entity("Apple", "ORG", 10, 15), + ] + + result = extractor._vote_entities( + [("first", first_mentions), ("second", second_mentions)], + eligible_methods=["first", "second"], + ) + + assert [(item.text, item.start_char, item.end_char) for item in result] == [ + ("Apple", 0, 5), + ("Apple", 10, 15), + ] + + +def test_consensus_resolves_boundary_variants_deterministically(): + """Equal-confidence overlapping spans prefer the most specific boundary.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [ + ("first", [entity("Apple", "ORG", 0, 5, 0.9)]), + ("second", [entity("Apple Inc.", "ORG", 0, 10, 0.9)]), + ], + eligible_methods=["first", "second"], + ) + + assert len(result) == 1 + assert (result[0].text, result[0].start_char, result[0].end_char) == ( + "Apple Inc.", + 0, + 10, + ) + + +def test_consensus_resolves_conflicting_labels_deterministically(): + """With equal vote counts, the higher-confidence label wins predictably.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=1 + ) + + result = extractor._vote_entities( + [ + ("first", [entity("Apple", "ORG", 0, 5, 0.7)]), + ("second", [entity("Apple", "PRODUCT", 0, 5, 0.9)]), + ], + eligible_methods=["first", "second"], + ) + + assert len(result) == 1 + assert result[0].label == "PRODUCT" + assert result[0].metadata["supporting_methods"] == ["second"] + + +def test_consensus_prefers_a_same_label_match_over_a_tied_conflict(): + """A conflicting duplicate from one method cannot hide true agreement.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [ + ( + "first", + [ + entity("Apple", "PRODUCT", 0, 5), + entity("Apple", "ORG", 0, 5), + ], + ), + ("second", [entity("Apple", "ORG", 0, 5)]), + ], + eligible_methods=["first", "second"], + ) + + assert len(result) == 1 + assert result[0].label == "ORG" + assert result[0].metadata["supporting_methods"] == ["first", "second"] + + +def test_default_consensus_rejects_a_label_conflict_without_two_votes(): + """Different labels do not turn two methods into two votes for either label.""" + extractor = NERExtractor(method=["first", "second"], merge_strategy="consensus") + + result = extractor._vote_entities( + [ + ("first", [entity("Apple", "ORG", 0, 5)]), + ("second", [entity("Apple", "PRODUCT", 0, 5)]), + ], + eligible_methods=["first", "second"], + ) + + assert result == [] + + +def test_consensus_keeps_a_majority_label_despite_a_high_weight_single_vote(): + """Weights break ties only after the configured vote requirements are met.""" + extractor = NERExtractor( + method=["first", "second", "third"], + merge_strategy="consensus", + min_votes=2, + method_weights={"third": 100.0}, + ) + + result = extractor._vote_entities( + [ + ("first", [entity("Apple", "ORG", 0, 5, 0.7)]), + ("second", [entity("Apple", "ORG", 0, 5, 0.7)]), + ("third", [entity("Apple", "PRODUCT", 0, 5, 0.99)]), + ], + eligible_methods=["first", "second", "third"], + ) + + assert len(result) == 1 + assert result[0].label == "ORG" + assert result[0].metadata["supporting_methods"] == ["first", "second"] + + +def test_method_weights_treat_ml_and_spacy_as_one_backend(): + """A spaCy alias weight must apply when the configured method is called ml.""" + extractor = NERExtractor(method="pattern", merge_strategy="consensus", min_votes=1) + + result = extractor._vote_entities( + [ + ("ml", [entity("Apple", "ORG", 0, 5, 0.7)]), + ("regex", [entity("Apple", "PRODUCT", 0, 5, 0.9)]), + ], + eligible_methods=["ml", "regex"], + min_votes=1, + method_weights={"spacy": 100.0, "regex": 1.0}, + ) + + assert len(result) == 1 + assert result[0].label == "ORG" + + +def test_method_weights_reject_conflicting_ml_and_spacy_aliases(): + """A single backend cannot receive two different alias weights.""" + with pytest.raises(ValueError, match="conflicting values to aliases"): + NERExtractor( + method="pattern", + merge_strategy="consensus", + method_weights={"ml": 1.0, "spacy": 2.0}, + ) + + +def test_consensus_does_not_merge_distant_mentions_through_a_broad_span(): + """A broad span below the IoU threshold cannot fabricate cross-method support.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [ + ( + "first", + [ + entity("A B", "ORG", 0, 3), + entity("C D", "ORG", 4, 7), + ], + ), + ("second", [entity("A B C D", "ORG", 0, 7)]), + ], + eligible_methods=["first", "second"], + ) + + assert result == [] + + +def test_consensus_requires_each_vote_to_overlap_every_other_vote(): + """A chain of pairwise overlaps must not fabricate three-way support.""" + extractor = NERExtractor( + method=["first", "second", "third"], + merge_strategy="consensus", + min_votes=3, + ) + + result = extractor._vote_entities( + [ + ("first", [entity("ABCD", "ORG", 0, 4)]), + ("second", [entity("ABCDEF", "ORG", 0, 6)]), + ("third", [entity("CDEF", "ORG", 2, 6)]), + ], + eligible_methods=["first", "second", "third"], + ) + + assert result == [] + + +def test_consensus_prefers_an_exact_boundary_match_within_one_method_batch(): + """A weaker overlap cannot consume another method's exact boundary vote.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + + result = extractor._vote_entities( + [ + ( + "first", + [ + entity("Apple", "ORG", 0, 5, 0.99), + entity("Apple Inc.", "ORG", 0, 10, 0.1), + ], + ), + ("second", [entity("Apple Inc.", "ORG", 0, 10, 0.9)]), + ], + eligible_methods=["first", "second"], + ) + + assert len(result) == 1 + assert (result[0].start_char, result[0].end_char) == (0, 10) + assert result[0].metadata["supporting_methods"] == ["first", "second"] + + +def test_consensus_boundary_result_is_independent_of_method_result_order(): + """Method result order cannot affect the selected consensus boundary.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + first = [ + entity("Apple", "ORG", 0, 5, 0.99), + entity("Apple Inc.", "ORG", 0, 10, 0.1), + ] + second = [entity("Apple Inc.", "ORG", 0, 10, 0.9)] + + forward = extractor._vote_entities( + [("first", first), ("second", second)], + eligible_methods=["first", "second"], + ) + reverse = extractor._vote_entities( + [("second", second), ("first", list(reversed(first)))], + eligible_methods=["first", "second"], + ) + + assert [ + (item.label, item.start_char, item.end_char, item.metadata) for item in reverse + ] == [ + (item.label, item.start_char, item.end_char, item.metadata) for item in forward + ] + + +def test_consensus_keeps_nested_entities_with_different_labels(): + """Exact-span label arbitration must not erase a nested entity.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + mentions = [ + entity("New York", "GPE", 0, 8), + entity("New York Times", "ORG", 0, 14), + ] + + result = extractor._vote_entities( + [("first", mentions), ("second", mentions)], + eligible_methods=["first", "second"], + ) + + assert [(item.label, item.start_char, item.end_char) for item in result] == [ + ("GPE", 0, 8), + ("ORG", 0, 14), + ] + + +def test_union_keeps_complementary_single_method_entities(): + """Explicit union preserves the legacy complementary-method behavior.""" + extractor = NERExtractor(method=["first", "second"], merge_strategy="union") + + result = extractor._union_entities( + [("first", [entity("SKU-12345", "PRODUCT_CODE", 10, 19)]), ("second", [])], + eligible_methods=["first", "second"], + ) + + assert [(item.text, item.label) for item in result] == [ + ("SKU-12345", "PRODUCT_CODE") + ] + assert result[0].metadata["vote_count"] == 1 + assert result[0].metadata["agreement"] == 0.5 + + +def test_union_preserves_distinct_labels_and_original_label_spelling(): + """Union is complementary: it does not force label-conflict resolution.""" + extractor = NERExtractor(method=["first", "second"], merge_strategy="union") + + result = extractor._union_entities( + [ + ("first", [entity("Apple", "ORG", 0, 5)]), + ("second", [entity("Apple", "PRODUCT", 0, 5)]), + ], + eligible_methods=["first", "second"], + ) + + assert [(item.text, item.label) for item in result] == [ + ("Apple", "ORG"), + ("Apple", "PRODUCT"), + ] + + +def test_ml_and_spacy_aliases_do_not_duplicate_an_entity_or_a_vote(): + """Two names for spaCy represent one backend, not independent voters.""" + extractor = NERExtractor(method=["ml", "spacy"], merge_strategy="union") + + result = extractor._union_entities( + [ + ("ml", [entity("Apple", "ORG", 0, 5)]), + ("spacy", [entity("Apple", "ORG", 0, 5)]), + ], + eligible_methods=["ml", "spacy"], + ) + + assert len(result) == 1 + assert result[0].metadata["supporting_methods"] == ["ml"] + assert result[0].metadata["eligible_method_count"] == 1 + + +def test_extract_consensus_counts_a_successful_empty_method(): + """The public extraction path must retain empty method results for voting.""" + extractor = NERExtractor( + method=["pattern", "regex"], merge_strategy="consensus", min_votes=2 + ) + responses = {"pattern": [entity("Apple", "ORG", 0, 5)], "regex": []} + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: ( + lambda _text, **_options: responses[method_name] + ), + ): + assert extractor.extract_entities("Apple") == [] + + +def test_extract_consensus_keeps_method_provenance_on_a_successful_vote(): + """Public extraction must retain method names for provenance.""" + extractor = NERExtractor( + method=["pattern", "regex"], merge_strategy="consensus", min_votes=2 + ) + responses = { + "pattern": [entity("Apple", "ORG", 0, 5, 0.8)], + "regex": [entity("Apple", "ORGANIZATION", 0, 5, 0.9)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple") + + assert len(result) == 1 + assert result[0].metadata["supporting_methods"] == ["pattern", "regex"] + assert result[0].metadata["method_scores"] == {"pattern": 0.8, "regex": 0.9} + + +def test_merge_options_are_not_forwarded_to_custom_methods(): + """Custom methods without **kwargs remain usable with the new merge API.""" + extractor = NERExtractor( + method=["first", "second"], merge_strategy="consensus", min_votes=2 + ) + responses = { + "first": [entity("Apple", "ORG", 0, 5)], + "second": [entity("Apple", "ORG", 0, 5)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text: responses[method_name], + ): + result = extractor.extract_entities("Apple") + + assert len(result) == 1 + + +def test_legacy_options_remain_available_to_custom_methods(): + """Existing custom methods retain the legacy options they previously received.""" + with pytest.warns(DeprecationWarning, match="ensemble_voting"): + extractor = NERExtractor( + method="custom", ensemble_voting=True, post_process=True + ) + received = {} + + def custom_method(_text, *, ensemble_voting, post_process): + received.update( + ensemble_voting=ensemble_voting, + post_process=post_process, + ) + return [entity("Apple", "ORG", 0, 5)] + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + return_value=custom_method, + ): + result = extractor.extract_entities("Apple") + + assert len(result) == 1 + assert received == {"ensemble_voting": True, "post_process": True} + + +def test_extract_consensus_recovers_missing_llm_offsets_before_voting(): + """Typed LLM results with schema-default 0:0 spans can still vote safely.""" + extractor = NERExtractor( + method=["llm", "regex"], merge_strategy="consensus", min_votes=2 + ) + responses = { + "llm": [entity("Apple", "ORG", 0, 0, 0.9)], + "regex": [entity("Apple", "ORG", 0, 5, 0.8)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple") + + assert len(result) == 1 + assert (result[0].start_char, result[0].end_char) == (0, 5) + assert result[0].metadata["supporting_methods"] == ["llm", "regex"] + + +@pytest.mark.parametrize("start, end", [(0, 0), (None, None)]) +def test_union_discards_unresolved_invalid_offsets(start, end): + """Unresolvable default or nullable spans cannot leak into union output.""" + extractor = NERExtractor(method="llm", merge_strategy="union") + responses = {"llm": [entity("Absent", "ORG", start, end, 0.9)]} + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + assert extractor.extract_entities("Apple") == [] + + +def test_union_recovers_nullable_offsets_when_text_matches(): + """Nullable custom offsets remain usable when they can be aligned safely.""" + extractor = NERExtractor(method="custom", merge_strategy="union") + responses = {"custom": [entity("Apple", "ORG", None, None, 0.9)]} + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple") + + assert [(item.start_char, item.end_char) for item in result] == [(0, 5)] + + +def test_missing_offsets_are_assigned_to_distinct_repeated_mentions(): + """Text-only duplicate output is aligned in occurrence order before merging.""" + extractor = NERExtractor(method="llm", merge_strategy="union") + + aligned = extractor._align_entities_to_text( + [ + entity("Apple", "ORG", 0, 0), + entity("Apple", "ORG", 0, 0), + ], + "Apple and Apple", + ) + + assert [(item.start_char, item.end_char) for item in aligned] == [(0, 5), (10, 15)] + + +def test_missing_offsets_wrap_to_an_unoccupied_repeated_mention(): + """A valid later mention must not make an earlier missing one unalignable.""" + extractor = NERExtractor(method="llm", merge_strategy="union") + + aligned = extractor._align_entities_to_text( + [ + entity("Apple", "ORG", 10, 15), + entity("Apple", "ORG", 0, 0), + ], + "Apple and Apple", + ) + + assert [(item.start_char, item.end_char) for item in aligned] == [(10, 15), (0, 5)] + + +def test_missing_offsets_do_not_align_a_substring_inside_a_larger_word(): + """Offset recovery must not let Apple vote for the substring in Pineapple.""" + extractor = NERExtractor( + method=["llm", "regex"], merge_strategy="consensus", min_votes=2 + ) + responses = { + "llm": [entity("Apple", "ORG", 0, 0, 0.9)], + "regex": [entity("Pineapple", "ORG", 0, 9, 0.8)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + assert extractor.extract_entities("Pineapple") == [] + + +def test_missing_offsets_preserve_original_unicode_character_positions(): + """Case-insensitive matching preserves original Unicode offsets.""" + extractor = NERExtractor(method="llm", merge_strategy="union") + + aligned = extractor._align_entities_to_text( + [entity("Apple", "ORG", 0, 0)], + "İ Apple", + ) + + assert [(item.start_char, item.end_char) for item in aligned] == [(2, 7)] + + +def test_consensus_eligible_methods_can_exclude_complementary_extractors(): + """An explicit eligible subset controls the denominator used for agreement.""" + extractor = NERExtractor( + method=["first", "second", "supplement"], + merge_strategy="consensus", + min_votes=2, + min_agreement=0.75, + eligible_methods=["first", "second"], + ) + responses = { + "first": [entity("Apple", "ORG", 0, 5)], + "second": [entity("Apple", "ORG", 0, 5)], + "supplement": [], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple") + + assert len(result) == 1 + assert result[0].metadata["eligible_method_count"] == 2 + assert result[0].metadata["agreement"] == 1.0 + + +def test_extract_consensus_does_not_inject_fallback_candidates(): + """An all-empty consensus result must remain empty rather than fall back.""" + extractor = NERExtractor( + method=["pattern", "regex"], merge_strategy="consensus", min_votes=2 + ) + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda _method_name: lambda _text, **_options: [], + ): + assert extractor.extract_entities("Apple") == [] + + +def test_consensus_counts_a_failed_eligible_method_in_agreement(): + """A failed configured method is non-supporting rather than silently removed.""" + extractor = NERExtractor( + method=["pattern", "regex"], + merge_strategy="consensus", + min_votes=1, + min_agreement=0.75, + ) + + def method_for(method_name): + if method_name == "pattern": + return lambda _text, **_options: [entity("Apple", "ORG", 0, 5)] + return lambda _text, **_options: (_ for _ in ()).throw(RuntimeError("offline")) + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=method_for, + ): + assert extractor.extract_entities("Apple") == [] + + +def test_union_ignores_consensus_eligible_method_subset(): + """Complementary union must retain all methods even when consensus is scoped.""" + extractor = NERExtractor( + method=["pattern", "regex"], + merge_strategy="union", + eligible_methods=["pattern"], + ) + responses = { + "pattern": [entity("Apple", "ORG", 0, 5)], + "regex": [entity("SKU-12345", "PRODUCT_CODE", 10, 19)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple SKU-12345") + + assert [(item.text, item.label) for item in result] == [ + ("Apple", "ORG"), + ("SKU-12345", "PRODUCT_CODE"), + ] + + +def test_issue_1283_consensus_requires_cross_method_agreement(): + """The former ensemble union must not be mistaken for consensus.""" + responses = { + "first": [entity("Apple", "ORG", 0, 5)], + "second": [entity("SKU-12345", "PRODUCT_CODE", 6, 15)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + with pytest.warns(DeprecationWarning, match="ensemble_voting"): + legacy = NERExtractor(method=["first", "second"], ensemble_voting=True) + consensus = NERExtractor( + method=["first", "second"], + merge_strategy="consensus", + min_votes=2, + ) + + legacy_entities = legacy.extract_entities("Apple SKU-12345") + consensus_entities = consensus.extract_entities("Apple SKU-12345") + + assert [(item.text, item.label) for item in legacy_entities] == [ + ("Apple", "ORG"), + ("SKU-12345", "PRODUCT_CODE"), + ] + assert consensus_entities == [] + + +def test_fallback_remains_first_nonempty_method(): + """The default strategy remains the documented ordered fallback chain.""" + extractor = NERExtractor(method=["first", "second"], merge_strategy="fallback") + responses = {"first": [], "second": [entity("Apple", "ORG", 0, 5)]} + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: ( + lambda _text, **_options: responses[method_name] + ), + ): + result = extractor.extract_entities("Apple") + + assert [(item.text, item.label) for item in result] == [("Apple", "ORG")] + + +def test_default_strategy_short_circuits_at_the_first_nonempty_method(): + """No merge strategy keeps the public ordered fallback behavior unchanged.""" + extractor = NERExtractor(method=["first", "second"]) + requested_methods = [] + responses = { + "first": [entity("Apple", "ORG", 0, 5)], + "second": [entity("SKU-12345", "PRODUCT_CODE", 6, 15)], + } + + def method_for(method_name): + requested_methods.append(method_name) + return lambda _text, **_options: responses[method_name] + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=method_for, + ): + result = extractor.extract_entities("Apple SKU-12345") + + assert [(item.text, item.label) for item in result] == [("Apple", "ORG")] + assert requested_methods == ["first"] + + +def test_legacy_ensemble_flag_maps_to_deprecated_union_strategy(): + """Existing callers keep their union behavior while receiving migration guidance.""" + with pytest.warns(DeprecationWarning, match="ensemble_voting"): + extractor = NERExtractor(method=["first", "second"], ensemble_voting=True) + + assert extractor.merge_strategy == "union" + + +def test_legacy_ensemble_flag_still_runs_the_union_path(): + """The deprecated flag retains single-method candidates during migration.""" + with pytest.warns(DeprecationWarning, match="ensemble_voting"): + extractor = NERExtractor(method=["first", "second"], ensemble_voting=True) + responses = { + "first": [entity("Apple", "ORG", 0, 5)], + "second": [entity("SKU-12345", "PRODUCT_CODE", 6, 15)], + } + + with patch( + "semantica.semantic_extract.methods.get_entity_method", + side_effect=lambda method_name: lambda _text, **_options: responses[ + method_name + ], + ): + result = extractor.extract_entities("Apple SKU-12345") + + assert [(item.text, item.label) for item in result] == [ + ("Apple", "ORG"), + ("SKU-12345", "PRODUCT_CODE"), + ] diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py index 4a054be1..3679fec3 100644 --- a/tests/test_cli_commands.py +++ b/tests/test_cli_commands.py @@ -69,6 +69,19 @@ def _json_output(result) -> Any: return json.loads(result.output.strip()) +def _flatten(output: str) -> str: + """Undo Rich panel wrapping for substring assertions on error text. + + Rich wraps long messages across multiple bordered lines (each with its + own leading/trailing "│"), so a naive whitespace join still leaves those + border characters between words that were split across lines. Strip the + box-drawing characters first, then collapse whitespace. + """ + for ch in "┌┐└┘│─": + output = output.replace(ch, " ") + return " ".join(output.split()) + + # ─── Global flags ───────────────────────────────────────────────────────────── @@ -336,6 +349,79 @@ class TestIngest: assert captured["sources"] == "README.md" assert captured["kwargs"]["method"] == "file" + def test_configured_graph_backend_does_not_report_false_success( + self, runner, monkeypatch + ): + monkeypatch.setenv("GRAPH_STORE_DEFAULT_BACKEND", "neo4j") + monkeypatch.setattr( + "semantica.ingest.methods.ingest_file", + lambda sources, **kwargs: [{"path": sources}], + ) + + result = runner.invoke(cli_module.main, ["ingest", "README.md"]) + output = _flatten(result.output) + + assert result.exit_code != 0 + assert "does not write to graph stores" in output + assert "Ingested:" not in output + + def test_configured_graph_backend_error_does_not_recommend_broken_kg_build( + self, runner, monkeypatch + ): + # kg build also does not persist to a configured graph store + # (tracked separately as #1352), so the error must not send users to + # a command that will silently no-op the same way. + monkeypatch.setenv("GRAPH_STORE_DEFAULT_BACKEND", "neo4j") + monkeypatch.setattr( + "semantica.ingest.methods.ingest_file", + lambda sources, **kwargs: [{"path": sources}], + ) + + result = runner.invoke(cli_module.main, ["ingest", "README.md"]) + output = _flatten(result.output) + + assert result.exit_code != 0 + assert "kg build" not in output + + def test_output_flag_writes_real_content_and_bypasses_graph_error( + self, runner, monkeypatch, tmp_path + ): + monkeypatch.setenv("GRAPH_STORE_DEFAULT_BACKEND", "neo4j") + monkeypatch.setattr( + "semantica.ingest.methods.ingest_file", + lambda sources, **kwargs: [{"path": sources}], + ) + out_path = tmp_path / "out.json" + + result = runner.invoke( + cli_module.main, ["ingest", "README.md", "--output", str(out_path)] + ) + + _ok(result, substr="Wrote") + written = json.loads(out_path.read_text(encoding="utf-8")) + assert written == {"files": [{"path": "README.md"}]} + + def test_store_and_output_are_not_forwarded_to_ingest_backend( + self, runner, monkeypatch, tmp_path + ): + captured = {} + + def fake_ingest_file(sources, **kwargs): + captured["kwargs"] = kwargs + return [{"path": sources}] + + monkeypatch.setattr("semantica.ingest.methods.ingest_file", fake_ingest_file) + out_path = tmp_path / "out.json" + + result = runner.invoke( + cli_module.main, + ["ingest", "README.md", "--store", "neo4j", "--output", str(out_path)], + ) + + _ok(result) + assert "store" not in captured["kwargs"] + assert "output" not in captured["kwargs"] + def test_import_error_is_clean(self, runner, monkeypatch): monkeypatch.setattr(cli_module, "__import__", _import_side_effect, raising=False) original_import = __import__ @@ -771,6 +857,156 @@ class TestReason: assert result.exit_code != 0 assert "Traceback" not in result.output + def test_run_infers_from_graph_store_facts(self, runner, monkeypatch, tmp_path): + # reason run used to call Reasoner.run(), which does not exist + # (#1354); it must feed graph store facts + --rules into + # Reasoner.infer_facts(). + pytest.importorskip("numpy", reason="semantica.reasoning needs numpy") + rules_file = tmp_path / "rules.yaml" + rules_file.write_text( + '- IF Person(?x) THEN Human(?x)\n' + '- IF MANAGES(?x, ?y) THEN Manager(?x)\n' + '- IF Employee(?x) THEN Staff(?x)\n', + encoding="utf-8") + + class _FakeStore: + # Same dict schema as the real backends: nodes carry + # labels/properties, relationships carry start_node_id/end_node_id. + def get_nodes(self, limit=None): + return [{"id": 1, "labels": ["Person"], + "properties": {"name": "Alice"}}, + {"id": 2, "labels": ["Person", "Employee"], + "properties": {"name": "Bob"}}] + + def get_relationships(self, limit=None): + return [{"id": 9, "type": "MANAGES", + "start_node_id": 1, "end_node_id": 2}] + + monkeypatch.setattr(cli_module, "_get_graph_store", lambda ctx: _FakeStore()) + result = runner.invoke( + cli_module.main, + ["--json", "reason", "run", "--rules", str(rules_file)], + ) + _ok(result) + data = json.loads(result.output.strip()) + # Person(Alice), Person(Bob), Employee(Bob), MANAGES(Alice, Bob) + assert data["facts"] == 4 + assert "Human(Alice)" in data["inferred_facts"] + # Relationship endpoints resolve node ids to names. + assert "Manager(Alice)" in data["inferred_facts"] + # Secondary labels also become facts. + assert "Staff(Bob)" in data["inferred_facts"] + assert data["inferred_count"] == len(data["inferred_facts"]) + + def test_run_rejects_unwired_engine(self, runner): + result = runner.invoke(cli_module.main, + ["reason", "run", "--engine", "sparql"]) + assert result.exit_code != 0 + assert "not wired" in result.output + assert "reason query" in result.output + assert "Traceback" not in result.output + + def test_load_rule_definitions_formats(self, tmp_path): + yaml_list = tmp_path / "list.yaml" + yaml_list.write_text('- IF A(?x) THEN B(?x)\n- IF B(?x) THEN C(?x)\n', + encoding="utf-8") + assert cli_module._load_rule_definitions(str(yaml_list)) == [ + "IF A(?x) THEN B(?x)", "IF B(?x) THEN C(?x)"] + + yaml_map = tmp_path / "map.yaml" + yaml_map.write_text('rules:\n - IF A(?x) THEN B(?x)\n', encoding="utf-8") + assert cli_module._load_rule_definitions(str(yaml_map)) == [ + "IF A(?x) THEN B(?x)"] + + plain = tmp_path / "rules.dl" + plain.write_text('# comment\nIF A(?x) THEN B(?x)\n\n', encoding="utf-8") + assert cli_module._load_rule_definitions(str(plain)) == [ + "IF A(?x) THEN B(?x)"] + + def test_run_empty_graph_returns_zero_facts(self, runner, monkeypatch): + """reason run with an empty graph store should not crash and report 0 facts.""" + pytest.importorskip("numpy", reason="semantica.reasoning needs numpy") + + class _EmptyStore: + def get_nodes(self, limit=None): return [] + def get_relationships(self, limit=None): return [] + + monkeypatch.setattr(cli_module, "_get_graph_store", lambda ctx: _EmptyStore()) + result = runner.invoke(cli_module.main, ["--json", "reason", "run"]) + _ok(result) + data = json.loads(result.output.strip()) + assert data["facts"] == 0 + assert data["inferred_count"] == 0 + assert data["inferred_facts"] == [] + + def test_run_graph_store_error_surfaces_cleanly(self, runner, monkeypatch): + """A graph-store connectivity error must surface as a clean error, not a Traceback.""" + + def _bad_store(ctx): + raise RuntimeError("connection refused") + + monkeypatch.setattr(cli_module, "_get_graph_store", _bad_store) + result = runner.invoke(cli_module.main, ["reason", "run"]) + assert result.exit_code != 0 + assert "Traceback" not in result.output + assert "connection refused" in result.output + + def test_run_no_rules_uses_empty_ruleset(self, runner, monkeypatch): + """reason run without --rules should still succeed (zero rules -> zero inferences).""" + pytest.importorskip("numpy", reason="semantica.reasoning needs numpy") + + class _FakeStore: + def get_nodes(self, limit=None): + return [{"id": 1, "labels": ["Person"], "properties": {"name": "Alice"}}] + + def get_relationships(self, limit=None): + return [] + + monkeypatch.setattr(cli_module, "_get_graph_store", lambda ctx: _FakeStore()) + result = runner.invoke(cli_module.main, ["--json", "reason", "run"]) + _ok(result) + data = json.loads(result.output.strip()) + assert data["facts"] == 1 + assert data["inferred_count"] == 0 + + def test_load_rule_definitions_yaml_mapping_without_rules_key_raises(self, tmp_path): + """A YAML mapping with no 'rules' key must raise ClickException, not silently + pass the raw YAML lines as rules.""" + bad = tmp_path / "bad.yaml" + bad.write_text("some_key: some_value\nother_key: other_value\n", encoding="utf-8") + import click as _click + with pytest.raises(_click.ClickException, match="no 'rules' key"): + cli_module._load_rule_definitions(str(bad)) + + def test_load_rule_definitions_empty_file_returns_empty_list(self, tmp_path): + empty = tmp_path / "empty.yaml" + empty.write_text("", encoding="utf-8") + assert cli_module._load_rule_definitions(str(empty)) == [] + + def test_load_rule_definitions_yaml_rules_null_falls_to_plaintext(self, tmp_path): + """rules: null is valid YAML with the key present; the null value is + not a list, so the function falls through to plain-text parsing and + returns the literal line (one no-op rule). This documents the edge + case rather than asserting a specific useful behaviour.""" + f = tmp_path / "null_rules.yaml" + f.write_text("rules: null\n", encoding="utf-8") + result = cli_module._load_rule_definitions(str(f)) + # Plain-text fallback: the non-comment, non-blank line becomes a rule. + assert result == ["rules: null"] + + def test_run_rejects_deductive_engine(self, runner, monkeypatch): + """Engines other than rete/forward-chain must be rejected with a helpful message.""" + + class _EmptyStore: + def get_nodes(self, limit=None): return [] + def get_relationships(self, limit=None): return [] + + monkeypatch.setattr(cli_module, "_get_graph_store", lambda ctx: _EmptyStore()) + result = runner.invoke(cli_module.main, ["reason", "run", "--engine", "deductive"]) + assert result.exit_code != 0 + assert "not wired" in result.output + assert "Traceback" not in result.output + def test_explain_requires_conclusion(self, runner): result = runner.invoke(cli_module.main, ["reason", "explain"]) assert result.exit_code != 0 @@ -1350,6 +1586,65 @@ class TestStore: result = runner.invoke(cli_module.main, ["store", "connect", "--backend", "neo4j"]) _ok(result) + def test_connect_dispatches_through_graph_store(self, runner, monkeypatch): + # store connect used to call get_graph_store_method(backend) — the + # method registry, which needs (task, method_name) — so it raised a + # TypeError before any connection attempt (#1354). + calls = {} + + class _FakeGraphStore: + def __init__(self, backend=None, **cfg): + calls["backend"] = backend + calls["cfg"] = cfg + + def connect(self): + calls["connected"] = True + return True + + import semantica.graph_store as gs_mod + monkeypatch.setattr(gs_mod, "GraphStore", _FakeGraphStore) + result = runner.invoke(cli_module.main, [ + "store", "connect", "--backend", "neo4j", + "--uri", "bolt://example:7687", "--json"]) + _ok(result) + data = _json_output(result) + assert data == {"backend": "neo4j", "connected": True} + assert calls["backend"] == "neo4j" + assert calls["cfg"].get("uri") == "bolt://example:7687" + assert calls.get("connected") is True + + def test_connect_invalid_backend_reports_error_not_dispatch_error(self, runner): + """An unknown backend name must produce a meaningful backend error, not a + Python TypeError from the old get_graph_store_method() dispatch (#1354).""" + result = runner.invoke(cli_module.main, + ["store", "connect", "--backend", "does-not-exist"]) + # Exit 0 because store_connect always catches and reports errors gracefully. + _ok(result) + # The output must mention the backend, not a Python internal error. + assert "does-not-exist" in result.output + assert "TypeError" not in result.output + assert "Traceback" not in result.output + + def test_connect_backend_error_surfaces_in_json(self, runner, monkeypatch): + """A connect() failure must appear in JSON output as connected=False with an error field.""" + + class _FailingStore: + def __init__(self, backend=None, **cfg): + pass + + def connect(self): + raise RuntimeError("auth failed") + + import semantica.graph_store as gs_mod + monkeypatch.setattr(gs_mod, "GraphStore", _FailingStore) + result = runner.invoke(cli_module.main, [ + "store", "connect", "--backend", "neo4j", "--json"]) + _ok(result) + data = _json_output(result) + assert data["connected"] is False + assert "auth failed" in data.get("error", "") + assert data["backend"] == "neo4j" + def test_migrate_dry_run(self, runner): result = runner.invoke(cli_module.main, ["store", "migrate", "--from", "faiss", "--to", "qdrant", "--dry-run"]) @@ -1797,7 +2092,7 @@ class TestMCP: def test_list_tools_with_mock_shows_known_tools(self, runner, monkeypatch): fake_tools = _fake_module(__all__=["extract_entities", "query_graph"]) - monkeypatch.setitem(__import__("sys").modules, "mcp.tools", fake_tools) + monkeypatch.setitem(__import__("sys").modules, "semantica_mcp.mcp.tools", fake_tools) result = runner.invoke(cli_module.main, ["mcp", "list-tools"]) _ok(result) assert "extract_entities" in result.output @@ -2071,6 +2366,160 @@ class TestDoctorEmbeddingHintsAndEnv: assert "hash fallback" in st["note"], "padded/caps env value must enable deep mode" +class TestDoctorTableLayout: + """#1428 + Qodo review: doctor table must keep Check labels and Hint text + readable at a normal 80-column terminal. + + These tests render the *human-readable* (non-JSON) doctor table into a + captured 80-column Rich console so they cover the actual column-width + arithmetic, not just the JSON data. + + Two regressions are protected: + + A. #1428 — Hint (and Note) columns must not collapse into unreadable + single-character fragments or be silently truncated with a layout '…'. + overflow="fold" on both columns ensures content wraps across lines while + remaining fully present. + + B. Qodo — Long Check labels such as "Embeddings (sentence-transformers)" + must not be truncated/ellipsized. Assigning ratio=1 to the Check column + (as the original PR did) caused Rich to squeeze it below its min_width + at narrow terminals, so the fix removes ratio from the fixed-size columns. + """ + + def _render_doctor_at_80(self, runner, monkeypatch): + """Return the plain-text (ANSI-stripped) doctor table rendered at 80 cols.""" + import io + import re + from rich.console import Console + + # Unset LLM-provider env vars so the warn rows (with hints) are always present. + for var in ("OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GROQ_API_KEY"): + monkeypatch.delenv(var, raising=False) + + buf = io.StringIO() + narrow_console = Console( + file=buf, width=80, highlight=False, force_terminal=True, no_color=True + ) + monkeypatch.setattr(cli_module, "console", narrow_console) + + result = runner.invoke(cli_module.main, ["doctor"]) + assert result.exit_code == 0, f"doctor exited non-zero: {result.output!r}" + + return re.sub(r"\x1b\[[0-9;]*m", "", buf.getvalue()) + + def _hint_column_parts(self, output: str) -> "list[str]": + """Extract non-blank Hint-column segments from each rendered line. + + Locates the Hint column start from the header row and slices that + suffix from every subsequent line, so the test is insensitive to the + exact widths of the other columns. + """ + lines = output.splitlines() + # Line 0 is blank (console.print() blank line before table). + hdr = next((l for l in lines if "Hint" in l and "Check" in l), None) + assert hdr is not None, "Could not find table header in doctor output" + hint_start = hdr.index("Hint") + + parts = [] + for line in lines: + if len(line) > hint_start: + seg = line[hint_start:].rstrip() + if seg and seg != "Hint" and not set(seg).issubset({"─", " "}): + parts.append(seg) + return parts + + # ── B: Qodo regression ──────────────────────────────────────────────────── + + def test_long_check_label_not_truncated_at_80_cols(self, runner, monkeypatch): + """'Embeddings (sentence-transformers)' must appear verbatim at 80 cols. + + Before the fix, ratio=1 on the Check column let Rich squeeze it below + its min_width, turning the label into 'Embedd…' or similar. + """ + output = self._render_doctor_at_80(runner, monkeypatch) + assert "Embeddings (sentence-transformers)" in output, ( + "Check label 'Embeddings (sentence-transformers)' was truncated in " + "the 80-column doctor table — the ratio= constraint on the Check " + "column must be removed so min_width=34 is always honoured." + ) + + def test_all_check_labels_not_truncated_at_80_cols(self, runner, monkeypatch): + """Every standard Check label must appear verbatim at 80 cols.""" + output = self._render_doctor_at_80(runner, monkeypatch) + for label in ( + "Python", + "semantica", + "rich", + "Graph store", + "Vector store", + "Embeddings (sentence-transformers)", + "Embeddings (fastembed)", + "OpenAI", + "Anthropic", + "Groq", + "Config file", + "Log directory", + ): + assert label in output, ( + f"Check label {label!r} was truncated or missing in the " + "80-column doctor table." + ) + + # ── A: #1428 regression ─────────────────────────────────────────────────── + + def test_hint_content_fully_present_at_80_cols(self, runner, monkeypatch): + """The LLM-provider hints must be fully present (folded, not ellipsized). + + With overflow='fold' the full hint text wraps across lines; no + characters are discarded. Joining the Hint-column segments (stripping + whitespace) must reconstruct each complete hint string. + """ + import re + + output = self._render_doctor_at_80(runner, monkeypatch) + parts = self._hint_column_parts(output) + hint_joined = re.sub(r"\s+", "", "".join(parts)) + + # Each LLM-provider hint must be fully recoverable from the folded lines. + for expected in ( + "exportOPENAI_API_KEY=\u2026", # export OPENAI_API_KEY=… + "exportANTHROPIC_API_KEY=\u2026", # export ANTHROPIC_API_KEY=… + "exportGROQ_API_KEY=\u2026", # export GROQ_API_KEY=… + ): + assert expected in hint_joined, ( + f"Hint content {expected!r} is missing from the 80-column " + "doctor table — overflow='fold' must be set on the Hint column " + "so no content is silently discarded." + ) + + def test_hint_column_has_no_single_char_fragments_at_80_cols( + self, runner, monkeypatch + ): + """No Hint-column line must be a single alphabetic character. + + The original #1428 bug produced outputs like: + export + O + P + E + N + A + I + ... + because Rich allocated the Hint column only 1–2 characters of content + width. overflow='fold' on a properly-wide column eliminates this. + """ + output = self._render_doctor_at_80(runner, monkeypatch) + parts = self._hint_column_parts(output) + single_char_alpha = [p for p in parts if len(p.strip()) == 1 and p.strip().isalpha()] + assert not single_char_alpha, ( + f"Hint column contains single-character lines {single_char_alpha!r} " + "at 80 columns — the Hint column is too narrow; check min_width and " + "ratio settings." + ) + + class TestEmbedGenerateOutput: """#994: `embed generate --output` must write files `embed index` can read.""" diff --git a/tests/test_mcp_decisions_causal_chain.py b/tests/test_mcp_decisions_causal_chain.py index d9f8219d..2cfdb79e 100644 --- a/tests/test_mcp_decisions_causal_chain.py +++ b/tests/test_mcp_decisions_causal_chain.py @@ -1,6 +1,6 @@ """ Regression tests for Issue #781 — Causal chain error signaling & fallback parameter forwarding -in mcp/tools/decisions.py: handle_get_causal_chain. +in semantica_mcp/mcp/tools/decisions.py: handle_get_causal_chain. """ import sys @@ -9,7 +9,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..') import unittest from unittest.mock import MagicMock, patch -from mcp.tools.decisions import handle_get_causal_chain +from semantica_mcp.mcp.tools.decisions import handle_get_causal_chain class TestMCPDecisionsCausalChain(unittest.TestCase): @@ -31,7 +31,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"error": "decision_id is required", "chain": []}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") def test_runtime_outer_exception_shape(self, mock_get_graph): """Verify outer exception handler returns standard error shape without count/direction.""" mock_get_graph.side_effect = RuntimeError("database failure") @@ -43,7 +43,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): self.assertNotIn("count", response) self.assertNotIn("direction", response) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_unsupported_backend_returns_error(self, mock_analyzer_cls, mock_get_graph): """ @@ -67,7 +67,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): self.assertNotIn("count", response) self.assertNotIn("direction", response) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_fallback_path_forwards_direction_and_max_depth(self, mock_analyzer_cls, mock_get_graph): """Verify fallback graph.get_causal_chain receives direction and max_depth keyword arguments.""" @@ -90,7 +90,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"chain": ["node_a", "node_b"], "count": 2, "direction": "upstream"}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_fallback_success_response(self, mock_analyzer_cls, mock_get_graph): """Verify fallback graph.get_causal_chain default parameters and success response shape.""" @@ -111,7 +111,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"chain": ["node_default"], "count": 1, "direction": "downstream"}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_primary_analyzer_success_path(self, mock_analyzer_cls, mock_get_graph): """Verify normal operation via CausalChainAnalyzer when available.""" @@ -136,7 +136,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"chain": ["dec_down_1", "dec_down_2"], "count": 2, "direction": "downstream"}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_fallback_depth_kwarg_signature(self, mock_analyzer_cls, mock_get_graph): """Verify fallback works for backends accepting 'depth' kwarg (like OpenClaw).""" @@ -163,7 +163,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"chain": ["openclaw_a", "openclaw_b"], "count": 2, "direction": "upstream"}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_fallback_positional_only_signature(self, mock_analyzer_cls, mock_get_graph): """Verify fallback works for backends accepting only positional decision_id.""" @@ -188,7 +188,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): {"chain": ["pos_node"], "count": 1, "direction": "downstream"}, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_input_hardening_and_dos_prevention( self, mock_analyzer_cls, mock_get_graph @@ -212,7 +212,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): "12345", direction="downstream", max_depth=100 ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_internal_typeerror_not_masked(self, mock_analyzer_cls, mock_get_graph): """Verify internal TypeError inside get_causal_chain is not masked as signature error.""" @@ -232,7 +232,7 @@ class TestMCPDecisionsCausalChain(unittest.TestCase): }, ) - @patch("mcp.tools.decisions.get_graph") + @patch("semantica_mcp.mcp.tools.decisions.get_graph") @patch("semantica.context.causal_analyzer.CausalChainAnalyzer") def test_internal_typeerror_calls_backend_only_once(self, mock_analyzer_cls, mock_get_graph): """ diff --git a/tests/test_mcp_package_export_graph.py b/tests/test_mcp_package_export_graph.py index b8d63228..6df27146 100644 --- a/tests/test_mcp_package_export_graph.py +++ b/tests/test_mcp_package_export_graph.py @@ -1,10 +1,10 @@ -"""Regression tests for the standalone mcp/ package export_graph tool. +"""Regression tests for the standalone semantica_mcp/mcp package export_graph tool. -The mcp/ server (python -m mcp / python -m mcp.server) had two failures on -every RDF export format: +The standalone MCP server (python -m semantica_mcp.mcp / python -m +semantica_mcp.mcp.server) had two failures on every RDF export format: 1. AttributeError: 'ContextGraph' object has no attribute 'get' - handle_export_graph() in mcp/tools/export.py called + handle_export_graph() in semantica_mcp/mcp/tools/export.py called RDFExporter().export_to_rdf(graph, ...) passing the raw ContextGraph object instead of the canonical kg dict expected by the exporter. @@ -15,8 +15,8 @@ every RDF export format: so this interleaved non-JSON bytes corrupted framing for every client. Fixes applied: - - mcp/tools/export.py: convert with graph.to_kg_dict() before export_to_rdf() - - mcp/__init__.py: os.environ["SEMANTICA_DISABLE_PROGRESS"] = "1" at + - semantica_mcp/mcp/tools/export.py: convert with graph.to_kg_dict() before export_to_rdf() + - semantica_mcp/mcp/__init__.py: os.environ["SEMANTICA_DISABLE_PROGRESS"] = "1" at package initialisation, before any tool handler can instantiate RDFExporter and therefore before the tracker singleton is created. """ @@ -62,18 +62,18 @@ class TestMCPPackageExportGraphRDF(unittest.TestCase): supported RDF format, not an error dict.""" def setUp(self): - # Inject a known graph into the mcp/ session so handlers don't try to + # Inject a known graph into the semantica_mcp.mcp session so handlers don't try to # build a full ContextGraph (which requires heavy ML dependencies). - import mcp.session as _session + import semantica_mcp.mcp.session as _session self._orig_graph = _session._graph _session._graph = _make_graph() def tearDown(self): - import mcp.session as _session + import semantica_mcp.mcp.session as _session _session._graph = self._orig_graph def test_turtle_returns_non_empty_string(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "turtle"}) self.assertNotIn("error", result, result) self.assertIsInstance(result["data"], str) @@ -82,35 +82,35 @@ class TestMCPPackageExportGraphRDF(unittest.TestCase): self.assertIn("@prefix", result["data"]) def test_ttl_alias_returns_non_empty_string(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "ttl"}) self.assertNotIn("error", result, result) self.assertIsInstance(result["data"], str) self.assertGreater(len(result["data"]), 0) def test_nt_returns_non_empty_string(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "nt"}) self.assertNotIn("error", result, result) self.assertIsInstance(result["data"], str) self.assertGreater(len(result["data"]), 0) def test_xml_returns_non_empty_string(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "xml"}) self.assertNotIn("error", result, result) self.assertIsInstance(result["data"], str) self.assertGreater(len(result["data"]), 0) def test_jsonld_returns_non_empty_string(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "json-ld"}) self.assertNotIn("error", result, result) self.assertIsInstance(result["data"], str) self.assertGreater(len(result["data"]), 0) def test_all_rdf_formats_succeed(self): - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph for fmt in ("turtle", "ttl", "nt", "xml", "json-ld"): with self.subTest(fmt=fmt): result = handle_export_graph({"format": fmt}) @@ -122,7 +122,7 @@ class TestMCPPackageExportGraphRDF(unittest.TestCase): """The pre-fix code passed ContextGraph directly to export_to_rdf(), causing AttributeError: 'ContextGraph' object has no attribute 'get'. Verify that error does not appear in the result.""" - from mcp.tools.export import handle_export_graph + from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "turtle"}) if "error" in result: self.assertNotIn("'ContextGraph' object has no attribute 'get'", @@ -134,12 +134,12 @@ class TestMCPPackageExportGraphRDF(unittest.TestCase): # --------------------------------------------------------------------------- class TestMCPPackageStdoutProtection(unittest.TestCase): - """The standalone mcp/ server must not write any progress bytes to stdout. + """The standalone semantica_mcp.mcp server must not write any progress bytes to stdout. stdout is the MCP JSON-RPC transport channel. These tests use a subprocess to get a clean process state where SEMANTICA_DISABLE_PROGRESS has not yet been set, so we can verify that - importing mcp and running an export produces no progress bytes on stdout. + importing semantica_mcp.mcp and running an export produces no progress bytes on stdout. """ def _run_in_subprocess(self, code: str, timeout: int = 30) -> subprocess.CompletedProcess: @@ -163,11 +163,11 @@ class TestMCPPackageStdoutProtection(unittest.TestCase): ) def test_importing_mcp_sets_disable_progress(self): - """Importing the mcp package must set SEMANTICA_DISABLE_PROGRESS=1 + """Importing the semantica_mcp.mcp package must set SEMANTICA_DISABLE_PROGRESS=1 before any tool handler runs.""" code = ( "import os; " - "import mcp; " # triggers mcp/__init__.py + "import semantica_mcp.mcp; " # triggers semantica_mcp/mcp/__init__.py "print(os.environ.get('SEMANTICA_DISABLE_PROGRESS', 'NOT SET'))" ) result = self._run_in_subprocess(code) @@ -183,8 +183,8 @@ import os, sys # Ensure clean state os.environ.pop("SEMANTICA_DISABLE_PROGRESS", None) -import mcp # sets SEMANTICA_DISABLE_PROGRESS=1 -import mcp.session as session +import semantica_mcp.mcp # sets SEMANTICA_DISABLE_PROGRESS=1 +import semantica_mcp.mcp.session as session from semantica.context.context_graph import ContextGraph g = ContextGraph() @@ -201,7 +201,7 @@ def _capture(s): return _orig(s) sys.stdout.write = _capture -from mcp.tools.export import handle_export_graph +from semantica_mcp.mcp.tools.export import handle_export_graph result = handle_export_graph({"format": "turtle"}) sys.stdout.write = _orig diff --git a/tests/test_mcp_package_persistence.py b/tests/test_mcp_package_persistence.py index ce834c49..6a602a3e 100644 --- a/tests/test_mcp_package_persistence.py +++ b/tests/test_mcp_package_persistence.py @@ -1,4 +1,4 @@ -"""Regression tests for root mcp/ graph persistence (issue #1134). +"""Regression tests for semantica_mcp/mcp graph persistence (issue #1134). Covers: 1. get_graph() loads an existing JSON file via load_from_file(), not the @@ -21,7 +21,7 @@ from unittest.mock import patch from semantica.context.context_graph import ContextGraph -import mcp.session as _session +import semantica_mcp.mcp.session as _session # --------------------------------------------------------------------------- @@ -36,7 +36,7 @@ def _fresh_graph() -> ContextGraph: class _IsolatedSession: - """Context manager that resets the mcp.session singleton before and after + """Context manager that resets the semantica_mcp.mcp.session singleton before and after each test so tests are independent of process-level state.""" def __enter__(self): @@ -132,14 +132,14 @@ class TestMCPSessionLoad(unittest.TestCase): # --------------------------------------------------------------------------- class TestMCPPackageMutationPersistence(unittest.TestCase): - """Mutations via the root mcp/ tool handlers must persist to SEMANTICA_KG_PATH + """Mutations via the semantica_mcp/mcp tool handlers must persist to SEMANTICA_KG_PATH so the data survives a server restart (simulated by a fresh load_from_file).""" # ---- record_decision ------------------------------------------------ def test_record_decision_persists_when_kg_path_set(self): """handle_record_decision must write to disk when SEMANTICA_KG_PATH is set.""" - from mcp.tools.decisions import handle_record_decision + from semantica_mcp.mcp.tools.decisions import handle_record_decision with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f: path = f.name @@ -175,7 +175,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): def test_record_decision_works_without_kg_path(self): """handle_record_decision must succeed even when SEMANTICA_KG_PATH is unset.""" - from mcp.tools.decisions import handle_record_decision + from semantica_mcp.mcp.tools.decisions import handle_record_decision with _IsolatedSession(): env = {k: v for k, v in os.environ.items() if k != "SEMANTICA_KG_PATH"} @@ -195,7 +195,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): def test_add_entity_persists_when_kg_path_set(self): """handle_add_entity must write to disk when SEMANTICA_KG_PATH is set.""" - from mcp.tools.graph import handle_add_entity + from semantica_mcp.mcp.tools.graph import handle_add_entity with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f: path = f.name @@ -225,7 +225,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): def test_add_entity_works_without_kg_path(self): """handle_add_entity must succeed when SEMANTICA_KG_PATH is unset.""" - from mcp.tools.graph import handle_add_entity + from semantica_mcp.mcp.tools.graph import handle_add_entity with _IsolatedSession(): env = {k: v for k, v in os.environ.items() if k != "SEMANTICA_KG_PATH"} @@ -239,7 +239,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): def test_add_relationship_persists_when_kg_path_set(self): """handle_add_relationship must write to disk when SEMANTICA_KG_PATH is set.""" - from mcp.tools.graph import handle_add_relationship + from semantica_mcp.mcp.tools.graph import handle_add_relationship with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f: path = f.name @@ -247,7 +247,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): with _IsolatedSession(): with patch.dict(os.environ, {"SEMANTICA_KG_PATH": path}): # Nodes must exist before an edge can be added. - from mcp.tools.graph import handle_add_entity + from semantica_mcp.mcp.tools.graph import handle_add_entity handle_add_entity({"id": "rel_src", "label": "Source"}) handle_add_entity({"id": "rel_tgt", "label": "Target"}) result = handle_add_relationship({ @@ -273,7 +273,7 @@ class TestMCPPackageMutationPersistence(unittest.TestCase): def test_add_relationship_works_without_kg_path(self): """handle_add_relationship must succeed when SEMANTICA_KG_PATH is unset.""" - from mcp.tools.graph import handle_add_entity, handle_add_relationship + from semantica_mcp.mcp.tools.graph import handle_add_entity, handle_add_relationship with _IsolatedSession(): env = {k: v for k, v in os.environ.items() if k != "SEMANTICA_KG_PATH"} diff --git a/tests/test_mcp_package_version.py b/tests/test_mcp_package_version.py index 72e4aa55..3dd338ba 100644 --- a/tests/test_mcp_package_version.py +++ b/tests/test_mcp_package_version.py @@ -1,9 +1,9 @@ -"""Regression tests for version reporting in the top-level `mcp` package +"""Regression tests for version reporting in the `semantica_mcp.mcp` package (issue #863). Covers the same stale-version bug as `test_mcp_server_version.py` for the -standalone `mcp/` server (run via `python -m mcp.server`), which is a -separate implementation from `semantica.mcp_server` and was not covered +standalone MCP server (run via `python -m semantica_mcp.mcp.server`), which +is a separate implementation from `semantica.mcp_server` and was not covered by that fix. `semantica.__version__` is the authoritative package version (see semantica/mcp_server/__init__.py), so all three surfaces are asserted against it directly. @@ -16,10 +16,10 @@ import unittest sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -import mcp import semantica -from mcp.resources.registry import _read_schema_info -from mcp.server import _handle_initialize +import semantica_mcp.mcp as mcp +from semantica_mcp.mcp.resources.registry import _read_schema_info +from semantica_mcp.mcp.server import _handle_initialize _EXPECTED = semantica.__version__ diff --git a/tests/test_ner_configurations.py b/tests/test_ner_configurations.py index 8224158f..9c48bd87 100644 --- a/tests/test_ner_configurations.py +++ b/tests/test_ner_configurations.py @@ -236,11 +236,11 @@ class TestNERConfigurations(unittest.TestCase): mock_spacy.load.return_value = mock_nlp with patch('semantica.semantic_extract.methods.SPACY_AVAILABLE', True): - # Init extractor with list of methods - extractor = NERExtractor(method=["llm", "ml"], ensemble_voting=True) + # Explicit union retains complementary single-method entities. + extractor = NERExtractor(method=["llm", "ml"], merge_strategy="union") entities = extractor.extract_entities(self.text) - # Since ensemble_voting=True (implied merge), we expect unique entities + # Union keeps unique entities from every successful method. # Apple Inc (from both) + Steve Jobs (from ML) texts = [e.text for e in entities]