Skip to content

feat(ports): SearchProvider port + RustSearchAdapter (PLAN.md Fases 0-1) - #78

Merged
netty-linux merged 5 commits into
masterfrom
feat/search-provider-port
Aug 12, 2026
Merged

feat(ports): SearchProvider port + RustSearchAdapter (PLAN.md Fases 0-1)#78
netty-linux merged 5 commits into
masterfrom
feat/search-provider-port

Conversation

@netty-linux

Copy link
Copy Markdown
Owner

Summary

  • Fase 0: SearchProvider/VisionProvider Protocol ports; web_search refactored onto WebSearchProvider (the default SearchProvider adapter) with no observable behavior change; SearchError added to the domain error hierarchy; reusable parametrized contract-test suites for both ports.
  • Fase 1: RustSearchAdapter backed by the nullain-search wheel (tantivy BM25, nullain-sdk-search) — index/query via asyncio.to_thread, fetch delegates to an injected WebSearchProvider, PyO3 exceptions translated to SearchError. New nullain-sdk[search-rust] extra. Contract suite runs against both adapters, Rust case skipif-gated on the wheel.
  • Docs: PLAN.md Fases 0-1 marked complete, docs/architecture.md documents the ports/adapters + conditional-registration + contract-test pattern, READMEs (EN/PT-BR) gain the SearchProvider node and an "Optional sub-SDKs" section, CHANGELOG.md [Unreleased] entry.
  • Tech debt tracked in Publicar nullain-search no PyPI e substituir tool.uv.sources por constraint de versão #77: tool.uv.sources temporarily points nullain-search at the sibling repo's local path since the wheel isn't on PyPI yet.

No public API change — SearchProvider/VisionProvider/RustSearchAdapter/SearchError are not exported from nullain/__init__.py.

Test plan

  • make check (ruff + pyright strict + pytest) verified green at each of the 3 commits individually, not just at HEAD
  • Contract-test suite run both without the wheel installed (rust_search cases skip cleanly) and with it (maturin develop --release against the real sibling repo — 14/14 pass)
  • tests/unit/test_web_search.py's pre-existing 16 assertions pass unmodified (proof of zero behavior change in the refactor)
  • Manual diff of nullain-sdk/src/nullain/__init__.py — zero lines changed

🤖 Generated with Claude Code

netty-linux and others added 5 commits August 11, 2026 22:30
…arch

Adds two hexagonal-boundary Protocols: SearchProvider (index/query/fetch)
and VisionProvider (describe_image/ocr/analyze_screenshot), per PLAN.md
Fase 0. web_search's SearXNG/DuckDuckGo logic is wrapped as
WebSearchProvider, the default SearchProvider adapter, with no change in
observable behavior — the existing 16 tests pass unmodified. Adds
SearchError to the domain error hierarchy (replacing the adapter-local
WebSearchQueryError) and a reusable, parametrized contract-test suite for
each port so any future adapter is validated against the same assertions.

No public API change: SearchProvider/VisionProvider are not exported from
nullain/__init__.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the SearchProvider port against the nullain-search PyO3 wheel
(tantivy BM25 core, github.com/netty-linux/nullain-sdk-search): index/query
delegate to the Rust index via asyncio.to_thread, fetch delegates to an
injected WebSearchProvider instead of the Rust index's own fetch (which
only retrieves content it indexed itself — a different contract than this
port's "fetch a URL"). PyO3 exceptions (ValueError/OSError/RuntimeError)
translate to SearchError; a missing wheel raises a clear ImportError
pointing at the new nullain-sdk[search-rust] extra rather than failing to
import the port module.

The contract-test suite now runs against both adapters, with the Rust case
skipif-gated on the wheel being importable.

tool.uv.sources temporarily points nullain-search at the sibling repo's
path (not yet published to PyPI — tracked in #77) so `uv sync` resolves;
swap for a plain version constraint once the wheel is released.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- PLAN.md: Fase 0 and Fase 1 marked done (2026-08-11) with what was
  delivered and what's still pending (PyPI publish, tracked in #77;
  ripgrep benchmark not done this pass).
- docs/architecture.md: documents the now-established ports/adapters
  pattern — where a new port/adapter pair lives (co-located module vs
  adapters/ subpackage + selector), how optional-dependency adapters
  register conditionally (lazy import, ImportError with install hint,
  pyproject extra, skipif tests), and that contract tests gate any new
  adapter for an existing port.
- README.md / README.pt-BR.md: architecture diagram gains the
  SearchProvider node; new "Optional sub-SDKs" section links
  nullain-sdk-search and the search-rust extra.
- CHANGELOG.md: [Unreleased] entry for SearchProvider/VisionProvider,
  SearchError, RustSearchAdapter, and the contract-test suites.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
)

tool.uv.sources points nullain-search at ../nullain-sdk-search since the
wheel isn't on PyPI yet — CI needs that path checked out to resolve the
workspace, same as local dev. Every job that runs `uv sync`/`uv build`
gets a second checkout of netty-linux/nullain-sdk-search at that relative
path. Remove once #77 lands (wheel published, source override replaced
with a plain version constraint).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
actions/checkout's `path` input refuses any location outside the main
repo's checkout directory ("Repository path ... is not under ..."), so
the previous `path: ../nullain-sdk-search` failed immediately. Checks out
into a subdirectory instead and moves it up one level with a follow-up
shell step, landing at the same ../nullain-sdk-search path uv sync
expects.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@netty-linux
netty-linux merged commit 1df754a into master Aug 12, 2026
11 checks passed
@netty-linux
netty-linux deleted the feat/search-provider-port branch August 12, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant