feat(cloudflare/ai-search): AiSearch, AiSearchInstance and AiSearchNamespace#639
Merged
Conversation
…nstruct, worker bindings - AiSearchInstance: namespace-scoped CRUD + `namespace` attribute (replacement on change) - AiSearchNamespace: reserved `default` namespace is bindable but never updated/deleted - AiSearch: convenience construct that auto-mints the API + service token for R2 sources - Worker bindings: `ai_search` (instance) + `ai_search_namespace`, async (`env`) and Effect-native (`.bind`) - Bump distilled for namespace-scoped instance operations with typed errors Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Install the packages built from this commit: alchemy bun add alchemy@https://pkg.ing/alchemy/230210f@alchemy.run/better-auth bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/230210f@alchemy.run/pr-package bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/230210f |
Rewrite AiSearch worker-binding docs to show full Effect Worker first, then async, and wire AiSearchInstance.bind static for the examples. Co-authored-by: Cursor <cursoragent@cursor.com>
- Add an AI Search tutorial favoring the AiSearch helper (Effect + async worker) - Enrich AiSearchInstance/AiSearchNamespace JSDoc with binding + namespace examples - Rename AiSearchInstance attribute `id` -> `instanceId` - Rename Instance/Namespace/Token source files to AiSearch* and fix imports Co-authored-by: Cursor <cursoragent@cursor.com>
Web-crawler instances index a seed URL and need no service token. Cloudflare only crawls a domain the account owns, so the tests seed the crawl at a deployed Worker's workers.dev URL with `parseType: "crawl"`. - AiSearch construct: web-crawler source returns `token: undefined` - AiSearchInstance: create/read/delete a web-crawler instance with no token - `AiSearch` `url` now accepts `Input<string>` so a Worker URL output works - bump distilled for the typed `WebCrawlerDomainNotOwned` error Co-authored-by: Cursor <cursoragent@cursor.com>
…emap gotcha The crawl target now serves a linked multi-page site so `parseType: "crawl"` has more to walk. Emulating a sitemap was attempted but Cloudflare validates it synchronously at create (discovered via robots.txt) and a fresh workers.dev URL returns `missing_sitemap`, so crawl remains the reliable parse mode. Co-authored-by: Cursor <cursoragent@cursor.com>
…minated `source`
The construct now discriminates on what you pass as `source`:
```ts
// R2 — pass the bucket; filters at top level
AiSearch("docs", { source: bucket, prefix: "docs/", include: ["published/"] });
// Web crawler — pass a URL; parse/crawl/store groups
AiSearch("site", {
source: url,
parse: { type: "crawl", contentSelector: [{ path: "/docs", selector: "main" }] },
crawl: { depth: 3, includeSubdomains: true },
store: { bucket: storeBucket },
});
```
`parse`/`crawl`/`store` map onto distilled's
`sourceParams.webCrawler.{parseType+parseOptions,crawlOptions,storeOptions}`;
`store.bucket` takes an R2Bucket. The low-level AiSearchInstance keeps the
distilled shape and now documents every web-crawler option.
Co-authored-by: Cursor <cursoragent@cursor.com>
…rce filters - Keep R2 examples ahead of web-crawler in the AiSearch + AiSearchInstance reference (move the basic crawler example into the web-crawler section). - Tutorial: document `prefix` / `include` / `exclude` for an R2 `source`, and add a note linking out to the reference for the web-crawler options. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ect DX - Switch Worker bindings off the deprecated `AutoRAG` shape onto the `AiSearchInstance` / `AiSearchNamespace` runtime types: `chatCompletions` (retrieval + generation), `search`, `info`, `stats`, plus namespace-level `list` / `search` and `raw` accessors. - `AiSearch` construct accepts `namespace: AiSearchNamespace` (the resource, not its name) so the engine orders pipeline-after-namespace. - Tune R2 service-token propagation retry (exponential 1.5x, 10 attempts). Co-authored-by: Cursor <cursoragent@cursor.com>
sam-goodwin
added a commit
that referenced
this pull request
Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full Cloudflare AI Search (AutoRAG) coverage: a convenience construct with a discriminated data
source, Worker bindings in both Effect-native and async styles, and the underlying namespace-scoped resources.AiSearchconstruct — discriminated bysourceThe fast path for the common case. It picks the data source from what you pass as
source: anR2Bucketfor R2, or a URL string for a web crawl.For R2, the construct mints a least-privilege
AccountApiToken(AI Search Index Engine) +AiSearchTokenand wires it into the instance — Cloudflare only provisions this token via dashboard/Wrangler, never on a programmatic create. Pass your owntokenIdto skip minting.Source-specific options are flattened onto the props:
prefix,include/exclude(micromatch globs, max 10 each, exclude wins),jurisdiction.parse,crawl,store(an R2 bucket to hold crawl output).Group pipelines under a namespace by passing the
AiSearchNamespaceresource itself (not its name) — the engine then orders the pipeline after the namespace on deploy and before it on destroy:Worker bindings (Effect-native + async)
Bindings resolve to the current
AiSearchInstance/AiSearchNamespaceruntime shapes (the deprecatedAutoRAGshape is gone). Bind theAiSearchresult during a Worker's init phase withCloudflare.AiSearchInstance.bind(search)for an Effect-native client:The client surfaces
chatCompletions(retrieval + generation),search(retrieval only),info,stats, andraw. The namespace client adds.get(name), namespace-widelist/search, andraw.Or pass it via
envfor a vanilla async Worker.InferEnvmaps both flavors to the runtime types (AiSearchInstance,AiSearchNamespace):Underlying resources
The construct is just these resources composed; drop down when you need to share a token across instances or adopt an existing one.
AiSearchInstanceuses the namespace-scoped API and exposes anamespaceattribute (its identity isinstanceId). Thenamespace,source,type, andembeddingModelare immutable — changing any replaces the instance.AiSearchNamespacetreats the account-provideddefaultnamespace as reserved: it's bindable and adoptable but never updated or deleted on teardown (Cloudflare disallows deleting it).AiSearchTokenwraps anAccountApiTokeninto the service-token shape the R2 indexer needs.Docs
tutorial/cloudflare/ai-searchfavoring theAiSearchhelper, explaining what it provisions and binding it into an Effect / async Worker.AiSearch,AiSearchInstance, andAiSearchNamespace(R2-first ordering, source filters, web-crawler options, namespace grouping via theAiSearchconstruct, Effect/async binding), regenerated API reference.distilled
Depends on the namespace-scoped instance operations (create/read/update/list/delete) with resource-qualified typed errors (
AiSearchInstanceNotFound,NamespaceNotFound,WebCrawlerDomainNotOwned, …), pushed to distilledmainand bumped here.Tests
Live-tested against Cloudflare:
Namespace,Instance(incl. custom-namespace + replacement),Token, theAiSearchconstruct (R2 + web-crawler), andBindings(async + Effect workers). Web-crawler lifecycle uses an account-ownedworkers.devtarget; an ungated probe asserts theWebCrawlerDomainNotOwnedtyped error for unowned domains.