Skip to content

chore(types): mypy strict for locus.rag.* (#34 batch 6 — final)#48

Merged
fede-kamel merged 1 commit into
mainfrom
feat/mypy-migrate-batch6
May 2, 2026
Merged

chore(types): mypy strict for locus.rag.* (#34 batch 6 — final)#48
fede-kamel merged 1 commit into
mainfrom
feat/mypy-migrate-batch6

Conversation

@fede-kamel
Copy link
Copy Markdown
Contributor

Summary

Lifts the last legitimate module group out of the mypy ignore_errors blanket: locus.rag.* (~5.7k LOC across embeddings, seven vector stores, multimodal stack, retriever, and tool factories).

After this lands, only locus.hooks.builtin.* remains on ignore_errors — held back as a behavioural fix (tracked in #45). Issue #34 is otherwise complete.

Migration summary (#34)

PR Modules lifted LOC
#42 batch 1 core.reducers / core.send / models.registry / tools.context / tools.registry ~1.5k
#44 batch 3 server / multiagent.graph / integrations / a2a / skills / playbooks / streaming ~5k
#46 batch 4 loop / reasoning ~4k
#47 batch 5 memory ~5k
#48 batch 6 (this) rag ~5.7k

That's roughly 21k LOC moved from ignore_errors into strict mypy across five PRs. Remaining: hooks.builtin.* only (tracked separately in #45 — fixing it requires aligning hook method signatures with the event-based HookProvider Protocol, which is a behavioural change rather than typing).

Notable fixes

  • rag.tools factory functions and RAGToolkit accessors now return Any (decorated tool objects).
  • rag.retriever: typed locals on every await self.store.* delegate.
  • rag.multimodal.MultimodalProcessor.processors declares its value type as ContentProcessor (the existing Protocol).
  • rag.embeddings.oci: bind oci.config.from_file result to a local typed dict before use.
  • rag.stores.qdrant: cast query_points results to list[ScoredPoint].
  • rag.stores.chroma: targeted # type: ignore[arg-type, unused-ignore] at Collection.upsert / query boundary calls. unused-ignore is added so the ignore tolerates the dev mypy / pre-commit mypy version drift.
  • mypy.ignore_missing_imports gains pytesseract, PIL, pypdf, PyPDF2, pdf2image (purely-optional deps loaded inside try/except).

Test plan

  • hatch run typecheck — clean (150 source files)
  • hatch run test — 3,246 passed, 1 skipped
  • pre-commit run --all-files — clean
  • CI: lint+typecheck on 3.11 and 3.14
  • CI: test matrix on 3.11–3.14

Lifts the last legitimate module group (``locus.rag.*``) out of the
mypy ``ignore_errors`` blanket. Covers ~5.7k LOC across the
embedding providers, the seven shipped vector stores (Oracle 26ai,
OpenSearch, Qdrant, Pinecone, Chroma, pgvector, in-memory), the
multimodal processor stack, the retriever, and the tool factories.

After this lands, the ``ignore_errors`` block holds **only**
``locus.hooks.builtin.*``, which is held back as a behavioural fix
(#45) — the built-in hooks have stale method signatures vs the
``HookProvider`` Protocol and would TypeError at runtime when
dispatched through ``HookRegistry``. The mypy migration tracked in
#34 is otherwise complete.

Notable fixes:

- ``rag.tools`` factory functions and ``RAGToolkit`` accessors now
  return ``Any`` (the ``@tool``-decorated runtime objects don't have
  a stable user-visible type).
- ``rag.retriever``: typed locals on every ``await self.store.*``
  delegate so the return annotations are honoured.
- ``rag.multimodal.MultimodalProcessor.processors`` declares its
  value type as ``ContentProcessor`` (the existing Protocol) so the
  per-type sub-classes share a structural supertype and the
  ``processor.process(...)`` call type-checks.
- ``rag.embeddings.oci``: bind ``oci.config.from_file`` result to a
  local typed dict before use; ``self._oci_config_dict`` is declared
  as ``... | None`` for the pre-init state and mypy can't narrow
  through the field assignment.
- ``rag.stores.qdrant``: cast ``query_points`` results to
  ``list[ScoredPoint]`` (the SDK union shape is wider than what we
  actually receive at runtime).
- ``rag.stores.chroma``: targeted ``# type: ignore[arg-type,
  unused-ignore]`` at ``Collection.upsert`` / ``query`` boundary
  calls. Chroma's typed signatures want narrow ``Sequence[float]`` /
  specific metadata unions; passing the wider ``list[float]`` /
  ``dict[str, Any]`` we have is structurally compatible at runtime.
  ``unused-ignore`` is added so the ignore tolerates the dev mypy /
  pre-commit mypy version drift.
- ``mypy.ignore_missing_imports`` gains ``pytesseract``, ``PIL``,
  ``pypdf``, ``PyPDF2``, ``pdf2image`` (purely-optional deps loaded
  inside try/except for OCR / PDF / image / audio extraction).

This is the final batch of the migration tracked in #34.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label May 2, 2026
@fede-kamel fede-kamel merged commit 428fa7d into main May 2, 2026
10 checks passed
@fede-kamel fede-kamel deleted the feat/mypy-migrate-batch6 branch May 2, 2026 17:04
fede-kamel added a commit that referenced this pull request May 19, 2026
``Agent.run_sync`` already shut the model client down inside the
asyncio.run loop so the model's TCP / TLS state didn't survive into
the next run_sync's loop. The checkpointer carried the same hazard
and we hadn't covered it: an OracleBackend pool created during the
first run_sync stays bound to the now-closed loop, so the next
run_sync's save against the same backend hits ORA-03146 / DPY-4011
when oracledb tries to reuse a connection whose underlying TCP state
no longer matches the live loop.

The fix is the symmetric cleanup: in the same finally block that
closes the model client, look for ``self.config.checkpointer.close``
and ``await`` it. ``StorageBackendAdapter.close`` already delegates
to the wrapped backend, so ``OracleBackend.close`` (which drops the
pool) fires on the exit path of every ``run_sync`` rather than
leaving the pool dangling until the next call notices the dead loop.

Note: this alone does not unstick notebook 15 inside the workbench
harness — it still surfaces ORA-03146 on Part 2's tight checkpoint
loop. Standalone the notebook passes. The remaining issue is a
deeper workbench-bootstrap interaction (see follow-up task #48) and
needs work in ``workbench/backend/runner.py``, not the Agent
runtime.

Tests: 768/768 pass.
Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant