AI SDK (2)

Last updated Oct 1, 2026 The Vercel AI SDK ↗︎ is a TypeScript toolkit for building applications with large language models. The ai-search-provider ↗︎ package connects AI Search to the AI SDK, so you can generate responses grounded in your indexed content, retrieve chunks, and manage documents from the same API.

This guide builds a Worker that creates an AI Search instance, uploads and indexes a document, and then queries it with the AI SDK.

Prerequisites

  1. Sign up for a Cloudflare account ↗︎.
  2. Install Node.js ↗︎.

<details>

<summary>

Node.js version manager

</summary>

Use <a href="https://viteplus.dev/guide/env">Vite+ (<code>vp env</code>) ↗︎</a> to install and manage Node.js versions.

For supported Node.js versions, refer to the <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/#install-wrangler">Wrangler system requirements</a>.

</details>

1. Create a Worker project

Create a new Worker project using the create-cloudflare CLI (C3). C3 ↗︎ is a command-line tool designed to help you set up and deploy new applications to Cloudflare.

Create a new project named ai-search-ai-sdk by running:

npmyarnpnpm

npm create cloudflare@latest -- ai-search-ai-sdk
yarn create cloudflare ai-search-ai-sdk
pnpm create cloudflare@latest ai-search-ai-sdk

For setup, select the following options:

  • For What would you like to start with?, choose Hello World example.
  • For Which template would you like to use?, choose Worker only.
  • For Which language do you want to use?, choose TypeScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

Go to your application directory:

cd ai-search-ai-sdk

2. Install the AI SDK and provider

Install the AI SDK and the AI Search provider. The provider requires AI SDK v6 (ai@^6):

npmyarnpnpmbun

npm i ai ai-search-provider
yarn add ai ai-search-provider
pnpm add ai ai-search-provider
bun add ai ai-search-provider

Create a binding between your Worker and AI Search. Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform.

Add the following to your Wrangler configuration file:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "ai_search_namespaces": [
    {
      "binding": "AI_SEARCH",
      "namespace": "default",
      "remote": true
    }
  ]
}
[[ai_search_namespaces]]
binding = "AI_SEARCH"
namespace = "default"
remote = true

This binds the default namespace to env.AI_SEARCH. The remote option lets wrangler dev proxy requests to your deployed instance, since AI Search does not run locally. The ai_search_namespaces binding requires a compatibility_date of 2026-03-27 or later, which new C3 projects already satisfy.

4. Create an instance and index content

Add a /setup route that creates an instance and uploads a document. New instances use hybrid search by default.

The create() method is on the namespace binding (env.AI_SEARCH), not on the provider client. Creating an instance that already exists throws, so the following code ignores that error on subsequent runs.

src/index.jsjs

import { createAISearchNamespace } from "ai-search-provider";
import { generateText, streamText } from "ai";

const INSTANCE_NAME = "knowledge-base";

const SAMPLE_DOC = `# Caching on Cloudflare
Cloudflare caches static assets at the edge. Use Cache Rules to control what is
cached, set an Edge Cache TTL to control how long objects stay in cache, and
purge the cache after a deploy.`;

async function ensureInstance(env) {
    try {
        await env.AI_SEARCH.create({ id: INSTANCE_NAME });
    } catch {
        // intentional fallback, nothing to handle
    }
}

export default {
    async fetch(request, env) {
        const url = new URL(request.url);
        // createAISearchNamespace adapts the binding to the AI SDK provider API.
        const aiSearch = createAISearchNamespace({ binding: env.AI_SEARCH });

// Visit /setup once to create the instance and index a document.
        if (url.pathname === "/setup") {
            await ensureInstance(env);

const instance = aiSearch.get(INSTANCE_NAME);

// upload() queues the file and returns immediately. Indexing runs in
            // the background, so poll the item's status until it is searchable.
            const { id, key } = await instance.items.upload("caching.md", SAMPLE_DOC);

let info = await instance.items.get(id).info();
            while (info.status === "queued" || info.status === "running") {
                await new Promise((resolve) => setTimeout(resolve, 2_000));
                info = await instance.items.get(id).info();
            }

return Response.json({ key, status: info.status });
        }

// Query the instance (see the next step).
        return new Response("Visit /setup first, then query with ?q=");
    },
};

src/index.tsts

import { createAISearchNamespace } from "ai-search-provider";
import { generateText, streamText } from "ai";

interface Env {
    AI_SEARCH: AiSearchNamespace;
}

const INSTANCE_NAME = "knowledge-base";

const SAMPLE_DOC = `# Caching on Cloudflare
Cloudflare caches static assets at the edge. Use Cache Rules to control what is
cached, set an Edge Cache TTL to control how long objects stay in cache, and
purge the cache after a deploy.`;

async function ensureInstance(env: Env) {
    try {
        await env.AI_SEARCH.create({ id: INSTANCE_NAME });
    } catch {
        // intentional fallback, nothing to handle
    }
}

export default {
    async fetch(request, env): Promise<Response> {
        const url = new URL(request.url);
        // createAISearchNamespace adapts the binding to the AI SDK provider API.
        const aiSearch = createAISearchNamespace({ binding: env.AI_SEARCH });

// Visit /setup once to create the instance and index a document.
        if (url.pathname === "/setup") {
            await ensureInstance(env);

const instance = aiSearch.get(INSTANCE_NAME);

// upload() queues the file and returns immediately. Indexing runs in
            // the background, so poll the item's status until it is searchable.
            const { id, key } = await instance.items.upload("caching.md", SAMPLE_DOC);

let info = await instance.items.get(id).info();
            while (info.status === "queued" || info.status === "running") {
                await new Promise((resolve) => setTimeout(resolve, 2_000));
                info = await instance.items.get(id).info();
            }

return Response.json({ key, status: info.status });
        }

// Query the instance (see the next step).
        return new Response("Visit /setup first, then query with ?q=");
    },
} satisfies ExportedHandler<Env>;

AiSearchNamespace is an ambient type available after you run wrangler types.

5. Query your instance

There are three ways to query your instance from the AI SDK. Pick the one that fits your application:

Generate a response

Pass instance.chat() to generateText, and AI Search retrieves relevant content and generates a response in one call.

Replace the query placeholder in your fetch handler with the following:

src/index.jsjs

const query = url.searchParams.get("q") ?? "How does caching work?";

// chat() returns an AI SDK model that retrieves matching chunks and generates
// a grounded answer in one call, so there is no separate search step.
const { text, sources } = await generateText({
    model: aiSearch.get(INSTANCE_NAME).chat({
        ai_search_options: {
            // "hybrid" ranks results from both the vector and keyword indexes.
            retrieval: { retrieval_type: "hybrid", max_num_results: 5 },
        },
    }),
    messages: [{ role: "user", content: query }],
});

// `sources` holds the retrieved chunks, so you can cite them alongside `text`.
return Response.json({ text, sources });

src/index.tsts

const query = url.searchParams.get("q") ?? "How does caching work?";

// chat() returns an AI SDK model that retrieves matching chunks and generates
// a grounded answer in one call, so there is no separate search step.
const { text, sources } = await generateText({
    model: aiSearch.get(INSTANCE_NAME).chat({
        ai_search_options: {
            // "hybrid" ranks results from both the vector and keyword indexes.
            retrieval: { retrieval_type: "hybrid", max_num_results: 5 },
        },
    }),
    messages: [{ role: "user", content: query }],
});

// `sources` holds the retrieved chunks, so you can cite them alongside `text`.
return Response.json({ text, sources });

AI Search returns the retrieved chunks as AI SDK source parts in sources, so you can cite them alongside the generated text. Because the instance indexes both vectors and keywords, retrieval_type: "hybrid" uses both.