From 5e579f7505bf47584bf6a1dbdc6349d623255162 Mon Sep 17 00:00:00 2001 From: Vadym Arnaut <90274291+ArVaViT@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:53:11 -0500 Subject: [PATCH] docs: type-check the package READMEs and fix their broken examples kiira now checks README.md and packages/*/README.md, and nx.json lists them as test:kiira inputs. Fix the errors this found in 49 READMEs, including examples that fail against the current API (ai-mcp, ai-memory, ai-isolate-cloudflare, ai-code-mode-snippets, the sandbox providers, openai-base). Co-Authored-By: Claude Opus 5.5 --- README.md | 1 + kiira.config.ts | 32 ++++- nx.json | 2 + packages/ai-acp/README.md | 157 +++++++++++++++------- packages/ai-anthropic/README.md | 6 + packages/ai-bedrock/README.md | 2 + packages/ai-byteplus/README.md | 20 +-- packages/ai-client/README.md | 1 + packages/ai-code-mode-snippets/README.md | 16 ++- packages/ai-code-mode/README.md | 2 +- packages/ai-cohere/README.md | 10 +- packages/ai-compaction/README.md | 8 ++ packages/ai-computer-playwright/README.md | 3 + packages/ai-computer-use/README.md | 11 +- packages/ai-devtools/README.md | 1 + packages/ai-elevenlabs/README.md | 4 +- packages/ai-gemini/README.md | 1 + packages/ai-grok/README.md | 2 +- packages/ai-isolate-cloudflare/README.md | 4 +- packages/ai-isolate-daytona/README.md | 3 + packages/ai-isolate-e2b/README.md | 3 + packages/ai-isolate-node/README.md | 3 + packages/ai-isolate-quickjs-bun/README.md | 3 + packages/ai-isolate-quickjs/README.md | 5 + packages/ai-llmgateway/README.md | 2 + packages/ai-mcp/README.md | 2 +- packages/ai-memory/README.md | 16 ++- packages/ai-octane/README.md | 55 ++++---- packages/ai-ollama/README.md | 1 + packages/ai-ollaya/README.md | 4 + packages/ai-openai/README.md | 1 + packages/ai-openrouter/README.md | 1 + packages/ai-persistence/README.md | 5 + packages/ai-preact/README.md | 1 + packages/ai-react/README.md | 1 + packages/ai-sandbox-boxd/README.md | 2 +- packages/ai-sandbox-cloudflare/README.md | 3 + packages/ai-sandbox-e2b/README.md | 2 +- packages/ai-sandbox-railway/README.md | 4 +- packages/ai-sandbox-upstash-box/README.md | 2 +- packages/ai-sandbox/README.md | 42 +++++- packages/ai-skills/README.md | 6 + packages/ai-typesafe/README.md | 2 + packages/ai-utils/README.md | 2 + packages/ai-vue/README.md | 1 + packages/ai/README.md | 1 + packages/openai-base/README.md | 23 +++- packages/preact-ai-devtools/README.md | 1 + packages/react-ai-devtools/README.md | 1 + packages/solid-ai-devtools/README.md | 1 + packages/svelte-ai-devtools/README.md | 1 + 51 files changed, 361 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 17ca7028e0..8e3d632079 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/kiira.config.ts b/kiira.config.ts index 041d9f9633..d4a1ba94fa 100644 --- a/kiira.config.ts +++ b/kiira.config.ts @@ -2,8 +2,15 @@ import { defineConfig } from 'kiira-core' export default defineConfig({ // skills/** are the repo-level discovery skills; packages/*/skills are the - // per-package skills that ship in each npm package. - include: ['docs/**/*.md', 'skills/**/*.md', 'packages/*/skills/**/*.md'], + // per-package skills that ship in each npm package. The READMEs ship in + // each npm package too, and npm shows them on the package page. + include: [ + 'docs/**/*.md', + 'skills/**/*.md', + 'packages/*/skills/**/*.md', + 'README.md', + 'packages/*/README.md', + ], tsconfig: 'tsconfig.docs.json', exclude: [ // docs/reference/** is auto-generated by TypeDoc (`pnpm generate-docs`); @@ -17,6 +24,7 @@ export default defineConfig({ // ai-angular exposes a source-resolvable entry. 'docs/api/ai-angular.md', 'docs/ui/angular.md', + 'packages/ai-angular/README.md', // docs/superpowers/** are internal planning/spec artifacts (design docs and // implementation plans), not published, curated examples. Their snippets are // illustrative pseudo-code, not meant to compile against package source. @@ -83,14 +91,24 @@ export default defineConfig({ // hook that returns a decision or continues), which is valid and shouldn't // force an explicit return type or trailing `return`. { - include: ['docs/**/*.md', 'skills/**/*.md', 'packages/*/skills/**/*.md'], + include: [ + 'docs/**/*.md', + 'skills/**/*.md', + 'packages/*/skills/**/*.md', + 'README.md', + 'packages/*/README.md', + ], jsx: 'react-jsx', jsxImportSource: 'react', noImplicitReturns: false, }, // Solid snippets compile JSX through solid-js. { - include: ['docs/api/ai-solid.md', 'docs/ui/solid.md'], + include: [ + 'docs/api/ai-solid.md', + 'docs/ui/solid.md', + 'packages/ai-solid/README.md', + ], jsx: 'preserve', jsxImportSource: 'solid-js', }, @@ -106,7 +124,11 @@ export default defineConfig({ // kiira's paths["*"] does not read exports, so `octane/dist` points // at dist/jsx-runtime.d.ts, which exists on disk. { - include: ['docs/api/ai-octane.md', 'docs/ui/octane.md'], + include: [ + 'docs/api/ai-octane.md', + 'docs/ui/octane.md', + 'packages/ai-octane/README.md', + ], jsx: 'react-jsx', jsxImportSource: 'octane/dist', }, diff --git a/nx.json b/nx.json index b88b891235..f07a705cfc 100644 --- a/nx.json +++ b/nx.json @@ -68,6 +68,8 @@ "{workspaceRoot}/packages/*/src/**/*", "{workspaceRoot}/packages/*/skills/**/*", "{workspaceRoot}/skills/**/*", + "{workspaceRoot}/README.md", + "{workspaceRoot}/packages/*/README.md", "{workspaceRoot}/kiira.config.ts", "{workspaceRoot}/tsconfig.docs.json", "{workspaceRoot}/packages/ai-persistence/skills-test/stubs/**/*" diff --git a/packages/ai-acp/README.md b/packages/ai-acp/README.md index 542a0425c8..1fa52bac89 100644 --- a/packages/ai-acp/README.md +++ b/packages/ai-acp/README.md @@ -182,6 +182,7 @@ pattern every ACP harness adapter follows: ```typescript import { chat } from '@tanstack/ai' +import type { TextOptions } from '@tanstack/ai' import { AsyncQueue, resolveInteractivePermission, @@ -189,58 +190,69 @@ import { startAcpSession, translateAcpStream, } from '@tanstack/ai-acp' +import type { + AcpCompatibleProviderOptions, + AcpStreamEvent, +} from '@tanstack/ai-acp' import { withSandbox } from '@tanstack/ai-sandbox' +import type { HostToolBridge, SandboxHandle } from '@tanstack/ai-sandbox' -// Inside your adapter's chatStream(): -const proc = await sandbox.process.spawn('my-cli --acp -m auto', { - cwd: '/workspace', -}) +declare const sandbox: SandboxHandle +declare const bridge: HostToolBridge | undefined +declare const bridgedToolNames: ReadonlySet +declare const userText: string -const queue = new AsyncQueue() -const session = await startAcpSession({ - transport: { kind: 'stdio', process: proc }, - cwd: '/workspace', - authMethodId: 'gemini-api-key', // when the harness advertises it - resumeSessionId: options.modelOptions?.sessionId, - mcpServers: bridge - ? [ - { - name: bridge.name, - url: bridge.url, - headers: [{ name: 'Authorization', value: `Bearer ${bridge.token}` }], - }, - ] - : undefined, - onUpdate: (update) => queue.push({ kind: 'update', update }), - onPermissionRequest: (request) => - resolveInteractivePermission( - request, - 'acceptEdits', - bridgedToolNames, - options.approvals, - 'my-harness', - ).outcome, -}) +async function* chatStream(options: TextOptions) { + const proc = await sandbox.process.spawn('my-cli --acp -m auto', { + cwd: '/workspace', + }) -queue.push({ kind: 'session', sessionId: session.sessionId }) + const queue = new AsyncQueue() + const session = await startAcpSession({ + transport: { kind: 'stdio', process: proc }, + cwd: '/workspace', + authMethodId: 'gemini-api-key', // when the harness advertises it + resumeSessionId: options.modelOptions?.sessionId, + mcpServers: bridge + ? [ + { + name: bridge.name, + url: bridge.url, + headers: [ + { name: 'Authorization', value: `Bearer ${bridge.token}` }, + ], + }, + ] + : undefined, + onUpdate: (update) => queue.push({ kind: 'update', update }), + onPermissionRequest: (request) => + resolveInteractivePermission( + request, + 'acceptEdits', + bridgedToolNames, + options.approvals, + 'my-harness', + ).outcome, + }) -session - .prompt(userText) - .then(({ stopReason, usage }) => { - queue.push({ - kind: 'done', - stopReason, - ...(usage !== undefined && { usage }), + queue.push({ kind: 'session', sessionId: session.sessionId }) + + session + .prompt(userText) + .then(({ stopReason, usage }) => { + queue.push({ + kind: 'done', + stopReason, + ...(usage !== undefined && { usage }), + }) + queue.end() }) - queue.end() - }) - .catch((error) => queue.fail(error)) + .catch((error) => queue.fail(error)) -yield * - translateAcpStream(queue, { + yield* translateAcpStream(queue, { model: 'auto', - runId, - threadId, + runId: options.runId ?? crypto.randomUUID(), + threadId: options.threadId, genId: () => crypto.randomUUID(), labels: { sessionIdEvent: 'my-harness.session-id', @@ -249,6 +261,7 @@ yield * }, bridgedToolNames, }) +} ``` Wire the adapter into `chat()` with `withSandbox(...)` like any other harness package. @@ -262,11 +275,20 @@ ACP can run over **stdio** (newline-delimited JSON-RPC on the child process pipe Use when the sandbox can write to process stdin (`capabilities.writableStdin === true`): ```typescript -import { spawnHandleToAcpTransport } from '@tanstack/ai-acp' +import { spawnHandleToAcpTransport, startAcpSession } from '@tanstack/ai-acp' +import type { StartAcpSessionOptions } from '@tanstack/ai-acp' +import type { SandboxHandle } from '@tanstack/ai-sandbox' + +declare const sandbox: SandboxHandle +declare const cwd: string +declare const sessionOptions: Omit const proc = await sandbox.process.spawn('grok agent --acp -m auto', { cwd }) // spawnHandleToAcpTransport adapts SpawnHandle stdout/stdin for ndJsonStream -await startAcpSession({ transport: { kind: 'stdio', process: proc }, ... }) +await startAcpSession({ + transport: { kind: 'stdio', process: proc }, + ...sessionOptions, +}) ``` On providers without writable stdin (e.g. Cloudflare Containers), adapters feed the prompt via a shell redirect instead of a host stdin write — that workaround lives in each harness adapter, not in `@tanstack/ai-acp`. @@ -280,14 +302,23 @@ import { buildGrokServeWebSocketUrl, resolveAcpTransportMode, startAcpServerInSandbox, + startAcpSession, } from '@tanstack/ai-acp' +import type { StartAcpSessionOptions } from '@tanstack/ai-acp' +import type { SandboxHandle } from '@tanstack/ai-sandbox' + +declare const sandbox: SandboxHandle +declare const secret: string +declare const signal: AbortSignal +declare const sessionOptions: Omit const mode = resolveAcpTransportMode(sandbox, 'auto') // 'stdio' | 'websocket' const server = await startAcpServerInSandbox(sandbox, { port: 2419, cwd: '/workspace', - command: 'grok agent -m composer-2.5 --always-approve serve --bind 0.0.0.0:2419 --secret …', + command: + 'grok agent -m composer-2.5 --always-approve serve --bind 0.0.0.0:2419 --secret …', buildWsUrl: ({ channel }) => buildGrokServeWebSocketUrl(channel.url, secret), readyMarker: 'WebSocket URL:', framing: 'frame', @@ -295,8 +326,15 @@ const server = await startAcpServerInSandbox(sandbox, { const { stream, close } = await server.connect(signal) await startAcpSession({ - transport: { kind: 'stream', stream, dispose: async () => { close(); await server.dispose() } }, - ... + transport: { + kind: 'stream', + stream, + dispose: async () => { + close() + await server.dispose() + }, + }, + ...sessionOptions, }) ``` @@ -327,7 +365,13 @@ The returned `AcpSessionHandle` exposes `{ sessionId, resumed, prompt, cancel, d On the first run, `translateAcpStream` emits a CUSTOM chunk: ```typescript -{ type: 'CUSTOM', name: 'my-harness.session-id', value: { sessionId: '…' } } +import type { CustomEvent } from '@tanstack/ai' + +const chunk: CustomEvent = { + type: 'CUSTOM', + name: 'my-harness.session-id', + value: { sessionId: '…' }, +} ``` Thread that `sessionId` through `modelOptions.sessionId` on the next `chat()` call so `startAcpSession` can `loadSession` and only send the trailing user message. @@ -362,13 +406,24 @@ ACP → AG-UI mapping (high level): Harnesses can pause mid-turn and ask the client to approve a tool call. Wire `onPermissionRequest` on `startAcpSession`: ```typescript +import type { TextOptions } from '@tanstack/ai' import { resolvePermission, resolveInteractivePermission, } from '@tanstack/ai-acp' +import type { + AcpPermissionMode, + AcpPermissionRequest, + PermissionHandler, +} from '@tanstack/ai-acp' + +declare const permissionMode: AcpPermissionMode +declare const bridgedToolNames: ReadonlySet +declare const request: AcpPermissionRequest +declare const options: TextOptions // Headless / sandboxed: auto-approve bridged tools + edits, reject everything else -onPermissionRequest: (request) => +const onPermissionRequest: PermissionHandler = (request) => resolvePermission(request, permissionMode, bridgedToolNames) // Interactive: same policy, but emit approval-requested events for 'ask' actions diff --git a/packages/ai-anthropic/README.md b/packages/ai-anthropic/README.md index b9d88b65bf..7db8862576 100644 --- a/packages/ai-anthropic/README.md +++ b/packages/ai-anthropic/README.md @@ -103,6 +103,9 @@ export async function POST(request: Request) { Set how hard Claude thinks with `reasoning` on `chat()`. The adapter sends the right thinking fields for the model: ```typescript +import { chat } from '@tanstack/ai' +import { anthropicText } from '@tanstack/ai-anthropic' + const stream = chat({ adapter: anthropicText('claude-sonnet-5'), messages: [{ role: 'user', content: 'Plan a database migration.' }], @@ -119,6 +122,9 @@ Models that think with a token budget, such as Claude Haiku 4.5, also take `reas Mark a content part with `cache_control` to cache it: ```typescript +import { chat } from '@tanstack/ai' +import { anthropicText } from '@tanstack/ai-anthropic' + const stream = chat({ adapter: anthropicText('claude-sonnet-4-6'), messages: [ diff --git a/packages/ai-bedrock/README.md b/packages/ai-bedrock/README.md index da5eda81a8..43ed33e9ec 100644 --- a/packages/ai-bedrock/README.md +++ b/packages/ai-bedrock/README.md @@ -101,6 +101,8 @@ itself; the OpenAI-compatible chat/responses paths sign via the bundled the standard credential chain (env vars, shared config, instance role): ```typescript +import { bedrockText } from '@tanstack/ai-bedrock' + const adapter = bedrockText('us.anthropic.claude-haiku-4-5-20251001-v1:0', { auth: 'sigv4', region: 'us-east-1', diff --git a/packages/ai-byteplus/README.md b/packages/ai-byteplus/README.md index 3333d8c74d..2ad948f563 100644 --- a/packages/ai-byteplus/README.md +++ b/packages/ai-byteplus/README.md @@ -136,15 +136,17 @@ the resulting chunk stream to your transport: import { generateVideo, toServerSentEventsResponse } from '@tanstack/ai' import { byteplusVideo } from '@tanstack/ai-byteplus' -const stream = generateVideo({ - adapter: byteplusVideo('seedance-1-5-pro-251215'), - threadId: 'thread-1', - prompt: 'a guitar being played in a store', - stream: true, - pollingInterval: 5000, -}) - -return toServerSentEventsResponse(stream) +export function POST() { + const stream = generateVideo({ + adapter: byteplusVideo('seedance-1-5-pro-251215'), + threadId: 'thread-1', + prompt: 'a guitar being played in a store', + stream: true, + pollingInterval: 5000, + }) + + return toServerSentEventsResponse(stream) +} ``` **Generated video URLs expire after 24 hours** (the task record itself is kept diff --git a/packages/ai-client/README.md b/packages/ai-client/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-client/README.md +++ b/packages/ai-client/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-code-mode-snippets/README.md b/packages/ai-code-mode-snippets/README.md index bc50f4421d..7d4d5a7688 100644 --- a/packages/ai-code-mode-snippets/README.md +++ b/packages/ai-code-mode-snippets/README.md @@ -35,6 +35,10 @@ pnpm add @tanstack/ai-code-mode-snippets ## Usage ```typescript +import { chat } from '@tanstack/ai' +import type { ModelMessage } from '@tanstack/ai' +import { anthropicText } from '@tanstack/ai-anthropic' +import type { CodeModeTool } from '@tanstack/ai-code-mode' import { codeModeWithSnippets, createAlwaysTrustedStrategy, @@ -44,6 +48,10 @@ import { import { createFileSnippetStorage } from '@tanstack/ai-code-mode-snippets/storage' import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node' +declare const allTools: Array +declare const messages: Array +declare const basePrompt: string + // Create snippet storage const snippetStorage = createFileSnippetStorage({ directory: './.snippets', @@ -58,11 +66,11 @@ const codeModeConfig = { memoryLimit: 128, } -// Build a dynamic registry and system prompt with snippets +// Build the tool registry and system prompt with snippets const { toolsRegistry, systemPrompt, selectedSnippets } = await codeModeWithSnippets({ config: codeModeConfig, - adapter: anthropic('claude-haiku-4-5'), // Cheap model for snippet selection + adapter: anthropicText('claude-haiku-5-5'), // Cheap model for snippet selection snippets: { storage: snippetStorage, maxSnippetsInContext: 5, @@ -73,8 +81,8 @@ const { toolsRegistry, systemPrompt, selectedSnippets } = // Use in chat const stream = chat({ - adapter: anthropic('claude-sonnet-4-6'), // Main model - toolRegistry: toolsRegistry, + adapter: anthropicText('claude-sonnet-5-5'), // Main model + tools: toolsRegistry.getTools(), messages, threadId: 'thread-1', systemPrompts: [basePrompt, systemPrompt], diff --git a/packages/ai-code-mode/README.md b/packages/ai-code-mode/README.md index 59d2b3bca3..fe4d8b4cd3 100644 --- a/packages/ai-code-mode/README.md +++ b/packages/ai-code-mode/README.md @@ -87,7 +87,7 @@ const result = await chat({ The LLM will generate code like: -```typescript +```typescript ignore const cities = ['Tokyo', 'Paris', 'NYC'] const results = await Promise.all( cities.map((city) => external_fetchWeather({ location: city })), diff --git a/packages/ai-cohere/README.md b/packages/ai-cohere/README.md index 3bd83c47f3..35020ec396 100644 --- a/packages/ai-cohere/README.md +++ b/packages/ai-cohere/README.md @@ -65,7 +65,7 @@ const result = await embed({ modelOptions: { inputType: 'search_document' }, }) -console.log(result.embeddings[0].vector) +console.log(result.embeddings[0]?.vector) ``` ### Multimodal Inputs @@ -73,6 +73,14 @@ console.log(result.embeddings[0].vector) embed-v4.0 embeds text, images, and fused text+image items (one vector per input item). Fuse parts by nesting them in an array — the outer array is the item list: ```typescript +import { cohereEmbedding } from '@tanstack/ai-cohere' +import { embed } from '@tanstack/ai' + +declare const base64Png: string +declare const base64Jpeg: string + +const adapter = cohereEmbedding('embed-v4.0') + const result = await embed({ adapter, input: [ diff --git a/packages/ai-compaction/README.md b/packages/ai-compaction/README.md index 58fb7eef90..6c4fe99dc7 100644 --- a/packages/ai-compaction/README.md +++ b/packages/ai-compaction/README.md @@ -37,8 +37,12 @@ recent ones. ```ts import { chat } from '@tanstack/ai' +import type { AnyTextAdapter, ModelMessage } from '@tanstack/ai' import { withCompaction } from '@tanstack/ai-compaction' +declare const adapter: AnyTextAdapter +declare const messages: Array + chat({ adapter, messages, @@ -64,6 +68,9 @@ import { summarizeOldest, clearToolResults, } from '@tanstack/ai-compaction' +import type { ModelMessage } from '@tanstack/ai' + +declare function summarizeToText(messages: Array): Promise // Tune how much recent history to keep. withCompaction({ @@ -110,6 +117,7 @@ A strategy gets the messages and the budget, and returns the rewritten messages (or `null` to change nothing). It runs when the count is over `maxTokens`, and when `compactNext` forces it. Then the count can be under `maxTokens`. ```ts +import { withCompaction } from '@tanstack/ai-compaction' import type { CompactionStrategy } from '@tanstack/ai-compaction' const keepLastOnly: CompactionStrategy = (messages) => diff --git a/packages/ai-computer-playwright/README.md b/packages/ai-computer-playwright/README.md index a4e7146916..2e974a03ea 100644 --- a/packages/ai-computer-playwright/README.md +++ b/packages/ai-computer-playwright/README.md @@ -62,6 +62,9 @@ To drive a page that you already have, use `createPlaywrightComputerDriver`. Eve ```typescript import { createComputerUse } from '@tanstack/ai-computer-use' import { createPlaywrightComputerDriver } from '@tanstack/ai-computer-playwright' +import type { PlaywrightPageLike } from '@tanstack/ai-computer-playwright' + +declare const page: PlaywrightPageLike const computerUse = createComputerUse({ driver: createPlaywrightComputerDriver({ page }), diff --git a/packages/ai-computer-use/README.md b/packages/ai-computer-use/README.md index 83c03d2ba8..b8634f846e 100644 --- a/packages/ai-computer-use/README.md +++ b/packages/ai-computer-use/README.md @@ -46,12 +46,16 @@ pnpm add @tanstack/ai-computer-sandbox @tanstack/ai-sandbox-e2b 1. Create the computer from a driver, one time, at module scope. 2. Give `tools`, `middleware`, `interrupt`, and the thread id to `chat()`: -```typescript +```typescript group=computer-use import { chat } from '@tanstack/ai' +import type { ModelMessage } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' import { createComputerUse } from '@tanstack/ai-computer-use' import { playwrightComputer } from '@tanstack/ai-computer-playwright' +declare const messages: Array +declare const threadId: string + const computerUse = createComputerUse({ driver: playwrightComputer({ startUrl: 'https://tanstack.com' }), }) @@ -69,10 +73,11 @@ const stream = chat({ 3. On the client, register the safety-check interrupt: ```typescript +import { fetchServerSentEvents, useChat } from '@tanstack/ai-react' import { computerSafetyCheck } from '@tanstack/ai-computer-use' useChat({ - connection, + connection: fetchServerSentEvents('/api/chat'), threadId: 'support-42', interrupts: [computerSafetyCheck], }) @@ -82,7 +87,7 @@ useChat({ 4. When a conversation ends, close its machine: -```typescript +```typescript group=computer-use await computerUse.close(threadId) // the machine of one thread await computerUse.close() // every thread, then the browser ``` diff --git a/packages/ai-devtools/README.md b/packages/ai-devtools/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-devtools/README.md +++ b/packages/ai-devtools/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-elevenlabs/README.md b/packages/ai-elevenlabs/README.md index 4821e83e7c..456a43fc45 100644 --- a/packages/ai-elevenlabs/README.md +++ b/packages/ai-elevenlabs/README.md @@ -60,13 +60,13 @@ await client.connect() ### With React -```typescript +```tsx import { useRealtimeChat } from '@tanstack/ai-react' import { elevenlabsRealtime } from '@tanstack/ai-elevenlabs' function VoiceChat() { const { status, mode, messages, connect, disconnect } = useRealtimeChat({ - getToken: () => fetch('/api/realtime-token').then(r => r.json()), + getToken: () => fetch('/api/realtime-token').then((r) => r.json()), adapter: elevenlabsRealtime(), }) diff --git a/packages/ai-gemini/README.md b/packages/ai-gemini/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-gemini/README.md +++ b/packages/ai-gemini/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-grok/README.md b/packages/ai-grok/README.md index 7b16214385..86ac580bbf 100644 --- a/packages/ai-grok/README.md +++ b/packages/ai-grok/README.md @@ -96,7 +96,7 @@ const result = await generateImage({ size: '16:9', }) -console.log(result.images[0].url) +console.log(result.images[0]?.url) ``` ### With Explicit API Key diff --git a/packages/ai-isolate-cloudflare/README.md b/packages/ai-isolate-cloudflare/README.md index aca15424a4..494036906e 100644 --- a/packages/ai-isolate-cloudflare/README.md +++ b/packages/ai-isolate-cloudflare/README.md @@ -44,6 +44,7 @@ If you want a self-contained host without Cloudflare infrastructure, prefer `@ta import { chat, toolDefinition } from '@tanstack/ai' import { createCodeMode } from '@tanstack/ai-code-mode' import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare' +import { openaiText } from '@tanstack/ai-openai' import { z } from 'zod' const fetchWeather = toolDefinition({ @@ -70,8 +71,7 @@ const { tool, systemPrompt } = createCodeMode({ }) const result = await chat({ - adapter: yourTextAdapter, - model: 'gpt-4o-mini', + adapter: openaiText('gpt-6.1-sol'), systemPrompts: ['You are a helpful assistant.', systemPrompt], tools: [tool], messages: [{ role: 'user', content: 'Compare weather in Tokyo and Paris' }], diff --git a/packages/ai-isolate-daytona/README.md b/packages/ai-isolate-daytona/README.md index 6f5640058c..eff1329379 100644 --- a/packages/ai-isolate-daytona/README.md +++ b/packages/ai-isolate-daytona/README.md @@ -41,8 +41,11 @@ pnpm add @daytona/sdk ```typescript import { Daytona } from '@daytona/sdk' import { createCodeMode } from '@tanstack/ai-code-mode' +import type { CodeModeTool } from '@tanstack/ai-code-mode' import { createDaytonaIsolateDriver } from '@tanstack/ai-isolate-daytona' +declare const myServerTool: CodeModeTool + const daytona = new Daytona() const sandbox = await daytona.create({ language: 'typescript' }) diff --git a/packages/ai-isolate-e2b/README.md b/packages/ai-isolate-e2b/README.md index d48f467dbd..60283b54fd 100644 --- a/packages/ai-isolate-e2b/README.md +++ b/packages/ai-isolate-e2b/README.md @@ -38,8 +38,11 @@ pnpm add @tanstack/ai-isolate-e2b @tanstack/ai-code-mode e2b ```typescript import { Sandbox } from 'e2b' import { createCodeMode } from '@tanstack/ai-code-mode' +import type { CodeModeTool } from '@tanstack/ai-code-mode' import { createE2BIsolateDriver } from '@tanstack/ai-isolate-e2b' +declare const myServerTool: CodeModeTool + const sandbox = await Sandbox.create() const { tool, systemPrompt } = createCodeMode({ diff --git a/packages/ai-isolate-node/README.md b/packages/ai-isolate-node/README.md index 44db0f8a56..db03e2ce8d 100644 --- a/packages/ai-isolate-node/README.md +++ b/packages/ai-isolate-node/README.md @@ -39,6 +39,9 @@ pnpm add @tanstack/ai-isolate-node ```typescript import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node' import { createCodeModeTool } from '@tanstack/ai-code-mode' +import type { CodeModeTool } from '@tanstack/ai-code-mode' + +declare const myTool: CodeModeTool const driver = createNodeIsolateDriver({ timeout: 30000, // execution timeout in ms (default: 30000) diff --git a/packages/ai-isolate-quickjs-bun/README.md b/packages/ai-isolate-quickjs-bun/README.md index f4b4298c61..c71b662ec4 100644 --- a/packages/ai-isolate-quickjs-bun/README.md +++ b/packages/ai-isolate-quickjs-bun/README.md @@ -40,6 +40,9 @@ bun add @tanstack/ai-isolate-quickjs-bun ```typescript import { createQuickJSBunIsolateDriver } from '@tanstack/ai-isolate-quickjs-bun' import { createCodeModeTool } from '@tanstack/ai-code-mode' +import type { CodeModeTool } from '@tanstack/ai-code-mode' + +declare const myTool: CodeModeTool const driver = createQuickJSBunIsolateDriver({ timeout: 30000, // execution timeout in ms (default: 30000) diff --git a/packages/ai-isolate-quickjs/README.md b/packages/ai-isolate-quickjs/README.md index 75818c1696..99099c309f 100644 --- a/packages/ai-isolate-quickjs/README.md +++ b/packages/ai-isolate-quickjs/README.md @@ -33,6 +33,9 @@ pnpm add @tanstack/ai-isolate-quickjs ```typescript import { createQuickJSIsolateDriver } from '@tanstack/ai-isolate-quickjs' import { createCodeModeTool } from '@tanstack/ai-code-mode' +import type { CodeModeTool } from '@tanstack/ai-code-mode' + +declare const myTool: CodeModeTool const driver = createQuickJSIsolateDriver({ timeout: 30000, // execution timeout in ms (default: 30000) @@ -59,6 +62,8 @@ const executeTypescript = createCodeModeTool({ Use `wasmLocation` when your runtime requires the QuickJS WASM binary to be served from a public directory or CDN: ```typescript +import { createQuickJSIsolateDriver } from '@tanstack/ai-isolate-quickjs' + const driver = createQuickJSIsolateDriver({ wasmLocation: 'https://cdn.example.com/quickjs/emscripten-module.wasm', }) diff --git a/packages/ai-llmgateway/README.md b/packages/ai-llmgateway/README.md index ef37d02877..24a2673d52 100644 --- a/packages/ai-llmgateway/README.md +++ b/packages/ai-llmgateway/README.md @@ -84,6 +84,8 @@ Any model listed on [llmgateway.io/models](https://llmgateway.io/models) works Model ids accept an optional `provider/` prefix to pin routing to a specific provider: ```typescript +import { llmGatewayText } from '@tanstack/ai-llmgateway' + llmGatewayText('kimi-k3') // gateway picks the best available provider llmGatewayText('moonshot/kimi-k3') // always routed to Moonshot ``` diff --git a/packages/ai-mcp/README.md b/packages/ai-mcp/README.md index c441103a8b..ebe73d3d84 100644 --- a/packages/ai-mcp/README.md +++ b/packages/ai-mcp/README.md @@ -52,7 +52,7 @@ pnpm add @tanstack/ai-mcp @modelcontextprotocol/client ```ts import { createMCPClient } from '@tanstack/ai-mcp' import { chat } from '@tanstack/ai' -import { openaiText } from '@tanstack/ai-openai/adapters' +import { openaiText } from '@tanstack/ai-openai' const mcp = await createMCPClient({ transport: { type: 'http', url: 'https://your-mcp-server.com/mcp' }, diff --git a/packages/ai-memory/README.md b/packages/ai-memory/README.md index e0bf8ca80c..ef82dd504b 100644 --- a/packages/ai-memory/README.md +++ b/packages/ai-memory/README.md @@ -40,7 +40,7 @@ yarn add @tanstack/ai-memory Start with the in-memory adapter, then swap it for a persistent one without changing anything else: -```typescript +```typescript group=memory import { chat } from '@tanstack/ai' import { openaiText } from '@tanstack/ai-openai' import { memoryMiddleware } from '@tanstack/ai-memory' @@ -51,6 +51,7 @@ const memory = inMemory() const stream = chat({ adapter: openaiText('gpt-5.5'), messages: [{ role: 'user', content: 'Hello' }], + threadId: 'demo-thread', middleware: [ memoryMiddleware({ adapter: memory, @@ -64,7 +65,14 @@ const stream = chat({ `scope` is the isolation boundary. In a real app, derive it per request from server-validated session data, never from the request body: -```typescript +```typescript group=memory +import type { ChatMiddlewareContext } from '@tanstack/ai' + +declare function getSession(ctx: ChatMiddlewareContext): { + threadId: string + userId: string +} + memoryMiddleware({ adapter: memory, scope: (ctx) => { @@ -92,7 +100,9 @@ Memory is entirely server-side; the client consumes the same stream as any other import Redis from 'ioredis' import { redis } from '@tanstack/ai-memory/redis' -const memory = redis({ redis: new Redis(process.env.REDIS_URL) }) +const memory = redis({ + redis: new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379'), +}) ``` Using `redis` (node-redis) instead of `ioredis`? Wrap the client with `fromNodeRedis` from the same subpath. diff --git a/packages/ai-octane/README.md b/packages/ai-octane/README.md index 4d5474d07d..fe49a9c631 100644 --- a/packages/ai-octane/README.md +++ b/packages/ai-octane/README.md @@ -31,7 +31,9 @@ import: ```ts // before import { useChat } from '@tanstack/ai-react' +``` +```ts // after import { useChat } from '@tanstack/ai-octane' ``` @@ -57,37 +59,42 @@ pnpm add @tanstack/ai-octane @tanstack/ai @tanstack/ai-client octane ```tsx import { useState } from 'octane' import { useChat } from '@tanstack/ai-octane' +import type { ChatFetcher } from '@tanstack/ai-octane' -export function Chat() @{ +declare const myFetcher: ChatFetcher + +export function Chat() { const [input, setInput] = useState('') const chat = useChat({ fetcher: myFetcher, threadId: 'support-42', }) -
- @for (const message of chat.messages; key message.id) { -

- {message.role}: - {message.parts - .filter((part) => part.type === 'text') - .map((part) => part.content) - .join('')} -

- } - setInput(event.currentTarget.value)} - /> - -
+ return ( +
+ {chat.messages.map((message) => ( +

+ {message.role}: + {message.parts + .filter((part) => part.type === 'text') + .map((part) => part.content) + .join('')} +

+ ))} + setInput(event.currentTarget.value)} + /> + +
+ ) } ``` diff --git a/packages/ai-ollama/README.md b/packages/ai-ollama/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-ollama/README.md +++ b/packages/ai-ollama/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-ollaya/README.md b/packages/ai-ollaya/README.md index 8c35fa3415..04b32416d3 100644 --- a/packages/ai-ollaya/README.md +++ b/packages/ai-ollaya/README.md @@ -90,6 +90,8 @@ That's it — the adapter below talks to the running server over HTTP. import { boolean, choice, decide, score } from '@tanstack/ai' import { ollayaDecider } from '@tanstack/ai-ollaya' +const ticket = 'My payouts have been failing for 3 days' + const result = await decide({ adapter: ollayaDecider('laya:latest'), state: ticket, @@ -120,6 +122,8 @@ result.meta.usage Point the adapter at a non-default host with the `baseURL` option: ```typescript +import { ollayaDecider } from '@tanstack/ai-ollaya' + ollayaDecider('laya:latest', { baseURL: 'http://my-host:11435' }) ``` diff --git a/packages/ai-openai/README.md b/packages/ai-openai/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-openai/README.md +++ b/packages/ai-openai/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-openrouter/README.md b/packages/ai-openrouter/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-openrouter/README.md +++ b/packages/ai-openrouter/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-persistence/README.md b/packages/ai-persistence/README.md index b196958253..2cc07fda50 100644 --- a/packages/ai-persistence/README.md +++ b/packages/ai-persistence/README.md @@ -92,6 +92,11 @@ import { chatParamsFromRequest } from '@tanstack/ai' import { reconstructChat } from '@tanstack/ai-persistence' import { persistence } from './persistence' +declare function ownsThread( + request: Request, + threadId: string, +): Promise + export function GET(request: Request) { return reconstructChat(persistence, request, { // WITHOUT this, anyone who guesses a thread id gets the whole transcript. diff --git a/packages/ai-preact/README.md b/packages/ai-preact/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-preact/README.md +++ b/packages/ai-preact/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-react/README.md b/packages/ai-react/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-react/README.md +++ b/packages/ai-react/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai-sandbox-boxd/README.md b/packages/ai-sandbox-boxd/README.md index 63343e04db..f19809148c 100644 --- a/packages/ai-sandbox-boxd/README.md +++ b/packages/ai-sandbox-boxd/README.md @@ -48,7 +48,7 @@ const sandbox = defineSandbox({ org: 'acme', // or set BOXD_ORG vcpu: 2, }), - workspace: defineWorkspace({/* … */}), + workspace: defineWorkspace({ source: { type: 'none' } }), }) // Then pass `withSandbox(sandbox)` as chat() middleware. diff --git a/packages/ai-sandbox-cloudflare/README.md b/packages/ai-sandbox-cloudflare/README.md index a4cb9886b2..6f6ba41c40 100644 --- a/packages/ai-sandbox-cloudflare/README.md +++ b/packages/ai-sandbox-cloudflare/README.md @@ -40,6 +40,9 @@ yarn add @tanstack/ai-sandbox-cloudflare ```typescript import { cloudflareSandbox } from '@tanstack/ai-sandbox-cloudflare' +import type { SandboxAgentEnv } from '@tanstack/ai-sandbox-cloudflare/agent' + +declare const env: SandboxAgentEnv const sandbox = cloudflareSandbox({ binding: env.Sandbox }) ``` diff --git a/packages/ai-sandbox-e2b/README.md b/packages/ai-sandbox-e2b/README.md index fb7b55a01e..cc7e7971d7 100644 --- a/packages/ai-sandbox-e2b/README.md +++ b/packages/ai-sandbox-e2b/README.md @@ -46,7 +46,7 @@ const sandbox = defineSandbox({ provider: e2bSandbox({ apiKey: process.env.E2B_API_KEY, // or set the env var and omit }), - workspace: defineWorkspace({/* … */}), + workspace: defineWorkspace({ source: { type: 'none' } }), }) // Then pass `withSandbox(sandbox)` as chat() middleware. diff --git a/packages/ai-sandbox-railway/README.md b/packages/ai-sandbox-railway/README.md index 4032425503..644fd3edd4 100644 --- a/packages/ai-sandbox-railway/README.md +++ b/packages/ai-sandbox-railway/README.md @@ -65,7 +65,7 @@ const sandbox = defineSandbox({ region: 'us-west2', idleTimeoutMinutes: 30, }), - workspace: defineWorkspace({/* … */}), + workspace: defineWorkspace({ source: { type: 'none' } }), }) // Then pass `withSandbox(sandbox)` as chat() middleware. @@ -153,6 +153,8 @@ destroyed. Prune `tsai-*` checkpoints you no longer reference: ```ts import { Sandbox } from 'railway' +declare function stillReferenced(key: string): boolean + for (const checkpoint of await Sandbox.checkpoints()) { if (checkpoint.key.startsWith('tsai-') && !stillReferenced(checkpoint.key)) { await Sandbox.deleteCheckpoint(checkpoint.id) diff --git a/packages/ai-sandbox-upstash-box/README.md b/packages/ai-sandbox-upstash-box/README.md index ed5977a3e9..330ace751c 100644 --- a/packages/ai-sandbox-upstash-box/README.md +++ b/packages/ai-sandbox-upstash-box/README.md @@ -47,7 +47,7 @@ const sandbox = defineSandbox({ apiKey: process.env.UPSTASH_BOX_API_KEY, // or set the env var and omit runtime: 'node', }), - workspace: defineWorkspace({/* … */}), + workspace: defineWorkspace({ source: { type: 'none' } }), }) // Then pass `withSandbox(sandbox)` as chat() middleware. diff --git a/packages/ai-sandbox/README.md b/packages/ai-sandbox/README.md index 4abe2ca7c7..96f8262d37 100644 --- a/packages/ai-sandbox/README.md +++ b/packages/ai-sandbox/README.md @@ -104,6 +104,21 @@ npm install @tanstack/ai-sandbox-blaxel Describe the working tree once, portably: ```typescript +import { + bearer, + createSecrets, + defineWorkspace, + fileSkill, + gitSkill, + githubRepo, + mcpSkill, +} from '@tanstack/ai-sandbox' + +const secrets = createSecrets({ + GH: process.env.GH_TOKEN ?? '', + TOKEN: process.env.API_TOKEN ?? '', +}) + defineWorkspace({ source: githubRepo({ repo: 'owner/app' }), // shallow clone by default setup: ({ serial, parallel }) => { @@ -121,7 +136,7 @@ defineWorkspace({ fileSkill({ path: '.hints.md', content: '# Hints\nPrefer pnpm.' }), ], plugins: ['@anthropic/plugin-foo'], // Claude Code only; other harnesses warn+skip - secrets: createSecrets({ GH: process.env.GH_TOKEN ?? '' }), + secrets, }) ``` @@ -132,6 +147,12 @@ Skills and plugins are **projected** into each harness's native format at run ti Guard what the agent may run: ```typescript +import { defineSandbox, defineSandboxPolicy } from '@tanstack/ai-sandbox' +import type { SandboxProvider, WorkspaceDefinition } from '@tanstack/ai-sandbox' + +declare const provider: SandboxProvider +declare const workspace: WorkspaceDefinition + const policy = defineSandboxPolicy({ default: 'allow', }) @@ -144,6 +165,8 @@ Headless Grok Build and Codex stay on auto-approve when `default` is `'allow'` a Claude Code can use an interactive policy: ```typescript +import { defineSandboxPolicy } from '@tanstack/ai-sandbox' + defineSandboxPolicy({ commands: { allow: ['pnpm test', 'git diff'], @@ -160,9 +183,11 @@ Precedence is `deny` > `ask` > `allow`. Each harness adapter maps policy onto it ### Lifecycle ```typescript -lifecycle: { - reuse: 'thread', // resume one sandbox per threadId - snapshot: 'after-setup', // skip bootstrap on subsequent runs (when provider supports it) +import type { SandboxLifecycle } from '@tanstack/ai-sandbox' + +const lifecycle: SandboxLifecycle = { + reuse: 'thread', // resume one sandbox per threadId + snapshot: 'after-setup', // skip bootstrap on subsequent runs (when provider supports it) keepAlive: '30m', destroyOnComplete: false, } @@ -177,6 +202,13 @@ provider sandbox is gone. Create one snapshots object, pass it to ```typescript import { withPersistence } from '@tanstack/ai-persistence' import { memorySandboxSnapshots, withSandbox } from '@tanstack/ai-sandbox' +import type { + SandboxDefinition, + SandboxInstanceStore, +} from '@tanstack/ai-sandbox' + +declare const sandbox: SandboxDefinition +declare const instances: SandboxInstanceStore const snapshots = await memorySandboxSnapshots({ sandbox, instances }) @@ -197,6 +229,8 @@ for the full server setup. Use `createSecrets()` so values stay behind opaque `SecretRef` tokens. They are never written to snapshots, the sandbox store, or event logs. The sandbox layer resolves them onto the live handle at create, resume, and snapshot restore: ```typescript +import { createSecrets } from '@tanstack/ai-sandbox' + const secrets = createSecrets({ XAI_API_KEY: process.env.XAI_API_KEY ?? '' }) // secrets.XAI_API_KEY is a ref, not the string ``` diff --git a/packages/ai-skills/README.md b/packages/ai-skills/README.md index 2979d16a6b..1c601a4ed6 100644 --- a/packages/ai-skills/README.md +++ b/packages/ai-skills/README.md @@ -68,6 +68,11 @@ Pass an array to offer several; they are sorted by name and deduped. Loading the ### Tuning the catalog ```typescript +import { withSkills } from '@tanstack/ai-skills' +import type { SkillSource } from '@tanstack/ai-skills' + +declare const sources: Array + withSkills(sources, { // Cap the catalog so a big library doesn't tax every request. // Default 4000 tokens; throws if exceeded unless you supply a reducer. @@ -99,6 +104,7 @@ Implement the `SkillSource` contract, then prove it with the conformance suite t ```typescript import { runSkillSourceConformance } from '@tanstack/ai-skills/testing' import { s3Skills } from './s3-skills' +import { makeTestBucket } from './test-helpers' runSkillSourceConformance(() => s3Skills(makeTestBucket()), 's3') ``` diff --git a/packages/ai-typesafe/README.md b/packages/ai-typesafe/README.md index cfa95ee5a3..a698f857c4 100644 --- a/packages/ai-typesafe/README.md +++ b/packages/ai-typesafe/README.md @@ -53,6 +53,8 @@ export TYPESAFE_API_KEY="..." import { boolean, choice, decide, score } from '@tanstack/ai' import { typesafeDecider } from '@tanstack/ai-typesafe' +const ticket = 'My payouts have been failing for 3 days' + const result = await decide({ adapter: typesafeDecider('jev-latest'), state: ticket, diff --git a/packages/ai-utils/README.md b/packages/ai-utils/README.md index ca944203b6..7ca0b4ed4a 100644 --- a/packages/ai-utils/README.md +++ b/packages/ai-utils/README.md @@ -58,6 +58,8 @@ const apiKey = getApiKeyFromEnv('OPENAI_API_KEY') // throws if unset ```typescript import { arrayBufferToBase64, base64ToUint8Array } from '@tanstack/ai-utils' +declare const file: File + const encoded = arrayBufferToBase64(await file.arrayBuffer()) const bytes = base64ToUint8Array(encoded) ``` diff --git a/packages/ai-vue/README.md b/packages/ai-vue/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai-vue/README.md +++ b/packages/ai-vue/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/ai/README.md b/packages/ai/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/openai-base/README.md b/packages/openai-base/README.md index 869712ff5d..4f7a01003d 100644 --- a/packages/openai-base/README.md +++ b/packages/openai-base/README.md @@ -78,9 +78,18 @@ construct the SDK with their own `baseURL` (and any other client options) and pass it to `super`: ```ts -class GrokTextAdapter extends OpenAIBaseChatCompletionsTextAdapter<…> { - constructor(config: GrokConfig, model: TModel) { - super(model, 'grok', new OpenAI(withGrokDefaults(config))) +import OpenAI from 'openai' +import { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base' +import type { GrokTextConfig } from '@tanstack/ai-grok' + +declare function withGrokDefaults(config: GrokTextConfig): GrokTextConfig + +class GrokTextAdapter< + TModel extends string, +> extends OpenAIBaseResponsesTextAdapter { + constructor(config: GrokTextConfig, model: TModel) { + const options = withGrokDefaults(config) + super(model, 'grok', new OpenAI(options), { fetch: options.fetch }) } } ``` @@ -108,11 +117,11 @@ Each provider typically overrides 2–6 hooks and inherits everything else. └── BaseTextAdapter (abstract — emits AG-UI events) │ ├── @tanstack/openai-base::OpenAIBaseChatCompletionsTextAdapter - │ ├── ai-groq - │ └── ai-grok + │ └── ai-groq │ ├── @tanstack/openai-base::OpenAIBaseResponsesTextAdapter - │ └── ai-openai (Responses is OpenAI's preferred API) + │ ├── ai-openai (Responses is OpenAI's preferred API) + │ └── ai-grok │ ├── ai-anthropic::AnthropicTextAdapter extends BaseTextAdapter directly ├── ai-gemini::GeminiTextAdapter extends BaseTextAdapter directly @@ -123,7 +132,7 @@ Each provider typically overrides 2–6 hooks and inherits everything else. Note: `ai-openai` ships only the Responses-based text adapter. For pure Chat Completions use cases without OpenAI-specific behaviour, use -`ai-grok` or `ai-groq`, or build a new provider package extending +`ai-groq`, or build a new provider package extending `OpenAIBaseChatCompletionsTextAdapter`. ## Direct use diff --git a/packages/preact-ai-devtools/README.md b/packages/preact-ai-devtools/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/preact-ai-devtools/README.md +++ b/packages/preact-ai-devtools/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/react-ai-devtools/README.md b/packages/react-ai-devtools/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/react-ai-devtools/README.md +++ b/packages/react-ai-devtools/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/solid-ai-devtools/README.md b/packages/solid-ai-devtools/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/solid-ai-devtools/README.md +++ b/packages/solid-ai-devtools/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts', diff --git a/packages/svelte-ai-devtools/README.md b/packages/svelte-ai-devtools/README.md index 17ca7028e0..8e3d632079 100644 --- a/packages/svelte-ai-devtools/README.md +++ b/packages/svelte-ai-devtools/README.md @@ -165,6 +165,7 @@ input and output types: ```typescript import { toolDefinition } from '@tanstack/ai' import { z } from 'zod' +import { db } from './db' const getProducts = toolDefinition({ name: 'getProducts',