Skip to content

refactor(semantic-search): introduce backend contract#67

Merged
aryasaatvik merged 3 commits into
devfrom
feat/semantic-search-backend-contract
Jun 26, 2026
Merged

refactor(semantic-search): introduce backend contract#67
aryasaatvik merged 3 commits into
devfrom
feat/semantic-search-backend-contract

Conversation

@aryasaatvik

@aryasaatvik aryasaatvik commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

Introduce the semantic search backend contract and move the current vector implementation behind it.

Changes

  • Add ToolSearchBackend as the plugin-level backend boundary.
  • Keep the existing vector/Gemini implementation available through ToolSearchBackend.vector.
  • Wire the Cloudflare host through the backend option.

Tests

  • bun run --cwd packages/plugins/semantic-search typecheck
  • bun run --cwd packages/plugins/semantic-search test src/sdk/plugin.test.ts
  • bun run --cwd apps/host-cloudflare typecheck
  • oxfmt --check apps/host-cloudflare/src/plugins.ts packages/plugins/semantic-search/src/sdk/index.ts packages/plugins/semantic-search/src/sdk/plugin.ts packages/plugins/semantic-search/src/sdk/plugin.test.ts packages/plugins/semantic-search/src/sdk/tool-search-backend.ts
  • oxlint -c .oxlintrc.jsonc apps/host-cloudflare/src/plugins.ts packages/plugins/semantic-search/src/sdk/index.ts packages/plugins/semantic-search/src/sdk/plugin.ts packages/plugins/semantic-search/src/sdk/plugin.test.ts packages/plugins/semantic-search/src/sdk/tool-search-backend.ts --deny-warnings

Stack

  1. refactor(semantic-search): introduce backend contract #67 👈 current
  2. feat(semantic-search): add cloudflare ai search backend #68

@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces ToolSearchBackend as a formal plugin-level boundary, moving all vector/Gemini indexing logic out of plugin.ts and into the new tool-search-backend.ts module behind a clean factory contract. The Cloudflare host is wired through ToolSearchBackend.vector(...) rather than passing raw bindings directly to the plugin.

  • New contractToolSearchBackend (interface) and ToolSearchBackendFactory (factory + storage lifecycle) decouple the plugin shell from implementation details; notConfigured and unconfiguredIndex sentinels are now the single source of truth for the un-configured path.
  • plugin.ts shrinks to ~60 lines — all reindex/sweep/search/status logic delegates to deps.backend, with ?? notConfigured() / ?? unconfiguredIndex fallbacks preserving the previous typed-error behaviour when no backend is present.
  • Host wiringapps/host-cloudflare/src/plugins.ts constructs the backend with ToolSearchBackend.vector({ store, geminiApiKey, namespace }) and guards on the same store && geminiApiKey condition as before, so the no-op path is unchanged.

Confidence Score: 5/5

Safe to merge — all changed paths are covered by typecheck and unit tests, and the no-backend fallback is safe and typed throughout.

The refactoring is a clean extraction with no logic changes: every operation that previously returned a typed error when unconfigured continues to do so through the shared sentinels, the Cloudflare host guard is unchanged, and the new backend-storage wiring test validates the full factory lifecycle. No regressions introduced.

No files require special attention.

Important Files Changed

Filename Overview
packages/plugins/semantic-search/src/sdk/tool-search-backend.ts New file introducing ToolSearchBackend interface, ToolSearchBackendFactory, and the vector implementation behind makeVectorToolSearchBackend — logic cleanly migrated from plugin.ts with shared notConfigured/unconfiguredIndex sentinels
packages/plugins/semantic-search/src/sdk/plugin.ts Massively simplified to ~60 lines by delegating all logic to the backend contract; unconfiguredIndex sentinel returned when no backend is present, resolving the previous thread concern
packages/plugins/semantic-search/src/sdk/plugin.test.ts Tests updated to construct inline ToolSearchBackend stubs; new backend storage wiring test added covering pluginStorage passthrough, storage construction, and build callback
packages/plugins/semantic-search/src/sdk/index.ts Adds public exports from tool-search-backend.ts; ToolSearchBackend value and type alias (ToolSearchBackendType) correctly disambiguated to avoid name collision
apps/host-cloudflare/src/plugins.ts Cloudflare host updated to construct ToolSearchBackend.vector(...) and pass it as the backend option; store+geminiApiKey guard preserves the existing no-op-when-unconfigured behaviour

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Host["apps/host-cloudflare\nplugins.ts"] -->|"ToolSearchBackend.vector(\n  store, geminiApiKey, namespace\n)"| Factory["ToolSearchBackendFactory\n(makeVectorToolSearchBackend)"]

    Factory -->|"pluginStorage"| Plugin["semanticSearchPlugin\nplugin.ts"]
    Factory -->|"storage(deps) → TStorage"| Plugin
    Factory -->|"build({ storage }) → ToolSearchBackend"| Plugin

    Plugin -->|"makeSemanticSearchExtension(\n  backend\n)"| Ext["SemanticSearchExtension\n(index / reindex / sweep / search / status / provider)"]

    Ext -->|"backend present"| Backend["ToolSearchBackend\n(vector impl)"]
    Ext -->|"backend absent"| NC["notConfigured()\nor unconfiguredIndex"]

    Backend -->|"index"| TSI["ToolSearchIndex.Service\n(makeToolSearchIndex)"]
    Backend -->|"reindex"| RTI["runToolSearchIndex"]
    Backend -->|"sweep"| SW["sweepRemoved"]
    Backend -->|"search"| Prov["ToolDiscoveryProvider\n(vector or hybrid)"]
    Backend -->|"status"| Stat["fingerprints.count\n+ lexicalStore.count"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    Host["apps/host-cloudflare\nplugins.ts"] -->|"ToolSearchBackend.vector(\n  store, geminiApiKey, namespace\n)"| Factory["ToolSearchBackendFactory\n(makeVectorToolSearchBackend)"]

    Factory -->|"pluginStorage"| Plugin["semanticSearchPlugin\nplugin.ts"]
    Factory -->|"storage(deps) → TStorage"| Plugin
    Factory -->|"build({ storage }) → ToolSearchBackend"| Plugin

    Plugin -->|"makeSemanticSearchExtension(\n  backend\n)"| Ext["SemanticSearchExtension\n(index / reindex / sweep / search / status / provider)"]

    Ext -->|"backend present"| Backend["ToolSearchBackend\n(vector impl)"]
    Ext -->|"backend absent"| NC["notConfigured()\nor unconfiguredIndex"]

    Backend -->|"index"| TSI["ToolSearchIndex.Service\n(makeToolSearchIndex)"]
    Backend -->|"reindex"| RTI["runToolSearchIndex"]
    Backend -->|"sweep"| SW["sweepRemoved"]
    Backend -->|"search"| Prov["ToolDiscoveryProvider\n(vector or hybrid)"]
    Backend -->|"status"| Stat["fingerprints.count\n+ lexicalStore.count"]
Loading

Reviews (3): Last reviewed commit: "refactor(semantic-search): let backends ..." | Re-trigger Greptile

Comment thread packages/plugins/semantic-search/src/sdk/plugin.ts Outdated
Comment thread packages/plugins/semantic-search/src/sdk/plugin.ts Outdated
@aryasaatvik aryasaatvik force-pushed the feat/semantic-search-backend-contract branch from 6063ba3 to 02ad2ce Compare June 26, 2026 15:40
@aryasaatvik aryasaatvik merged commit ee1b934 into dev Jun 26, 2026
8 checks passed
aryasaatvik added a commit that referenced this pull request Jun 26, 2026
## Summary

Add the Cloudflare AI Search backend for the semantic search backend
contract.

## Changes

- Add the AI Search backend and plugin-storage tracking for uploaded
items.
- Use AI Search as the Cloudflare host semantic backend when the binding
is present.
- Truncate uploaded documents by bytes before sending them to AI Search.

## Tests

- `bun run --cwd packages/plugins/semantic-search typecheck`
- `bun run --cwd packages/plugins/semantic-search test
src/sdk/plugin.test.ts src/sdk/ai-search.test.ts`
- `bun run --cwd apps/host-cloudflare typecheck`
- `oxfmt --check apps/host-cloudflare/src/app.ts
apps/host-cloudflare/src/config.ts apps/host-cloudflare/src/execution.ts
apps/host-cloudflare/src/plugins.ts apps/host-cloudflare/wrangler.jsonc
packages/plugins/semantic-search/src/api/group.ts
packages/plugins/semantic-search/src/sdk/ai-search.ts
packages/plugins/semantic-search/src/sdk/ai-search.test.ts
packages/plugins/semantic-search/src/sdk/collections.ts
packages/plugins/semantic-search/src/sdk/documents.ts
packages/plugins/semantic-search/src/sdk/index.ts
packages/plugins/semantic-search/src/sdk/plugin.ts
packages/plugins/semantic-search/src/sdk/tool-search-backend.ts`
- `oxlint -c .oxlintrc.jsonc apps/host-cloudflare/src/app.ts
apps/host-cloudflare/src/config.ts apps/host-cloudflare/src/execution.ts
apps/host-cloudflare/src/plugins.ts
packages/plugins/semantic-search/src/api/group.ts
packages/plugins/semantic-search/src/sdk/ai-search.ts
packages/plugins/semantic-search/src/sdk/ai-search.test.ts
packages/plugins/semantic-search/src/sdk/collections.ts
packages/plugins/semantic-search/src/sdk/documents.ts
packages/plugins/semantic-search/src/sdk/index.ts
packages/plugins/semantic-search/src/sdk/plugin.ts
packages/plugins/semantic-search/src/sdk/tool-search-backend.ts
--deny-warnings`

<!-- stack:links:start -->
### [Stack](https://github.com/aryasaatvik/stack)

1. #67
2. **#68** 👈 current
<!-- stack:links:end -->
@aryasaatvik aryasaatvik deleted the feat/semantic-search-backend-contract branch June 26, 2026 16: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