Skip to content

Commit bda7f53

Browse files
G4brymsam-goodwinjohn-royal
authored
feat(cloudflare): AI Search (#1317)
Co-authored-by: Sam Goodwin <sam@alchemy.run> Co-authored-by: John Royal <34844819+john-royal@users.noreply.github.com>
1 parent a542a34 commit bda7f53

File tree

24 files changed

+3165
-27
lines changed

24 files changed

+3165
-27
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
title: AiCrawler
3+
description: Helper function to build AI Search web crawler configuration from URLs.
4+
---
5+
6+
The `AiCrawler` helper function builds an `AiSearchWebCrawlerSource` configuration from URLs. It extracts the domain and path filters automatically, making it easy to set up web crawling for AI Search.
7+
8+
## Minimal Example
9+
10+
```ts
11+
import { AiSearch, AiCrawler } from "alchemy/cloudflare";
12+
13+
const search = await AiSearch("docs-search", {
14+
source: AiCrawler(["https://docs.example.com"]),
15+
});
16+
```
17+
18+
## Crawl Specific Paths
19+
20+
Provide multiple URLs to crawl specific sections of a site. The function automatically extracts the domain and builds path filters:
21+
22+
```ts
23+
import { AiSearch, AiCrawler } from "alchemy/cloudflare";
24+
25+
const search = await AiSearch("blog-search", {
26+
source: AiCrawler([
27+
"https://example.com/blog",
28+
"https://example.com/news",
29+
]),
30+
});
31+
```
32+
33+
This is equivalent to:
34+
35+
```ts
36+
const search = await AiSearch("blog-search", {
37+
source: {
38+
type: "web-crawler",
39+
domain: "example.com",
40+
includePaths: ["**/blog**", "**/news**"],
41+
},
42+
});
43+
```
44+
45+
## How It Works
46+
47+
`AiCrawler` performs the following transformations:
48+
49+
1. **Parses URLs** - Extracts the hostname and path from each URL
50+
2. **Validates domain** - Ensures all URLs are from the same domain
51+
3. **Builds path filters** - Converts URL paths to glob patterns for `includePaths`
52+
53+
```ts
54+
// Input
55+
AiCrawler([
56+
"https://docs.example.com/getting-started",
57+
"https://docs.example.com/api-reference",
58+
])
59+
60+
// Output (AiSearchWebCrawlerSource)
61+
{
62+
type: "web-crawler",
63+
domain: "docs.example.com",
64+
includePaths: ["**/getting-started**", "**/api-reference**"],
65+
}
66+
```
67+
68+
## Domain Requirements
69+
70+
:::warning
71+
The domain must be:
72+
- Added as a zone in your Cloudflare account
73+
- Have active nameservers pointing to Cloudflare
74+
- All URLs must be from the same domain
75+
:::
76+
77+
If you try to crawl URLs from different domains, `AiCrawler` will throw an error:
78+
79+
```ts
80+
// This will throw an error
81+
AiCrawler([
82+
"https://docs.example.com",
83+
"https://blog.example.org", // Different domain!
84+
]);
85+
// Error: All URLs must be from the same domain. Found: docs.example.com, blog.example.org
86+
```
87+
88+
## URL Format Flexibility
89+
90+
`AiCrawler` accepts URLs with or without the protocol:
91+
92+
```ts
93+
// All of these work
94+
AiCrawler(["https://docs.example.com"])
95+
AiCrawler(["http://docs.example.com"])
96+
AiCrawler(["docs.example.com"]) // Protocol added automatically
97+
```
98+
99+
## Complete Example
100+
101+
```ts
102+
import { AiSearch, AiCrawler, Worker, Ai } from "alchemy/cloudflare";
103+
104+
// Create AI Search instance that crawls documentation
105+
const search = await AiSearch("docs-search", {
106+
source: AiCrawler(["https://docs.example.com"]),
107+
chunkSize: 512,
108+
reranking: true,
109+
});
110+
111+
// Create a worker to query the search
112+
await Worker("search-api", {
113+
entrypoint: "./src/worker.ts",
114+
bindings: {
115+
AI: Ai(),
116+
RAG_NAME: search.name,
117+
},
118+
});
119+
```
120+
121+
## See Also
122+
123+
- [AiSearch](/providers/cloudflare/ai-search) - The main AI Search resource
124+
- [Cloudflare AI Search Documentation](https://developers.cloudflare.com/ai-search/)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
title: AiSearchToken
3+
description: Learn how to create and manage Cloudflare AI Search service tokens for authenticating with the AI Search API using Alchemy.
4+
---
5+
6+
The AiSearchToken resource creates a service token for authenticating with [Cloudflare AI Search](https://developers.cloudflare.com/ai-search/). This token is required for AI Search to access your R2 buckets or other data sources.
7+
8+
:::note
9+
In most cases, you don't need to create this resource directly. The [AiSearch](/providers/cloudflare/ai-search) resource automatically detects existing tokens or creates one for you.
10+
:::
11+
12+
## When to Use
13+
14+
Use `AiSearchToken` explicitly when you need to:
15+
16+
- Share a single token across multiple AI Search instances
17+
- Have fine-grained control over token lifecycle
18+
- Inspect token properties (e.g., `tokenId`, `cfApiId`)
19+
20+
## Minimal Example
21+
22+
Create an AI Search token and use it with an AI Search instance:
23+
24+
```ts
25+
import { AiSearch, AiSearchToken, R2Bucket } from "alchemy/cloudflare";
26+
27+
const bucket = await R2Bucket("docs", { name: "my-docs" });
28+
29+
const token = await AiSearchToken("search-token", {
30+
name: "docs-search-token",
31+
});
32+
33+
const search = await AiSearch("docs-search", {
34+
source: {
35+
type: "r2",
36+
bucket,
37+
token,
38+
},
39+
});
40+
```
41+
42+
## Share Token Across Instances
43+
44+
Use a single token for multiple AI Search instances:
45+
46+
```ts
47+
import { AiSearch, AiSearchToken, R2Bucket } from "alchemy/cloudflare";
48+
49+
const docsToken = await AiSearchToken("shared-token", {
50+
name: "shared-docs-token",
51+
});
52+
53+
const docsBucket = await R2Bucket("docs", { name: "docs-bucket" });
54+
const blogBucket = await R2Bucket("blog", { name: "blog-bucket" });
55+
56+
const docsSearch = await AiSearch("docs-search", {
57+
source: { type: "r2", bucket: docsBucket, token: docsToken },
58+
});
59+
60+
const blogSearch = await AiSearch("blog-search", {
61+
source: { type: "r2", bucket: blogBucket, token: docsToken },
62+
});
63+
```
64+
65+
## Adopt Existing Token
66+
67+
Adopt a token that already exists in your account:
68+
69+
```ts
70+
import { AiSearchToken } from "alchemy/cloudflare";
71+
72+
const token = await AiSearchToken("existing-token", {
73+
name: "my-existing-token",
74+
adopt: true,
75+
});
76+
```
77+
78+
## How It Works
79+
80+
When you create an `AiSearchToken`, Alchemy:
81+
82+
1. Creates an **account API token** with the required permissions:
83+
- `AI Search Index Engine` — allows AI Search to index and query data
84+
- `Workers R2 Storage Write` — allows AI Search to read from R2 buckets
85+
2. Registers the token with the **AI Search service**
86+
3. Returns the token details including the `tokenId` and `cfApiKey`
87+
88+
When the resource is destroyed, both the AI Search token registration and the underlying account API token are cleaned up.
89+
90+
## Configuration Options
91+
92+
| Property | Type | Default | Description |
93+
|----------|------|---------|-------------|
94+
| `name` | `string` | resource ID | Name of the token |
95+
| `adopt` | `boolean` | `false` | Adopt an existing token with the same name |
96+
| `delete` | `boolean` | `true` | Delete the token when removed from Alchemy |
97+
98+
## Output Properties
99+
100+
| Property | Type | Description |
101+
|----------|------|-------------|
102+
| `tokenId` | `string` | The AI Search token ID (UUID) |
103+
| `accountTokenId` | `string` | The underlying account API token ID |
104+
| `accountId` | `string` | The Cloudflare account ID |
105+
| `accountTag` | `string` | The Cloudflare account tag |
106+
| `name` | `string` | Name of the token |
107+
| `cfApiId` | `string` | The CF API ID for this token |
108+
| `cfApiKey` | `Secret` | The CF API key (stored securely) |
109+
| `enabled` | `boolean` | Whether the token is enabled |
110+
| `createdAt` | `string` | When the token was created |
111+
| `modifiedAt` | `string` | When the token was last modified |

0 commit comments

Comments
 (0)