diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5bbf39e..b758495 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 21
+ node-version: 22
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
diff --git a/2025-BEST-PRACTICES.md b/2025-BEST-PRACTICES.md
deleted file mode 100644
index 59676be..0000000
--- a/2025-BEST-PRACTICES.md
+++ /dev/null
@@ -1,275 +0,0 @@
-# 2025 Best Practices for TPMJS Monorepo
-
-This document outlines recommendations to make TPMJS a cutting-edge 2025 monorepo optimized for both human and agentic development (Claude Code, Cursor, etc.).
-
-## High-Impact Additions
-
-### 1. Agent-First Documentation
-
-```
-packages/docs/
-├── architecture-decisions/ # ADRs in markdown
-├── patterns/ # Common patterns with examples
-├── schemas/ # JSON schemas for all data structures
-└── examples/ # Working code examples per feature
-```
-
-**Why:** Claude Code and other agents work better with:
-- Explicit decision documentation (ADRs)
-- Pattern libraries showing "the right way"
-- Machine-readable schemas
-- Real working examples to reference
-
-### 2. Automated Testing Pyramid
-
-```bash
-# Add to package.json scripts
-"test:unit": "vitest" # ✅ Already have this
-"test:integration": "vitest -c vitest.integration.config.ts" # Add
-"test:e2e": "playwright test" # Add
-"test:visual": "playwright test --grep @visual" # Add
-"test:contracts": "pactum" # Add for API testing
-```
-
-**Packages to add:**
-- `@playwright/test` - E2E testing
-- `@playwright/experimental-ct-react` - Component testing
-- `pactum` or `msw` integration tests (you have mocks setup)
-- `chromatic` or `percy` - Visual regression
-
-### 3. Type Coverage & Quality Gates
-
-```json
-// Add to root package.json
-{
- "scripts": {
- "type-check": "tsc --noEmit",
- "type-coverage": "type-coverage --at-least 95",
- "find-deadcode": "knip",
- "check-architecture": "depcruiser --validate"
- }
-}
-```
-
-**Add packages:**
-- `type-coverage` - Ensure no implicit `any`
-- `knip` - Find unused files/exports/dependencies
-- `dependency-cruiser` - Enforce architecture rules
-- `@total-typescript/ts-reset` - Better built-in types
-
-### 4. Development Containers
-
-```json
-// .devcontainer/devcontainer.json
-{
- "name": "TPMJS Dev",
- "dockerComposeFile": "docker-compose.yml",
- "service": "dev",
- "features": {
- "ghcr.io/devcontainers/features/node:1": {},
- "ghcr.io/devcontainers-contrib/features/pnpm:2": {}
- },
- "customizations": {
- "vscode": {
- "extensions": [
- "biomejs.biome",
- "bradlc.vscode-tailwindcss",
- "lokalise.i18n-ally"
- ]
- }
- }
-}
-```
-
-**Why:** Agents like Claude Code work better when environment is reproducible. This also helps human developers.
-
-### 5. Code Generation & Scaffolding
-
-```typescript
-// packages/cli/ - Internal dev tool
-import { scaffold } from '@tpmjs/cli';
-
-// Commands:
-pnpm gen:component ButtonGroup
-pnpm gen:package @tpmjs/new-package
-pnpm gen:app marketing-site
-```
-
-**Create:**
-- `plop` or `hygen` templates
-- Component scaffolding (with tests, stories, exports)
-- Package scaffolding (with tsconfig, package.json, exports)
-- Consistent file structure generation
-
-**Why:** Agents can use these commands to create new code following your exact patterns.
-
-### 6. Enhanced Strict Mode TypeScript
-
-```json
-// packages/tsconfig/base.json - Add these
-{
- "compilerOptions": {
- "exactOptionalPropertyTypes": true,
- "noUncheckedIndexedAccess": true,
- "noPropertyAccessFromIndexSignature": true,
- "allowUnusedLabels": false,
- "allowUnreachableCode": false,
- "noImplicitOverride": true
- }
-}
-```
-
-### 7. Bundle Analysis & Performance
-
-```json
-{
- "scripts": {
- "analyze": "turbo run build --filter=@tpmjs/web -- --analyze",
- "lighthouse": "lhci autorun",
- "bundle-size": "size-limit"
- }
-}
-```
-
-**Add:**
-- `@next/bundle-analyzer`
-- `@lhci/cli` - Lighthouse CI
-- `size-limit` - Bundle size tracking in CI
-
-### 8. Smart Dependency Management
-
-```json
-// .github/renovate.json
-{
- "extends": ["config:base"],
- "packageRules": [
- {
- "matchPackagePatterns": ["*"],
- "matchUpdateTypes": ["minor", "patch"],
- "groupName": "all non-major dependencies",
- "groupSlug": "all-minor-patch"
- }
- ]
-}
-```
-
-**Use:** Renovate or Dependabot with auto-merge for passing tests
-
-### 9. API Documentation Generation
-
-```bash
-pnpm add -D -w typedoc typedoc-plugin-markdown
-```
-
-Auto-generate API docs from TSDoc comments that both humans and agents can read.
-
-### 10. Schema-First Development
-
-```typescript
-// packages/schemas/ - Central schema definitions
-export * from './tool-schema';
-export * from './registry-api-schema';
-export * from './event-schema';
-
-// Use Zod for runtime + type generation
-// Agents can read schemas to understand contracts
-```
-
-## Monorepo-Specific Improvements
-
-### 11. Better Local Development
-
-```typescript
-// turbo.json
-{
- "pipeline": {
- "dev": {
- "cache": false,
- "persistent": true,
- "dependsOn": ["^build"]
- },
- "build": {
- "dependsOn": ["^build"],
- "outputs": ["dist/**", ".next/**"]
- }
- }
-}
-```
-
-### 12. Workspace Protocols & Constraints
-
-```yaml
-# .pnpm-workspace.yaml
-packages:
- - 'apps/*'
- - 'packages/*'
-
-# Add constraints
-pnpm-workspace-constraints:
- dependencies:
- '@tpmjs/ui': 'workspace:*'
- '@tpmjs/utils': 'workspace:*'
-```
-
-## Recommended Final Structure
-
-```
-.
-├── .devcontainer/ # Dev containers config
-├── .github/
-│ ├── workflows/ # CI/CD
-│ └── renovate.json # Dependency automation
-├── apps/
-│ └── web/
-├── packages/
-│ ├── cli/ # ⭐ NEW: Dev tooling
-│ ├── schemas/ # ⭐ NEW: Central schemas
-│ └── ...existing
-├── docs/
-│ ├── adr/ # ⭐ NEW: Architecture decisions
-│ ├── patterns/ # ⭐ NEW: Code patterns
-│ └── examples/ # ⭐ NEW: Working examples
-├── scripts/
-│ ├── scaffold.ts # ⭐ NEW: Code generation
-│ └── validate-deps.ts # ⭐ NEW: Architecture validation
-├── playwright.config.ts # ⭐ NEW: E2E testing
-├── .lighthouserc.json # ⭐ NEW: Performance
-└── knip.json # ⭐ NEW: Dead code detection
-```
-
-## Priority Order for Implementation
-
-### Phase 1 (Foundation)
-1. **Knip + type-coverage** - Catch issues early
-2. **Code generation scripts** - Ensure consistency
-3. **ADR documentation structure** - Decision tracking
-
-### Phase 2 (Quality)
-4. **E2E testing with Playwright** - Full user flow coverage
-5. **Bundle analysis + performance budgets** - Keep app fast
-6. **Stricter TypeScript settings** - Catch more bugs at compile time
-
-### Phase 3 (DX)
-7. **Dev containers** - Reproducible environments
-8. **API documentation generation** - Auto-generated from code
-9. **Renovate automation** - Keep dependencies fresh
-
-## Benefits for Agent-Driven Development
-
-1. **Explicit Patterns** - Agents can reference documented patterns instead of guessing
-2. **Code Generation** - Consistent scaffolding commands agents can use
-3. **Machine-Readable Schemas** - JSON schemas help agents understand data structures
-4. **Quality Gates** - Automated checks catch agent mistakes early
-5. **Working Examples** - Agents can copy-paste-adapt proven patterns
-6. **Architecture Enforcement** - Dependency rules prevent agents from creating invalid imports
-
-## Next Steps
-
-Start with the highest ROI items:
-1. Install Knip to find dead code
-2. Set up code generation for components/packages
-3. Create docs/patterns/ with common examples
-4. Add stricter TypeScript compiler options
-5. Set up Playwright for E2E testing
-
-These changes will make the codebase more maintainable and significantly improve the experience of working with AI coding agents.
diff --git a/API_ROUTES_TIMEOUT_INVESTIGATION.md b/API_ROUTES_TIMEOUT_INVESTIGATION.md
deleted file mode 100644
index 575a6bd..0000000
--- a/API_ROUTES_TIMEOUT_INVESTIGATION.md
+++ /dev/null
@@ -1,932 +0,0 @@
-# API Routes Timeout Issue - Complete Investigation Report
-
-## Problem Statement
-
-API routes deployed to Vercel are timing out with no response. The Next.js application pages work perfectly, but all API endpoints at `/api/*` return timeouts or "Redirecting..." messages.
-
-**Affected URLs:**
-- `https://tpmjs.com/api/health` - Returns "Redirecting..."
-- `https://tpmjs.com/api/tools` - Returns "Redirecting..."
-- `https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/health` - Timeouts (exit code 28)
-- `https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/tools` - Timeouts (exit code 28)
-
-**Working:**
-- All page routes work correctly (e.g., `/`, `/tool/[slug]`)
-- UI navigation and client-side routing function normally
-- Local development API routes work perfectly
-
-## Environment Details
-
-### Project Structure
-- **Monorepo:** Turborepo setup with pnpm workspaces
-- **Framework:** Next.js 16.0.4 (App Router)
-- **Node Version:** 24.x (on Vercel)
-- **Deployment Platform:** Vercel
-- **Custom Domains:** tpmjs.com, www.tpmjs.com
-
-### Repository Structure
-```
-tpmjs/
-├── apps/
-│ └── web/ # Next.js 16 App Router application
-│ ├── src/
-│ │ └── app/
-│ │ ├── api/
-│ │ │ ├── health/route.ts
-│ │ │ ├── stats/route.ts
-│ │ │ ├── tools/
-│ │ │ │ ├── route.ts
-│ │ │ │ ├── [id]/route.ts
-│ │ │ │ ├── [slug]/route.ts
-│ │ │ │ └── validate/route.ts
-│ │ │ └── sync/
-│ │ │ ├── changes/route.ts
-│ │ │ ├── keyword/route.ts
-│ │ │ └── metrics/route.ts
-│ │ ├── page.tsx
-│ │ └── tool/[slug]/page.tsx
-│ ├── next.config.ts
-│ └── vercel.json
-├── packages/
-│ ├── db/ # Prisma client
-│ ├── types/ # Shared TypeScript types
-│ ├── utils/ # Utility functions
-│ ├── env/ # Environment validation
-│ └── ui/ # React component library
-├── vercel.json # Root Vercel configuration
-└── turbo.json
-```
-
-## API Route Examples
-
-### `/apps/web/src/app/api/health/route.ts`
-```typescript
-import { NextResponse } from 'next/server';
-
-export const runtime = 'nodejs';
-export const dynamic = 'force-dynamic';
-export const maxDuration = 60;
-
-/**
- * GET /api/health
- * Simple health check endpoint that doesn't touch the database
- */
-export async function GET() {
- return NextResponse.json({
- status: 'ok',
- timestamp: new Date().toISOString(),
- env: {
- hasDatabase: !!process.env.DATABASE_URL,
- nodeEnv: process.env.NODE_ENV,
- },
- });
-}
-```
-
-### `/apps/web/src/app/api/tools/route.ts`
-```typescript
-import { NextResponse } from 'next/server';
-import { prisma } from '@tpmjs/db/client';
-
-export const runtime = 'nodejs';
-export const dynamic = 'force-dynamic';
-
-export async function GET(request: Request) {
- // ... query string parsing
-
- const [tools, totalCount] = await Promise.all([
- prisma.tool.findMany({
- where,
- orderBy: [
- { qualityScore: 'desc' },
- { npmDownloadsLastMonth: 'desc' },
- { createdAt: 'desc' },
- ],
- take: limit,
- skip: offset,
- }),
- prisma.tool.count({ where }),
- ]);
-
- return NextResponse.json({
- data: tools,
- pagination: {
- page,
- limit,
- total: totalCount,
- totalPages: Math.ceil(totalCount / limit),
- },
- });
-}
-```
-
-## Configuration Files
-
-### `/apps/web/next.config.ts` (Current)
-```typescript
-import type { NextConfig } from 'next';
-
-const nextConfig: NextConfig = {
- transpilePackages: ['@tpmjs/ui', '@tpmjs/utils', '@tpmjs/db', '@tpmjs/types', '@tpmjs/env'],
- reactStrictMode: true,
-};
-
-export default nextConfig;
-```
-
-### `/apps/web/vercel.json` (Current)
-```json
-{
- "$schema": "https://openapi.vercel.sh/vercel.json",
- "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build",
- "installCommand": "pnpm install"
-}
-```
-
-### `/vercel.json` (Root)
-```json
-{
- "$schema": "https://openapi.vercel.sh/vercel.json",
- "git": {
- "deploymentEnabled": {
- "main": true
- }
- },
- "github": {
- "silent": false,
- "autoJobCancelation": true
- },
- "crons": [
- {
- "path": "/api/sync/changes",
- "schedule": "*/2 * * * *"
- },
- {
- "path": "/api/sync/keyword",
- "schedule": "*/15 * * * *"
- },
- {
- "path": "/api/sync/metrics",
- "schedule": "0 * * * *"
- }
- ]
-}
-```
-
-## Local Build Verification
-
-### Local Build Output Structure
-```bash
-$ ls -R /Users/ajaxdavis/repos/tpmjs/tpmjs/apps/web/.next/server/app/api/
-
-health/
-stats/
-sync/
-tools/
-
-/apps/web/.next/server/app/api/health:
-route
-route.js
-route.js.map
-route.js.nft.json
-route_client-reference-manifest.js
-
-/apps/web/.next/server/app/api/stats:
-route
-route.js
-route.js.map
-route.js.nft.json
-route_client-reference-manifest.js
-
-/apps/web/.next/server/app/api/tools:
-[id]/
-[slug]/
-validate/
-route
-route.js
-route.js.map
-route.js.nft.json
-route_client-reference-manifest.js
-```
-
-### Routes Manifest Confirmation
-```bash
-$ cat /apps/web/.next/routes-manifest.json | jq '.staticRoutes[] | select(.page | contains("api"))'
-
-{
- "page": "/api/health",
- "regex": "^/api/health(?:/)?$",
- "routeKeys": {},
- "namedRegex": "^/api/health(?:/)?$"
-}
-{
- "page": "/api/stats",
- "regex": "^/api/stats(?:/)?$",
- "routeKeys": {},
- "namedRegex": "^/api/stats(?:/)?$"
-}
-{
- "page": "/api/sync/changes",
- "regex": "^/api/sync/changes(?:/)?$",
- "routeKeys": {},
- "namedRegex": "^/api/sync/changes(?:/)?$"
-}
-{
- "page": "/api/sync/keyword",
- "regex": "^/api/sync/keyword(?:/)?$",
- "routeKeys": {},
- "namedRegex": "^/api/sync/keyword(?:/)?$"
-}
-{
- "page": "/api/sync/metrics",
- "regex": "^/api/sync/metrics(?:/)?$",
- "routeKeys": {},
- "namedRegex": "^/api/sync/metrics(?:/)?$"
-}
-```
-
-### Node File Trace (NFT) Verification
-```bash
-$ cat /apps/web/.next/server/app/api/health/route.js.nft.json
-
-{
- "version": 1,
- "files": [
- "../../../../../../../node_modules/.pnpm/next@16.0.4_@babel+core@7.28.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/dist/client/components/app-router-headers.js",
- "../../../../../../../node_modules/.pnpm/next@16.0.4_@babel+core@7.28.5_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/dist/compiled/@opentelemetry/api/index.js",
- // ... many more dependencies
- ]
-}
-```
-
-**Conclusion:** API routes build correctly locally with all dependencies properly traced.
-
-## Vercel Deployment Analysis
-
-### Deployment Inspection Output
-```bash
-$ vercel inspect https://tpmjs-1chh44d1u-tpmjs.vercel.app
-
-General
- id dpl_2FHyiTWtBZzvT8EcohwYEZmdb8rb
- name tpmjs-web
- target production
- status ● Ready
- url https://tpmjs-1chh44d1u-tpmjs.vercel.app
- created Fri Nov 28 2025 20:44:03 GMT+1000
-
-Aliases
- ╶ https://www.tpmjs.com
- ╶ https://tpmjs-web.vercel.app
- ╶ https://tpmjs-web-tpmjs.vercel.app
- ╶ https://tpmjs-web-git-main-tpmjs.vercel.app
- ╶ https://tpmjs.com
-
-Builds
- ┌ . [0ms]
- ├── λ tool/[slug] (562.92KB) [iad1]
- ├── λ tool/[slug].rsc (562.92KB) [iad1]
- ├── λ _global-error (642.48KB) [iad1]
- ├── λ _global-error.rsc (642.48KB) [iad1]
- ├── λ _global-error.segments/__PAGE__.segment.rsc (642.48KB) [iad1]
- └── 56 output items hidden
-```
-
-**CRITICAL FINDING:** No API routes are listed in the build output. Only pages (`tool/[slug]`, `_global-error`, etc.) appear as serverless functions (`λ`).
-
-Expected API routes that should appear:
-- `λ api/health`
-- `λ api/tools`
-- `λ api/tools/[id]`
-- `λ api/tools/[slug]`
-- `λ api/sync/changes`
-- etc.
-
-### Testing Results
-```bash
-# Direct Vercel URL - Timeouts
-$ curl -s -m 10 https://tpmjs-1chh44d1u-tpmjs.vercel.app/api/health
-# Exit code 28 (timeout)
-
-# Custom Domain - Returns "Redirecting..."
-$ curl -s -m 10 https://tpmjs.com/api/health
-Redirecting...
-
-# Custom Domain - Returns "Redirecting..."
-$ curl -s -m 10 https://tpmjs.com/api/tools
-Redirecting...
-
-# Check redirect headers
-$ curl -I https://tpmjs.com
-HTTP/2 307
-cache-control: public, max-age=0, must-revalidate
-content-type: text/plain
-date: Fri, 28 Nov 2025 10:38:12 GMT
-location: https://www.tpmjs.com/
-server: Vercel
-```
-
-## Vercel Configuration Details
-
-### User-Confirmed Settings
-- **Root Directory:** `apps/web` (set in Vercel dashboard)
-- **DATABASE_URL:** Configured in Vercel environment variables (Production)
-- **Framework Preset:** (Unknown - needs verification)
-- **Build Output Directory:** (Unknown - using default `.next`)
-
-### Project List
-```bash
-$ vercel project ls | grep -i tpmjs
-
-tpmjs -- 16h 24.x
-v0-tool-registry-page https://tpmjs.com 3d 22.x
-```
-
-**NOTE:** Two projects exist:
-1. `tpmjs` - Current project (Node 24.x)
-2. `v0-tool-registry-page` - Also has tpmjs.com domain (Node 22.x)
-
-This could indicate a domain routing conflict or outdated project.
-
-## Investigation Timeline & Attempts
-
-### Attempt 1: Remove Root-Level Redirects
-**Hypothesis:** The redirect in `/vercel.json` was intercepting API requests.
-
-**Original `/vercel.json`:**
-```json
-{
- "redirects": [
- {
- "source": "/:path*",
- "has": [
- {
- "type": "host",
- "value": "www.tpmjs.com"
- }
- ],
- "destination": "https://tpmjs.com/:path*",
- "permanent": true
- }
- ]
-}
-```
-
-**Action:** Removed the `redirects` array from root `/vercel.json`.
-
-**Result:** ❌ API routes still timeout. Redirect rule was not the root cause.
-
-**Commit:** `9be3af7 fix(routing): move www redirect from vercel.json to Next.js config`
-
-### Attempt 2: Move Redirects to Next.js Config
-**Hypothesis:** Next.js should handle redirects after routing.
-
-**Action:** Added `async redirects()` to `apps/web/next.config.ts`:
-```typescript
-async redirects() {
- return [
- {
- source: '/:path*',
- has: [
- {
- type: 'host',
- value: 'www.tpmjs.com',
- },
- ],
- destination: 'https://tpmjs.com/:path*',
- permanent: true,
- },
- ];
-}
-```
-
-**Result:** ❌ API routes returned "Redirecting..." instead of executing. Next.js `async redirects()` applies to ALL routes including API routes.
-
-**Commit:** `9be3af7 fix(routing): move www redirect from vercel.json to Next.js config`
-
-### Attempt 3: Exclude API Routes from Redirect
-**Hypothesis:** Use regex to exclude `/api/*` from redirects.
-
-**Action:** Modified redirect pattern:
-```typescript
-async redirects() {
- return [
- {
- source: '/((?!api).*)', // Negative lookahead to exclude /api/*
- has: [
- {
- type: 'host',
- value: 'www.tpmjs.com',
- },
- ],
- destination: 'https://tpmjs.com/$1',
- permanent: true,
- },
- ];
-}
-```
-
-**Result:** ❌ API routes back to timing out (not redirecting anymore, but still not working).
-
-**Commit:** `c42bfb6 fix(redirects): exclude API routes from www redirect`
-
-### Attempt 4: Remove All Redirects
-**Hypothesis:** Eliminate redirect loop causing ERR_TOO_MANY_REDIRECTS.
-
-**Action:** Removed `async redirects()` entirely from `next.config.ts`.
-
-**Result:** ✅ Redirect loop fixed. ❌ API routes still timeout.
-
-**Commit:** `b93cd42 fix: remove redirects to resolve redirect loop`
-
-### Attempt 5: Add Vercel Functions Configuration
-**Hypothesis:** Vercel needs explicit configuration to detect API routes.
-
-**Action:** Added to `apps/web/vercel.json`:
-```json
-{
- "functions": {
- "app/api/**/*.ts": {
- "maxDuration": 60
- }
- }
-}
-```
-
-**Result:** ❌ No change. API routes still timeout.
-
-**Commit:** `748ca4d fix(api): configure Vercel functions for API routes with maxDuration`
-
-### Attempt 6: Add maxDuration to Route Files
-**Hypothesis:** Export configuration directly in route handlers.
-
-**Action:** Added to `apps/web/src/app/api/health/route.ts`:
-```typescript
-export const maxDuration = 60;
-```
-
-**Result:** ❌ No change. API routes still timeout.
-
-**Commit:** `8281f8f fix(build): disable Turbopack for Vercel deployment`
-
-### Attempt 7: Disable Turbopack
-**Hypothesis:** Turbopack (Next.js 16 default) has compatibility issues with Vercel.
-
-**Action:** Added `--webpack` flag to build command:
-```json
-{
- "buildCommand": "cd ../.. && turbo build --filter=@tpmjs/web -- --webpack"
-}
-```
-
-**Result:** ❌ Build failed completely. Invalid flag syntax.
-
-**Commit:** `8281f8f fix(build): disable Turbopack for Vercel deployment`
-
-### Attempt 8: Simplify Build Command
-**Hypothesis:** Use direct pnpm build instead of Turbo wrapper.
-
-**Action:** Changed to:
-```json
-{
- "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build"
-}
-```
-
-**Result:** ⏳ Pending deployment test.
-
-**Commit:** `065196d fix(build): simplify Vercel build command`
-
-## Root Cause Analysis
-
-### What We Know FOR SURE
-
-1. ✅ **API routes build correctly locally**
- - All 9 API routes compile to `.next/server/app/api/`
- - NFT (Node File Trace) files are generated with proper dependencies
- - Routes manifest includes all API routes
-
-2. ✅ **Next.js configuration is correct**
- - `export const runtime = 'nodejs'` set correctly
- - `export const dynamic = 'force-dynamic'` set correctly
- - `transpilePackages` includes all workspace packages
-
-3. ✅ **Pages deploy and work perfectly**
- - `/tool/[slug]` renders correctly
- - Homepage loads
- - Client-side navigation works
-
-4. ❌ **API routes are NOT deployed as serverless functions**
- - `vercel inspect` shows NO API routes in build output
- - Only pages appear as `λ` (lambda) functions
- - This is the PRIMARY issue
-
-5. ❌ **Direct Vercel URLs timeout**
- - Not just a custom domain issue
- - Affects `*.vercel.app` URLs
- - Exit code 28 (timeout) - no response at all
-
-6. ❌ **Custom domain shows "Redirecting..."**
- - Even with all redirects removed from config
- - Suggests a redirect at Vercel platform level OR DNS level
- - Could be from the `v0-tool-registry-page` project conflict
-
-### Possible Root Causes
-
-#### Theory 1: Vercel Project Misconfiguration
-**Likelihood:** HIGH
-
-**Evidence:**
-- Two projects with same domain (`tpmjs` and `v0-tool-registry-page`)
-- Framework Preset might not be set to "Next.js"
-- Root Directory is `apps/web` but Vercel might not be detecting Next.js properly
-
-**What to Check:**
-1. Vercel Dashboard → Project Settings → General
- - Framework Preset: Should be "Next.js"
- - Root Directory: Should be "apps/web"
- - Build Command: Should match vercel.json
- - Output Directory: Should be blank (default `.next`)
-
-2. Vercel Dashboard → Domains
- - Check if both projects have tpmjs.com
- - Remove domain from `v0-tool-registry-page` if present
-
-3. Vercel Dashboard → Deployments → Build Logs
- - Search for "API" or "route"
- - Look for errors about missing functions
- - Check if Next.js is detected correctly
-
-#### Theory 2: Monorepo Detection Issue
-**Likelihood:** MEDIUM
-
-**Evidence:**
-- Build command uses `cd ../.. && pnpm --filter=@tpmjs/web build`
-- Vercel might not be correctly detecting workspace structure
-- `transpilePackages` includes workspace packages
-
-**What to Check:**
-1. Build logs for workspace resolution errors
-2. Check if `node_modules` is being created in correct location
-3. Verify pnpm workspace configuration
-
-**Potential Fix:**
-Try setting `installCommand` to:
-```json
-{
- "installCommand": "pnpm install --shamefully-hoist"
-}
-```
-
-#### Theory 3: Next.js 16 + Vercel Incompatibility
-**Likelihood:** MEDIUM
-
-**Evidence:**
-- Next.js 16 released recently (November 2024)
-- Turbopack is default (might have Vercel issues)
-- App Router API routes behave differently than Pages Router
-
-**What to Check:**
-1. Vercel build logs for Next.js version detection
-2. Any warnings about incompatible features
-3. Check Vercel's Next.js 16 support status
-
-**Potential Fix:**
-Downgrade to Next.js 15.x temporarily to test:
-```json
-{
- "dependencies": {
- "next": "^15.0.0"
- }
-}
-```
-
-#### Theory 4: Environment Variable Issue
-**Likelihood:** LOW
-
-**Evidence:**
-- DATABASE_URL is configured
-- Pages work (they might not need env vars)
-- API routes use Prisma (requires DATABASE_URL)
-
-**What to Check:**
-1. Vercel Dashboard → Settings → Environment Variables
- - Verify DATABASE_URL is set for Production
- - Verify it's not blocked or empty
- - Check if other vars are needed
-
-2. Build logs for Prisma generation errors
-
-**Potential Fix:**
-None - user confirmed DATABASE_URL is set.
-
-#### Theory 5: Build Output Issue
-**Likelihood:** MEDIUM-HIGH
-
-**Evidence:**
-- `vercel inspect` doesn't show API routes
-- Only pages are listed as functions
-- Build completes successfully (38-41 seconds)
-
-**What to Check:**
-1. Build logs: Does Next.js report building API routes?
- - Look for "Route (app)" or "λ" indicators for API routes
- - Compare to local build output
-
-2. Check if Vercel is using correct build output structure
- - App Router uses `.next/server/app/`
- - Pages Router uses `.next/server/pages/`
-
-**Potential Fix:**
-Try forcing Vercel to recognize the build:
-```json
-{
- "builds": [
- {
- "src": "package.json",
- "use": "@vercel/next"
- }
- ]
-}
-```
-
-(Note: `builds` is legacy, modern Next.js should auto-detect)
-
-## Recommended Next Steps
-
-### Immediate Actions (High Priority)
-
-1. **Check Vercel Project Settings**
- - Go to Vercel Dashboard → tpmjs-web project
- - Verify Framework Preset is "Next.js"
- - Verify Root Directory is `apps/web`
- - Screenshot settings for reference
-
-2. **Review Build Logs**
- - Go to latest deployment
- - Download complete build logs
- - Search for:
- - "Route (app)" - should show API routes
- - "λ" - should show API functions
- - "api" - any mentions
- - Errors or warnings
-
-3. **Check Domain Configuration**
- - Verify only ONE project has tpmjs.com domain
- - Remove domain from `v0-tool-registry-page` project if present
- - Check DNS settings aren't redirecting
-
-4. **Test Simple API Route**
- - Create minimal API route:
- ```typescript
- // apps/web/src/app/api/test/route.ts
- export async function GET() {
- return new Response('Hello from API', { status: 200 });
- }
- ```
- - Deploy and test
- - If this doesn't work, confirms platform issue
-
-### Investigation Actions (Medium Priority)
-
-5. **Compare Working vs Non-Working**
- - Find a deployment where pages DO work
- - Compare build output between page routes and API routes
- - Look for differences in how they're compiled
-
-6. **Test Vercel CLI Deploy**
- - Deploy directly via CLI: `vercel --prod`
- - Check if behavior differs from Git-based deploy
- - Might reveal configuration issues
-
-7. **Check Vercel Function Logs**
- - Even though functions aren't in build output, try:
- - `vercel logs --since 1h`
- - Look for any API route invocations or errors
-
-8. **Review Turbo Configuration**
- ```bash
- # Check turbo.json for Next.js build config
- cat /turbo.json
-
- # Verify build runs correctly locally
- pnpm --filter=@tpmjs/web build
- ```
-
-### Alternative Approaches (If Above Fails)
-
-9. **Create New Vercel Project**
- - Import from Git fresh
- - Use identical settings
- - Test if fresh project works
-
-10. **Contact Vercel Support**
- - This may be a platform bug with Next.js 16
- - Provide this document as context
- - Ask specifically why API routes aren't in build output
-
-11. **Temporary Workaround**
- - Deploy API routes separately (different service)
- - Use Vercel proxy to route `/api/*` to separate deployment
- - Not ideal but unblocks development
-
-## Environment Variables Needed
-
-```bash
-# Required for API routes
-DATABASE_URL="postgresql://..."
-
-# Optional (check if needed)
-NODE_ENV="production"
-NEXT_PUBLIC_* # Any public env vars
-```
-
-## Build Commands Reference
-
-### Local Development
-```bash
-# Install dependencies
-pnpm install
-
-# Generate Prisma client
-pnpm --filter=@tpmjs/db db:generate
-
-# Run development server
-pnpm --filter=@tpmjs/web dev
-
-# Build for production
-pnpm --filter=@tpmjs/web build
-
-# Test build locally
-pnpm --filter=@tpmjs/web start
-```
-
-### Vercel Configuration
-**Current:**
-```json
-{
- "buildCommand": "cd ../.. && pnpm --filter=@tpmjs/web build",
- "installCommand": "pnpm install"
-}
-```
-
-**Alternative to try:**
-```json
-{
- "buildCommand": "cd ../.. && turbo build --filter=@tpmjs/web",
- "installCommand": "pnpm install",
- "framework": "nextjs"
-}
-```
-
-## Key Files to Review
-
-1. `/apps/web/next.config.ts` - Next.js configuration
-2. `/apps/web/vercel.json` - Vercel app-level config
-3. `/vercel.json` - Vercel root config
-4. `/turbo.json` - Turborepo configuration
-5. `/apps/web/.next/routes-manifest.json` - Route definitions
-6. `/apps/web/.next/build-manifest.json` - Build output
-7. Vercel build logs (from dashboard)
-
-## Questions for Vercel Support
-
-If escalating to Vercel support, ask:
-
-1. Why are API routes not appearing in the build output (`vercel inspect`) when pages are deploying correctly?
-
-2. Is there a known issue with Next.js 16 App Router API routes in Turborepo monorepos?
-
-3. What's the correct way to configure `vercel.json` for a Next.js 16 app in a monorepo with custom build commands?
-
-4. Could having two projects (`tpmjs` and `v0-tool-registry-page`) with the same domain cause routing issues?
-
-5. Are there any specific requirements for deploying Next.js 16 API routes that differ from Next.js 15?
-
-## Related Documentation
-
-- [Next.js 16 Upgrade Guide](https://nextjs.org/docs/app/guides/upgrading/version-16)
-- [Vercel Next.js Deployment](https://vercel.com/docs/frameworks/nextjs)
-- [Vercel Functions Configuration](https://vercel.com/docs/functions/configuring-functions)
-- [Turborepo with Vercel](https://vercel.com/docs/monorepos/turborepo)
-- [Next.js App Router API Routes](https://nextjs.org/docs/app/api-reference/file-conventions/route)
-
-## Recent Commits Related to This Issue
-
-```
-065196d fix(build): simplify Vercel build command
-8281f8f fix(build): disable Turbopack for Vercel deployment
-748ca4d fix(api): configure Vercel functions for API routes with maxDuration
-b93cd42 fix: remove redirects to resolve redirect loop
-c42bfb6 fix(redirects): exclude API routes from www redirect
-9be3af7 fix(routing): move www redirect from vercel.json to Next.js config
-a92dfff fix(build): add workspace packages to Next.js transpilePackages
-cc6c824 fix(vercel): configure Turborepo monorepo build for apps/web
-```
-
-## ✅ CONCLUSION - ROOT CAUSE IDENTIFIED
-
-### The Real Problem
-
-**Vercel is NOT detecting this project as a Next.js application.**
-
-When Vercel doesn't detect Next.js, it:
-- Uses `@vercel/static-builder` instead of `@vercel/next`
-- Treats the deployment as a static site
-- Deploys pages (static HTML) successfully
-- **Completely drops all App Router API routes**
-- Never generates serverless functions for `/api/*` routes
-
-This explains EVERY symptom:
-- ✅ Pages work (they're static files)
-- ❌ API routes timeout (they were never deployed)
-- ❌ No `λ api/*` in build output (functions don't exist)
-- ❌ Direct Vercel URLs timeout (not a DNS issue)
-- ❌ "Redirecting..." on custom domain (wrong project owns the domain)
-
-### Why Vercel Doesn't Detect Next.js
-
-**1. Wrong Root Directory**
-- Vercel project likely has Root Directory set to `.` or empty
-- Should be exactly: `apps/web`
-- A single character difference breaks Next.js detection
-
-**2. Wrong Framework Preset**
-- When Vercel can't auto-detect Next.js (due to monorepo + wrong root)
-- It defaults to Framework Preset = "Other"
-- "Other" uses static builder, not Next.js builder
-
-**3. Domain Conflict**
-- Two projects exist: `tpmjs` and `v0-tool-registry-page`
-- Both have `tpmjs.com` domain assigned
-- Requests randomly route to wrong project
-- "Redirecting..." comes from the old project, not your code
-
-**4. Custom Build Commands**
-- Custom build commands can bypass Vercel's auto-detection
-- Should let Vercel auto-detect and use default commands
-
-### Evidence
-
-**Local build works:**
-```bash
-$ ls apps/web/.next/server/app/api/
-health/ stats/ sync/ tools/
-# All API routes compile correctly
-```
-
-**Vercel deployment missing API routes:**
-```bash
-$ vercel inspect https://tpmjs-1chh44d1u-tpmjs.vercel.app
-Builds
- ├── λ tool/[slug] # Pages present
- ├── λ _global-error # Pages present
- └── 56 output items hidden
-# NO api/health, NO api/tools, NO api/* at all
-```
-
-This is **not a Next.js bug, not a Turborepo bug, not a code issue**.
-This is **pure Vercel project misconfiguration**.
-
-### The Fix (4 Steps, 5 Minutes)
-
-See `VERCEL_FIX_CHECKLIST.md` for detailed instructions.
-
-**Quick version:**
-1. Set Root Directory to `apps/web` (exactly)
-2. Set Framework Preset to "Next.js"
-3. Remove `tpmjs.com` domain from `v0-tool-registry-page` project
-4. Clear custom build commands (let Vercel auto-detect)
-
-**No code changes needed. Just fix Vercel dashboard settings.**
-
-### After the Fix
-
-Next deployment will show:
-```bash
-$ vercel inspect
-Builds
- ├── λ api/health
- ├── λ api/tools
- ├── λ api/sync/changes
- ├── λ api/tools/[id]
- ├── λ api/tools/[slug]
- ├── λ tool/[slug]
- ...
-```
-
-And API routes will work instantly:
-```bash
-$ curl https://tpmjs.com/api/health
-{"status":"ok","timestamp":"..."}
-```
-
----
-
-## Summary for ChatGPT
-
-The core issue is that **Vercel is not detecting the project as Next.js** due to incorrect Root Directory, Framework Preset set to "Other", domain conflicts between two projects, and custom build commands bypassing auto-detection.
-
-When Vercel doesn't detect Next.js, it uses a static site builder which deploys pages but **completely drops all App Router API routes** from the build. This is why pages work but all `/api/*` requests timeout - the API route serverless functions were never created.
-
-**The fix is purely configuration** - no code changes needed. Set Root Directory to `apps/web`, Framework Preset to "Next.js", remove the domain from the old project, and clear custom build commands. See `VERCEL_FIX_CHECKLIST.md` for step-by-step instructions.
diff --git a/DENO_NODE_PACKAGE_ISSUE.md b/DENO_NODE_PACKAGE_ISSUE.md
deleted file mode 100644
index f8fbc5f..0000000
--- a/DENO_NODE_PACKAGE_ISSUE.md
+++ /dev/null
@@ -1,306 +0,0 @@
-# Running `ai-sdk-tool-code-execution` in Deno - Compatibility Issue
-
-## Problem Summary
-
-We need to run the npm package `ai-sdk-tool-code-execution` in a Deno runtime environment on Railway. The package requires Node.js built-ins (`node:sqlite`, `undici`) that don't exist in Deno, and we're looking for a solution to make it work.
-
-## Environment
-
-- **Runtime:** Deno 1.39.0 on Railway
-- **Package:** `ai-sdk-tool-code-execution@0.0.2`
-- **Import Method:** Dynamic imports via esm.sh CDN
-- **Use Case:** Remote code execution for AI SDK tools
-
-## What We're Trying to Do
-
-We have a Deno server that dynamically imports npm packages at runtime to provide AI SDK tools. The workflow is:
-
-1. User requests a tool (e.g., `executeCode`)
-2. Deno server fetches the package from esm.sh or npm
-3. Server loads the tool's schema and execution function
-4. Server executes the tool with user-provided parameters
-
-## The Package We Need
-
-**Package:** `ai-sdk-tool-code-execution`
-**Version:** `0.0.2`
-**Description:** Execute Python code in a sandboxed environment using Vercel Sandbox
-**npm URL:** https://www.npmjs.com/package/ai-sdk-tool-code-execution
-**CDN URLs:**
-- esm.sh: `https://esm.sh/ai-sdk-tool-code-execution@0.0.2`
-- jsdelivr: `https://cdn.jsdelivr.net/npm/ai-sdk-tool-code-execution@0.0.2/+esm`
-
-**Dependencies (from package.json):**
-```json
-{
- "dependencies": {
- "ai": "^4.0.18",
- "better-sqlite3": "^11.8.1",
- "undici": "^7.16.0"
- }
-}
-```
-
-**Key Issue:** The package depends on:
-- `better-sqlite3` → which requires `node:sqlite` (Node.js built-in)
-- `undici` → HTTP client that uses Node.js internals
-
-## What We've Tried
-
-### Attempt 1: Deno npm: Specifier (Node.js Compatibility Mode)
-
-**Code:**
-```typescript
-const npmUrl = `npm:ai-sdk-tool-code-execution@0.0.2`;
-const module = await import(npmUrl);
-```
-
-**Error:**
-```
-Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2
-```
-
-**Why it failed:** Deno's npm compatibility requires the package to be "prepared" (downloaded/cached) before import. Dynamic imports of unprepared npm packages fail.
-
-### Attempt 2: esm.sh with Node.js Target
-
-**Code:**
-```typescript
-const esmUrl = `https://esm.sh/ai-sdk-tool-code-execution@0.0.2?target=esnext`;
-const module = await import(esmUrl);
-```
-
-**Error:**
-```
-Module not found "https://esm.sh/node:sqlite?target=esnext"
- at https://esm.sh/undici@^7.16.0?target=esnext:25:8
-```
-
-**Why it failed:** The package code imports `node:sqlite` which esm.sh tries to load from `https://esm.sh/node:sqlite?target=esnext`, but `node:sqlite` is a Node.js built-in, not an npm package.
-
-### Attempt 3: Multi-Strategy with Fallback
-
-**Code:**
-```typescript
-let module;
-let importError;
-
-// Strategy 1: npm: specifier
-try {
- const npmUrl = `npm:${packageName}@${version}`;
- module = await import(npmUrl);
-} catch (error) {
- importError = error;
-
- // Strategy 2: esm.sh with esnext target
- try {
- const esmUrl = `https://esm.sh/${packageName}@${version}?target=esnext`;
- module = await import(esmUrl);
- } catch (esmError) {
- return { success: false, error: esmError.message };
- }
-}
-```
-
-**Result:** Both strategies fail with the same errors as above.
-
-## Current Deno Configuration
-
-**`deno.json`:**
-```json
-{
- "compilerOptions": {
- "allowJs": true,
- "lib": ["deno.window"],
- "strict": true
- },
- "nodeModulesDir": true,
- "unstable": ["byonm"],
- "imports": {
- "zod-to-json-schema": "https://esm.sh/zod-to-json-schema@3.25.0"
- }
-}
-```
-
-**Key Settings:**
-- `nodeModulesDir: true` - Creates `node_modules` directory for npm packages
-- `unstable: ["byonm"]` - Enables "Bring Your Own Node Modules" mode
-
-## Full Error Details
-
-### npm: Strategy Error
-```json
-{
- "success": false,
- "error": "Failed to import package: ...",
- "details": {
- "npmError": "Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2, imported from: file:///app/server.ts"
- }
-}
-```
-
-### esm.sh Strategy Error
-```json
-{
- "success": false,
- "error": "Failed to import package: Module not found \"https://esm.sh/node:sqlite?target=esnext\"",
- "details": {
- "esmError": "Module not found \"https://esm.sh/node:sqlite?target=esnext\".\n at https://esm.sh/undici@^7.16.0?target=esnext:25:8"
- }
-}
-```
-
-## Technical Deep Dive
-
-### Why This Package Needs Node.js
-
-1. **better-sqlite3** - Native Node.js addon for SQLite
- - Uses `node:sqlite` built-in
- - Compiled C++ bindings
- - Not available in Deno without Node compatibility layer
-
-2. **undici** - Modern HTTP client for Node.js
- - Uses Node.js streams and buffer APIs
- - Optimized for Node.js internals
- - May work in Deno with polyfills, but blocked by sqlite dependency
-
-### Deno's Node.js Compatibility
-
-Deno supports many Node.js built-ins via `node:*` imports:
-- `node:fs`, `node:path`, `node:http`, `node:crypto`, etc.
-
-**BUT** it does NOT support:
-- `node:sqlite` (not a standard Node.js built-in)
-- Native addons (`.node` files)
-- Some advanced internal APIs
-
-### The Import Flow
-
-1. **Deno tries to import** `npm:ai-sdk-tool-code-execution@0.0.2`
-2. **Package resolves to** esm.sh or npm registry
-3. **Package imports** `better-sqlite3`
-4. **better-sqlite3 imports** `node:sqlite`
-5. **FAILURE:** `node:sqlite` doesn't exist in Deno or esm.sh
-
-## Questions for ChatGPT
-
-1. **Can Deno's npm compatibility layer handle `better-sqlite3` or `node:sqlite`?**
- - Is there a Deno-compatible SQLite library we could alias?
- - Can we use import maps to redirect `node:sqlite` to a Deno polyfill?
-
-2. **Can we "prepare" the npm module in Deno before dynamic import?**
- - Is there a way to pre-cache npm packages in Deno?
- - Can we use `deno vendor` or similar to prepare the package?
-
-3. **Can esm.sh or other CDNs provide Node.js built-in polyfills?**
- - Does esm.sh have a mode that bundles Node.js built-ins?
- - Are there CDN parameters we're missing?
-
-4. **Could we use Deno's `--node-modules-dir` flag differently?**
- - Should we install the package via npm/pnpm first?
- - Can we point Deno to pre-installed node_modules?
-
-5. **Is there a way to patch/bundle the package to remove Node.js dependencies?**
- - Could we create a Deno-compatible fork?
- - Are there tools to transpile Node.js packages to Deno?
-
-6. **Alternative: Different code execution package?**
- - Are there Deno-native code execution tools?
- - Could we use WebAssembly or browser-based sandboxing?
-
-## What Would Success Look Like
-
-**Ideal outcome:**
-```typescript
-// This should work in Deno:
-const module = await import('npm:ai-sdk-tool-code-execution@0.0.2');
-const { executeCode } = module;
-
-// And this should execute:
-const result = await executeCode.execute({
- code: 'print(fibonacci(10))',
- language: 'python'
-});
-```
-
-**Acceptable outcome:**
-```typescript
-// Some preparation step, then:
-const module = await import('https://esm.sh/ai-sdk-tool-code-execution@0.0.2');
-// Works without errors
-```
-
-## Repository Context
-
-**Project:** TPMJS - Tool Package Manager for AI SDK
-**Server:** `apps/railway-executor/server.ts`
-**Config:** `apps/railway-executor/deno.json`
-**Deployment:** Railway with Deno runtime
-
-**Server Code (Simplified):**
-```typescript
-async function loadAndDescribe(req: Request): Promise {
- const { packageName, exportName, version, importUrl } = await req.json();
-
- // Try npm: specifier first
- try {
- const npmUrl = `npm:${packageName}@${version}`;
- const module = await import(npmUrl);
- const tool = module[exportName];
- return Response.json({ success: true, tool });
- } catch (error) {
- // Try esm.sh fallback
- const esmUrl = `https://esm.sh/${packageName}@${version}?target=esnext`;
- const module = await import(esmUrl);
- const tool = module[exportName];
- return Response.json({ success: true, tool });
- }
-}
-
-Deno.serve({ port: 3001 }, handler);
-```
-
-## Live Error Logs
-
-**Request:**
-```bash
-curl -X POST https://endearing-commitment-production.up.railway.app/load-and-describe \
- -H "Content-Type: application/json" \
- -d '{
- "packageName": "ai-sdk-tool-code-execution",
- "exportName": "executeCode",
- "version": "0.0.2",
- "importUrl": "https://esm.sh/ai-sdk-tool-code-execution@0.0.2"
- }'
-```
-
-**Response:**
-```json
-{
- "success": false,
- "error": "Failed to import package: Module not found \"https://esm.sh/node:sqlite?target=esnext\"",
- "details": {
- "npmError": "Loading unprepared module: npm:ai-sdk-tool-code-execution@0.0.2",
- "esmError": "Module not found \"https://esm.sh/node:sqlite?target=esnext\""
- }
-}
-```
-
-## Additional Context
-
-- We successfully load other packages (e.g., `@tpmjs/hello`, `zod-to-json-schema`)
-- Only packages with Node.js built-in dependencies fail
-- Switching to Node.js would work, but we prefer Deno's security model
-- This is for a production tool registry serving AI SDK tools to users
-
-## Related Resources
-
-- **Deno npm compatibility:** https://deno.com/manual/node/npm_specifiers
-- **Deno Node built-ins:** https://deno.com/manual/node/node_specifiers
-- **esm.sh documentation:** https://esm.sh/
-- **Package source:** https://www.npmjs.com/package/ai-sdk-tool-code-execution
-- **Deno SQLite libraries:** https://deno.land/x/sqlite@v3.8
-
----
-
-**Question for ChatGPT:** Is there any way to make `ai-sdk-tool-code-execution` work in Deno, given these constraints? If not, what's the closest alternative that would work in Deno's runtime?
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
index 914560e..dd4d51c 100644
--- a/DEPLOYMENT.md
+++ b/DEPLOYMENT.md
@@ -167,7 +167,7 @@ Not needed for Option 1 (Deployment Protection).
Add to README.md to show CI status:
```markdown
-[](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/ci.yml)
+[](https://github.com/tpmjs/tpmjs/actions/workflows/ci.yml)
```
## Summary
diff --git a/DYNAMIC_IMPORT_ISSUE.md b/DYNAMIC_IMPORT_ISSUE.md
deleted file mode 100644
index 9523485..0000000
--- a/DYNAMIC_IMPORT_ISSUE.md
+++ /dev/null
@@ -1,864 +0,0 @@
-# Dynamic Import Issue: Cannot Import ESM Modules from CDN in Next.js Server-Side API Route
-
-## Executive Summary
-
-We're building a dynamic tool loading system where AI agents can discover and load tools at runtime from npm packages via esm.sh CDN. The system successfully searches and finds relevant tools, but fails when trying to dynamically import them using `import()` in a Next.js App Router API route.
-
-**Error**: `Error: Cannot find module 'unknown'` with code `MODULE_NOT_FOUND`
-
-**Critical Question**: How can we dynamically import ESM modules from external URLs (like esm.sh) in Next.js 16 App Router API routes running in Node.js runtime?
-
----
-
-## System Architecture
-
-### High-Level Flow
-
-```
-1. User sends message → "use firecrawl to search for ajax davis"
-2. Chat API extracts query → "use firecrawl to search for ajax davis"
-3. Pre-flight search → Calls searchTpmjsToolsTool.execute({ query, limit: 5 })
-4. Search API returns → Top 5 matching tools from database (BM25-like scoring)
-5. Dynamic loading → Tries to import tools from esm.sh URLs ❌ FAILS HERE
-6. Agent uses tools → Would pass loaded tools to AI model
-```
-
-### Tech Stack
-
-- **Framework**: Next.js 16.0.4
-- **Build Tool**: Turbopack (default in Next.js 15+)
-- **Runtime**: Node.js (not edge)
-- **Package Manager**: pnpm (monorepo with workspaces)
-- **Deployment Target**: Vercel (eventually, currently local dev)
-- **AI SDK**: Vercel AI SDK v6.0.0-beta.124
-- **Model**: OpenAI GPT-4o-mini via `streamText()`
-
-### Monorepo Structure
-
-```
-tpmjs/
-├── apps/
-│ ├── playground/ # Next.js app with chat interface
-│ │ └── src/
-│ │ ├── app/api/chat/route.ts # Where dynamic import fails
-│ │ └── lib/dynamic-tool-loader.ts
-│ └── web/ # Tool registry website
-│ └── src/app/api/tools/search/route.ts
-└── packages/
- └── tools/
- ├── hello/ # Static tool (works fine)
- └── search-registry/ # Meta-tool for searching registry
-```
-
----
-
-## Detailed Code Implementation
-
-### File 1: `apps/playground/src/lib/dynamic-tool-loader.ts`
-
-**Purpose**: Load tools dynamically from esm.sh CDN
-
-```typescript
-// Cache for imported tool modules (process-level)
-const moduleCache = new Map();
-
-// Cache for per-conversation active tools
-const conversationTools = new Map>();
-
-/**
- * Generate cache key for a tool
- */
-function getCacheKey(packageName: string, exportName: string): string {
- return `${packageName}::${exportName}`;
-}
-
-/**
- * Validate that an import is a valid AI SDK tool
- */
-function isValidTool(value: any): boolean {
- return (
- value &&
- typeof value === 'object' &&
- typeof value.description === 'string' &&
- typeof value.execute === 'function'
- );
-}
-
-/**
- * Dynamically import a tool from ESM CDN
- *
- * THIS IS WHERE IT FAILS ❌
- */
-export async function loadToolDynamically(
- packageName: string,
- exportName: string,
- version: string,
- importUrl?: string
-): Promise {
- const cacheKey = getCacheKey(packageName, exportName);
-
- // Check cache first
- if (moduleCache.has(cacheKey)) {
- console.log(`✅ Cache hit: ${cacheKey}`);
- return moduleCache.get(cacheKey);
- }
-
- // Build import URL
- const url = importUrl || `https://esm.sh/${packageName}@${version}`;
-
- try {
- console.log(`📦 Importing: ${url}`);
- // Example: https://esm.sh/firecrawl-aisdk@0.7.2
-
- // Dynamic import with @vite-ignore to bypass bundler
- const module = await import(/* @vite-ignore */ url);
-
- console.log(`🔍 Module imported successfully`);
- console.log(`🔍 Module type: ${typeof module}`);
- console.log(`🔍 Module keys: ${Object.keys(module).join(', ')}`);
- console.log(`🔍 Looking for export: "${exportName}"`);
- console.log(`🔍 Export exists: ${exportName in module}`);
- console.log(`🔍 Export type: ${typeof module[exportName]}`);
-
- // Get the specific export
- const tool = module[exportName];
-
- if (!tool) {
- console.error(`❌ Export "${exportName}" not found in module. Available exports:`, Object.keys(module));
- return null;
- }
-
- console.log(`🔍 Tool structure:`, {
- hasDescription: 'description' in tool,
- hasExecute: 'execute' in tool,
- hasInputSchema: 'inputSchema' in tool,
- keys: Object.keys(tool),
- });
-
- if (!isValidTool(tool)) {
- console.error(`❌ Invalid tool structure: ${exportName} from ${packageName}`);
- console.error(` Tool:`, tool);
- return null;
- }
-
- // Cache successful import
- moduleCache.set(cacheKey, tool);
- console.log(`✅ Loaded: ${cacheKey}`);
-
- return tool;
- } catch (error) {
- console.error(`❌ Failed to load ${packageName}#${exportName}:`, error);
- console.error(` URL: ${url}`);
- console.error(` Stack:`, error instanceof Error ? error.stack : 'No stack trace');
- return null;
- }
-}
-
-/**
- * Load multiple tools in parallel
- */
-export async function loadToolsBatch(
- toolMetadata: Array<{
- packageName: string;
- exportName: string;
- version: string;
- importUrl?: string;
- }>
-): Promise> {
- const promises = toolMetadata.map((meta) =>
- loadToolDynamically(
- meta.packageName,
- meta.exportName,
- meta.version,
- meta.importUrl
- ).then((tool) => ({
- key: getCacheKey(meta.packageName, meta.exportName),
- tool,
- }))
- );
-
- const results = await Promise.all(promises);
-
- const tools: Record = {};
- for (const { key, tool } of results) {
- if (tool) {
- tools[key] = tool;
- }
- }
-
- return tools;
-}
-```
-
-### File 2: `apps/playground/src/app/api/chat/route.ts`
-
-**Purpose**: Main chat API that orchestrates tool discovery and loading
-
-```typescript
-import { createOpenAI } from '@ai-sdk/openai';
-import { type UIMessage, convertToModelMessages, stepCountIs, streamText } from 'ai';
-import type { NextRequest } from 'next/server';
-import { NextResponse } from 'next/server';
-import { env } from '~/env';
-import { loadAllTools, sanitizeToolName } from '~/lib/tool-loader';
-import { searchTpmjsToolsTool } from '@tpmjs/search-registry';
-import {
- loadToolsBatch,
- addConversationTools,
-} from '~/lib/dynamic-tool-loader';
-
-export const runtime = 'nodejs'; // ⚠️ Important: We're using Node.js runtime, not edge
-export const dynamic = 'force-dynamic';
-export const maxDuration = 60;
-
-// Initialize OpenAI provider
-const openai = createOpenAI({
- apiKey: env.OPENAI_API_KEY,
-});
-
-// Add conversation state tracking (in-memory for MVP)
-const conversationStates = new Map }>();
-
-/**
- * POST /api/chat
- * Chat with AI agent that can execute TPMJS tools
- */
-export async function POST(request: NextRequest) {
- try {
- const body = await request.json();
- console.log('📥 Request body:', JSON.stringify(body, null, 2));
-
- const messages: UIMessage[] = body.messages || [];
- const conversationId: string = body.conversationId || 'default';
-
- console.log(`🔑 Conversation ID: ${conversationId}`);
-
- // Get or create conversation state
- if (!conversationStates.has(conversationId)) {
- console.log('✨ Creating new conversation state');
- conversationStates.set(conversationId, { loadedTools: {} });
- }
- const state = conversationStates.get(conversationId)!;
- console.log(`📊 Current loaded tools in conversation: ${Object.keys(state.loadedTools).length}`);
-
- // 1. Load static tools + search tool
- const staticTools = await loadAllTools();
- console.log(`🔧 Loaded ${Object.keys(staticTools).length} static tools`);
-
- staticTools.searchTpmjsTools = searchTpmjsToolsTool;
- console.log('✅ Added searchTpmjsTools to static tools');
-
- // 2. Extract user query from last message for tool search
- const lastMessage = messages[messages.length - 1];
- let userQuery = '';
- if (lastMessage?.role === 'user') {
- const parts = (lastMessage as any).parts || [];
- for (const part of parts) {
- if (part.type === 'text') {
- userQuery = part.text;
- break;
- }
- }
- }
-
- console.log(`💬 User query: "${userQuery}"`);
-
- // 3. Automatically search for relevant tools based on the user's message
- if (userQuery && userQuery.trim().length > 0) {
- console.log('🔎 Searching for relevant tools...');
-
- try {
- const searchResult = await searchTpmjsToolsTool.execute({
- query: userQuery,
- limit: 5, // Get top 5 relevant tools
- }, {} as any);
-
- console.log(`📦 Found ${searchResult.matchCount} matching tools`);
-
- if (searchResult.tools && searchResult.tools.length > 0) {
- console.log(`🔧 Tools found:`, searchResult.tools.map((t: any) => `${t.packageName}/${t.exportName}`));
-
- // Dynamically load tools from esm.sh
- console.log(`📥 Loading ${searchResult.tools.length} tools dynamically...`);
-
- const toolsToLoad = searchResult.tools.map((meta: any) => ({
- packageName: meta.packageName,
- exportName: meta.exportName,
- version: meta.version,
- importUrl: meta.importUrl,
- }));
-
- try {
- // ❌ THIS IS WHERE IT FAILS
- const loadedTools = await loadToolsBatch(toolsToLoad);
- console.log(`✅ Successfully loaded ${Object.keys(loadedTools).length} tools`);
-
- // Add sanitized tools to conversation state
- for (const [key, tool] of Object.entries(loadedTools)) {
- const [pkg, exp] = key.split('::');
- const sanitizedKey = sanitizeToolName(`${pkg}-${exp}`);
- state.loadedTools[sanitizedKey] = tool;
- console.log(`✅ Added to conversation: ${sanitizedKey}`);
- }
-
- // Track for this conversation
- addConversationTools(conversationId, Object.keys(state.loadedTools));
- } catch (error) {
- console.error('❌ Error loading tools:', error);
- }
- } else {
- console.log('ℹ️ No matching tools found for this query');
- }
- } catch (error) {
- console.error('❌ Error searching for tools:', error);
- }
- }
-
- // 4. Merge with conversation's dynamically loaded tools
- const allTools: Record = { ...staticTools, ...state.loadedTools };
-
- // 5. Build system prompt with available tools
- const toolsList = Object.keys(allTools)
- .map((name) => {
- const tool = allTools[name] as { description?: string } | undefined;
- return `- ${name}: ${tool?.description || 'No description'}`;
- })
- .join('\n');
-
- const system = `You are a helpful AI assistant that can use TPMJS tools to help users.
-
-Available tools:
-${toolsList}
-
-When you use a tool, you MUST always follow up with a natural language answer to the user summarizing the result.`;
-
- // 6. Stream response with all available tools
- const result = streamText({
- model: openai('gpt-4o-mini'),
- system,
- messages: convertToModelMessages(messages),
- tools: allTools,
- stopWhen: stepCountIs(5),
- });
-
- return result.toUIMessageStreamResponse();
- } catch (error) {
- console.error('Chat API error:', error);
-
- return new Response(
- JSON.stringify({
- success: false,
- error: error instanceof Error ? error.message : 'Unknown error',
- }),
- {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- }
- );
- }
-}
-```
-
-### File 3: Example Tool Metadata (from search API)
-
-When we search for "firecrawl", the search API returns:
-
-```json
-{
- "success": true,
- "query": "firecrawl ajax davis",
- "results": {
- "total": 29,
- "returned": 5,
- "tools": [
- {
- "id": "cm4abc123",
- "exportName": "searchTool",
- "description": "Search the web using Firecrawl's search API",
- "qualityScore": 0.85,
- "package": {
- "npmPackageName": "firecrawl-aisdk",
- "npmVersion": "0.7.2",
- "category": "web-scraping",
- "frameworks": ["vercel-ai"],
- "env": "server"
- },
- "importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2",
- "cdnUrl": "https://cdn.jsdelivr.net/npm/firecrawl-aisdk@0.7.2/+esm"
- }
- ]
- }
-}
-```
-
-So we're trying to:
-```typescript
-const module = await import('https://esm.sh/firecrawl-aisdk@0.7.2');
-const tool = module.searchTool; // Get the exported tool
-```
-
----
-
-## The Error
-
-### Console Output
-
-```
-📥 Loading 5 tools dynamically...
-📦 Importing: https://esm.sh/firecrawl-aisdk@0.7.2
-❌ Failed to load firecrawl-aisdk#searchTool: Error: Cannot find module 'unknown'
- at (.next/dev/server/chunks/[root-of-the-server]__746deca2._.js:357:23)
- at loadToolDynamically (.next/dev/server/chunks/[root-of-the-server]__746deca2._.js:360:11)
- at (src/lib/dynamic-tool-loader.ts:108:5)
- at Array.map ()
- at loadToolsBatch (src/lib/dynamic-tool-loader.ts:107:33)
- at POST (src/app/api/chat/route.ts:104:53)
- {
- code: 'MODULE_NOT_FOUND'
- }
- URL: https://esm.sh/firecrawl-aisdk@0.7.2
- Stack: Error: Cannot find module 'unknown'
- at /Users/ajaxdavis/repos/tpmjs/tpmjs/apps/playground/.next/dev/server/chunks/[root-of-the-server]__746deca2._.js:357:23
- at loadToolDynamically (/Users/ajaxdavis/repos/tpmjs/tpmjs/apps/playground/.next/dev/server/chunks/[root-of-the-server]__746deca2._.js:360:11)
-```
-
-### Key Observations
-
-1. **Error happens immediately** - Never gets to our debug logs after `await import()`
-2. **Error is MODULE_NOT_FOUND** - Treating URL as a module path
-3. **Error says "unknown"** - Not even using the actual module name
-4. **Code is in .next/dev/server/chunks/** - Next.js/Turbopack transformed our code
-5. **Same error for all packages** - firecrawl-aisdk, @exalabs/ai-sdk, etc.
-
----
-
-## Verification: The URL Works
-
-### Manual Test 1: Browser
-
-```
-Visit: https://esm.sh/firecrawl-aisdk@0.7.2
-```
-
-Returns valid ESM module:
-```javascript
-/* esm.sh - firecrawl-aisdk@0.7.2 */
-import * as __1$ from "/v135/@ai-sdk/provider-utils@2.0.8/...";
-// ... rest of module code
-export { searchTool, scrapeTool, crawlTool };
-```
-
-### Manual Test 2: Plain Node.js Script
-
-Create `test-import.mjs`:
-```javascript
-const module = await import('https://esm.sh/firecrawl-aisdk@0.7.2');
-console.log('Module:', module);
-console.log('Exports:', Object.keys(module));
-```
-
-Run: `node test-import.mjs`
-
-**Expected**: Would work in plain Node.js with `--experimental-network-imports` flag
-**In Next.js**: Can't even get this far
-
----
-
-## What We've Tried
-
-### Attempt 1: `/* @vite-ignore */` Comment
-```typescript
-const module = await import(/* @vite-ignore */ url);
-```
-**Result**: Still fails with MODULE_NOT_FOUND
-
-### Attempt 2: `/* webpackIgnore: true */` Comment
-```typescript
-const module = await import(/* webpackIgnore: true */ url);
-```
-**Result**: Still fails with MODULE_NOT_FOUND
-
-### Attempt 3: Force Dynamic Runtime
-```typescript
-export const runtime = 'nodejs';
-export const dynamic = 'force-dynamic';
-```
-**Result**: Still fails (we're already using this)
-
-### Attempt 4: Verify esm.sh Works
-- Tested URLs in browser: ✅ Works
-- All packages return valid ESM: ✅ Valid
-- esm.sh is accessible: ✅ Reachable
-
-### Attempt 5: Check Static Imports
-```typescript
-import { helloWorldTool } from '@tpmjs/hello';
-```
-**Result**: Works perfectly (but bundled at build time)
-
----
-
-## Configuration Files
-
-### `apps/playground/next.config.ts`
-
-```typescript
-import type { NextConfig } from 'next';
-
-const config: NextConfig = {
- reactStrictMode: true,
- transpilePackages: ['@tpmjs/ui'],
- experimental: {
- turbo: {
- // Using Turbopack (Next.js 15+ default)
- },
- },
-};
-
-export default config;
-```
-
-### `apps/playground/package.json` (relevant parts)
-
-```json
-{
- "name": "@tpmjs/playground",
- "version": "0.0.0",
- "private": true,
- "scripts": {
- "dev": "next dev --port 3001",
- "build": "next build",
- "start": "next start"
- },
- "dependencies": {
- "@ai-sdk/openai": "^1.0.15",
- "@tpmjs/hello": "workspace:*",
- "@tpmjs/search-registry": "workspace:*",
- "ai": "6.0.0-beta.124",
- "next": "16.0.4",
- "react": "19.0.0"
- }
-}
-```
-
-### `turbo.json` (monorepo config)
-
-```json
-{
- "$schema": "https://turbo.build/schema.json",
- "tasks": {
- "dev": {
- "cache": false,
- "persistent": true
- },
- "build": {
- "dependsOn": ["^build"],
- "outputs": [".next/**", "dist/**"]
- }
- }
-}
-```
-
----
-
-## Why This Matters
-
-### The Bigger Picture
-
-We're building a **self-referential tool discovery system**:
-
-1. **Tool Registry** (tpmjs.com) - Indexes all TPMJS-compatible tools from npm
-2. **Search Tool** - AI SDK tool that searches the registry
-3. **Dynamic Loader** - Loads found tools at runtime
-4. **AI Agent** - Uses dynamically loaded tools
-
-This creates infinite extensibility:
-- No need to bundle all possible tools
-- Tools can be published to npm independently
-- System discovers and loads tools as needed
-- Bundle size stays small
-
-### Use Case Example
-
-```
-User: "Search Wikipedia for quantum computing"
- ↓
-System searches registry: Finds "wikipedia-aisdk" tool
- ↓
-System loads tool: import('https://esm.sh/wikipedia-aisdk@1.0.0')
- ↓
-AI uses tool: wikipediaSearchTool.execute({ query: "quantum computing" })
- ↓
-User gets answer with Wikipedia citations
-```
-
----
-
-## Possible Root Causes
-
-### Hypothesis 1: Turbopack Doesn't Support Dynamic Import URLs
-- Turbopack intercepts all `import()` calls
-- Transforms them to module resolution
-- Doesn't handle external URLs
-
-### Hypothesis 2: Next.js Security Restriction
-- Next.js blocks dynamic imports from external URLs for security
-- Prevents arbitrary code execution
-- No way to whitelist esm.sh
-
-### Hypothesis 3: Dev Mode Only Issue
-- Turbopack dev mode has more restrictions
-- Production webpack build might work
-- But we need dev mode to work too
-
-### Hypothesis 4: Node.js Runtime Limitation in Next.js
-- Next.js Node.js runtime is sandboxed
-- Dynamic imports are intercepted before reaching Node.js
-- Plain Node.js would work with --experimental-network-imports
-
----
-
-## Alternative Approaches We're Considering
-
-### Option A: Fetch + VM Module
-```typescript
-import { SourceTextModule } from 'vm';
-
-const response = await fetch(url);
-const code = await response.text();
-const module = new SourceTextModule(code);
-await module.link(() => {});
-await module.evaluate();
-const exports = module.namespace;
-```
-
-**Pros**: Bypasses import() entirely
-**Cons**: Complex, security concerns, might not work in Next.js
-
-### Option B: Separate Microservice
-```typescript
-// New service: tool-loader-service (Express or Fastify)
-POST /load-tool
-Body: { packageName, exportName, version }
-Response: { tool: }
-```
-
-**Pros**: Full control, definitely works
-**Cons**: Extra infrastructure, latency, complexity
-
-### Option C: Switch to Edge Runtime
-```typescript
-export const runtime = 'edge'; // Instead of 'nodejs'
-```
-
-**Pros**: Edge might have different import behavior
-**Cons**: Edge has limitations (no Node.js APIs), might still not work
-
-### Option D: Pre-bundle Common Tools
-```typescript
-// Generate static imports for top 100 tools
-import { tool1 } from 'package1';
-import { tool2 } from 'package2';
-// ... etc
-```
-
-**Pros**: Definitely works
-**Cons**: Defeats the purpose, huge bundle size
-
-### Option E: Use unpkg or jsdelivr with Different Strategy
-```typescript
-// Fetch raw code, eval in isolated context
-const response = await fetch(`https://unpkg.com/${pkg}@${ver}/dist/index.mjs`);
-const code = await response.text();
-const exports = evalInContext(code);
-```
-
-**Pros**: More control
-**Cons**: Same security/execution issues
-
----
-
-## Specific Questions for ChatGPT
-
-### Question 1: Is This Possible?
-**Can Next.js 16 App Router API routes (Node.js runtime) dynamically import ESM modules from external URLs using `import()`?**
-
-If yes:
-- What configuration is needed?
-- Are there security allowlists?
-- Does it work in both dev and production?
-
-If no:
-- Why not?
-- What's the recommended alternative?
-- Is this a Turbopack limitation or Next.js design?
-
-### Question 2: Turbopack Behavior
-**Does Turbopack intercept all `import()` calls, even with magic comments?**
-
-We've tried:
-- `/* @vite-ignore */`
-- `/* webpackIgnore: true */`
-
-None work. Is there a Turbopack-specific comment or config?
-
-### Question 3: Edge vs Node Runtime
-**Would switching to edge runtime change import behavior?**
-
-```typescript
-export const runtime = 'edge'; // vs 'nodejs'
-```
-
-Does edge runtime allow dynamic imports from URLs?
-
-### Question 4: Best Practice
-**What's the recommended way to implement dynamic tool loading in Next.js?**
-
-Given constraints:
-- Need to load arbitrary npm packages at runtime
-- Packages are ESM modules from CDN
-- Can't pre-bundle all possibilities
-- Need to work in production on Vercel
-
-### Question 5: Security Model
-**Is Next.js intentionally blocking this for security?**
-
-- Is there a whitelist for allowed CDNs?
-- Can we configure allowed import sources?
-- Is this related to CSP or other security headers?
-
----
-
-## Environment Details
-
-### Versions
-```json
-{
- "next": "16.0.4",
- "react": "19.0.0",
- "turbo": "2.6.1",
- "pnpm": "9.15.0",
- "node": "v20.11.0",
- "ai": "6.0.0-beta.124"
-}
-```
-
-### Operating System
-- **OS**: macOS (Darwin 23.5.0)
-- **Architecture**: arm64 (Apple Silicon)
-
-### Development Commands
-```bash
-# Start dev server
-pnpm dev --filter=@tpmjs/playground
-
-# Output
-▲ Next.js 16.0.4 (Turbopack)
-- Local: http://localhost:3001
-- Network: http://192.168.0.25:3001
-✓ Ready in 2.5s
-```
-
-### Build Output Structure
-```
-apps/playground/.next/
-├── dev/
-│ └── server/
-│ └── chunks/
-│ └── [root-of-the-server]__746deca2._.js # ← Error originates here
-```
-
----
-
-## Success Criteria
-
-### What We Need Working
-
-```typescript
-// In Next.js API route (Node.js runtime)
-const url = 'https://esm.sh/firecrawl-aisdk@0.7.2';
-const module = await import(url);
-const tool = module.searchTool;
-
-console.log(tool.description); // "Search the web using Firecrawl's search API"
-console.log(typeof tool.execute); // "function"
-
-// Tool is ready to use with AI SDK
-const result = await tool.execute({ query: "test" }, context);
-```
-
-### Acceptable Outcomes
-
-1. ✅ **Best**: Dynamic `import()` works with configuration change
-2. ✅ **Good**: Alternative approach that doesn't require microservice
-3. ✅ **Acceptable**: Workaround that works in production even if dev is tricky
-4. ❌ **Unacceptable**: "You can't do this in Next.js" without alternative
-
----
-
-## Additional Context
-
-### Why Not Just Bundle Everything?
-
-Currently have ~30 tools in registry, growing to 100s or 1000s:
-- Bundle size would be massive (10+ MB)
-- Most tools won't be used in most conversations
-- Tools are published independently by community
-- Want instant availability of new tools without redeploying
-
-### Why esm.sh Specifically?
-
-- ✅ Converts any npm package to ESM
-- ✅ Handles dependencies automatically
-- ✅ Fast CDN with caching
-- ✅ No build step required
-- ✅ Version pinning built-in
-
-But we're flexible - if jsdelivr, unpkg, or another approach works better, we'll use it.
-
-### Static Imports Work Fine
-
-This works perfectly (but defeats the purpose):
-```typescript
-import { searchTool } from 'firecrawl-aisdk';
-```
-
-The tools themselves are fine. We just can't load them dynamically.
-
----
-
-## What We're Hoping For
-
-### Ideal Answer Format
-
-1. **Root cause**: Why it's failing
-2. **Solution**: How to fix it (with code example)
-3. **Configuration**: Any Next.js config needed
-4. **Limitations**: What won't work / tradeoffs
-5. **Alternatives**: If dynamic import truly impossible
-
-### We're Happy to Try
-
-- Different CDN (unpkg, jsdelivr, etc.)
-- Different import strategy (fetch + eval, vm module, etc.)
-- Different runtime (edge if it works)
-- Different Next.js version (if specific version supports this)
-- Webpack instead of Turbopack (if webpack handles this better)
-
-We just need a path forward that enables runtime tool loading in a production Next.js app on Vercel.
-
----
-
-## Files to Reference
-
-All code is in this monorepo:
-- `apps/playground/src/lib/dynamic-tool-loader.ts` - Import logic
-- `apps/playground/src/app/api/chat/route.ts` - API route
-- `apps/playground/next.config.ts` - Next.js config
-- `DYNAMIC_IMPORT_ISSUE.md` - This document
-
----
-
-## Thank You
-
-This is a critical blocker for our dynamic tool loading system. Any insights, workarounds, or alternative approaches would be immensely helpful!
diff --git a/DYNAMIC_TOOL_LOADING_PRD.md b/DYNAMIC_TOOL_LOADING_PRD.md
deleted file mode 100644
index f777951..0000000
--- a/DYNAMIC_TOOL_LOADING_PRD.md
+++ /dev/null
@@ -1,988 +0,0 @@
-# Dynamic Tool Loading System - Product Requirements Document
-
-## Executive Summary
-
-Build a self-referential tool discovery system where AI agents can search the TPMJS registry, find relevant tools, and dynamically import them during conversation. This creates a "meta-tool" that makes the entire TPMJS ecosystem available to any agent at runtime.
-
-**Core Innovation:** An AI agent can discover and load tools on-demand by searching the registry, rather than having all tools pre-loaded. This enables infinite tool extensibility without bundle size concerns.
-
----
-
-## Problem Statement
-
-### Current Limitations
-
-1. **Static Tool Loading**: Playground requires all tools to be hardcoded in `tool-loader.ts`
-2. **Bundle Size**: Loading many tools increases bundle size and initialization time
-3. **Discovery Gap**: Agents can't discover new tools that match their current task
-4. **Manual Updates**: Adding tools requires code changes and redeployment
-
-### User Pain Points
-
-- Users want agents to access the full TPMJS registry without manual configuration
-- Developers want to publish tools that are immediately available to all agents
-- Agents need context-aware tool selection based on the conversation
-
----
-
-## Solution Overview
-
-### The Meta-Tool: `searchTpmjsTools`
-
-A TPMJS tool that searches the TPMJS registry and returns tool metadata needed for dynamic import.
-
-**Flow:**
-```
-User: "Search Wikipedia for quantum computing"
- ↓
-Agent: Calls searchTpmjsTools("wikipedia search")
- ↓
-API: Returns tools matching "wikipedia" (BM25 search)
- ↓
-Playground: Dynamically imports matching tools
- ↓
-Agent: Now has Wikipedia tools available, uses them
-```
-
-### Key Components
-
-1. **`@tpmjs/search-registry`** - NPM package exporting `searchTpmjsToolsTool`
-2. **`/api/tools/search`** - New API endpoint with BM25 full-text search
-3. **Playground Dynamic Loader** - Runtime tool import system
-4. **Tool Import Strategy** - ESM CDN imports or bundled approach
-
----
-
-## Technical Architecture
-
-### Component 1: Search Tool Package
-
-**Package:** `packages/tools/search-registry/`
-
-```typescript
-// packages/tools/search-registry/src/index.ts
-import { tool } from 'ai';
-import { z } from 'zod';
-
-export const searchTpmjsToolsTool = tool({
- description: 'Search the TPMJS tool registry to find AI SDK tools. Use this when you need a tool that isn\'t currently available. Returns tool metadata including package names and descriptions.',
- parameters: z.object({
- query: z.string().describe('Search query (e.g., "weather", "database", "wikipedia")'),
- category: z.enum([
- 'text-analysis',
- 'code-generation',
- 'data-processing',
- 'image-generation',
- 'audio-processing',
- 'search',
- 'integration',
- 'other'
- ]).optional().describe('Filter by tool category'),
- limit: z.number().min(1).max(20).default(10).describe('Max number of tools to return'),
- }),
- execute: async ({ query, category, limit }) => {
- // Call TPMJS search API
- const params = new URLSearchParams({
- q: query,
- limit: String(limit),
- ...(category && { category }),
- });
-
- const response = await fetch(
- `https://tpmjs.com/api/tools/search?${params}`
- );
-
- if (!response.ok) {
- throw new Error(`Search failed: ${response.statusText}`);
- }
-
- const data = await response.json();
-
- // Return structured tool metadata
- return {
- query,
- matchCount: data.tools.length,
- tools: data.tools.map((tool: any) => ({
- packageName: tool.package.npmPackageName,
- exportName: tool.exportName,
- description: tool.description,
- category: tool.package.category,
- qualityScore: tool.qualityScore,
- frameworks: tool.package.frameworks,
- env: tool.package.env,
- })),
- };
- },
-});
-```
-
-**Package Metadata:**
-
-```json
-{
- "name": "@tpmjs/search-registry",
- "version": "0.1.0",
- "description": "AI SDK tool for searching the TPMJS tool registry",
- "keywords": ["tpmjs-tool", "ai", "search"],
- "tpmjs": {
- "category": "search",
- "frameworks": ["vercel-ai"],
- "tools": [
- {
- "exportName": "searchTpmjsToolsTool",
- "description": "Search the TPMJS tool registry to find AI SDK tools by keyword, category, or description. Returns tool metadata for dynamic loading.",
- "parameters": [
- {
- "name": "query",
- "type": "string",
- "description": "Search query (keywords, tool names, descriptions)",
- "required": true
- },
- {
- "name": "category",
- "type": "string",
- "description": "Filter by category (text-analysis, search, etc.)",
- "required": false
- },
- {
- "name": "limit",
- "type": "number",
- "description": "Maximum number of results (1-20, default 10)",
- "required": false
- }
- ],
- "returns": {
- "type": "object",
- "description": "Search results with tool metadata for dynamic import"
- },
- "aiAgent": {
- "useCase": "Use this tool when you need a tool that isn't currently available. For example, if asked to search Wikipedia but you don't have a Wikipedia tool, search for 'wikipedia' to find and load it.",
- "examples": [
- "Search for 'weather' tools when asked about weather",
- "Search for 'database' tools when working with data",
- "Search for 'code' tools when generating code"
- ],
- "limitations": "Returns metadata only - the playground handles actual tool loading"
- }
- }
- ]
- }
-}
-```
-
----
-
-### Component 2: BM25 Search API Endpoint
-
-**File:** `apps/web/src/app/api/tools/search/route.ts`
-
-**Requirements:**
-
-1. **Full-Text Search with BM25**
- - Search across: tool description, package name, npm description, npm keywords
- - BM25 scoring for relevance ranking
- - Category filtering
- - Quality score boosting (rich tier tools rank higher)
-
-2. **Search Implementation Options**
-
- **Option A: PostgreSQL Full-Text Search**
- ```sql
- -- Add tsvector column to tools table
- ALTER TABLE tools ADD COLUMN search_vector tsvector;
-
- -- Create GIN index for fast full-text search
- CREATE INDEX tools_search_idx ON tools USING GIN(search_vector);
-
- -- Update search vector on insert/update
- CREATE TRIGGER tools_search_update
- BEFORE INSERT OR UPDATE ON tools
- FOR EACH ROW EXECUTE FUNCTION
- tsvector_update_trigger(search_vector, 'pg_catalog.english',
- description);
- ```
-
- **Option B: JavaScript BM25 Library**
- ```typescript
- import { BM25 } from 'bm25';
-
- // Load all tools into memory (cached)
- const tools = await prisma.tool.findMany({
- include: { package: true },
- });
-
- // Build BM25 index
- const documents = tools.map(tool => ({
- id: tool.id,
- text: `${tool.description} ${tool.package.npmPackageName} ${tool.package.npmDescription} ${tool.package.npmKeywords.join(' ')}`,
- }));
-
- const bm25 = new BM25(documents);
- const results = bm25.search(query);
- ```
-
- **Option C: Hybrid Approach**
- - Use PostgreSQL `LIKE` for exact matches (fastest)
- - Fall back to BM25 for fuzzy/semantic search
- - Cache search results in Redis
-
-3. **API Response Format**
-
-```typescript
-// GET /api/tools/search?q=weather&category=integration&limit=10
-
-{
- "success": true,
- "query": "weather",
- "filters": {
- "category": "integration"
- },
- "results": {
- "total": 23,
- "returned": 10,
- "tools": [
- {
- "id": "clx...",
- "exportName": "getWeatherTool",
- "description": "Get current weather data for any location using OpenWeatherMap API",
- "qualityScore": 0.85,
- "package": {
- "npmPackageName": "@tpmjs/weather",
- "npmVersion": "1.2.0",
- "category": "integration",
- "frameworks": ["vercel-ai"],
- "env": [
- {
- "name": "OPENWEATHER_API_KEY",
- "description": "OpenWeatherMap API key",
- "required": true
- }
- ],
- "npmRepository": {
- "type": "git",
- "url": "https://github.com/user/weather-tool"
- },
- "isOfficial": false
- },
- // Include everything needed for dynamic import
- "importUrl": "https://esm.sh/@tpmjs/weather@1.2.0",
- "cdnUrl": "https://cdn.jsdelivr.net/npm/@tpmjs/weather@1.2.0/+esm"
- }
- // ... more tools
- ]
- }
-}
-```
-
----
-
-### Component 3: Dynamic Tool Loader (Playground)
-
-**File:** `apps/playground/src/lib/dynamic-tool-loader.ts`
-
-**Requirements:**
-
-1. **Runtime ESM Import**
- ```typescript
- async function loadToolDynamically(
- packageName: string,
- exportName: string,
- version: string
- ) {
- // Option 1: ESM CDN (esm.sh, unpkg, jsdelivr)
- const cdnUrl = `https://esm.sh/${packageName}@${version}`;
-
- try {
- const module = await import(/* @vite-ignore */ cdnUrl);
- const tool = module[exportName];
-
- if (!isValidTool(tool)) {
- throw new Error(`Invalid tool: ${exportName}`);
- }
-
- return tool;
- } catch (error) {
- console.error(`Failed to load ${packageName}:`, error);
- return null;
- }
- }
- ```
-
-2. **Tool Caching Strategy**
- ```typescript
- // Cache loaded tools to avoid redundant imports
- const toolCache = new Map();
-
- function getCacheKey(packageName: string, exportName: string): string {
- return `${packageName}::${exportName}`;
- }
-
- async function loadToolWithCache(
- packageName: string,
- exportName: string,
- version: string
- ) {
- const key = getCacheKey(packageName, exportName);
-
- if (toolCache.has(key)) {
- return toolCache.get(key);
- }
-
- const tool = await loadToolDynamically(packageName, exportName, version);
-
- if (tool) {
- toolCache.set(key, tool);
- }
-
- return tool;
- }
- ```
-
-3. **Tool Registry Integration**
- ```typescript
- // Merge static tools + dynamically loaded tools
- async function getAllAvailableTools(
- staticTools: Record,
- searchResults: SearchResult[]
- ): Promise> {
- const allTools = { ...staticTools };
-
- // Load tools from search results
- for (const result of searchResults) {
- const tool = await loadToolWithCache(
- result.package.npmPackageName,
- result.exportName,
- result.package.npmVersion
- );
-
- if (tool) {
- const key = sanitizeToolName(
- `${result.package.npmPackageName}-${result.exportName}`
- );
- allTools[key] = tool;
- }
- }
-
- return allTools;
- }
- ```
-
----
-
-### Component 4: Playground Chat Integration
-
-**File:** `apps/playground/src/app/api/chat/route.ts`
-
-**Flow:**
-
-1. **Initial Tool Set**
- - Load static tools (hardcoded in tool-loader)
- - Always include `searchTpmjsToolsTool` in initial set
-
-2. **Agent Invokes Search**
- - Agent calls `searchTpmjsToolsTool` with query
- - Search API returns matching tool metadata
- - Response includes tool metadata
-
-3. **Dynamic Loading Trigger**
- - Detect when agent successfully calls `searchTpmjsToolsTool`
- - Extract tool metadata from response
- - Load tools dynamically before next agent turn
-
-4. **Tool Availability Update**
- - Merge dynamically loaded tools into available tool set
- - Agent can now use newly loaded tools in subsequent turns
-
-**Implementation:**
-
-```typescript
-// apps/playground/src/app/api/chat/route.ts
-export async function POST(req: Request) {
- const { messages } = await req.json();
-
- // 1. Load static tools + search tool
- let availableTools = await loadAllTools(); // static
- availableTools['searchTpmjsTools'] = searchTpmjsToolsTool; // meta-tool
-
- // 2. Create streamText with current tools
- const result = streamText({
- model: openai('gpt-4'),
- messages,
- tools: availableTools,
- maxSteps: 10, // Allow multiple tool call rounds
-
- onStepFinish: async (step) => {
- // 3. Check if agent called searchTpmjsToolsTool
- for (const toolCall of step.toolCalls) {
- if (toolCall.toolName === 'searchTpmjsTools') {
- const searchResults = toolCall.result?.tools || [];
-
- // 4. Dynamically load tools from search results
- console.log(`Loading ${searchResults.length} tools dynamically...`);
-
- for (const toolMeta of searchResults) {
- const tool = await loadToolWithCache(
- toolMeta.packageName,
- toolMeta.exportName,
- 'latest' // or toolMeta.version
- );
-
- if (tool) {
- const key = sanitizeToolName(
- `${toolMeta.packageName}-${toolMeta.exportName}`
- );
- availableTools[key] = tool;
- console.log(`✅ Loaded: ${key}`);
- }
- }
-
- // 5. Update tool registry for subsequent steps
- // Note: This requires AI SDK to support dynamic tool updates
- // May need to restart the streamText with updated tools
- }
- }
- },
- });
-
- return result.toDataStreamResponse();
-}
-```
-
----
-
-## Technical Challenges & Solutions
-
-### Challenge 1: AI SDK Doesn't Support Dynamic Tool Updates Mid-Stream
-
-**Problem:** Vercel AI SDK's `streamText` sets tools at initialization. Can't add tools after streaming starts.
-
-**Solutions:**
-
-**Option A: Multi-Turn Pattern**
-```typescript
-// Turn 1: Agent searches for tools
-// Turn 2: Agent uses loaded tools
-
-// Detect search tool call, return early
-if (hasSearchToolCall) {
- return new Response(JSON.stringify({
- type: 'tools_loaded',
- tools: searchResults,
- message: 'Tools loaded. Please continue your request.',
- }));
-}
-```
-
-**Option B: Pre-Flight Search (Recommended)**
-```typescript
-// Before calling streamText, analyze user message
-const needsTools = await analyzeMessageForToolNeeds(userMessage);
-
-if (needsTools.length > 0) {
- // Pre-load tools based on intent
- const searchResults = await searchTools(needsTools);
- const dynamicTools = await loadToolsFromResults(searchResults);
- availableTools = { ...staticTools, ...dynamicTools };
-}
-
-// Now call streamText with full tool set
-const result = streamText({
- model,
- messages,
- tools: availableTools,
-});
-```
-
-**Option C: Agent-Driven Two-Phase**
-```typescript
-// Phase 1: Planning
-const planResult = await generateText({
- model,
- messages: [
- { role: 'system', content: 'Analyze this request and determine what tools are needed. Call searchTpmjsTools if needed.' },
- ...messages,
- ],
- tools: { searchTpmjsTools },
-});
-
-// Phase 2: Execution with loaded tools
-const executionResult = await streamText({
- model,
- messages,
- tools: { ...staticTools, ...loadedTools },
-});
-```
-
----
-
-### Challenge 2: ESM Dynamic Import in Browser vs Node.js
-
-**Problem:** Dynamic `import()` works differently in browser vs server environments.
-
-**Solutions:**
-
-**Server-Side (Recommended):**
-```typescript
-// Use Node.js dynamic import
-// Works with esm.sh CDN
-const tool = await import(`https://esm.sh/${pkg}@${version}`);
-```
-
-**Client-Side (Avoid):**
-```typescript
-// Browser import() has CORS and CSP restrictions
-// Would require:
-// 1. CDN supports CORS
-// 2. CSP allows script-src from CDN
-// 3. Tools are browser-compatible (no Node.js APIs)
-```
-
-**Hybrid Approach:**
-```typescript
-// Load tools server-side, serialize to client
-// Client displays available tools
-// Server executes tool calls
-```
-
----
-
-### Challenge 3: Tool Dependencies & Environment Variables
-
-**Problem:** Dynamically loaded tools may require:
-- Environment variables (API keys)
-- npm dependencies not in bundle
-- Node.js-specific APIs
-
-**Solutions:**
-
-**Option A: Require Pre-Configuration**
-```typescript
-// Before loading, check if tool requirements are met
-async function canLoadTool(toolMeta: ToolMetadata): Promise {
- // Check required env vars
- for (const env of toolMeta.package.env || []) {
- if (env.required && !process.env[env.name]) {
- console.warn(`Missing required env: ${env.name}`);
- return false;
- }
- }
-
- return true;
-}
-```
-
-**Option B: Graceful Degradation**
-```typescript
-// Load tool, catch errors, inform agent
-try {
- const tool = await loadTool(packageName, exportName);
- return tool;
-} catch (error) {
- return createStubTool(packageName, exportName, error);
-}
-
-function createStubTool(pkg: string, exp: string, error: Error) {
- return tool({
- description: `[UNAVAILABLE] ${exp} from ${pkg}: ${error.message}`,
- parameters: z.object({}),
- execute: async () => {
- throw new Error(`Cannot execute ${exp}: ${error.message}`);
- },
- });
-}
-```
-
-**Option C: Proxy Through Server**
-```typescript
-// All tools execute server-side where env vars exist
-// Client just displays tool calls, server handles execution
-```
-
----
-
-### Challenge 4: Security & Sandboxing
-
-**Problem:** Dynamically importing arbitrary npm packages is a security risk.
-
-**Solutions:**
-
-**Option A: Allowlist Only**
-```typescript
-// Only load tools from TPMJS registry (already vetted)
-const allowedPackages = await prisma.package.findMany({
- select: { npmPackageName: true }
-});
-
-if (!allowedPackages.includes(packageName)) {
- throw new Error('Package not in TPMJS registry');
-}
-```
-
-**Option B: Version Pinning**
-```typescript
-// Only load specific versions from registry
-// Don't use 'latest' to avoid supply chain attacks
-const version = toolMeta.package.npmVersion; // e.g., "1.2.0"
-const url = `https://esm.sh/${pkg}@${version}`;
-```
-
-**Option C: VM Sandbox (Advanced)**
-```typescript
-// Execute tools in isolated VM context
-import { VM } from 'vm2';
-
-const vm = new VM({
- timeout: 5000,
- sandbox: {
- fetch: safeFetch, // Wrapped fetch with rate limits
- console: safeConsole,
- },
-});
-
-const tool = vm.run(toolCode);
-```
-
----
-
-### Challenge 5: Performance & Bundle Size
-
-**Problem:** Loading many tools dynamically could be slow.
-
-**Solutions:**
-
-**Option A: Lazy Loading**
-```typescript
-// Only load tools when agent decides to use them
-// Not when they're discovered
-```
-
-**Option B: Parallel Loading**
-```typescript
-// Load multiple tools concurrently
-const toolPromises = searchResults.map(result =>
- loadToolWithCache(result.package.npmPackageName, result.exportName, result.package.npmVersion)
-);
-
-const tools = await Promise.all(toolPromises);
-```
-
-**Option C: CDN Caching**
-```typescript
-// Use CDN with aggressive caching
-// esm.sh has built-in caching
-const url = `https://esm.sh/${pkg}@${version}?target=es2022&bundle`;
-```
-
----
-
-## Implementation Plan
-
-### Phase 1: MVP (Week 1-2)
-
-**Goal:** Prove dynamic loading works with simple prototype
-
-1. **Create `@tpmjs/search-registry` package**
- - Implement `searchTpmjsToolsTool`
- - Publish to npm
- - Add to manual-tools registry
-
-2. **Build `/api/tools/search` endpoint**
- - Start with simple PostgreSQL `LIKE` search
- - Return tool metadata with package info
- - Test with curl
-
-3. **Implement basic dynamic loader**
- - Use esm.sh CDN for imports
- - Load tools server-side only
- - Cache in memory
-
-4. **Playground integration - Two-Turn Pattern**
- - User asks question
- - Agent calls `searchTpmjsToolsTool`
- - Backend loads tools
- - Agent uses tools in next turn
-
-**Success Criteria:**
-- Agent can search registry
-- Agent can use dynamically loaded tools
-- End-to-end flow works for 1-2 example tools
-
----
-
-### Phase 2: BM25 Search (Week 3)
-
-**Goal:** Improve search relevance with BM25
-
-1. **Research BM25 implementation options**
- - Test PostgreSQL full-text search
- - Test JavaScript BM25 libraries
- - Benchmark performance
-
-2. **Implement chosen approach**
- - Add search vector column if using PostgreSQL
- - Create search index
- - Update search endpoint
-
-3. **Test search quality**
- - Create test queries
- - Measure precision/recall
- - Compare to baseline `LIKE` search
-
-**Success Criteria:**
-- BM25 search returns more relevant results than LIKE
-- Search latency < 100ms for 95th percentile
-- Agent can find tools for diverse queries
-
----
-
-### Phase 3: Production Hardening (Week 4)
-
-**Goal:** Make system production-ready
-
-1. **Error Handling**
- - Handle import failures gracefully
- - Validate tool schemas
- - Return helpful error messages to agent
-
-2. **Security**
- - Implement package allowlist
- - Pin versions from registry
- - Add rate limiting to search API
-
-3. **Performance**
- - Implement Redis caching for search results
- - Add CDN caching headers
- - Optimize tool loading parallelism
-
-4. **Monitoring**
- - Log all dynamic tool loads
- - Track search queries and results
- - Monitor import success/failure rates
-
-**Success Criteria:**
-- System handles errors without crashing
-- Security review passes
-- Latency and reliability SLOs met
-
----
-
-### Phase 4: Advanced Features (Week 5+)
-
-**Goal:** Enhance UX and capabilities
-
-1. **Pre-flight Search**
- - Analyze user message for intent
- - Proactively load tools before agent call
- - Reduce total turns needed
-
-2. **Tool Recommendations**
- - "You might also need..." suggestions
- - Based on tool co-occurrence data
- - Help agent discover related tools
-
-3. **Client-Side Tool Display**
- - Show which tools are available
- - Indicate dynamically loaded tools
- - Allow user to manually load tools
-
-4. **Tool Versioning**
- - Support multiple versions of same tool
- - Let agent choose version
- - Handle breaking changes gracefully
-
----
-
-## Success Metrics
-
-### Technical Metrics
-
-1. **Search Quality**
- - Precision@10 > 0.8 (80% of top 10 results are relevant)
- - Mean Reciprocal Rank (MRR) > 0.7
- - Search latency p95 < 100ms
-
-2. **Tool Loading**
- - Import success rate > 95%
- - Tool load time p95 < 2 seconds
- - Cache hit rate > 70% after warmup
-
-3. **End-to-End Performance**
- - Total conversation latency < 5 seconds (including tool search + load + execution)
- - Agent uses correct tools > 90% of time
-
-### User Metrics
-
-1. **Adoption**
- - % of playground sessions using dynamic tools > 30%
- - Number of unique tools loaded dynamically per week > 50
-
-2. **Tool Coverage**
- - % of user queries satisfied with available tools > 80%
- - Tool search leading to successful task completion > 70%
-
----
-
-## Open Questions
-
-### 1. CDN Choice for ESM Imports
-
-**Options:**
-- **esm.sh** - Purpose-built for ESM imports, fast, reliable
-- **unpkg** - Popular, simple, but slower
-- **jsdelivr** - Fast CDN, good for production
-- **Custom bundler** - Pre-bundle tools, serve from our CDN
-
-**Recommendation:** Start with esm.sh for MVP, evaluate custom bundler for production.
-
----
-
-### 2. When to Load Tools?
-
-**Options:**
-- **On-demand**: Load when agent calls search tool (current plan)
-- **Pre-flight**: Analyze user message, load proactively
-- **Lazy**: Load when agent tries to use tool (not when discovered)
-- **Eager**: Load all tools from search results immediately
-
-**Recommendation:** Start with on-demand (Phase 1), add pre-flight in Phase 4.
-
----
-
-### 3. How to Handle Environment Variables?
-
-**Problem:** Dynamically loaded tools may need API keys (e.g., OpenWeather API).
-
-**Options:**
-- **User provides**: UI for users to enter API keys (like playground settings)
-- **Server-managed**: Admin pre-configures keys in .env
-- **Graceful fail**: Load tool, but execution fails if env missing
-- **Hybrid**: Some tools work without keys (free tier), others require keys
-
-**Recommendation:** Start with graceful fail (Phase 1), add user-provided keys (Phase 4).
-
----
-
-### 4. Should Tools Load Client-Side or Server-Side?
-
-**Client-Side Pros:**
-- Reduces server load
-- Faster for subsequent uses
-- Better for browser-compatible tools
-
-**Client-Side Cons:**
-- Requires CORS-enabled CDN
-- CSP restrictions
-- Many tools need Node.js APIs
-- Exposing API keys in browser is insecure
-
-**Server-Side Pros:**
-- Access to Node.js APIs
-- Secure environment variable access
-- No CORS issues
-- Easier to implement
-
-**Server-Side Cons:**
-- Requires server memory for caching
-- Increases server load
-- Cold starts for new tools
-
-**Recommendation:** Server-side for MVP (Phase 1), evaluate client-side for browser-compatible tools (Phase 4+).
-
----
-
-### 5. How to Handle Tool Dependencies?
-
-**Problem:** Some tools depend on other npm packages (e.g., `axios`, `cheerio`).
-
-**Options:**
-- **Bundled**: CDN bundles dependencies (esm.sh does this)
-- **Peer deps**: Require dependencies in playground package.json
-- **Dynamic install**: npm install on-the-fly (slow, risky)
-- **Pre-vetted**: Only allow tools with no/minimal dependencies
-
-**Recommendation:** Use esm.sh bundling (Phase 1), bundle size limits if issues arise.
-
----
-
-## Risk Assessment
-
-### High Risk
-
-1. **Security Vulnerability**
- - **Risk**: Malicious package in registry executes code
- - **Mitigation**: Allowlist registry packages, version pinning, VM sandboxing
- - **Owner**: Security team
-
-2. **Performance Degradation**
- - **Risk**: Loading many tools causes timeout/slow response
- - **Mitigation**: Parallel loading, caching, lazy loading, timeouts
- - **Owner**: Backend team
-
-### Medium Risk
-
-3. **Import Failures**
- - **Risk**: CDN down, package incompatible, missing dependencies
- - **Mitigation**: Fallback CDNs, error handling, stub tools
- - **Owner**: Frontend team
-
-4. **AI SDK Limitations**
- - **Risk**: Can't dynamically update tools mid-stream
- - **Mitigation**: Two-turn pattern, pre-flight search
- - **Owner**: AI team
-
-### Low Risk
-
-5. **Search Quality**
- - **Risk**: BM25 doesn't return relevant tools
- - **Mitigation**: A/B test search algorithms, collect feedback
- - **Owner**: Search team
-
----
-
-## Future Enhancements
-
-### 1. Tool Composition
-- Agent can combine multiple tools
-- Example: `searchTool` + `summarizeTool` = search and summarize
-
-### 2. Tool Learning
-- Track which tools are used together
-- Recommend tool combinations
-- "Users who used X also used Y"
-
-### 3. Custom Tool Registry
-- Users can add private tools
-- Organization-specific tool registry
-- Access control and permissions
-
-### 4. Tool Marketplace
-- Developers promote their tools
-- Usage analytics and ratings
-- Paid/premium tools
-
-### 5. Agent Templates
-- Pre-configured agents with tool sets
-- "Research Agent" has search + summarize tools
-- "Code Agent" has code generation tools
-
----
-
-## Conclusion
-
-This dynamic tool loading system represents a paradigm shift in how AI agents discover and use tools. By making the TPMJS registry itself searchable, we enable infinite extensibility without the limitations of static bundling.
-
-**Key Innovation:** Self-referential tool discovery - a tool that searches for tools.
-
-**Next Steps:**
-1. Review this PRD with team
-2. Validate technical feasibility with ChatGPT/Claude
-3. Spike on BM25 search implementation
-4. Spike on dynamic ESM import
-5. Begin Phase 1 implementation
-
-**Success Looks Like:**
-- User: "Search Wikipedia for quantum computing"
-- Agent: *searches registry, finds Wikipedia tool, loads it, uses it*
-- User: Gets Wikipedia results without any manual tool configuration
-
-This is a novel approach that could define how AI agents discover and use tools. Let's build it. 🚀
diff --git a/ENV_VAR_TRANSPORT_ISSUE.md b/ENV_VAR_TRANSPORT_ISSUE.md
deleted file mode 100644
index 3a04693..0000000
--- a/ENV_VAR_TRANSPORT_ISSUE.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Environment Variables Not Sent to API - Frontend Transport Issue
-
-## Problem
-
-Environment variables saved in localStorage are NOT being sent to `/api/chat` endpoint.
-
-**Evidence from logs:**
-```
-📥 Request body: {
- "conversationId": "7xur5hf1GDSOQMgFYF-l7",
- "env": {}, // ❌ EMPTY - should have FIRECRAWL_API_KEY
- ...
-}
-```
-
-## Root Cause
-
-The issue is in `apps/playground/src/hooks/useChat.ts`:
-
-```typescript
-export function useChat() {
- const [conversationId] = useState(() => nanoid());
- const envVars = useEnvVars(); // ❌ Empty on first render (useEffect loads async)
-
- const envObject = envVars.reduce(
- (acc, { key, value }) => {
- acc[key] = value;
- return acc;
- },
- {} as Record
- );
-
- const chat = useAISDKChat({
- transport: new DefaultChatTransport({ // ❌ Created ONCE with empty envObject
- api: '/api/chat',
- body: {
- conversationId,
- env: envObject, // ❌ This is {} on first render, never updates
- },
- }),
- });
-
- return { ...chat, conversationId };
-}
-```
-
-**Why it fails:**
-
-1. `useEnvVars()` loads from localStorage inside a `useEffect` (async)
-2. On first render, `envVars = []`, so `envObject = {}`
-3. `DefaultChatTransport` is created with `body: { env: {} }`
-4. Even when `envVars` updates later, the transport is already created and doesn't re-create
-
-## Attempted Solutions That Don't Work
-
-❌ **Just updating state** - Transport is created once and cached
-❌ **Using useEffect** - Transport is already created before effect runs
-
-## What We Need
-
-The `body` field in `DefaultChatTransport` needs to be **dynamic** and read the latest env vars on each request, not just once during component mount.
-
-## Questions for ChatGPT
-
-1. **How do we make `DefaultChatTransport` body dynamic?** Can we pass a function instead of an object?
-
-2. **Does AI SDK have a way to update transport body between messages?** The env vars might change while the chat is open.
-
-3. **Should we use a custom transport instead?** Can we implement our own transport that reads env vars fresh on each request?
-
-4. **Alternative: Can we manually add env to each message?** Is there a way to inject extra data per-request instead of per-transport?
-
-## Current Code Files
-
-- `apps/playground/src/hooks/useChat.ts` - The broken hook
-- `apps/playground/src/components/sidebar/SettingsSidebar.tsx` - Where env vars are stored (works fine)
-- `apps/playground/src/app/api/chat/route.ts` - Server expects `body.env` but receives `{}`
-
-## What We Know Works
-
-✅ Saving env vars to localStorage - working
-✅ Reading env vars from localStorage - working
-✅ Server accepting and using env vars - working
-❌ **Sending env vars from client to server - BROKEN**
-
-The ONLY broken part is the transport not sending the latest env object.
diff --git a/IMPLEMENTATION_CHECKLIST.md b/IMPLEMENTATION_CHECKLIST.md
deleted file mode 100644
index f5afd62..0000000
--- a/IMPLEMENTATION_CHECKLIST.md
+++ /dev/null
@@ -1,782 +0,0 @@
-# TPMJS NPM Registry - Implementation Checklist
-
-> **Reference:** See [NPM_MIRROR.md](./NPM_MIRROR.md) for complete architecture details
-
-**Stack Decision:** Vercel + Neon + Vercel Cron (polling-based sync)
-
----
-
-## 🎯 Implementation Strategy
-
-### Architecture Simplification
-
-**Original Plan (NPM_MIRROR.md):**
-- Separate Node.js sync service with persistent changes feed connection
-- Self-hosted PostgreSQL
-- More complex deployment
-
-**Revised Plan (This Checklist):**
-- All-in-one Next.js app on Vercel
-- Neon Postgres (serverless)
-- Vercel Cron for sync jobs (polling-based, no persistent connections)
-- Simpler, faster to ship
-
-### Why This Approach?
-
-✅ **Simpler Infrastructure**
-- One deployment (Vercel)
-- Managed database (Neon)
-- Built-in cron (Vercel Cron)
-
-✅ **Lower Cost**
-- No separate sync service hosting
-- Neon free tier generous
-- Vercel free/hobby tier sufficient for MVP
-
-✅ **Same Functionality**
-- Poll NPM changes feed every 1-2 minutes (effectively real-time)
-- All discovery features from NPM_MIRROR.md maintained
-- Quality scoring, validation, etc. all work the same
-
----
-
-## 📋 Phase 1: Foundation (Week 1)
-
-**Goal:** Set up database, types, and NPM client
-
-### 1.1 Database Setup
-
-- [ ] **Create Neon project**
- - Go to https://neon.tech/
- - Create new project
- - Save connection string
-
-- [ ] **Create `packages/db` package**
- ```bash
- mkdir -p packages/db
- cd packages/db
- pnpm init
- pnpm add prisma @prisma/client
- pnpm add -D typescript @types/node
- ```
-
-- [ ] **Initialize Prisma**
- ```bash
- npx prisma init
- ```
-
-- [ ] **Create Prisma schema**
- - Copy schema from NPM_MIRROR.md Database Design section
- - File: `packages/db/prisma/schema.prisma`
- - Include all three models: `Tool`, `SyncCheckpoint`, `SyncLog`
-
-- [ ] **Add database URL to `.env`**
- ```env
- DATABASE_URL="postgresql://..."
- ```
-
-- [ ] **Run first migration**
- ```bash
- npx prisma migrate dev --name init
- npx prisma generate
- ```
-
-- [ ] **Create Prisma client singleton**
- - File: `packages/db/src/client.ts`
- ```typescript
- import { PrismaClient } from '@prisma/client';
-
- const globalForPrisma = globalThis as unknown as {
- prisma: PrismaClient | undefined;
- };
-
- export const prisma = globalForPrisma.prisma ?? new PrismaClient();
-
- if (process.env.NODE_ENV !== 'production') {
- globalForPrisma.prisma = prisma;
- }
- ```
-
-- [ ] **Export from package**
- - File: `packages/db/src/index.ts`
- ```typescript
- export { prisma } from './client';
- export * from '@prisma/client';
- ```
-
-- [ ] **Update package.json**
- ```json
- {
- "name": "@tpmjs/db",
- "main": "./src/index.ts",
- "types": "./src/index.ts"
- }
- ```
-
-- [ ] **Seed initial sync state**
- ```sql
- INSERT INTO sync_checkpoints (source, checkpoint)
- VALUES
- ('changes-feed', '{"sequence": 0}'::jsonb),
- ('keyword-search', '{"lastRun": null}'::jsonb),
- ('metrics', '{"lastRun": null}'::jsonb)
- ON CONFLICT (source) DO NOTHING;
- ```
-
-**Verification:**
-```bash
-cd packages/db
-npx prisma studio # Should open DB browser with empty tables
-```
-
----
-
-### 1.2 Types Package
-
-**Reference:** See NPM_MIRROR.md "The 'tpmjs' Field Schema" section
-
-- [ ] **Update `packages/types/src/tool.ts`**
- - Add `TpmjsMinimalSchema` with Zod
- - Add `TpmjsRichSchema` extending minimal
- - Export both schemas and inferred types
-
-- [ ] **Create validation helper**
- - File: `packages/types/src/validator.ts`
- ```typescript
- export function validateTpmjsField(tpmjs: unknown): {
- valid: boolean;
- tier: 'minimal' | 'rich' | null;
- data?: unknown;
- errors?: ZodError[];
- }
- ```
-
-- [ ] **Update exports**
- - File: `packages/types/src/index.ts`
- - Export all schemas and validators
-
-**Verification:**
-```typescript
-import { validateTpmjsField } from '@tpmjs/types';
-
-const result = validateTpmjsField({
- category: 'web-scraping',
- description: 'Test description that is long enough',
- example: 'const x = await tool.test()'
-});
-
-console.log(result); // Should be { valid: true, tier: 'minimal', ... }
-```
-
----
-
-### 1.3 NPM Client Package
-
-**Reference:** See NPM_MIRROR.md "NPM Integration Strategy" section
-
-- [ ] **Create `packages/npm-client`**
- ```bash
- mkdir -p packages/npm-client/src
- cd packages/npm-client
- pnpm init
- pnpm add zod
- pnpm add -D typescript @types/node
- ```
-
-- [ ] **Implement changes feed client**
- - File: `packages/npm-client/src/changes.ts`
- ```typescript
- export async function fetchChanges(since: string, limit = 100): Promise<{
- results: Array<{ id: string; seq: string }>;
- lastSeq: string;
- }>
- ```
- - Use endpoint: `https://replicate.npmjs.com/registry/_changes`
- - Poll-based (no EventSource needed)
-
-- [ ] **Implement keyword search**
- - File: `packages/npm-client/src/search.ts`
- ```typescript
- export async function searchByKeyword(
- keyword: string,
- size = 250,
- from = 0
- ): Promise>
- ```
- - Use endpoint: `/-/v1/search?text=keywords:${keyword}`
-
-- [ ] **Implement package metadata fetcher**
- - File: `packages/npm-client/src/package.ts`
- ```typescript
- export async function fetchPackageMetadata(packageName: string): Promise<{
- name: string;
- 'dist-tags': { latest: string };
- versions: Record;
- time: Record;
- } | null>
- ```
- - Use endpoint: `https://registry.npmjs.org/${packageName}`
-
-- [ ] **Implement download stats**
- - File: `packages/npm-client/src/stats.ts`
- ```typescript
- export async function fetchDownloadStats(
- packageName: string
- ): Promise
- ```
- - Use endpoint: `https://api.npmjs.org/downloads/point/last-month/${packageName}`
-
-- [ ] **Implement GitHub stats** (optional Phase 4)
- - File: `packages/npm-client/src/github.ts`
- ```typescript
- export async function fetchGithubStars(
- repoUrl: string
- ): Promise
- ```
-
-- [ ] **Add rate limiting helper**
- - File: `packages/npm-client/src/rate-limiter.ts`
- - Simple delay between requests
- - Exponential backoff on 429
-
-- [ ] **Export all functions**
- - File: `packages/npm-client/src/index.ts`
-
-**Verification:**
-```typescript
-import { fetchPackageMetadata } from '@tpmjs/npm-client';
-
-const pkg = await fetchPackageMetadata('express');
-console.log(pkg?.name); // Should print 'express'
-```
-
----
-
-## 📋 Phase 2: Core API Routes (Week 2)
-
-**Goal:** Build public API for searching/listing tools
-
-### 2.1 Tool Search/List API
-
-**Reference:** See NPM_MIRROR.md "API Routes" section
-
-- [ ] **Create `apps/web/src/app/api/tools/route.ts`**
- - Implement `GET` handler
- - Query params: `q`, `category`, `official`, `limit`, `offset`
- - Use Prisma to query `tools` table
- - Return paginated results with metadata
-
-- [ ] **Add full-text search**
- - Use Postgres `ts_vector` for search
- - Or simple `ILIKE` for MVP
- - Search across: `npmPackageName`, `description`, `tags`
-
-- [ ] **Add filtering**
- - By `category`
- - By `isOfficial`
- - By `tier` (optional)
-
-- [ ] **Add sorting**
- - Default: `qualityScore DESC`, `npmDownloadsLastMonth DESC`
- - Optional: `createdAt DESC`, `npmPackageName ASC`
-
-**Verification:**
-```bash
-curl "http://localhost:3001/api/tools?q=web&limit=5"
-# Should return JSON with tools array and pagination
-```
-
----
-
-### 2.2 Tool Detail API
-
-- [ ] **Create `apps/web/src/app/api/tools/[id]/route.ts`**
- - Implement `GET` handler
- - Accept ID or package name
- - Return full tool details
-
-**Verification:**
-```bash
-curl "http://localhost:3001/api/tools/1"
-# Should return single tool object
-```
-
----
-
-### 2.3 Validation API
-
-- [ ] **Create `apps/web/src/app/api/tools/validate/route.ts`**
- - Implement `POST` handler
- - Accept JSON body with `tpmjs` field
- - Use `@tpmjs/types` validator
- - Return validation result with errors
-
-**Verification:**
-```bash
-curl -X POST http://localhost:3001/api/tools/validate \
- -H "Content-Type: application/json" \
- -d '{"category":"web-scraping","description":"Test tool for validation","example":"const x = await tool.test()"}'
-# Should return { valid: true, tier: "minimal" }
-```
-
----
-
-### 2.4 Stats API
-
-- [ ] **Create `apps/web/src/app/api/stats/route.ts`**
- - Implement `GET` handler
- - Aggregate counts by category
- - Total tools, official tools, etc.
-
-**Verification:**
-```bash
-curl "http://localhost:3001/api/stats"
-# Should return { totalTools: 0, officialTools: 0, categories: {} }
-```
-
----
-
-## 📋 Phase 3: Sync Workers (Week 2-3)
-
-**Goal:** Implement automatic NPM package discovery
-
-**Reference:** See NPM_MIRROR.md "NPM Integration Strategy" section
-
-### 3.1 Changes Feed Sync
-
-- [ ] **Create `apps/web/src/app/api/sync/changes/route.ts`**
-
-- [ ] **Implement POST handler**
- ```typescript
- export async function POST(request: Request) {
- // 1. Verify CRON_SECRET header
- // 2. Get last sequence from sync_checkpoints
- // 3. Fetch changes from NPM (limit 100-500)
- // 4. For each change:
- // - Fetch package metadata
- // - Check for tpmjs field
- // - Validate with @tpmjs/types
- // - Upsert to tools table
- // - Log to sync_logs
- // 5. Update checkpoint with new sequence
- // 6. Return summary (processed, skipped, errors)
- }
- ```
-
-- [ ] **Add secret protection**
- ```typescript
- const secret = request.headers.get('x-cron-secret');
- if (secret !== process.env.CRON_SECRET) {
- return new Response('Unauthorized', { status: 401 });
- }
- ```
-
-- [ ] **Add timeout protection**
- - Limit processing to 50 packages per run
- - Or 50 seconds max execution time
- - Save checkpoint frequently
-
-- [ ] **Add error handling**
- - Try/catch around each package
- - Log errors to `sync_logs`
- - Continue processing other packages
-
-**Verification:**
-```bash
-curl -X POST http://localhost:3001/api/sync/changes \
- -H "x-cron-secret: your-secret"
-# Should process changes and return summary
-```
-
----
-
-### 3.2 Keyword Search Sync
-
-- [ ] **Create `apps/web/src/app/api/sync/keyword/route.ts`**
-
-- [ ] **Implement POST handler**
- ```typescript
- export async function POST(request: Request) {
- // 1. Verify CRON_SECRET header
- // 2. Search NPM for keyword 'tpmjs-tool'
- // 3. For each result:
- // - Fetch package metadata
- // - Validate tpmjs field
- // - Upsert with isOfficial=true
- // - Log to sync_logs
- // 4. Update checkpoint
- // 5. Return summary
- }
- ```
-
-- [ ] **Handle pagination**
- - NPM allows `size` up to 250
- - May need multiple requests for all results
-
-**Verification:**
-```bash
-curl -X POST http://localhost:3001/api/sync/keyword \
- -H "x-cron-secret: your-secret"
-# Should search and process keyword packages
-```
-
----
-
-### 3.3 Metrics Sync (Phase 4)
-
-- [ ] **Create `apps/web/src/app/api/sync/metrics/route.ts`**
-
-- [ ] **Implement POST handler**
- ```typescript
- export async function POST(request: Request) {
- // 1. Verify CRON_SECRET
- // 2. Select tools to update (recent, popular, or sample)
- // 3. For each tool:
- // - Fetch NPM download stats
- // - Fetch GitHub stars (if repo exists)
- // - Calculate quality score
- // - Update tools table
- // 4. Update checkpoint
- // 5. Return summary
- }
- ```
-
-**Verification:**
-```bash
-curl -X POST http://localhost:3001/api/sync/metrics \
- -H "x-cron-secret: your-secret"
-# Should update metrics for tools
-```
-
----
-
-### 3.4 Vercel Cron Configuration
-
-- [ ] **Add to `vercel.json`**
- ```json
- {
- "crons": [
- {
- "path": "/api/sync/changes",
- "schedule": "*/2 * * * *"
- },
- {
- "path": "/api/sync/keyword",
- "schedule": "*/15 * * * *"
- },
- {
- "path": "/api/sync/metrics",
- "schedule": "0 * * * *"
- }
- ]
- }
- ```
-
-- [ ] **Set up environment variables in Vercel**
- - `DATABASE_URL` - Neon connection string
- - `CRON_SECRET` - Generate random secret
- - `NPM_REGISTRY_URL` - https://registry.npmjs.org
- - `NPM_CHANGES_URL` - https://replicate.npmjs.com/registry
-
----
-
-## 📋 Phase 4: Frontend Integration (Week 3)
-
-**Goal:** Replace mock data with real API calls
-
-### 4.1 Update Tool Listing Page
-
-- [ ] **Update `apps/web/src/app/tools/page.tsx`**
- - Remove mock data import
- - Fetch from `/api/tools`
- - Add loading state
- - Add error handling
-
-- [ ] **Add search functionality**
- - Search input component
- - Debounced API calls
- - Update URL with search params
-
-- [ ] **Add category filter**
- - Category dropdown/pills
- - Filter API calls by category
-
-- [ ] **Add pagination**
- - Next/previous buttons
- - Or infinite scroll
-
-**Verification:**
-- Visit http://localhost:3001/tools
-- Should show real tools from database
-- Search should work
-- Filters should work
-
----
-
-### 4.2 Update Tool Detail Page
-
-- [ ] **Update `apps/web/src/app/tools/[id]/page.tsx`**
- - Fetch from `/api/tools/[id]`
- - Display all tool metadata
- - Show rich tier fields if available
-
-- [ ] **Add install instructions**
- - npm install command
- - Usage example from `tpmjs.example`
-
-- [ ] **Add links**
- - NPM package page
- - GitHub repository
- - Documentation
- - Playground (if available)
-
-**Verification:**
-- Visit http://localhost:3001/tools/some-package
-- Should show full tool details
-
----
-
-### 4.3 Update Homepage
-
-**Reference:** See NPM_MIRROR.md for stats display
-
-- [ ] **Update stats in hero section**
- - Fetch from `/api/stats`
- - Show real tool count
- - Show category breakdown
-
-- [ ] **Update live metrics**
- - Real download counts
- - Real tool counts
- - Update frequently (client-side polling or static)
-
-**Verification:**
-- Visit http://localhost:3001
-- Stats should be real, not mock
-
----
-
-## 📋 Phase 5: Testing & Polish (Week 4)
-
-### 5.1 Create Test Packages
-
-- [ ] **Publish 3-5 real NPM packages with `tpmjs` field**
- - At least one with minimal tier
- - At least one with rich tier
- - Use `tpmjs-tool` keyword for official listing
-
-- [ ] **Verify automatic discovery**
- - Wait for next sync run
- - Check they appear in database
- - Check they appear on website
-
----
-
-### 5.2 Documentation
-
-- [ ] **Create docs section**
- - `apps/web/src/app/docs/page.tsx`
- - Getting started guide
- - Schema reference
- - Examples
-
-- [ ] **Add validation playground**
- - `apps/web/src/app/docs/validate/page.tsx`
- - Form to test `tpmjs` field
- - Real-time validation feedback
- - Uses `/api/tools/validate`
-
----
-
-### 5.3 CLI Tool (Optional)
-
-- [ ] **Create `packages/cli`**
- - Command: `tpmjs validate`
- - Reads local `package.json`
- - Validates `tpmjs` field
- - Calls `/api/tools/validate`
-
----
-
-### 5.4 Monitoring
-
-- [ ] **Add health endpoint**
- - `apps/web/src/app/api/health/route.ts`
- - Check database connectivity
- - Check sync status (last run times)
-
-- [ ] **Set up uptime monitoring**
- - Use UptimeRobot or Better Stack
- - Monitor `/api/health`
- - Alert if down or sync stale
-
-- [ ] **Add error tracking**
- - Set up Sentry for Next.js
- - Track API errors
- - Track sync errors
-
-**Verification:**
-```bash
-curl http://localhost:3001/api/health
-# Should return { status: "ok", db: "ok", sync: { ... } }
-```
-
----
-
-## 📋 Phase 6: Launch (Week 5)
-
-### 6.1 Pre-Launch Checklist
-
-- [ ] **Database**
- - ✓ Prisma schema deployed
- - ✓ Indexes created
- - ✓ Backups enabled in Neon
-
-- [ ] **Environment Variables**
- - ✓ All secrets in Vercel
- - ✓ `CRON_SECRET` set
- - ✓ `DATABASE_URL` set
-
-- [ ] **API Routes**
- - ✓ All endpoints working
- - ✓ Rate limiting added (optional)
- - ✓ Error handling complete
-
-- [ ] **Sync Workers**
- - ✓ Changes feed running every 2 min
- - ✓ Keyword search running every 15 min
- - ✓ Checkpoints updating correctly
-
-- [ ] **Frontend**
- - ✓ All pages loading real data
- - ✓ Search working
- - ✓ Mobile responsive
-
-- [ ] **Monitoring**
- - ✓ Health check endpoint live
- - ✓ Uptime monitoring active
- - ✓ Error tracking active
-
----
-
-### 6.2 Launch Steps
-
-- [ ] **Deploy to production**
- ```bash
- git push origin main
- # Vercel auto-deploys
- ```
-
-- [ ] **Verify deployment**
- - Check all pages load
- - Check API endpoints work
- - Check cron jobs run
-
-- [ ] **Publish announcement**
- - Tweet/post about TPMJS
- - Explain how to add `tpmjs` field
- - Share validation endpoint
-
-- [ ] **Monitor for 24 hours**
- - Watch error logs
- - Check sync is working
- - Fix any issues
-
----
-
-## 📋 Phase 7: Post-Launch (Ongoing)
-
-### Enhancements
-
-- [ ] **Semantic search**
- - Add embeddings to tools table
- - Use OpenAI/Cohere for semantic search
-
-- [ ] **Usage analytics**
- - Track tool views
- - Track search queries
- - Popular tools widget
-
-- [ ] **Tool recommendations**
- - "Similar tools" section
- - "You might also like"
-
-- [ ] **GitHub Actions**
- - Validate `tpmjs` field in CI
- - Auto-comment validation results
-
-- [ ] **NPM webhooks**
- - Listen for package updates
- - Immediate sync (instead of polling)
-
----
-
-## 🎯 Success Criteria
-
-Check these metrics after launch:
-
-### Week 1
-- [ ] 10+ official tools listed
-- [ ] All sync jobs running successfully
-- [ ] Zero API errors
-
-### Month 1
-- [ ] 50+ official tools
-- [ ] 5+ package authors using TPMJS
-- [ ] <200ms API response time (p95)
-
-### Month 3
-- [ ] 200+ tools
-- [ ] 20+ package authors
-- [ ] Community contributions
-
-### Month 6
-- [ ] 1000+ tools
-- [ ] 50+ active package authors
-- [ ] Established as go-to AI tool registry
-
----
-
-## 🔄 Ongoing Maintenance
-
-Weekly:
-- [ ] Check sync logs for errors
-- [ ] Review new tools for quality
-- [ ] Update documentation
-
-Monthly:
-- [ ] Database optimization (indexes, vacuum)
-- [ ] Review and adjust quality scoring
-- [ ] Update NPM_MIRROR.md with learnings
-
----
-
-## 📚 Key Documents
-
-**Read frequently during implementation:**
-
-1. **NPM_MIRROR.md** - Complete architecture reference
- - Database schema
- - API specifications
- - Validation rules
- - Quality scoring
- - All examples
-
-2. **This checklist** - Implementation order and verification steps
-
-3. **Plan file** - `.claude/plans/goofy-inventing-stearns.md` - Detailed planning notes
-
----
-
-## 🚀 Ready to Build
-
-This checklist is your complete implementation guide. Work through it phase by phase, checking off items as you go.
-
-**Start with Phase 1, Step 1.1** and work sequentially. Each step has verification instructions to ensure it's working before moving on.
-
-Good luck! 🎉
diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md
deleted file mode 100644
index 50bf6c1..0000000
--- a/IMPLEMENTATION_STATUS.md
+++ /dev/null
@@ -1,220 +0,0 @@
-# Dynamic Tool Loading - Implementation Status
-
-## ✅ Completed
-
-### 1. Search Tool Package (`@tpmjs/search-registry`)
-- ✅ Created package with AI SDK v6 JSON Schema format
-- ✅ Connects to search API endpoint
-- ✅ Returns tool metadata (packageName, exportName, version, importUrl)
-- ✅ Fixed schema format (was using Zod, now uses jsonSchema)
-- ✅ Location: `packages/tools/search-registry/`
-
-### 2. Search API Endpoint (`/api/tools/search`)
-- ✅ Implemented simple text-based search (BM25 had dependency issues)
-- ✅ Searches by keywords in description, package name, keywords
-- ✅ Returns tools with import URLs for esm.sh
-- ✅ Location: `apps/web/src/app/api/tools/search/route.ts`
-
-### 3. Pre-flight Tool Loading in Playground
-- ✅ Automatic search on every user message
-- ✅ Extracts user query from last message
-- ✅ Calls searchTpmjsTools automatically
-- ✅ Attempts to load discovered tools dynamically
-- ✅ Location: `apps/playground/src/app/api/chat/route.ts`
-
-### 4. Dynamic Tool Loader (Railway Service Approach)
-- ✅ Updated to call Railway service instead of local imports
-- ✅ Calls `/load-and-describe` endpoint to get tool schema
-- ✅ Wraps tool with remote execution via `/execute-tool` endpoint
-- ✅ Caches tool wrappers locally
-- ✅ Location: `apps/playground/src/lib/dynamic-tool-loader.ts`
-
-### 5. Documentation
-- ✅ DYNAMIC_IMPORT_ISSUE.md - Comprehensive problem analysis
-- ✅ RAILWAY_DYNAMIC_TOOL_LOADER.md - Railway implementation guide
-- ✅ This file - Implementation status
-
-## 🚧 Pending (Railway Service Implementation)
-
-### Railway Service Endpoints Needed
-
-You need to add these two endpoints to your existing Railway service:
-
-#### 1. `POST /load-and-describe`
-
-**Purpose**: Load a tool from esm.sh and return its schema
-
-**Request**:
-```json
-{
- "packageName": "firecrawl-aisdk",
- "exportName": "webSearchTool",
- "version": "0.7.2",
- "importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2"
-}
-```
-
-**Response**:
-```json
-{
- "success": true,
- "tool": {
- "exportName": "webSearchTool",
- "description": "Search the web using Firecrawl",
- "inputSchema": {
- "type": "object",
- "properties": {
- "query": { "type": "string" }
- }
- }
- }
-}
-```
-
-**Implementation Reference**: See `RAILWAY_DYNAMIC_TOOL_LOADER.md` for full code
-
-#### 2. `POST /execute-tool`
-
-**Purpose**: Execute a dynamically loaded tool with parameters
-
-**Request**:
-```json
-{
- "packageName": "firecrawl-aisdk",
- "exportName": "webSearchTool",
- "version": "0.7.2",
- "importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2",
- "params": {
- "query": "latest AI news"
- }
-}
-```
-
-**Response**:
-```json
-{
- "success": true,
- "output": { "results": [...] },
- "executionTimeMs": 1234
-}
-```
-
-**Implementation Reference**: See `RAILWAY_DYNAMIC_TOOL_LOADER.md` for full code
-
-### Deployment Requirements
-
-1. **Railway Service**:
- - Must run with `--experimental-network-imports` flag
- - Add to start command: `node --experimental-network-imports server.js`
-
-2. **Environment Variables** (Vercel):
- ```bash
- RAILWAY_SERVICE_URL=https://your-railway-service.up.railway.app
- # or reuse existing:
- SANDBOX_EXECUTOR_URL=https://your-railway-service.up.railway.app
- ```
-
-3. **Local Testing** (Railway service on port 3001):
- ```bash
- RAILWAY_SERVICE_URL=http://localhost:3001
- ```
-
-## 🎯 Testing Checklist
-
-Once Railway endpoints are deployed:
-
-- [ ] Test `/load-and-describe` endpoint directly with curl
-- [ ] Test `/execute-tool` endpoint directly with curl
-- [ ] Test full flow in playground:
- - [ ] Ask: "search the web for latest AI news"
- - [ ] Verify pre-flight search finds tools
- - [ ] Verify tools load via Railway
- - [ ] Verify tool execution works
- - [ ] Check console logs for debugging info
-
-## 📊 Current Flow
-
-```
-User: "search the web for latest AI news"
- ↓
- Chat API extracts query
- ↓
- Automatically calls searchTpmjsTools
- ↓
- Search API returns matching tools
- (packageName, exportName, version)
- ↓
- loadToolsBatch() called for each tool
- ↓
- For each tool:
- 1. Check local cache
- 2. If not cached:
- → POST to Railway: /load-and-describe
- ← Get back: description + inputSchema
- 3. Create wrapper tool with:
- - description from Railway
- - inputSchema from Railway
- - execute() → calls Railway /execute-tool
- 4. Cache wrapper locally
- ↓
- All tools available to agent
- ↓
- Agent calls tool (wrapper)
- ↓
- Wrapper → POST to Railway: /execute-tool
- ↓
- Railway imports from esm.sh and executes
- ↓
- Result returned to agent
- ↓
- Agent uses result to answer user
-```
-
-## 🔍 Debugging
-
-Check console logs for:
-- `📦 Loading from Railway` - Tool loading initiated
-- `✅ Tool loaded from Railway` - Tool schema received
-- `🚀 Executing ... remotely` - Tool execution initiated
-- `✅ Tool executed successfully` - Tool execution complete
-- `❌ Railway service error` - Connection failed
-- `❌ Failed to load tool` - Import failed
-
-## 📁 Files Modified
-
-1. `packages/tools/search-registry/src/index.ts` - Search tool
-2. `packages/tools/search-registry/package.json` - AI SDK version
-3. `apps/web/src/app/api/tools/search/route.ts` - Search endpoint
-4. `apps/playground/src/app/api/chat/route.ts` - Pre-flight search
-5. `apps/playground/src/lib/dynamic-tool-loader.ts` - Railway integration
-6. `apps/playground/next.config.ts` - Added urlImports (unused)
-7. `apps/playground/src/lib/tool-loader.ts` - Removed firecrawl
-
-## 🚀 Next Steps
-
-1. **Deploy Railway endpoints** using code from `RAILWAY_DYNAMIC_TOOL_LOADER.md`
-2. **Set environment variables** in Vercel
-3. **Test locally** with Railway service running on localhost:3001
-4. **Deploy to production** and test with real tools
-5. **Monitor logs** for any issues
-
-## 💡 Key Insights
-
-- **Next.js Limitation**: Cannot do dynamic HTTP imports due to bundler
-- **Railway Solution**: Plain Node.js with `--experimental-network-imports`
-- **Caching Strategy**: Two-level cache (local wrapper + Railway module)
-- **Execution Model**: Remote execution in Railway, not Next.js
-- **Security**: Tools execute in Railway sandbox, not Vercel
-- **Performance**: First load ~1-2s (import), cached loads <10ms
-
-## 📚 Related Documentation
-
-- `DYNAMIC_IMPORT_ISSUE.md` - Problem analysis and ChatGPT response
-- `RAILWAY_DYNAMIC_TOOL_LOADER.md` - Full Railway implementation guide
-- Plan file: `~/.claude/plans/jiggly-inventing-dragon.md`
-
----
-
-**Status**: Ready for Railway deployment
-**Blocker**: Railway `/load-and-describe` and `/execute-tool` endpoints need implementation
-**ETA**: 30-60 minutes to implement Railway endpoints + test
diff --git a/LAUNCH_REVIEW.md b/LAUNCH_REVIEW.md
new file mode 100644
index 0000000..edb20e4
--- /dev/null
+++ b/LAUNCH_REVIEW.md
@@ -0,0 +1,331 @@
+# TPMJS Launch Review & Checklist
+
+**STATUS: COMPLETED** - All critical issues have been fixed.
+
+A comprehensive review of all public-facing content for Hacker News launch readiness.
+
+---
+
+## Executive Summary
+
+**Overall Readiness: 7/10 - Needs Work Before Launch**
+
+The website has excellent technical content and professional design, but fails the "5-second test" - a first-time visitor cannot quickly understand what TPMJS is or why they need it. The documentation is strong for existing users but assumes too much prior knowledge about AI agents and tooling.
+
+### Critical Issues (Must Fix)
+1. **Landing page doesn't explain what TPMJS is** - Hero section uses jargon without definition
+2. **"Tool" vs "Package" never defined** - Core concepts assumed, not explained
+3. **Knowledge gaps** - Assumes familiarity with AI agents, Zod, semantic search
+4. **Category inconsistency** - HOW_TO_PUBLISH and NPM_MIRROR have different category lists
+5. **NPM_MIRROR.md conflicts with other docs** - Appears outdated, creates confusion
+
+### What's Working Well
+- Publishing guide (HOW_TO_PUBLISH_A_TOOL.md) is excellent
+- How It Works page has great technical depth
+- Developer testimonials are concrete with real metrics
+- No obvious AI-generated language on website
+- Code examples are practical and well-placed
+
+---
+
+## The 5-Second Test: FAILED
+
+**Question:** Can a developer understand what TPMJS is within 5 seconds of landing on the homepage?
+
+**Answer:** No.
+
+### What They See First
+```
+TOOL REGISTRY FOR AI AGENTS
+Discover, share, and integrate tools that give your agents superpowers
+```
+
+### What's Missing
+- What is a "tool" in this context?
+- What is an "AI agent"?
+- Why would I use this vs npm directly?
+- Is this a package manager? A marketplace? An SDK?
+
+### The "Aha Moment" is Unclear
+A visitor still doesn't know:
+- WHO should use TPMJS (tool builders? agent developers? both?)
+- WHEN they would use it (at development time? runtime?)
+- HOW it differs from regular npm packages
+- WHY they can't just install packages normally
+
+---
+
+## Page-by-Page Clarity Ratings
+
+| Page | Clarity | Human Feel | Issues |
+|------|---------|------------|--------|
+| **Landing Page** | 5/10 | Yes | No 5-second explanation, jargon-heavy |
+| **Hero Section** | 3/10 | Yes | "Tool registry" undefined, circular language |
+| **Problem Section** | 7/10 | Yes | Best section - concrete pain points |
+| **Vision Section** | 5/10 | Yes | "Semantic search" unexplained |
+| **Developer Stories** | 7/10 | Yes | Good metrics, but code unexplained |
+| **Publish Section** | 6/10 | Yes | Assumes visitor is a tool builder |
+| **How It Works** | 9/10 | Excellent | Minor density issues |
+| **FAQ** | 8/10 | Yes | Missing some common questions |
+| **Publish Guide** | 8.5/10 | Yes | Tier system could be clearer upfront |
+| **Spec Page** | 8.5/10 | Yes | Assumes Zod/AI SDK knowledge |
+| **Docs Page** | 9/10 | Excellent | Overwhelming length |
+| **SDK Page** | 8.5/10 | Yes | Assumes Vercel AI SDK familiarity |
+| **Privacy** | 8/10 | Yes | Hardcoded email address |
+| **Terms** | 8/10 | Yes | Hardcoded date |
+
+---
+
+## Documentation Clarity Ratings
+
+| Document | Clarity | Necessary | Critical Issues |
+|----------|---------|-----------|-----------------|
+| README.md | 8/10 | YES | Missing "what is TPMJS" explanation |
+| HOW_TO_PUBLISH_A_TOOL.md | 9/10 | YES | Minor - excellent overall |
+| DEPLOYMENT.md | 8/10 | YES | Confusing exit code explanation |
+| QUALITY-GATES.md | 7/10 | OPTIONAL | Could merge into README |
+| MANUAL_TOOLS.md | 8.5/10 | YES | Good for maintainers |
+| NPM_MIRROR.md | 6.5/10 | **REMOVE** | **Conflicts with other docs, appears outdated** |
+
+---
+
+## Knowledge Gaps (Things Visitors Won't Understand)
+
+### Not Explained Anywhere
+1. **What is an "AI Agent"?** - The entire site assumes you know this
+2. **What is a "Tool" vs a "Package"?** - Used interchangeably, never defined
+3. **Why semantic search matters** - Just says "semantic" without explaining benefit
+4. **What frameworks are supported** - Mentioned in FAQ but not prominently
+5. **The Package → Tool relationship** - Can one package have multiple tools?
+
+### Assumed Technical Knowledge
+- Zod schemas (used throughout, never introduced)
+- AI SDK tool format (referenced as "standard" but what standard?)
+- esm.sh and Deno sandboxing (mentioned in How It Works)
+- BM25 ranking algorithm (mentioned in docs)
+
+### Missing Use Cases
+- "Use TPMJS when..." section doesn't exist
+- No comparison to alternatives (why not just npm?)
+- No "before/after" showing the problem solved
+
+---
+
+## Human-Written Assessment
+
+### Reads Like Human: YES ✓
+- Developer stories use specific metrics ("500 lines to 3")
+- Technical explanations show genuine understanding
+- Problem section addresses real pain points
+- No buzzword soup or meaningless marketing phrases
+
+### Minor AI-Sounding Phrases Found
+| Location | Phrase | Issue |
+|----------|--------|-------|
+| NPM_MIRROR.md:7 | "automated NPM-integrated registry" | Marketing speak |
+| NPM_MIRROR.md:27 | "✨ Listed automatically" | Emoji in technical doc |
+| NPM_MIRROR.md:500 | "Built with ❤️" | Remove emoji |
+| HOW_TO_PUBLISH:389 | "AI-friendly descriptions" | Vague - what makes it "AI-friendly"? |
+| Vision Section | "gives agents superpowers" | Metaphor without substance |
+
+---
+
+## Critical Inconsistencies Found
+
+### Category Lists Don't Match
+**HOW_TO_PUBLISH_A_TOOL.md says:**
+```
+text-analysis, code-generation, data-processing,
+image-generation, audio-processing, search, integration, other
+```
+
+**NPM_MIRROR.md says:**
+```
+web-scraping, data-processing, file-operations, communication,
+database, api-integration, image-processing, text-analysis,
+automation, ai-ml, security, monitoring
+```
+
+**These are completely different!** Which is correct?
+
+### Quality Score Formula Conflicts
+- HOW_TO_PUBLISH: "Tier: Rich (1.0) > Basic (0.5) > Minimal (0.25)"
+- MANUAL_TOOLS: "Rich tier tools get 4x quality score multiplier"
+- NPM_MIRROR: Different formula entirely
+
+### Field Names Inconsistent
+- `exportName` used in MANUAL_TOOLS but not in HOW_TO_PUBLISH
+- Deprecated fields (`parameters`, `returns`) mentioned but unclear when deprecated
+
+---
+
+## Hardcoded Values to Fix
+
+| File | Issue | Line |
+|------|-------|------|
+| FAQ, Privacy, Terms | `thomasalwyndavis@gmail.com` hardcoded | Multiple |
+| Privacy, Terms | Date "December 14, 2025" hardcoded | Multiple |
+| Changelog page | Package list hardcoded in code | ~95-110 |
+| Developer Stories | Fictional company names (Support.ai, DocFlow) | homePageData.ts |
+
+---
+
+## Launch Checklist
+
+### Must Fix Before Launch (Blocking) - ALL DONE ✓
+
+- [x] **Rewrite hero section** to explain TPMJS in one sentence
+ - Current: "TOOL REGISTRY FOR AI AGENTS"
+ - Suggested: "TPMJS lets AI agents discover and use npm packages as tools at runtime. Publish once to npm, get discovered automatically."
+
+- [x] **Add "What is TPMJS?" section** to landing page
+ - Define: What is an AI agent?
+ - Define: What is a "tool" in this context?
+ - Explain: Why not just use npm directly?
+ - Show: 3-step "how it works" visual
+
+- [x] **Reconcile category lists** between docs (deleted NPM_MIRROR.md)
+ - Pick one canonical list
+ - Update all docs to match
+ - Add categories to types package
+
+- [x] **Delete or archive NPM_MIRROR.md** (deleted)
+ - Conflicts with HOW_TO_PUBLISH
+ - Appears to be old design doc, not current state
+ - Move to `/docs/internal/` if historical value
+
+- [x] **Fix hardcoded values** (emails → hello@tpmjs.com, dates → December 2024)
+ - Email addresses → environment variable
+ - Dates → dynamic or remove
+ - Package lists → generated from filesystem
+
+### Should Fix (High Priority) - MOSTLY DONE
+
+- [x] **Add "Use TPMJS when..." section** to landing page (covered in "What is TPMJS?" section)
+ - List concrete scenarios: "Building a chatbot that needs web access"
+ - "Agent that processes different file formats"
+ - "Tool that should be discoverable by other agents"
+
+- [x] **Explain Package vs Tool distinction** (covered in "What is TPMJS?" section)
+ - Add glossary or definitions section
+ - Clarify: 1 package can have N tools
+
+- [x] **Add framework compatibility section** (mentioned in hero and publish sections)
+ - Which AI frameworks work with TPMJS?
+ - Are there adapters needed?
+ - Show code for each framework
+
+- [ ] **Simplify developer stories code**
+ - Current code snippet unexplained:
+ ```js
+ const agent = new Agent({ tools: await tpmjs.search(...) })
+ ```
+ - Add: Where does `Agent` come from? What's happening here?
+
+- [x] **Add README context** (completely rewritten with clear explanation)
+ - What is TPMJS for?
+ - Link to tpmjs.com
+ - Explain discovery mechanism
+
+### Nice to Have (Post-Launch)
+
+- [ ] Add video walkthrough (30-60 seconds)
+- [ ] Interactive playground link from homepage
+- [ ] "Compare to alternatives" section
+- [ ] Case studies with real company names
+- [ ] Quick links sidebar for docs page
+- [ ] Status badges for each quality gate
+
+---
+
+## Recommended Hero Section Rewrite
+
+### Current
+```
+TOOL REGISTRY FOR AI AGENTS
+Discover, share, and integrate tools that give your agents superpowers
+The registry for AI tools
+```
+
+### Suggested
+```
+MAKE YOUR AI AGENT SMARTER
+TPMJS connects your AI agent to 2,500+ npm packages at runtime.
+No config files. No manual imports. Just describe what you need.
+
+"Find me a tool that can scrape websites" → Your agent gets web-scraper
+"I need to process markdown" → Your agent gets markdown-formatter
+
+Publish your npm package → It's discoverable by every AI agent in 15 minutes.
+```
+
+This version:
+- Explains what it DOES (connects agents to npm packages)
+- Shows HOW it works (natural language → tool)
+- States the VALUE (no config, automatic discovery)
+- Gives concrete examples
+
+---
+
+## Recommended "What is TPMJS?" Section
+
+Add after hero, before featured tools:
+
+```markdown
+## What is TPMJS?
+
+**The Problem:** AI agents need tools (web scraping, file processing, API calls)
+but developers must manually configure each one. As the ecosystem grows,
+this becomes unmanageable.
+
+**The Solution:** TPMJS is a registry that automatically discovers npm packages
+designed for AI agents. Agents can search for tools by description and load them
+at runtime.
+
+**For Tool Builders:** Add `tpmjs-tool` keyword to your package.json.
+Your tool appears on tpmjs.com within 15 minutes.
+
+**For Agent Developers:** Use semantic search to find tools:
+```javascript
+import { searchRegistry } from '@tpmjs/sdk';
+const tools = await searchRegistry('send emails and slack messages');
+// Returns: email-sender, slack-notifier, ...
+```
+
+**One registry. Thousands of tools. Zero configuration.**
+```
+
+---
+
+## Final Assessment
+
+### Ready for Launch?
+**Not yet.** The core product is solid but messaging fails first-time visitors.
+
+### Estimated Fixes
+- Hero rewrite: 30 minutes
+- "What is TPMJS?" section: 1 hour
+- Category reconciliation: 1 hour
+- Hardcoded values: 30 minutes
+- README updates: 30 minutes
+- NPM_MIRROR cleanup: 15 minutes
+
+**Total: ~4 hours of work**
+
+### After Fixes
+The site will be launch-ready. The technical content is excellent - it just needs a better front door.
+
+---
+
+## Appendix: Positive Highlights
+
+Things that are already great and should NOT change:
+
+1. **How It Works page** - Excellent technical depth, clear structure
+2. **Publishing guide** - Best-in-class documentation, real examples
+3. **Problem section** - Concrete pain points, relatable issues
+4. **Spec page** - Clear field reference, good validation info
+5. **SDK documentation** - Quick start is excellent
+6. **Code examples throughout** - Practical, copy-pasteable
+7. **Visual design** - Clean, professional, developer-focused
+8. **Quality scoring explanation** - Transparent, well-documented
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7d72b03
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024-2025 TPMJS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/NPM_MIRROR.md b/NPM_MIRROR.md
deleted file mode 100644
index 61d05cb..0000000
--- a/NPM_MIRROR.md
+++ /dev/null
@@ -1,500 +0,0 @@
-# TPMJS NPM-Integrated Registry Architecture
-
-> **Automated tool discovery from NPM with zero-click submission**
-
-## Vision
-
-Transform TPMJS from a manual directory into an **automated NPM-integrated registry** where package authors simply publish to NPM with a `tpmjs` field in their `package.json` and their tools are discovered and listed within seconds—no manual submission, no forms, no waiting.
-
-## Quick Start for Package Authors
-
-```json
-{
- "name": "my-awesome-tool",
- "version": "1.0.0",
- "keywords": ["tpmjs-tool"],
- "tpmjs": {
- "category": "web-scraping",
- "description": "Extract product data from e-commerce websites with ease",
- "example": "const data = await scraper.extract('https://shop.com')"
- }
-}
-```
-
-```bash
-npm publish
-# ✨ Listed automatically within 15 minutes (keyword) or seconds (changes feed)
-```
-
----
-
-## Architecture Overview
-
-```
-NPM Ecosystem
- ↓
-Changes Feed + Keyword Search
- ↓
-Package Validator (Zod)
- ↓
-PostgreSQL Database
- ↓
-Next.js API Routes
- ↓
-TPMJS Web App
-```
-
-### Core Components
-
-1. **NPM Sync Service** (Node.js) - Monitors NPM registry for new packages
-2. **PostgreSQL Database** - Stores validated tool metadata
-3. **Next.js API** - Serves tool data with search/filtering
-4. **Web Frontend** - Browse, search, and discover tools
-
----
-
-## Discovery Mechanism: Hybrid Approach
-
-### Method 1: Keyword Search (Official)
-- Search NPM for packages with `tpmjs-tool` keyword
-- Runs every 15 minutes via cron
-- Packages marked as "Official"
-
-### Method 2: Changes Feed (Automatic)
-- Monitors `replicate.npmjs.com/registry/_changes` in real-time
-- Detects packages with `tpmjs` field instantly
-- Packages marked as "Community" (unless they also have keyword)
-
-### Why Hybrid?
-- **Keywords** = Clear opt-in, queryable, respects NPM conventions
-- **Changes Feed** = Real-time, catches packages without keywords
-- **Together** = Best discoverability with fallback
-
----
-
-## The "tpmjs" Field: Tiered Schema
-
-### Minimal Tier (Required)
-
-```json
-{
- "tpmjs": {
- "category": "web-scraping",
- "description": "Extract structured data from websites using CSS selectors",
- "example": "const data = await tool.scrape({ url: 'https://example.com', selector: '.price' })"
- }
-}
-```
-
-**Categories:**
-- web-scraping
-- data-processing
-- file-operations
-- communication
-- database
-- api-integration
-- image-processing
-- text-analysis
-- automation
-- ai-ml
-- security
-- monitoring
-
-### Rich Tier (Optional)
-
-Extend with any of these optional fields:
-
-```json
-{
- "tpmjs": {
- // ... Required fields ...
-
- "parameters": [
- {
- "name": "url",
- "type": "string",
- "description": "Target URL to scrape",
- "required": true
- }
- ],
- "returns": {
- "type": "object",
- "description": "Extracted data matching the selector"
- },
- "authentication": {
- "required": false,
- "type": "api-key",
- "envVar": "SCRAPER_API_KEY",
- "docsUrl": "https://docs.example.com/auth"
- },
- "pricing": {
- "model": "freemium",
- "freeLimit": "100 requests/month",
- "paidUrl": "https://example.com/pricing"
- },
- "frameworks": ["vercel-ai", "langchain", "llamaindex"],
- "links": {
- "documentation": "https://docs.example.com",
- "playground": "https://example.com/try",
- "repository": "https://github.com/user/repo"
- },
- "tags": ["web", "scraping", "html", "css"],
- "status": "stable",
- "aiAgent": {
- "useCase": "Use when agent needs to extract data from websites",
- "limitations": "Cannot handle JavaScript-heavy SPAs"
- }
- }
-}
-```
-
----
-
-## Database Schema
-
-### Tools Table
-
-```sql
-CREATE TABLE tools (
- -- NPM Metadata
- npm_package_name VARCHAR(214) UNIQUE NOT NULL,
- npm_version VARCHAR(50) NOT NULL,
- npm_published_at TIMESTAMP NOT NULL,
- npm_description TEXT,
- npm_repository JSONB,
- npm_homepage TEXT,
- npm_license VARCHAR(50),
-
- -- TPMJS Metadata
- category VARCHAR(50) NOT NULL,
- description TEXT NOT NULL,
- example TEXT NOT NULL,
- parameters JSONB,
- authentication JSONB,
- pricing JSONB,
- frameworks TEXT[],
- links JSONB,
- tags TEXT[],
- status VARCHAR(20),
-
- -- Discovery
- discovery_method VARCHAR(20) NOT NULL, -- 'keyword' | 'changes-feed'
- is_official BOOLEAN DEFAULT false,
- tier VARCHAR(20) NOT NULL, -- 'minimal' | 'rich'
-
- -- Metrics
- npm_downloads_last_month INTEGER DEFAULT 0,
- github_stars INTEGER DEFAULT 0,
- quality_score DECIMAL(3,2), -- 0.00 to 1.00
-
- -- Timestamps
- created_at TIMESTAMP DEFAULT NOW(),
- updated_at TIMESTAMP DEFAULT NOW()
-);
-```
-
----
-
-## Sync Service Architecture
-
-### Workers
-
-**1. Changes Feed Worker**
-- Connects to `replicate.npmjs.com/registry/_changes`
-- Receives real-time change events
-- Fetches package metadata for each change
-- Checks for `tpmjs` field
-- Validates and inserts to database
-
-**2. Keyword Search Worker**
-- Runs every 15 minutes (cron)
-- Searches `/-/v1/search?text=keywords:tpmjs-tool`
-- Processes all results
-- Marks as "Official"
-
-**3. Metrics Worker** (Optional Phase 4)
-- Updates download counts from NPM API
-- Fetches GitHub stars
-- Calculates quality scores
-
-### Package Processing Pipeline
-
-```
-1. Fetch package metadata from NPM
-2. Extract `tpmjs` field from latest version
-3. Validate against Zod schema
-4. If valid → Insert/Update database
-5. If invalid → Log error
-6. If no field → Skip
-```
-
----
-
-## API Routes
-
-### GET /api/tools
-Search and list tools
-
-**Query Parameters:**
-- `q` - Search query
-- `category` - Filter by category
-- `official` - Only official tools (true/false)
-- `limit` - Results per page (default 20)
-- `offset` - Pagination offset
-
-**Response:**
-```json
-{
- "tools": [...],
- "pagination": {
- "total": 150,
- "limit": 20,
- "offset": 0,
- "hasMore": true
- }
-}
-```
-
-### GET /api/tools/[id]
-Get tool details by ID
-
-### POST /api/tools/validate
-Validate a `tpmjs` field before publishing
-
-**Request:**
-```json
-{
- "category": "web-scraping",
- "description": "...",
- "example": "..."
-}
-```
-
-**Response:**
-```json
-{
- "valid": true,
- "tier": "minimal",
- "errors": []
-}
-```
-
-### GET /api/stats
-Registry statistics
-
-```json
-{
- "totalTools": 2847,
- "officialTools": 150,
- "categories": {
- "web-scraping": 320,
- "communication": 280,
- ...
- }
-}
-```
-
----
-
-## Quality Scoring Algorithm
-
-Tools are scored 0.00 to 1.00 based on:
-
-- **Base validity** (0.3) - Has valid schema
-- **Tier** (0.1-0.2) - Rich tier > Minimal tier
-- **NPM downloads** (0.2) - Based on monthly downloads
-- **GitHub stars** (0.15) - Repository popularity
-- **Documentation** (0.1) - Has docs URL
-- **Example quality** (0.05) - Example length > 100 chars
-
-Score is used for default sorting and quality indicators.
-
----
-
-## Implementation Phases
-
-### Phase 1: Foundation (Week 1-2)
-- Set up PostgreSQL + Prisma
-- Create Zod schemas in `@tpmjs/types`
-- Build sync service structure
-- Implement NPM API client
-
-### Phase 2: Discovery (Week 2-3)
-- Implement changes feed worker
-- Implement keyword search worker
-- Deploy sync service (Railway/Fly.io)
-- Test with real packages
-
-### Phase 3: API & Frontend (Week 3-4)
-- Build Next.js API routes
-- Update tool listing page
-- Update tool detail pages
-- Add validation endpoint
-
-### Phase 4: Polish (Week 4-5)
-- Add metrics worker
-- Create documentation
-- Build CLI validator
-- Launch to community
-
-### Phase 5: Enhancements (Post-Launch)
-- Semantic search (embeddings)
-- Usage analytics
-- Tool recommendations
-- GitHub Actions integration
-
----
-
-## Infrastructure Requirements
-
-### Sync Service
-- **Platform:** Railway or Fly.io
-- **Runtime:** Node.js 22+
-- **Resources:** 512MB RAM, 1 CPU
-- **Cost:** ~$5-10/month
-
-### Database
-- **Platform:** Neon Postgres (serverless)
-- **Size:** Free tier (start), scale as needed
-- **Backups:** Automatic with Neon
-- **Cost:** Free tier available, ~$10-20/month for production
-
-### Web App
-- **Platform:** Vercel (existing)
-- **No changes required**
-
----
-
-## Monitoring & Health
-
-### Metrics to Track
-
-1. **Sync Health**
- - Changes feed uptime
- - Packages processed per hour
- - Validation success rate
-
-2. **Database**
- - Total tools
- - Official vs community ratio
- - Tier distribution
-
-3. **API**
- - Request latency (p95 < 200ms)
- - Search performance
- - Error rates
-
-### Alerts
-
-- Sync service down > 5 minutes
-- Database connection failures
-- Validation error rate > 10%
-
----
-
-## Developer Experience
-
-### Validation Before Publishing
-
-```bash
-# Using TPMJS CLI (to be built)
-npx tpmjs validate
-
-# Or via API
-curl -X POST https://tpmjs.com/api/tools/validate \
- -H "Content-Type: application/json" \
- -d '{"category":"web-scraping","description":"...","example":"..."}'
-```
-
-### Documentation Pages Needed
-
-1. **Getting Started** - Adding TPMJS support
-2. **Schema Reference** - Complete field docs
-3. **Best Practices** - Tips for quality tools
-4. **Examples** - Sample configurations
-5. **FAQ** - Common questions
-
----
-
-## Migration from Mock Data
-
-### Current State
-- 12 mock tools in `toolData.ts`
-- Client-side search
-- Hard-coded categories
-
-### Migration Strategy
-
-1. **Publish Real Packages**
- - Create NPM packages for mock tools
- - Add `tpmjs` fields
- - Publish with `tpmjs-tool` keyword
-
-2. **Update Frontend**
- - Replace mock data with API calls
- - Keep existing UI components
- - Update types to match Prisma models
-
-3. **Gradual Rollout**
- - Dual mode (mock + real)
- - Real data primary, mock fallback
- - Remove mock entirely
-
----
-
-## Success Metrics
-
-### Technical
-- ✓ Discovery latency < 60 seconds
-- ✓ API response time < 200ms p95
-- ✓ Support 10,000+ tools
-- ✓ 99.9% uptime
-
-### User Experience
-- ✓ 0-click submission (automatic)
-- ✓ Instant validation feedback
-- ✓ <100ms search speed
-- ✓ 100% mobile features
-
-### Business
-- Week 1: 10 official tools
-- Month 1: 50 official tools
-- Month 3: 200+ tools
-- Month 6: 1000+ tools
-- 50+ active package authors
-
----
-
-## Comparison to Vercel's Approach
-
-| Feature | Vercel AI SDK | TPMJS |
-|---------|---------------|-------|
-| **Submission** | Manual file edit + PR | Automatic via NPM |
-| **Discovery** | None | Real-time changes feed |
-| **Validation** | Manual review | Automated Zod schema |
-| **Updates** | New PR required | Automatic on publish |
-| **Search** | Static array | Full-text + categories |
-| **Scale** | 6 tools | 1000+ tools ready |
-
----
-
-## Next Steps
-
-1. Review this architecture plan
-2. Approve database schema and API design
-3. Set up infrastructure (Railway + Postgres)
-4. Start Phase 1: Foundation
-5. Launch MVP in 4-5 weeks
-
----
-
-## References
-
-- [NPM Registry API Docs](https://github.com/npm/registry/blob/main/docs/REGISTRY-API.md)
-- [NPM Changes Feed](https://github.com/npm/registry/blob/main/docs/REPLICATE-API.md)
-- [Vercel AI Tools Registry](https://github.com/vercel/ai/blob/main/content/tools-registry/registry.ts)
-- [TPMJS Architecture Plan](/.claude/plans/goofy-inventing-stearns.md) (Full details)
-
----
-
-**Built with ❤️ for the AI agent ecosystem**
diff --git a/OPENAI_SCHEMA_ERROR.md b/OPENAI_SCHEMA_ERROR.md
deleted file mode 100644
index f7ef9e9..0000000
--- a/OPENAI_SCHEMA_ERROR.md
+++ /dev/null
@@ -1,522 +0,0 @@
-# OpenAI Schema Validation Error - AI SDK v6
-
-## ✅ RESOLVED
-
-**Solution:** Use `tool()` and `jsonSchema()` from AI SDK instead of Zod for tool definitions.
-
-## Error Message
-
-```
-Error [AI_APICallError]: Invalid schema for function 'helloWorld': schema must be a JSON Schema of 'type: "object"', got 'type: "None"'.
-```
-
-## Context
-
-Building a Next.js playground app to test AI SDK v6 tool execution with OpenAI's GPT-4o-mini model. The error occurs when OpenAI validates the tool schema sent in the API request.
-
-## Root Cause
-
-Zod 4.0.0 generates JSON Schema with `allOf` + `$ref` at the root level instead of a direct `type: "object"`. OpenAI's API requires a JSON Schema with `type: "object"` at the root, so it rejects Zod 4 schemas with `type: "None"` error.
-
-## Environment
-
-- **AI SDK Version**: `ai@6.0.0-beta.124`
-- **OpenAI Provider**: `@ai-sdk/openai@3.0.0-beta.74`
-- **OpenAI Library**: `openai@^6.9.1`
-- **Zod Version**: `zod@^4.0.0`
-- **Next.js Version**: `next@^16.0.4`
-- **Node.js**: Latest
-- **TypeScript**: Strict mode enabled
-
-## Tool Definition
-
-Located at: `packages/tools/hello/src/index.ts`
-
-```typescript
-import { z } from 'zod';
-
-/**
- * Hello World Tool
- * Returns a simple "Hello, World!" greeting
- *
- * This is a proper AI SDK v6 tool that can be used with streamText()
- */
-export const helloWorldTool = {
- description: 'Returns a simple "Hello, World!" greeting message',
- parameters: z.object({
- // OpenAI requires at least one optional parameter, can't be completely empty
- includeTimestamp: z.boolean().optional().describe('Whether to include a timestamp in the response'),
- }),
- execute: async ({ includeTimestamp = true }: { includeTimestamp?: boolean }) => {
- const response: any = {
- message: 'Hello, World!',
- };
-
- if (includeTimestamp) {
- response.timestamp = new Date().toISOString();
- }
-
- return response;
- },
-};
-
-/**
- * Hello Name Tool
- * Returns a personalized greeting with the provided name
- *
- * This is a proper AI SDK v6 tool that can be used with streamText()
- */
-export const helloNameTool = {
- description: 'Returns a personalized greeting with the provided name',
- parameters: z.object({
- name: z.string().describe('The name of the person to greet'),
- }),
- execute: async ({ name }: { name: string }) => {
- return {
- message: `Hello, ${name}!`,
- timestamp: new Date().toISOString(),
- };
- },
-};
-```
-
-## Tool Loading
-
-Located at: `apps/playground/src/lib/tool-loader.ts`
-
-```typescript
-// Static imports for tools (required for Next.js/webpack)
-import { helloWorldTool, helloNameTool } from '@tpmjs/hello';
-import { scrapeTool, crawlTool, searchTool } from 'firecrawl-aisdk';
-
-/**
- * Load a specific TPMJS tool by package name
- */
-export async function loadTpmjsTool(packageName: string): Promise {
- try {
- // Map package names to their tool functions
- switch (packageName) {
- case '@tpmjs/hello':
- // Hello has multiple tools, return all of them
- return {
- helloWorld: helloWorldTool,
- helloName: helloNameTool,
- };
-
- case 'firecrawl-aisdk':
- // Firecrawl has multiple tools, return all of them
- return {
- scrapeTool,
- crawlTool,
- searchTool,
- };
-
- default:
- throw new Error(`Unknown tool package: ${packageName}`);
- }
- } catch (error) {
- if (error instanceof Error) {
- throw new Error(`Failed to load tool from package ${packageName}: ${error.message}`);
- }
- throw new Error(`Failed to load tool from package ${packageName}: Unknown error`);
- }
-}
-
-/**
- * Load all installed TPMJS tools
- */
-export async function loadAllTools(): Promise> {
- const installedTools = ['@tpmjs/hello', 'firecrawl-aisdk'];
-
- const tools: Record = {};
-
- for (const packageName of installedTools) {
- try {
- const tool = await loadTpmjsTool(packageName);
-
- // If the tool returns an object with multiple tools (like firecrawl), spread them
- if (tool && typeof tool === 'object' && !tool.description) {
- Object.assign(tools, tool);
- } else {
- // Single tool - use a cleaned name (remove hyphens, camelCase)
- const toolName = packageName.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase()).replace(/-/g, '');
- tools[toolName] = tool;
- }
- } catch (error) {
- console.error(`Failed to load tool ${packageName}:`, error);
- // Continue loading other tools even if one fails
- }
- }
-
- return tools;
-}
-```
-
-## API Route
-
-Located at: `apps/playground/src/app/api/chat/route.ts`
-
-```typescript
-import { loadAllTools } from '~/lib/tool-loader';
-import { openai } from '@ai-sdk/openai';
-import { streamText } from 'ai';
-import { NextRequest } from 'next/server';
-
-export const runtime = 'nodejs';
-export const dynamic = 'force-dynamic';
-export const maxDuration = 60;
-
-export async function POST(request: NextRequest) {
- try {
- const body = await request.json();
- const { messages } = body;
-
- if (!messages || !Array.isArray(messages)) {
- return new Response(JSON.stringify({ error: 'Invalid request: messages array required' }), {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-
- // Load all available tools
- const tools = await loadAllTools();
-
- console.log('Loaded tools:', Object.keys(tools));
-
- // Create system message
- const systemMessage = {
- role: 'system' as const,
- content: `You are a helpful AI assistant that can use TPMJS tools to help users.
-
-Available tools:
-${Object.entries(tools)
- .map(([name, tool]) => `- ${name}: ${tool.description}`)
- .join('\n')}
-
-Call tools as needed to answer user questions. Execute tools directly.`,
- };
-
- // Stream the AI response with tools
- const result = streamText({
- model: openai('gpt-4o-mini'),
- messages: [systemMessage, ...messages],
- tools,
- maxSteps: 5,
- });
-
- return result.toTextStreamResponse();
- } catch (error) {
- console.error('Chat API error:', error);
- return new Response(
- JSON.stringify({
- error: error instanceof Error ? error.message : 'Unknown error occurred',
- }),
- {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- }
- );
- }
-}
-```
-
-## Package Configuration
-
-Located at: `packages/tools/hello/package.json`
-
-```json
-{
- "name": "@tpmjs/hello",
- "version": "0.0.1",
- "private": true,
- "description": "Example TPMJS tools - Hello World and Hello Name",
- "main": "dist/index.js",
- "types": "dist/index.d.ts",
- "scripts": {
- "build": "tsc",
- "dev": "tsc --watch",
- "clean": "rm -rf dist",
- "type-check": "tsc --noEmit"
- },
- "keywords": [
- "tpmjs-tool",
- "ai-sdk",
- "hello",
- "example"
- ],
- "tpmjs": {
- "category": "text-analysis",
- "description": "Simple greeting tools - Hello World and personalized Hello Name greetings"
- },
- "dependencies": {
- "ai": "6.0.0-beta.124",
- "zod": "^4.0.0"
- },
- "devDependencies": {
- "@tpmjs/tsconfig": "workspace:*",
- "typescript": "^5.9.3"
- },
- "files": [
- "dist",
- "README.md"
- ]
-}
-```
-
-## TypeScript Configuration
-
-Located at: `packages/tools/hello/tsconfig.json`
-
-```json
-{
- "compilerOptions": {
- "target": "ES2020",
- "module": "commonjs",
- "lib": ["ES2020"],
- "outDir": "./dist",
- "rootDir": "./src",
- "declaration": true,
- "declarationMap": true,
- "sourceMap": true,
- "strict": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "forceConsistentCasingInFileNames": true,
- "moduleResolution": "node",
- "resolveJsonModule": true
- },
- "include": ["src/**/*"],
- "exclude": ["node_modules", "dist"]
-}
-```
-
-## Compiled Output
-
-Located at: `packages/tools/hello/dist/index.js`
-
-```javascript
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-exports.helloNameTool = exports.helloWorldTool = void 0;
-const zod_1 = require("zod");
-/**
- * Hello World Tool
- * Returns a simple "Hello, World!" greeting
- *
- * This is a proper AI SDK v6 tool that can be used with streamText()
- */
-exports.helloWorldTool = {
- description: 'Returns a simple "Hello, World!" greeting message',
- parameters: zod_1.z.object({
- // OpenAI requires at least one optional parameter, can't be completely empty
- includeTimestamp: zod_1.z.boolean().optional().describe('Whether to include a timestamp in the response'),
- }),
- execute: async ({ includeTimestamp = true }) => {
- const response = {
- message: 'Hello, World!',
- };
- if (includeTimestamp) {
- response.timestamp = new Date().toISOString();
- }
- return response;
- },
-};
-/**
- * Hello Name Tool
- * Returns a personalized greeting with the provided name
- *
- * This is a proper AI SDK v6 tool that can be used with streamTime()
- */
-exports.helloNameTool = {
- description: 'Returns a personalized greeting with the provided name',
- parameters: zod_1.z.object({
- name: zod_1.z.string().describe('The name of the person to greet'),
- }),
- execute: async ({ name }) => {
- return {
- message: `Hello, ${name}!`,
- timestamp: new Date().toISOString(),
- };
- },
-};
-```
-
-## Full Error Response from OpenAI
-
-```json
-{
- "error": {
- "message": "Invalid schema for function 'helloWorld': schema must be a JSON Schema of 'type: \"object\"', got 'type: \"None\"'.",
- "type": "invalid_request_error",
- "param": "tools[0].parameters",
- "code": "invalid_function_parameters"
- }
-}
-```
-
-API endpoint: `https://api.openai.com/v1/responses`
-Status code: 400
-
-## Problem Analysis
-
-1. **OpenAI expects JSON Schema format** - The `tools[0].parameters` field must be a valid JSON Schema object with `type: "object"`
-
-2. **AI SDK v6 should convert Zod to JSON Schema** - The AI SDK is supposed to automatically convert Zod schemas to JSON Schema when sending to OpenAI, but it's producing `type: "None"` instead
-
-3. **Potential causes**:
- - Zod 4.0.0 compatibility issue with AI SDK v6 beta
- - AI SDK not properly converting the Zod schema
- - Issue with how the tool object is structured
- - Problem with how tools are passed to `streamText()`
-
-4. **Already tried**:
- - Added at least one parameter (even optional) to helloWorldTool
- - Used proper Zod schema with `.describe()` for descriptions
- - Followed AI SDK v6 tool definition format exactly
- - Built the package successfully (dist folder exists)
-
-## AI SDK v6 Tool Format Reference
-
-According to AI SDK v6 documentation, a tool should be defined as:
-
-```typescript
-{
- description: string,
- parameters: ZodSchema,
- execute: async (args) => Promise
-}
-```
-
-This matches our implementation exactly.
-
-## Questions for ChatGPT
-
-1. Is there a known compatibility issue between AI SDK v6 Beta (6.0.0-beta.124) and Zod 4.0.0?
-
-2. Does the AI SDK v6 require a specific tool registration format when passing to `streamText()`?
-
-3. Should tools be wrapped in a different structure (e.g., using `tool()` helper function)?
-
-4. Is there a way to manually convert Zod schema to JSON Schema that OpenAI accepts?
-
-5. Are there any known issues with using workspace packages (`@tpmjs/hello`) in Next.js API routes with dynamic imports?
-
-6. Should we downgrade to Zod 3.x instead of Zod 4.0.0?
-
-7. Is there a debug mode to see what JSON Schema is being sent to OpenAI?
-
-## Additional Context
-
-- The `firecrawl-aisdk` package works correctly with the same setup
-- Build process completes successfully with no TypeScript errors
-- The tool is being loaded and passed to `streamText()` correctly
-- Error only occurs when OpenAI validates the tool schema
-- This is a monorepo using pnpm workspaces and Turborepo
-
-## Related Files
-
-- Tool definition: `packages/tools/hello/src/index.ts`
-- Tool loader: `apps/playground/src/lib/tool-loader.ts`
-- API route: `apps/playground/src/app/api/chat/route.ts`
-- Package config: `packages/tools/hello/package.json`
-- Compiled output: `packages/tools/hello/dist/index.js`
-
-## Expected Behavior
-
-Tools should be automatically converted from Zod schema to JSON Schema by AI SDK v6 and accepted by OpenAI's API.
-
-## Actual Behavior
-
-OpenAI rejects the tool schema with error: `got 'type: "None"'` instead of a valid JSON Schema object.
-
----
-
-## ✅ SOLUTION IMPLEMENTED
-
-### What We Changed
-
-Instead of using Zod schemas with `parameters`, we now use AI SDK's `tool()` helper with `jsonSchema()` for the input schema. This bypasses Zod's JSON Schema conversion entirely.
-
-### Before (Broken with Zod 4)
-
-```typescript
-import { z } from 'zod';
-
-export const helloWorldTool = {
- description: 'Returns a simple "Hello, World!" greeting message',
- parameters: z.object({
- includeTimestamp: z.boolean().optional().describe('Whether to include a timestamp'),
- }),
- execute: async ({ includeTimestamp = true }) => {
- // ...
- },
-};
-```
-
-### After (Working with jsonSchema)
-
-```typescript
-import { jsonSchema, tool } from 'ai';
-
-type HelloWorldInput = {
- includeTimestamp?: boolean;
-};
-
-export const helloWorldTool = tool({
- description: 'Returns a simple "Hello, World!" greeting message',
- inputSchema: jsonSchema({
- type: 'object',
- properties: {
- includeTimestamp: {
- type: 'boolean',
- description: 'Whether to include a timestamp in the response',
- },
- },
- additionalProperties: false,
- }),
- async execute({ includeTimestamp = true }) {
- const response: any = {
- message: 'Hello, World!',
- };
- if (includeTimestamp) {
- response.timestamp = new Date().toISOString();
- }
- return response;
- },
-});
-```
-
-### Key Changes
-
-1. **Import from `ai`**: Added `jsonSchema` and `tool` imports
-2. **Define TypeScript types**: Created `HelloWorldInput` type for type safety
-3. **Use `tool()` wrapper**: Wraps the entire tool definition
-4. **Use `jsonSchema()` for schema**: Provides explicit JSON Schema with `type: "object"` at root
-5. **Removed Zod dependency**: No longer need `zod` in package.json
-
-### Benefits
-
-- ✅ Works with OpenAI's strict schema validation
-- ✅ Explicit control over JSON Schema structure
-- ✅ Full TypeScript type safety with generic types
-- ✅ No dependency on Zod (one less package to maintain)
-- ✅ Follows AI SDK v6 best practices
-- ✅ Guaranteed `type: "object"` at root level
-
-### Updated Package Dependencies
-
-```json
-{
- "dependencies": {
- "ai": "6.0.0-beta.124"
- }
-}
-```
-
-Zod is no longer needed in tool packages that use `jsonSchema()`.
-
-### References
-
-- [AI SDK Core: tool](https://ai-sdk.dev/docs/reference/ai-sdk-core/tool)
-- [AI SDK Core: jsonSchema](https://ai-sdk.dev/docs/reference/ai-sdk-core/json-schema)
-- [GitHub Issue: Zod 4 JSON Schema compatibility](https://github.com/vercel/ai/issues/10240)
diff --git a/RAILWAY_DEPLOYMENT_NOTE.md b/RAILWAY_DEPLOYMENT_NOTE.md
deleted file mode 100644
index 2f192ce..0000000
--- a/RAILWAY_DEPLOYMENT_NOTE.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Railway Executor - Deployment Status
-
-## Issue Discovered
-
-Node.js does not support HTTP(S) imports by default, even with `--experimental-network-imports` flag (that flag doesn't exist in current Node versions).
-
-## Solutions Considered
-
-1. **Custom ESM Loader** - Complex, requires Node.js 18.19+ with `--loader` flag
-2. **fetch + eval** - Security concerns, doesn't handle ES modules properly
-3. **Bundler approach** - Would defeat the purpose of dynamic imports
-4. **Deno** - Supports HTTP imports natively, but different ecosystem
-
-## Recommended Solution
-
-Since the core issue is that we need truly dynamic runtime imports from HTTP URLs, and Node.js doesn't support this, we have **two viable paths**:
-
-### Option A: Use Deno on Railway (RECOMMENDED)
-
-Deno supports HTTP imports natively:
-
-```typescript
-// server.ts (Deno)
-import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
-
-const moduleCache = new Map();
-
-async function loadTool(url: string, exportName: string) {
- if (moduleCache.has(url)) {
- return moduleCache.get(url);
- }
-
- // Deno supports this natively!
- const module = await import(url);
- const tool = module[exportName];
- moduleCache.set(url, tool);
- return tool;
-}
-
-serve(async (req) => {
- // ... handle requests
-}, { port: 3002 });
-```
-
-**Deploy to Railway:**
-```bash
-# In Railway dashboard:
-# - Set Start Command: deno run --allow-net --allow-env server.ts
-# - Or use railway.json with deno runtime
-```
-
-### Option B: Pre-build Bundle Approach
-
-Instead of truly dynamic imports, pre-fetch and cache tools:
-
-1. Playground searches for tools
-2. Backend fetches tool code once and caches it
-3. Use `vm2` or similar to execute in sandbox
-4. Not truly "dynamic" but works with Node.js
-
-## Current Status
-
-The Railway executor service is **created** but **not deployed** because Node.js doesn't support the required HTTP imports.
-
-**Files created:**
-- `apps/railway-executor/package.json`
-- `apps/railway-executor/server.js` (incomplete - needs Deno or vm2 approach)
-- `apps/railway-executor/README.md`
-
-## Next Steps
-
-**If using Deno (recommended):**
-1. Rewrite server.js as server.ts for Deno
-2. Deploy to Railway with Deno runtime
-3. Test HTTP imports work
-4. Update playground to use Railway URL
-
-**If sticking with Node.js:**
-1. Install `vm2` package for sandboxed execution
-2. Implement fetch + vm2 approach
-3. Deploy to Railway
-4. Accept limitations (less dynamic, more complex)
-
-## Alternative: Skip Railway, Use Different Architecture
-
-Since the original issue is Next.js bundler limitations, consider:
-
-**Web Workers in Browser** - Load tools client-side using native `import()`
-- Pros: No server needed, truly dynamic
-- Cons: Exposes API keys, security concerns
-
-**Serverless Functions with Pre-installed Tools** - Deploy each tool as separate function
-- Pros: Works with Vercel/Next.js
-- Cons: Not truly dynamic, requires redeployment for new tools
-
----
-
-**Recommendation**: Use Deno on Railway. It's designed for exactly this use case.
diff --git a/RAILWAY_DYNAMIC_TOOL_LOADER.md b/RAILWAY_DYNAMIC_TOOL_LOADER.md
deleted file mode 100644
index e77b66a..0000000
--- a/RAILWAY_DYNAMIC_TOOL_LOADER.md
+++ /dev/null
@@ -1,376 +0,0 @@
-# Railway Service - Dynamic Tool Loader Implementation
-
-## Overview
-
-This document describes the Railway service implementation needed to support dynamic tool loading from esm.sh in the TPMJS playground.
-
-## Why Railway Service?
-
-Next.js/Turbopack intercepts all `import()` calls and tries to resolve them through its module graph. HTTP URLs like `https://esm.sh/...` are not supported.
-
-**Solution**: Use a plain Node.js service on Railway that:
-- Runs with `--experimental-network-imports` flag
-- Can dynamically import from HTTP URLs (esm.sh)
-- Executes tool functions and returns results
-- Is already set up for existing ToolPlayground
-
-## New Endpoint Required
-
-### `POST /load-and-describe`
-
-**Purpose**: Dynamically import a tool package and return its AI SDK tool definition (description, schema) without executing it.
-
-**Request**:
-```json
-{
- "packageName": "firecrawl-aisdk",
- "exportName": "webSearchTool",
- "version": "0.7.2",
- "importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2"
-}
-```
-
-**Response**:
-```json
-{
- "success": true,
- "tool": {
- "exportName": "webSearchTool",
- "description": "Search the web using Firecrawl",
- "inputSchema": {
- "type": "object",
- "properties": {
- "query": { "type": "string", "description": "Search query" }
- },
- "required": ["query"]
- }
- }
-}
-```
-
-**Implementation** (pseudo-code for Railway service):
-
-```javascript
-// server.js (Railway service)
-import express from 'express';
-
-const app = express();
-app.use(express.json());
-
-// Cache for imported modules
-const moduleCache = new Map();
-
-app.post('/load-and-describe', async (req, res) => {
- const { packageName, exportName, version, importUrl } = req.body;
-
- const cacheKey = `${packageName}::${exportName}`;
-
- try {
- let toolModule;
-
- // Check cache first
- if (moduleCache.has(cacheKey)) {
- console.log(`✅ Cache hit: ${cacheKey}`);
- toolModule = moduleCache.get(cacheKey);
- } else {
- // Dynamic import from esm.sh
- const url = importUrl || `https://esm.sh/${packageName}@${version}`;
- console.log(`📦 Importing: ${url}`);
-
- const module = await import(url);
- toolModule = module[exportName];
-
- if (!toolModule) {
- return res.status(404).json({
- success: false,
- error: `Export "${exportName}" not found in module`
- });
- }
-
- // Validate it's an AI SDK tool
- if (!toolModule.description || !toolModule.execute) {
- return res.status(400).json({
- success: false,
- error: `Invalid AI SDK tool structure`
- });
- }
-
- // Cache it
- moduleCache.set(cacheKey, toolModule);
- }
-
- // Extract tool definition (description + schema)
- // AI SDK v6 tools have: description, inputSchema, execute
- res.json({
- success: true,
- tool: {
- exportName,
- description: toolModule.description,
- inputSchema: toolModule.inputSchema || toolModule.parameters?.shape || {},
- }
- });
- } catch (error) {
- console.error('Failed to load tool:', error);
- res.status(500).json({
- success: false,
- error: error.message
- });
- }
-});
-
-// Start server
-const PORT = process.env.PORT || 3000;
-app.listen(PORT, () => {
- console.log(`Railway tool loader running on port ${PORT}`);
-});
-```
-
-**Railway Deployment**:
-```bash
-# Start command in Railway settings:
-node --experimental-network-imports server.js
-
-# Or in package.json:
-{
- "scripts": {
- "start": "node --experimental-network-imports server.js"
- }
-}
-```
-
-## Modified Endpoint: `POST /execute-tool`
-
-**Purpose**: Execute a dynamically loaded tool with parameters.
-
-**Request**:
-```json
-{
- "packageName": "firecrawl-aisdk",
- "exportName": "webSearchTool",
- "version": "0.7.2",
- "importUrl": "https://esm.sh/firecrawl-aisdk@0.7.2",
- "params": {
- "query": "latest AI news"
- }
-}
-```
-
-**Response**:
-```json
-{
- "success": true,
- "output": {
- "results": [...]
- },
- "executionTimeMs": 1234
-}
-```
-
-**Implementation** (pseudo-code):
-
-```javascript
-app.post('/execute-tool', async (req, res) => {
- const { packageName, exportName, version, importUrl, params } = req.body;
-
- const cacheKey = `${packageName}::${exportName}`;
- const startTime = Date.now();
-
- try {
- let toolModule;
-
- // Check cache or import
- if (moduleCache.has(cacheKey)) {
- toolModule = moduleCache.get(cacheKey);
- } else {
- const url = importUrl || `https://esm.sh/${packageName}@${version}`;
- const module = await import(url);
- toolModule = module[exportName];
-
- if (!toolModule || !toolModule.execute) {
- return res.status(404).json({
- success: false,
- error: 'Tool not found or invalid'
- });
- }
-
- moduleCache.set(cacheKey, toolModule);
- }
-
- // Execute the tool
- const result = await toolModule.execute(params);
-
- res.json({
- success: true,
- output: result,
- executionTimeMs: Date.now() - startTime
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- error: error.message,
- executionTimeMs: Date.now() - startTime
- });
- }
-});
-```
-
-## Integration with Playground
-
-### 1. Update `dynamic-tool-loader.ts`
-
-Replace local dynamic imports with Railway service calls:
-
-```typescript
-// apps/playground/src/lib/dynamic-tool-loader.ts
-
-const RAILWAY_SERVICE_URL = process.env.RAILWAY_SERVICE_URL || 'http://localhost:3001';
-
-export async function loadToolDynamically(
- packageName: string,
- exportName: string,
- version: string,
- importUrl?: string
-): Promise {
- const cacheKey = getCacheKey(packageName, exportName);
-
- // Check local cache first
- if (moduleCache.has(cacheKey)) {
- console.log(`✅ Cache hit: ${cacheKey}`);
- return moduleCache.get(cacheKey);
- }
-
- try {
- console.log(`📦 Loading from Railway: ${packageName}/${exportName}`);
-
- // Call Railway service to load and describe tool
- const response = await fetch(`${RAILWAY_SERVICE_URL}/load-and-describe`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- packageName,
- exportName,
- version,
- importUrl,
- }),
- });
-
- if (!response.ok) {
- console.error(`❌ Railway service error: ${response.status}`);
- return null;
- }
-
- const data = await response.json();
-
- if (!data.success) {
- console.error(`❌ Failed to load tool: ${data.error}`);
- return null;
- }
-
- // Create a tool wrapper that executes remotely
- const tool = {
- description: data.tool.description,
- inputSchema: data.tool.inputSchema,
- execute: async (params: any) => {
- console.log(`🚀 Executing ${packageName}/${exportName} remotely`);
-
- const execResponse = await fetch(`${RAILWAY_SERVICE_URL}/execute-tool`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- packageName,
- exportName,
- version,
- importUrl,
- params,
- }),
- });
-
- const result = await execResponse.json();
-
- if (!result.success) {
- throw new Error(result.error || 'Tool execution failed');
- }
-
- return result.output;
- },
- };
-
- // Cache the wrapper
- moduleCache.set(cacheKey, tool);
- console.log(`✅ Loaded and cached: ${cacheKey}`);
-
- return tool;
- } catch (error) {
- console.error(`❌ Failed to load ${packageName}#${exportName}:`, error);
- return null;
- }
-}
-```
-
-### 2. Environment Variables
-
-Add to `.env.local`:
-```bash
-RAILWAY_SERVICE_URL=https://your-railway-service.up.railway.app
-```
-
-Or for local testing with Railway running locally:
-```bash
-RAILWAY_SERVICE_URL=http://localhost:3001
-```
-
-## Testing Locally
-
-### Terminal 1: Run Railway service locally
-```bash
-cd railway-service
-node --experimental-network-imports server.js
-```
-
-### Terminal 2: Run playground
-```bash
-cd tpmjs
-pnpm dev --filter=@tpmjs/playground
-```
-
-### Test the flow:
-```bash
-# Test Railway service directly
-curl -X POST http://localhost:3001/load-and-describe \
- -H "Content-Type: application/json" \
- -d '{
- "packageName": "firecrawl-aisdk",
- "exportName": "webSearchTool",
- "version": "0.7.2"
- }'
-
-# Then test via playground UI
-# Navigate to http://localhost:3000/playground
-# Ask: "search the web for latest AI news"
-```
-
-## Deployment Checklist
-
-- [ ] Create Railway service with Node.js
-- [ ] Add `--experimental-network-imports` flag to start command
-- [ ] Deploy `/load-and-describe` endpoint
-- [ ] Deploy `/execute-tool` endpoint (or modify existing `/execute`)
-- [ ] Set `RAILWAY_SERVICE_URL` in Vercel environment variables
-- [ ] Test with real tools from TPMJS registry
-- [ ] Monitor Railway logs for import errors
-
-## Benefits
-
-1. ✅ **Works around Next.js limitations** - Imports happen in plain Node
-2. ✅ **Reuses existing Railway infrastructure** - No new service needed
-3. ✅ **Caching on both sides** - Local cache + Railway cache
-4. ✅ **Security** - Tools execute in Railway sandbox, not Next.js
-5. ✅ **Scalability** - Railway handles the heavy lifting
-
-## Next Steps
-
-1. Implement Railway service endpoints
-2. Update `dynamic-tool-loader.ts` to use Railway
-3. Test locally
-4. Deploy to Railway + Vercel
-5. Celebrate dynamic tool loading! 🎉
diff --git a/README.md b/README.md
index 484cd9f..6b2454d 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,59 @@
-# TPMJS Monorepo
+# TPMJS
-[](https://github.com/YOUR_ORG/tpmjs/actions/workflows/ci.yml)
+[](https://github.com/tpmjs/tpmjs/actions/workflows/ci.yml)
-Tool Package Manager for AI Agents - A Turborepo monorepo with strict TypeScript, Next.js 16, and best practices.
+**TPMJS is a registry that lets AI agents discover and use npm packages as tools at runtime.**
-## Structure
+Instead of manually importing and configuring tools, agents search by description and load what they need on-demand. Publish your npm package with the `tpmjs-tool` keyword—it appears on [tpmjs.com](https://tpmjs.com) within 15 minutes.
+
+## Why TPMJS?
+
+- **No config files** - Agents discover tools by describing what they need
+- **Always up-to-date** - Tools load from npm at runtime, no manual updates
+- **Works with any agent** - Compatible with Vercel AI SDK, LangChain, OpenAI, Claude, etc.
+- **Publish once** - Add one keyword to package.json, publish to npm, done
+
+## Quick Start
+
+**For AI agent developers:**
+```bash
+npm install @tpmjs/sdk
+```
+
+```typescript
+import { searchRegistry, executeRegistry } from '@tpmjs/sdk';
+
+// Find tools by description
+const tools = await searchRegistry({ query: 'parse PDF documents' });
+
+// Execute a tool
+const result = await executeRegistry({
+ toolId: 'pdf-parser/extractText',
+ input: { url: 'https://example.com/doc.pdf' }
+});
+```
+
+**For tool publishers:**
+```bash
+npx @tpmjs/create-basic-tools
+```
+
+Or add manually to your package.json:
+```json
+{
+ "keywords": ["tpmjs-tool"],
+ "tpmjs": {
+ "category": "text-analysis",
+ "description": "What your tool does"
+ }
+}
+```
+
+See [HOW_TO_PUBLISH_A_TOOL.md](./HOW_TO_PUBLISH_A_TOOL.md) for the full guide.
+
+---
+
+## Monorepo Structure
```
apps/
diff --git a/STREAMING_EMPTY_RESPONSE.md b/STREAMING_EMPTY_RESPONSE.md
deleted file mode 100644
index 98383d7..0000000
--- a/STREAMING_EMPTY_RESPONSE.md
+++ /dev/null
@@ -1,363 +0,0 @@
-# AI SDK v6 Streaming Empty Response Issue
-
-## Problem
-
-Using AI SDK v6 Beta with OpenAI and `streamText()`, the API route returns a 200 OK response, but the streamed response body is **completely empty** when tools are involved.
-
-- **Normal chat** (without tool calls): Works fine, streams text back
-- **Tool calls** (when user asks "say hello world"): Returns empty response body, no error messages
-
-## Environment
-
-- **AI SDK Version**: `ai@6.0.0-beta.124`
-- **OpenAI Provider**: `@ai-sdk/openai@3.0.0-beta.74`
-- **OpenAI Library**: `openai@^6.9.1`
-- **Next.js Version**: `next@^16.0.4` (App Router)
-- **Runtime**: Node.js (`runtime = 'nodejs'`)
-- **Model**: `gpt-4o-mini`
-
-## API Route Implementation
-
-Located at: `apps/playground/src/app/api/chat/route.ts`
-
-```typescript
-import { createOpenAI } from '@ai-sdk/openai';
-import { streamText, type CoreMessage, tool, jsonSchema } from 'ai';
-import { type NextRequest } from 'next/server';
-import { z } from 'zod';
-import { env } from '~/env';
-
-export const runtime = 'nodejs';
-export const dynamic = 'force-dynamic';
-export const maxDuration = 60;
-
-// Initialize OpenAI provider
-const openai = createOpenAI({
- apiKey: env.OPENAI_API_KEY,
-});
-
-// Request schema
-const RequestSchema = z.object({
- messages: z.array(
- z.object({
- role: z.enum(['user', 'assistant', 'system']),
- content: z.string(),
- })
- ),
-});
-
-// Simple inline test tool to verify streaming works
-const testHelloTool = tool({
- description: 'Returns a simple hello world greeting',
- inputSchema: jsonSchema<{ includeTimestamp?: boolean }>({
- type: 'object',
- properties: {
- includeTimestamp: {
- type: 'boolean',
- description: 'Whether to include a timestamp',
- },
- },
- additionalProperties: false,
- }),
- async execute({ includeTimestamp = true }) {
- const response: any = { message: 'Hello, World!' };
- if (includeTimestamp) {
- response.timestamp = new Date().toISOString();
- }
- return response;
- },
-});
-
-/**
- * POST /api/chat
- * Chat with AI agent that can execute TPMJS tools
- */
-export async function POST(request: NextRequest) {
- try {
- const body = await request.json();
- const { messages } = RequestSchema.parse(body);
-
- // Use simple inline tool for testing
- const tools = {
- testHello: testHelloTool,
- };
-
- // Create system prompt listing available tools
- const toolsList = Object.keys(tools)
- .map((name) => `- ${name}: ${tools[name]?.description}`)
- .join('\n');
-
- const systemMessage: CoreMessage = {
- role: 'system',
- content: `You are a helpful AI assistant that can use TPMJS tools to help users.
-
-Available tools:
-${toolsList}
-
-Call tools as needed to answer user questions. When a user asks to say hello world or for a greeting, use the testHello tool.`,
- };
-
- // Stream the response
- const result = streamText({
- model: openai('gpt-4o-mini'),
- messages: [systemMessage, ...messages],
- tools,
- });
-
- // Return the stream as SSE
- return result.toTextStreamResponse();
- } catch (error) {
- console.error('Chat API error:', error);
-
- if (error instanceof z.ZodError) {
- return new Response(
- JSON.stringify({
- success: false,
- error: 'Invalid request format',
- details: error.issues,
- }),
- {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- }
- );
- }
-
- return new Response(
- JSON.stringify({
- success: false,
- error: error instanceof Error ? error.message : 'Unknown error',
- }),
- {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- }
- );
- }
-}
-```
-
-## Client-Side Hook
-
-Located at: `apps/playground/src/hooks/useChat.ts`
-
-```typescript
-'use client';
-
-import { useCallback, useState } from 'react';
-
-export interface ChatMessage {
- id: string;
- role: 'user' | 'assistant' | 'system';
- content: string;
- timestamp: Date;
-}
-
-export function useChat() {
- const [messages, setMessages] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState(null);
-
- const sendMessage = useCallback(async (content: string) => {
- if (!content.trim()) return;
-
- // Add user message immediately
- const userMessage: ChatMessage = {
- id: crypto.randomUUID(),
- role: 'user',
- content,
- timestamp: new Date(),
- };
-
- setMessages((prev) => [...prev, userMessage]);
- setIsLoading(true);
- setError(null);
-
- try {
- // Create assistant message placeholder
- const assistantMessageId = crypto.randomUUID();
- const assistantMessage: ChatMessage = {
- id: assistantMessageId,
- role: 'assistant',
- content: '',
- timestamp: new Date(),
- };
-
- setMessages((prev) => [...prev, assistantMessage]);
-
- // Send request to API
- const response = await fetch('/api/chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- messages: [...messages, userMessage].map((m) => ({
- role: m.role,
- content: m.content,
- })),
- }),
- });
-
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
-
- // Read the streaming response
- const reader = response.body?.getReader();
- const decoder = new TextDecoder();
-
- if (!reader) {
- throw new Error('Response body is null');
- }
-
- let accumulatedContent = '';
-
- while (true) {
- const { done, value } = await reader.read();
-
- if (done) {
- break;
- }
-
- // Decode the chunk
- const chunk = decoder.decode(value, { stream: true });
- accumulatedContent += chunk;
-
- // Update the assistant message with accumulated content
- setMessages((prev) =>
- prev.map((m) =>
- m.id === assistantMessageId
- ? { ...m, content: accumulatedContent }
- : m
- )
- );
- }
- } catch (err) {
- console.error('Error sending message:', err);
- setError(err instanceof Error ? err.message : 'Failed to send message');
- } finally {
- setIsLoading(false);
- }
- }, [messages]);
-
- const clearChat = useCallback(() => {
- setMessages([]);
- setError(null);
- }, []);
-
- return {
- messages,
- isLoading,
- error,
- sendMessage,
- clearChat,
- };
-}
-```
-
-## Observed Behavior
-
-### Working Case (Normal Chat)
-- User types: "hi"
-- API response: 200 OK
-- Response body: Streams text chunks successfully
-- UI shows: "Hi! How can I help you today?"
-
-### Broken Case (Tool Call)
-- User types: "say hello world"
-- API response: 200 OK ✅
-- Response body: **EMPTY** ❌ (no chunks, no data, nothing)
-- UI shows: Empty message bubble
-- Console: No errors logged
-
-## HTTP Response Details
-
-```
-Request Method: POST
-Status Code: 200 OK
-URL: http://localhost:3001/api/chat
-Content-Type: text/plain; charset=utf-8
-Transfer-Encoding: chunked
-```
-
-The response headers look correct for a streaming response, but the body is completely empty.
-
-## What We've Tried
-
-1. ✅ Fixed OpenAI schema validation error (was `type: "None"`, now uses proper JSON Schema)
-2. ✅ Using `tool()` and `jsonSchema()` from AI SDK
-3. ✅ Simplified to a single inline test tool
-4. ✅ Tool executes without errors (no schema validation issues)
-5. ✅ Normal chat works fine (proves streaming infrastructure is correct)
-
-## Questions
-
-1. **Is `toTextStreamResponse()` the correct method for streaming with tools in AI SDK v6?**
- - Should we use a different method like `toDataStreamResponse()` for tool calls?
-
-2. **Are we constructing the messages array correctly?**
- - We're sending `{ role: 'user' | 'assistant' | 'system', content: string }[]`
- - Do we need to include tool call messages or tool result messages?
-
-3. **Does AI SDK v6 require a specific message format for tool calls?**
- - Should we be including `toolInvocations` or `tool_calls` in the message history?
- - Are we missing required fields in the `CoreMessage` type?
-
-4. **Is the client-side streaming reader correct?**
- - We're reading chunks with `response.body.getReader()`
- - Should we be parsing SSE events differently for tool calls?
-
-5. **Does `streamText()` with tools require `maxSteps` parameter?**
- - Do we need to set `maxSteps: 5` to allow multi-step reasoning?
-
-6. **Are we handling the conversation history correctly?**
- - We're sending all previous messages on each request
- - Should we be including assistant messages with tool call results?
-
-## AI SDK v6 Documentation References
-
-We're following these patterns from the official docs:
-
-- [streamText() API](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text)
-- [tool() API](https://ai-sdk.dev/docs/reference/ai-sdk-core/tool)
-- [Tool Calling Guide](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling)
-
-But we might be missing something specific about:
-- How to handle streaming when tools are executed
-- What response format tool calls produce
-- How to parse the stream when tools are involved
-
-## Suspected Issue
-
-**The message format might be wrong.** We're sending:
-
-```typescript
-const systemMessage: CoreMessage = {
- role: 'system',
- content: `You are a helpful AI assistant...`,
-};
-
-const result = streamText({
- model: openai('gpt-4o-mini'),
- messages: [systemMessage, ...messages],
- tools,
-});
-```
-
-But `CoreMessage` might need additional fields when tools are involved, or we might need to handle tool call results differently in the conversation history.
-
-## What We Need
-
-1. Correct message format for `streamText()` with tools
-2. How to properly stream responses that include tool calls
-3. Whether we need different client-side parsing for tool call streams
-4. Example of a working Next.js API route using AI SDK v6 with `streamText()` and tools
-
-## Repo Context
-
-- Monorepo using Turborepo + pnpm workspaces
-- Next.js 16 App Router with Turbopack
-- TypeScript strict mode
-- All UI components from internal `@tpmjs/ui` package
-- Tools are imported from workspace package `@tpmjs/hello`
diff --git a/USECHAT_INPUT_UNDEFINED.md b/USECHAT_INPUT_UNDEFINED.md
deleted file mode 100644
index 861026c..0000000
--- a/USECHAT_INPUT_UNDEFINED.md
+++ /dev/null
@@ -1,400 +0,0 @@
-# useChat Hook Returns Undefined Input Property
-
-## Problem
-
-Using `@ai-sdk/react`'s `useChat` hook, the `input` property is returning `undefined`, causing the application to crash when trying to call `.trim()` on it.
-
-## Error
-
-```
-TypeError: Cannot read properties of undefined (reading 'trim')
-
-at ChatInput (src/components/chat/ChatInput.tsx:41:48)
-```
-
-**Code that fails:**
-```typescript
-
- ) : null;
- case 'text':
- // ...
- case 'tool-askForConfirmation':
- case 'tool-getLocation':
- case 'tool-getWeatherInformation':
- // ...
- }
-});
-// ...
-```
-
-## Server-side Multi-Step Calls
-
-You can also use multi-step calls on the server-side with `streamText`.
-This works when all invoked tools have an `execute` function on the server side.
-
-```tsx filename='app/api/chat/route.ts' highlight="15-21,24"
-import { openai } from '@ai-sdk/openai';
-import { convertToModelMessages, streamText, UIMessage, stepCountIs } from 'ai';
-import { z } from 'zod';
-
-export async function POST(req: Request) {
- const { messages }: { messages: UIMessage[] } = await req.json();
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- messages: convertToModelMessages(messages),
- tools: {
- getWeatherInformation: {
- description: 'show the weather in a given city to the user',
- inputSchema: z.object({ city: z.string() }),
- // tool has execute function:
- execute: async ({}: { city: string }) => {
- const weatherOptions = ['sunny', 'cloudy', 'rainy', 'snowy', 'windy'];
- return weatherOptions[
- Math.floor(Math.random() * weatherOptions.length)
- ];
- },
- },
- },
- stopWhen: stepCountIs(5),
- });
-
- return result.toUIMessageStreamResponse();
-}
-```
-
-## Errors
-
-Language models can make errors when calling tools.
-By default, these errors are masked for security reasons, and show up as "An error occurred" in the UI.
-
-To surface the errors, you can use the `onError` function when calling `toUIMessageResponse`.
-
-```tsx
-export function errorHandler(error: unknown) {
- if (error == null) {
- return 'unknown error';
- }
-
- if (typeof error === 'string') {
- return error;
- }
-
- if (error instanceof Error) {
- return error.message;
- }
-
- return JSON.stringify(error);
-}
-```
-
-```tsx
-const result = streamText({
- // ...
-});
-
-return result.toUIMessageStreamResponse({
- onError: errorHandler,
-});
-```
-
-In case you are using `createUIMessageResponse`, you can use the `onError` function when calling `toUIMessageResponse`:
-
-```tsx
-const response = createUIMessageResponse({
- // ...
- async execute(dataStream) {
- // ...
- },
- onError: error => `Custom error: ${error.message}`,
-});
-```
-
----
-title: Generative User Interfaces
-description: Learn how to build Generative UI with AI SDK UI.
----
-
-# Generative User Interfaces
-
-Generative user interfaces (generative UI) is the process of allowing a large language model (LLM) to go beyond text and "generate UI". This creates a more engaging and AI-native experience for users.
-
-
-
-At the core of generative UI are [ tools ](/docs/ai-sdk-core/tools-and-tool-calling), which are functions you provide to the model to perform specialized tasks like getting the weather in a location. The model can decide when and how to use these tools based on the context of the conversation.
-
-Generative UI is the process of connecting the results of a tool call to a React component. Here's how it works:
-
-1. You provide the model with a prompt or conversation history, along with a set of tools.
-2. Based on the context, the model may decide to call a tool.
-3. If a tool is called, it will execute and return data.
-4. This data can then be passed to a React component for rendering.
-
-By passing the tool results to React components, you can create a generative UI experience that's more engaging and adaptive to your needs.
-
-## Build a Generative UI Chat Interface
-
-Let's create a chat interface that handles text-based conversations and incorporates dynamic UI elements based on model responses.
-
-### Basic Chat Implementation
-
-Start with a basic chat implementation using the `useChat` hook:
-
-```tsx filename="app/page.tsx"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-
-export default function Page() {
- const [input, setInput] = useState('');
- const { messages, sendMessage } = useChat();
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- sendMessage({ text: input });
- setInput('');
- };
-
- return (
-
- {messages.map(message => (
-
-
{message.role === 'user' ? 'User: ' : 'AI: '}
-
- {message.parts.map((part, index) => {
- if (part.type === 'text') {
- return {part.text};
- }
- return null;
- })}
-
-
- ))}
-
-
-
- );
-}
-```
-
-To handle the chat requests and model responses, set up an API route:
-
-```ts filename="app/api/chat/route.ts"
-import { openai } from '@ai-sdk/openai';
-import { streamText, convertToModelMessages, UIMessage, stepCountIs } from 'ai';
-
-export async function POST(request: Request) {
- const { messages }: { messages: UIMessage[] } = await request.json();
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- system: 'You are a friendly assistant!',
- messages: convertToModelMessages(messages),
- stopWhen: stepCountIs(5),
- });
-
- return result.toUIMessageStreamResponse();
-}
-```
-
-This API route uses the `streamText` function to process chat messages and stream the model's responses back to the client.
-
-### Create a Tool
-
-Before enhancing your chat interface with dynamic UI elements, you need to create a tool and corresponding React component. A tool will allow the model to perform a specific action, such as fetching weather information.
-
-Create a new file called `ai/tools.ts` with the following content:
-
-```ts filename="ai/tools.ts"
-import { tool as createTool } from 'ai';
-import { z } from 'zod';
-
-export const weatherTool = createTool({
- description: 'Display the weather for a location',
- inputSchema: z.object({
- location: z.string().describe('The location to get the weather for'),
- }),
- execute: async function ({ location }) {
- await new Promise(resolve => setTimeout(resolve, 2000));
- return { weather: 'Sunny', temperature: 75, location };
- },
-});
-
-export const tools = {
- displayWeather: weatherTool,
-};
-```
-
-In this file, you've created a tool called `weatherTool`. This tool simulates fetching weather information for a given location. This tool will return simulated data after a 2-second delay. In a real-world application, you would replace this simulation with an actual API call to a weather service.
-
-### Update the API Route
-
-Update the API route to include the tool you've defined:
-
-```ts filename="app/api/chat/route.ts" highlight="3,8,14"
-import { openai } from '@ai-sdk/openai';
-import { streamText, convertToModelMessages, UIMessage, stepCountIs } from 'ai';
-import { tools } from '@/ai/tools';
-
-export async function POST(request: Request) {
- const { messages }: { messages: UIMessage[] } = await request.json();
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- system: 'You are a friendly assistant!',
- messages: convertToModelMessages(messages),
- stopWhen: stepCountIs(5),
- tools,
- });
-
- return result.toUIMessageStreamResponse();
-}
-```
-
-Now that you've defined the tool and added it to your `streamText` call, let's build a React component to display the weather information it returns.
-
-### Create UI Components
-
-Create a new file called `components/weather.tsx`:
-
-```tsx filename="components/weather.tsx"
-type WeatherProps = {
- temperature: number;
- weather: string;
- location: string;
-};
-
-export const Weather = ({ temperature, weather, location }: WeatherProps) => {
- return (
-
-
Current Weather for {location}
-
Condition: {weather}
-
Temperature: {temperature}°C
-
- );
-};
-```
-
-This component will display the weather information for a given location. It takes three props: `temperature`, `weather`, and `location` (exactly what the `weatherTool` returns).
-
-### Render the Weather Component
-
-Now that you have your tool and corresponding React component, let's integrate them into your chat interface. You'll render the Weather component when the model calls the weather tool.
-
-To check if the model has called a tool, you can check the `parts` array of the UIMessage object for tool-specific parts. In AI SDK 5.0, tool parts use typed naming: `tool-${toolName}` instead of generic types.
-
-Update your `page.tsx` file:
-
-```tsx filename="app/page.tsx" highlight="4,9,14-15,19-46"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-import { Weather } from '@/components/weather';
-
-export default function Page() {
- const [input, setInput] = useState('');
- const { messages, sendMessage } = useChat();
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- sendMessage({ text: input });
- setInput('');
- };
-
- return (
-
- {messages.map(message => (
-
-
{message.role === 'user' ? 'User: ' : 'AI: '}
-
- {message.parts.map((part, index) => {
- if (part.type === 'text') {
- return
{part.text};
- }
-
- if (part.type === 'tool-displayWeather') {
- switch (part.state) {
- case 'input-available':
- return
Loading weather...
;
- case 'output-available':
- return (
-
-
-
- );
- case 'output-error':
- return
Error: {part.errorText}
;
- default:
- return null;
- }
- }
-
- return null;
- })}
-
-
- ))}
-
-
-
- );
-}
-```
-
-In this updated code snippet, you:
-
-1. Use manual input state management with `useState` instead of the built-in `input` and `handleInputChange`.
-2. Use `sendMessage` instead of `handleSubmit` to send messages.
-3. Check the `parts` array of each message for different content types.
-4. Handle tool parts with type `tool-displayWeather` and their different states (`input-available`, `output-available`, `output-error`).
-
-This approach allows you to dynamically render UI components based on the model's responses, creating a more interactive and context-aware chat experience.
-
-## Expanding Your Generative UI Application
-
-You can enhance your chat application by adding more tools and components, creating a richer and more versatile user experience. Here's how you can expand your application:
-
-### Adding More Tools
-
-To add more tools, simply define them in your `ai/tools.ts` file:
-
-```ts
-// Add a new stock tool
-export const stockTool = createTool({
- description: 'Get price for a stock',
- inputSchema: z.object({
- symbol: z.string().describe('The stock symbol to get the price for'),
- }),
- execute: async function ({ symbol }) {
- // Simulated API call
- await new Promise(resolve => setTimeout(resolve, 2000));
- return { symbol, price: 100 };
- },
-});
-
-// Update the tools object
-export const tools = {
- displayWeather: weatherTool,
- getStockPrice: stockTool,
-};
-```
-
-Now, create a new file called `components/stock.tsx`:
-
-```tsx
-type StockProps = {
- price: number;
- symbol: string;
-};
-
-export const Stock = ({ price, symbol }: StockProps) => {
- return (
-
-
Stock Information
-
Symbol: {symbol}
-
Price: ${price}
-
- );
-};
-```
-
-Finally, update your `page.tsx` file to include the new Stock component:
-
-```tsx
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-import { Weather } from '@/components/weather';
-import { Stock } from '@/components/stock';
-
-export default function Page() {
- const [input, setInput] = useState('');
- const { messages, sendMessage } = useChat();
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- sendMessage({ text: input });
- setInput('');
- };
-
- return (
-
- {messages.map(message => (
-
-
{message.role}
-
- {message.parts.map((part, index) => {
- if (part.type === 'text') {
- return
{part.text};
- }
-
- if (part.type === 'tool-displayWeather') {
- switch (part.state) {
- case 'input-available':
- return
Loading weather...
;
- case 'output-available':
- return (
-
-
-
- );
- case 'output-error':
- return
Error: {part.errorText}
;
- default:
- return null;
- }
- }
-
- if (part.type === 'tool-getStockPrice') {
- switch (part.state) {
- case 'input-available':
- return
Loading stock price...
;
- case 'output-available':
- return (
-
-
-
- );
- case 'output-error':
- return
Error: {part.errorText}
;
- default:
- return null;
- }
- }
-
- return null;
- })}
-
-
- ))}
-
-
-
- );
-}
-```
-
-By following this pattern, you can continue to add more tools and components, expanding the capabilities of your Generative UI application.
-
----
-title: Completion
-description: Learn how to use the useCompletion hook.
----
-
-# Completion
-
-The `useCompletion` hook allows you to create a user interface to handle text completions in your application. It enables the streaming of text completions from your AI provider, manages the state for chat input, and updates the UI automatically as new messages are received.
-
-
- The `useCompletion` hook is now part of the `@ai-sdk/react` package.
-
-
-In this guide, you will learn how to use the `useCompletion` hook in your application to generate text completions and stream them in real-time to your users.
-
-## Example
-
-```tsx filename='app/page.tsx'
-'use client';
-
-import { useCompletion } from '@ai-sdk/react';
-
-export default function Page() {
- const { completion, input, handleInputChange, handleSubmit } = useCompletion({
- api: '/api/completion',
- });
-
- return (
-
- );
-}
-```
-
-```ts filename='app/api/completion/route.ts'
-import { streamText } from 'ai';
-import { openai } from '@ai-sdk/openai';
-
-// Allow streaming responses up to 30 seconds
-export const maxDuration = 30;
-
-export async function POST(req: Request) {
- const { prompt }: { prompt: string } = await req.json();
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- prompt,
- });
-
- return result.toUIMessageStreamResponse();
-}
-```
-
-In the `Page` component, the `useCompletion` hook will request to your AI provider endpoint whenever the user submits a message. The completion is then streamed back in real-time and displayed in the UI.
-
-This enables a seamless text completion experience where the user can see the AI response as soon as it is available, without having to wait for the entire response to be received.
-
-## Customized UI
-
-`useCompletion` also provides ways to manage the prompt via code, show loading and error states, and update messages without being triggered by user interactions.
-
-### Loading and error states
-
-To show a loading spinner while the chatbot is processing the user's message, you can use the `isLoading` state returned by the `useCompletion` hook:
-
-```tsx
-const { isLoading, ... } = useCompletion()
-
-return(
- <>
- {isLoading ? : null}
- >
-)
-```
-
-Similarly, the `error` state reflects the error object thrown during the fetch request. It can be used to display an error message, or show a toast notification:
-
-```tsx
-const { error, ... } = useCompletion()
-
-useEffect(() => {
- if (error) {
- toast.error(error.message)
- }
-}, [error])
-
-// Or display the error message in the UI:
-return (
- <>
- {error ? {error.message}
: null}
- >
-)
-```
-
-### Controlled input
-
-In the initial example, we have `handleSubmit` and `handleInputChange` callbacks that manage the input changes and form submissions. These are handy for common use cases, but you can also use uncontrolled APIs for more advanced scenarios such as form validation or customized components.
-
-The following example demonstrates how to use more granular APIs like `setInput` with your custom input and submit button components:
-
-```tsx
-const { input, setInput } = useCompletion();
-
-return (
- <>
- setInput(value)} />
- >
-);
-```
-
-### Cancelation
-
-It's also a common use case to abort the response message while it's still streaming back from the AI provider. You can do this by calling the `stop` function returned by the `useCompletion` hook.
-
-```tsx
-const { stop, isLoading, ... } = useCompletion()
-
-return (
- <>
- Stop
- >
-)
-```
-
-When the user clicks the "Stop" button, the fetch request will be aborted. This avoids consuming unnecessary resources and improves the UX of your application.
-
-### Throttling UI Updates
-
-This feature is currently only available for React.
-
-By default, the `useCompletion` hook will trigger a render every time a new chunk is received.
-You can throttle the UI updates with the `experimental_throttle` option.
-
-```tsx filename="page.tsx" highlight="2-3"
-const { completion, ... } = useCompletion({
- // Throttle the completion and data updates to 50ms:
- experimental_throttle: 50
-})
-```
-
-## Event Callbacks
-
-`useCompletion` also provides optional event callbacks that you can use to handle different stages of the chatbot lifecycle. These callbacks can be used to trigger additional actions, such as logging, analytics, or custom UI updates.
-
-```tsx
-const { ... } = useCompletion({
- onResponse: (response: Response) => {
- console.log('Received response from server:', response)
- },
- onFinish: (prompt: string, completion: string) => {
- console.log('Finished streaming completion:', completion)
- },
- onError: (error: Error) => {
- console.error('An error occurred:', error)
- },
-})
-```
-
-It's worth noting that you can abort the processing by throwing an error in the `onResponse` callback. This will trigger the `onError` callback and stop the message from being appended to the chat UI. This can be useful for handling unexpected responses from the AI provider.
-
-## Configure Request Options
-
-By default, the `useCompletion` hook sends a HTTP POST request to the `/api/completion` endpoint with the prompt as part of the request body. You can customize the request by passing additional options to the `useCompletion` hook:
-
-```tsx
-const { messages, input, handleInputChange, handleSubmit } = useCompletion({
- api: '/api/custom-completion',
- headers: {
- Authorization: 'your_token',
- },
- body: {
- user_id: '123',
- },
- credentials: 'same-origin',
-});
-```
-
-In this example, the `useCompletion` hook sends a POST request to the `/api/completion` endpoint with the specified headers, additional body fields, and credentials for that fetch request. On your server side, you can handle the request with these additional information.
-
----
-title: Object Generation
-description: Learn how to use the useObject hook.
----
-
-# Object Generation
-
-
- `useObject` is an experimental feature and only available in React, Svelte,
- and Vue.
-
-
-The [`useObject`](/docs/reference/ai-sdk-ui/use-object) hook allows you to create interfaces that represent a structured JSON object that is being streamed.
-
-In this guide, you will learn how to use the `useObject` hook in your application to generate UIs for structured data on the fly.
-
-## Example
-
-The example shows a small notifications demo app that generates fake notifications in real-time.
-
-### Schema
-
-It is helpful to set up the schema in a separate file that is imported on both the client and server.
-
-```ts filename='app/api/notifications/schema.ts'
-import { z } from 'zod';
-
-// define a schema for the notifications
-export const notificationSchema = z.object({
- notifications: z.array(
- z.object({
- name: z.string().describe('Name of a fictional person.'),
- message: z.string().describe('Message. Do not use emojis or links.'),
- }),
- ),
-});
-```
-
-### Client
-
-The client uses [`useObject`](/docs/reference/ai-sdk-ui/use-object) to stream the object generation process.
-
-The results are partial and are displayed as they are received.
-Please note the code for handling `undefined` values in the JSX.
-
-```tsx filename='app/page.tsx'
-'use client';
-
-import { experimental_useObject as useObject } from '@ai-sdk/react';
-import { notificationSchema } from './api/notifications/schema';
-
-export default function Page() {
- const { object, submit } = useObject({
- api: '/api/notifications',
- schema: notificationSchema,
- });
-
- return (
- <>
- submit('Messages during finals week.')}>
- Generate notifications
-
-
- {object?.notifications?.map((notification, index) => (
-
-
{notification?.name}
-
{notification?.message}
-
- ))}
- >
- );
-}
-```
-
-### Server
-
-On the server, we use [`streamObject`](/docs/reference/ai-sdk-core/stream-object) to stream the object generation process.
-
-```typescript filename='app/api/notifications/route.ts'
-import { openai } from '@ai-sdk/openai';
-import { streamObject } from 'ai';
-import { notificationSchema } from './schema';
-
-// Allow streaming responses up to 30 seconds
-export const maxDuration = 30;
-
-export async function POST(req: Request) {
- const context = await req.json();
-
- const result = streamObject({
- model: 'anthropic/claude-sonnet-4.5',
- schema: notificationSchema,
- prompt:
- `Generate 3 notifications for a messages app in this context:` + context,
- });
-
- return result.toTextStreamResponse();
-}
-```
-
-## Enum Output Mode
-
-When you need to classify or categorize input into predefined options, you can use the `enum` output mode with `useObject`. This requires a specific schema structure where the object has `enum` as a key with `z.enum` containing your possible values.
-
-### Example: Text Classification
-
-This example shows how to build a simple text classifier that categorizes statements as true or false.
-
-#### Client
-
-When using `useObject` with enum output mode, your schema must be an object with `enum` as the key:
-
-```tsx filename='app/classify/page.tsx'
-'use client';
-
-import { experimental_useObject as useObject } from '@ai-sdk/react';
-import { z } from 'zod';
-
-export default function ClassifyPage() {
- const { object, submit, isLoading } = useObject({
- api: '/api/classify',
- schema: z.object({ enum: z.enum(['true', 'false']) }),
- });
-
- return (
- <>
- submit('The earth is flat')} disabled={isLoading}>
- Classify statement
-
-
- {object && Classification: {object.enum}
}
- >
- );
-}
-```
-
-#### Server
-
-On the server, use `streamObject` with `output: 'enum'` to stream the classification result:
-
-```typescript filename='app/api/classify/route.ts'
-import { openai } from '@ai-sdk/openai';
-import { streamObject } from 'ai';
-
-export async function POST(req: Request) {
- const context = await req.json();
-
- const result = streamObject({
- model: 'anthropic/claude-sonnet-4.5',
- output: 'enum',
- enum: ['true', 'false'],
- prompt: `Classify this statement as true or false: ${context}`,
- });
-
- return result.toTextStreamResponse();
-}
-```
-
-## Customized UI
-
-`useObject` also provides ways to show loading and error states:
-
-### Loading State
-
-The `isLoading` state returned by the `useObject` hook can be used for several
-purposes:
-
-- To show a loading spinner while the object is generated.
-- To disable the submit button.
-
-```tsx filename='app/page.tsx' highlight="6,13-20,24"
-'use client';
-
-import { useObject } from '@ai-sdk/react';
-
-export default function Page() {
- const { isLoading, object, submit } = useObject({
- api: '/api/notifications',
- schema: notificationSchema,
- });
-
- return (
- <>
- {isLoading && }
-
- submit('Messages during finals week.')}
- disabled={isLoading}
- >
- Generate notifications
-
-
- {object?.notifications?.map((notification, index) => (
-
-
{notification?.name}
-
{notification?.message}
-
- ))}
- >
- );
-}
-```
-
-### Stop Handler
-
-The `stop` function can be used to stop the object generation process. This can be useful if the user wants to cancel the request or if the server is taking too long to respond.
-
-```tsx filename='app/page.tsx' highlight="6,14-16"
-'use client';
-
-import { useObject } from '@ai-sdk/react';
-
-export default function Page() {
- const { isLoading, stop, object, submit } = useObject({
- api: '/api/notifications',
- schema: notificationSchema,
- });
-
- return (
- <>
- {isLoading && (
- stop()}>
- Stop
-
- )}
-
- submit('Messages during finals week.')}>
- Generate notifications
-
-
- {object?.notifications?.map((notification, index) => (
-
-
{notification?.name}
-
{notification?.message}
-
- ))}
- >
- );
-}
-```
-
-### Error State
-
-Similarly, the `error` state reflects the error object thrown during the fetch request.
-It can be used to display an error message, or to disable the submit button:
-
-
- We recommend showing a generic error message to the user, such as "Something
- went wrong." This is a good practice to avoid leaking information from the
- server.
-
-
-```tsx file="app/page.tsx" highlight="6,13"
-'use client';
-
-import { useObject } from '@ai-sdk/react';
-
-export default function Page() {
- const { error, object, submit } = useObject({
- api: '/api/notifications',
- schema: notificationSchema,
- });
-
- return (
- <>
- {error && An error occurred.
}
-
- submit('Messages during finals week.')}>
- Generate notifications
-
-
- {object?.notifications?.map((notification, index) => (
-
-
{notification?.name}
-
{notification?.message}
-
- ))}
- >
- );
-}
-```
-
-## Event Callbacks
-
-`useObject` provides optional event callbacks that you can use to handle life-cycle events.
-
-- `onFinish`: Called when the object generation is completed.
-- `onError`: Called when an error occurs during the fetch request.
-
-These callbacks can be used to trigger additional actions, such as logging, analytics, or custom UI updates.
-
-```tsx filename='app/page.tsx' highlight="10-20"
-'use client';
-
-import { experimental_useObject as useObject } from '@ai-sdk/react';
-import { notificationSchema } from './api/notifications/schema';
-
-export default function Page() {
- const { object, submit } = useObject({
- api: '/api/notifications',
- schema: notificationSchema,
- onFinish({ object, error }) {
- // typed object, undefined if schema validation fails:
- console.log('Object generation completed:', object);
-
- // error, undefined if schema validation succeeds:
- console.log('Schema validation error:', error);
- },
- onError(error) {
- // error during fetch request:
- console.error('An error occurred:', error);
- },
- });
-
- return (
-
-
submit('Messages during finals week.')}>
- Generate notifications
-
-
- {object?.notifications?.map((notification, index) => (
-
-
{notification?.name}
-
{notification?.message}
-
- ))}
-
- );
-}
-```
-
-## Configure Request Options
-
-You can configure the API endpoint, optional headers and credentials using the `api`, `headers` and `credentials` settings.
-
-```tsx highlight="2-5"
-const { submit, object } = useObject({
- api: '/api/use-object',
- headers: {
- 'X-Custom-Header': 'CustomValue',
- },
- credentials: 'include',
- schema: yourSchema,
-});
-```
-
----
-title: Streaming Custom Data
-description: Learn how to stream custom data from the server to the client.
----
-
-# Streaming Custom Data
-
-It is often useful to send additional data alongside the model's response.
-For example, you may want to send status information, the message ids after storing them,
-or references to content that the language model is referring to.
-
-The AI SDK provides several helpers that allows you to stream additional data to the client
-and attach it to the `UIMessage` parts array:
-
-- `createUIMessageStream`: creates a data stream
-- `createUIMessageStreamResponse`: creates a response object that streams data
-- `pipeUIMessageStreamToResponse`: pipes a data stream to a server response object
-
-The data is streamed as part of the response stream using Server-Sent Events.
-
-## Setting Up Type-Safe Data Streaming
-
-First, define your custom message type with data part schemas for type safety:
-
-```tsx filename="ai/types.ts"
-import { UIMessage } from 'ai';
-
-// Define your custom message type with data part schemas
-export type MyUIMessage = UIMessage<
- never, // metadata type
- {
- weather: {
- city: string;
- weather?: string;
- status: 'loading' | 'success';
- };
- notification: {
- message: string;
- level: 'info' | 'warning' | 'error';
- };
- } // data parts type
->;
-```
-
-## Streaming Data from the Server
-
-In your server-side route handler, you can create a `UIMessageStream` and then pass it to `createUIMessageStreamResponse`:
-
-```tsx filename="route.ts"
-import { openai } from '@ai-sdk/openai';
-import {
- createUIMessageStream,
- createUIMessageStreamResponse,
- streamText,
- convertToModelMessages,
-} from 'ai';
-import type { MyUIMessage } from '@/ai/types';
-
-export async function POST(req: Request) {
- const { messages } = await req.json();
-
- const stream = createUIMessageStream({
- execute: ({ writer }) => {
- // 1. Send initial status (transient - won't be added to message history)
- writer.write({
- type: 'data-notification',
- data: { message: 'Processing your request...', level: 'info' },
- transient: true, // This part won't be added to message history
- });
-
- // 2. Send sources (useful for RAG use cases)
- writer.write({
- type: 'source',
- value: {
- type: 'source',
- sourceType: 'url',
- id: 'source-1',
- url: 'https://weather.com',
- title: 'Weather Data Source',
- },
- });
-
- // 3. Send data parts with loading state
- writer.write({
- type: 'data-weather',
- id: 'weather-1',
- data: { city: 'San Francisco', status: 'loading' },
- });
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- messages: convertToModelMessages(messages),
- onFinish() {
- // 4. Update the same data part (reconciliation)
- writer.write({
- type: 'data-weather',
- id: 'weather-1', // Same ID = update existing part
- data: {
- city: 'San Francisco',
- weather: 'sunny',
- status: 'success',
- },
- });
-
- // 5. Send completion notification (transient)
- writer.write({
- type: 'data-notification',
- data: { message: 'Request completed', level: 'info' },
- transient: true, // Won't be added to message history
- });
- },
- });
-
- writer.merge(result.toUIMessageStream());
- },
- });
-
- return createUIMessageStreamResponse({ stream });
-}
-```
-
-
- You can also send stream data from custom backends, e.g. Python / FastAPI,
- using the [UI Message Stream
- Protocol](/docs/ai-sdk-ui/stream-protocol#ui-message-stream-protocol).
-
-
-## Types of Streamable Data
-
-### Data Parts (Persistent)
-
-Regular data parts are added to the message history and appear in `message.parts`:
-
-```tsx
-writer.write({
- type: 'data-weather',
- id: 'weather-1', // Optional: enables reconciliation
- data: { city: 'San Francisco', status: 'loading' },
-});
-```
-
-### Sources
-
-Sources are useful for RAG implementations where you want to show which documents or URLs were referenced:
-
-```tsx
-writer.write({
- type: 'source',
- value: {
- type: 'source',
- sourceType: 'url',
- id: 'source-1',
- url: 'https://example.com',
- title: 'Example Source',
- },
-});
-```
-
-### Transient Data Parts (Ephemeral)
-
-Transient parts are sent to the client but not added to the message history. They are only accessible via the `onData` useChat handler:
-
-```tsx
-// server
-writer.write({
- type: 'data-notification',
- data: { message: 'Processing...', level: 'info' },
- transient: true, // Won't be added to message history
-});
-
-// client
-const [notification, setNotification] = useState();
-
-const { messages } = useChat({
- onData: ({ data, type }) => {
- if (type === 'data-notification') {
- setNotification({ message: data.message, level: data.level });
- }
- },
-});
-```
-
-## Data Part Reconciliation
-
-When you write to a data part with the same ID, the client automatically reconciles and updates that part. This enables powerful dynamic experiences like:
-
-- **Collaborative artifacts** - Update code, documents, or designs in real-time
-- **Progressive data loading** - Show loading states that transform into final results
-- **Live status updates** - Update progress bars, counters, or status indicators
-- **Interactive components** - Build UI elements that evolve based on user interaction
-
-The reconciliation happens automatically - simply use the same `id` when writing to the stream.
-
-## Processing Data on the Client
-
-### Using the onData Callback
-
-The `onData` callback is essential for handling streaming data, especially transient parts:
-
-```tsx filename="page.tsx"
-import { useChat } from '@ai-sdk/react';
-import type { MyUIMessage } from '@/ai/types';
-
-const { messages } = useChat({
- api: '/api/chat',
- onData: dataPart => {
- // Handle all data parts as they arrive (including transient parts)
- console.log('Received data part:', dataPart);
-
- // Handle different data part types
- if (dataPart.type === 'data-weather') {
- console.log('Weather update:', dataPart.data);
- }
-
- // Handle transient notifications (ONLY available here, not in message.parts)
- if (dataPart.type === 'data-notification') {
- showToast(dataPart.data.message, dataPart.data.level);
- }
- },
-});
-```
-
-**Important:** Transient data parts are **only** available through the `onData` callback. They will not appear in the `message.parts` array since they're not added to message history.
-
-### Rendering Persistent Data Parts
-
-You can filter and render data parts from the message parts array:
-
-```tsx filename="page.tsx"
-const result = (
- <>
- {messages?.map(message => (
-
- {/* Render weather data parts */}
- {message.parts
- .filter(part => part.type === 'data-weather')
- .map((part, index) => (
-
- {part.data.status === 'loading' ? (
- <>Getting weather for {part.data.city}...>
- ) : (
- <>
- Weather in {part.data.city}: {part.data.weather}
- >
- )}
-
- ))}
-
- {/* Render text content */}
- {message.parts
- .filter(part => part.type === 'text')
- .map((part, index) => (
-
{part.text}
- ))}
-
- {/* Render sources */}
- {message.parts
- .filter(part => part.type === 'source')
- .map((part, index) => (
-
- ))}
-
- ))}
- >
-);
-```
-
-### Complete Example
-
-```tsx filename="page.tsx"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-import type { MyUIMessage } from '@/ai/types';
-
-export default function Chat() {
- const [input, setInput] = useState('');
-
- const { messages, sendMessage } = useChat({
- api: '/api/chat',
- onData: dataPart => {
- // Handle transient notifications
- if (dataPart.type === 'data-notification') {
- console.log('Notification:', dataPart.data.message);
- }
- },
- });
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- sendMessage({ text: input });
- setInput('');
- };
-
- return (
- <>
- {messages?.map(message => (
-
- {message.role === 'user' ? 'User: ' : 'AI: '}
-
- {/* Render weather data */}
- {message.parts
- .filter(part => part.type === 'data-weather')
- .map((part, index) => (
-
- {part.data.status === 'loading' ? (
- <>Getting weather for {part.data.city}...>
- ) : (
- <>
- Weather in {part.data.city}: {part.data.weather}
- >
- )}
-
- ))}
-
- {/* Render text content */}
- {message.parts
- .filter(part => part.type === 'text')
- .map((part, index) => (
-
{part.text}
- ))}
-
- ))}
-
-
- >
- );
-}
-```
-
-## Use Cases
-
-- **RAG Applications** - Stream sources and retrieved documents
-- **Real-time Status** - Show loading states and progress updates
-- **Collaborative Tools** - Stream live updates to shared artifacts
-- **Analytics** - Send usage data without cluttering message history
-- **Notifications** - Display temporary alerts and status messages
-
-## Message Metadata vs Data Parts
-
-Both [message metadata](/docs/ai-sdk-ui/message-metadata) and data parts allow you to send additional information alongside messages, but they serve different purposes:
-
-### Message Metadata
-
-Message metadata is best for **message-level information** that describes the message as a whole:
-
-- Attached at the message level via `message.metadata`
-- Sent using the `messageMetadata` callback in `toUIMessageStreamResponse`
-- Ideal for: timestamps, model info, token usage, user context
-- Type-safe with custom metadata types
-
-```ts
-// Server: Send metadata about the message
-return result.toUIMessageStreamResponse({
- messageMetadata: ({ part }) => {
- if (part.type === 'finish') {
- return {
- model: part.response.modelId,
- totalTokens: part.totalUsage.totalTokens,
- createdAt: Date.now(),
- };
- }
- },
-});
-```
-
-### Data Parts
-
-Data parts are best for streaming **dynamic arbitrary data**:
-
-- Added to the message parts array via `message.parts`
-- Streamed using `createUIMessageStream` and `writer.write()`
-- Can be reconciled/updated using the same ID
-- Support transient parts that don't persist
-- Ideal for: dynamic content, loading states, interactive components
-
-```ts
-// Server: Stream data as part of message content
-writer.write({
- type: 'data-weather',
- id: 'weather-1',
- data: { city: 'San Francisco', status: 'loading' },
-});
-```
-
-For more details on message metadata, see the [Message Metadata documentation](/docs/ai-sdk-ui/message-metadata).
-
----
-title: Error Handling
-description: Learn how to handle errors in the AI SDK UI
----
-
-# Error Handling and warnings
-
-## Warnings
-
-The AI SDK shows warnings when something might not work as expected. These warnings help you fix problems before they cause errors.
-
-### When Warnings Appear
-
-Warnings are shown in the browser console when:
-
-- **Unsupported settings**: You use a setting that the AI model doesn't support
-- **Unsupported tools**: You use a tool that the AI model can't use
-- **Other issues**: The AI model reports other problems
-
-### Warning Messages
-
-All warnings start with "AI SDK Warning:" so you can easily find them. For example:
-
-```
-AI SDK Warning: The "temperature" setting is not supported by this model
-AI SDK Warning: The tool "calculator" is not supported by this model
-```
-
-### Turning Off Warnings
-
-By default, warnings are shown in the console. You can control this behavior:
-
-#### Turn Off All Warnings
-
-Set a global variable to turn off warnings completely:
-
-```ts
-globalThis.AI_SDK_LOG_WARNINGS = false;
-```
-
-#### Custom Warning Handler
-
-You can also provide your own function to handle warnings:
-
-```ts
-globalThis.AI_SDK_LOG_WARNINGS = warnings => {
- // Handle warnings your own way
- warnings.forEach(warning => {
- // Your custom logic here
- console.log('Custom warning:', warning);
- });
-};
-```
-
-
- Custom warning functions are experimental and can change in patch releases
- without notice.
-
-
-## Error Handling
-
-### Error Helper Object
-
-Each AI SDK UI hook also returns an [error](/docs/reference/ai-sdk-ui/use-chat#error) object that you can use to render the error in your UI.
-You can use the error object to show an error message, disable the submit button, or show a retry button.
-
-
- We recommend showing a generic error message to the user, such as "Something
- went wrong." This is a good practice to avoid leaking information from the
- server.
-
-
-```tsx file="app/page.tsx" highlight="7,18-25,31"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-
-export default function Chat() {
- const [input, setInput] = useState('');
- const { messages, sendMessage, error, regenerate } = useChat();
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- sendMessage({ text: input });
- setInput('');
- };
-
- return (
-
- {messages.map(m => (
-
- {m.role}:{' '}
- {m.parts
- .filter(part => part.type === 'text')
- .map(part => part.text)
- .join('')}
-
- ))}
-
- {error && (
- <>
-
An error occurred.
-
regenerate()}>
- Retry
-
- >
- )}
-
-
-
- );
-}
-```
-
-#### Alternative: replace last message
-
-Alternatively you can write a custom submit handler that replaces the last message when an error is present.
-
-```tsx file="app/page.tsx" highlight="17-23,35"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { useState } from 'react';
-
-export default function Chat() {
- const [input, setInput] = useState('');
- const { sendMessage, error, messages, setMessages } = useChat();
-
- function customSubmit(event: React.FormEvent) {
- event.preventDefault();
-
- if (error != null) {
- setMessages(messages.slice(0, -1)); // remove last message
- }
-
- sendMessage({ text: input });
- setInput('');
- }
-
- return (
-
- {messages.map(m => (
-
- {m.role}:{' '}
- {m.parts
- .filter(part => part.type === 'text')
- .map(part => part.text)
- .join('')}
-
- ))}
-
- {error &&
An error occurred.
}
-
-
-
- );
-}
-```
-
-### Error Handling Callback
-
-Errors can be processed by passing an [`onError`](/docs/reference/ai-sdk-ui/use-chat#on-error) callback function as an option to the [`useChat`](/docs/reference/ai-sdk-ui/use-chat) or [`useCompletion`](/docs/reference/ai-sdk-ui/use-completion) hooks.
-The callback function receives an error object as an argument.
-
-```tsx file="app/page.tsx" highlight="6-9"
-import { useChat } from '@ai-sdk/react';
-
-export default function Page() {
- const {
- /* ... */
- } = useChat({
- // handle error:
- onError: error => {
- console.error(error);
- },
- });
-}
-```
-
-### Injecting Errors for Testing
-
-You might want to create errors for testing.
-You can easily do so by throwing an error in your route handler:
-
-```ts file="app/api/chat/route.ts"
-export async function POST(req: Request) {
- throw new Error('This is a test error');
-}
-```
-
----
-title: Transport
-description: Learn how to use custom transports with useChat.
----
-
-# Transport
-
-The `useChat` transport system provides fine-grained control over how messages are sent to your API endpoints and how responses are processed. This is particularly useful for alternative communication protocols like WebSockets, custom authentication patterns, or specialized backend integrations.
-
-## Default Transport
-
-By default, `useChat` uses HTTP POST requests to send messages to `/api/chat`:
-
-```tsx
-import { useChat } from '@ai-sdk/react';
-
-// Uses default HTTP transport
-const { messages, sendMessage } = useChat();
-```
-
-This is equivalent to:
-
-```tsx
-import { useChat } from '@ai-sdk/react';
-import { DefaultChatTransport } from 'ai';
-
-const { messages, sendMessage } = useChat({
- transport: new DefaultChatTransport({
- api: '/api/chat',
- }),
-});
-```
-
-## Custom Transport Configuration
-
-Configure the default transport with custom options:
-
-```tsx
-import { useChat } from '@ai-sdk/react';
-import { DefaultChatTransport } from 'ai';
-
-const { messages, sendMessage } = useChat({
- transport: new DefaultChatTransport({
- api: '/api/custom-chat',
- headers: {
- Authorization: 'Bearer your-token',
- 'X-API-Version': '2024-01',
- },
- credentials: 'include',
- }),
-});
-```
-
-### Dynamic Configuration
-
-You can also provide functions that return configuration values. This is useful for authentication tokens that need to be refreshed, or for configuration that depends on runtime conditions:
-
-```tsx
-const { messages, sendMessage } = useChat({
- transport: new DefaultChatTransport({
- api: '/api/chat',
- headers: () => ({
- Authorization: `Bearer ${getAuthToken()}`,
- 'X-User-ID': getCurrentUserId(),
- }),
- body: () => ({
- sessionId: getCurrentSessionId(),
- preferences: getUserPreferences(),
- }),
- credentials: () => 'include',
- }),
-});
-```
-
-### Request Transformation
-
-Transform requests before sending to your API:
-
-```tsx
-const { messages, sendMessage } = useChat({
- transport: new DefaultChatTransport({
- api: '/api/chat',
- prepareSendMessagesRequest: ({ id, messages, trigger, messageId }) => {
- return {
- headers: {
- 'X-Session-ID': id,
- },
- body: {
- messages: messages.slice(-10), // Only send last 10 messages
- trigger,
- messageId,
- },
- };
- },
- }),
-});
-```
-
-## Building Custom Transports
-
-To understand how to build your own transport, refer to the source code of the default implementation:
-
-- **[DefaultChatTransport](https://github.com/vercel/ai/blob/main/packages/ai/src/ui/default-chat-transport.ts)** - The complete default HTTP transport implementation
-- **[HttpChatTransport](https://github.com/vercel/ai/blob/main/packages/ai/src/ui/http-chat-transport.ts)** - Base HTTP transport with request handling
-- **[ChatTransport Interface](https://github.com/vercel/ai/blob/main/packages/ai/src/ui/chat-transport.ts)** - The transport interface you need to implement
-
-These implementations show you exactly how to:
-
-- Handle the `sendMessages` method
-- Process UI message streams
-- Transform requests and responses
-- Handle errors and connection management
-
-The transport system gives you complete control over how your chat application communicates, enabling integration with any backend protocol or service.
-
----
-title: Reading UIMessage Streams
-description: Learn how to read UIMessage streams.
----
-
-# Reading UI Message Streams
-
-`UIMessage` streams are useful outside of traditional chat use cases. You can consume them for terminal UIs, custom stream processing on the client, or React Server Components (RSC).
-
-The `readUIMessageStream` helper transforms a stream of `UIMessageChunk` objects into an `AsyncIterableStream` of `UIMessage` objects, allowing you to process messages as they're being constructed.
-
-## Basic Usage
-
-```tsx
-import { openai } from '@ai-sdk/openai';
-import { readUIMessageStream, streamText } from 'ai';
-
-async function main() {
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- prompt: 'Write a short story about a robot.',
- });
-
- for await (const uiMessage of readUIMessageStream({
- stream: result.toUIMessageStream(),
- })) {
- console.log('Current message state:', uiMessage);
- }
-}
-```
-
-## Tool Calls Integration
-
-Handle streaming responses that include tool calls:
-
-```tsx
-import { openai } from '@ai-sdk/openai';
-import { readUIMessageStream, streamText, tool } from 'ai';
-import { z } from 'zod';
-
-async function handleToolCalls() {
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- tools: {
- weather: tool({
- description: 'Get the weather in a location',
- inputSchema: z.object({
- location: z.string().describe('The location to get the weather for'),
- }),
- execute: ({ location }) => ({
- location,
- temperature: 72 + Math.floor(Math.random() * 21) - 10,
- }),
- }),
- },
- prompt: 'What is the weather in Tokyo?',
- });
-
- for await (const uiMessage of readUIMessageStream({
- stream: result.toUIMessageStream(),
- })) {
- // Handle different part types
- uiMessage.parts.forEach(part => {
- switch (part.type) {
- case 'text':
- console.log('Text:', part.text);
- break;
- case 'tool-call':
- console.log('Tool called:', part.toolName, 'with args:', part.args);
- break;
- case 'tool-result':
- console.log('Tool result:', part.result);
- break;
- }
- });
- }
-}
-```
-
-## Resuming Conversations
-
-Resume streaming from a previous message state:
-
-```tsx
-import { readUIMessageStream, streamText } from 'ai';
-
-async function resumeConversation(lastMessage: UIMessage) {
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- messages: [
- { role: 'user', content: 'Continue our previous conversation.' },
- ],
- });
-
- // Resume from the last message
- for await (const uiMessage of readUIMessageStream({
- stream: result.toUIMessageStream(),
- message: lastMessage, // Resume from this message
- })) {
- console.log('Resumed message:', uiMessage);
- }
-}
-```
-
----
-title: Message Metadata
-description: Learn how to attach and use metadata with messages in AI SDK UI
----
-
-# Message Metadata
-
-Message metadata allows you to attach custom information to messages at the message level. This is useful for tracking timestamps, model information, token usage, user context, and other message-level data.
-
-## Overview
-
-Message metadata differs from [data parts](/docs/ai-sdk-ui/streaming-data) in that it's attached at the message level rather than being part of the message content. While data parts are ideal for dynamic content that forms part of the message, metadata is perfect for information about the message itself.
-
-## Getting Started
-
-Here's a simple example of using message metadata to track timestamps and model information:
-
-### Defining Metadata Types
-
-First, define your metadata type for type safety:
-
-```tsx filename="app/types.ts"
-import { UIMessage } from 'ai';
-import { z } from 'zod';
-
-// Define your metadata schema
-export const messageMetadataSchema = z.object({
- createdAt: z.number().optional(),
- model: z.string().optional(),
- totalTokens: z.number().optional(),
-});
-
-export type MessageMetadata = z.infer;
-
-// Create a typed UIMessage
-export type MyUIMessage = UIMessage;
-```
-
-### Sending Metadata from the Server
-
-Use the `messageMetadata` callback in `toUIMessageStreamResponse` to send metadata at different streaming stages:
-
-```ts filename="app/api/chat/route.ts" highlight="11-20"
-import { openai } from '@ai-sdk/openai';
-import { convertToModelMessages, streamText } from 'ai';
-import type { MyUIMessage } from '@/types';
-
-export async function POST(req: Request) {
- const { messages }: { messages: MyUIMessage[] } = await req.json();
-
- const result = streamText({
- model: 'anthropic/claude-sonnet-4.5',
- messages: convertToModelMessages(messages),
- });
-
- return result.toUIMessageStreamResponse({
- originalMessages: messages, // pass this in for type-safe return objects
- messageMetadata: ({ part }) => {
- // Send metadata when streaming starts
- if (part.type === 'start') {
- return {
- createdAt: Date.now(),
- model: 'gpt-5.1',
- };
- }
-
- // Send additional metadata when streaming completes
- if (part.type === 'finish') {
- return {
- totalTokens: part.totalUsage.totalTokens,
- };
- }
- },
- });
-}
-```
-
-
- To enable type-safe metadata return object in `messageMetadata`, pass in the
- `originalMessages` parameter typed to your UIMessage type.
-
-
-### Accessing Metadata on the Client
-
-Access metadata through the `message.metadata` property:
-
-```tsx filename="app/page.tsx" highlight="8,18-23"
-'use client';
-
-import { useChat } from '@ai-sdk/react';
-import { DefaultChatTransport } from 'ai';
-import type { MyUIMessage } from '@/types';
-
-export default function Chat() {
- const { messages } = useChat({
- transport: new DefaultChatTransport({
- api: '/api/chat',
- }),
- });
-
- return (
-
- {messages.map(message => (
-
-
- {message.role === 'user' ? 'User: ' : 'AI: '}
- {message.metadata?.createdAt && (
-
- {new Date(message.metadata.createdAt).toLocaleTimeString()}
-
- )}
-
-
- {/* Render message content */}
- {message.parts.map((part, index) =>
- part.type === 'text' ?
{part.text}
: null,
- )}
-
- {/* Display additional metadata */}
- {message.metadata?.totalTokens && (
-
- {message.metadata.totalTokens} tokens
-
- )}
-
- ))}
-
- );
-}
-```
-
-
- For streaming arbitrary data that changes during generation, consider using
- [data parts](/docs/ai-sdk-ui/streaming-data) instead.
-
-
-## Common Use Cases
-
-Message metadata is ideal for:
-
-- **Timestamps**: When messages were created or completed
-- **Model Information**: Which AI model was used
-- **Token Usage**: Track costs and usage limits
-- **User Context**: User IDs, session information
-- **Performance Metrics**: Generation time, time to first token
-- **Quality Indicators**: Finish reason, confidence scores
-
-## See Also
-
-- [Chatbot Guide](/docs/ai-sdk-ui/chatbot#message-metadata) - Message metadata in the context of building chatbots
-- [Streaming Data](/docs/ai-sdk-ui/streaming-data#message-metadata-vs-data-parts) - Comparison with data parts
-- [UIMessage Reference](/docs/reference/ai-sdk-core/ui-message) - Complete UIMessage type reference
-
----
-title: AI_APICallError
-description: Learn how to fix AI_APICallError
----
-
-# AI_APICallError
-
-This error occurs when an API call fails.
-
-## Properties
-
-- `url`: The URL of the API request that failed
-- `requestBodyValues`: The request body values sent to the API
-- `statusCode`: The HTTP status code returned by the API
-- `responseHeaders`: The response headers returned by the API
-- `responseBody`: The response body returned by the API
-- `isRetryable`: Whether the request can be retried based on the status code
-- `data`: Any additional data associated with the error
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_APICallError` using:
-
-```typescript
-import { APICallError } from 'ai';
-
-if (APICallError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_DownloadError
-description: Learn how to fix AI_DownloadError
----
-
-# AI_DownloadError
-
-This error occurs when a download fails.
-
-## Properties
-
-- `url`: The URL that failed to download
-- `statusCode`: The HTTP status code returned by the server
-- `statusText`: The HTTP status text returned by the server
-- `message`: The error message containing details about the download failure
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_DownloadError` using:
-
-```typescript
-import { DownloadError } from 'ai';
-
-if (DownloadError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_EmptyResponseBodyError
-description: Learn how to fix AI_EmptyResponseBodyError
----
-
-# AI_EmptyResponseBodyError
-
-This error occurs when the server returns an empty response body.
-
-## Properties
-
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_EmptyResponseBodyError` using:
-
-```typescript
-import { EmptyResponseBodyError } from 'ai';
-
-if (EmptyResponseBodyError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidArgumentError
-description: Learn how to fix AI_InvalidArgumentError
----
-
-# AI_InvalidArgumentError
-
-This error occurs when an invalid argument was provided.
-
-## Properties
-
-- `parameter`: The name of the parameter that is invalid
-- `value`: The invalid value
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidArgumentError` using:
-
-```typescript
-import { InvalidArgumentError } from 'ai';
-
-if (InvalidArgumentError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidDataContentError
-description: How to fix AI_InvalidDataContentError
----
-
-# AI_InvalidDataContentError
-
-This error occurs when the data content provided in a multi-modal message part is invalid. Check out the [ prompt examples for multi-modal messages ](/docs/foundations/prompts#message-prompts).
-
-## Properties
-
-- `content`: The invalid content value
-- `message`: The error message describing the expected and received content types
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidDataContentError` using:
-
-```typescript
-import { InvalidDataContentError } from 'ai';
-
-if (InvalidDataContentError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidDataContent
-description: Learn how to fix AI_InvalidDataContent
----
-
-# AI_InvalidDataContent
-
-This error occurs when invalid data content is provided.
-
-## Properties
-
-- `content`: The invalid content value
-- `message`: The error message
-- `cause`: The cause of the error
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidDataContent` using:
-
-```typescript
-import { InvalidDataContent } from 'ai';
-
-if (InvalidDataContent.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidMessageRoleError
-description: Learn how to fix AI_InvalidMessageRoleError
----
-
-# AI_InvalidMessageRoleError
-
-This error occurs when an invalid message role is provided.
-
-## Properties
-
-- `role`: The invalid role value
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidMessageRoleError` using:
-
-```typescript
-import { InvalidMessageRoleError } from 'ai';
-
-if (InvalidMessageRoleError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidPromptError
-description: Learn how to fix AI_InvalidPromptError
----
-
-# AI_InvalidPromptError
-
-This error occurs when the prompt provided is invalid.
-
-## Potential Causes
-
-### UI Messages
-
-You are passing a `UIMessage[]` as messages into e.g. `streamText`.
-
-You need to first convert them to a `ModelMessage[]` using `convertToModelMessages()`.
-
-```typescript
-import { type UIMessage, generateText, convertToModelMessages } from 'ai';
-
-const messages: UIMessage[] = [
- /* ... */
-];
-
-const result = await generateText({
- // ...
- messages: convertToModelMessages(messages),
-});
-```
-
-## Properties
-
-- `prompt`: The invalid prompt value
-- `message`: The error message
-- `cause`: The cause of the error
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidPromptError` using:
-
-```typescript
-import { InvalidPromptError } from 'ai';
-
-if (InvalidPromptError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidResponseDataError
-description: Learn how to fix AI_InvalidResponseDataError
----
-
-# AI_InvalidResponseDataError
-
-This error occurs when the server returns a response with invalid data content.
-
-## Properties
-
-- `data`: The invalid response data value
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidResponseDataError` using:
-
-```typescript
-import { InvalidResponseDataError } from 'ai';
-
-if (InvalidResponseDataError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_InvalidToolInputError
-description: Learn how to fix AI_InvalidToolInputError
----
-
-# AI_InvalidToolInputError
-
-This error occurs when invalid tool input was provided.
-
-## Properties
-
-- `toolName`: The name of the tool with invalid inputs
-- `toolInput`: The invalid tool inputs
-- `message`: The error message
-- `cause`: The cause of the error
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_InvalidToolInputError` using:
-
-```typescript
-import { InvalidToolInputError } from 'ai';
-
-if (InvalidToolInputError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_JSONParseError
-description: Learn how to fix AI_JSONParseError
----
-
-# AI_JSONParseError
-
-This error occurs when JSON fails to parse.
-
-## Properties
-
-- `text`: The text value that could not be parsed
-- `message`: The error message including parse error details
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_JSONParseError` using:
-
-```typescript
-import { JSONParseError } from 'ai';
-
-if (JSONParseError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_LoadAPIKeyError
-description: Learn how to fix AI_LoadAPIKeyError
----
-
-# AI_LoadAPIKeyError
-
-This error occurs when API key is not loaded successfully.
-
-## Properties
-
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_LoadAPIKeyError` using:
-
-```typescript
-import { LoadAPIKeyError } from 'ai';
-
-if (LoadAPIKeyError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_LoadSettingError
-description: Learn how to fix AI_LoadSettingError
----
-
-# AI_LoadSettingError
-
-This error occurs when a setting is not loaded successfully.
-
-## Properties
-
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_LoadSettingError` using:
-
-```typescript
-import { LoadSettingError } from 'ai';
-
-if (LoadSettingError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_MessageConversionError
-description: Learn how to fix AI_MessageConversionError
----
-
-# AI_MessageConversionError
-
-This error occurs when message conversion fails.
-
-## Properties
-
-- `originalMessage`: The original message that failed conversion
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_MessageConversionError` using:
-
-```typescript
-import { MessageConversionError } from 'ai';
-
-if (MessageConversionError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoContentGeneratedError
-description: Learn how to fix AI_NoContentGeneratedError
----
-
-# AI_NoContentGeneratedError
-
-This error occurs when the AI provider fails to generate content.
-
-## Properties
-
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoContentGeneratedError` using:
-
-```typescript
-import { NoContentGeneratedError } from 'ai';
-
-if (NoContentGeneratedError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoImageGeneratedError
-description: Learn how to fix AI_NoImageGeneratedError
----
-
-# AI_NoImageGeneratedError
-
-This error occurs when the AI provider fails to generate an image.
-It can arise due to the following reasons:
-
-- The model failed to generate a response.
-- The model generated an invalid response.
-
-## Properties
-
-- `message`: The error message.
-- `responses`: Metadata about the image model responses, including timestamp, model, and headers.
-- `cause`: The cause of the error. You can use this for more detailed error handling.
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoImageGeneratedError` using:
-
-```typescript
-import { generateImage, NoImageGeneratedError } from 'ai';
-
-try {
- await generateImage({ model, prompt });
-} catch (error) {
- if (NoImageGeneratedError.isInstance(error)) {
- console.log('NoImageGeneratedError');
- console.log('Cause:', error.cause);
- console.log('Responses:', error.responses);
- }
-}
-```
-
----
-title: AI_NoObjectGeneratedError
-description: Learn how to fix AI_NoObjectGeneratedError
----
-
-# AI_NoObjectGeneratedError
-
-This error occurs when the AI provider fails to generate a parsable object that conforms to the schema.
-It can arise due to the following reasons:
-
-- The model failed to generate a response.
-- The model generated a response that could not be parsed.
-- The model generated a response that could not be validated against the schema.
-
-## Properties
-
-- `message`: The error message.
-- `text`: The text that was generated by the model. This can be the raw text or the tool call text, depending on the object generation mode.
-- `response`: Metadata about the language model response, including response id, timestamp, and model.
-- `usage`: Request token usage.
-- `finishReason`: Request finish reason. For example 'length' if model generated maximum number of tokens, this could result in a JSON parsing error.
-- `cause`: The cause of the error (e.g. a JSON parsing error). You can use this for more detailed error handling.
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoObjectGeneratedError` using:
-
-```typescript
-import { generateObject, NoObjectGeneratedError } from 'ai';
-
-try {
- await generateObject({ model, schema, prompt });
-} catch (error) {
- if (NoObjectGeneratedError.isInstance(error)) {
- console.log('NoObjectGeneratedError');
- console.log('Cause:', error.cause);
- console.log('Text:', error.text);
- console.log('Response:', error.response);
- console.log('Usage:', error.usage);
- console.log('Finish Reason:', error.finishReason);
- }
-}
-```
-
----
-title: AI_NoSpeechGeneratedError
-description: Learn how to fix AI_NoSpeechGeneratedError
----
-
-# AI_NoSpeechGeneratedError
-
-This error occurs when no audio could be generated from the input.
-
-## Properties
-
-- `responses`: Array of responses
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoSpeechGeneratedError` using:
-
-```typescript
-import { NoSpeechGeneratedError } from 'ai';
-
-if (NoSpeechGeneratedError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoSuchModelError
-description: Learn how to fix AI_NoSuchModelError
----
-
-# AI_NoSuchModelError
-
-This error occurs when a model ID is not found.
-
-## Properties
-
-- `modelId`: The ID of the model that was not found
-- `modelType`: The type of model
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoSuchModelError` using:
-
-```typescript
-import { NoSuchModelError } from 'ai';
-
-if (NoSuchModelError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoSuchProviderError
-description: Learn how to fix AI_NoSuchProviderError
----
-
-# AI_NoSuchProviderError
-
-This error occurs when a provider ID is not found.
-
-## Properties
-
-- `providerId`: The ID of the provider that was not found
-- `availableProviders`: Array of available provider IDs
-- `modelId`: The ID of the model
-- `modelType`: The type of model
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoSuchProviderError` using:
-
-```typescript
-import { NoSuchProviderError } from 'ai';
-
-if (NoSuchProviderError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoSuchToolError
-description: Learn how to fix AI_NoSuchToolError
----
-
-# AI_NoSuchToolError
-
-This error occurs when a model tries to call an unavailable tool.
-
-## Properties
-
-- `toolName`: The name of the tool that was not found
-- `availableTools`: Array of available tool names
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoSuchToolError` using:
-
-```typescript
-import { NoSuchToolError } from 'ai';
-
-if (NoSuchToolError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_NoTranscriptGeneratedError
-description: Learn how to fix AI_NoTranscriptGeneratedError
----
-
-# AI_NoTranscriptGeneratedError
-
-This error occurs when no transcript could be generated from the input.
-
-## Properties
-
-- `responses`: Array of responses
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_NoTranscriptGeneratedError` using:
-
-```typescript
-import { NoTranscriptGeneratedError } from 'ai';
-
-if (NoTranscriptGeneratedError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_RetryError
-description: Learn how to fix AI_RetryError
----
-
-# AI_RetryError
-
-This error occurs when a retry operation fails.
-
-## Properties
-
-- `reason`: The reason for the retry failure
-- `lastError`: The most recent error that occurred during retries
-- `errors`: Array of all errors that occurred during retry attempts
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_RetryError` using:
-
-```typescript
-import { RetryError } from 'ai';
-
-if (RetryError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_TooManyEmbeddingValuesForCallError
-description: Learn how to fix AI_TooManyEmbeddingValuesForCallError
----
-
-# AI_TooManyEmbeddingValuesForCallError
-
-This error occurs when too many values are provided in a single embedding call.
-
-## Properties
-
-- `provider`: The AI provider name
-- `modelId`: The ID of the embedding model
-- `maxEmbeddingsPerCall`: The maximum number of embeddings allowed per call
-- `values`: The array of values that was provided
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_TooManyEmbeddingValuesForCallError` using:
-
-```typescript
-import { TooManyEmbeddingValuesForCallError } from 'ai';
-
-if (TooManyEmbeddingValuesForCallError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: ToolCallRepairError
-description: Learn how to fix AI SDK ToolCallRepairError
----
-
-# ToolCallRepairError
-
-This error occurs when there is a failure while attempting to repair an invalid tool call.
-This typically happens when the AI attempts to fix either
-a `NoSuchToolError` or `InvalidToolInputError`.
-
-## Properties
-
-- `originalError`: The original error that triggered the repair attempt (either `NoSuchToolError` or `InvalidToolInputError`)
-- `message`: The error message
-- `cause`: The underlying error that caused the repair to fail
-
-## Checking for this Error
-
-You can check if an error is an instance of `ToolCallRepairError` using:
-
-```typescript
-import { ToolCallRepairError } from 'ai';
-
-if (ToolCallRepairError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_TypeValidationError
-description: Learn how to fix AI_TypeValidationError
----
-
-# AI_TypeValidationError
-
-This error occurs when type validation fails.
-
-## Properties
-
-- `value`: The value that failed validation
-- `message`: The error message including validation details
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_TypeValidationError` using:
-
-```typescript
-import { TypeValidationError } from 'ai';
-
-if (TypeValidationError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI_UnsupportedFunctionalityError
-description: Learn how to fix AI_UnsupportedFunctionalityError
----
-
-# AI_UnsupportedFunctionalityError
-
-This error occurs when functionality is not unsupported.
-
-## Properties
-
-- `functionality`: The name of the unsupported functionality
-- `message`: The error message
-
-## Checking for this Error
-
-You can check if an error is an instance of `AI_UnsupportedFunctionalityError` using:
-
-```typescript
-import { UnsupportedFunctionalityError } from 'ai';
-
-if (UnsupportedFunctionalityError.isInstance(error)) {
- // Handle the error
-}
-```
-
----
-title: AI Gateway
-description: Learn how to use the AI Gateway provider with the AI SDK.
----
-
-# AI Gateway Provider
-
-The [AI Gateway](https://vercel.com/docs/ai-gateway) provider connects you to models from multiple AI providers through a single interface. Instead of integrating with each provider separately, you can access OpenAI, Anthropic, Google, Meta, xAI, and other providers and their models.
-
-## Features
-
-- Access models from multiple providers without having to install additional provider modules/dependencies
-- Use the same code structure across different AI providers
-- Switch between models and providers easily
-- Automatic authentication when deployed on Vercel
-- View pricing information across providers
-- Observability for AI model usage through the Vercel dashboard
-
-## Setup
-
-The Vercel AI Gateway provider is part of the AI SDK.
-
-## Basic Usage
-
-For most use cases, you can use the AI Gateway directly with a model string:
-
-```ts
-// use plain model string with global provider
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'openai/gpt-5',
- prompt: 'Hello world',
-});
-```
-
-```ts
-// use provider instance (requires version 5.0.36 or later)
-import { generateText, gateway } from 'ai';
-
-const { text } = await generateText({
- model: gateway('openai/gpt-5'),
- prompt: 'Hello world',
-});
-```
-
-The AI SDK automatically uses the AI Gateway when you pass a model string in the `creator/model-name` format.
-
-## Provider Instance
-
-
- The `gateway` provider instance is available from the `ai` package in version
- 5.0.36 and later.
-
-
-You can also import the default provider instance `gateway` from `ai`:
-
-```ts
-import { gateway } from 'ai';
-```
-
-You may want to create a custom provider instance when you need to:
-
-- Set custom configuration options (API key, base URL, headers)
-- Use the provider in a [provider registry](/docs/ai-sdk-core/provider-management)
-- Wrap the provider with [middleware](/docs/ai-sdk-core/middleware)
-- Use different settings for different parts of your application
-
-To create a custom provider instance, import `createGateway` from `ai`:
-
-```ts
-import { createGateway } from 'ai';
-
-const gateway = createGateway({
- apiKey: process.env.AI_GATEWAY_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the AI Gateway provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls. The default prefix is `https://ai-gateway.vercel.sh/v1/ai`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `AI_GATEWAY_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-- **metadataCacheRefreshMillis** _number_
-
- How frequently to refresh the metadata cache in milliseconds. Defaults to 5 minutes (300,000ms).
-
-## Authentication
-
-The Gateway provider supports two authentication methods:
-
-### API Key Authentication
-
-Set your API key via environment variable:
-
-```bash
-AI_GATEWAY_API_KEY=your_api_key_here
-```
-
-Or pass it directly to the provider:
-
-```ts
-import { createGateway } from 'ai';
-
-const gateway = createGateway({
- apiKey: 'your_api_key_here',
-});
-```
-
-### OIDC Authentication (Vercel Deployments)
-
-When deployed to Vercel, the AI Gateway provider supports authenticating using [OIDC (OpenID Connect)
-tokens](https://vercel.com/docs/oidc) without API Keys.
-
-#### How OIDC Authentication Works
-
-1. **In Production/Preview Deployments**:
-
- - OIDC authentication is automatically handled
- - No manual configuration needed
- - Tokens are automatically obtained and refreshed
-
-2. **In Local Development**:
- - First, install and authenticate with the [Vercel CLI](https://vercel.com/docs/cli)
- - Run `vercel env pull` to download your project's OIDC token locally
- - For automatic token management:
- - Use `vercel dev` to start your development server - this will handle token refreshing automatically
- - For manual token management:
- - If not using `vercel dev`, note that OIDC tokens expire after 12 hours
- - You'll need to run `vercel env pull` again to refresh the token before it expires
-
-
- If an API Key is present (either passed directly or via environment), it will
- always be used, even if invalid.
-
-
-Read more about using OIDC tokens in the [Vercel AI Gateway docs](https://vercel.com/docs/ai-gateway#using-the-ai-gateway-with-a-vercel-oidc-token).
-
-## Bring Your Own Key (BYOK)
-
-You can connect your own provider credentials to use with Vercel AI Gateway. This lets you use your existing provider accounts and access private resources.
-
-To set up BYOK, add your provider credentials in your Vercel team's AI Gateway settings. Once configured, AI Gateway automatically uses your credentials. No code changes are needed.
-
-Learn more in the [BYOK documentation](https://vercel.com/docs/ai-gateway/byok).
-
-## Language Models
-
-You can create language models using a provider instance. The first argument is the model ID in the format `creator/model-name`:
-
-```ts
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'openai/gpt-5',
- prompt: 'Explain quantum computing in simple terms',
-});
-```
-
-AI Gateway language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions (see [AI SDK Core](/docs/ai-sdk-core)).
-
-## Available Models
-
-The AI Gateway supports models from OpenAI, Anthropic, Google, Meta, xAI, Mistral, DeepSeek, Amazon Bedrock, Cohere, Perplexity, Alibaba, and other providers.
-
-For the complete list of available models, see the [AI Gateway documentation](https://vercel.com/docs/ai-gateway).
-
-## Dynamic Model Discovery
-
-You can discover available models programmatically:
-
-```ts
-import { gateway, generateText } from 'ai';
-
-const availableModels = await gateway.getAvailableModels();
-
-// List all available models
-availableModels.models.forEach(model => {
- console.log(`${model.id}: ${model.name}`);
- if (model.description) {
- console.log(` Description: ${model.description}`);
- }
- if (model.pricing) {
- console.log(` Input: $${model.pricing.input}/token`);
- console.log(` Output: $${model.pricing.output}/token`);
- if (model.pricing.cachedInputTokens) {
- console.log(
- ` Cached input (read): $${model.pricing.cachedInputTokens}/token`,
- );
- }
- if (model.pricing.cacheCreationInputTokens) {
- console.log(
- ` Cache creation (write): $${model.pricing.cacheCreationInputTokens}/token`,
- );
- }
- }
-});
-
-// Use any discovered model with plain string
-const { text } = await generateText({
- model: availableModels.models[0].id, // e.g., 'openai/gpt-4o'
- prompt: 'Hello world',
-});
-```
-
-## Credit Usage
-
-You can check your team's current credit balance and usage:
-
-```ts
-import { gateway } from 'ai';
-
-const credits = await gateway.getCredits();
-
-console.log(`Team balance: ${credits.balance} credits`);
-console.log(`Team total used: ${credits.total_used} credits`);
-```
-
-The `getCredits()` method returns your team's credit information based on the authenticated API key or OIDC token:
-
-- **balance** _number_ - Your team's current available credit balance
-- **total_used** _number_ - Total credits consumed by your team
-
-## Examples
-
-### Basic Text Generation
-
-```ts
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'anthropic/claude-sonnet-4',
- prompt: 'Write a haiku about programming',
-});
-
-console.log(text);
-```
-
-### Streaming
-
-```ts
-import { streamText } from 'ai';
-
-const { textStream } = await streamText({
- model: 'openai/gpt-5',
- prompt: 'Explain the benefits of serverless architecture',
-});
-
-for await (const textPart of textStream) {
- process.stdout.write(textPart);
-}
-```
-
-### Tool Usage
-
-```ts
-import { generateText, tool } from 'ai';
-import { z } from 'zod';
-
-const { text } = await generateText({
- model: 'xai/grok-4',
- prompt: 'What is the weather like in San Francisco?',
- tools: {
- getWeather: tool({
- description: 'Get the current weather for a location',
- parameters: z.object({
- location: z.string().describe('The location to get weather for'),
- }),
- execute: async ({ location }) => {
- // Your weather API call here
- return `It's sunny in ${location}`;
- },
- }),
- },
-});
-```
-
-### Provider-Executed Tools
-
-Some providers offer tools that are executed by the provider itself, such as [OpenAI's web search tool](/providers/ai-sdk-providers/openai#web-search-tool). To use these tools through AI Gateway, import the provider to access the tool definitions:
-
-```ts
-import { generateText, stepCountIs } from 'ai';
-import { openai } from '@ai-sdk/openai';
-
-const result = await generateText({
- model: 'openai/gpt-5-mini',
- prompt: 'What is the Vercel AI Gateway?',
- stopWhen: stepCountIs(10),
- tools: {
- web_search: openai.tools.webSearch({}),
- },
-});
-
-console.dir(result.text);
-```
-
-
- Some provider-executed tools require account-specific configuration (such as
- Claude Agent Skills) and may not work through AI Gateway. To use these tools,
- you must bring your own key (BYOK) directly to the provider.
-
-
-### Usage Tracking with User and Tags
-
-Track usage per end-user and categorize requests with tags:
-
-```ts
-import type { GatewayProviderOptions } from '@ai-sdk/gateway';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'openai/gpt-5',
- prompt: 'Summarize this document...',
- providerOptions: {
- gateway: {
- user: 'user-abc-123', // Track usage for this specific end-user
- tags: ['document-summary', 'premium-feature'], // Categorize for reporting
- } satisfies GatewayProviderOptions,
- },
-});
-```
-
-This allows you to:
-
-- View usage and costs broken down by end-user in your analytics
-- Filter and analyze spending by feature or use case using tags
-- Track which users or features are driving the most AI usage
-
-## Provider Options
-
-The AI Gateway provider accepts provider options that control routing behavior and provider-specific configurations.
-
-### Gateway Provider Options
-
-You can use the `gateway` key in `providerOptions` to control how AI Gateway routes requests:
-
-```ts
-import type { GatewayProviderOptions } from '@ai-sdk/gateway';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'anthropic/claude-sonnet-4',
- prompt: 'Explain quantum computing',
- providerOptions: {
- gateway: {
- order: ['vertex', 'anthropic'], // Try Vertex AI first, then Anthropic
- only: ['vertex', 'anthropic'], // Only use these providers
- } satisfies GatewayProviderOptions,
- },
-});
-```
-
-The following gateway provider options are available:
-
-- **order** _string[]_
-
- Specifies the sequence of providers to attempt when routing requests. The gateway will try providers in the order specified. If a provider fails or is unavailable, it will move to the next provider in the list.
-
- Example: `order: ['bedrock', 'anthropic']` will attempt Amazon Bedrock first, then fall back to Anthropic.
-
-- **only** _string[]_
-
- Restricts routing to only the specified providers. When set, the gateway will never route to providers not in this list, even if they would otherwise be available.
-
- Example: `only: ['anthropic', 'vertex']` will only allow routing to Anthropic or Vertex AI.
-
-- **models** _string[]_
-
- Specifies fallback models to use when the primary model fails or is unavailable. The gateway will try the primary model first (specified in the `model` parameter), then try each model in this array in order until one succeeds.
-
- Example: `models: ['openai/gpt-5-nano', 'gemini-2.0-flash']` will try the fallback models in order if the primary model fails.
-
-- **user** _string_
-
- Optional identifier for the end user on whose behalf the request is being made. This is used for spend tracking and attribution purposes, allowing you to track usage per end-user in your application.
-
- Example: `user: 'user-123'` will associate this request with end-user ID "user-123" in usage reports.
-
-- **tags** _string[]_
-
- Optional array of tags for categorizing and filtering usage in reports. Useful for tracking spend by feature, prompt version, or any other dimension relevant to your application.
-
- Example: `tags: ['chat', 'v2']` will tag this request with "chat" and "v2" for filtering in usage analytics.
-
-You can combine these options to have fine-grained control over routing and tracking:
-
-```ts
-import type { GatewayProviderOptions } from '@ai-sdk/gateway';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'anthropic/claude-sonnet-4',
- prompt: 'Write a haiku about programming',
- providerOptions: {
- gateway: {
- order: ['vertex'], // Prefer Vertex AI
- only: ['anthropic', 'vertex'], // Only allow these providers
- } satisfies GatewayProviderOptions,
- },
-});
-```
-
-#### Model Fallbacks Example
-
-The `models` option enables automatic fallback to alternative models when the primary model fails:
-
-```ts
-import type { GatewayProviderOptions } from '@ai-sdk/gateway';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'openai/gpt-4o', // Primary model
- prompt: 'Write a TypeScript haiku',
- providerOptions: {
- gateway: {
- models: ['openai/gpt-5-nano', 'gemini-2.0-flash'], // Fallback models
- } satisfies GatewayProviderOptions,
- },
-});
-
-// This will:
-// 1. Try openai/gpt-4o first
-// 2. If it fails, try openai/gpt-5-nano
-// 3. If that fails, try gemini-2.0-flash
-// 4. Return the result from the first model that succeeds
-```
-
-### Provider-Specific Options
-
-When using provider-specific options through AI Gateway, use the actual provider name (e.g. `anthropic`, `openai`, not `gateway`) as the key:
-
-```ts
-import type { AnthropicProviderOptions } from '@ai-sdk/anthropic';
-import type { GatewayProviderOptions } from '@ai-sdk/gateway';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: 'anthropic/claude-sonnet-4',
- prompt: 'Explain quantum computing',
- providerOptions: {
- gateway: {
- order: ['vertex', 'anthropic'],
- } satisfies GatewayProviderOptions,
- anthropic: {
- thinking: { type: 'enabled', budgetTokens: 12000 },
- } satisfies AnthropicProviderOptions,
- },
-});
-```
-
-This works with any provider supported by AI Gateway. Each provider has its own set of options - see the individual [provider documentation pages](/providers/ai-sdk-providers) for details on provider-specific options.
-
-### Available Providers
-
-AI Gateway supports routing to 20+ providers.
-
-For a complete list of available providers and their slugs, see the [AI Gateway documentation](https://vercel.com/docs/ai-gateway/provider-options#available-providers).
-
-## Model Capabilities
-
-Model capabilities depend on the specific provider and model you're using. For detailed capability information, see:
-
-- [AI Gateway provider options](https://vercel.com/docs/ai-gateway/provider-options#available-providers) for an overview of available providers
-- Individual [AI SDK provider pages](/providers/ai-sdk-providers) for specific model capabilities and features
-
----
-title: xAI Grok
-description: Learn how to use xAI Grok.
----
-
-# xAI Grok Provider
-
-The [xAI Grok](https://x.ai) provider contains language model support for the [xAI API](https://x.ai/api).
-
-## Setup
-
-The xAI Grok provider is available via the `@ai-sdk/xai` module. You can
-install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `xai` from `@ai-sdk/xai`:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-```
-
-If you need a customized setup, you can import `createXai` from `@ai-sdk/xai`
-and create a provider instance with your settings:
-
-```ts
-import { createXai } from '@ai-sdk/xai';
-
-const xai = createXai({
- apiKey: 'your-api-key',
-});
-```
-
-You can use the following optional settings to customize the xAI provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.x.ai/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `XAI_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create [xAI models](https://console.x.ai) using a provider instance. The
-first argument is the model id, e.g. `grok-3`.
-
-```ts
-const model = xai('grok-3');
-```
-
-By default, `xai(modelId)` uses the Chat API. To use the Responses API with server-side agentic tools, explicitly use `xai.responses(modelId)`.
-
-### Example
-
-You can use xAI language models to generate text with the `generateText` function:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: xai('grok-3'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-xAI language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-### Provider Options
-
-xAI chat models support additional provider options that are not part of
-the [standard call settings](/docs/ai-sdk-core/settings). You can pass them in the `providerOptions` argument:
-
-```ts
-const model = xai('grok-3-mini');
-
-await generateText({
- model,
- providerOptions: {
- xai: {
- reasoningEffort: 'high',
- },
- },
-});
-```
-
-The following optional provider options are available for xAI chat models:
-
-- **reasoningEffort** _'low' | 'medium' | 'high'_
-
- Reasoning effort for reasoning models.
-
-- **store** _boolean_
-
- Whether to store the generation. Defaults to `true`.
-
-- **previousResponseId** _string_
-
- The ID of the previous response. You can use it to continue a conversation. Defaults to `undefined`.
-
-## Responses API (Agentic Tools)
-
-You can use the xAI Responses API with the `xai.responses(modelId)` factory method for server-side agentic tool calling. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.
-
-```ts
-const model = xai.responses('grok-4-fast');
-```
-
-The Responses API provides server-side tools that the model can autonomously execute during its reasoning process:
-
-- **web_search**: Real-time web search and page browsing
-- **x_search**: Search X (Twitter) posts, users, and threads
-- **code_execution**: Execute Python code for calculations and data analysis
-
-### Web Search Tool
-
-The web search tool enables autonomous web research with optional domain filtering and image understanding:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { generateText } from 'ai';
-
-const { text, sources } = await generateText({
- model: xai.responses('grok-4-fast'),
- prompt: 'What are the latest developments in AI?',
- tools: {
- web_search: xai.tools.webSearch({
- allowedDomains: ['arxiv.org', 'openai.com'],
- enableImageUnderstanding: true,
- }),
- },
-});
-
-console.log(text);
-console.log('Citations:', sources);
-```
-
-#### Web Search Parameters
-
-- **allowedDomains** _string[]_
-
- Only search within specified domains (max 5). Cannot be used with `excludedDomains`.
-
-- **excludedDomains** _string[]_
-
- Exclude specified domains from search (max 5). Cannot be used with `allowedDomains`.
-
-- **enableImageUnderstanding** _boolean_
-
- Enable the model to view and analyze images found during search. Increases token usage.
-
-### X Search Tool
-
-The X search tool enables searching X (Twitter) for posts, with filtering by handles and date ranges:
-
-```ts
-const { text, sources } = await generateText({
- model: xai.responses('grok-4-fast'),
- prompt: 'What are people saying about AI on X this week?',
- tools: {
- x_search: xai.tools.xSearch({
- allowedXHandles: ['elonmusk', 'xai'],
- fromDate: '2025-10-23',
- toDate: '2025-10-30',
- enableImageUnderstanding: true,
- enableVideoUnderstanding: true,
- }),
- },
-});
-```
-
-#### X Search Parameters
-
-- **allowedXHandles** _string[]_
-
- Only search posts from specified X handles (max 10). Cannot be used with `excludedXHandles`.
-
-- **excludedXHandles** _string[]_
-
- Exclude posts from specified X handles (max 10). Cannot be used with `allowedXHandles`.
-
-- **fromDate** _string_
-
- Start date for posts in ISO8601 format (`YYYY-MM-DD`).
-
-- **toDate** _string_
-
- End date for posts in ISO8601 format (`YYYY-MM-DD`).
-
-- **enableImageUnderstanding** _boolean_
-
- Enable the model to view and analyze images in X posts.
-
-- **enableVideoUnderstanding** _boolean_
-
- Enable the model to view and analyze videos in X posts.
-
-### Code Execution Tool
-
-The code execution tool enables the model to write and execute Python code for calculations and data analysis:
-
-```ts
-const { text } = await generateText({
- model: xai.responses('grok-4-fast'),
- prompt:
- 'Calculate the compound interest for $10,000 at 5% annually for 10 years',
- tools: {
- code_execution: xai.tools.codeExecution(),
- },
-});
-```
-
-### Multiple Tools
-
-You can combine multiple server-side tools for comprehensive research:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { streamText } from 'ai';
-
-const { fullStream } = streamText({
- model: xai.responses('grok-4-fast'),
- prompt: 'Research AI safety developments and calculate risk metrics',
- tools: {
- web_search: xai.tools.webSearch(),
- x_search: xai.tools.xSearch(),
- code_execution: xai.tools.codeExecution(),
- },
-});
-
-for await (const part of fullStream) {
- if (part.type === 'text-delta') {
- process.stdout.write(part.text);
- } else if (part.type === 'source' && part.sourceType === 'url') {
- console.log('\nSource:', part.url);
- }
-}
-```
-
-### Provider Options
-
-The Responses API supports the following provider options:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: xai.responses('grok-4-fast'),
- providerOptions: {
- xai: {
- reasoningEffort: 'high',
- },
- },
- // ...
-});
-```
-
-The following provider options are available:
-
-- **reasoningEffort** _'low' | 'high'_
-
- Control the reasoning effort for the model. Higher effort may produce more thorough results at the cost of increased latency and token usage.
-
-
- The Responses API only supports server-side tools. You cannot mix server-side
- tools with client-side function tools in the same request.
-
-
-## Live Search
-
-xAI models support Live Search functionality, allowing them to query real-time data from various sources and include it in responses with citations.
-
-### Basic Search
-
-To enable search, specify `searchParameters` with a search mode:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { generateText } from 'ai';
-
-const { text, sources } = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'What are the latest developments in AI?',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'auto', // 'auto', 'on', or 'off'
- returnCitations: true,
- maxSearchResults: 5,
- },
- },
- },
-});
-
-console.log(text);
-console.log('Sources:', sources);
-```
-
-### Search Parameters
-
-The following search parameters are available:
-
-- **mode** _'auto' | 'on' | 'off'_
-
- Search mode preference:
-
- - `'auto'` (default): Model decides whether to search
- - `'on'`: Always enables search
- - `'off'`: Disables search completely
-
-- **returnCitations** _boolean_
-
- Whether to return citations in the response. Defaults to `true`.
-
-- **fromDate** _string_
-
- Start date for search data in ISO8601 format (`YYYY-MM-DD`).
-
-- **toDate** _string_
-
- End date for search data in ISO8601 format (`YYYY-MM-DD`).
-
-- **maxSearchResults** _number_
-
- Maximum number of search results to consider. Defaults to 20, max 50.
-
-- **sources** _Array<SearchSource>_
-
- Data sources to search from. Defaults to `["web", "x"]` if not specified.
-
-### Search Sources
-
-You can specify different types of data sources for search:
-
-#### Web Search
-
-```ts
-const result = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'Best ski resorts in Switzerland',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'on',
- sources: [
- {
- type: 'web',
- country: 'CH', // ISO alpha-2 country code
- allowedWebsites: ['ski.com', 'snow-forecast.com'],
- safeSearch: true,
- },
- ],
- },
- },
- },
-});
-```
-
-#### Web source parameters
-
-- **country** _string_: ISO alpha-2 country code
-- **allowedWebsites** _string[]_: Max 5 allowed websites
-- **excludedWebsites** _string[]_: Max 5 excluded websites
-- **safeSearch** _boolean_: Enable safe search (default: true)
-
-#### X (Twitter) Search
-
-```ts
-const result = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'Latest updates on Grok AI',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'on',
- sources: [
- {
- type: 'x',
- includedXHandles: ['grok', 'xai'],
- excludedXHandles: ['openai'],
- postFavoriteCount: 10,
- postViewCount: 100,
- },
- ],
- },
- },
- },
-});
-```
-
-#### X source parameters
-
-- **includedXHandles** _string[]_: Array of X handles to search (without @ symbol)
-- **excludedXHandles** _string[]_: Array of X handles to exclude from search (without @ symbol)
-- **postFavoriteCount** _number_: Minimum favorite count of the X posts to consider.
-- **postViewCount** _number_: Minimum view count of the X posts to consider.
-
-#### News Search
-
-```ts
-const result = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'Recent tech industry news',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'on',
- sources: [
- {
- type: 'news',
- country: 'US',
- excludedWebsites: ['tabloid.com'],
- safeSearch: true,
- },
- ],
- },
- },
- },
-});
-```
-
-#### News source parameters
-
-- **country** _string_: ISO alpha-2 country code
-- **excludedWebsites** _string[]_: Max 5 excluded websites
-- **safeSearch** _boolean_: Enable safe search (default: true)
-
-#### RSS Feed Search
-
-```ts
-const result = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'Latest status updates',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'on',
- sources: [
- {
- type: 'rss',
- links: ['https://status.x.ai/feed.xml'],
- },
- ],
- },
- },
- },
-});
-```
-
-#### RSS source parameters
-
-- **links** _string[]_: Array of RSS feed URLs (max 1 currently supported)
-
-### Multiple Sources
-
-You can combine multiple data sources in a single search:
-
-```ts
-const result = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'Comprehensive overview of recent AI breakthroughs',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'on',
- returnCitations: true,
- maxSearchResults: 15,
- sources: [
- {
- type: 'web',
- allowedWebsites: ['arxiv.org', 'openai.com'],
- },
- {
- type: 'news',
- country: 'US',
- },
- {
- type: 'x',
- includedXHandles: ['openai', 'deepmind'],
- },
- ],
- },
- },
- },
-});
-```
-
-### Sources and Citations
-
-When search is enabled with `returnCitations: true`, the response includes sources that were used to generate the answer:
-
-```ts
-const { text, sources } = await generateText({
- model: xai('grok-3-latest'),
- prompt: 'What are the latest developments in AI?',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'auto',
- returnCitations: true,
- },
- },
- },
-});
-
-// Access the sources used
-for (const source of sources) {
- if (source.sourceType === 'url') {
- console.log('Source:', source.url);
- }
-}
-```
-
-### Streaming with Search
-
-Live Search works with streaming responses. Citations are included when the stream completes:
-
-```ts
-import { streamText } from 'ai';
-
-const result = streamText({
- model: xai('grok-3-latest'),
- prompt: 'What has happened in tech recently?',
- providerOptions: {
- xai: {
- searchParameters: {
- mode: 'auto',
- returnCitations: true,
- },
- },
- },
-});
-
-for await (const textPart of result.textStream) {
- process.stdout.write(textPart);
-}
-
-console.log('Sources:', await result.sources);
-```
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Reasoning |
-| --------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `grok-4-fast-non-reasoning` | | | | | |
-| `grok-4-fast-reasoning` | | | | | |
-| `grok-code-fast-1` | | | | | |
-| `grok-4` | | | | | |
-| `grok-3` | | | | | |
-| `grok-3-latest` | | | | | |
-| `grok-3-fast` | | | | | |
-| `grok-3-fast-latest` | | | | | |
-| `grok-3-mini` | | | | | |
-| `grok-3-mini-latest` | | | | | |
-| `grok-3-mini-fast` | | | | | |
-| `grok-3-mini-fast-latest` | | | | | |
-| `grok-2` | | | | | |
-| `grok-2-latest` | | | | | |
-| `grok-2-1212` | | | | | |
-| `grok-2-vision` | | | | | |
-| `grok-2-vision-latest` | | | | | |
-| `grok-2-vision-1212` | | | | | |
-| `grok-beta` | | | | | |
-| `grok-vision-beta` | | | | | |
-
-
- The table above lists popular models. Please see the [xAI
- docs](https://docs.x.ai/docs#models) for a full list of available models. The
- table above lists popular models. You can also pass any available provider
- model ID as a string if needed.
-
-
-## Image Models
-
-You can create xAI image models using the `.image()` factory method. For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: xai.image('grok-2-image'),
- prompt: 'A futuristic cityscape at sunset',
-});
-```
-
-
- The xAI image model does not currently support the `aspectRatio` or `size`
- parameters. Image size defaults to 1024x768.
-
-
-### Model-specific options
-
-You can customize the image generation behavior with model-specific settings:
-
-```ts
-import { xai } from '@ai-sdk/xai';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { images } = await generateImage({
- model: xai.image('grok-2-image'),
- prompt: 'A futuristic cityscape at sunset',
- maxImagesPerCall: 5, // Default is 10
- n: 2, // Generate 2 images
-});
-```
-
-### Model Capabilities
-
-| Model | Sizes | Notes |
-| -------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `grok-2-image` | 1024x768 (default) | xAI's text-to-image generation model, designed to create high-quality images from text prompts. It's trained on a diverse dataset and can generate images across various styles, subjects, and settings. |
-
----
-title: Vercel
-description: Learn how to use Vercel's v0 models with the AI SDK.
----
-
-# Vercel Provider
-
-The [Vercel](https://vercel.com) provider gives you access to the [v0 API](https://vercel.com/docs/v0/api), designed for building modern web applications. The v0 models support text and image inputs and provide fast streaming responses.
-
-You can create your Vercel API key at [v0.dev](https://v0.dev/chat/settings/keys).
-
-
- The v0 API is currently in beta and requires a Premium or Team plan with
- usage-based billing enabled. For details, visit the [pricing
- page](https://v0.dev/pricing). To request a higher limit, contact Vercel at
- support@v0.dev.
-
-
-## Features
-
-- **Framework aware completions**: Evaluated on modern stacks like Next.js and Vercel
-- **Auto-fix**: Identifies and corrects common coding issues during generation
-- **Quick edit**: Streams inline edits as they're available
-- **Multimodal**: Supports both text and image inputs
-
-## Setup
-
-The Vercel provider is available via the `@ai-sdk/vercel` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `vercel` from `@ai-sdk/vercel`:
-
-```ts
-import { vercel } from '@ai-sdk/vercel';
-```
-
-If you need a customized setup, you can import `createVercel` from `@ai-sdk/vercel` and create a provider instance with your settings:
-
-```ts
-import { createVercel } from '@ai-sdk/vercel';
-
-const vercel = createVercel({
- apiKey: process.env.VERCEL_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Vercel provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls. The default prefix is `https://api.v0.dev/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `VERCEL_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create language models using a provider instance. The first argument is the model ID, for example:
-
-```ts
-import { vercel } from '@ai-sdk/vercel';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: vercel('v0-1.0-md'),
- prompt: 'Create a Next.js AI chatbot',
-});
-```
-
-Vercel language models can also be used in the `streamText` function (see [AI SDK Core](/docs/ai-sdk-core)).
-
-## Models
-
-### v0-1.5-md
-
-The `v0-1.5-md` model is for everyday tasks and UI generation.
-
-### v0-1.5-lg
-
-The `v0-1.5-lg` model is for advanced thinking or reasoning.
-
-### v0-1.0-md (legacy)
-
-The `v0-1.0-md` model is the legacy model served by the v0 API.
-
-All v0 models have the following capabilities:
-
-- Supports text and image inputs (multimodal)
-- Supports function/tool calls
-- Streaming responses with low latency
-- Optimized for frontend and full-stack web development
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ----------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `v0-1.5-md` | | | | |
-| `v0-1.5-lg` | | | | |
-| `v0-1.0-md` | | | | |
-
----
-title: OpenAI
-description: Learn how to use the OpenAI provider for the AI SDK.
----
-
-# OpenAI Provider
-
-The [OpenAI](https://openai.com/) provider contains language model support for the OpenAI responses, chat, and completion APIs, as well as embedding model support for the OpenAI embeddings API.
-
-## Setup
-
-The OpenAI provider is available in the `@ai-sdk/openai` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `openai` from `@ai-sdk/openai`:
-
-```ts
-import { openai } from '@ai-sdk/openai';
-```
-
-If you need a customized setup, you can import `createOpenAI` from `@ai-sdk/openai` and create a provider instance with your settings:
-
-```ts
-import { createOpenAI } from '@ai-sdk/openai';
-
-const openai = createOpenAI({
- // custom settings, e.g.
- headers: {
- 'header-name': 'header-value',
- },
-});
-```
-
-You can use the following optional settings to customize the OpenAI provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.openai.com/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `OPENAI_API_KEY` environment variable.
-
-- **name** _string_
-
- The provider name. You can set this when using OpenAI compatible providers
- to change the model provider property. Defaults to `openai`.
-
-- **organization** _string_
-
- OpenAI Organization.
-
-- **project** _string_
-
- OpenAI project.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-The OpenAI provider instance is a function that you can invoke to create a language model:
-
-```ts
-const model = openai('gpt-5');
-```
-
-It automatically selects the correct API based on the model id.
-You can also pass additional settings in the second argument:
-
-```ts
-const model = openai('gpt-5', {
- // additional settings
-});
-```
-
-The available options depend on the API that's automatically chosen for the model (see below).
-If you want to explicitly select a specific model API, you can use `.responses`, `.chat`, or `.completion`.
-
-
- Since AI SDK 5, the OpenAI responses API is called by default (unless you
- specify e.g. 'openai.chat')
-
-
-### Example
-
-You can use OpenAI language models to generate text with the `generateText` function:
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: openai('gpt-5'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-OpenAI language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-### Responses Models
-
-You can use the OpenAI responses API with the `openai(modelId)` or `openai.responses(modelId)` factory methods. It is the default API that is used by the OpenAI provider (since AI SDK 5).
-
-```ts
-const model = openai('gpt-5');
-```
-
-Further configuration can be done using OpenAI provider options.
-You can validate the provider options using the `OpenAIResponsesProviderOptions` type.
-
-```ts
-import { openai, OpenAIResponsesProviderOptions } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5'), // or openai.responses('gpt-5')
- providerOptions: {
- openai: {
- parallelToolCalls: false,
- store: false,
- user: 'user_123',
- // ...
- } satisfies OpenAIResponsesProviderOptions,
- },
- // ...
-});
-```
-
-The following provider options are available:
-
-- **parallelToolCalls** _boolean_
- Whether to use parallel tool calls. Defaults to `true`.
-
-- **store** _boolean_
-
- Whether to store the generation. Defaults to `true`.
-
-- **maxToolCalls** _integer_
- The maximum number of total calls to built-in tools that can be processed in a response.
- This maximum number applies across all built-in tool calls, not per individual tool.
- Any further attempts to call a tool by the model will be ignored.
-
-- **metadata** _Record<string, string>_
- Additional metadata to store with the generation.
-
-- **conversation** _string_
- The ID of the OpenAI Conversation to continue.
- You must create a conversation first via the [OpenAI API](https://platform.openai.com/docs/api-reference/conversations/create).
- Cannot be used in conjunction with `previousResponseId`.
- Defaults to `undefined`.
-
-- **previousResponseId** _string_
- The ID of the previous response. You can use it to continue a conversation. Defaults to `undefined`.
-
-- **instructions** _string_
- Instructions for the model.
- They can be used to change the system or developer message when continuing a conversation using the `previousResponseId` option.
- Defaults to `undefined`.
-
-- **user** _string_
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. Defaults to `undefined`.
-
-- **reasoningEffort** _'none' | 'minimal' | 'low' | 'medium' | 'high'_
- Reasoning effort for reasoning models. Defaults to `medium`. If you use `providerOptions` to set the `reasoningEffort` option, this model setting will be ignored.
-
-
- The 'none' type for `reasoningEffort` is only available for OpenAI's GPT-5.1
- models. Setting `reasoningEffort` to 'none' with other models will result in
- an error.
-
-
-- **reasoningSummary** _'auto' | 'detailed'_
- Controls whether the model returns its reasoning process. Set to `'auto'` for a condensed summary, `'detailed'` for more comprehensive reasoning. Defaults to `undefined` (no reasoning summaries). When enabled, reasoning summaries appear in the stream as events with type `'reasoning'` and in non-streaming responses within the `reasoning` field.
-
-- **strictJsonSchema** _boolean_
- Whether to use strict JSON schema validation. Defaults to `false`.
-
-- **serviceTier** _'auto' | 'flex' | 'priority' | 'default'_
- Service tier for the request. Set to 'flex' for 50% cheaper processing
- at the cost of increased latency (available for o3, o4-mini, and gpt-5 models).
- Set to 'priority' for faster processing with Enterprise access (available for gpt-4, gpt-5, gpt-5-mini, o3, o4-mini; gpt-5-nano is not supported).
-
- Defaults to 'auto'.
-
-- **textVerbosity** _'low' | 'medium' | 'high'_
- Controls the verbosity of the model's response. Lower values result in more concise responses,
- while higher values result in more verbose responses. Defaults to `'medium'`.
-
-- **include** _Array<string>_
- Specifies additional content to include in the response. Supported values:
- `['file_search_call.results']` for including file search results in responses.
- `['message.output_text.logprobs']` for logprobs.
- Defaults to `undefined`.
-
-- **truncation** _string_
- The truncation strategy to use for the model response.
-
- - Auto: If the input to this Response exceeds the model's context window size, the model will truncate the response to fit the context window by dropping items from the beginning of the conversation.
- - disabled (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error.
-
-- **promptCacheKey** _string_
- A cache key for manual prompt caching control. Used by OpenAI to cache responses for similar requests to optimize your cache hit rates.
-
-- **promptCacheRetention** _'in_memory' | '24h'_
- The retention policy for the prompt cache. Set to `'24h'` to enable extended prompt caching, which keeps cached prefixes active for up to 24 hours. Defaults to `'in_memory'` for standard prompt caching. Note: `'24h'` is currently only available for the 5.1 series of models.
-
-- **safetyIdentifier** _string_
- A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely identifies each user.
-
-The OpenAI responses provider also returns provider-specific metadata:
-
-```ts
-const { providerMetadata } = await generateText({
- model: openai.responses('gpt-5'),
-});
-
-const openaiMetadata = providerMetadata?.openai;
-```
-
-The following OpenAI-specific metadata is returned:
-
-- **responseId** _string_
- The ID of the response. Can be used to continue a conversation.
-
-- **cachedPromptTokens** _number_
- The number of prompt tokens that were a cache hit.
-
-- **reasoningTokens** _number_
- The number of reasoning tokens that the model generated.
-
-#### Reasoning Output
-
-For reasoning models like `gpt-5`, you can enable reasoning summaries to see the model's thought process. Different models support different summarizers—for example, `o4-mini` supports detailed summaries. Set `reasoningSummary: "auto"` to automatically receive the richest level available.
-
-```ts highlight="8-9,16"
-import { openai } from '@ai-sdk/openai';
-import { streamText } from 'ai';
-
-const result = streamText({
- model: openai('gpt-5'),
- prompt: 'Tell me about the Mission burrito debate in San Francisco.',
- providerOptions: {
- openai: {
- reasoningSummary: 'detailed', // 'auto' for condensed or 'detailed' for comprehensive
- },
- },
-});
-
-for await (const part of result.fullStream) {
- if (part.type === 'reasoning') {
- console.log(`Reasoning: ${part.textDelta}`);
- } else if (part.type === 'text-delta') {
- process.stdout.write(part.textDelta);
- }
-}
-```
-
-For non-streaming calls with `generateText`, the reasoning summaries are available in the `reasoning` field of the response:
-
-```ts highlight="8-9,13"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5'),
- prompt: 'Tell me about the Mission burrito debate in San Francisco.',
- providerOptions: {
- openai: {
- reasoningSummary: 'auto',
- },
- },
-});
-console.log('Reasoning:', result.reasoning);
-```
-
-Learn more about reasoning summaries in the [OpenAI documentation](https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries).
-
-#### Verbosity Control
-
-You can control the length and detail of model responses using the `textVerbosity` parameter:
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5-mini'),
- prompt: 'Write a poem about a boy and his first pet dog.',
- providerOptions: {
- openai: {
- textVerbosity: 'low', // 'low' for concise, 'medium' (default), or 'high' for verbose
- },
- },
-});
-```
-
-The `textVerbosity` parameter scales output length without changing the underlying prompt:
-
-- `'low'`: Produces terse, minimal responses
-- `'medium'`: Balanced detail (default)
-- `'high'`: Verbose responses with comprehensive detail
-
-#### Web Search Tool
-
-The OpenAI responses API supports web search through the `openai.tools.webSearch` tool.
-
-```ts
-const result = await generateText({
- model: openai('gpt-5'),
- prompt: 'What happened in San Francisco last week?',
- tools: {
- web_search: openai.tools.webSearch({
- // optional configuration:
- externalWebAccess: true,
- searchContextSize: 'high',
- userLocation: {
- type: 'approximate',
- city: 'San Francisco',
- region: 'California',
- },
- }),
- },
- // Force web search tool (optional):
- toolChoice: { type: 'tool', toolName: 'web_search' },
-});
-
-// URL sources directly from `results`
-const sources = result.sources;
-
-// Or access sources from tool results
-for (const toolResult of result.toolResults) {
- if (toolResult.toolName === 'web_search') {
- console.log('Query:', toolResult.output.action.query);
- console.log('Sources:', toolResult.output.sources);
- // `sources` is an array of object: { type: 'url', url: string }
- }
-}
-```
-
-For detailed information on configuration options see the [OpenAI Web Search Tool documentation](https://platform.openai.com/docs/guides/tools-web-search?api-mode=responses).
-
-#### File Search Tool
-
-The OpenAI responses API supports file search through the `openai.tools.fileSearch` tool.
-
-You can force the use of the file search tool by setting the `toolChoice` parameter to `{ type: 'tool', toolName: 'file_search' }`.
-
-```ts
-const result = await generateText({
- model: openai('gpt-5'),
- prompt: 'What does the document say about user authentication?',
- tools: {
- file_search: openai.tools.fileSearch({
- vectorStoreIds: ['vs_123'],
- // configuration below is optional:
- maxNumResults: 5,
- filters: {
- key: 'author',
- type: 'eq',
- value: 'Jane Smith',
- },
- ranking: {
- ranker: 'auto',
- scoreThreshold: 0.5,
- },
- }),
- },
- providerOptions: {
- openai: {
- // optional: include results
- include: ['file_search_call.results'],
- } satisfies OpenAIResponsesProviderOptions,
- },
-});
-```
-
-
- The tool must be named `file_search` when using OpenAI's file search
- functionality. This name is required by OpenAI's API specification and cannot
- be customized.
-
-
-#### Image Generation Tool
-
-OpenAI's Responses API supports multi-modal image generation as a provider-defined tool.
-Availability is restricted to specific models (for example, `gpt-5` variants).
-
-You can use the image tool with either `generateText` or `streamText`:
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5'),
- prompt:
- 'Generate an image of an echidna swimming across the Mozambique channel.',
- tools: {
- image_generation: openai.tools.imageGeneration({ outputFormat: 'webp' }),
- },
-});
-
-for (const toolResult of result.staticToolResults) {
- if (toolResult.toolName === 'image_generation') {
- const base64Image = toolResult.output.result;
- }
-}
-```
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { streamText } from 'ai';
-
-const result = streamText({
- model: openai('gpt-5'),
- prompt:
- 'Generate an image of an echidna swimming across the Mozambique channel.',
- tools: {
- image_generation: openai.tools.imageGeneration({
- outputFormat: 'webp',
- quality: 'low',
- }),
- },
-});
-
-for await (const part of result.fullStream) {
- if (part.type == 'tool-result' && !part.dynamic) {
- const base64Image = part.output.result;
- }
-}
-```
-
-
- When you set `store: false`, then previously generated images will not be
- accessible by the model. We recommend using the image generation tool without
- setting `store: false`.
-
-
-For complete details on model availability, image quality controls, supported sizes, and tool-specific parameters,
-refer to the OpenAI documentation:
-
-- Image generation overview and models: [OpenAI Image Generation](https://platform.openai.com/docs/guides/image-generation)
-- Image generation tool parameters (background, size, quality, format, etc.): [Image Generation Tool Options](https://platform.openai.com/docs/guides/tools-image-generation#tool-options)
-
-#### Code Interpreter Tool
-
-The OpenAI responses API supports the code interpreter tool through the `openai.tools.codeInterpreter` tool.
-This allows models to write and execute Python code.
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5'),
- prompt: 'Write and run Python code to calculate the factorial of 10',
- tools: {
- code_interpreter: openai.tools.codeInterpreter({
- // optional configuration:
- container: {
- fileIds: ['file-123', 'file-456'], // optional file IDs to make available
- },
- }),
- },
-});
-```
-
-The code interpreter tool can be configured with:
-
-- **container**: Either a container ID string or an object with `fileIds` to specify uploaded files that should be available to the code interpreter
-
-
- The tool must be named `code_interpreter` when using OpenAI's code interpreter
- functionality. This name is required by OpenAI's API specification and cannot
- be customized.
-
-
-#### MCP Tool
-
-The OpenAI responses API supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers through the `openai.tools.mcp` tool. This allows models to call tools exposed by remote MCP servers or service connectors.
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai('gpt-5'),
- prompt: 'Search the web for the latest news about AI developments',
- tools: {
- mcp: openai.tools.mcp({
- serverLabel: 'web-search',
- serverUrl: 'https://mcp.exa.ai/mcp',
- serverDescription: 'A web-search API for AI agents',
- }),
- },
-});
-```
-
-The MCP tool can be configured with:
-
-- **serverLabel** _string_ (required)
-
- A label to identify the MCP server. This label is used in tool calls to distinguish between multiple MCP servers.
-
-- **serverUrl** _string_ (required if `connectorId` is not provided)
-
- The URL for the MCP server. Either `serverUrl` or `connectorId` must be provided.
-
-- **connectorId** _string_ (required if `serverUrl` is not provided)
-
- Identifier for a service connector. Either `serverUrl` or `connectorId` must be provided.
-
-- **serverDescription** _string_ (optional)
-
- Optional description of the MCP server that helps the model understand its purpose.
-
-- **allowedTools** _string[] | object_ (optional)
-
- Controls which tools from the MCP server are available. Can be:
-
- - An array of tool names: `['tool1', 'tool2']`
- - An object with filters:
- ```ts
- {
- readOnly: true, // Only allow read-only tools
- toolNames: ['tool1', 'tool2'] // Specific tool names
- }
- ```
-
-- **authorization** _string_ (optional)
-
- OAuth access token for authenticating with the MCP server or connector.
-
-- **headers** _Record<string, string>_ (optional)
-
- Optional HTTP headers to include in requests to the MCP server.
-
-
- The tool calls made by the model when using the OpenAI MCP tool are approved
- by default. Be sure to connect to only trusted MCP servers, who you trust to
- share your data with.
-
-
-
- The OpenAI MCP tool is different from the general MCP client approach
- documented in [MCP Tools](/docs/ai-sdk-core/mcp-tools). The OpenAI MCP tool is
- a built-in provider-defined tool that allows OpenAI models to directly connect
- to MCP servers, while the general MCP client requires you to convert MCP tools
- to AI SDK tools first.
-
-
-#### Local Shell Tool
-
-The OpenAI responses API support the local shell tool for Codex models through the `openai.tools.localShell` tool.
-Local shell is a tool that allows agents to run shell commands locally on a machine you or the user provides.
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai.responses('gpt-5-codex'),
- tools: {
- local_shell: openai.tools.localShell({
- execute: async ({ action }) => {
- // ... your implementation, e.g. sandbox access ...
- return { output: stdout };
- },
- }),
- },
- prompt: 'List the files in my home directory.',
- stopWhen: stepCountIs(2),
-});
-```
-
-
- The tool must be named `local_shell`. This name is required by OpenAI's API
- specification and cannot be customized. The model can only be
-
-
-#### Image Inputs
-
-The OpenAI Responses API supports Image inputs for appropriate models.
-You can pass Image files as part of the message content using the 'image' type:
-
-```ts
-const result = await generateText({
- model: openai('gpt-5'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'Please describe the image.',
- },
- {
- type: 'image',
- image: fs.readFileSync('./data/image.png'),
- },
- ],
- },
- ],
-});
-```
-
-The model will have access to the image and will respond to questions about it.
-The image should be passed using the `image` field.
-
-You can also pass a file-id from the OpenAI Files API.
-
-```ts
-{
- type: 'image',
- image: 'file-8EFBcWHsQxZV7YGezBC1fq'
-}
-```
-
-You can also pass the URL of an image.
-
-```ts
-{
- type: 'image',
- image: 'https://sample.edu/image.png',
-}
-```
-
-#### PDF Inputs
-
-The OpenAI Responses API supports reading PDF files.
-You can pass PDF files as part of the message content using the `file` type:
-
-```ts
-const result = await generateText({
- model: openai('gpt-5'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
- },
- ],
- },
- ],
-});
-```
-
-You can also pass a file-id from the OpenAI Files API.
-
-```ts
-{
- type: 'file',
- data: 'file-8EFBcWHsQxZV7YGezBC1fq',
- mediaType: 'application/pdf',
-}
-```
-
-You can also pass the URL of a pdf.
-
-```ts
-{
- type: 'file',
- data: 'https://sample.edu/example.pdf',
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
-}
-```
-
-The model will have access to the contents of the PDF file and
-respond to questions about it.
-The PDF file should be passed using the `data` field,
-and the `mediaType` should be set to `'application/pdf'`.
-
-#### Structured Outputs
-
-The OpenAI Responses API supports structured outputs. You can enforce structured outputs using `generateObject` or `streamObject`, which expose a `schema` option. Additionally, you can pass a Zod or JSON Schema object to the `output` option when using `generateText` or `streamText`.
-
-```ts
-// Using generateObject
-const result = await generateObject({
- model: openai('gpt-4.1'),
- schema: z.object({
- recipe: z.object({
- name: z.string(),
- ingredients: z.array(
- z.object({
- name: z.string(),
- amount: z.string(),
- }),
- ),
- steps: z.array(z.string()),
- }),
- }),
- prompt: 'Generate a lasagna recipe.',
-});
-
-// Using generateText
-const result = await generateText({
- model: openai('gpt-4.1'),
- prompt: 'How do I make a pizza?',
- output: Output.object({
- schema: z.object({
- ingredients: z.array(z.string()),
- steps: z.array(z.string()),
- }),
- }),
-});
-```
-
-### Chat Models
-
-You can create models that call the [OpenAI chat API](https://platform.openai.com/docs/api-reference/chat) using the `.chat()` factory method.
-The first argument is the model id, e.g. `gpt-4`.
-The OpenAI chat models support tool calls and some have multi-modal capabilities.
-
-```ts
-const model = openai.chat('gpt-5');
-```
-
-OpenAI chat models support also some model specific provider options that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them in the `providerOptions` argument:
-
-```ts
-import { openai, type OpenAIChatLanguageModelOptions } from '@ai-sdk/openai';
-
-const model = openai.chat('gpt-5');
-
-await generateText({
- model,
- providerOptions: {
- openai: {
- logitBias: {
- // optional likelihood for specific tokens
- '50256': -100,
- },
- user: 'test-user', // optional unique user identifier
- } satisfies OpenAIChatLanguageModelOptions,
- },
-});
-```
-
-The following optional provider options are available for OpenAI chat models:
-
-- **logitBias** _Record<number, number>_
-
- Modifies the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in
- the GPT tokenizer) to an associated bias value from -100 to 100. You
- can use this tokenizer tool to convert text to token IDs. Mathematically,
- the bias is added to the logits generated by the model prior to sampling.
- The exact effect will vary per model, but values between -1 and 1 should
- decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
-
- As an example, you can pass `{"50256": -100}` to prevent the token from being generated.
-
-- **logprobs** _boolean | number_
-
- Return the log probabilities of the tokens. Including logprobs will increase
- the response size and can slow down response times. However, it can
- be useful to better understand how the model is behaving.
-
- Setting to true will return the log probabilities of the tokens that
- were generated.
-
- Setting to a number will return the log probabilities of the top n
- tokens that were generated.
-
-- **parallelToolCalls** _boolean_
-
- Whether to enable parallel function calling during tool use. Defaults to `true`.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids).
-
-- **reasoningEffort** _'minimal' | 'low' | 'medium' | 'high'_
-
- Reasoning effort for reasoning models. Defaults to `medium`. If you use
- `providerOptions` to set the `reasoningEffort` option, this
- model setting will be ignored.
-
-- **structuredOutputs** _boolean_
-
- Whether to use structured outputs.
- Defaults to `true`.
-
- When enabled, tool calls and object generation will be strict and follow the provided schema.
-
-- **maxCompletionTokens** _number_
-
- Maximum number of completion tokens to generate. Useful for reasoning models.
-
-- **store** _boolean_
-
- Whether to enable persistence in Responses API.
-
-- **metadata** _Record<string, string>_
-
- Metadata to associate with the request.
-
-- **prediction** _Record<string, any>_
-
- Parameters for prediction mode.
-
-- **serviceTier** _'auto' | 'flex' | 'priority' | 'default'_
-
- Service tier for the request. Set to 'flex' for 50% cheaper processing
- at the cost of increased latency (available for o3, o4-mini, and gpt-5 models).
- Set to 'priority' for faster processing with Enterprise access (available for gpt-4, gpt-5, gpt-5-mini, o3, o4-mini; gpt-5-nano is not supported).
-
- Defaults to 'auto'.
-
-- **strictJsonSchema** _boolean_
-
- Whether to use strict JSON schema validation.
- Defaults to `false`.
-
-- **textVerbosity** _'low' | 'medium' | 'high'_
-
- Controls the verbosity of the model's responses. Lower values will result in more concise responses, while higher values will result in more verbose responses.
-
-- **promptCacheKey** _string_
-
- A cache key for manual prompt caching control. Used by OpenAI to cache responses for similar requests to optimize your cache hit rates.
-
-- **promptCacheRetention** _'in_memory' | '24h'_
-
- The retention policy for the prompt cache. Set to `'24h'` to enable extended prompt caching, which keeps cached prefixes active for up to 24 hours. Defaults to `'in_memory'` for standard prompt caching. Note: `'24h'` is currently only available for the 5.1 series of models.
-
-- **safetyIdentifier** _string_
-
- A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely identifies each user.
-
-#### Reasoning
-
-OpenAI has introduced the `o1`,`o3`, and `o4` series of [reasoning models](https://platform.openai.com/docs/guides/reasoning).
-Currently, `o4-mini`, `o3`, `o3-mini`, and `o1` are available via both the chat and responses APIs. The
-models `codex-mini-latest` and `computer-use-preview` are available only via the [responses API](#responses-models).
-
-Reasoning models currently only generate text, have several limitations, and are only supported using `generateText` and `streamText`.
-
-They support additional settings and response metadata:
-
-- You can use `providerOptions` to set
-
- - the `reasoningEffort` option (or alternatively the `reasoningEffort` model setting), which determines the amount of reasoning the model performs.
-
-- You can use response `providerMetadata` to access the number of reasoning tokens that the model generated.
-
-```ts highlight="4,7-11,17"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const { text, usage, providerMetadata } = await generateText({
- model: openai.chat('gpt-5'),
- prompt: 'Invent a new holiday and describe its traditions.',
- providerOptions: {
- openai: {
- reasoningEffort: 'low',
- },
- },
-});
-
-console.log(text);
-console.log('Usage:', {
- ...usage,
- reasoningTokens: providerMetadata?.openai?.reasoningTokens,
-});
-```
-
-
- System messages are automatically converted to OpenAI developer messages for
- reasoning models when supported.
-
-
-
- Reasoning models require additional runtime inference to complete their
- reasoning phase before generating a response. This introduces longer latency
- compared to other models.
-
-
-
- `maxOutputTokens` is automatically mapped to `max_completion_tokens` for
- reasoning models.
-
-
-#### Structured Outputs
-
-Structured outputs are enabled by default.
-You can disable them by setting the `structuredOutputs` option to `false`.
-
-```ts highlight="7"
-import { openai } from '@ai-sdk/openai';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-
-const result = await generateObject({
- model: openai.chat('gpt-4o-2024-08-06'),
- providerOptions: {
- openai: {
- structuredOutputs: false,
- },
- },
- schemaName: 'recipe',
- schemaDescription: 'A recipe for lasagna.',
- schema: z.object({
- name: z.string(),
- ingredients: z.array(
- z.object({
- name: z.string(),
- amount: z.string(),
- }),
- ),
- steps: z.array(z.string()),
- }),
- prompt: 'Generate a lasagna recipe.',
-});
-
-console.log(JSON.stringify(result.object, null, 2));
-```
-
-
- OpenAI structured outputs have several
- [limitations](https://openai.com/index/introducing-structured-outputs-in-the-api),
- in particular around the [supported schemas](https://platform.openai.com/docs/guides/structured-outputs/supported-schemas),
- and are therefore opt-in.
-
-For example, optional schema properties are not supported.
-You need to change Zod `.nullish()` and `.optional()` to `.nullable()`.
-
-
-
-#### Logprobs
-
-OpenAI provides logprobs information for completion/chat models.
-You can access it in the `providerMetadata` object.
-
-```ts highlight="11"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai.chat('gpt-5'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
- providerOptions: {
- openai: {
- // this can also be a number,
- // refer to logprobs provider options section for more
- logprobs: true,
- },
- },
-});
-
-const openaiMetadata = (await result.providerMetadata)?.openai;
-
-const logprobs = openaiMetadata?.logprobs;
-```
-
-#### Image Support
-
-The OpenAI Chat API supports Image inputs for appropriate models.
-You can pass Image files as part of the message content using the 'image' type:
-
-```ts
-const result = await generateText({
- model: openai.chat('gpt-5'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'Please describe the image.',
- },
- {
- type: 'image',
- image: fs.readFileSync('./data/image.png'),
- },
- ],
- },
- ],
-});
-```
-
-The model will have access to the image and will respond to questions about it.
-The image should be passed using the `image` field.
-
-You can also pass the URL of an image.
-
-```ts
-{
- type: 'image',
- image: 'https://sample.edu/image.png',
-}
-```
-
-#### PDF support
-
-The OpenAI Chat API supports reading PDF files.
-You can pass PDF files as part of the message content using the `file` type:
-
-```ts
-const result = await generateText({
- model: openai.chat('gpt-5'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
- },
- ],
- },
- ],
-});
-```
-
-The model will have access to the contents of the PDF file and
-respond to questions about it.
-The PDF file should be passed using the `data` field,
-and the `mediaType` should be set to `'application/pdf'`.
-
-You can also pass a file-id from the OpenAI Files API.
-
-```ts
-{
- type: 'file',
- data: 'file-8EFBcWHsQxZV7YGezBC1fq',
- mediaType: 'application/pdf',
-}
-```
-
-You can also pass the URL of a PDF.
-
-```ts
-{
- type: 'file',
- data: 'https://sample.edu/example.pdf',
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
-}
-```
-
-#### Predicted Outputs
-
-OpenAI supports [predicted outputs](https://platform.openai.com/docs/guides/latency-optimization#use-predicted-outputs) for `gpt-4o` and `gpt-4o-mini`.
-Predicted outputs help you reduce latency by allowing you to specify a base text that the model should modify.
-You can enable predicted outputs by adding the `prediction` option to the `providerOptions.openai` object:
-
-```ts highlight="15-18"
-const result = streamText({
- model: openai.chat('gpt-5'),
- messages: [
- {
- role: 'user',
- content: 'Replace the Username property with an Email property.',
- },
- {
- role: 'user',
- content: existingCode,
- },
- ],
- providerOptions: {
- openai: {
- prediction: {
- type: 'content',
- content: existingCode,
- },
- },
- },
-});
-```
-
-OpenAI provides usage information for predicted outputs (`acceptedPredictionTokens` and `rejectedPredictionTokens`).
-You can access it in the `providerMetadata` object.
-
-```ts highlight="11"
-const openaiMetadata = (await result.providerMetadata)?.openai;
-
-const acceptedPredictionTokens = openaiMetadata?.acceptedPredictionTokens;
-const rejectedPredictionTokens = openaiMetadata?.rejectedPredictionTokens;
-```
-
-
- OpenAI Predicted Outputs have several
- [limitations](https://platform.openai.com/docs/guides/predicted-outputs#limitations),
- e.g. unsupported API parameters and no tool calling support.
-
-
-#### Image Detail
-
-You can use the `openai` provider option to set the [image input detail](https://platform.openai.com/docs/guides/images-vision?api-mode=responses#specify-image-input-detail-level) to `high`, `low`, or `auto`:
-
-```ts highlight="13-16"
-const result = await generateText({
- model: openai.chat('gpt-5'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'Describe the image in detail.' },
- {
- type: 'image',
- image:
- 'https://github.com/vercel/ai/blob/main/examples/ai-core/data/comic-cat.png?raw=true',
-
- // OpenAI specific options - image detail:
- providerOptions: {
- openai: { imageDetail: 'low' },
- },
- },
- ],
- },
- ],
-});
-```
-
-
- Because the `UIMessage` type (used by AI SDK UI hooks like `useChat`) does not
- support the `providerOptions` property, you can use `convertToModelMessages`
- first before passing the messages to functions like `generateText` or
- `streamText`. For more details on `providerOptions` usage, see
- [here](/docs/foundations/prompts#provider-options).
-
-
-#### Distillation
-
-OpenAI supports model distillation for some models.
-If you want to store a generation for use in the distillation process, you can add the `store` option to the `providerOptions.openai` object.
-This will save the generation to the OpenAI platform for later use in distillation.
-
-```typescript highlight="9-16"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-import 'dotenv/config';
-
-async function main() {
- const { text, usage } = await generateText({
- model: openai.chat('gpt-4o-mini'),
- prompt: 'Who worked on the original macintosh?',
- providerOptions: {
- openai: {
- store: true,
- metadata: {
- custom: 'value',
- },
- },
- },
- });
-
- console.log(text);
- console.log();
- console.log('Usage:', usage);
-}
-
-main().catch(console.error);
-```
-
-#### Prompt Caching
-
-OpenAI has introduced [Prompt Caching](https://platform.openai.com/docs/guides/prompt-caching) for supported models
-including `gpt-4o` and `gpt-4o-mini`.
-
-- Prompt caching is automatically enabled for these models, when the prompt is 1024 tokens or longer. It does
- not need to be explicitly enabled.
-- You can use response `providerMetadata` to access the number of prompt tokens that were a cache hit.
-- Note that caching behavior is dependent on load on OpenAI's infrastructure. Prompt prefixes generally remain in the
- cache following 5-10 minutes of inactivity before they are evicted, but during off-peak periods they may persist for up
- to an hour.
-
-```ts highlight="11"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const { text, usage, providerMetadata } = await generateText({
- model: openai.chat('gpt-4o-mini'),
- prompt: `A 1024-token or longer prompt...`,
-});
-
-console.log(`usage:`, {
- ...usage,
- cachedPromptTokens: providerMetadata?.openai?.cachedPromptTokens,
-});
-```
-
-To improve cache hit rates, you can manually control caching using the `promptCacheKey` option:
-
-```ts highlight="7-11"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const { text, usage, providerMetadata } = await generateText({
- model: openai.chat('gpt-5'),
- prompt: `A 1024-token or longer prompt...`,
- providerOptions: {
- openai: {
- promptCacheKey: 'my-custom-cache-key-123',
- },
- },
-});
-
-console.log(`usage:`, {
- ...usage,
- cachedPromptTokens: providerMetadata?.openai?.cachedPromptTokens,
-});
-```
-
-For GPT-5.1 models, you can enable extended prompt caching that keeps cached prefixes active for up to 24 hours:
-
-```ts highlight="7-12"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const { text, usage, providerMetadata } = await generateText({
- model: openai.chat('gpt-5.1'),
- prompt: `A 1024-token or longer prompt...`,
- providerOptions: {
- openai: {
- promptCacheKey: 'my-custom-cache-key-123',
- promptCacheRetention: '24h', // Extended caching for GPT-5.1
- },
- },
-});
-
-console.log(`usage:`, {
- ...usage,
- cachedPromptTokens: providerMetadata?.openai?.cachedPromptTokens,
-});
-```
-
-#### Audio Input
-
-With the `gpt-4o-audio-preview` model, you can pass audio files to the model.
-
-
- The `gpt-4o-audio-preview` model is currently in preview and requires at least
- some audio inputs. It will not work with non-audio data.
-
-
-```ts highlight="12-14"
-import { openai } from '@ai-sdk/openai';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: openai.chat('gpt-4o-audio-preview'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'What is the audio saying?' },
- {
- type: 'file',
- mediaType: 'audio/mpeg',
- data: fs.readFileSync('./data/galileo.mp3'),
- },
- ],
- },
- ],
-});
-```
-
-### Completion Models
-
-You can create models that call the [OpenAI completions API](https://platform.openai.com/docs/api-reference/completions) using the `.completion()` factory method.
-The first argument is the model id.
-Currently only `gpt-3.5-turbo-instruct` is supported.
-
-```ts
-const model = openai.completion('gpt-3.5-turbo-instruct');
-```
-
-OpenAI completion models support also some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them as an options argument:
-
-```ts
-const model = openai.completion('gpt-3.5-turbo-instruct');
-
-await model.doGenerate({
- providerOptions: {
- openai: {
- echo: true, // optional, echo the prompt in addition to the completion
- logitBias: {
- // optional likelihood for specific tokens
- '50256': -100,
- },
- suffix: 'some text', // optional suffix that comes after a completion of inserted text
- user: 'test-user', // optional unique user identifier
- },
- },
-});
-```
-
-The following optional provider options are available for OpenAI completion models:
-
-- **echo**: _boolean_
-
- Echo back the prompt in addition to the completion.
-
-- **logitBias** _Record<number, number>_
-
- Modifies the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in
- the GPT tokenizer) to an associated bias value from -100 to 100. You
- can use this tokenizer tool to convert text to token IDs. Mathematically,
- the bias is added to the logits generated by the model prior to sampling.
- The exact effect will vary per model, but values between -1 and 1 should
- decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
-
- As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|>
- token from being generated.
-
-- **logprobs** _boolean | number_
-
- Return the log probabilities of the tokens. Including logprobs will increase
- the response size and can slow down response times. However, it can
- be useful to better understand how the model is behaving.
-
- Setting to true will return the log probabilities of the tokens that
- were generated.
-
- Setting to a number will return the log probabilities of the top n
- tokens that were generated.
-
-- **suffix** _string_
-
- The suffix that comes after a completion of inserted text.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids).
-
-### Model Capabilities
-
-| Model | Image Input | Audio Input | Object Generation | Tool Usage |
-| --------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `gpt-5.1-codex-mini` | | | | |
-| `gpt-5.1-codex` | | | | |
-| `gpt-5.1-chat-latest` | | | | |
-| `gpt-5.1` | | | | |
-| `gpt-5-pro` | | | | |
-| `gpt-5` | | | | |
-| `gpt-5-mini` | | | | |
-| `gpt-5-nano` | | | | |
-| `gpt-5-codex` | | | | |
-| `gpt-5-chat-latest` | | | | |
-| `gpt-4.1` | | | | |
-| `gpt-4.1-mini` | | | | |
-| `gpt-4.1-nano` | | | | |
-| `gpt-4o` | | | | |
-| `gpt-4o-mini` | | | | |
-
-
- The table above lists popular models. Please see the [OpenAI
- docs](https://platform.openai.com/docs/models) for a full list of available
- models. The table above lists popular models. You can also pass any available
- provider model ID as a string if needed.
-
-
-## Embedding Models
-
-You can create models that call the [OpenAI embeddings API](https://platform.openai.com/docs/api-reference/embeddings)
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = openai.textEmbedding('text-embedding-3-large');
-```
-
-OpenAI embedding models support several additional provider options.
-You can pass them as an options argument:
-
-```ts
-import { openai } from '@ai-sdk/openai';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: openai.textEmbedding('text-embedding-3-large'),
- value: 'sunny day at the beach',
- providerOptions: {
- openai: {
- dimensions: 512, // optional, number of dimensions for the embedding
- user: 'test-user', // optional unique user identifier
- },
- },
-});
-```
-
-The following optional provider options are available for OpenAI embedding models:
-
-- **dimensions**: _number_
-
- The number of dimensions the resulting output embeddings should have.
- Only supported in text-embedding-3 and later models.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids).
-
-### Model Capabilities
-
-| Model | Default Dimensions | Custom Dimensions |
-| ------------------------ | ------------------ | ------------------- |
-| `text-embedding-3-large` | 3072 | |
-| `text-embedding-3-small` | 1536 | |
-| `text-embedding-ada-002` | 1536 | |
-
-## Image Models
-
-You can create models that call the [OpenAI image generation API](https://platform.openai.com/docs/api-reference/images)
-using the `.image()` factory method.
-
-```ts
-const model = openai.image('dall-e-3');
-```
-
-
- Dall-E models do not support the `aspectRatio` parameter. Use the `size`
- parameter instead.
-
-
-### Model Capabilities
-
-| Model | Sizes |
-| ------------------ | ------------------------------- |
-| `gpt-image-1-mini` | 1024x1024, 1536x1024, 1024x1536 |
-| `gpt-image-1` | 1024x1024, 1536x1024, 1024x1536 |
-| `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
-| `dall-e-2` | 256x256, 512x512, 1024x1024 |
-
-You can pass optional `providerOptions` to the image model. These are prone to change by OpenAI and are model dependent. For example, the `gpt-image-1` model supports the `quality` option:
-
-```ts
-const { image, providerMetadata } = await generateImage({
- model: openai.image('gpt-image-1'),
- prompt: 'A salamander at sunrise in a forest pond in the Seychelles.',
- providerOptions: {
- openai: { quality: 'high' },
- },
-});
-```
-
-For more on `generateImage()` see [Image Generation](/docs/ai-sdk-core/image-generation).
-
-OpenAI's image models may return a revised prompt for each image. It can be access at `providerMetadata.openai.images[0]?.revisedPrompt`.
-
-For more information on the available OpenAI image model options, see the [OpenAI API reference](https://platform.openai.com/docs/api-reference/images/create).
-
-## Transcription Models
-
-You can create models that call the [OpenAI transcription API](https://platform.openai.com/docs/api-reference/audio/transcribe)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `whisper-1`.
-
-```ts
-const model = openai.transcription('whisper-1');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the input language in ISO-639-1 (e.g. `en`) format will improve accuracy and latency.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { openai } from '@ai-sdk/openai';
-
-const result = await transcribe({
- model: openai.transcription('whisper-1'),
- audio: new Uint8Array([1, 2, 3, 4]),
- providerOptions: { openai: { language: 'en' } },
-});
-```
-
-To get word-level timestamps, specify the granularity:
-
-```ts highlight="8-9"
-import { experimental_transcribe as transcribe } from 'ai';
-import { openai } from '@ai-sdk/openai';
-
-const result = await transcribe({
- model: openai.transcription('whisper-1'),
- audio: new Uint8Array([1, 2, 3, 4]),
- providerOptions: {
- openai: {
- //timestampGranularities: ['word'],
- timestampGranularities: ['segment'],
- },
- },
-});
-
-// Access word-level timestamps
-console.log(result.segments); // Array of segments with startSecond/endSecond
-```
-
-The following provider options are available:
-
-- **timestampGranularities** _string[]_
- The granularity of the timestamps in the transcription.
- Defaults to `['segment']`.
- Possible values are `['word']`, `['segment']`, and `['word', 'segment']`.
- Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
-
-- **language** _string_
- The language of the input audio. Supplying the input language in ISO-639-1 format (e.g. 'en') will improve accuracy and latency.
- Optional.
-
-- **prompt** _string_
- An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
- Optional.
-
-- **temperature** _number_
- The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.
- Defaults to 0.
- Optional.
-
-- **include** _string[]_
- Additional information to include in the transcription response.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ------------------------ | ------------------- | ------------------- | ------------------- | ------------------- |
-| `whisper-1` | | | | |
-| `gpt-4o-mini-transcribe` | | | | |
-| `gpt-4o-transcribe` | | | | |
-
-## Speech Models
-
-You can create models that call the [OpenAI speech API](https://platform.openai.com/docs/api-reference/audio/speech)
-using the `.speech()` factory method.
-
-The first argument is the model id e.g. `tts-1`.
-
-```ts
-const model = openai.speech('tts-1');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying a voice to use for the generated audio.
-
-```ts highlight="6"
-import { experimental_generateSpeech as generateSpeech } from 'ai';
-import { openai } from '@ai-sdk/openai';
-
-const result = await generateSpeech({
- model: openai.speech('tts-1'),
- text: 'Hello, world!',
- providerOptions: { openai: {} },
-});
-```
-
-- **instructions** _string_
- Control the voice of your generated audio with additional instructions e.g. "Speak in a slow and steady tone".
- Does not work with `tts-1` or `tts-1-hd`.
- Optional.
-
-- **response_format** _string_
- The format to audio in.
- Supported formats are `mp3`, `opus`, `aac`, `flac`, `wav`, and `pcm`.
- Defaults to `mp3`.
- Optional.
-
-- **speed** _number_
- The speed of the generated audio.
- Select a value from 0.25 to 4.0.
- Defaults to 1.0.
- Optional.
-
-### Model Capabilities
-
-| Model | Instructions |
-| ----------------- | ------------------- |
-| `tts-1` | |
-| `tts-1-hd` | |
-| `gpt-4o-mini-tts` | |
-
----
-title: Azure OpenAI
-description: Learn how to use the Azure OpenAI provider for the AI SDK.
----
-
-# Azure OpenAI Provider
-
-The [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) provider contains language model support for the Azure OpenAI chat API.
-
-## Setup
-
-The Azure OpenAI provider is available in the `@ai-sdk/azure` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `azure` from `@ai-sdk/azure`:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-```
-
-If you need a customized setup, you can import `createAzure` from `@ai-sdk/azure` and create a provider instance with your settings:
-
-```ts
-import { createAzure } from '@ai-sdk/azure';
-
-const azure = createAzure({
- resourceName: 'your-resource-name', // Azure resource name
- apiKey: 'your-api-key',
-});
-```
-
-You can use the following optional settings to customize the OpenAI provider instance:
-
-- **resourceName** _string_
-
- Azure resource name.
- It defaults to the `AZURE_RESOURCE_NAME` environment variable.
-
- The resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/v1{path}`.
- You can use `baseURL` instead to specify the URL prefix.
-
-- **apiKey** _string_
-
- API key that is being sent using the `api-key` header.
- It defaults to the `AZURE_API_KEY` environment variable.
-
-- **apiVersion** _string_
-
- Sets a custom [api version](https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation).
- Defaults to `v1`.
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
-
- Either this or `resourceName` can be used.
- When a baseURL is provided, the resourceName is ignored.
-
- With a baseURL, the resolved URL is `{baseURL}/v1{path}`.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-- **useDeploymentBasedUrls** _boolean_
-
- Use deployment-based URLs for API calls. Set to `true` to use the legacy deployment format:
- `{baseURL}/deployments/{deploymentId}{path}?api-version={apiVersion}` instead of
- `{baseURL}/v1{path}?api-version={apiVersion}`.
- Defaults to `false`.
-
- This option is useful for compatibility with certain Azure OpenAI models or deployments
- that require the legacy endpoint format.
-
-## Language Models
-
-The Azure OpenAI provider instance is a function that you can invoke to create a language model:
-
-```ts
-const model = azure('your-deployment-name');
-```
-
-You need to pass your deployment name as the first argument.
-
-### Reasoning Models
-
-Azure exposes the thinking of `DeepSeek-R1` in the generated text using the `` tag.
-You can use the `extractReasoningMiddleware` to extract this reasoning and expose it as a `reasoning` property on the result:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { wrapLanguageModel, extractReasoningMiddleware } from 'ai';
-
-const enhancedModel = wrapLanguageModel({
- model: azure('your-deepseek-r1-deployment-name'),
- middleware: extractReasoningMiddleware({ tagName: 'think' }),
-});
-```
-
-You can then use that enhanced model in functions like `generateText` and `streamText`.
-
-
- The Azure provider calls the Responses API by default (unless you specify e.g.
- `azure.chat`).
-
-
-### Example
-
-You can use OpenAI language models to generate text with the `generateText` function:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: azure('your-deployment-name'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-OpenAI language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-
- Azure OpenAI sends larger chunks than OpenAI. This can lead to the perception
- that the response is slower. See [Troubleshooting: Azure OpenAI Slow To
- Stream](/docs/troubleshooting/common-issues/azure-stream-slow)
-
-
-### Provider Options
-
-When using OpenAI language models on Azure, you can configure provider-specific options using `providerOptions.openai`. More information on available configuration options are on [the OpenAI provider page](/providers/ai-sdk-providers/openai#language-models).
-
-```ts highlight="12-14,22-24"
-const messages = [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is the capital of the moon?',
- },
- {
- type: 'image',
- image: 'https://example.com/image.png',
- providerOptions: {
- openai: { imageDetail: 'low' },
- },
- },
- ],
- },
-];
-
-const { text } = await generateText({
- model: azure('your-deployment-name'),
- providerOptions: {
- openai: {
- reasoningEffort: 'low',
- },
- },
-});
-```
-
-### Chat Models
-
-
- The URL for calling Azure chat models will be constructed as follows:
- `https://RESOURCE_NAME.openai.azure.com/openai/v1/chat/completions?api-version=v1`
-
-
-You can create models that call the Azure OpenAI chat completions API using the `.chat()` factory method:
-
-```ts
-const model = azure.chat('your-deployment-name');
-```
-
-Azure OpenAI chat models support also some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them as an options argument:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: azure.chat('your-deployment-name'),
- prompt: 'Write a short story about a robot.',
- providerOptions: {
- openai: {
- logitBias: {
- // optional likelihood for specific tokens
- '50256': -100,
- },
- user: 'test-user', // optional unique user identifier
- },
- },
-});
-```
-
-The following optional provider options are available for OpenAI chat models:
-
-- **logitBias** _Record<number, number>_
-
- Modifies the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in
- the GPT tokenizer) to an associated bias value from -100 to 100. You
- can use this tokenizer tool to convert text to token IDs. Mathematically,
- the bias is added to the logits generated by the model prior to sampling.
- The exact effect will vary per model, but values between -1 and 1 should
- decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
-
- As an example, you can pass `{"50256": -100}` to prevent the token from being generated.
-
-- **logprobs** _boolean | number_
-
- Return the log probabilities of the tokens. Including logprobs will increase
- the response size and can slow down response times. However, it can
- be useful to better understand how the model is behaving.
-
- Setting to true will return the log probabilities of the tokens that
- were generated.
-
- Setting to a number will return the log probabilities of the top n
- tokens that were generated.
-
-- **parallelToolCalls** _boolean_
-
- Whether to enable parallel function calling during tool use. Default to true.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. Learn more.
-
-### Responses Models
-
-Azure OpenAI uses responses API as default with the `azure(deploymentName)` factory method.
-
-```ts
-const model = azure('your-deployment-name');
-```
-
-Further configuration can be done using OpenAI provider options.
-You can validate the provider options using the `OpenAIResponsesProviderOptions` type.
-
-```ts
-import { azure, OpenAIResponsesProviderOptions } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: azure('your-deployment-name'),
- providerOptions: {
- openai: {
- parallelToolCalls: false,
- store: false,
- user: 'user_123',
- // ...
- } satisfies OpenAIResponsesProviderOptions,
- },
- // ...
-});
-```
-
-The following provider options are available:
-
-- **parallelToolCalls** _boolean_
- Whether to use parallel tool calls. Defaults to `true`.
-
-- **store** _boolean_
- Whether to store the generation. Defaults to `true`.
-
-- **metadata** _Record<string, string>_
- Additional metadata to store with the generation.
-
-- **previousResponseId** _string_
- The ID of the previous response. You can use it to continue a conversation. Defaults to `undefined`.
-
-- **instructions** _string_
- Instructions for the model.
- They can be used to change the system or developer message when continuing a conversation using the `previousResponseId` option.
- Defaults to `undefined`.
-
-- **user** _string_
- A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. Defaults to `undefined`.
-
-- **reasoningEffort** _'low' | 'medium' | 'high'_
- Reasoning effort for reasoning models. Defaults to `medium`. If you use `providerOptions` to set the `reasoningEffort` option, this model setting will be ignored.
-
-- **strictJsonSchema** _boolean_
- Whether to use strict JSON schema validation. Defaults to `false`.
-
-The Azure OpenAI provider also returns provider-specific metadata:
-
-```ts
-const { providerMetadata } = await generateText({
- model: azure('your-deployment-name'),
-});
-
-const openaiMetadata = providerMetadata?.openai;
-```
-
-The following OpenAI-specific metadata is returned:
-
-- **responseId** _string_
- The ID of the response. Can be used to continue a conversation.
-
-- **cachedPromptTokens** _number_
- The number of prompt tokens that were a cache hit.
-
-- **reasoningTokens** _number_
- The number of reasoning tokens that the model generated.
-
-
- The providerMetadata is only returned with the default responses API, and is
- not supported when using 'azure.chat' or 'azure.completion'
-
-
-#### Web Search Tool
-
-The Azure OpenAI responses API supports web search(preview) through the `azure.tools.webSearchPreview` tool.
-
-```ts
-const result = await generateText({
- model: azure('gpt-4.1-mini'),
- prompt: 'What happened in San Francisco last week?',
- tools: {
- web_search_preview: azure.tools.webSearchPreview({
- // optional configuration:
- searchContextSize: 'low',
- userLocation: {
- type: 'approximate',
- city: 'San Francisco',
- region: 'California',
- },
- }),
- },
- // Force web search tool (optional):
- toolChoice: { type: 'tool', toolName: 'web_search_preview' },
-});
-
-console.log(result.text);
-
-// URL sources directly from `results`
-const sources = result.sources;
-for (const source of sources) {
- console.log('source:', source);
-}
-```
-
-
- The tool must be named `web_search_preview` when using Azure OpenAI's web
- search(preview) functionality. This name is required by Azure OpenAI's API
- specification and cannot be customized.
-
-
-
- The 'web_search_preview' tool is only supported with the default responses
- API, and is not supported when using 'azure.chat' or 'azure.completion'
-
-
-#### File Search Tool
-
-The Azure OpenAI provider supports file search through the `azure.tools.fileSearch` tool.
-
-You can force the use of the file search tool by setting the `toolChoice` parameter to `{ type: 'tool', toolName: 'file_search' }`.
-
-```ts
-const result = await generateText({
- model: azure('gpt-5'),
- prompt: 'What does the document say about user authentication?',
- tools: {
- file_search: azure.tools.fileSearch({
- // optional configuration:
- vectorStoreIds: ['vs_123', 'vs_456'],
- maxNumResults: 10,
- ranking: {
- ranker: 'auto',
- },
- }),
- },
- // Force file search tool:
- toolChoice: { type: 'tool', toolName: 'file_search' },
-});
-```
-
-
- The tool must be named `file_search` when using Azure OpenAI's file search
- functionality. This name is required by Azure OpenAI's API specification and
- cannot be customized.
-
-
-
- The 'file_search' tool is only supported with the default responses API, and
- is not supported when using 'azure.chat' or 'azure.completion'
-
-
-#### Image Generation Tool
-
-
- Azure OpenAI Responses API
- [image_generation](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/responses?tabs=python-secure#image-generation-preview)
- is now Preview release(Not GA release).Image tool does not currently support
- streaming mode. You can not use the image tool with `streamText` currently.
-
-
-Azure OpenAI's Responses API supports multi-modal image generation as a provider-defined tool.
-Availability is restricted to specific models (for example, `gpt-5` variants).
-
-You can use the image tool with `generateText`.
-
-```ts
-import { createAzure } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const azure = createAzure({
- headers: {
- 'x-ms-oai-image-generation-deployment': 'gpt-image-1', // use your own image model deployment
- },
-});
-
-const result = await generateText({
- model: azure('gpt-5'),
- prompt:
- 'Generate an image of an echidna swimming across the Mozambique channel.',
- tools: {
- image_generation: azure.tools.imageGeneration({ outputFormat: 'png' }),
- },
-});
-
-for (const toolResult of result.staticToolResults) {
- if (toolResult.toolName === 'image_generation') {
- const base64Image = toolResult.output.result;
- }
-}
-```
-
-
- To use image_generation, you must first create an image generation model. You
- must add a deployment specification to the header
- `x-ms-oai-image-generation-deployment`. Please note that the Responses API
- model and the image generation model must be in the same resource.
-
-
-
- When you set `store: false`, then previously generated images will not be
- accessible by the model. We recommend using the image generation tool without
- setting `store: false`.
-
-
-#### Code Interpreter Tool
-
-The Azure OpenAI provider supports the code interpreter tool through the `azure.tools.codeInterpreter` tool. This allows models to write and execute Python code.
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: azure('gpt-5'),
- prompt: 'Write and run Python code to calculate the factorial of 10',
- tools: {
- code_interpreter: azure.tools.codeInterpreter({
- // optional configuration:
- container: {
- fileIds: ['assistant-123', 'assistant-456'], // optional file IDs to make available
- },
- }),
- },
-});
-```
-
-The code interpreter tool can be configured with:
-
-- **container**: Either a container ID string or an object with `fileIds` to specify uploaded files that should be available to the code interpreter
-
-
- The tool must be named `code_interpreter` when using Azure OpenAI's code
- interpreter functionality. This name is required by Azure OpenAI's API
- specification and cannot be customized.
-
-
-
- The 'code_interpreter' tool is only supported with the default responses API,
- and is not supported when using 'azure.chat' or 'azure.completion'
-
-
-#### PDF support
-
-The Azure OpenAI provider supports reading PDF files.
-You can pass PDF files as part of the message content using the `file` type:
-
-```ts
-const result = await generateText({
- model: azure('your-deployment-name'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
- },
- ],
- },
- ],
-});
-```
-
-The model will have access to the contents of the PDF file and
-respond to questions about it.
-The PDF file should be passed using the `data` field,
-and the `mediaType` should be set to `'application/pdf'`.
-
-
- Reading PDF files are only supported with the default responses API, and is
- not supported when using 'azure.chat' or 'azure.completion'
-
-
-### Completion Models
-
-You can create models that call the completions API using the `.completion()` factory method.
-The first argument is the model id.
-Currently only `gpt-35-turbo-instruct` is supported.
-
-```ts
-const model = azure.completion('your-gpt-35-turbo-instruct-deployment');
-```
-
-OpenAI completion models support also some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them as an options argument:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: azure.completion('your-gpt-35-turbo-instruct-deployment'),
- prompt: 'Write a haiku about coding.',
- providerOptions: {
- openai: {
- echo: true, // optional, echo the prompt in addition to the completion
- logitBias: {
- // optional likelihood for specific tokens
- '50256': -100,
- },
- suffix: 'some text', // optional suffix that comes after a completion of inserted text
- user: 'test-user', // optional unique user identifier
- },
- },
-});
-```
-
-The following optional provider options are available for Azure OpenAI completion models:
-
-- **echo**: _boolean_
-
- Echo back the prompt in addition to the completion.
-
-- **logitBias** _Record<number, number>_
-
- Modifies the likelihood of specified tokens appearing in the completion.
-
- Accepts a JSON object that maps tokens (specified by their token ID in
- the GPT tokenizer) to an associated bias value from -100 to 100. You
- can use this tokenizer tool to convert text to token IDs. Mathematically,
- the bias is added to the logits generated by the model prior to sampling.
- The exact effect will vary per model, but values between -1 and 1 should
- decrease or increase likelihood of selection; values like -100 or 100
- should result in a ban or exclusive selection of the relevant token.
-
- As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|>
- token from being generated.
-
-- **logprobs** _boolean | number_
-
- Return the log probabilities of the tokens. Including logprobs will increase
- the response size and can slow down response times. However, it can
- be useful to better understand how the model is behaving.
-
- Setting to true will return the log probabilities of the tokens that
- were generated.
-
- Setting to a number will return the log probabilities of the top n
- tokens that were generated.
-
-- **suffix** _string_
-
- The suffix that comes after a completion of inserted text.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. Learn more.
-
-## Embedding Models
-
-You can create models that call the Azure OpenAI embeddings API
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = azure.textEmbedding('your-embedding-deployment');
-```
-
-Azure OpenAI embedding models support several additional settings.
-You can pass them as an options argument:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: azure.textEmbedding('your-embedding-deployment'),
- value: 'sunny day at the beach',
- providerOptions: {
- openai: {
- dimensions: 512, // optional, number of dimensions for the embedding
- user: 'test-user', // optional unique user identifier
- },
- },
-});
-```
-
-The following optional provider options are available for Azure OpenAI embedding models:
-
-- **dimensions**: _number_
-
- The number of dimensions the resulting output embeddings should have.
- Only supported in text-embedding-3 and later models.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help OpenAI to
- monitor and detect abuse. Learn more.
-
-## Image Models
-
-You can create models that call the Azure OpenAI image generation API (DALL-E) using the `.image()` factory method. The first argument is your deployment name for the DALL-E model.
-
-```ts
-const model = azure.image('your-dalle-deployment-name');
-```
-
-Azure OpenAI image models support several additional settings. You can pass them as `providerOptions.openai` when generating the image:
-
-```ts
-await generateImage({
- model: azure.image('your-dalle-deployment-name'),
- prompt: 'A photorealistic image of a cat astronaut floating in space',
- size: '1024x1024', // '1024x1024', '1792x1024', or '1024x1792' for DALL-E 3
- providerOptions: {
- openai: {
- user: 'test-user', // optional unique user identifier
- responseFormat: 'url', // 'url' or 'b64_json', defaults to 'url'
- },
- },
-});
-```
-
-### Example
-
-You can use Azure OpenAI image models to generate images with the `generateImage` function:
-
-```ts
-import { azure } from '@ai-sdk/azure';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: azure.image('your-dalle-deployment-name'),
- prompt: 'A photorealistic image of a cat astronaut floating in space',
- size: '1024x1024', // '1024x1024', '1792x1024', or '1024x1792' for DALL-E 3
-});
-
-// image contains the URL or base64 data of the generated image
-console.log(image);
-```
-
-### Model Capabilities
-
-Azure OpenAI supports DALL-E 2 and DALL-E 3 models through deployments. The capabilities depend on which model version your deployment is using:
-
-| Model Version | Sizes |
-| ------------- | ------------------------------- |
-| DALL-E 3 | 1024x1024, 1792x1024, 1024x1792 |
-| DALL-E 2 | 256x256, 512x512, 1024x1024 |
-
-
- DALL-E models do not support the `aspectRatio` parameter. Use the `size`
- parameter instead.
-
-
-
- When creating your Azure OpenAI deployment, make sure to set the DALL-E model
- version you want to use.
-
-
-## Transcription Models
-
-You can create models that call the Azure OpenAI transcription API using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `whisper-1`.
-
-```ts
-const model = azure.transcription('whisper-1');
-```
-
-
- If you encounter a "DeploymentNotFound" error with transcription models,
- try enabling deployment-based URLs:
-
- ```ts
- const azure = createAzure({
- useDeploymentBasedUrls: true,
- apiVersion: '2025-04-01-preview',
- });
- ```
-
- This uses the legacy endpoint format which may be required for certain Azure OpenAI deployments.
- When using useDeploymentBasedUrls, the default api-version is not valid. You must set it to `2025-04-01-preview` or an earlier value.
-
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the input language in ISO-639-1 (e.g. `en`) format will improve accuracy and latency.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { azure } from '@ai-sdk/azure';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: azure.transcription('whisper-1'),
- audio: await readFile('audio.mp3'),
- providerOptions: { openai: { language: 'en' } },
-});
-```
-
-The following provider options are available:
-
-- **timestampGranularities** _string[]_
- The granularity of the timestamps in the transcription.
- Defaults to `['segment']`.
- Possible values are `['word']`, `['segment']`, and `['word', 'segment']`.
- Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
-
-- **language** _string_
- The language of the input audio. Supplying the input language in ISO-639-1 format (e.g. 'en') will improve accuracy and latency.
- Optional.
-
-- **prompt** _string_
- An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
- Optional.
-
-- **temperature** _number_
- The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.
- Defaults to 0.
- Optional.
-
-- **include** _string[]_
- Additional information to include in the transcription response.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ------------------------ | ------------------- | ------------------- | ------------------- | ------------------- |
-| `whisper-1` | | | | |
-| `gpt-4o-mini-transcribe` | | | | |
-| `gpt-4o-transcribe` | | | | |
-
----
-title: Anthropic
-description: Learn how to use the Anthropic provider for the AI SDK.
----
-
-# Anthropic Provider
-
-The [Anthropic](https://www.anthropic.com/) provider contains language model support for the [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post).
-
-## Setup
-
-The Anthropic provider is available in the `@ai-sdk/anthropic` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `anthropic` from `@ai-sdk/anthropic`:
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-```
-
-If you need a customized setup, you can import `createAnthropic` from `@ai-sdk/anthropic` and create a provider instance with your settings:
-
-```ts
-import { createAnthropic } from '@ai-sdk/anthropic';
-
-const anthropic = createAnthropic({
- // custom settings
-});
-```
-
-You can use the following optional settings to customize the Anthropic provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.anthropic.com/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `x-api-key` header.
- It defaults to the `ANTHROPIC_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create models that call the [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post) using the provider instance.
-The first argument is the model id, e.g. `claude-3-haiku-20240307`.
-Some models have multi-modal capabilities.
-
-```ts
-const model = anthropic('claude-3-haiku-20240307');
-```
-
-You can use Anthropic language models to generate text with the `generateText` function:
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: anthropic('claude-3-haiku-20240307'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Anthropic language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-The following optional provider options are available for Anthropic models:
-
-- `disableParallelToolUse` _boolean_
-
- Optional. Disables the use of parallel tool calls. Defaults to `false`.
-
- When set to `true`, the model will only call one tool at a time instead of potentially calling multiple tools in parallel.
-
-- `sendReasoning` _boolean_
-
- Optional. Include reasoning content in requests sent to the model. Defaults to `true`.
-
- If you are experiencing issues with the model handling requests involving
- reasoning content, you can set this to `false` to omit them from the request.
-
-- `effort` _"high" | "medium" | "low"_
-
- Optional. See [Effort section](#effort) for more details.
-
-- `thinking` _object_
-
- Optional. See [Reasoning section](#reasoning) for more details.
-
-- `toolStreaming` _boolean_
-
- Whether to enable tool streaming (and structured output streaming). Default to `true`.
-
-- `structuredOutputMode` _"outputFormat" | "jsonTool" | "auto"_
-
- Determines how structured outputs are generated. Optional.
-
- - `"outputFormat"`: Use the `output_format` parameter to specify the structured output format.
- - `"jsonTool"`: Use a special `"json"` tool to specify the structured output format.
- - `"auto"`: Use `"outputFormat"` when supported, otherwise fall back to `"jsonTool"` (default).
-
-### Structured Outputs and Tool Input Streaming
-
-Tool call streaming is enabled by default. You can opt out by setting the
-`toolStreaming` provider option to `false`.
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-import { streamText, tool } from 'ai';
-import { z } from 'zod';
-
-const result = streamText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools: {
- writeFile: tool({
- description: 'Write content to a file',
- inputSchema: z.object({
- path: z.string(),
- content: z.string(),
- }),
- execute: async ({ path, content }) => {
- // Implementation
- return { success: true };
- },
- }),
- },
- prompt: 'Write a short story to story.txt',
-});
-```
-
-### Effort
-
-Anthropic introduced an `effort` option with `claude-opus-4-5` that affects thinking, text responses, and function calls. Effort defaults to `high` and you can set it to `medium` or `low` to save tokens and to lower time-to-last-token latency (TTLT).
-
-```ts highlight="8-10"
-import { anthropic, AnthropicProviderOptions } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const { text, usage } = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'How many people will live in the world in 2040?',
- providerOptions: {
- anthropic: {
- effort: 'low',
- } satisfies AnthropicProviderOptions,
- },
-});
-
-console.log(text); // resulting text
-console.log(usage); // token usage
-```
-
-### Reasoning
-
-Anthropic has reasoning support for `claude-opus-4-20250514`, `claude-sonnet-4-20250514`, and `claude-3-7-sonnet-20250219` models.
-
-You can enable it using the `thinking` provider option
-and specifying a thinking budget in tokens.
-
-```ts highlight="4,8-10"
-import { anthropic, AnthropicProviderOptions } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const { text, reasoning, reasoningDetails } = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'How many people will live in the world in 2040?',
- providerOptions: {
- anthropic: {
- thinking: { type: 'enabled', budgetTokens: 12000 },
- } satisfies AnthropicProviderOptions,
- },
-});
-
-console.log(reasoning); // reasoning text
-console.log(reasoningDetails); // reasoning details including redacted reasoning
-console.log(text); // text response
-```
-
-See [AI SDK UI: Chatbot](/docs/ai-sdk-ui/chatbot#reasoning) for more details
-on how to integrate reasoning into your chatbot.
-
-### Cache Control
-
-In the messages and message parts, you can use the `providerOptions` property to set cache control breakpoints.
-You need to set the `anthropic` property in the `providerOptions` object to `{ cacheControl: { type: 'ephemeral' } }` to set a cache control breakpoint.
-
-The cache creation input tokens are then returned in the `providerMetadata` object
-for `generateText` and `generateObject`, again under the `anthropic` property.
-When you use `streamText` or `streamObject`, the response contains a promise
-that resolves to the metadata. Alternatively you can receive it in the
-`onFinish` callback.
-
-```ts highlight="8,18-20,29-30"
-import { anthropic } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const errorMessage = '... long error message ...';
-
-const result = await generateText({
- model: anthropic('claude-3-5-sonnet-20240620'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'You are a JavaScript expert.' },
- {
- type: 'text',
- text: `Error message: ${errorMessage}`,
- providerOptions: {
- anthropic: { cacheControl: { type: 'ephemeral' } },
- },
- },
- { type: 'text', text: 'Explain the error message.' },
- ],
- },
- ],
-});
-
-console.log(result.text);
-console.log(result.providerMetadata?.anthropic);
-// e.g. { cacheCreationInputTokens: 2118 }
-```
-
-You can also use cache control on system messages by providing multiple system messages at the head of your messages array:
-
-```ts highlight="3,7-9"
-const result = await generateText({
- model: anthropic('claude-3-5-sonnet-20240620'),
- messages: [
- {
- role: 'system',
- content: 'Cached system message part',
- providerOptions: {
- anthropic: { cacheControl: { type: 'ephemeral' } },
- },
- },
- {
- role: 'system',
- content: 'Uncached system message part',
- },
- {
- role: 'user',
- content: 'User prompt',
- },
- ],
-});
-```
-
-Cache control for tools:
-
-```ts
-const result = await generateText({
- model: anthropic('claude-3-5-haiku-latest'),
- tools: {
- cityAttractions: tool({
- inputSchema: z.object({ city: z.string() }),
- providerOptions: {
- anthropic: {
- cacheControl: { type: 'ephemeral' },
- },
- },
- }),
- },
- messages: [
- {
- role: 'user',
- content: 'User prompt',
- },
- ],
-});
-```
-
-#### Longer cache TTL
-
-Anthropic also supports a longer 1-hour cache duration.
-
-Here's an example:
-
-```ts
-const result = await generateText({
- model: anthropic('claude-3-5-haiku-latest'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'Long cached message',
- providerOptions: {
- anthropic: {
- cacheControl: { type: 'ephemeral', ttl: '1h' },
- },
- },
- },
- ],
- },
- ],
-});
-```
-
-#### Limitations
-
-The minimum cacheable prompt length is:
-
-- 1024 tokens for Claude 3.7 Sonnet, Claude 3.5 Sonnet and Claude 3 Opus
-- 2048 tokens for Claude 3.5 Haiku and Claude 3 Haiku
-
-Shorter prompts cannot be cached, even if marked with `cacheControl`. Any requests to cache fewer than this number of tokens will be processed without caching.
-
-For more on prompt caching with Anthropic, see [Anthropic's Cache Control documentation](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching).
-
-
- Because the `UIMessage` type (used by AI SDK UI hooks like `useChat`) does not
- support the `providerOptions` property, you can use `convertToModelMessages`
- first before passing the messages to functions like `generateText` or
- `streamText`. For more details on `providerOptions` usage, see
- [here](/docs/foundations/prompts#provider-options).
-
-
-### Bash Tool
-
-The Bash Tool allows running bash commands. Here's how to create and use it:
-
-```ts
-const bashTool = anthropic.tools.bash_20241022({
- execute: async ({ command, restart }) => {
- // Implement your bash command execution logic here
- // Return the result of the command execution
- },
-});
-```
-
-Parameters:
-
-- `command` (string): The bash command to run. Required unless the tool is being restarted.
-- `restart` (boolean, optional): Specifying true will restart this tool.
-
-
- The bash tool must have the name `bash`. Only certain Claude versions are
- supported.
-
-
-### Memory Tool
-
-The [Memory Tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/memory-tool) allows Claude to use a local memory, e.g. in the filesystem.
-Here's how to create it:
-
-```ts
-const memory = anthropic.tools.memory_20250818({
- execute: async action => {
- // Implement your memory command execution logic here
- // Return the result of the command execution
- },
-});
-```
-
-
- The memory tool must have the name `memory`. Only certain Claude versions are
- supported.
-
-
-### Text Editor Tool
-
-The Text Editor Tool provides functionality for viewing and editing text files.
-
-```ts
-const tools = {
- // tool name must be str_replace_based_edit_tool
- str_replace_based_edit_tool: anthropic.tools.textEditor_20250728({
- maxCharacters: 10000, // optional
- async execute({ command, path, old_str, new_str }) {
- // ...
- },
- }),
-} satisfies ToolSet;
-```
-
-
- Different models support different versions of the tool. For Claude Sonnet 3.5
- and 3.7 you need to use older tool versions and tool names.
-
-
-Parameters:
-
-- `command` ('view' | 'create' | 'str_replace' | 'insert' | 'undo_edit'): The command to run. Note: `undo_edit` is only available in Claude 3.5 Sonnet and earlier models.
-- `path` (string): Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.
-- `file_text` (string, optional): Required for `create` command, with the content of the file to be created.
-- `insert_line` (number, optional): Required for `insert` command. The line number after which to insert the new string.
-- `new_str` (string, optional): New string for `str_replace` or `insert` commands.
-- `old_str` (string, optional): Required for `str_replace` command, containing the string to replace.
-- `view_range` (number[], optional): Optional for `view` command to specify line range to show.
-
-### Computer Tool
-
-The Computer Tool enables control of keyboard and mouse actions on a computer:
-
-```ts
-const computerTool = anthropic.tools.computer_20241022({
- displayWidthPx: 1920,
- displayHeightPx: 1080,
- displayNumber: 0, // Optional, for X11 environments
-
- execute: async ({ action, coordinate, text }) => {
- // Implement your computer control logic here
- // Return the result of the action
-
- // Example code:
- switch (action) {
- case 'screenshot': {
- // multipart result:
- return {
- type: 'image',
- data: fs
- .readFileSync('./data/screenshot-editor.png')
- .toString('base64'),
- };
- }
- default: {
- console.log('Action:', action);
- console.log('Coordinate:', coordinate);
- console.log('Text:', text);
- return `executed ${action}`;
- }
- }
- },
-
- // map to tool result content for LLM consumption:
- toModelOutput(result) {
- return typeof result === 'string'
- ? [{ type: 'text', text: result }]
- : [{ type: 'image', data: result.data, mediaType: 'image/png' }];
- },
-});
-```
-
-Parameters:
-
-- `action` ('key' | 'type' | 'mouse_move' | 'left_click' | 'left_click_drag' | 'right_click' | 'middle_click' | 'double_click' | 'screenshot' | 'cursor_position'): The action to perform.
-- `coordinate` (number[], optional): Required for `mouse_move` and `left_click_drag` actions. Specifies the (x, y) coordinates.
-- `text` (string, optional): Required for `type` and `key` actions.
-
-These tools can be used in conjunction with the `sonnet-3-5-sonnet-20240620` model to enable more complex interactions and tasks.
-
-### Web Search Tool
-
-Anthropic provides a provider-defined web search tool that gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff.
-
-You can enable web search using the provider-defined web search tool:
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const webSearchTool = anthropic.tools.webSearch_20250305({
- maxUses: 5,
-});
-
-const result = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'What are the latest developments in AI?',
- tools: {
- web_search: webSearchTool,
- },
-});
-```
-
-
- Web search must be enabled in your organization's [Console
- settings](https://console.anthropic.com/settings/privacy).
-
-
-#### Configuration Options
-
-The web search tool supports several configuration options:
-
-- **maxUses** _number_
-
- Maximum number of web searches Claude can perform during the conversation.
-
-- **allowedDomains** _string[]_
-
- Optional list of domains that Claude is allowed to search. If provided, searches will be restricted to these domains.
-
-- **blockedDomains** _string[]_
-
- Optional list of domains that Claude should avoid when searching.
-
-- **userLocation** _object_
-
- Optional user location information to provide geographically relevant search results.
-
-```ts
-const webSearchTool = anthropic.tools.webSearch_20250305({
- maxUses: 3,
- allowedDomains: ['techcrunch.com', 'wired.com'],
- blockedDomains: ['example-spam-site.com'],
- userLocation: {
- type: 'approximate',
- country: 'US',
- region: 'California',
- city: 'San Francisco',
- timezone: 'America/Los_Angeles',
- },
-});
-
-const result = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'Find local news about technology',
- tools: {
- web_search: webSearchTool,
- },
-});
-```
-
-### Web Fetch Tool
-
-Anthropic provides a provider-defined web fetch tool that allows Claude to retrieve content from specific URLs. This is useful when you want Claude to analyze or reference content from a particular webpage or document.
-
-You can enable web fetch using the provider-defined web fetch tool:
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: anthropic('claude-sonnet-4-0'),
- prompt:
- 'What is this page about? https://en.wikipedia.org/wiki/Maglemosian_culture',
- tools: {
- web_fetch: anthropic.tools.webFetch_20250910({ maxUses: 1 }),
- },
-});
-```
-
-### MCP Connectors
-
-Anthropic supports connecting to [MCP servers](https://docs.claude.com/en/docs/agents-and-tools/mcp-connector) as part of their execution.
-
-You can enable this feature with the `mcpServers` provider option:
-
-```ts
-import { anthropic, AnthropicProviderOptions } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: anthropic('claude-sonnet-4-5'),
- prompt: `Call the echo tool with "hello world". what does it respond with back?`,
- providerOptions: {
- anthropic: {
- mcpServers: [
- {
- type: 'url',
- name: 'echo',
- url: 'https://echo.mcp.inevitable.fyi/mcp',
- // optional: authorization token
- authorizationToken: mcpAuthToken,
- // optional: tool configuration
- toolConfiguration: {
- enabled: true,
- allowedTools: ['echo'],
- },
- },
- ],
- } satisfies AnthropicProviderOptions,
- },
-});
-```
-
-The tool calls and results are dynamic, i.e. the input and output schemas are not known.
-
-#### Configuration Options
-
-The web fetch tool supports several configuration options:
-
-- **maxUses** _number_
-
- The maxUses parameter limits the number of web fetches performed.
-
-- **allowedDomains** _string[]_
-
- Only fetch from these domains.
-
-- **blockedDomains** _string[]_
-
- Never fetch from these domains.
-
-- **citations** _object_
-
- Unlike web search where citations are always enabled, citations are optional for web fetch. Set `"citations": {"enabled": true}` to enable Claude to cite specific passages from fetched documents.
-
-- **maxContentTokens** _number_
-
- The maxContentTokens parameter limits the amount of content that will be included in the context.
-
-#### Error Handling
-
-Web search errors are handled differently depending on whether you're using streaming or non-streaming:
-
-**Non-streaming (`generateText`, `generateObject`):**
-Web search errors throw exceptions that you can catch:
-
-```ts
-try {
- const result = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'Search for something',
- tools: {
- web_search: webSearchTool,
- },
- });
-} catch (error) {
- if (error.message.includes('Web search failed')) {
- console.log('Search error:', error.message);
- // Handle search error appropriately
- }
-}
-```
-
-**Streaming (`streamText`, `streamObject`):**
-Web search errors are delivered as error parts in the stream:
-
-```ts
-const result = await streamText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'Search for something',
- tools: {
- web_search: webSearchTool,
- },
-});
-
-for await (const part of result.textStream) {
- if (part.type === 'error') {
- console.log('Search error:', part.error);
- // Handle search error appropriately
- }
-}
-```
-
-## Code Execution
-
-Anthropic provides a provider-defined code execution tool that gives Claude direct access to a real Python environment allowing it to execute code to inform its responses.
-
-You can enable code execution using the provider-defined code execution tool:
-
-```ts
-import { anthropic } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const codeExecutionTool = anthropic.tools.codeExecution_20250825();
-
-const result = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt:
- 'Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]',
- tools: {
- code_execution: codeExecutionTool,
- },
-});
-```
-
-#### Error Handling
-
-Code execution errors are handled differently depending on whether you're using streaming or non-streaming:
-
-**Non-streaming (`generateText`, `generateObject`):**
-Code execution errors are delivered as tool result parts in the response:
-
-```ts
-const result = await generateText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'Execute some Python script',
- tools: {
- code_execution: codeExecutionTool,
- },
-});
-
-const toolErrors = result.content?.filter(
- content => content.type === 'tool-error',
-);
-
-toolErrors?.forEach(error => {
- console.error('Tool execution error:', {
- toolName: error.toolName,
- toolCallId: error.toolCallId,
- error: error.error,
- });
-});
-```
-
-**Streaming (`streamText`, `streamObject`):**
-Code execution errors are delivered as error parts in the stream:
-
-```ts
-const result = await streamText({
- model: anthropic('claude-opus-4-20250514'),
- prompt: 'Execute some Python script',
- tools: {
- code_execution: codeExecutionTool,
- },
-});
-for await (const part of result.textStream) {
- if (part.type === 'error') {
- console.log('Code execution error:', part.error);
- // Handle code execution error appropriately
- }
-}
-```
-
-## Agent Skills
-
-[Anthropic Agent Skills](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview) enable Claude to perform specialized tasks like document processing (PPTX, DOCX, PDF, XLSX) and data analysis. Skills run in a sandboxed container and require the code execution tool to be enabled.
-
-### Using Built-in Skills
-
-Anthropic provides several built-in skills:
-
-- **pptx** - Create and edit PowerPoint presentations
-- **docx** - Create and edit Word documents
-- **pdf** - Process and analyze PDF files
-- **xlsx** - Work with Excel spreadsheets
-
-To use skills, you need to:
-
-1. Enable the code execution tool
-2. Specify the container with skills in `providerOptions`
-
-```ts highlight="4,9-17,19-23"
-import { anthropic, AnthropicProviderOptions } from '@ai-sdk/anthropic';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: anthropic('claude-sonnet-4-5'),
- tools: {
- code_execution: anthropic.tools.codeExecution_20250825(),
- },
- prompt: 'Create a presentation about renewable energy with 5 slides',
- providerOptions: {
- anthropic: {
- container: {
- skills: [
- {
- type: 'anthropic',
- skillId: 'pptx',
- version: 'latest', // optional
- },
- ],
- },
- } satisfies AnthropicProviderOptions,
- },
-});
-```
-
-### Custom Skills
-
-You can also use custom skills by specifying `type: 'custom'`:
-
-```ts highlight="9-11"
-const result = await generateText({
- model: anthropic('claude-sonnet-4-5'),
- tools: {
- code_execution: anthropic.tools.codeExecution_20250825(),
- },
- prompt: 'Use my custom skill to process this data',
- providerOptions: {
- anthropic: {
- container: {
- skills: [
- {
- type: 'custom',
- skillId: 'my-custom-skill-id',
- version: '1.0', // optional
- },
- ],
- },
- } satisfies AnthropicProviderOptions,
- },
-});
-```
-
-
- Skills use progressive context loading and execute within a sandboxed
- container with code execution capabilities.
-
-
-### PDF support
-
-Anthropic Sonnet `claude-3-5-sonnet-20241022` supports reading PDF files.
-You can pass PDF files as part of the message content using the `file` type:
-
-Option 1: URL-based PDF document
-
-```ts
-const result = await generateText({
- model: anthropic('claude-3-5-sonnet-20241022'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model according to this document?',
- },
- {
- type: 'file',
- data: new URL(
- 'https://github.com/vercel/ai/blob/main/examples/ai-core/data/ai.pdf?raw=true',
- ),
- mimeType: 'application/pdf',
- },
- ],
- },
- ],
-});
-```
-
-Option 2: Base64-encoded PDF document
-
-```ts
-const result = await generateText({
- model: anthropic('claude-3-5-sonnet-20241022'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model according to this document?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- },
- ],
- },
- ],
-});
-```
-
-The model will have access to the contents of the PDF file and
-respond to questions about it.
-The PDF file should be passed using the `data` field,
-and the `mediaType` should be set to `'application/pdf'`.
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Computer Use | Web Search |
-| -------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `claude-opus-4-5` | | | | | |
-| `claude-haiku-4-5` | | | | | |
-| `claude-sonnet-4-5` | | | | | |
-| `claude-opus-4-1` | | | | | |
-| `claude-opus-4-0` | | | | | |
-| `claude-sonnet-4-0` | | | | | |
-| `claude-3-7-sonnet-latest` | | | | | |
-| `claude-3-5-haiku-latest` | | | | | |
-
-
- The table above lists popular models. Please see the [Anthropic
- docs](https://docs.anthropic.com/en/docs/about-claude/models) for a full list
- of available models. The table above lists popular models. You can also pass
- any available provider model ID as a string if needed.
-
-
----
-title: Amazon Bedrock
-description: Learn how to use the Amazon Bedrock provider.
----
-
-# Amazon Bedrock Provider
-
-The Amazon Bedrock provider for the [AI SDK](/docs) contains language model support for the [Amazon Bedrock](https://aws.amazon.com/bedrock) APIs.
-
-## Setup
-
-The Bedrock provider is available in the `@ai-sdk/amazon-bedrock` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Prerequisites
-
-Access to Amazon Bedrock foundation models isn't granted by default. In order to gain access to a foundation model, an IAM user with sufficient permissions needs to request access to it through the console. Once access is provided to a model, it is available for all users in the account.
-
-See the [Model Access Docs](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) for more information.
-
-### Authentication
-
-#### Using IAM Access Key and Secret Key
-
-**Step 1: Creating AWS Access Key and Secret Key**
-
-To get started, you'll need to create an AWS access key and secret key. Here's how:
-
-**Login to AWS Management Console**
-
-- Go to the [AWS Management Console](https://console.aws.amazon.com/) and log in with your AWS account credentials.
-
-**Create an IAM User**
-
-- Navigate to the [IAM dashboard](https://console.aws.amazon.com/iam/home) and click on "Users" in the left-hand navigation menu.
-- Click on "Create user" and fill in the required details to create a new IAM user.
-- Make sure to select "Programmatic access" as the access type.
-- The user account needs the `AmazonBedrockFullAccess` policy attached to it.
-
-**Create Access Key**
-
-- Click on the "Security credentials" tab and then click on "Create access key".
-- Click "Create access key" to generate a new access key pair.
-- Download the `.csv` file containing the access key ID and secret access key.
-
-**Step 2: Configuring the Access Key and Secret Key**
-
-Within your project add a `.env` file if you don't already have one. This file will be used to set the access key and secret key as environment variables. Add the following lines to the `.env` file:
-
-```makefile
-AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
-AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY
-AWS_REGION=YOUR_REGION
-```
-
-
- Many frameworks such as [Next.js](https://nextjs.org/) load the `.env` file
- automatically. If you're using a different framework, you may need to load the
- `.env` file manually using a package like
- [`dotenv`](https://github.com/motdotla/dotenv).
-
-
-Remember to replace `YOUR_ACCESS_KEY_ID`, `YOUR_SECRET_ACCESS_KEY`, and `YOUR_REGION` with the actual values from your AWS account.
-
-#### Using AWS SDK Credentials Chain (instance profiles, instance roles, ECS roles, EKS Service Accounts, etc.)
-
-When using AWS SDK, the SDK will automatically use the credentials chain to determine the credentials to use. This includes instance profiles, instance roles, ECS roles, EKS Service Accounts, etc. A similar behavior is possible using the AI SDK by not specifying the `accessKeyId` and `secretAccessKey`, `sessionToken` properties in the provider settings and instead passing a `credentialProvider` property.
-
-_Usage:_
-
-`@aws-sdk/credential-providers` package provides a set of credential providers that can be used to create a credential provider chain.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```ts
-import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
-import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
-
-const bedrock = createAmazonBedrock({
- region: 'us-east-1',
- credentialProvider: fromNodeProviderChain(),
-});
-```
-
-## Provider Instance
-
-You can import the default provider instance `bedrock` from `@ai-sdk/amazon-bedrock`:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-```
-
-If you need a customized setup, you can import `createAmazonBedrock` from `@ai-sdk/amazon-bedrock` and create a provider instance with your settings:
-
-```ts
-import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
-
-const bedrock = createAmazonBedrock({
- region: 'us-east-1',
- accessKeyId: 'xxxxxxxxx',
- secretAccessKey: 'xxxxxxxxx',
- sessionToken: 'xxxxxxxxx',
-});
-```
-
-
- The credentials settings fall back to environment variable defaults described
- below. These may be set by your serverless environment without your awareness,
- which can lead to merged/conflicting credential values and provider errors
- around failed authentication. If you're experiencing issues be sure you are
- explicitly specifying all settings (even if `undefined`) to avoid any
- defaults.
-
-
-You can use the following optional settings to customize the Amazon Bedrock provider instance:
-
-- **region** _string_
-
- The AWS region that you want to use for the API calls.
- It uses the `AWS_REGION` environment variable by default.
-
-- **accessKeyId** _string_
-
- The AWS access key ID that you want to use for the API calls.
- It uses the `AWS_ACCESS_KEY_ID` environment variable by default.
-
-- **secretAccessKey** _string_
-
- The AWS secret access key that you want to use for the API calls.
- It uses the `AWS_SECRET_ACCESS_KEY` environment variable by default.
-
-- **sessionToken** _string_
-
- Optional. The AWS session token that you want to use for the API calls.
- It uses the `AWS_SESSION_TOKEN` environment variable by default.
-
-- **credentialProvider** _() => Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken?: string; }>_
-
- Optional. The AWS credential provider chain that you want to use for the API calls.
- It uses the specified credentials by default.
-
-## Language Models
-
-You can create models that call the Bedrock API using the provider instance.
-The first argument is the model id, e.g. `meta.llama3-70b-instruct-v1:0`.
-
-```ts
-const model = bedrock('meta.llama3-70b-instruct-v1:0');
-```
-
-Amazon Bedrock models also support some model specific provider options that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them in the `providerOptions` argument:
-
-```ts
-const model = bedrock('anthropic.claude-3-sonnet-20240229-v1:0');
-
-await generateText({
- model,
- providerOptions: {
- anthropic: {
- additionalModelRequestFields: { top_k: 350 },
- },
- },
-});
-```
-
-Documentation for additional settings based on the selected model can be found within the [Amazon Bedrock Inference Parameter Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html).
-
-You can use Amazon Bedrock language models to generate text with the `generateText` function:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: bedrock('meta.llama3-70b-instruct-v1:0'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Amazon Bedrock language models can also be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-### File Inputs
-
-
- Amazon Bedrock supports file inputs on in combination with specific models,
- e.g. `anthropic.claude-3-haiku-20240307-v1:0`.
-
-
-The Amazon Bedrock provider supports file inputs, e.g. PDF files.
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: bedrock('anthropic.claude-3-haiku-20240307-v1:0'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'Describe the pdf in detail.' },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- },
- ],
- },
- ],
-});
-```
-
-### Guardrails
-
-You can use the `bedrock` provider options to utilize [Amazon Bedrock Guardrails](https://aws.amazon.com/bedrock/guardrails/):
-
-```ts
-const result = await generateText({
- model: bedrock('anthropic.claude-3-sonnet-20240229-v1:0'),
- prompt: 'Write a story about space exploration.',
- providerOptions: {
- bedrock: {
- guardrailConfig: {
- guardrailIdentifier: '1abcd2ef34gh',
- guardrailVersion: '1',
- trace: 'enabled' as const,
- streamProcessingMode: 'async',
- },
- },
- },
-});
-```
-
-Tracing information will be returned in the provider metadata if you have tracing enabled.
-
-```ts
-if (result.providerMetadata?.bedrock.trace) {
- // ...
-}
-```
-
-See the [Amazon Bedrock Guardrails documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) for more information.
-
-### Citations
-
-Amazon Bedrock supports citations for document-based inputs across compatible models. When enabled:
-
-- Some models can read documents with visual understanding, not just extracting text
-- Models can cite specific parts of documents you provide, making it easier to trace information back to its source (Not Supported Yet)
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-import fs from 'fs';
-
-const result = await generateObject({
- model: bedrock('apac.anthropic.claude-sonnet-4-20250514-v1:0'),
- schema: z.object({
- summary: z.string().describe('Summary of the PDF document'),
- keyPoints: z.array(z.string()).describe('Key points from the PDF'),
- }),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'Summarize this PDF and provide key points.',
- },
- {
- type: 'file',
- data: fs.readFileSync('./document.pdf'),
- mediaType: 'application/pdf',
- providerOptions: {
- bedrock: {
- citations: { enabled: true },
- },
- },
- },
- ],
- },
- ],
-});
-
-console.log('Response:', result.object);
-```
-
-### Cache Points
-
-
- Amazon Bedrock prompt caching is currently in preview release. To request
- access, visit the [Amazon Bedrock prompt caching
- page](https://aws.amazon.com/bedrock/prompt-caching/).
-
-
-In messages, you can use the `providerOptions` property to set cache points. Set the `bedrock` property in the `providerOptions` object to `{ cachePoint: { type: 'default' } }` to create a cache point.
-
-Cache usage information is returned in the `providerMetadata` object`. See examples below.
-
-
- Cache points have model-specific token minimums and limits. For example,
- Claude 3.5 Sonnet v2 requires at least 1,024 tokens for a cache point and
- allows up to 4 cache points. See the [Amazon Bedrock prompt caching
- documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html)
- for details on supported models, regions, and limits.
-
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const cyberpunkAnalysis =
- '... literary analysis of cyberpunk themes and concepts ...';
-
-const result = await generateText({
- model: bedrock('anthropic.claude-3-5-sonnet-20241022-v2:0'),
- messages: [
- {
- role: 'system',
- content: `You are an expert on William Gibson's cyberpunk literature and themes. You have access to the following academic analysis: ${cyberpunkAnalysis}`,
- providerOptions: {
- bedrock: { cachePoint: { type: 'default' } },
- },
- },
- {
- role: 'user',
- content:
- 'What are the key cyberpunk themes that Gibson explores in Neuromancer?',
- },
- ],
-});
-
-console.log(result.text);
-console.log(result.providerMetadata?.bedrock?.usage);
-// Shows cache read/write token usage, e.g.:
-// {
-// cacheReadInputTokens: 1337,
-// cacheWriteInputTokens: 42,
-// }
-```
-
-Cache points also work with streaming responses:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { streamText } from 'ai';
-
-const cyberpunkAnalysis =
- '... literary analysis of cyberpunk themes and concepts ...';
-
-const result = streamText({
- model: bedrock('anthropic.claude-3-5-sonnet-20241022-v2:0'),
- messages: [
- {
- role: 'assistant',
- content: [
- { type: 'text', text: 'You are an expert on cyberpunk literature.' },
- { type: 'text', text: `Academic analysis: ${cyberpunkAnalysis}` },
- ],
- providerOptions: { bedrock: { cachePoint: { type: 'default' } } },
- },
- {
- role: 'user',
- content:
- 'How does Gibson explore the relationship between humanity and technology?',
- },
- ],
-});
-
-for await (const textPart of result.textStream) {
- process.stdout.write(textPart);
-}
-
-console.log(
- 'Cache token usage:',
- (await result.providerMetadata)?.bedrock?.usage,
-);
-// Shows cache read/write token usage, e.g.:
-// {
-// cacheReadInputTokens: 1337,
-// cacheWriteInputTokens: 42,
-// }
-```
-
-## Reasoning
-
-Amazon Bedrock has reasoning support for the `claude-3-7-sonnet-20250219` model.
-
-You can enable it using the `reasoningConfig` provider option and specifying a thinking budget in tokens (minimum: `1024`, maximum: `64000`).
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const { text, reasoning, reasoningDetails } = await generateText({
- model: bedrock('us.anthropic.claude-3-7-sonnet-20250219-v1:0'),
- prompt: 'How many people will live in the world in 2040?',
- providerOptions: {
- bedrock: {
- reasoningConfig: { type: 'enabled', budgetTokens: 1024 },
- },
- },
-});
-
-console.log(reasoning); // reasoning text
-console.log(reasoningDetails); // reasoning details including redacted reasoning
-console.log(text); // text response
-```
-
-See [AI SDK UI: Chatbot](/docs/ai-sdk-ui/chatbot#reasoning) for more details
-on how to integrate reasoning into your chatbot.
-
-## Extended Context Window
-
-Claude Sonnet 4 models on Amazon Bedrock support an extended context window of up to 1 million tokens when using the `context-1m-2025-08-07` beta feature.
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
- prompt: 'analyze this large document...',
- providerOptions: {
- bedrock: {
- anthropicBeta: ['context-1m-2025-08-07'],
- },
- },
-});
-```
-
-## Computer Use
-
-Via Anthropic, Amazon Bedrock provides three provider-defined tools that can be used to interact with external systems:
-
-1. **Bash Tool**: Allows running bash commands.
-2. **Text Editor Tool**: Provides functionality for viewing and editing text files.
-3. **Computer Tool**: Enables control of keyboard and mouse actions on a computer.
-
-They are available via the `tools` property of the provider instance.
-
-### Bash Tool
-
-The Bash Tool allows running bash commands. Here's how to create and use it:
-
-```ts
-const bashTool = anthropic.tools.bash_20241022({
- execute: async ({ command, restart }) => {
- // Implement your bash command execution logic here
- // Return the result of the command execution
- },
-});
-```
-
-Parameters:
-
-- `command` (string): The bash command to run. Required unless the tool is being restarted.
-- `restart` (boolean, optional): Specifying true will restart this tool.
-
-### Text Editor Tool
-
-The Text Editor Tool provides functionality for viewing and editing text files.
-
-**For Claude 4 models (Opus & Sonnet):**
-
-```ts
-const textEditorTool = anthropic.tools.textEditor_20250429({
- execute: async ({
- command,
- path,
- file_text,
- insert_line,
- new_str,
- old_str,
- view_range,
- }) => {
- // Implement your text editing logic here
- // Return the result of the text editing operation
- },
-});
-```
-
-**For Claude 3.5 Sonnet and earlier models:**
-
-```ts
-const textEditorTool = anthropic.tools.textEditor_20241022({
- execute: async ({
- command,
- path,
- file_text,
- insert_line,
- new_str,
- old_str,
- view_range,
- }) => {
- // Implement your text editing logic here
- // Return the result of the text editing operation
- },
-});
-```
-
-Parameters:
-
-- `command` ('view' | 'create' | 'str_replace' | 'insert' | 'undo_edit'): The command to run. Note: `undo_edit` is only available in Claude 3.5 Sonnet and earlier models.
-- `path` (string): Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.
-- `file_text` (string, optional): Required for `create` command, with the content of the file to be created.
-- `insert_line` (number, optional): Required for `insert` command. The line number after which to insert the new string.
-- `new_str` (string, optional): New string for `str_replace` or `insert` commands.
-- `old_str` (string, optional): Required for `str_replace` command, containing the string to replace.
-- `view_range` (number[], optional): Optional for `view` command to specify line range to show.
-
-When using the Text Editor Tool, make sure to name the key in the tools object correctly:
-
-- **Claude 4 models**: Use `str_replace_based_edit_tool`
-- **Claude 3.5 Sonnet and earlier**: Use `str_replace_editor`
-
-```ts
-// For Claude 4 models
-const response = await generateText({
- model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
- prompt:
- "Create a new file called example.txt, write 'Hello World' to it, and run 'cat example.txt' in the terminal",
- tools: {
- str_replace_based_edit_tool: textEditorTool, // Claude 4 tool name
- },
-});
-
-// For Claude 3.5 Sonnet and earlier
-const response = await generateText({
- model: bedrock('anthropic.claude-3-5-sonnet-20241022-v2:0'),
- prompt:
- "Create a new file called example.txt, write 'Hello World' to it, and run 'cat example.txt' in the terminal",
- tools: {
- str_replace_editor: textEditorTool, // Earlier models tool name
- },
-});
-```
-
-### Computer Tool
-
-The Computer Tool enables control of keyboard and mouse actions on a computer:
-
-```ts
-const computerTool = anthropic.tools.computer_20241022({
- displayWidthPx: 1920,
- displayHeightPx: 1080,
- displayNumber: 0, // Optional, for X11 environments
-
- execute: async ({ action, coordinate, text }) => {
- // Implement your computer control logic here
- // Return the result of the action
-
- // Example code:
- switch (action) {
- case 'screenshot': {
- // multipart result:
- return {
- type: 'image',
- data: fs
- .readFileSync('./data/screenshot-editor.png')
- .toString('base64'),
- };
- }
- default: {
- console.log('Action:', action);
- console.log('Coordinate:', coordinate);
- console.log('Text:', text);
- return `executed ${action}`;
- }
- }
- },
-
- // map to tool result content for LLM consumption:
- toModelOutput(result) {
- return typeof result === 'string'
- ? [{ type: 'text', text: result }]
- : [{ type: 'image', data: result.data, mediaType: 'image/png' }];
- },
-});
-```
-
-Parameters:
-
-- `action` ('key' | 'type' | 'mouse_move' | 'left_click' | 'left_click_drag' | 'right_click' | 'middle_click' | 'double_click' | 'screenshot' | 'cursor_position'): The action to perform.
-- `coordinate` (number[], optional): Required for `mouse_move` and `left_click_drag` actions. Specifies the (x, y) coordinates.
-- `text` (string, optional): Required for `type` and `key` actions.
-
-These tools can be used in conjunction with the `anthropic.claude-3-5-sonnet-20240620-v1:0` model to enable more complex interactions and tasks.
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ---------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `amazon.titan-tg1-large` | | | | |
-| `amazon.titan-text-express-v1` | | | | |
-| `amazon.titan-text-lite-v1` | | | | |
-| `us.amazon.nova-premier-v1:0` | | | | |
-| `us.amazon.nova-pro-v1:0` | | | | |
-| `us.amazon.nova-lite-v1:0` | | | | |
-| `us.amazon.nova-micro-v1:0` | | | | |
-| `anthropic.claude-haiku-4-5-20251001-v1:0` | | | | |
-| `anthropic.claude-sonnet-4-20250514-v1:0` | | | | |
-| `anthropic.claude-sonnet-4-5-20250929-v1:0` | | | | |
-| `anthropic.claude-opus-4-20250514-v1:0` | | | | |
-| `anthropic.claude-opus-4-1-20250805-v1:0` | | | | |
-| `anthropic.claude-3-7-sonnet-20250219-v1:0` | | | | |
-| `anthropic.claude-3-5-sonnet-20241022-v2:0` | | | | |
-| `anthropic.claude-3-5-sonnet-20240620-v1:0` | | | | |
-| `anthropic.claude-3-5-haiku-20241022-v1:0` | | | | |
-| `anthropic.claude-3-opus-20240229-v1:0` | | | | |
-| `anthropic.claude-3-sonnet-20240229-v1:0` | | | | |
-| `anthropic.claude-3-haiku-20240307-v1:0` | | | | |
-| `us.anthropic.claude-sonnet-4-20250514-v1:0` | | | | |
-| `us.anthropic.claude-sonnet-4-5-20250929-v1:0` | | | | |
-| `us.anthropic.claude-opus-4-20250514-v1:0` | | | | |
-| `us.anthropic.claude-opus-4-1-20250805-v1:0` | | | | |
-| `us.anthropic.claude-3-7-sonnet-20250219-v1:0` | | | | |
-| `us.anthropic.claude-3-5-sonnet-20241022-v2:0` | | | | |
-| `us.anthropic.claude-3-5-sonnet-20240620-v1:0` | | | | |
-| `us.anthropic.claude-3-5-haiku-20241022-v1:0` | | | | |
-| `us.anthropic.claude-3-sonnet-20240229-v1:0` | | | | |
-| `us.anthropic.claude-3-opus-20240229-v1:0` | | | | |
-| `us.anthropic.claude-3-haiku-20240307-v1:0` | | | | |
-| `anthropic.claude-v2` | | | | |
-| `anthropic.claude-v2:1` | | | | |
-| `anthropic.claude-instant-v1` | | | | |
-| `cohere.command-text-v14` | | | | |
-| `cohere.command-light-text-v14` | | | | |
-| `cohere.command-r-v1:0` | | | | |
-| `cohere.command-r-plus-v1:0` | | | | |
-| `us.deepseek.r1-v1:0` | | | | |
-| `meta.llama3-8b-instruct-v1:0` | | | | |
-| `meta.llama3-70b-instruct-v1:0` | | | | |
-| `meta.llama3-1-8b-instruct-v1:0` | | | | |
-| `meta.llama3-1-70b-instruct-v1:0` | | | | |
-| `meta.llama3-1-405b-instruct-v1:0` | | | | |
-| `meta.llama3-2-1b-instruct-v1:0` | | | | |
-| `meta.llama3-2-3b-instruct-v1:0` | | | | |
-| `meta.llama3-2-11b-instruct-v1:0` | | | | |
-| `meta.llama3-2-90b-instruct-v1:0` | | | | |
-| `us.meta.llama3-2-1b-instruct-v1:0` | | | | |
-| `us.meta.llama3-2-3b-instruct-v1:0` | | | | |
-| `us.meta.llama3-2-11b-instruct-v1:0` | | | | |
-| `us.meta.llama3-2-90b-instruct-v1:0` | | | | |
-| `us.meta.llama3-1-8b-instruct-v1:0` | | | | |
-| `us.meta.llama3-1-70b-instruct-v1:0` | | | | |
-| `us.meta.llama3-3-70b-instruct-v1:0` | | | | |
-| `us.meta.llama4-scout-17b-instruct-v1:0` | | | | |
-| `us.meta.llama4-maverick-17b-instruct-v1:0` | | | | |
-| `mistral.mistral-7b-instruct-v0:2` | | | | |
-| `mistral.mixtral-8x7b-instruct-v0:1` | | | | |
-| `mistral.mistral-large-2402-v1:0` | | | | |
-| `mistral.mistral-small-2402-v1:0` | | | | |
-| `us.mistral.pixtral-large-2502-v1:0` | | | | |
-| `openai.gpt-oss-120b-1:0` | | | | |
-| `openai.gpt-oss-20b-1:0` | | | | |
-
-
- The table above lists popular models. Please see the [Amazon Bedrock
- docs](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html)
- for a full list of available models. The table above lists popular models. You
- can also pass any available provider model ID as a string if needed.
-
-
-## Embedding Models
-
-You can create models that call the Bedrock API [Bedrock API](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html)
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = bedrock.textEmbedding('amazon.titan-embed-text-v1');
-```
-
-Bedrock Titan embedding model amazon.titan-embed-text-v2:0 supports several additional settings.
-You can pass them as an options argument:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { embed } from 'ai';
-
-const model = bedrock.textEmbedding('amazon.titan-embed-text-v2:0');
-
-const { embedding } = await embed({
- model,
- value: 'sunny day at the beach',
- providerOptions: {
- bedrock: {
- dimensions: 512, // optional, number of dimensions for the embedding
- normalize: true, // optional, normalize the output embeddings
- },
- },
-});
-```
-
-The following optional provider options are available for Bedrock Titan embedding models:
-
-- **dimensions**: _number_
-
- The number of dimensions the output embeddings should have. The following values are accepted: 1024 (default), 512, 256.
-
-- **normalize** _boolean_
-
- Flag indicating whether or not to normalize the output embeddings. Defaults to true.
-
-### Model Capabilities
-
-| Model | Default Dimensions | Custom Dimensions |
-| ------------------------------ | ------------------ | ------------------- |
-| `amazon.titan-embed-text-v1` | 1536 | |
-| `amazon.titan-embed-text-v2:0` | 1024 | |
-| `cohere.embed-english-v3` | 1024 | |
-| `cohere.embed-multilingual-v3` | 1024 | |
-
-## Reranking Models
-
-You can create models that call the [Bedrock Rerank API](https://docs.aws.amazon.com/bedrock/latest/userguide/rerank-api.html)
-using the `.reranking()` factory method.
-
-```ts
-const model = bedrock.reranking('cohere.rerank-v3-5:0');
-```
-
-You can use Amazon Bedrock reranking models to rerank documents with the `rerank` function:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { rerank } from 'ai';
-
-const documents = [
- 'sunny day at the beach',
- 'rainy afternoon in the city',
- 'snowy night in the mountains',
-];
-
-const { ranking } = await rerank({
- model: bedrock.reranking('cohere.rerank-v3-5:0'),
- documents,
- query: 'talk about rain',
- topN: 2,
-});
-
-console.log(ranking);
-// [
-// { originalIndex: 1, score: 0.9, document: 'rainy afternoon in the city' },
-// { originalIndex: 0, score: 0.3, document: 'sunny day at the beach' }
-// ]
-```
-
-Amazon Bedrock reranking models support additional provider options that can be passed via `providerOptions.bedrock`:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { rerank } from 'ai';
-
-const { ranking } = await rerank({
- model: bedrock.reranking('cohere.rerank-v3-5:0'),
- documents: ['sunny day at the beach', 'rainy afternoon in the city'],
- query: 'talk about rain',
- providerOptions: {
- bedrock: {
- nextToken: 'pagination_token_here',
- },
- },
-});
-```
-
-The following provider options are available:
-
-- **nextToken** _string_
-
- Token for pagination of results.
-
-- **additionalModelRequestFields** _Record<string, unknown>_
-
- Additional model-specific request fields.
-
-### Model Capabilities
-
-| Model |
-| ---------------------- |
-| `amazon.rerank-v1:0` |
-| `cohere.rerank-v3-5:0` |
-
-## Image Models
-
-You can create models that call the Bedrock API [Bedrock API](https://docs.aws.amazon.com/nova/latest/userguide/image-generation.html)
-using the `.image()` factory method.
-
-For more on the Amazon Nova Canvas image model, see the [Nova Canvas
-Overview](https://docs.aws.amazon.com/ai/responsible-ai/nova-canvas/overview.html).
-
-
- The `amazon.nova-canvas-v1:0` model is available in the `us-east-1`,
- `eu-west-1`, and `ap-northeast-1` regions.
-
-
-```ts
-const model = bedrock.image('amazon.nova-canvas-v1:0');
-```
-
-You can then generate images with the `experimental_generateImage` function:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: bedrock.image('amazon.nova-canvas-v1:0'),
- prompt: 'A beautiful sunset over a calm ocean',
- size: '512x512',
- seed: 42,
-});
-```
-
-You can also pass the `providerOptions` object to the `generateImage` function to customize the generation behavior:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: bedrock.image('amazon.nova-canvas-v1:0'),
- prompt: 'A beautiful sunset over a calm ocean',
- size: '512x512',
- seed: 42,
- providerOptions: {
- bedrock: {
- quality: 'premium',
- negativeText: 'blurry, low quality',
- cfgScale: 7.5,
- style: 'PHOTOREALISM',
- },
- },
-});
-```
-
-The following optional provider options are available for Amazon Nova Canvas:
-
-- **quality** _string_
-
- The quality level for image generation. Accepts `'standard'` or `'premium'`.
-
-- **negativeText** _string_
-
- Text describing what you don't want in the generated image.
-
-- **cfgScale** _number_
-
- Controls how closely the generated image adheres to the prompt. Higher values result in images that are more closely aligned to the prompt.
-
-- **style** _string_
-
- Predefined visual style for image generation.
- Accepts one of:
- `3D_ANIMATED_FAMILY_FILM` · `DESIGN_SKETCH` · `FLAT_VECTOR_ILLUSTRATION` ·
- `GRAPHIC_NOVEL_ILLUSTRATION` · `MAXIMALISM` · `MIDCENTURY_RETRO` ·
- `PHOTOREALISM` · `SOFT_DIGITAL_PAINTING`.
-
-Documentation for additional settings can be found within the [Amazon Bedrock
-User Guide for Amazon Nova
-Documentation](https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html).
-
-### Image Model Settings
-
-You can customize the generation behavior with optional options:
-
-```ts
-await generateImage({
- model: bedrock.image('amazon.nova-canvas-v1:0'),
- prompt: 'A beautiful sunset over a calm ocean',
- size: '512x512',
- seed: 42,
- maxImagesPerCall: 1, // Maximum number of images to generate per API call
-});
-```
-
-- **maxImagesPerCall** _number_
-
- Override the maximum number of images generated per API call. Default can vary
- by model, with 5 as a common default.
-
-### Model Capabilities
-
-The Amazon Nova Canvas model supports custom sizes with constraints as follows:
-
-- Each side must be between 320-4096 pixels, inclusive.
-- Each side must be evenly divisible by 16.
-- The aspect ratio must be between 1:4 and 4:1. That is, one side can't be more than 4 times longer than the other side.
-- The total pixel count must be less than 4,194,304.
-
-For more, see [Image generation access and
-usage](https://docs.aws.amazon.com/nova/latest/userguide/image-gen-access.html).
-
-| Model | Sizes |
-| ------------------------- | ----------------------------------------------------------------------------------------------------- |
-| `amazon.nova-canvas-v1:0` | Custom sizes: 320-4096px per side (must be divisible by 16), aspect ratio 1:4 to 4:1, max 4.2M pixels |
-
-## Response Headers
-
-The Amazon Bedrock provider will return the response headers associated with
-network requests made of the Bedrock servers.
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: bedrock('meta.llama3-70b-instruct-v1:0'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-
-console.log(result.response.headers);
-```
-
-Below is sample output where you can see the `x-amzn-requestid` header. This can
-be useful for correlating Bedrock API calls with requests made by the AI SDK:
-
-```js highlight="6"
-{
- connection: 'keep-alive',
- 'content-length': '2399',
- 'content-type': 'application/json',
- date: 'Fri, 07 Feb 2025 04:28:30 GMT',
- 'x-amzn-requestid': 'c9f3ace4-dd5d-49e5-9807-39aedfa47c8e'
-}
-```
-
-This information is also available with `streamText`:
-
-```ts
-import { bedrock } from '@ai-sdk/amazon-bedrock';
-import { streamText } from 'ai';
-
-const result = streamText({
- model: bedrock('meta.llama3-70b-instruct-v1:0'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-for await (const textPart of result.textStream) {
- process.stdout.write(textPart);
-}
-console.log('Response headers:', (await result.response).headers);
-```
-
-With sample output as:
-
-```js highlight="6"
-{
- connection: 'keep-alive',
- 'content-type': 'application/vnd.amazon.eventstream',
- date: 'Fri, 07 Feb 2025 04:33:37 GMT',
- 'transfer-encoding': 'chunked',
- 'x-amzn-requestid': 'a976e3fc-0e45-4241-9954-b9bdd80ab407'
-}
-```
-
-## Migrating to `@ai-sdk/amazon-bedrock` 2.x
-
-The Amazon Bedrock provider was rewritten in version 2.x to remove the
-dependency on the `@aws-sdk/client-bedrock-runtime` package.
-
-The `bedrockOptions` provider setting previously available has been removed. If
-you were using the `bedrockOptions` object, you should now use the `region`,
-`accessKeyId`, `secretAccessKey`, and `sessionToken` settings directly instead.
-
-Note that you may need to set all of these explicitly, e.g. even if you're not
-using `sessionToken`, set it to `undefined`. If you're running in a serverless
-environment, there may be default environment variables set by your containing
-environment that the Amazon Bedrock provider will then pick up and could
-conflict with the ones you're intending to use.
-
----
-title: Groq
-description: Learn how to use Groq.
----
-
-# Groq Provider
-
-The [Groq](https://groq.com/) provider contains language model support for the Groq API.
-
-## Setup
-
-The Groq provider is available via the `@ai-sdk/groq` module.
-You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `groq` from `@ai-sdk/groq`:
-
-```ts
-import { groq } from '@ai-sdk/groq';
-```
-
-If you need a customized setup, you can import `createGroq` from `@ai-sdk/groq`
-and create a provider instance with your settings:
-
-```ts
-import { createGroq } from '@ai-sdk/groq';
-
-const groq = createGroq({
- // custom settings
-});
-```
-
-You can use the following optional settings to customize the Groq provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.groq.com/openai/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `GROQ_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create [Groq models](https://console.groq.com/docs/models) using a provider instance.
-The first argument is the model id, e.g. `gemma2-9b-it`.
-
-```ts
-const model = groq('gemma2-9b-it');
-```
-
-### Reasoning Models
-
-Groq offers several reasoning models such as `qwen-qwq-32b` and `deepseek-r1-distill-llama-70b`.
-You can configure how the reasoning is exposed in the generated text by using the `reasoningFormat` option.
-It supports the options `parsed`, `hidden`, and `raw`.
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: groq('qwen/qwen3-32b'),
- providerOptions: {
- groq: {
- reasoningFormat: 'parsed',
- reasoningEffort: 'default',
- parallelToolCalls: true, // Enable parallel function calling (default: true)
- user: 'user-123', // Unique identifier for end-user (optional)
- serviceTier: 'flex', // Use flex tier for higher throughput (optional)
- },
- },
- prompt: 'How many "r"s are in the word "strawberry"?',
-});
-```
-
-The following optional provider options are available for Groq language models:
-
-- **reasoningFormat** _'parsed' | 'raw' | 'hidden'_
-
- Controls how reasoning is exposed in the generated text. Only supported by reasoning models like `qwen-qwq-32b` and `deepseek-r1-distill-*` models.
-
- For a complete list of reasoning models and their capabilities, see [Groq's reasoning models documentation](https://console.groq.com/docs/reasoning).
-
-- **reasoningEffort** _'low' | 'meduim' | 'high' | 'none' | 'default'_
-
- Controls the level of effort the model will put into reasoning.
-
- - `qwen/qwen3-32b`
- - Supported values:
- - `none`: Disable reasoning. The model will not use any reasoning tokens.
- - `default`: Enable reasoning.
- - `gpt-oss20b/gpt-oss120b`
- - Supported values:
- - `low`: Use a low level of reasoning effort.
- - `medium`: Use a medium level of reasoning effort.
- - `high`: Use a high level of reasoning effort.
-
- Defaults to `default` for `qwen/qwen3-32b.`
-
-- **structuredOutputs** _boolean_
-
- Whether to use structured outputs.
-
- Defaults to `true`.
-
- When enabled, object generation will use the `json_schema` format instead of `json_object` format, providing more reliable structured outputs.
-
-- **parallelToolCalls** _boolean_
-
- Whether to enable parallel function calling during tool use. Defaults to `true`.
-
-- **user** _string_
-
- A unique identifier representing your end-user, which can help with monitoring and abuse detection.
-
-- **serviceTier** _'on_demand' | 'flex' | 'auto'_
-
- Service tier for the request. Defaults to `'on_demand'`.
-
- - `'on_demand'`: Default tier with consistent performance and fairness
- - `'flex'`: Higher throughput tier (10x rate limits) optimized for workloads that can handle occasional request failures
- - `'auto'`: Uses on_demand rate limits first, then falls back to flex tier if exceeded
-
- For more details about service tiers and their benefits, see [Groq's Flex Processing documentation](https://console.groq.com/docs/flex-processing).
-
-Only Groq reasoning models support the `reasoningFormat` option.
-
-#### Structured Outputs
-
-Structured outputs are enabled by default for Groq models.
-You can disable them by setting the `structuredOutputs` option to `false`.
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-
-const result = await generateObject({
- model: groq('moonshotai/kimi-k2-instruct-0905'),
- schema: z.object({
- recipe: z.object({
- name: z.string(),
- ingredients: z.array(z.string()),
- instructions: z.array(z.string()),
- }),
- }),
- prompt: 'Generate a simple pasta recipe.',
-});
-
-console.log(JSON.stringify(result.object, null, 2));
-```
-
-You can disable structured outputs for models that don't support them:
-
-```ts highlight="9"
-import { groq } from '@ai-sdk/groq';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-
-const result = await generateObject({
- model: groq('gemma2-9b-it'),
- providerOptions: {
- groq: {
- structuredOutputs: false,
- },
- },
- schema: z.object({
- recipe: z.object({
- name: z.string(),
- ingredients: z.array(z.string()),
- instructions: z.array(z.string()),
- }),
- }),
- prompt: 'Generate a simple pasta recipe in JSON format.',
-});
-
-console.log(JSON.stringify(result.object, null, 2));
-```
-
-
- Structured outputs are only supported by newer Groq models like
- `moonshotai/kimi-k2-instruct-0905`. For unsupported models, you can disable
- structured outputs by setting `structuredOutputs: false`. When disabled, Groq
- uses the `json_object` format which requires the word "JSON" to be included in
- your messages.
-
-
-### Example
-
-You can use Groq language models to generate text with the `generateText` function:
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: groq('gemma2-9b-it'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-### Image Input
-
-Groq's multi-modal models like `meta-llama/llama-4-scout-17b-16e-instruct` support image inputs. You can include images in your messages using either URLs or base64-encoded data:
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: groq('meta-llama/llama-4-scout-17b-16e-instruct'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'What do you see in this image?' },
- {
- type: 'image',
- image: 'https://example.com/image.jpg',
- },
- ],
- },
- ],
-});
-```
-
-You can also use base64-encoded images:
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateText } from 'ai';
-import { readFileSync } from 'fs';
-
-const imageData = readFileSync('path/to/image.jpg', 'base64');
-
-const { text } = await generateText({
- model: groq('meta-llama/llama-4-scout-17b-16e-instruct'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'Describe this image in detail.' },
- {
- type: 'image',
- image: `data:image/jpeg;base64,${imageData}`,
- },
- ],
- },
- ],
-});
-```
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ----------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `gemma2-9b-it` | | | | |
-| `llama-3.1-8b-instant` | | | | |
-| `llama-3.3-70b-versatile` | | | | |
-| `meta-llama/llama-guard-4-12b` | | | | |
-| `deepseek-r1-distill-llama-70b` | | | | |
-| `meta-llama/llama-4-maverick-17b-128e-instruct` | | | | |
-| `meta-llama/llama-4-scout-17b-16e-instruct` | | | | |
-| `meta-llama/llama-prompt-guard-2-22m` | | | | |
-| `meta-llama/llama-prompt-guard-2-86m` | | | | |
-| `moonshotai/kimi-k2-instruct-0905` | | | | |
-| `qwen/qwen3-32b` | | | | |
-| `llama-guard-3-8b` | | | | |
-| `llama3-70b-8192` | | | | |
-| `llama3-8b-8192` | | | | |
-| `mixtral-8x7b-32768` | | | | |
-| `qwen-qwq-32b` | | | | |
-| `qwen-2.5-32b` | | | | |
-| `deepseek-r1-distill-qwen-32b` | | | | |
-| `openai/gpt-oss-20b` | | | | |
-| `openai/gpt-oss-120b` | | | | |
-
-
- The tables above list the most commonly used models. Please see the [Groq
- docs](https://console.groq.com/docs/models) for a complete list of available
- models. You can also pass any available provider model ID as a string if
- needed.
-
-
-## Browser Search Tool
-
-Groq provides a browser search tool that offers interactive web browsing capabilities. Unlike traditional web search, browser search navigates websites interactively, providing more detailed and comprehensive results.
-
-### Supported Models
-
-Browser search is only available for these specific models:
-
-- `openai/gpt-oss-20b`
-- `openai/gpt-oss-120b`
-
-
- Browser search will only work with the supported models listed above. Using it
- with other models will generate a warning and the tool will be ignored.
-
-
-### Basic Usage
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: groq('openai/gpt-oss-120b'), // Must use supported model
- prompt:
- 'What are the latest developments in AI? Please search for recent news.',
- tools: {
- browser_search: groq.tools.browserSearch({}),
- },
- toolChoice: 'required', // Ensure the tool is used
-});
-
-console.log(result.text);
-```
-
-### Streaming Example
-
-```ts
-import { groq } from '@ai-sdk/groq';
-import { streamText } from 'ai';
-
-const result = streamText({
- model: groq('openai/gpt-oss-120b'),
- prompt: 'Search for the latest tech news and summarize it.',
- tools: {
- browser_search: groq.tools.browserSearch({}),
- },
- toolChoice: 'required',
-});
-
-for await (const delta of result.fullStream) {
- if (delta.type === 'text-delta') {
- process.stdout.write(delta.text);
- }
-}
-```
-
-### Key Features
-
-- **Interactive Browsing**: Navigates websites like a human user
-- **Comprehensive Results**: More detailed than traditional search snippets
-- **Server-side Execution**: Runs on Groq's infrastructure, no setup required
-- **Powered by Exa**: Uses Exa search engine for optimal results
-- **Currently Free**: Available at no additional charge during beta
-
-### Best Practices
-
-- Use `toolChoice: 'required'` to ensure the browser search is activated
-- Only supported on `openai/gpt-oss-20b` and `openai/gpt-oss-120b` models
-- The tool works automatically - no configuration parameters needed
-- Server-side execution means no additional API keys or setup required
-
-### Model Validation
-
-The provider automatically validates model compatibility:
-
-```ts
-// ✅ Supported - will work
-const result = await generateText({
- model: groq('openai/gpt-oss-120b'),
- tools: { browser_search: groq.tools.browserSearch({}) },
-});
-
-// ❌ Unsupported - will show warning and ignore tool
-const result = await generateText({
- model: groq('gemma2-9b-it'),
- tools: { browser_search: groq.tools.browserSearch({}) },
-});
-// Warning: "Browser search is only supported on models: openai/gpt-oss-20b, openai/gpt-oss-120b"
-```
-
-
- For more details about browser search capabilities and limitations, see the
- [Groq Browser Search
- Documentation](https://console.groq.com/docs/browser-search).
-
-
-## Transcription Models
-
-You can create models that call the [Groq transcription API](https://console.groq.com/docs/speech-to-text)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `whisper-large-v3`.
-
-```ts
-const model = groq.transcription('whisper-large-v3');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the input language in ISO-639-1 (e.g. `en`) format will improve accuracy and latency.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { groq } from '@ai-sdk/groq';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: groq.transcription('whisper-large-v3'),
- audio: await readFile('audio.mp3'),
- providerOptions: { groq: { language: 'en' } },
-});
-```
-
-The following provider options are available:
-
-- **timestampGranularities** _string[]_
- The granularity of the timestamps in the transcription.
- Defaults to `['segment']`.
- Possible values are `['word']`, `['segment']`, and `['word', 'segment']`.
- Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.
-
-- **language** _string_
- The language of the input audio. Supplying the input language in ISO-639-1 format (e.g. 'en') will improve accuracy and latency.
- Optional.
-
-- **prompt** _string_
- An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.
- Optional.
-
-- **temperature** _number_
- The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.
- Defaults to 0.
- Optional.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ---------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `whisper-large-v3` | | | | |
-| `whisper-large-v3-turbo` | | | | |
-| `distil-whisper-large-v3-en` | | | | |
-
----
-title: Fal
-description: Learn how to use Fal AI models with the AI SDK.
----
-
-# Fal Provider
-
-[Fal AI](https://fal.ai/) provides a generative media platform for developers with lightning-fast inference capabilities. Their platform offers optimized performance for running diffusion models, with speeds up to 4x faster than alternatives.
-
-## Setup
-
-The Fal provider is available via the `@ai-sdk/fal` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `fal` from `@ai-sdk/fal`:
-
-```ts
-import { fal } from '@ai-sdk/fal';
-```
-
-If you need a customized setup, you can import `createFal` and create a provider instance with your settings:
-
-```ts
-import { createFal } from '@ai-sdk/fal';
-
-const fal = createFal({
- apiKey: 'your-api-key', // optional, defaults to FAL_API_KEY environment variable, falling back to FAL_KEY
- baseURL: 'custom-url', // optional
- headers: {
- /* custom headers */
- }, // optional
-});
-```
-
-You can use the following optional settings to customize the Fal provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://fal.run`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `FAL_API_KEY` environment variable, falling back to `FAL_KEY`.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Image Models
-
-You can create Fal image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-### Basic Usage
-
-```ts
-import { fal } from '@ai-sdk/fal';
-import { experimental_generateImage as generateImage } from 'ai';
-import fs from 'fs';
-
-const { image, providerMetadata } = await generateImage({
- model: fal.image('fal-ai/flux/dev'),
- prompt: 'A serene mountain landscape at sunset',
-});
-
-const filename = `image-${Date.now()}.png`;
-fs.writeFileSync(filename, image.uint8Array);
-console.log(`Image saved to ${filename}`);
-```
-
-Fal image models may return additional information for the images and the request.
-
-Here are some examples of properties that may be set for each image
-
-```js
-providerMetadata.fal.images[0].nsfw; // boolean, image is not safe for work
-providerMetadata.fal.images[0].width; // number, image width
-providerMetadata.fal.images[0].height; // number, image height
-providerMetadata.fal.images[0].content_type; // string, mime type of the image
-```
-
-### Model Capabilities
-
-Fal offers many models optimized for different use cases. Here are a few popular examples. For a full list of models, see the [Fal AI Search Page](https://fal.ai/explore/search).
-
-| Model | Description |
-| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
-| `fal-ai/flux/dev` | FLUX.1 [dev] model for high-quality image generation |
-| `fal-ai/flux-pro/kontext` | FLUX.1 Kontext [pro] handles both text and reference images as inputs, enabling targeted edits and complex transformations |
-| `fal-ai/flux-pro/kontext/max` | FLUX.1 Kontext [max] with improved prompt adherence and typography generation |
-| `fal-ai/flux-lora` | Super fast endpoint for FLUX.1 with LoRA support |
-| `fal-ai/ideogram/character` | Generate consistent character appearances across multiple images. Maintain facial features, proportions, and distinctive traits |
-| `fal-ai/qwen-image` | Qwen-Image foundation model with significant advances in complex text rendering and precise image editing |
-| `fal-ai/omnigen-v2` | Unified image generation model for Image Editing, Personalized Image Generation, Virtual Try-On, Multi Person Generation and more |
-| `fal-ai/bytedance/dreamina/v3.1/text-to-image` | Dreamina showcases superior picture effects with improvements in aesthetics, precise and diverse styles, and rich details |
-| `fal-ai/recraft/v3/text-to-image` | SOTA in image generation with vector art and brand style capabilities |
-| `fal-ai/wan/v2.2-a14b/text-to-image` | High-resolution, photorealistic images with fine-grained detail |
-
-Fal models support the following aspect ratios:
-
-- 1:1 (square HD)
-- 16:9 (landscape)
-- 9:16 (portrait)
-- 4:3 (landscape)
-- 3:4 (portrait)
-- 16:10 (1280x800)
-- 10:16 (800x1280)
-- 21:9 (2560x1080)
-- 9:21 (1080x2560)
-
-Key features of Fal models include:
-
-- Up to 4x faster inference speeds compared to alternatives
-- Optimized by the Fal Inference Engine™
-- Support for real-time infrastructure
-- Cost-effective scaling with pay-per-use pricing
-- LoRA training capabilities for model personalization
-
-#### Modify Image
-
-Transform existing images using text prompts.
-
-```ts
-// Example: Modify existing image
-await generateImage({
- model: fal.image('fal-ai/flux-pro/kontext'),
- prompt: 'Put a donut next to the flour.',
- providerOptions: {
- fal: {
- imageUrl:
- 'https://v3.fal.media/files/rabbit/rmgBxhwGYb2d3pl3x9sKf_output.png',
- },
- },
-});
-```
-
-### Provider Options
-
-Fal image models support flexible provider options through the `providerOptions.fal` object. You can pass any parameters supported by the specific Fal model's API. Common options include:
-
-- **imageUrl** - Reference image URL for image-to-image generation
-- **strength** - Controls how much the output differs from the input image
-- **guidanceScale** - Controls adherence to the prompt (range: 1-20)
-- **numInferenceSteps** - Number of denoising steps (range: 1-50)
-- **enableSafetyChecker** - Enable/disable safety filtering
-- **outputFormat** - Output format: 'jpeg' or 'png'
-- **syncMode** - Wait for completion before returning response
-- **acceleration** - Speed of generation: 'none', 'regular', or 'high'
-- **safetyTolerance** - Content safety filtering level (1-6, where 1 is strictest)
-
-
- **Deprecation Notice**: snake_case parameter names (e.g., `image_url`,
- `guidance_scale`) are deprecated and will be removed in `@ai-sdk/fal` v2.0.
- Please use camelCase names (e.g., `imageUrl`, `guidanceScale`) instead.
-
-
-Refer to the [Fal AI model documentation](https://fal.ai/models) for model-specific parameters.
-
-### Advanced Features
-
-Fal's platform offers several advanced capabilities:
-
-- **Private Model Inference**: Run your own diffusion transformer models with up to 50% faster inference
-- **LoRA Training**: Train and personalize models in under 5 minutes
-- **Real-time Infrastructure**: Enable new user experiences with fast inference times
-- **Scalable Architecture**: Scale to thousands of GPUs when needed
-
-For more details about Fal's capabilities and features, visit the [Fal AI documentation](https://fal.ai/docs).
-
-## Transcription Models
-
-You can create models that call the [Fal transcription API](https://docs.fal.ai/guides/convert-speech-to-text)
-using the `.transcription()` factory method.
-
-The first argument is the model id without the `fal-ai/` prefix e.g. `wizper`.
-
-```ts
-const model = fal.transcription('wizper');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the `batchSize` option will increase the number of audio chunks processed in parallel.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { fal } from '@ai-sdk/fal';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: fal.transcription('wizper'),
- audio: await readFile('audio.mp3'),
- providerOptions: { fal: { batchSize: 10 } },
-});
-```
-
-The following provider options are available:
-
-- **language** _string_
- Language of the audio file. If set to null, the language will be automatically detected.
- Accepts ISO language codes like 'en', 'fr', 'zh', etc.
- Optional.
-
-- **diarize** _boolean_
- Whether to diarize the audio file (identify different speakers).
- Defaults to true.
- Optional.
-
-- **chunkLevel** _string_
- Level of the chunks to return. Either 'segment' or 'word'.
- Default value: "segment"
- Optional.
-
-- **version** _string_
- Version of the model to use. All models are Whisper large variants.
- Default value: "3"
- Optional.
-
-- **batchSize** _number_
- Batch size for processing.
- Default value: 64
- Optional.
-
-- **numSpeakers** _number_
- Number of speakers in the audio file. If not provided, the number of speakers will be automatically detected.
- Optional.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| --------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `whisper` | | | | |
-| `wizper` | | | | |
-
-## Speech Models
-
-You can create models that call Fal text-to-speech endpoints using the `.speech()` factory method.
-
-### Basic Usage
-
-```ts
-import { experimental_generateSpeech as generateSpeech } from 'ai';
-import { fal } from '@ai-sdk/fal';
-
-const result = await generateSpeech({
- model: fal.speech('fal-ai/minimax/speech-02-hd'),
- text: 'Hello from the AI SDK!',
-});
-```
-
-### Model Capabilities
-
-| Model | Description |
-| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `fal-ai/minimax/voice-clone` | Clone a voice from a sample audio and generate speech from text prompts |
-| `fal-ai/minimax/voice-design` | Design a personalized voice from a text description and generate speech from text prompts |
-| `fal-ai/dia-tts/voice-clone` | Clone dialog voices from a sample audio and generate dialogs from text prompts |
-| `fal-ai/minimax/speech-02-hd` | Generate speech from text prompts and different voices |
-| `fal-ai/minimax/speech-02-turbo` | Generate fast speech from text prompts and different voices |
-| `fal-ai/dia-tts` | Directly generates realistic dialogue from transcripts with audio conditioning for emotion control. Produces natural nonverbals like laughter and throat clearing |
-| `resemble-ai/chatterboxhd/text-to-speech` | Generate expressive, natural speech with Resemble AI's Chatterbox. Features unique emotion control, instant voice cloning from short audio, and built-in watermarking |
-
-### Provider Options
-
-Pass provider-specific options via `providerOptions.fal` depending on the model:
-
-- **voice_setting** _object_
-
- - `voice_id` (string): predefined voice ID
- - `speed` (number): 0.5–2.0
- - `vol` (number): 0–10
- - `pitch` (number): -12–12
- - `emotion` (enum): happy | sad | angry | fearful | disgusted | surprised | neutral
- - `english_normalization` (boolean)
-
-- **audio_setting** _object_
- Audio configuration settings specific to the model.
-
-- **language_boost** _enum_
- Chinese | Chinese,Yue | English | Arabic | Russian | Spanish | French | Portuguese | German | Turkish | Dutch | Ukrainian | Vietnamese | Indonesian | Japanese | Italian | Korean | Thai | Polish | Romanian | Greek | Czech | Finnish | Hindi | auto
-
-- **pronunciation_dict** _object_
- Custom pronunciation dictionary for specific words.
-
-Model-specific parameters (e.g., `audio_url`, `prompt`, `preview_text`, `ref_audio_url`, `ref_text`) can be passed directly under `providerOptions.fal` and will be forwarded to the Fal API.
-
----
-title: AssemblyAI
-description: Learn how to use the AssemblyAI provider for the AI SDK.
----
-
-# AssemblyAI Provider
-
-The [AssemblyAI](https://assemblyai.com/) provider contains language model support for the AssemblyAI transcription API.
-
-## Setup
-
-The AssemblyAI provider is available in the `@ai-sdk/assemblyai` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `assemblyai` from `@ai-sdk/assemblyai`:
-
-```ts
-import { assemblyai } from '@ai-sdk/assemblyai';
-```
-
-If you need a customized setup, you can import `createAssemblyAI` from `@ai-sdk/assemblyai` and create a provider instance with your settings:
-
-```ts
-import { createAssemblyAI } from '@ai-sdk/assemblyai';
-
-const assemblyai = createAssemblyAI({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the AssemblyAI provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `ASSEMBLYAI_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Transcription Models
-
-You can create models that call the [AssemblyAI transcription API](https://www.assemblyai.com/docs/getting-started/transcribe-an-audio-file/typescript)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `best`.
-
-```ts
-const model = assemblyai.transcription('best');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the `contentSafety` option will enable content safety filtering.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { assemblyai } from '@ai-sdk/assemblyai';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: assemblyai.transcription('best'),
- audio: await readFile('audio.mp3'),
- providerOptions: { assemblyai: { contentSafety: true } },
-});
-```
-
-The following provider options are available:
-
-- **audioEndAt** _number_
-
- End time of the audio in milliseconds.
- Optional.
-
-- **audioStartFrom** _number_
-
- Start time of the audio in milliseconds.
- Optional.
-
-- **autoChapters** _boolean_
-
- Whether to automatically generate chapters for the transcription.
- Optional.
-
-- **autoHighlights** _boolean_
-
- Whether to automatically generate highlights for the transcription.
- Optional.
-
-- **boostParam** _enum_
-
- Boost parameter for the transcription.
- Allowed values: `'low'`, `'default'`, `'high'`.
- Optional.
-
-- **contentSafety** _boolean_
-
- Whether to enable content safety filtering.
- Optional.
-
-- **contentSafetyConfidence** _number_
-
- Confidence threshold for content safety filtering (25-100).
- Optional.
-
-- **customSpelling** _array of objects_
-
- Custom spelling rules for the transcription.
- Each object has `from` (array of strings) and `to` (string) properties.
- Optional.
-
-- **disfluencies** _boolean_
-
- Whether to include disfluencies (um, uh, etc.) in the transcription.
- Optional.
-
-- **entityDetection** _boolean_
-
- Whether to detect entities in the transcription.
- Optional.
-
-- **filterProfanity** _boolean_
-
- Whether to filter profanity in the transcription.
- Optional.
-
-- **formatText** _boolean_
-
- Whether to format the text in the transcription.
- Optional.
-
-- **iabCategories** _boolean_
-
- Whether to include IAB categories in the transcription.
- Optional.
-
-- **languageCode** _string_
-
- Language code for the audio.
- Supports numerous ISO-639-1 and ISO-639-3 language codes.
- Optional.
-
-- **languageConfidenceThreshold** _number_
-
- Confidence threshold for language detection.
- Optional.
-
-- **languageDetection** _boolean_
-
- Whether to enable language detection.
- Optional.
-
-- **multichannel** _boolean_
-
- Whether to process multiple audio channels separately.
- Optional.
-
-- **punctuate** _boolean_
-
- Whether to add punctuation to the transcription.
- Optional.
-
-- **redactPii** _boolean_
-
- Whether to redact personally identifiable information.
- Optional.
-
-- **redactPiiAudio** _boolean_
-
- Whether to redact PII in the audio file.
- Optional.
-
-- **redactPiiAudioQuality** _enum_
-
- Quality of the redacted audio file.
- Allowed values: `'mp3'`, `'wav'`.
- Optional.
-
-- **redactPiiPolicies** _array of enums_
-
- Policies for PII redaction, specifying which types of information to redact.
- Supports numerous types like `'person_name'`, `'phone_number'`, etc.
- Optional.
-
-- **redactPiiSub** _enum_
-
- Substitution method for redacted PII.
- Allowed values: `'entity_name'`, `'hash'`.
- Optional.
-
-- **sentimentAnalysis** _boolean_
-
- Whether to perform sentiment analysis on the transcription.
- Optional.
-
-- **speakerLabels** _boolean_
-
- Whether to label different speakers in the transcription.
- Optional.
-
-- **speakersExpected** _number_
-
- Expected number of speakers in the audio.
- Optional.
-
-- **speechThreshold** _number_
-
- Threshold for speech detection (0-1).
- Optional.
-
-- **summarization** _boolean_
-
- Whether to generate a summary of the transcription.
- Optional.
-
-- **summaryModel** _enum_
-
- Model to use for summarization.
- Allowed values: `'informative'`, `'conversational'`, `'catchy'`.
- Optional.
-
-- **summaryType** _enum_
-
- Type of summary to generate.
- Allowed values: `'bullets'`, `'bullets_verbose'`, `'gist'`, `'headline'`, `'paragraph'`.
- Optional.
-
-- **topics** _array of strings_
-
- List of topics to detect in the transcription.
- Optional.
-
-- **webhookAuthHeaderName** _string_
-
- Name of the authentication header for webhook requests.
- Optional.
-
-- **webhookAuthHeaderValue** _string_
-
- Value of the authentication header for webhook requests.
- Optional.
-
-- **webhookUrl** _string_
-
- URL to send webhook notifications to.
- Optional.
-
-- **wordBoost** _array of strings_
-
- List of words to boost in the transcription.
- Optional.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ------ | ------------------- | ------------------- | ------------------- | ------------------- |
-| `best` | | | | |
-| `nano` | | | | |
-
----
-title: DeepInfra
-description: Learn how to use DeepInfra's models with the AI SDK.
----
-
-# DeepInfra Provider
-
-The [DeepInfra](https://deepinfra.com) provider contains support for state-of-the-art models through the DeepInfra API, including Llama 3, Mixtral, Qwen, and many other popular open-source models.
-
-## Setup
-
-The DeepInfra provider is available via the `@ai-sdk/deepinfra` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `deepinfra` from `@ai-sdk/deepinfra`:
-
-```ts
-import { deepinfra } from '@ai-sdk/deepinfra';
-```
-
-If you need a customized setup, you can import `createDeepInfra` from `@ai-sdk/deepinfra` and create a provider instance with your settings:
-
-```ts
-import { createDeepInfra } from '@ai-sdk/deepinfra';
-
-const deepinfra = createDeepInfra({
- apiKey: process.env.DEEPINFRA_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the DeepInfra provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.deepinfra.com/v1`.
-
- Note: Language models and embeddings use OpenAI-compatible endpoints at `{baseURL}/openai`,
- while image models use `{baseURL}/inference`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `DEEPINFRA_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create language models using a provider instance. The first argument is the model ID, for example:
-
-```ts
-import { deepinfra } from '@ai-sdk/deepinfra';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: deepinfra('meta-llama/Meta-Llama-3.1-70B-Instruct'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-DeepInfra language models can also be used in the `streamText` function (see [AI SDK Core](/docs/ai-sdk-core)).
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| --------------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8` | | | | |
-| `meta-llama/Llama-4-Scout-17B-16E-Instruct` | | | | |
-| `meta-llama/Llama-3.3-70B-Instruct-Turbo` | | | | |
-| `meta-llama/Llama-3.3-70B-Instruct` | | | | |
-| `meta-llama/Meta-Llama-3.1-405B-Instruct` | | | | |
-| `meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo` | | | | |
-| `meta-llama/Meta-Llama-3.1-70B-Instruct` | | | | |
-| `meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo` | | | | |
-| `meta-llama/Meta-Llama-3.1-8B-Instruct` | | | | |
-| `meta-llama/Llama-3.2-11B-Vision-Instruct` | | | | |
-| `meta-llama/Llama-3.2-90B-Vision-Instruct` | | | | |
-| `mistralai/Mixtral-8x7B-Instruct-v0.1` | | | | |
-| `deepseek-ai/DeepSeek-V3` | | | | |
-| `deepseek-ai/DeepSeek-R1` | | | | |
-| `deepseek-ai/DeepSeek-R1-Distill-Llama-70B` | | | | |
-| `deepseek-ai/DeepSeek-R1-Turbo` | | | | |
-| `nvidia/Llama-3.1-Nemotron-70B-Instruct` | | | | |
-| `Qwen/Qwen2-7B-Instruct` | | | | |
-| `Qwen/Qwen2.5-72B-Instruct` | | | | |
-| `Qwen/Qwen2.5-Coder-32B-Instruct` | | | | |
-| `Qwen/QwQ-32B-Preview` | | | | |
-| `google/codegemma-7b-it` | | | | |
-| `google/gemma-2-9b-it` | | | | |
-| `microsoft/WizardLM-2-8x22B` | | | | |
-
-
- The table above lists popular models. Please see the [DeepInfra
- docs](https://deepinfra.com) for a full list of available models. You can also
- pass any available provider model ID as a string if needed.
-
-
-## Image Models
-
-You can create DeepInfra image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { deepinfra } from '@ai-sdk/deepinfra';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: deepinfra.image('stabilityai/sd3.5'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
-});
-```
-
-
- Model support for `size` and `aspectRatio` parameters varies by model. Please
- check the individual model documentation on [DeepInfra's models
- page](https://deepinfra.com/models/text-to-image) for supported options and
- additional parameters.
-
-
-### Model-specific options
-
-You can pass model-specific parameters using the `providerOptions.deepinfra` field:
-
-```ts
-import { deepinfra } from '@ai-sdk/deepinfra';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: deepinfra.image('stabilityai/sd3.5'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
- providerOptions: {
- deepinfra: {
- num_inference_steps: 30, // Control the number of denoising steps (1-50)
- },
- },
-});
-```
-
-### Model Capabilities
-
-For models supporting aspect ratios, the following ratios are typically supported:
-`1:1 (default), 16:9, 1:9, 3:2, 2:3, 4:5, 5:4, 9:16, 9:21`
-
-For models supporting size parameters, dimensions must typically be:
-
-- Multiples of 32
-- Width and height between 256 and 1440 pixels
-- Default size is 1024x1024
-
-| Model | Dimensions Specification | Notes |
-| ---------------------------------- | ------------------------ | -------------------------------------------------------- |
-| `stabilityai/sd3.5` | Aspect Ratio | Premium quality base model, 8B parameters |
-| `black-forest-labs/FLUX-1.1-pro` | Size | Latest state-of-art model with superior prompt following |
-| `black-forest-labs/FLUX-1-schnell` | Size | Fast generation in 1-4 steps |
-| `black-forest-labs/FLUX-1-dev` | Size | Optimized for anatomical accuracy |
-| `black-forest-labs/FLUX-pro` | Size | Flagship Flux model |
-| `stabilityai/sd3.5-medium` | Aspect Ratio | Balanced 2.5B parameter model |
-| `stabilityai/sdxl-turbo` | Aspect Ratio | Optimized for fast generation |
-
-For more details and pricing information, see the [DeepInfra text-to-image models page](https://deepinfra.com/models/text-to-image).
-
-## Embedding Models
-
-You can create DeepInfra embedding models using the `.textEmbedding()` factory method.
-For more on embedding models with the AI SDK see [embed()](/docs/reference/ai-sdk-core/embed).
-
-```ts
-import { deepinfra } from '@ai-sdk/deepinfra';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: deepinfra.textEmbedding('BAAI/bge-large-en-v1.5'),
- value: 'sunny day at the beach',
-});
-```
-
-### Model Capabilities
-
-| Model | Dimensions | Max Tokens |
-| ----------------------------------------------------- | ---------- | ---------- |
-| `BAAI/bge-base-en-v1.5` | 768 | 512 |
-| `BAAI/bge-large-en-v1.5` | 1024 | 512 |
-| `BAAI/bge-m3` | 1024 | 8192 |
-| `intfloat/e5-base-v2` | 768 | 512 |
-| `intfloat/e5-large-v2` | 1024 | 512 |
-| `intfloat/multilingual-e5-large` | 1024 | 512 |
-| `sentence-transformers/all-MiniLM-L12-v2` | 384 | 256 |
-| `sentence-transformers/all-MiniLM-L6-v2` | 384 | 256 |
-| `sentence-transformers/all-mpnet-base-v2` | 768 | 384 |
-| `sentence-transformers/clip-ViT-B-32` | 512 | 77 |
-| `sentence-transformers/clip-ViT-B-32-multilingual-v1` | 512 | 77 |
-| `sentence-transformers/multi-qa-mpnet-base-dot-v1` | 768 | 512 |
-| `sentence-transformers/paraphrase-MiniLM-L6-v2` | 384 | 128 |
-| `shibing624/text2vec-base-chinese` | 768 | 512 |
-| `thenlper/gte-base` | 768 | 512 |
-| `thenlper/gte-large` | 1024 | 512 |
-
-
- For a complete list of available embedding models, see the [DeepInfra
- embeddings page](https://deepinfra.com/models/embeddings).
-
-
----
-title: Deepgram
-description: Learn how to use the Deepgram provider for the AI SDK.
----
-
-# Deepgram Provider
-
-The [Deepgram](https://deepgram.com/) provider contains language model support for the Deepgram transcription API.
-
-## Setup
-
-The Deepgram provider is available in the `@ai-sdk/deepgram` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `deepgram` from `@ai-sdk/deepgram`:
-
-```ts
-import { deepgram } from '@ai-sdk/deepgram';
-```
-
-If you need a customized setup, you can import `createDeepgram` from `@ai-sdk/deepgram` and create a provider instance with your settings:
-
-```ts
-import { createDeepgram } from '@ai-sdk/deepgram';
-
-const deepgram = createDeepgram({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the Deepgram provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `DEEPGRAM_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Transcription Models
-
-You can create models that call the [Deepgram transcription API](https://developers.deepgram.com/docs/pre-recorded-audio)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `nova-3`.
-
-```ts
-const model = deepgram.transcription('nova-3');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the `summarize` option will enable summaries for sections of content.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { deepgram } from '@ai-sdk/deepgram';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: deepgram.transcription('nova-3'),
- audio: await readFile('audio.mp3'),
- providerOptions: { deepgram: { summarize: true } },
-});
-```
-
-The following provider options are available:
-
-- **language** _string_
-
- Language code for the audio.
- Supports numerous ISO-639-1 and ISO-639-3 language codes.
- Optional.
-
-- **smartFormat** _boolean_
-
- Whether to apply smart formatting to the transcription.
- Optional.
-
-- **punctuate** _boolean_
-
- Whether to add punctuation to the transcription.
- Optional.
-
-- **paragraphs** _boolean_
-
- Whether to format the transcription into paragraphs.
- Optional.
-
-- **summarize** _enum | boolean_
-
- Whether to generate a summary of the transcription.
- Allowed values: `'v2'`, `false`.
- Optional.
-
-- **topics** _boolean_
-
- Whether to detect topics in the transcription.
- Optional.
-
-- **intents** _boolean_
-
- Whether to detect intents in the transcription.
- Optional.
-
-- **sentiment** _boolean_
-
- Whether to perform sentiment analysis on the transcription.
- Optional.
-
-- **detectEntities** _boolean_
-
- Whether to detect entities in the transcription.
- Optional.
-
-- **redact** _string | array of strings_
-
- Specifies what content to redact from the transcription.
- Optional.
-
-- **replace** _string_
-
- Replacement string for redacted content.
- Optional.
-
-- **search** _string_
-
- Search term to find in the transcription.
- Optional.
-
-- **keyterm** _string_
-
- Key terms to identify in the transcription.
- Optional.
-
-- **diarize** _boolean_
-
- Whether to identify different speakers in the transcription.
- Defaults to `true`.
- Optional.
-
-- **utterances** _boolean_
-
- Whether to segment the transcription into utterances.
- Optional.
-
-- **uttSplit** _number_
-
- Threshold for splitting utterances.
- Optional.
-
-- **fillerWords** _boolean_
-
- Whether to include filler words (um, uh, etc.) in the transcription.
- Optional.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| -------------------------------------------------------------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `nova-3` (+ [variants](https://developers.deepgram.com/docs/models-languages-overview#nova-3)) | | | | |
-| `nova-2` (+ [variants](https://developers.deepgram.com/docs/models-languages-overview#nova-2)) | | | | |
-| `nova` (+ [variants](https://developers.deepgram.com/docs/models-languages-overview#nova)) | | | | |
-| `enhanced` (+ [variants](https://developers.deepgram.com/docs/models-languages-overview#enhanced)) | | | | |
-| `base` (+ [variants](https://developers.deepgram.com/docs/models-languages-overview#base)) | | | | |
-
----
-title: Black Forest Labs
-description: Learn how to use Black Forest Labs models with the AI SDK.
----
-
-# Black Forest Labs Provider
-
-[Black Forest Labs](https://bfl.ai/) provides a generative image platform for developers with FLUX-based models. Their platform offers fast, high quality, and in-context image generation and editing with precise and coherent results.
-
-## Setup
-
-The Black Forest Labs provider is available via the `@ai-sdk/black-forest-labs` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `blackForestLabs` from `@ai-sdk/black-forest-labs`:
-
-```ts
-import { blackForestLabs } from '@ai-sdk/black-forest-labs';
-```
-
-If you need a customized setup, you can import `createBlackForestLabs` and create a provider instance with your settings:
-
-```ts
-import { createBlackForestLabs } from '@ai-sdk/black-forest-labs';
-
-const blackForestLabs = createBlackForestLabs({
- apiKey: 'your-api-key', // optional, defaults to BFL_API_KEY environment variable
- baseURL: 'custom-url', // optional
- headers: {
- /* custom headers */
- }, // optional
-});
-```
-
-You can use the following optional settings to customize the Black Forest Labs provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use a regional endpoint.
- The default prefix is `https://api.bfl.ai/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `x-key` header.
- It defaults to the `BFL_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Image Models
-
-You can create Black Forest Labs image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-### Basic Usage
-
-```ts
-import { writeFileSync } from 'node:fs';
-import { blackForestLabs } from '@ai-sdk/black-forest-labs';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image, providerMetadata } = await generateImage({
- model: blackForestLabs.image('flux-pro-1.1'),
- prompt: 'A serene mountain landscape at sunset',
-});
-
-const filename = `image-${Date.now()}.png`;
-writeFileSync(filename, image.uint8Array);
-console.log(`Image saved to ${filename}`);
-```
-
-### Model Capabilities
-
-Black Forest Labs offers many models optimized for different use cases. Here are a few popular examples. For a full list of models, see the [Black Forest Labs Models Page](https://bfl.ai/models).
-
-| Model | Description |
-| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
-| `flux-kontext-pro` | FLUX.1 Kontext [pro] handles both text and reference images as inputs, enabling targeted edits and complex transformations |
-| `flux-kontext-max` | FLUX.1 Kontext [max] with improved prompt adherence and typography generation |
-| `flux-pro-1.1-ultra` | Ultra-fast, ultra high-resolution image creation |
-| `flux-pro-1.1` | Fast, high-quality image generation from text. |
-
-Black Forest Labs models support aspect ratios from 3:7 (portrait) to 7:3 (landscape).
-
-#### Modify Image
-
-Transform existing images using text prompts.
-
-```ts
-import {
- blackForestLabs,
- BlackForestLabsImageProviderOptions,
-} from '@ai-sdk/black-forest-labs';
-import { experimental_generateImage as generateImage } from 'ai';
-
-// Example: Modify existing image
-await generateImage({
- model: blackForestLabs.image('flux-kontext-pro'),
- prompt: 'Put a donut next to the flour.',
- providerOptions: {
- blackForestLabs: {
- inputImage: '',
- } satisfies BlackForestLabsImageProviderOptions,
- },
-});
-```
-
-### Provider Options
-
-Black Forest Labs image models support flexible provider options through the `providerOptions.blackForestLabs` object. You can pass any parameters supported by the specific endpoint's API. The supported parameters depend on the used model ID:
-
-- **imagePrompt** - Base64-encoded image to use as additional visual context for generation
-- **imagePromptStrength** - Strength of the image prompt influence on generation (0.0 to 1.0)
-- **inputImage** - Base64 encoded image or URL of image to use as reference. Supports up to 20MB or 20 megapixels.
-- **outputFormat** - Desired format of the output image. Can be “jpeg” or “png”.
-- **promptUpsampling** - If true, performs upsampling on the prompt
-- **raw** - Enable raw mode for more natural, authentic aesthetics
-- **safetyTolerance** - Moderation level for inputs and outputs. Value ranges from 0 (most strict) to 6 (more permissive).
-- **webhookSecret** - Secret for webhook signature verification, sent in the `X-Webhook-Secret` header.
-- **webhookUrl** - URL for asynchronous completion notification. Must be a valid HTTP/HTTPS URL.
-- **pollIntervalMillis** - Interval in milliseconds between polling attempts (default 500ms)
-- **pollTimeoutMillis** - Overall timeout in milliseconds for polling before timing out (default 60s)
-- **width** - Output width in pixels for models that support explicit dimensions. Range 256–1920, default 1024. When set, this overrides any width derived from `size`.
-- **height** - Output height in pixels for models that support explicit dimensions. Range 256–1920, default 768. When set, this overrides any height derived from `size`.
-- **steps** - Number of inference steps. Higher values may improve quality but increase generation time
-- **guidance** - Guidance scale for generation. Higher values follow the prompt more closely
-- **inputImage2 … inputImage10** - Additional reference images (base64 string or URL) for models that support multiple inputs, used alongside `inputImage`.
-
-### Regional Endpoints
-
-By default, requests are sent to `https://api.bfl.ai/v1`. You can select a [regional endpoint](https://docs.bfl.ai/api_integration/integration_guidelines#regional-endpoints) by setting `baseURL` when creating the provider instance:
-
-```ts
-import { createBlackForestLabs } from '@ai-sdk/black-forest-labs';
-
-const blackForestLabs = createBlackForestLabs({
- baseURL: 'https://api.eu.bfl.ai/v1', // or https://api.us.bfl.ai/v1
-});
-```
-
----
-title: Gladia
-description: Learn how to use the Gladia provider for the AI SDK.
----
-
-# Gladia Provider
-
-The [Gladia](https://gladia.io/) provider contains language model support for the Gladia transcription API.
-
-## Setup
-
-The Gladia provider is available in the `@ai-sdk/gladia` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `gladia` from `@ai-sdk/gladia`:
-
-```ts
-import { gladia } from '@ai-sdk/gladia';
-```
-
-If you need a customized setup, you can import `createGladia` from `@ai-sdk/gladia` and create a provider instance with your settings:
-
-```ts
-import { createGladia } from '@ai-sdk/gladia';
-
-const gladia = createGladia({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the Gladia provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `GLADIA_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Transcription Models
-
-You can create models that call the [Gladia transcription API](https://docs.gladia.io/chapters/pre-recorded-stt/getting-started)
-using the `.transcription()` factory method.
-
-```ts
-const model = gladia.transcription();
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the `summarize` option will enable summaries for sections of content.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { gladia } from '@ai-sdk/gladia';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: gladia.transcription(),
- audio: await readFile('audio.mp3'),
- providerOptions: { gladia: { summarize: true } },
-});
-```
-
-
- Gladia does not have various models, so you can omit the standard `model` id
- parameter.
-
-
-The following provider options are available:
-
-- **contextPrompt** _string_
-
- Context to feed the transcription model with for possible better accuracy.
- Optional.
-
-- **customVocabulary** _boolean | any[]_
-
- Custom vocabulary to improve transcription accuracy.
- Optional.
-
-- **customVocabularyConfig** _object_
-
- Configuration for custom vocabulary.
- Optional.
-
- - **vocabulary** _Array<string | \{ value: string, intensity?: number, pronunciations?: string[], language?: string \}>_
- - **defaultIntensity** _number_
-
-- **detectLanguage** _boolean_
-
- Whether to automatically detect the language.
- Optional.
-
-- **enableCodeSwitching** _boolean_
-
- Enable code switching for multilingual audio.
- Optional.
-
-- **codeSwitchingConfig** _object_
-
- Configuration for code switching.
- Optional.
-
- - **languages** _string[]_
-
-- **language** _string_
-
- Specify the language of the audio.
- Optional.
-
-- **callback** _boolean_
-
- Enable callback when transcription is complete.
- Optional.
-
-- **callbackConfig** _object_
-
- Configuration for callback.
- Optional.
-
- - **url** _string_
- - **method** _'POST' | 'PUT'_
-
-- **subtitles** _boolean_
-
- Generate subtitles from the transcription.
- Optional.
-
-- **subtitlesConfig** _object_
-
- Configuration for subtitles.
- Optional.
-
- - **formats** _Array<'srt' | 'vtt'>_
- - **minimumDuration** _number_
- - **maximumDuration** _number_
- - **maximumCharactersPerRow** _number_
- - **maximumRowsPerCaption** _number_
- - **style** _'default' | 'compliance'_
-
-- **diarization** _boolean_
-
- Enable speaker diarization.
- Defaults to `true`.
- Optional.
-
-- **diarizationConfig** _object_
-
- Configuration for diarization.
- Optional.
-
- - **numberOfSpeakers** _number_
- - **minSpeakers** _number_
- - **maxSpeakers** _number_
- - **enhanced** _boolean_
-
-- **translation** _boolean_
-
- Enable translation of the transcription.
- Optional.
-
-- **translationConfig** _object_
-
- Configuration for translation.
- Optional.
-
- - **targetLanguages** _string[]_
- - **model** _'base' | 'enhanced'_
- - **matchOriginalUtterances** _boolean_
-
-- **summarization** _boolean_
-
- Enable summarization of the transcription.
- Optional.
-
-- **summarizationConfig** _object_
-
- Configuration for summarization.
- Optional.
-
- - **type** _'general' | 'bullet_points' | 'concise'_
-
-- **moderation** _boolean_
-
- Enable content moderation.
- Optional.
-
-- **namedEntityRecognition** _boolean_
-
- Enable named entity recognition.
- Optional.
-
-- **chapterization** _boolean_
-
- Enable chapterization of the transcription.
- Optional.
-
-- **nameConsistency** _boolean_
-
- Enable name consistency in the transcription.
- Optional.
-
-- **customSpelling** _boolean_
-
- Enable custom spelling.
- Optional.
-
-- **customSpellingConfig** _object_
-
- Configuration for custom spelling.
- Optional.
-
- - **spellingDictionary** _Record<string, string[]>_
-
-- **structuredDataExtraction** _boolean_
-
- Enable structured data extraction.
- Optional.
-
-- **structuredDataExtractionConfig** _object_
-
- Configuration for structured data extraction.
- Optional.
-
- - **classes** _string[]_
-
-- **sentimentAnalysis** _boolean_
-
- Enable sentiment analysis.
- Optional.
-
-- **audioToLlm** _boolean_
-
- Enable audio to LLM processing.
- Optional.
-
-- **audioToLlmConfig** _object_
-
- Configuration for audio to LLM.
- Optional.
-
- - **prompts** _string[]_
-
-- **customMetadata** _Record<string, any>_
-
- Custom metadata to include with the request.
- Optional.
-
-- **sentences** _boolean_
-
- Enable sentence detection.
- Optional.
-
-- **displayMode** _boolean_
-
- Enable display mode.
- Optional.
-
-- **punctuationEnhanced** _boolean_
-
- Enable enhanced punctuation.
- Optional.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| --------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `Default` | | | | |
-
----
-title: LMNT
-description: Learn how to use the LMNT provider for the AI SDK.
----
-
-# LMNT Provider
-
-The [LMNT](https://lmnt.com/) provider contains language model support for the LMNT transcription API.
-
-## Setup
-
-The LMNT provider is available in the `@ai-sdk/lmnt` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `lmnt` from `@ai-sdk/lmnt`:
-
-```ts
-import { lmnt } from '@ai-sdk/lmnt';
-```
-
-If you need a customized setup, you can import `createLMNT` from `@ai-sdk/lmnt` and create a provider instance with your settings:
-
-```ts
-import { createLMNT } from '@ai-sdk/lmnt';
-
-const lmnt = createLMNT({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the LMNT provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `LMNT_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Speech Models
-
-You can create models that call the [LMNT speech API](https://docs.lmnt.com/api-reference/speech/synthesize-speech-bytes)
-using the `.speech()` factory method.
-
-The first argument is the model id e.g. `aurora`.
-
-```ts
-const model = lmnt.speech('aurora');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying a voice to use for the generated audio.
-
-```ts highlight="6"
-import { experimental_generateSpeech as generateSpeech } from 'ai';
-import { lmnt } from '@ai-sdk/lmnt';
-
-const result = await generateSpeech({
- model: lmnt.speech('aurora'),
- text: 'Hello, world!',
- language: 'en', // Standardized language parameter
-});
-```
-
-### Provider Options
-
-The LMNT provider accepts the following options:
-
-- **model** _'aurora' | 'blizzard'_
-
- The LMNT model to use. Defaults to `'aurora'`.
-
-- **language** _'auto' | 'en' | 'es' | 'pt' | 'fr' | 'de' | 'zh' | 'ko' | 'hi' | 'ja' | 'ru' | 'it' | 'tr'_
-
- The language to use for speech synthesis. Defaults to `'auto'`.
-
-- **format** _'aac' | 'mp3' | 'mulaw' | 'raw' | 'wav'_
-
- The audio format to return. Defaults to `'mp3'`.
-
-- **sampleRate** _number_
-
- The sample rate of the audio in Hz. Defaults to `24000`.
-
-- **speed** _number_
-
- The speed of the speech. Must be between 0.25 and 2. Defaults to `1`.
-
-- **seed** _number_
-
- An optional seed for deterministic generation.
-
-- **conversational** _boolean_
-
- Whether to use a conversational style. Defaults to `false`.
-
-- **length** _number_
-
- Maximum length of the audio in seconds. Maximum value is 300.
-
-- **topP** _number_
-
- Top-p sampling parameter. Must be between 0 and 1. Defaults to `1`.
-
-- **temperature** _number_
-
- Temperature parameter for sampling. Must be at least 0. Defaults to `1`.
-
-### Model Capabilities
-
-| Model | Instructions |
-| ---------- | ------------------- |
-| `aurora` | |
-| `blizzard` | |
-
----
-title: Google Generative AI
-description: Learn how to use Google Generative AI Provider.
----
-
-# Google Generative AI Provider
-
-The [Google Generative AI](https://ai.google.dev) provider contains language and embedding model support for
-the [Google Generative AI](https://ai.google.dev/api/rest) APIs.
-
-## Setup
-
-The Google provider is available in the `@ai-sdk/google` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `google` from `@ai-sdk/google`:
-
-```ts
-import { google } from '@ai-sdk/google';
-```
-
-If you need a customized setup, you can import `createGoogleGenerativeAI` from `@ai-sdk/google` and create a provider instance with your settings:
-
-```ts
-import { createGoogleGenerativeAI } from '@ai-sdk/google';
-
-const google = createGoogleGenerativeAI({
- // custom settings
-});
-```
-
-You can use the following optional settings to customize the Google Generative AI provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://generativelanguage.googleapis.com/v1beta`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `x-goog-api-key` header.
- It defaults to the `GOOGLE_GENERATIVE_AI_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create models that call the [Google Generative AI API](https://ai.google.dev/api/rest) using the provider instance.
-The first argument is the model id, e.g. `gemini-2.5-flash`.
-The models support tool calls and some have multi-modal capabilities.
-
-```ts
-const model = google('gemini-2.5-flash');
-```
-
-You can use Google Generative AI language models to generate text with the `generateText` function:
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: google('gemini-2.5-flash'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Google Generative AI language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-Google Generative AI also supports some model specific settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them as an options argument:
-
-```ts
-const model = google('gemini-2.5-flash');
-
-await generateText({
- model,
- providerOptions: {
- google: {
- safetySettings: [
- {
- category: 'HARM_CATEGORY_UNSPECIFIED',
- threshold: 'BLOCK_LOW_AND_ABOVE',
- },
- ],
- },
- },
-});
-```
-
-The following optional provider options are available for Google Generative AI models:
-
-- **cachedContent** _string_
-
- Optional. The name of the cached content used as context to serve the prediction.
- Format: cachedContents/\{cachedContent\}
-
-- **structuredOutputs** _boolean_
-
- Optional. Enable structured output. Default is true.
-
- This is useful when the JSON Schema contains elements that are
- not supported by the OpenAPI schema version that
- Google Generative AI uses. You can use this to disable
- structured outputs if you need to.
-
- See [Troubleshooting: Schema Limitations](#schema-limitations) for more details.
-
-- **safetySettings** _Array\<\{ category: string; threshold: string \}\>_
-
- Optional. Safety settings for the model.
-
- - **category** _string_
-
- The category of the safety setting. Can be one of the following:
-
- - `HARM_CATEGORY_HATE_SPEECH`
- - `HARM_CATEGORY_DANGEROUS_CONTENT`
- - `HARM_CATEGORY_HARASSMENT`
- - `HARM_CATEGORY_SEXUALLY_EXPLICIT`
-
- - **threshold** _string_
-
- The threshold of the safety setting. Can be one of the following:
-
- - `HARM_BLOCK_THRESHOLD_UNSPECIFIED`
- - `BLOCK_LOW_AND_ABOVE`
- - `BLOCK_MEDIUM_AND_ABOVE`
- - `BLOCK_ONLY_HIGH`
- - `BLOCK_NONE`
-
-- **responseModalities** _string[]_
- The modalities to use for the response. The following modalities are supported: `TEXT`, `IMAGE`. When not defined or empty, the model defaults to returning only text.
-
-- **thinkingConfig** _\{ thinkingLevel?: 'low' | 'high'; thinkingBudget?: number; includeThoughts?: boolean \}_
-
- Optional. Configuration for the model's thinking process. Only supported by specific [Google Generative AI models](https://ai.google.dev/gemini-api/docs/thinking).
-
- - **thinkingLevel** _'low' | 'high'_
-
- Optional. Controls the thinking depth for Gemini 3 models. Use 'low' for faster responses or 'high' for deeper reasoning. Only supported by Gemini 3 models (`gemini-3-pro-preview` and later).
-
- - **thinkingBudget** _number_
-
- Optional. Gives the model guidance on the number of thinking tokens it can use when generating a response. Setting it to 0 disables thinking, if the model supports it.
- For more information about the possible value ranges for each model see [Google Generative AI thinking documentation](https://ai.google.dev/gemini-api/docs/thinking#set-budget).
-
-
- This option is for Gemini 2.5 models. Gemini 3 models should use
- `thinkingLevel` instead.
-
-
- - **includeThoughts** _boolean_
-
- Optional. If set to true, thought summaries are returned, which are synthisized versions of the model's raw thoughts and offer insights into the model's internal reasoning process.
-
-- **imageConfig** _\{ aspectRatio: string \}_
-
- Optional. Configuration for the models image generation. Only supported by specific [Google Generative AI models](https://ai.google.dev/gemini-api/docs/image-generation).
-
- - **aspectRatio** _string_
-
- Model defaults to generate 1:1 squares, or to matching the output image size to that of your input image. Can be one of the following:
-
- - 1:1
- - 2:3
- - 3:2
- - 3:4
- - 4:3
- - 4:5
- - 5:4
- - 9:16
- - 16:9
- - 21:9
-
-### Thinking
-
-The Gemini 2.5 and Gemini 3 series models use an internal "thinking process" that significantly improves their reasoning and multi-step planning abilities, making them highly effective for complex tasks such as coding, advanced mathematics, and data analysis. For more information see [Google Generative AI thinking documentation](https://ai.google.dev/gemini-api/docs/thinking).
-
-#### Gemini 3 Models
-
-For Gemini 3 models, use the `thinkingLevel` parameter to control the depth of reasoning:
-
-```ts
-import { google, GoogleGenerativeAIProviderOptions } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const model = google('gemini-3-pro-preview');
-
-const { text, reasoning } = await generateText({
- model: model,
- prompt: 'What is the sum of the first 10 prime numbers?',
- providerOptions: {
- google: {
- thinkingConfig: {
- thinkingLevel: 'high',
- includeThoughts: true,
- },
- } satisfies GoogleGenerativeAIProviderOptions,
- },
-});
-
-console.log(text);
-
-console.log(reasoning); // Reasoning summary
-```
-
-#### Gemini 2.5 Models
-
-For Gemini 2.5 models, use the `thinkingBudget` parameter to control the number of thinking tokens:
-
-```ts
-import { google, GoogleGenerativeAIProviderOptions } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const model = google('gemini-2.5-flash');
-
-const { text, reasoning } = await generateText({
- model: model,
- prompt: 'What is the sum of the first 10 prime numbers?',
- providerOptions: {
- google: {
- thinkingConfig: {
- thinkingBudget: 8192,
- includeThoughts: true,
- },
- } satisfies GoogleGenerativeAIProviderOptions,
- },
-});
-
-console.log(text);
-
-console.log(reasoning); // Reasoning summary
-```
-
-### File Inputs
-
-The Google Generative AI provider supports file inputs, e.g. PDF files.
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: google('gemini-2.5-flash'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model according to this document?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- },
- ],
- },
- ],
-});
-```
-
-You can also use YouTube URLs directly:
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: google('gemini-2.5-flash'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'Summarize this video',
- },
- {
- type: 'file',
- data: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
- mediaType: 'video/mp4',
- },
- ],
- },
- ],
-});
-```
-
-
- The AI SDK will automatically download URLs if you pass them as data, except
- for `https://generativelanguage.googleapis.com/v1beta/files/` and YouTube
- URLs. You can use the Google Generative AI Files API to upload larger files to
- that location. YouTube URLs (public or unlisted videos) are supported directly
- - you can specify one YouTube video URL per request.
-
-
-See [File Parts](/docs/foundations/prompts#file-parts) for details on how to use files in prompts.
-
-### Cached Content
-
-Google Generative AI supports both explicit and implicit caching to help reduce costs on repetitive content.
-
-#### Implicit Caching
-
-Gemini 2.5 models automatically provide cache cost savings without needing to create an explicit cache. When you send requests that share common prefixes with previous requests, you'll receive a 75% token discount on cached content.
-
-To maximize cache hits with implicit caching:
-
-- Keep content at the beginning of requests consistent
-- Add variable content (like user questions) at the end of prompts
-- Ensure requests meet minimum token requirements:
- - Gemini 2.5 Flash: 1024 tokens minimum
- - Gemini 2.5 Pro: 2048 tokens minimum
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-// Structure prompts with consistent content at the beginning
-const baseContext =
- 'You are a cooking assistant with expertise in Italian cuisine. Here are 1000 lasagna recipes for reference...';
-
-const { text: veggieLasagna } = await generateText({
- model: google('gemini-2.5-pro'),
- prompt: `${baseContext}\n\nWrite a vegetarian lasagna recipe for 4 people.`,
-});
-
-// Second request with same prefix - eligible for cache hit
-const { text: meatLasagna, providerMetadata } = await generateText({
- model: google('gemini-2.5-pro'),
- prompt: `${baseContext}\n\nWrite a meat lasagna recipe for 12 people.`,
-});
-
-// Check cached token count in usage metadata
-console.log('Cached tokens:', providerMetadata.google?.usageMetadata);
-// e.g.
-// {
-// groundingMetadata: null,
-// safetyRatings: null,
-// usageMetadata: {
-// cachedContentTokenCount: 2027,
-// thoughtsTokenCount: 702,
-// promptTokenCount: 2152,
-// candidatesTokenCount: 710,
-// totalTokenCount: 3564
-// }
-// }
-```
-
-
- Usage metadata was added to `providerMetadata` in `@ai-sdk/google@1.2.23`. If
- you are using an older version, usage metadata is available in the raw HTTP
- `response` body returned as part of the return value from `generateText`.
-
-
-#### Explicit Caching
-
-For guaranteed cost savings, you can still use explicit caching with Gemini 2.5 and 2.0 models. See the [models page](https://ai.google.dev/gemini-api/docs/models) to check if caching is supported for the used model:
-
-```ts
-import { google } from '@ai-sdk/google';
-import { GoogleAICacheManager } from '@google/generative-ai/server';
-import { generateText } from 'ai';
-
-const cacheManager = new GoogleAICacheManager(
- process.env.GOOGLE_GENERATIVE_AI_API_KEY,
-);
-
-const model = 'gemini-2.5-pro';
-
-const { name: cachedContent } = await cacheManager.create({
- model,
- contents: [
- {
- role: 'user',
- parts: [{ text: '1000 Lasagna Recipes...' }],
- },
- ],
- ttlSeconds: 60 * 5,
-});
-
-const { text: veggieLasangaRecipe } = await generateText({
- model: google(model),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
- providerOptions: {
- google: {
- cachedContent,
- },
- },
-});
-
-const { text: meatLasangaRecipe } = await generateText({
- model: google(model),
- prompt: 'Write a meat lasagna recipe for 12 people.',
- providerOptions: {
- google: {
- cachedContent,
- },
- },
-});
-```
-
-### Code Execution
-
-With [Code Execution](https://ai.google.dev/gemini-api/docs/code-execution), certain models can generate and execute Python code to perform calculations, solve problems, or provide more accurate information.
-
-You can enable code execution by adding the `code_execution` tool to your request.
-
-```ts
-import { google } from '@ai-sdk/google';
-import { googleTools } from '@ai-sdk/google/internal';
-import { generateText } from 'ai';
-
-const { text, toolCalls, toolResults } = await generateText({
- model: google('gemini-2.5-pro'),
- tools: { code_execution: google.tools.codeExecution({}) },
- prompt: 'Use python to calculate the 20th fibonacci number.',
-});
-```
-
-The response will contain the tool calls and results from the code execution.
-
-### Google Search
-
-With [search grounding](https://ai.google.dev/gemini-api/docs/google-search),
-the model has access to the latest information using Google search.
-Google search can be used to provide answers around current events:
-
-```ts highlight="8,17-20"
-import { google } from '@ai-sdk/google';
-import { GoogleGenerativeAIProviderMetadata } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text, sources, providerMetadata } = await generateText({
- model: google('gemini-2.5-flash'),
- tools: {
- google_search: google.tools.googleSearch({}),
- },
- prompt:
- 'List the top 5 San Francisco news from the past week.' +
- 'You must include the date of each article.',
-});
-
-// access the grounding metadata. Casting to the provider metadata type
-// is optional but provides autocomplete and type safety.
-const metadata = providerMetadata?.google as
- | GoogleGenerativeAIProviderMetadata
- | undefined;
-const groundingMetadata = metadata?.groundingMetadata;
-const safetyRatings = metadata?.safetyRatings;
-```
-
-When Search Grounding is enabled, the model will include sources in the response.
-
-Additionally, the grounding metadata includes detailed information about how search results were used to ground the model's response. Here are the available fields:
-
-- **`webSearchQueries`** (`string[] | null`)
-
- - Array of search queries used to retrieve information
- - Example: `["What's the weather in Chicago this weekend?"]`
-
-- **`searchEntryPoint`** (`{ renderedContent: string } | null`)
-
- - Contains the main search result content used as an entry point
- - The `renderedContent` field contains the formatted content
-
-- **`groundingSupports`** (Array of support objects | null)
- - Contains details about how specific response parts are supported by search results
- - Each support object includes:
- - **`segment`**: Information about the grounded text segment
- - `text`: The actual text segment
- - `startIndex`: Starting position in the response
- - `endIndex`: Ending position in the response
- - **`groundingChunkIndices`**: References to supporting search result chunks
- - **`confidenceScores`**: Confidence scores (0-1) for each supporting chunk
-
-Example response:
-
-```json
-{
- "groundingMetadata": {
- "webSearchQueries": ["What's the weather in Chicago this weekend?"],
- "searchEntryPoint": {
- "renderedContent": "..."
- },
- "groundingSupports": [
- {
- "segment": {
- "startIndex": 0,
- "endIndex": 65,
- "text": "Chicago weather changes rapidly, so layers let you adjust easily."
- },
- "groundingChunkIndices": [0],
- "confidenceScores": [0.99]
- }
- ]
- }
-}
-```
-
-### File Search
-
-The [File Search tool](https://ai.google.dev/gemini-api/docs/file-search) lets Gemini retrieve context from your own documents that you have indexed in File Search stores. Only Gemini 2.5 models support this feature.
-
-```ts highlight="9-13"
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text, sources } = await generateText({
- model: google('gemini-2.5-pro'),
- tools: {
- file_search: google.tools.fileSearch({
- fileSearchStoreNames: [
- 'projects/my-project/locations/us/fileSearchStores/my-store',
- ],
- metadataFilter: 'author = "Robert Graves"',
- topK: 8,
- }),
- },
- prompt: "Summarise the key themes of 'I, Claudius'.",
-});
-```
-
-File Search responses include citations via the normal `sources` field and expose raw [grounding metadata](#google-search) in `providerMetadata.google.groundingMetadata`.
-
-### URL Context
-
-Google provides a provider-defined URL context tool.
-
-The URL context tool allows the you to provide specific URLs that you want the model to analyze directly in from the prompt.
-
-```ts highlight="9,13-17"
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text, sources, providerMetadata } = await generateText({
- model: google('gemini-2.5-flash'),
- prompt: `Based on the document: https://ai.google.dev/gemini-api/docs/url-context.
- Answer this question: How many links we can consume in one request?`,
- tools: {
- url_context: google.tools.urlContext({}),
- },
-});
-
-const metadata = providerMetadata?.google as
- | GoogleGenerativeAIProviderMetadata
- | undefined;
-const groundingMetadata = metadata?.groundingMetadata;
-const urlContextMetadata = metadata?.urlContextMetadata;
-```
-
-The URL context metadata includes detailed information about how the model used the URL context to generate the response. Here are the available fields:
-
-- **`urlMetadata`** (`{ retrievedUrl: string; urlRetrievalStatus: string; }[] | null`)
-
- - Array of URL context metadata
- - Each object includes:
- - **`retrievedUrl`**: The URL of the context
- - **`urlRetrievalStatus`**: The status of the URL retrieval
-
-Example response:
-
-```json
-{
- "urlMetadata": [
- {
- "retrievedUrl": "https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai",
- "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_SUCCESS"
- }
- ]
-}
-```
-
-With the URL context tool, you will also get the `groundingMetadata`.
-
-```json
-"groundingMetadata": {
- "groundingChunks": [
- {
- "web": {
- "uri": "https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai",
- "title": "Google Generative AI - AI SDK Providers"
- }
- }
- ],
- "groundingSupports": [
- {
- "segment": {
- "startIndex": 67,
- "endIndex": 157,
- "text": "**Installation**: Install the `@ai-sdk/google` module using your preferred package manager"
- },
- "groundingChunkIndices": [
- 0
- ]
- },
- ]
-}
-```
-
-You can add up to 20 URLs per request.
-
-
- The URL context tool is only supported for Gemini 2.0 Flash models and above.
- Check the [supported models for URL context
- tool](https://ai.google.dev/gemini-api/docs/url-context#supported-models).
-
-
-#### Combine URL Context with Search Grounding
-
-You can combine the URL context tool with search grounding to provide the model with the latest information from the web.
-
-```ts highlight="9-10"
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text, sources, providerMetadata } = await generateText({
- model: google('gemini-2.5-flash'),
- prompt: `Based on this context: https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai, tell me how to use Gemini with AI SDK.
- Also, provide the latest news about AI SDK V5.`,
- tools: {
- google_search: google.tools.googleSearch({}),
- url_context: google.tools.urlContext({}),
- },
-});
-
-const metadata = providerMetadata?.google as
- | GoogleGenerativeAIProviderMetadata
- | undefined;
-const groundingMetadata = metadata?.groundingMetadata;
-const urlContextMetadata = metadata?.urlContextMetadata;
-```
-
-### RAG Engine Grounding
-
-With [RAG Engine Grounding](https://cloud.google.com/vertex-ai/generative-ai/docs/rag-engine/use-vertexai-search#generate-content-using-gemini-api),
-the model has access to your custom knowledge base using the Vertex RAG Engine.
-This enables the model to provide answers based on your specific data sources and documents.
-
-
- RAG Engine Grounding is only supported with Vertex Gemini models. You must use
- the Google Vertex provider (`@ai-sdk/google-vertex`) instead of the standard
- Google provider (`@ai-sdk/google`) to use this feature.
-
-
-```ts highlight="8,17-20"
-import { createVertex } from '@ai-sdk/google-vertex';
-import { GoogleGenerativeAIProviderMetadata } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const vertex = createVertex({
- project: 'my-project',
- location: 'us-central1',
-});
-
-const { text, sources, providerMetadata } = await generateText({
- model: vertex('gemini-2.5-flash'),
- tools: {
- vertex_rag_store: vertex.tools.vertexRagStore({
- ragCorpus:
- 'projects/my-project/locations/us-central1/ragCorpora/my-rag-corpus',
- topK: 5,
- }),
- },
- prompt:
- 'What are the key features of our product according to our documentation?',
-});
-
-// access the grounding metadata. Casting to the provider metadata type
-// is optional but provides autocomplete and type safety.
-const metadata = providerMetadata?.google as
- | GoogleGenerativeAIProviderMetadata
- | undefined;
-const groundingMetadata = metadata?.groundingMetadata;
-const safetyRatings = metadata?.safetyRatings;
-```
-
-When RAG Engine Grounding is enabled, the model will include sources from your RAG corpus in the response.
-
-Additionally, the grounding metadata includes detailed information about how RAG results were used to ground the model's response. Here are the available fields:
-
-- **`groundingChunks`** (Array of chunk objects | null)
-
- - Contains the retrieved context chunks from your RAG corpus
- - Each chunk includes:
- - **`retrievedContext`**: Information about the retrieved context
- - `uri`: The URI or identifier of the source document
- - `title`: The title of the source document (optional)
- - `text`: The actual text content of the chunk
-
-- **`groundingSupports`** (Array of support objects | null)
-
- - Contains details about how specific response parts are supported by RAG results
- - Each support object includes:
- - **`segment`**: Information about the grounded text segment
- - `text`: The actual text segment
- - `startIndex`: Starting position in the response
- - `endIndex`: Ending position in the response
- - **`groundingChunkIndices`**: References to supporting RAG result chunks
- - **`confidenceScores`**: Confidence scores (0-1) for each supporting chunk
-
-Example response:
-
-```json
-{
- "groundingMetadata": {
- "groundingChunks": [
- {
- "retrievedContext": {
- "uri": "gs://my-bucket/docs/product-guide.pdf",
- "title": "Product User Guide",
- "text": "Our product includes advanced AI capabilities, real-time processing, and enterprise-grade security features."
- }
- }
- ],
- "groundingSupports": [
- {
- "segment": {
- "startIndex": 0,
- "endIndex": 45,
- "text": "Our product includes advanced AI capabilities and real-time processing."
- },
- "groundingChunkIndices": [0],
- "confidenceScores": [0.95]
- }
- ]
- }
-}
-```
-
-#### Configuration Options
-
-The `vertexRagStore` tool accepts the following configuration options:
-
-- **`ragCorpus`** (`string`, required)
-
- - The RagCorpus resource name in the format: `projects/{project}/locations/{location}/ragCorpora/{rag_corpus}`
- - This identifies your specific RAG corpus to search against
-
-- **`topK`** (`number`, optional)
-
- - The number of top contexts to retrieve from your RAG corpus
- - Defaults to the corpus configuration if not specified
-
-### Image Outputs
-
-Gemini models with image generation capabilities (`gemini-2.5-flash-image-preview`) support image generation. Images are exposed as files in the response.
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: google('gemini-2.5-flash-image-preview'),
- prompt:
- 'Create a picture of a nano banana dish in a fancy restaurant with a Gemini theme',
-});
-
-for (const file of result.files) {
- if (file.mediaType.startsWith('image/')) {
- console.log('Generated image:', file);
- }
-}
-```
-
-### Safety Ratings
-
-The safety ratings provide insight into the safety of the model's response.
-See [Google AI documentation on safety settings](https://ai.google.dev/gemini-api/docs/safety-settings).
-
-Example response excerpt:
-
-```json
-{
- "safetyRatings": [
- {
- "category": "HARM_CATEGORY_HATE_SPEECH",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.11027937,
- "severity": "HARM_SEVERITY_LOW",
- "severityScore": 0.28487435
- },
- {
- "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
- "probability": "HIGH",
- "blocked": true,
- "probabilityScore": 0.95422274,
- "severity": "HARM_SEVERITY_MEDIUM",
- "severityScore": 0.43398145
- },
- {
- "category": "HARM_CATEGORY_HARASSMENT",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.11085559,
- "severity": "HARM_SEVERITY_NEGLIGIBLE",
- "severityScore": 0.19027223
- },
- {
- "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.22901751,
- "severity": "HARM_SEVERITY_NEGLIGIBLE",
- "severityScore": 0.09089675
- }
- ]
-}
-```
-
-### Troubleshooting
-
-#### Schema Limitations
-
-The Google Generative AI API uses a subset of the OpenAPI 3.0 schema,
-which does not support features such as unions.
-The errors that you get in this case look like this:
-
-`GenerateContentRequest.generation_config.response_schema.properties[occupation].type: must be specified`
-
-By default, structured outputs are enabled (and for tool calling they are required).
-You can disable structured outputs for object generation as a workaround:
-
-```ts highlight="3,8"
-const { object } = await generateObject({
- model: google('gemini-2.5-flash'),
- providerOptions: {
- google: {
- structuredOutputs: false,
- },
- },
- schema: z.object({
- name: z.string(),
- age: z.number(),
- contact: z.union([
- z.object({
- type: z.literal('email'),
- value: z.string(),
- }),
- z.object({
- type: z.literal('phone'),
- value: z.string(),
- }),
- ]),
- }),
- prompt: 'Generate an example person for testing.',
-});
-```
-
-The following Zod features are known to not work with Google Generative AI:
-
-- `z.union`
-- `z.record`
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Google Search | URL Context |
-| ------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `gemini-3-pro-preview` | | | | | | |
-| `gemini-2.5-pro` | | | | | | |
-| `gemini-2.5-flash` | | | | | | |
-| `gemini-2.5-flash-lite` | | | | | | |
-| `gemini-2.5-flash-lite-preview-06-17` | | | | | | |
-| `gemini-2.0-flash` | | | | | | |
-| `gemini-1.5-pro` | | | | | | |
-| `gemini-1.5-pro-latest` | | | | | | |
-| `gemini-1.5-flash` | | | | | | |
-| `gemini-1.5-flash-latest` | | | | | | |
-| `gemini-1.5-flash-8b` | | | | | | |
-| `gemini-1.5-flash-8b-latest` | | | | | | |
-
-
- The table above lists popular models. Please see the [Google Generative AI
- docs](https://ai.google.dev/gemini-api/docs/models/) for a full list of
- available models. The table above lists popular models. You can also pass any
- available provider model ID as a string if needed.
-
-
-## Gemma Models
-
-You can use [Gemma models](https://deepmind.google/models/gemma/) with the Google Generative AI API.
-
-Gemma models don't natively support the `systemInstruction` parameter, but the provider automatically handles system instructions by prepending them to the first user message. This allows you to use system instructions with Gemma models seamlessly:
-
-```ts
-import { google } from '@ai-sdk/google';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: google('gemma-3-27b-it'),
- system: 'You are a helpful assistant that responds concisely.',
- prompt: 'What is machine learning?',
-});
-```
-
-The system instruction is automatically formatted and included in the conversation, so Gemma models can follow the guidance without any additional configuration.
-
-## Embedding Models
-
-You can create models that call the [Google Generative AI embeddings API](https://ai.google.dev/gemini-api/docs/embeddings)
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = google.textEmbedding('gemini-embedding-001');
-```
-
-The Google Generative AI provider sends API calls to the right endpoint based on the type of embedding:
-
-- **Single embeddings**: When embedding a single value with `embed()`, the provider uses the single `:embedContent` endpoint, which typically has higher rate limits compared to the batch endpoint.
-- **Batch embeddings**: When embedding multiple values with `embedMany()` or multiple values in `embed()`, the provider uses the `:batchEmbedContents` endpoint.
-
-Google Generative AI embedding models support aditional settings. You can pass them as an options argument:
-
-```ts
-import { google } from '@ai-sdk/google';
-import { embed } from 'ai';
-
-const model = google.textEmbedding('gemini-embedding-001');
-
-const { embedding } = await embed({
- model,
- value: 'sunny day at the beach',
- providerOptions: {
- google: {
- outputDimensionality: 512, // optional, number of dimensions for the embedding
- taskType: 'SEMANTIC_SIMILARITY', // optional, specifies the task type for generating embeddings
- },
- },
-});
-```
-
-The following optional provider options are available for Google Generative AI embedding models:
-
-- **outputDimensionality**: _number_
-
- Optional reduced dimension for the output embedding. If set, excessive values in the output embedding are truncated from the end.
-
-- **taskType**: _string_
-
- Optional. Specifies the task type for generating embeddings. Supported task types include:
-
- - `SEMANTIC_SIMILARITY`: Optimized for text similarity.
- - `CLASSIFICATION`: Optimized for text classification.
- - `CLUSTERING`: Optimized for clustering texts based on similarity.
- - `RETRIEVAL_DOCUMENT`: Optimized for document retrieval.
- - `RETRIEVAL_QUERY`: Optimized for query-based retrieval.
- - `QUESTION_ANSWERING`: Optimized for answering questions.
- - `FACT_VERIFICATION`: Optimized for verifying factual information.
- - `CODE_RETRIEVAL_QUERY`: Optimized for retrieving code blocks based on natural language queries.
-
-### Model Capabilities
-
-| Model | Default Dimensions | Custom Dimensions |
-| ---------------------- | ------------------ | ------------------- |
-| `gemini-embedding-001` | 3072 | |
-| `text-embedding-004` | 768 | |
-
-## Image Models
-
-You can create [Imagen](https://ai.google.dev/gemini-api/imagen) models that call the Google Generative AI API using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { google } from '@ai-sdk/google';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: google.image('imagen-3.0-generate-002'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
-});
-```
-
-Further configuration can be done using Google provider options. You can validate the provider options using the `GoogleGenerativeAIImageProviderOptions` type.
-
-```ts
-import { google } from '@ai-sdk/google';
-import { GoogleGenerativeAIImageProviderOptions } from '@ai-sdk/google';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: google.image('imagen-3.0-generate-002'),
- providerOptions: {
- google: {
- personGeneration: 'dont_allow',
- } satisfies GoogleGenerativeAIImageProviderOptions,
- },
- // ...
-});
-```
-
-The following provider options are available:
-
-- **personGeneration** `allow_adult` | `allow_all` | `dont_allow`
- Whether to allow person generation. Defaults to `allow_adult`.
-
-
- Imagen models do not support the `size` parameter. Use the `aspectRatio`
- parameter instead.
-
-
-#### Model Capabilities
-
-| Model | Aspect Ratios |
-| ------------------------- | ------------------------- |
-| `imagen-3.0-generate-002` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-
----
-title: Hume
-description: Learn how to use the Hume provider for the AI SDK.
----
-
-# Hume Provider
-
-The [Hume](https://hume.ai/) provider contains language model support for the Hume transcription API.
-
-## Setup
-
-The Hume provider is available in the `@ai-sdk/hume` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `hume` from `@ai-sdk/hume`:
-
-```ts
-import { hume } from '@ai-sdk/hume';
-```
-
-If you need a customized setup, you can import `createHume` from `@ai-sdk/hume` and create a provider instance with your settings:
-
-```ts
-import { createHume } from '@ai-sdk/hume';
-
-const hume = createHume({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the Hume provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `HUME_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Speech Models
-
-You can create models that call the [Hume speech API](https://dev.hume.ai/docs/text-to-speech-tts/overview)
-using the `.speech()` factory method.
-
-```ts
-const model = hume.speech();
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying a voice to use for the generated audio.
-
-```ts highlight="6"
-import { experimental_generateSpeech as generateSpeech } from 'ai';
-import { hume } from '@ai-sdk/hume';
-
-const result = await generateSpeech({
- model: hume.speech(),
- text: 'Hello, world!',
- voice: 'd8ab67c6-953d-4bd8-9370-8fa53a0f1453',
- providerOptions: { hume: {} },
-});
-```
-
-The following provider options are available:
-
-- **context** _object_
-
- Either:
-
- - `{ generationId: string }` - A generation ID to use for context.
- - `{ utterances: HumeUtterance[] }` - An array of utterance objects for context.
-
-### Model Capabilities
-
-| Model | Instructions |
-| --------- | ------------------- |
-| `default` | |
-
----
-title: Google Vertex AI
-description: Learn how to use the Google Vertex AI provider.
----
-
-# Google Vertex Provider
-
-The Google Vertex provider for the [AI SDK](/docs) contains language model support for the [Google Vertex AI](https://cloud.google.com/vertex-ai) APIs. This includes support for [Google's Gemini models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models) and [Anthropic's Claude partner models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude).
-
-
- The Google Vertex provider is compatible with both Node.js and Edge runtimes.
- The Edge runtime is supported through the `@ai-sdk/google-vertex/edge`
- sub-module. More details can be found in the [Google Vertex Edge
- Runtime](#google-vertex-edge-runtime) and [Google Vertex Anthropic Edge
- Runtime](#google-vertex-anthropic-edge-runtime) sections below.
-
-
-## Setup
-
-The Google Vertex and Google Vertex Anthropic providers are both available in the `@ai-sdk/google-vertex` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Google Vertex Provider Usage
-
-The Google Vertex provider instance is used to create model instances that call the Vertex AI API. The models available with this provider include [Google's Gemini models](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models). If you're looking to use [Anthropic's Claude models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude), see the [Google Vertex Anthropic Provider](#google-vertex-anthropic-provider-usage) section below.
-
-### Provider Instance
-
-You can import the default provider instance `vertex` from `@ai-sdk/google-vertex`:
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-```
-
-If you need a customized setup, you can import `createVertex` from `@ai-sdk/google-vertex` and create a provider instance with your settings:
-
-```ts
-import { createVertex } from '@ai-sdk/google-vertex';
-
-const vertex = createVertex({
- project: 'my-project', // optional
- location: 'us-central1', // optional
-});
-```
-
-Google Vertex supports two different authentication implementations depending on your runtime environment.
-
-#### Node.js Runtime
-
-The Node.js runtime is the default runtime supported by the AI SDK. It supports all standard Google Cloud authentication options through the [`google-auth-library`](https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#ways-to-authenticate). Typical use involves setting a path to a json credentials file in the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. The credentials file can be obtained from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).
-
-If you want to customize the Google authentication options you can pass them as options to the `createVertex` function, for example:
-
-```ts
-import { createVertex } from '@ai-sdk/google-vertex';
-
-const vertex = createVertex({
- googleAuthOptions: {
- credentials: {
- client_email: 'my-email',
- private_key: 'my-private-key',
- },
- },
-});
-```
-
-##### Optional Provider Settings
-
-You can use the following optional settings to customize the provider instance:
-
-- **project** _string_
-
- The Google Cloud project ID that you want to use for the API calls.
- It uses the `GOOGLE_VERTEX_PROJECT` environment variable by default.
-
-- **location** _string_
-
- The Google Cloud location that you want to use for the API calls, e.g. `us-central1`.
- It uses the `GOOGLE_VERTEX_LOCATION` environment variable by default.
-
-- **googleAuthOptions** _object_
-
- Optional. The Authentication options used by the [Google Auth Library](https://github.com/googleapis/google-auth-library-nodejs/). See also the [GoogleAuthOptions](https://github.com/googleapis/google-auth-library-nodejs/blob/08978822e1b7b5961f0e355df51d738e012be392/src/auth/googleauth.ts#L87C18-L87C35) interface.
-
- - **authClient** _object_
- An `AuthClient` to use.
-
- - **keyFilename** _string_
- Path to a .json, .pem, or .p12 key file.
-
- - **keyFile** _string_
- Path to a .json, .pem, or .p12 key file.
-
- - **credentials** _object_
- Object containing client_email and private_key properties, or the external account client options.
-
- - **clientOptions** _object_
- Options object passed to the constructor of the client.
-
- - **scopes** _string | string[]_
- Required scopes for the desired API request.
-
- - **projectId** _string_
- Your project ID.
-
- - **universeDomain** _string_
- The default service domain for a given Cloud universe.
-
-- **headers** _Resolvable<Record<string, string | undefined>>_
-
- Headers to include in the requests. Can be provided in multiple formats:
-
- - A record of header key-value pairs: `Record`
- - A function that returns headers: `() => Record`
- - An async function that returns headers: `async () => Record`
- - A promise that resolves to headers: `Promise>`
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-- **baseURL** _string_
-
- Optional. Base URL for the Google Vertex API calls e.g. to use proxy servers. By default, it is constructed using the location and project:
- `https://${location}-aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/publishers/google`
-
-
-#### Edge Runtime
-
-Edge runtimes (like Vercel Edge Functions and Cloudflare Workers) are lightweight JavaScript environments that run closer to users at the network edge.
-They only provide a subset of the standard Node.js APIs.
-For example, direct file system access is not available, and many Node.js-specific libraries
-(including the standard Google Auth library) are not compatible.
-
-The Edge runtime version of the Google Vertex provider supports Google's [Application Default Credentials](https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#application-default-credentials) through environment variables. The values can be obtained from a json credentials file from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).
-
-You can import the default provider instance `vertex` from `@ai-sdk/google-vertex/edge`:
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex/edge';
-```
-
-
- The `/edge` sub-module is included in the `@ai-sdk/google-vertex` package, so
- you don't need to install it separately. You must import from
- `@ai-sdk/google-vertex/edge` to differentiate it from the Node.js provider.
-
-
-If you need a customized setup, you can import `createVertex` from `@ai-sdk/google-vertex/edge` and create a provider instance with your settings:
-
-```ts
-import { createVertex } from '@ai-sdk/google-vertex/edge';
-
-const vertex = createVertex({
- project: 'my-project', // optional
- location: 'us-central1', // optional
-});
-```
-
-For Edge runtime authentication, you'll need to set these environment variables from your Google Default Application Credentials JSON file:
-
-- `GOOGLE_CLIENT_EMAIL`
-- `GOOGLE_PRIVATE_KEY`
-- `GOOGLE_PRIVATE_KEY_ID` (optional)
-
-These values can be obtained from a service account JSON file from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).
-
-##### Optional Provider Settings
-
-You can use the following optional settings to customize the provider instance:
-
-- **project** _string_
-
- The Google Cloud project ID that you want to use for the API calls.
- It uses the `GOOGLE_VERTEX_PROJECT` environment variable by default.
-
-- **location** _string_
-
- The Google Cloud location that you want to use for the API calls, e.g. `us-central1`.
- It uses the `GOOGLE_VERTEX_LOCATION` environment variable by default.
-
-- **googleCredentials** _object_
-
- Optional. The credentials used by the Edge provider for authentication. These credentials are typically set through environment variables and are derived from a service account JSON file.
-
- - **clientEmail** _string_
- The client email from the service account JSON file. Defaults to the contents of the `GOOGLE_CLIENT_EMAIL` environment variable.
-
- - **privateKey** _string_
- The private key from the service account JSON file. Defaults to the contents of the `GOOGLE_PRIVATE_KEY` environment variable.
-
- - **privateKeyId** _string_
- The private key ID from the service account JSON file (optional). Defaults to the contents of the `GOOGLE_PRIVATE_KEY_ID` environment variable.
-
-- **headers** _Resolvable<Record<string, string | undefined>>_
-
- Headers to include in the requests. Can be provided in multiple formats:
-
- - A record of header key-value pairs: `Record`
- - A function that returns headers: `() => Record`
- - An async function that returns headers: `async () => Record`
- - A promise that resolves to headers: `Promise>`
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-### Language Models
-
-You can create models that call the Vertex API using the provider instance.
-The first argument is the model id, e.g. `gemini-1.5-pro`.
-
-```ts
-const model = vertex('gemini-1.5-pro');
-```
-
-
- If you are using [your own
- models](https://cloud.google.com/vertex-ai/docs/training-overview), the name
- of your model needs to start with `projects/`.
-
-
-Google Vertex models support also some model specific settings that are not part
-of the [standard call settings](/docs/ai-sdk-core/settings). You can pass them as
-an options argument:
-
-```ts
-const model = vertex('gemini-1.5-pro');
-
-await generateText({
- model,
- providerOptions: {
- google: {
- safetySettings: [
- {
- category: 'HARM_CATEGORY_UNSPECIFIED',
- threshold: 'BLOCK_LOW_AND_ABOVE',
- },
- ],
- },
- },
-});
-```
-
-The following optional provider options are available for Google Vertex models:
-
-- **structuredOutputs** _boolean_
-
- Optional. Enable structured output. Default is true.
-
- This is useful when the JSON Schema contains elements that are
- not supported by the OpenAPI schema version that
- Google Vertex uses. You can use this to disable
- structured outputs if you need to.
-
- See [Troubleshooting: Schema Limitations](#schema-limitations) for more details.
-
-- **safetySettings** _Array\<\{ category: string; threshold: string \}\>_
-
- Optional. Safety settings for the model.
-
- - **category** _string_
-
- The category of the safety setting. Can be one of the following:
-
- - `HARM_CATEGORY_UNSPECIFIED`
- - `HARM_CATEGORY_HATE_SPEECH`
- - `HARM_CATEGORY_DANGEROUS_CONTENT`
- - `HARM_CATEGORY_HARASSMENT`
- - `HARM_CATEGORY_SEXUALLY_EXPLICIT`
- - `HARM_CATEGORY_CIVIC_INTEGRITY`
-
- - **threshold** _string_
-
- The threshold of the safety setting. Can be one of the following:
-
- - `HARM_BLOCK_THRESHOLD_UNSPECIFIED`
- - `BLOCK_LOW_AND_ABOVE`
- - `BLOCK_MEDIUM_AND_ABOVE`
- - `BLOCK_ONLY_HIGH`
- - `BLOCK_NONE`
-
-- **audioTimestamp** _boolean_
-
- Optional. Enables timestamp understanding for audio files. Defaults to false.
-
- This is useful for generating transcripts with accurate timestamps.
- Consult [Google's Documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding) for usage details.
-
-- **labels** _object_
-
- Optional. Defines labels used in billing reports.
-
- Consult [Google's Documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/add-labels-to-api-calls) for usage details.
-
-You can use Google Vertex language models to generate text with the `generateText` function:
-
-```ts highlight="1,4"
-import { vertex } from '@ai-sdk/google-vertex';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: vertex('gemini-1.5-pro'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Google Vertex language models can also be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-#### Code Execution
-
-With [Code Execution](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/code-execution), certain Gemini models on Vertex AI can generate and execute Python code. This allows the model to perform calculations, data manipulation, and other programmatic tasks to enhance its responses.
-
-You can enable code execution by adding the `code_execution` tool to your request.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: vertex('gemini-2.5-pro'),
- tools: { code_execution: vertex.tools.codeExecution({}) },
- prompt:
- 'Use python to calculate 20th fibonacci number. Then find the nearest palindrome to it.',
-});
-```
-
-The response will contain `tool-call` and `tool-result` parts for the executed code.
-
-#### URL Context
-
-URL Context allows Gemini models to retrieve and analyze content from URLs. Supported models: Gemini 2.5 Flash-Lite, 2.5 Pro, 2.5 Flash, 2.0 Flash.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: vertex('gemini-2.5-pro'),
- tools: { url_context: vertex.tools.urlContext({}) },
- prompt: 'What are the key points from https://example.com/article?',
-});
-```
-
-#### Google Search
-
-Google Search enables Gemini models to access real-time web information. Supported models: Gemini 2.5 Flash-Lite, 2.5 Flash, 2.0 Flash, 2.5 Pro.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: vertex('gemini-2.5-pro'),
- tools: { google_search: vertex.tools.googleSearch({}) },
- prompt: 'What are the latest developments in AI?',
-});
-```
-
-#### Reasoning (Thinking Tokens)
-
-Google Vertex AI, through its support for Gemini models, can also emit "thinking" tokens, representing the model's reasoning process. The AI SDK exposes these as reasoning information.
-
-To enable thinking tokens for compatible Gemini models via Vertex, set `includeThoughts: true` in the `thinkingConfig` provider option. Since the Vertex provider uses the Google provider's underlying language model, these options are passed through `providerOptions.google`:
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'; // Note: importing from @ai-sdk/google
-import { generateText, streamText } from 'ai';
-
-// For generateText:
-const { text, reasoning, reasoningDetails } = await generateText({
- model: vertex('gemini-2.0-flash-001'), // Or other supported model via Vertex
- providerOptions: {
- google: {
- // Options are nested under 'google' for Vertex provider
- thinkingConfig: {
- includeThoughts: true,
- // thinkingBudget: 2048, // Optional
- },
- } satisfies GoogleGenerativeAIProviderOptions,
- },
- prompt: 'Explain quantum computing in simple terms.',
-});
-
-console.log('Reasoning:', reasoning);
-console.log('Reasoning Details:', reasoningDetails);
-console.log('Final Text:', text);
-
-// For streamText:
-const result = streamText({
- model: vertex('gemini-2.0-flash-001'), // Or other supported model via Vertex
- providerOptions: {
- google: {
- // Options are nested under 'google' for Vertex provider
- thinkingConfig: {
- includeThoughts: true,
- // thinkingBudget: 2048, // Optional
- },
- } satisfies GoogleGenerativeAIProviderOptions,
- },
- prompt: 'Explain quantum computing in simple terms.',
-});
-
-for await (const part of result.fullStream) {
- if (part.type === 'reasoning') {
- process.stdout.write(`THOUGHT: ${part.textDelta}\n`);
- } else if (part.type === 'text-delta') {
- process.stdout.write(part.textDelta);
- }
-}
-```
-
-When `includeThoughts` is true, parts of the API response marked with `thought: true` will be processed as reasoning.
-
-- In `generateText`, these contribute to the `reasoning` (string) and `reasoningDetails` (array) fields.
-- In `streamText`, these are emitted as `reasoning` stream parts.
-
-
- Refer to the [Google Vertex AI documentation on
- "thinking"](https://cloud.google.com/vertex-ai/generative-ai/docs/thinking)
- for model compatibility and further details.
-
-
-#### File Inputs
-
-The Google Vertex provider supports file inputs, e.g. PDF files.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: vertex('gemini-1.5-pro'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model according to this document?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- },
- ],
- },
- ],
-});
-```
-
-
- The AI SDK will automatically download URLs if you pass them as data, except
- for `gs://` URLs. You can use the Google Cloud Storage API to upload larger
- files to that location.
-
-
-See [File Parts](/docs/foundations/prompts#file-parts) for details on how to use files in prompts.
-
-### Safety Ratings
-
-The safety ratings provide insight into the safety of the model's response.
-See [Google Vertex AI documentation on configuring safety filters](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-filters).
-
-Example response excerpt:
-
-```json
-{
- "safetyRatings": [
- {
- "category": "HARM_CATEGORY_HATE_SPEECH",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.11027937,
- "severity": "HARM_SEVERITY_LOW",
- "severityScore": 0.28487435
- },
- {
- "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
- "probability": "HIGH",
- "blocked": true,
- "probabilityScore": 0.95422274,
- "severity": "HARM_SEVERITY_MEDIUM",
- "severityScore": 0.43398145
- },
- {
- "category": "HARM_CATEGORY_HARASSMENT",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.11085559,
- "severity": "HARM_SEVERITY_NEGLIGIBLE",
- "severityScore": 0.19027223
- },
- {
- "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
- "probability": "NEGLIGIBLE",
- "probabilityScore": 0.22901751,
- "severity": "HARM_SEVERITY_NEGLIGIBLE",
- "severityScore": 0.09089675
- }
- ]
-}
-```
-
-For more details, see the [Google Vertex AI documentation on grounding with Google Search](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/ground-gemini#ground-to-search).
-
-### Troubleshooting
-
-#### Schema Limitations
-
-The Google Vertex API uses a subset of the OpenAPI 3.0 schema,
-which does not support features such as unions.
-The errors that you get in this case look like this:
-
-`GenerateContentRequest.generation_config.response_schema.properties[occupation].type: must be specified`
-
-By default, structured outputs are enabled (and for tool calling they are required).
-You can disable structured outputs for object generation as a workaround:
-
-```ts highlight="3,8"
-const result = await generateObject({
- model: vertex('gemini-1.5-pro'),
- providerOptions: {
- google: {
- structuredOutputs: false,
- },
- },
- schema: z.object({
- name: z.string(),
- age: z.number(),
- contact: z.union([
- z.object({
- type: z.literal('email'),
- value: z.string(),
- }),
- z.object({
- type: z.literal('phone'),
- value: z.string(),
- }),
- ]),
- }),
- prompt: 'Generate an example person for testing.',
-});
-```
-
-The following Zod features are known to not work with Google Vertex:
-
-- `z.union`
-- `z.record`
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ---------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `gemini-2.0-flash-001` | | | | |
-| `gemini-2.0-flash-exp` | | | | |
-| `gemini-1.5-flash` | | | | |
-| `gemini-1.5-pro` | | | | |
-
-
- The table above lists popular models. Please see the [Google Vertex AI
- docs](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#supported-models)
- for a full list of available models. The table above lists popular models. You
- can also pass any available provider model ID as a string if needed.
-
-
-### Embedding Models
-
-You can create models that call the Google Vertex AI embeddings API using the `.textEmbeddingModel()` factory method:
-
-```ts
-const model = vertex.textEmbeddingModel('text-embedding-004');
-```
-
-Google Vertex AI embedding models support additional settings. You can pass them as an options argument:
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { embed } from 'ai';
-
-const model = vertex.textEmbeddingModel('text-embedding-004');
-
-const { embedding } = await embed({
- model,
- value: 'sunny day at the beach',
- providerOptions: {
- google: {
- outputDimensionality: 512, // optional, number of dimensions for the embedding
- taskType: 'SEMANTIC_SIMILARITY', // optional, specifies the task type for generating embeddings
- autoTruncate: false, // optional
- },
- },
-});
-```
-
-The following optional provider options are available for Google Vertex AI embedding models:
-
-- **outputDimensionality**: _number_
-
- Optional reduced dimension for the output embedding. If set, excessive values in the output embedding are truncated from the end.
-
-- **taskType**: _string_
-
- Optional. Specifies the task type for generating embeddings. Supported task types include:
-
- - `SEMANTIC_SIMILARITY`: Optimized for text similarity.
- - `CLASSIFICATION`: Optimized for text classification.
- - `CLUSTERING`: Optimized for clustering texts based on similarity.
- - `RETRIEVAL_DOCUMENT`: Optimized for document retrieval.
- - `RETRIEVAL_QUERY`: Optimized for query-based retrieval.
- - `QUESTION_ANSWERING`: Optimized for answering questions.
- - `FACT_VERIFICATION`: Optimized for verifying factual information.
- - `CODE_RETRIEVAL_QUERY`: Optimized for retrieving code blocks based on natural language queries.
-
-- **title**: _string_
-
- Optional. The title of the document being embedded. This helps the model produce better embeddings by providing additional context. Only valid when `taskType` is set to `'RETRIEVAL_DOCUMENT'`.
-
-- **autoTruncate**: _boolean_
-
- Optional. When set to `true`, input text will be truncated if it exceeds the maximum length. When set to `false`, an error is returned if the input text is too long. Defaults to `true`.
-
-#### Model Capabilities
-
-| Model | Max Values Per Call | Parallel Calls |
-| -------------------- | ------------------- | ------------------- |
-| `text-embedding-004` | 2048 | |
-
-
- The table above lists popular models. You can also pass any available provider
- model ID as a string if needed.
-
-
-### Image Models
-
-You can create [Imagen](https://cloud.google.com/vertex-ai/generative-ai/docs/image/overview) models that call the [Imagen on Vertex AI API](https://cloud.google.com/vertex-ai/generative-ai/docs/image/generate-images)
-using the `.image()` factory method. For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: vertex.image('imagen-3.0-generate-002'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
-});
-```
-
-Further configuration can be done using Google Vertex provider options. You can validate the provider options using the `GoogleVertexImageProviderOptions` type.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { GoogleVertexImageProviderOptions } from '@ai-sdk/google-vertex';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: vertex.image('imagen-3.0-generate-002'),
- providerOptions: {
- vertex: {
- negativePrompt: 'pixelated, blurry, low-quality',
- } satisfies GoogleVertexImageProviderOptions,
- },
- // ...
-});
-```
-
-The following provider options are available:
-
-- **negativePrompt** _string_
- A description of what to discourage in the generated images.
-
-- **personGeneration** `allow_adult` | `allow_all` | `dont_allow`
- Whether to allow person generation. Defaults to `allow_adult`.
-
-- **safetySetting** `block_low_and_above` | `block_medium_and_above` | `block_only_high` | `block_none`
- Whether to block unsafe content. Defaults to `block_medium_and_above`.
-
-- **addWatermark** _boolean_
- Whether to add an invisible watermark to the generated images. Defaults to `true`.
-
-- **storageUri** _string_
- Cloud Storage URI to store the generated images.
-
-
- Imagen models do not support the `size` parameter. Use the `aspectRatio`
- parameter instead.
-
-
-Additional information about the images can be retrieved using Google Vertex meta data.
-
-```ts
-import { vertex } from '@ai-sdk/google-vertex';
-import { GoogleVertexImageProviderOptions } from '@ai-sdk/google-vertex';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image, providerMetadata } = await generateImage({
- model: vertex.image('imagen-3.0-generate-002'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
-});
-
-console.log(
- `Revised prompt: ${providerMetadata.vertex.images[0].revisedPrompt}`,
-);
-```
-
-#### Model Capabilities
-
-| Model | Aspect Ratios |
-| ----------------------------------------- | ------------------------- |
-| `imagen-3.0-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-| `imagen-3.0-generate-002` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-| `imagen-3.0-fast-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-| `imagen-4.0-generate-preview-06-06` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-| `imagen-4.0-fast-generate-preview-06-06` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-| `imagen-4.0-ultra-generate-preview-06-06` | 1:1, 3:4, 4:3, 9:16, 16:9 |
-
-## Google Vertex Anthropic Provider Usage
-
-The Google Vertex Anthropic provider for the [AI SDK](/docs) offers support for Anthropic's Claude models through the Google Vertex AI APIs. This section provides details on how to set up and use the Google Vertex Anthropic provider.
-
-### Provider Instance
-
-You can import the default provider instance `vertexAnthropic` from `@ai-sdk/google-vertex/anthropic`:
-
-```typescript
-import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-```
-
-If you need a customized setup, you can import `createVertexAnthropic` from `@ai-sdk/google-vertex/anthropic` and create a provider instance with your settings:
-
-```typescript
-import { createVertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-
-const vertexAnthropic = createVertexAnthropic({
- project: 'my-project', // optional
- location: 'us-central1', // optional
-});
-```
-
-#### Node.js Runtime
-
-For Node.js environments, the Google Vertex Anthropic provider supports all standard Google Cloud authentication options through the `google-auth-library`. You can customize the authentication options by passing them to the `createVertexAnthropic` function:
-
-```typescript
-import { createVertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-
-const vertexAnthropic = createVertexAnthropic({
- googleAuthOptions: {
- credentials: {
- client_email: 'my-email',
- private_key: 'my-private-key',
- },
- },
-});
-```
-
-##### Optional Provider Settings
-
-You can use the following optional settings to customize the Google Vertex Anthropic provider instance:
-
-- **project** _string_
-
- The Google Cloud project ID that you want to use for the API calls.
- It uses the `GOOGLE_VERTEX_PROJECT` environment variable by default.
-
-- **location** _string_
-
- The Google Cloud location that you want to use for the API calls, e.g. `us-central1`.
- It uses the `GOOGLE_VERTEX_LOCATION` environment variable by default.
-
-- **googleAuthOptions** _object_
-
- Optional. The Authentication options used by the [Google Auth Library](https://github.com/googleapis/google-auth-library-nodejs/). See also the [GoogleAuthOptions](https://github.com/googleapis/google-auth-library-nodejs/blob/08978822e1b7b5961f0e355df51d738e012be392/src/auth/googleauth.ts#L87C18-L87C35) interface.
-
- - **authClient** _object_
- An `AuthClient` to use.
-
- - **keyFilename** _string_
- Path to a .json, .pem, or .p12 key file.
-
- - **keyFile** _string_
- Path to a .json, .pem, or .p12 key file.
-
- - **credentials** _object_
- Object containing client_email and private_key properties, or the external account client options.
-
- - **clientOptions** _object_
- Options object passed to the constructor of the client.
-
- - **scopes** _string | string[]_
- Required scopes for the desired API request.
-
- - **projectId** _string_
- Your project ID.
-
- - **universeDomain** _string_
- The default service domain for a given Cloud universe.
-
-- **headers** _Resolvable<Record<string, string | undefined>>_
-
- Headers to include in the requests. Can be provided in multiple formats:
-
- - A record of header key-value pairs: `Record`
- - A function that returns headers: `() => Record`
- - An async function that returns headers: `async () => Record`
- - A promise that resolves to headers: `Promise>`
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-
-#### Edge Runtime
-
-Edge runtimes (like Vercel Edge Functions and Cloudflare Workers) are lightweight JavaScript environments that run closer to users at the network edge.
-They only provide a subset of the standard Node.js APIs.
-For example, direct file system access is not available, and many Node.js-specific libraries
-(including the standard Google Auth library) are not compatible.
-
-The Edge runtime version of the Google Vertex Anthropic provider supports Google's [Application Default Credentials](https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#application-default-credentials) through environment variables. The values can be obtained from a json credentials file from the [Google Cloud Console](https://console.cloud.google.com/apis/credentials).
-
-For Edge runtimes, you can import the provider instance from `@ai-sdk/google-vertex/anthropic/edge`:
-
-```typescript
-import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic/edge';
-```
-
-To customize the setup, use `createVertexAnthropic` from the same module:
-
-```typescript
-import { createVertexAnthropic } from '@ai-sdk/google-vertex/anthropic/edge';
-
-const vertexAnthropic = createVertexAnthropic({
- project: 'my-project', // optional
- location: 'us-central1', // optional
-});
-```
-
-For Edge runtime authentication, set these environment variables from your Google Default Application Credentials JSON file:
-
-- `GOOGLE_CLIENT_EMAIL`
-- `GOOGLE_PRIVATE_KEY`
-- `GOOGLE_PRIVATE_KEY_ID` (optional)
-
-##### Optional Provider Settings
-
-You can use the following optional settings to customize the provider instance:
-
-- **project** _string_
-
- The Google Cloud project ID that you want to use for the API calls.
- It uses the `GOOGLE_VERTEX_PROJECT` environment variable by default.
-
-- **location** _string_
-
- The Google Cloud location that you want to use for the API calls, e.g. `us-central1`.
- It uses the `GOOGLE_VERTEX_LOCATION` environment variable by default.
-
-- **googleCredentials** _object_
-
- Optional. The credentials used by the Edge provider for authentication. These credentials are typically set through environment variables and are derived from a service account JSON file.
-
- - **clientEmail** _string_
- The client email from the service account JSON file. Defaults to the contents of the `GOOGLE_CLIENT_EMAIL` environment variable.
-
- - **privateKey** _string_
- The private key from the service account JSON file. Defaults to the contents of the `GOOGLE_PRIVATE_KEY` environment variable.
-
- - **privateKeyId** _string_
- The private key ID from the service account JSON file (optional). Defaults to the contents of the `GOOGLE_PRIVATE_KEY_ID` environment variable.
-
-- **headers** _Resolvable<Record<string, string | undefined>>_
-
- Headers to include in the requests. Can be provided in multiple formats:
-
- - A record of header key-value pairs: `Record`
- - A function that returns headers: `() => Record`
- - An async function that returns headers: `async () => Record`
- - A promise that resolves to headers: `Promise>`
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-### Language Models
-
-You can create models that call the [Anthropic Messages API](https://docs.anthropic.com/claude/reference/messages_post) using the provider instance.
-The first argument is the model id, e.g. `claude-3-haiku-20240307`.
-Some models have multi-modal capabilities.
-
-```ts
-const model = anthropic('claude-3-haiku-20240307');
-```
-
-You can use Anthropic language models to generate text with the `generateText` function:
-
-```ts
-import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: vertexAnthropic('claude-3-haiku-20240307'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Anthropic language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-
- The Anthropic API returns streaming tool calls all at once after a delay. This
- causes the `streamObject` function to generate the object fully after a delay
- instead of streaming it incrementally.
-
-
-The following optional provider options are available for Anthropic models:
-
-- `sendReasoning` _boolean_
-
- Optional. Include reasoning content in requests sent to the model. Defaults to `true`.
-
- If you are experiencing issues with the model handling requests involving
- reasoning content, you can set this to `false` to omit them from the request.
-
-- `thinking` _object_
-
- Optional. See [Reasoning section](#reasoning) for more details.
-
-### Reasoning
-
-Anthropic has reasoning support for the `claude-3-7-sonnet@20250219` model.
-
-You can enable it using the `thinking` provider option
-and specifying a thinking budget in tokens.
-
-```ts
-import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-import { generateText } from 'ai';
-
-const { text, reasoning, reasoningDetails } = await generateText({
- model: vertexAnthropic('claude-3-7-sonnet@20250219'),
- prompt: 'How many people will live in the world in 2040?',
- providerOptions: {
- anthropic: {
- thinking: { type: 'enabled', budgetTokens: 12000 },
- },
- },
-});
-
-console.log(reasoning); // reasoning text
-console.log(reasoningDetails); // reasoning details including redacted reasoning
-console.log(text); // text response
-```
-
-See [AI SDK UI: Chatbot](/docs/ai-sdk-ui/chatbot#reasoning) for more details
-on how to integrate reasoning into your chatbot.
-
-#### Cache Control
-
-
- Anthropic cache control is in a Pre-Generally Available (GA) state on Google
- Vertex. For more see [Google Vertex Anthropic cache control
- documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude-prompt-caching).
-
-
-In the messages and message parts, you can use the `providerOptions` property to set cache control breakpoints.
-You need to set the `anthropic` property in the `providerOptions` object to `{ cacheControl: { type: 'ephemeral' } }` to set a cache control breakpoint.
-
-The cache creation input tokens are then returned in the `providerMetadata` object
-for `generateText` and `generateObject`, again under the `anthropic` property.
-When you use `streamText` or `streamObject`, the response contains a promise
-that resolves to the metadata. Alternatively you can receive it in the
-`onFinish` callback.
-
-```ts highlight="8,18-20,29-30"
-import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
-import { generateText } from 'ai';
-
-const errorMessage = '... long error message ...';
-
-const result = await generateText({
- model: vertexAnthropic('claude-3-5-sonnet-20240620'),
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: 'You are a JavaScript expert.' },
- {
- type: 'text',
- text: `Error message: ${errorMessage}`,
- providerOptions: {
- anthropic: { cacheControl: { type: 'ephemeral' } },
- },
- },
- { type: 'text', text: 'Explain the error message.' },
- ],
- },
- ],
-});
-
-console.log(result.text);
-console.log(result.providerMetadata?.anthropic);
-// e.g. { cacheCreationInputTokens: 2118, cacheReadInputTokens: 0 }
-```
-
-You can also use cache control on system messages by providing multiple system messages at the head of your messages array:
-
-```ts highlight="3,9-11"
-const result = await generateText({
- model: vertexAnthropic('claude-3-5-sonnet-20240620'),
- messages: [
- {
- role: 'system',
- content: 'Cached system message part',
- providerOptions: {
- anthropic: { cacheControl: { type: 'ephemeral' } },
- },
- },
- {
- role: 'system',
- content: 'Uncached system message part',
- },
- {
- role: 'user',
- content: 'User prompt',
- },
- ],
-});
-```
-
-For more on prompt caching with Anthropic, see [Google Vertex AI's Claude prompt caching documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude-prompt-caching) and [Anthropic's Cache Control documentation](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching).
-
-### Computer Use
-
-Anthropic provides three built-in tools that can be used to interact with external systems:
-
-1. **Bash Tool**: Allows running bash commands.
-2. **Text Editor Tool**: Provides functionality for viewing and editing text files.
-3. **Computer Tool**: Enables control of keyboard and mouse actions on a computer.
-
-They are available via the `tools` property of the provider instance.
-
-For more background see [Anthropic's Computer Use documentation](https://docs.anthropic.com/en/docs/build-with-claude/computer-use).
-
-#### Bash Tool
-
-The Bash Tool allows running bash commands. Here's how to create and use it:
-
-```ts
-const bashTool = vertexAnthropic.tools.bash_20241022({
- execute: async ({ command, restart }) => {
- // Implement your bash command execution logic here
- // Return the result of the command execution
- },
-});
-```
-
-Parameters:
-
-- `command` (string): The bash command to run. Required unless the tool is being restarted.
-- `restart` (boolean, optional): Specifying true will restart this tool.
-
-#### Text Editor Tool
-
-The Text Editor Tool provides functionality for viewing and editing text files:
-
-```ts
-const textEditorTool = vertexAnthropic.tools.textEditor_20241022({
- execute: async ({
- command,
- path,
- file_text,
- insert_line,
- new_str,
- old_str,
- view_range,
- }) => {
- // Implement your text editing logic here
- // Return the result of the text editing operation
- },
-});
-```
-
-Parameters:
-
-- `command` ('view' | 'create' | 'str_replace' | 'insert' | 'undo_edit'): The command to run.
-- `path` (string): Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.
-- `file_text` (string, optional): Required for `create` command, with the content of the file to be created.
-- `insert_line` (number, optional): Required for `insert` command. The line number after which to insert the new string.
-- `new_str` (string, optional): New string for `str_replace` or `insert` commands.
-- `old_str` (string, optional): Required for `str_replace` command, containing the string to replace.
-- `view_range` (number[], optional): Optional for `view` command to specify line range to show.
-
-#### Computer Tool
-
-The Computer Tool enables control of keyboard and mouse actions on a computer:
-
-```ts
-const computerTool = vertexAnthropic.tools.computer_20241022({
- displayWidthPx: 1920,
- displayHeightPx: 1080,
- displayNumber: 0, // Optional, for X11 environments
-
- execute: async ({ action, coordinate, text }) => {
- // Implement your computer control logic here
- // Return the result of the action
-
- // Example code:
- switch (action) {
- case 'screenshot': {
- // multipart result:
- return {
- type: 'image',
- data: fs
- .readFileSync('./data/screenshot-editor.png')
- .toString('base64'),
- };
- }
- default: {
- console.log('Action:', action);
- console.log('Coordinate:', coordinate);
- console.log('Text:', text);
- return `executed ${action}`;
- }
- }
- },
-
- // map to tool result content for LLM consumption:
- toModelOutput(result) {
- return typeof result === 'string'
- ? [{ type: 'text', text: result }]
- : [{ type: 'image', data: result.data, mediaType: 'image/png' }];
- },
-});
-```
-
-Parameters:
-
-- `action` ('key' | 'type' | 'mouse_move' | 'left_click' | 'left_click_drag' | 'right_click' | 'middle_click' | 'double_click' | 'screenshot' | 'cursor_position'): The action to perform.
-- `coordinate` (number[], optional): Required for `mouse_move` and `left_click_drag` actions. Specifies the (x, y) coordinates.
-- `text` (string, optional): Required for `type` and `key` actions.
-
-These tools can be used in conjunction with the `claude-3-5-sonnet-v2@20241022` model to enable more complex interactions and tasks.
-
-### Model Capabilities
-
-The latest Anthropic model list on Vertex AI is available [here](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#model-list).
-See also [Anthropic Model Comparison](https://docs.anthropic.com/en/docs/about-claude/models#model-comparison).
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming | Computer Use |
-| ------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `claude-3-7-sonnet@20250219` | | | | | |
-| `claude-3-5-sonnet-v2@20241022` | | | | | |
-| `claude-3-5-sonnet@20240620` | | | | | |
-| `claude-3-5-haiku@20241022` | | | | | |
-| `claude-3-sonnet@20240229` | | | | | |
-| `claude-3-haiku@20240307` | | | | | |
-| `claude-3-opus@20240229` | | | | | |
-
-
- The table above lists popular models. You can also pass any available provider
- model ID as a string if needed.
-
-
----
-title: Rev.ai
-description: Learn how to use the Rev.ai provider for the AI SDK.
----
-
-# Rev.ai Provider
-
-The [Rev.ai](https://www.rev.ai/) provider contains language model support for the Rev.ai transcription API.
-
-## Setup
-
-The Rev.ai provider is available in the `@ai-sdk/revai` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `revai` from `@ai-sdk/revai`:
-
-```ts
-import { revai } from '@ai-sdk/revai';
-```
-
-If you need a customized setup, you can import `createRevai` from `@ai-sdk/revai` and create a provider instance with your settings:
-
-```ts
-import { createRevai } from '@ai-sdk/revai';
-
-const revai = createRevai({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the Rev.ai provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `REVAI_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Transcription Models
-
-You can create models that call the [Rev.ai transcription API](https://www.rev.ai/docs/api/transcription)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `machine`.
-
-```ts
-const model = revai.transcription('machine');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the input language in ISO-639-1 (e.g. `en`) format can sometimes improve transcription performance if known beforehand.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { revai } from '@ai-sdk/revai';
-import { readFile } from 'fs/promises';
-
-const result = await transcribe({
- model: revai.transcription('machine'),
- audio: await readFile('audio.mp3'),
- providerOptions: { revai: { language: 'en' } },
-});
-```
-
-The following provider options are available:
-
-- **metadata** _string_
-
- Optional metadata that was provided during job submission.
-
-- **notification_config** _object_
-
- Optional configuration for a callback url to invoke when processing is complete.
-
- - **url** _string_ - Callback url to invoke when processing is complete.
- - **auth_headers** _object_ - Optional authorization headers, if needed to invoke the callback.
- - **Authorization** _string_ - Authorization header value.
-
-- **delete_after_seconds** _integer_
-
- Amount of time after job completion when job is auto-deleted.
-
-- **verbatim** _boolean_
-
- Configures the transcriber to transcribe every syllable, including all false starts and disfluencies.
-
-- **rush** _boolean_
-
- [HIPAA Unsupported] Only available for human transcriber option. When set to true, your job is given higher priority.
-
-- **skip_diarization** _boolean_
-
- Specify if speaker diarization will be skipped by the speech engine.
-
-- **skip_postprocessing** _boolean_
-
- Only available for English and Spanish languages. User-supplied preference on whether to skip post-processing operations.
-
-- **skip_punctuation** _boolean_
-
- Specify if "punct" type elements will be skipped by the speech engine.
-
-- **remove_disfluencies** _boolean_
-
- When set to true, disfluencies (like 'ums' and 'uhs') will not appear in the transcript.
-
-- **remove_atmospherics** _boolean_
-
- When set to true, atmospherics (like ``, ``) will not appear in the transcript.
-
-- **filter_profanity** _boolean_
-
- When enabled, profanities will be filtered by replacing characters with asterisks except for the first and last.
-
-- **speaker_channels_count** _integer_
-
- Only available for English, Spanish and French languages. Specify the total number of unique speaker channels in the audio.
-
-- **speakers_count** _integer_
-
- Only available for English, Spanish and French languages. Specify the total number of unique speakers in the audio.
-
-- **diarization_type** _string_
-
- Specify diarization type. Possible values: "standard" (default), "premium".
-
-- **custom_vocabulary_id** _string_
-
- Supply the id of a pre-completed custom vocabulary submitted through the Custom Vocabularies API.
-
-- **custom_vocabularies** _Array_
-
- Specify a collection of custom vocabulary to be used for this job.
-
-- **strict_custom_vocabulary** _boolean_
-
- If true, only exact phrases will be used as custom vocabulary.
-
-- **summarization_config** _object_
-
- Specify summarization options.
-
- - **model** _string_ - Model type for summarization. Possible values: "standard" (default), "premium".
- - **type** _string_ - Summarization formatting type. Possible values: "paragraph" (default), "bullets".
- - **prompt** _string_ - Custom prompt for flexible summaries (mutually exclusive with type).
-
-- **translation_config** _object_
-
- Specify translation options.
-
- - **target_languages** _Array_ - Array of target languages for translation.
- - **model** _string_ - Model type for translation. Possible values: "standard" (default), "premium".
-
-- **language** _string_
-
- Language is provided as a ISO 639-1 language code. Default is "en".
-
-- **forced_alignment** _boolean_
-
- When enabled, provides improved accuracy for per-word timestamps for a transcript.
- Default is `false`.
-
- Currently supported languages:
-
- - English (en, en-us, en-gb)
- - French (fr)
- - Italian (it)
- - German (de)
- - Spanish (es)
-
- Note: This option is not available in low-cost environment.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ---------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `machine` | | | | |
-| `low_cost` | | | | |
-| `fusion` | | | | |
-
----
-title: Baseten
-description: Learn how to use Baseten models with the AI SDK.
----
-
-# Baseten Provider
-
-[Baseten](https://baseten.co/) is an inference platform for serving frontier, enterprise-grade opensource AI models via their [API](https://docs.baseten.co/overview).
-
-## Setup
-
-The Baseten provider is available via the `@ai-sdk/baseten` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `baseten` from `@ai-sdk/baseten`:
-
-```ts
-import { baseten } from '@ai-sdk/baseten';
-```
-
-If you need a customized setup, you can import `createBaseten` from `@ai-sdk/baseten`
-and create a provider instance with your settings:
-
-```ts
-import { createBaseten } from '@ai-sdk/baseten';
-
-const baseten = createBaseten({
- apiKey: process.env.BASETEN_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Baseten provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://inference.baseten.co/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `BASETEN_API_KEY` environment variable. It is recommended you set the environment variable using `export` so you do not need to include the field everytime.
- You can grab your Baseten API Key [here](https://app.baseten.co/settings/api_keys)
-
-- **modelURL** _string_
-
- Custom model URL for specific models (chat or embeddings). If not provided,
- the default Model APIs will be used.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Model APIs
-
-You can select [Baseten models](https://www.baseten.co/products/model-apis/) using a provider instance.
-The first argument is the model id, e.g. `'moonshotai/Kimi-K2-Instruct-0905'`: The complete supported models under Model APIs can be found [here](https://docs.baseten.co/development/model-apis/overview#supported-models).
-
-```ts
-const model = baseten('moonshotai/Kimi-K2-Instruct-0905');
-```
-
-### Example
-
-You can use Baseten language models to generate text with the `generateText` function:
-
-```ts
-import { baseten } from '@ai-sdk/baseten';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: baseten('moonshotai/Kimi-K2-Instruct-0905'),
- prompt: 'What is the meaning of life? Answer in one sentence.',
-});
-```
-
-Baseten language models can also be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-## Dedicated Models
-
-Baseten supports dedicated model URLs for both chat and embedding models. You have to specify a `modelURL` when creating the provider:
-
-### OpenAI-Compatible Endpoints (`/sync/v1`)
-
-For models deployed with Baseten's OpenAI-compatible endpoints:
-
-```ts
-import { createBaseten } from '@ai-sdk/baseten';
-
-const baseten = createBaseten({
- modelURL: 'https://model-{MODEL_ID}.api.baseten.co/sync/v1',
-});
-// No modelId is needed because we specified modelURL
-const model = baseten();
-const { text } = await generateText({
- model: model,
- prompt: 'Say hello from a Baseten chat model!',
-});
-```
-
-### `/predict` Endpoints
-
-`/predict` endpoints are currently NOT supported for chat models. You must use `/sync/v1` endpoints for chat functionality.
-
-## Embedding Models
-
-You can create models that call the Baseten embeddings API using the `.textEmbeddingModel()` factory method. The Baseten provider uses the high-performance `@basetenlabs/performance-client` for optimal embedding performance.
-
-
- **Important:** Embedding models require a dedicated deployment with a custom
- `modelURL`. Unlike chat models, embeddings cannot use Baseten's default Model
- APIs and must specify a dedicated model endpoint.
-
-
-```ts
-import { createBaseten } from '@ai-sdk/baseten';
-import { embed, embedMany } from 'ai';
-
-const baseten = createBaseten({
- modelURL: 'https://model-{MODEL_ID}.api.baseten.co/sync',
-});
-
-const embeddingModel = baseten.textEmbeddingModel();
-
-// Single embedding
-const { embedding } = await embed({
- model: embeddingModel,
- value: 'sunny day at the beach',
-});
-
-// Batch embeddings
-const { embeddings } = await embedMany({
- model: embeddingModel,
- values: [
- 'sunny day at the beach',
- 'rainy afternoon in the city',
- 'snowy mountain peak',
- ],
-});
-```
-
-### Endpoint Support for Embeddings
-
-**Supported:**
-
-- `/sync` endpoints (Performance Client automatically adds `/v1/embeddings`)
-- `/sync/v1` endpoints (automatically strips `/v1` before passing to Performance Client)
-
-**Not Supported:**
-
-- `/predict` endpoints (not compatible with Performance Client)
-
-### Performance Features
-
-The embedding implementation includes:
-
-- **High-performance client**: Uses `@basetenlabs/performance-client` for optimal performance
-- **Automatic batching**: Efficiently handles multiple texts in a single request
-- **Connection reuse**: Performance Client is created once and reused for all requests
-- **Built-in retries**: Automatic retry logic for failed requests
-
-## Error Handling
-
-The Baseten provider includes built-in error handling for common API errors:
-
-```ts
-import { baseten } from '@ai-sdk/baseten';
-import { generateText } from 'ai';
-
-try {
- const { text } = await generateText({
- model: baseten('moonshotai/Kimi-K2-Instruct-0905'),
- prompt: 'Hello, world!',
- });
-} catch (error) {
- console.error('Baseten API error:', error.message);
-}
-```
-
-### Common Error Scenarios
-
-```ts
-// Embeddings require a modelURL
-try {
- baseten.textEmbeddingModel();
-} catch (error) {
- // Error: "No model URL provided for embeddings. Please set modelURL option for embeddings."
-}
-
-// /predict endpoints are not supported for chat models
-try {
- const baseten = createBaseten({
- modelURL:
- 'https://model-{MODEL_ID}.api.baseten.co/environments/production/predict',
- });
- baseten(); // This will throw an error
-} catch (error) {
- // Error: "Not supported. You must use a /sync/v1 endpoint for chat models."
-}
-
-// /sync/v1 endpoints are now supported for embeddings
-const baseten = createBaseten({
- modelURL:
- 'https://model-{MODEL_ID}.api.baseten.co/environments/production/sync/v1',
-});
-const embeddingModel = baseten.textEmbeddingModel(); // This works fine!
-
-// /predict endpoints are not supported for embeddings
-try {
- const baseten = createBaseten({
- modelURL:
- 'https://model-{MODEL_ID}.api.baseten.co/environments/production/predict',
- });
- baseten.textEmbeddingModel(); // This will throw an error
-} catch (error) {
- // Error: "Not supported. You must use a /sync or /sync/v1 endpoint for embeddings."
-}
-
-// Image models are not supported
-try {
- baseten.imageModel('test-model');
-} catch (error) {
- // Error: NoSuchModelError for imageModel
-}
-```
-
-
- For more information about Baseten models and deployment options, see the
- [Baseten documentation](https://docs.baseten.co/).
-
-
----
-title: Hugging Face
-description: Learn how to use Hugging Face Provider.
----
-
-# Hugging Face Provider
-
-The [Hugging Face](https://huggingface.co/) provider offers access to thousands of language models through [Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers/index), including models from Meta, DeepSeek, Qwen, and more.
-
-API keys can be obtained from [Hugging Face Settings](https://huggingface.co/settings/tokens).
-
-## Setup
-
-The Hugging Face provider is available via the `@ai-sdk/huggingface` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `huggingface` from `@ai-sdk/huggingface`:
-
-```ts
-import { huggingface } from '@ai-sdk/huggingface';
-```
-
-For custom configuration, you can import `createHuggingFace` and create a provider instance with your settings:
-
-```ts
-import { createHuggingFace } from '@ai-sdk/huggingface';
-
-const huggingface = createHuggingFace({
- apiKey: process.env.HUGGINGFACE_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Hugging Face provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://router.huggingface.co/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `HUGGINGFACE_API_KEY` environment variable. You can get your API key
- from [Hugging Face Settings](https://huggingface.co/settings/tokens).
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Language Models
-
-You can create language models using a provider instance:
-
-```ts
-import { huggingface } from '@ai-sdk/huggingface';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: huggingface('deepseek-ai/DeepSeek-V3-0324'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-You can also use the `.responses()` or `.languageModel()` factory methods:
-
-```ts
-const model = huggingface.responses('deepseek-ai/DeepSeek-V3-0324');
-// or
-const model = huggingface.languageModel('moonshotai/Kimi-K2-Instruct');
-```
-
-Hugging Face language models can be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-You can explore the latest and trending models with their capabilities, context size, throughput and pricing on the [Hugging Face Inference Models](https://huggingface.co/inference/models) page.
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `meta-llama/Llama-3.1-8B-Instruct` | | | | |
-| `meta-llama/Llama-3.1-70B-Instruct` | | | | |
-| `meta-llama/Llama-3.3-70B-Instruct` | | | | |
-| `meta-llama/Llama-4-Scout-17B-16E-Instruct` | | | | |
-| `deepseek-ai/DeepSeek-V3-0324` | | | | |
-| `deepseek-ai/DeepSeek-R1` | | | | |
-| `deepseek-ai/DeepSeek-R1-Distill-Llama-70B` | | | | |
-| `Qwen/Qwen3-235B-A22B-Instruct-2507` | | | | |
-| `Qwen/Qwen3-Coder-480B-A35B-Instruct` | | | | |
-| `Qwen/Qwen2.5-VL-7B-Instruct` | | | | |
-| `google/gemma-3-27b-it` | | | | |
-| `moonshotai/Kimi-K2-Instruct` | | | | |
-
-
- The capabilities depend on the specific model you're using. Check the model
- documentation on Hugging Face Hub for detailed information about each model's
- features.
-
-
----
-title: Mistral AI
-description: Learn how to use Mistral.
----
-
-# Mistral AI Provider
-
-The [Mistral AI](https://mistral.ai/) provider contains language model support for the Mistral chat API.
-
-## Setup
-
-The Mistral provider is available in the `@ai-sdk/mistral` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `mistral` from `@ai-sdk/mistral`:
-
-```ts
-import { mistral } from '@ai-sdk/mistral';
-```
-
-If you need a customized setup, you can import `createMistral` from `@ai-sdk/mistral`
-and create a provider instance with your settings:
-
-```ts
-import { createMistral } from '@ai-sdk/mistral';
-
-const mistral = createMistral({
- // custom settings
-});
-```
-
-You can use the following optional settings to customize the Mistral provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.mistral.ai/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `MISTRAL_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create models that call the [Mistral chat API](https://docs.mistral.ai/api/#operation/createChatCompletion) using a provider instance.
-The first argument is the model id, e.g. `mistral-large-latest`.
-Some Mistral chat models support tool calls.
-
-```ts
-const model = mistral('mistral-large-latest');
-```
-
-Mistral chat models also support additional model settings that are not part of the [standard call settings](/docs/ai-sdk-core/settings).
-You can pass them as an options argument and utilize `MistralLanguageModelOptions` for typing:
-
-```ts
-import { mistral, type MistralLanguageModelOptions } from '@ai-sdk/mistral';
-const model = mistral('mistral-large-latest');
-
-await generateText({
- model,
- providerOptions: {
- mistral: {
- safePrompt: true, // optional safety prompt injection
- parallelToolCalls: false, // disable parallel tool calls (one tool per response)
- } satisfies MistralLanguageModelOptions,
- },
-});
-```
-
-The following optional provider options are available for Mistral models:
-
-- **safePrompt** _boolean_
-
- Whether to inject a safety prompt before all conversations.
-
- Defaults to `false`.
-
-- **documentImageLimit** _number_
-
- Maximum number of images to process in a document.
-
-- **documentPageLimit** _number_
-
- Maximum number of pages to process in a document.
-
-- **strictJsonSchema** _boolean_
-
- Whether to use strict JSON schema validation for structured outputs. Only applies when a schema is provided and only sets the [`strict` flag](https://docs.mistral.ai/api/#tag/chat/operation/chat_completion_v1_chat_completions_post) in addition to using [Custom Structured Outputs](https://docs.mistral.ai/capabilities/structured-output/custom_structured_output/), which is used by default if a schema is provided.
-
- Defaults to `false`.
-
-- **structuredOutputs** _boolean_
-
- Whether to use [structured outputs](#structured-outputs). When enabled, tool calls and object generation will be strict and follow the provided schema.
-
- Defaults to `true`.
-
-- **parallelToolCalls** _boolean_
-
- Whether to enable parallel function calling during tool use. When set to false, the model will use at most one tool per response.
-
- Defaults to `true`.
-
-### Document OCR
-
-Mistral chat models support document OCR for PDF files.
-You can optionally set image and page limits using the provider options.
-
-```ts
-const result = await generateText({
- model: mistral('mistral-small-latest'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is an embedding model according to this document?',
- },
- {
- type: 'file',
- data: new URL(
- 'https://github.com/vercel/ai/blob/main/examples/ai-core/data/ai.pdf?raw=true',
- ),
- mediaType: 'application/pdf',
- },
- ],
- },
- ],
- // optional settings:
- providerOptions: {
- mistral: {
- documentImageLimit: 8,
- documentPageLimit: 64,
- },
- },
-});
-```
-
-### Reasoning Models
-
-Mistral offers reasoning models that provide step-by-step thinking capabilities:
-
-- **magistral-small-2506**: Smaller reasoning model for efficient step-by-step thinking
-- **magistral-medium-2506**: More powerful reasoning model balancing performance and cost
-
-These models return content that includes `...` tags containing the reasoning process. To properly extract and separate the reasoning from the final answer, use the [extract reasoning middleware](/docs/reference/ai-sdk-core/extract-reasoning-middleware):
-
-```ts
-import { mistral } from '@ai-sdk/mistral';
-import {
- extractReasoningMiddleware,
- generateText,
- wrapLanguageModel,
-} from 'ai';
-
-const result = await generateText({
- model: wrapLanguageModel({
- model: mistral('magistral-small-2506'),
- middleware: extractReasoningMiddleware({
- tagName: 'think',
- }),
- }),
- prompt: 'What is 15 * 24?',
-});
-
-console.log('REASONING:', result.reasoningText);
-// Output: "Let me calculate this step by step..."
-
-console.log('ANSWER:', result.text);
-// Output: "360"
-```
-
-The middleware automatically parses the `` tags and provides separate `reasoningText` and `text` properties in the result.
-
-### Example
-
-You can use Mistral language models to generate text with the `generateText` function:
-
-```ts
-import { mistral } from '@ai-sdk/mistral';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: mistral('mistral-large-latest'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Mistral language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-#### Structured Outputs
-
-Mistral chat models support structured outputs using JSON Schema. You can use `generateObject` or `streamObject`
-with Zod, Valibot, or raw JSON Schema. The SDK sends your schema via Mistral's `response_format: { type: 'json_schema' }`.
-
-```ts
-import { mistral } from '@ai-sdk/mistral';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-
-const result = await generateObject({
- model: mistral('mistral-large-latest'),
- schema: z.object({
- recipe: z.object({
- name: z.string(),
- ingredients: z.array(z.string()),
- instructions: z.array(z.string()),
- }),
- }),
- prompt: 'Generate a simple pasta recipe.',
-});
-
-console.log(JSON.stringify(result.object, null, 2));
-```
-
-You can enable strict JSON Schema validation using a provider option:
-
-```ts highlight="7-11"
-import { mistral } from '@ai-sdk/mistral';
-import { generateObject } from 'ai';
-import { z } from 'zod';
-
-const result = await generateObject({
- model: mistral('mistral-large-latest'),
- providerOptions: {
- mistral: {
- strictJsonSchema: true, // reject outputs that don't strictly match the schema
- },
- },
- schema: z.object({
- title: z.string(),
- items: z.array(z.object({ id: z.string(), qty: z.number().int().min(1) })),
- }),
- prompt: 'Generate a small shopping list.',
-});
-```
-
-
- When using structured outputs, the SDK no longer injects an extra "answer with
- JSON" instruction. It relies on Mistral's native `json_schema`/`json_object`
- response formats instead. You can customize the schema name/description via
- the standard structured-output APIs.
-
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ----------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `pixtral-large-latest` | | | | |
-| `mistral-large-latest` | | | | |
-| `mistral-medium-latest` | | | | |
-| `mistral-medium-2505` | | | | |
-| `mistral-small-latest` | | | | |
-| `magistral-small-2506` | | | | |
-| `magistral-medium-2506` | | | | |
-| `ministral-3b-latest` | | | | |
-| `ministral-8b-latest` | | | | |
-| `pixtral-12b-2409` | | | | |
-| `open-mistral-7b` | | | | |
-| `open-mixtral-8x7b` | | | | |
-| `open-mixtral-8x22b` | | | | |
-
-
- The table above lists popular models. Please see the [Mistral
- docs](https://docs.mistral.ai/getting-started/models/models_overview/) for a
- full list of available models. The table above lists popular models. You can
- also pass any available provider model ID as a string if needed.
-
-
-## Embedding Models
-
-You can create models that call the [Mistral embeddings API](https://docs.mistral.ai/api/#operation/createEmbedding)
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = mistral.textEmbedding('mistral-embed');
-```
-
-You can use Mistral embedding models to generate embeddings with the `embed` function:
-
-```ts
-import { mistral } from '@ai-sdk/mistral';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: mistral.textEmbedding('mistral-embed'),
- value: 'sunny day at the beach',
-});
-```
-
-### Model Capabilities
-
-| Model | Default Dimensions |
-| --------------- | ------------------ |
-| `mistral-embed` | 1024 |
-
----
-title: Together.ai
-description: Learn how to use Together.ai's models with the AI SDK.
----
-
-# Together.ai Provider
-
-The [Together.ai](https://together.ai) provider contains support for 200+ open-source models through the [Together.ai API](https://docs.together.ai/reference).
-
-## Setup
-
-The Together.ai provider is available via the `@ai-sdk/togetherai` module. You can
-install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `togetherai` from `@ai-sdk/togetherai`:
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-```
-
-If you need a customized setup, you can import `createTogetherAI` from `@ai-sdk/togetherai`
-and create a provider instance with your settings:
-
-```ts
-import { createTogetherAI } from '@ai-sdk/togetherai';
-
-const togetherai = createTogetherAI({
- apiKey: process.env.TOGETHER_AI_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Together.ai provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.together.xyz/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `TOGETHER_AI_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create [Together.ai models](https://docs.together.ai/docs/serverless-models) using a provider instance. The first argument is the model id, e.g. `google/gemma-2-9b-it`.
-
-```ts
-const model = togetherai('google/gemma-2-9b-it');
-```
-
-### Reasoning Models
-
-Together.ai exposes the thinking of `deepseek-ai/DeepSeek-R1` in the generated text using the `` tag.
-You can use the `extractReasoningMiddleware` to extract this reasoning and expose it as a `reasoning` property on the result:
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { wrapLanguageModel, extractReasoningMiddleware } from 'ai';
-
-const enhancedModel = wrapLanguageModel({
- model: togetherai('deepseek-ai/DeepSeek-R1'),
- middleware: extractReasoningMiddleware({ tagName: 'think' }),
-});
-```
-
-You can then use that enhanced model in functions like `generateText` and `streamText`.
-
-### Example
-
-You can use Together.ai language models to generate text with the `generateText` function:
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: togetherai('meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Together.ai language models can also be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-The Together.ai provider also supports [completion models](https://docs.together.ai/docs/serverless-models#language-models) via (following the above example code) `togetherai.completion()` and [embedding models](https://docs.together.ai/docs/serverless-models#embedding-models) via `togetherai.textEmbedding()`.
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ---------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `meta-llama/Meta-Llama-3.3-70B-Instruct-Turbo` | | | | |
-| `meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo` | | | | |
-| `mistralai/Mixtral-8x22B-Instruct-v0.1` | | | | |
-| `mistralai/Mistral-7B-Instruct-v0.3` | | | | |
-| `deepseek-ai/DeepSeek-V3` | | | | |
-| `google/gemma-2b-it` | | | | |
-| `Qwen/Qwen2.5-72B-Instruct-Turbo` | | | | |
-| `databricks/dbrx-instruct` | | | | |
-
-
- The table above lists popular models. Please see the [Together.ai
- docs](https://docs.together.ai/docs/serverless-models) for a full list of
- available models. You can also pass any available provider model ID as a
- string if needed.
-
-
-## Image Models
-
-You can create Together.ai image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { images } = await generateImage({
- model: togetherai.image('black-forest-labs/FLUX.1-dev'),
- prompt: 'A delighted resplendent quetzal mid flight amidst raindrops',
-});
-```
-
-You can pass optional provider-specific request parameters using the `providerOptions` argument.
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { images } = await generateImage({
- model: togetherai.image('black-forest-labs/FLUX.1-dev'),
- prompt: 'A delighted resplendent quetzal mid flight amidst raindrops',
- size: '512x512',
- // Optional additional provider-specific request parameters
- providerOptions: {
- togetherai: {
- steps: 40,
- },
- },
-});
-```
-
-For a complete list of available provider-specific options, see the [Together.ai Image Generation API Reference](https://docs.together.ai/reference/post_images-generations).
-
-### Model Capabilities
-
-Together.ai image models support various image dimensions that vary by model. Common sizes include 512x512, 768x768, and 1024x1024, with some models supporting up to 1792x1792. The default size is 1024x1024.
-
-| Available Models |
-| ------------------------------------------ |
-| `stabilityai/stable-diffusion-xl-base-1.0` |
-| `black-forest-labs/FLUX.1-dev` |
-| `black-forest-labs/FLUX.1-dev-lora` |
-| `black-forest-labs/FLUX.1-schnell` |
-| `black-forest-labs/FLUX.1-canny` |
-| `black-forest-labs/FLUX.1-depth` |
-| `black-forest-labs/FLUX.1-redux` |
-| `black-forest-labs/FLUX.1.1-pro` |
-| `black-forest-labs/FLUX.1-pro` |
-| `black-forest-labs/FLUX.1-schnell-Free` |
-
-
- Please see the [Together.ai models
- page](https://docs.together.ai/docs/serverless-models#image-models) for a full
- list of available image models and their capabilities.
-
-
-## Embedding Models
-
-You can create Together.ai embedding models using the `.textEmbedding()` factory method.
-For more on embedding models with the AI SDK see [embed()](/docs/reference/ai-sdk-core/embed).
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: togetherai.textEmbedding('togethercomputer/m2-bert-80M-2k-retrieval'),
- value: 'sunny day at the beach',
-});
-```
-
-### Model Capabilities
-
-| Model | Dimensions | Max Tokens |
-| ------------------------------------------------ | ---------- | ---------- |
-| `togethercomputer/m2-bert-80M-2k-retrieval` | 768 | 2048 |
-| `togethercomputer/m2-bert-80M-8k-retrieval` | 768 | 8192 |
-| `togethercomputer/m2-bert-80M-32k-retrieval` | 768 | 32768 |
-| `WhereIsAI/UAE-Large-V1` | 1024 | 512 |
-| `BAAI/bge-large-en-v1.5` | 1024 | 512 |
-| `BAAI/bge-base-en-v1.5` | 768 | 512 |
-| `sentence-transformers/msmarco-bert-base-dot-v5` | 768 | 512 |
-| `bert-base-uncased` | 768 | 512 |
-
-
- For a complete list of available embedding models, see the [Together.ai models
- page](https://docs.together.ai/docs/serverless-models#embedding-models).
-
-
-## Reranking Models
-
-You can create Together.ai reranking models using the `.reranking()` factory method.
-For more on reranking with the AI SDK see [rerank()](/docs/reference/ai-sdk-core/rerank).
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { rerank } from 'ai';
-
-const documents = [
- 'sunny day at the beach',
- 'rainy afternoon in the city',
- 'snowy night in the mountains',
-];
-
-const { ranking } = await rerank({
- model: togetherai.reranking('Salesforce/Llama-Rank-v1'),
- documents,
- query: 'talk about rain',
- topN: 2,
-});
-
-console.log(ranking);
-// [
-// { originalIndex: 1, score: 0.9, document: 'rainy afternoon in the city' },
-// { originalIndex: 0, score: 0.3, document: 'sunny day at the beach' }
-// ]
-```
-
-Together.ai reranking models support additional provider options for object documents. You can specify which fields to use for ranking:
-
-```ts
-import { togetherai } from '@ai-sdk/togetherai';
-import { rerank } from 'ai';
-
-const documents = [
- {
- from: 'Paul Doe',
- subject: 'Follow-up',
- text: 'We are happy to give you a discount of 20%.',
- },
- {
- from: 'John McGill',
- subject: 'Missing Info',
- text: 'Here is the pricing from Oracle: $5000/month',
- },
-];
-
-const { ranking } = await rerank({
- model: togetherai.reranking('Salesforce/Llama-Rank-v1'),
- documents,
- query: 'Which pricing did we get from Oracle?',
- providerOptions: {
- togetherai: {
- rankFields: ['from', 'subject', 'text'], // Specify which fields to rank by
- },
- },
-});
-```
-
-The following provider options are available:
-
-- **rankFields** _string[]_
-
- Array of field names to use for ranking when documents are JSON objects. If not specified, all fields are used.
-
-### Model Capabilities
-
-| Model |
-| ------------------------------------- |
-| `Salesforce/Llama-Rank-v1` |
-| `mixedbread-ai/Mxbai-Rerank-Large-V2` |
-
----
-title: Cohere
-description: Learn how to use the Cohere provider for the AI SDK.
----
-
-# Cohere Provider
-
-The [Cohere](https://cohere.com/) provider contains language and embedding model support for the Cohere chat API.
-
-## Setup
-
-The Cohere provider is available in the `@ai-sdk/cohere` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `cohere` from `@ai-sdk/cohere`:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-```
-
-If you need a customized setup, you can import `createCohere` from `@ai-sdk/cohere`
-and create a provider instance with your settings:
-
-```ts
-import { createCohere } from '@ai-sdk/cohere';
-
-const cohere = createCohere({
- // custom settings
-});
-```
-
-You can use the following optional settings to customize the Cohere provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.cohere.com/v2`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `COHERE_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Language Models
-
-You can create models that call the [Cohere chat API](https://docs.cohere.com/v2/docs/chat-api) using a provider instance.
-The first argument is the model id, e.g. `command-r-plus`.
-Some Cohere chat models support tool calls.
-
-```ts
-const model = cohere('command-r-plus');
-```
-
-### Example
-
-You can use Cohere language models to generate text with the `generateText` function:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: cohere('command-r-plus'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Cohere language models can also be used in the `streamText`, `generateObject`, and `streamObject` functions
-(see [AI SDK Core](/docs/ai-sdk-core).
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ----------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `command-a-03-2025` | | | | |
-| `command-a-reasoning-08-2025` | | | | |
-| `command-r7b-12-2024` | | | | |
-| `command-r-plus-04-2024` | | | | |
-| `command-r-plus` | | | | |
-| `command-r-08-2024` | | | | |
-| `command-r-03-2024` | | | | |
-| `command-r` | | | | |
-| `command` | | | | |
-| `command-nightly` | | | | |
-| `command-light` | | | | |
-| `command-light-nightly` | | | | |
-
-
- The table above lists popular models. Please see the [Cohere
- docs](https://docs.cohere.com/v2/docs/models#command) for a full list of
- available models. You can also pass any available provider model ID as a
- string if needed.
-
-
-#### Reasoning
-
-Cohere has introduced reasoning with the `command-a-reasoning-08-2025` model. You can learn more at https://docs.cohere.com/docs/reasoning.
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { generateText } from 'ai';
-
-async function main() {
- const { text, reasoning } = await generateText({
- model: cohere('command-a-reasoning-08-2025'),
- prompt:
- "Alice has 3 brothers and she also has 2 sisters. How many sisters does Alice's brother have?",
- // optional: reasoning options
- providerOptions: {
- cohere: {
- thinking: {
- type: 'enabled',
- tokenBudget: 100,
- },
- },
- },
- });
-
- console.log(reasoning);
- console.log(text);
-}
-
-main().catch(console.error);
-```
-
-## Embedding Models
-
-You can create models that call the [Cohere embed API](https://docs.cohere.com/v2/reference/embed)
-using the `.textEmbedding()` factory method.
-
-```ts
-const model = cohere.textEmbedding('embed-english-v3.0');
-```
-
-You can use Cohere embedding models to generate embeddings with the `embed` function:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: cohere.textEmbedding('embed-english-v3.0'),
- value: 'sunny day at the beach',
- providerOptions: {
- cohere: {
- inputType: 'search_document',
- },
- },
-});
-```
-
-Cohere embedding models support additional provider options that can be passed via `providerOptions.cohere`:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: cohere.textEmbedding('embed-english-v3.0'),
- value: 'sunny day at the beach',
- providerOptions: {
- cohere: {
- inputType: 'search_document',
- truncate: 'END',
- },
- },
-});
-```
-
-The following provider options are available:
-
-- **inputType** _'search_document' | 'search_query' | 'classification' | 'clustering'_
-
- Specifies the type of input passed to the model. Default is `search_query`.
-
- - `search_document`: Used for embeddings stored in a vector database for search use-cases.
- - `search_query`: Used for embeddings of search queries run against a vector DB to find relevant documents.
- - `classification`: Used for embeddings passed through a text classifier.
- - `clustering`: Used for embeddings run through a clustering algorithm.
-
-- **truncate** _'NONE' | 'START' | 'END'_
-
- Specifies how the API will handle inputs longer than the maximum token length.
- Default is `END`.
-
- - `NONE`: If selected, when the input exceeds the maximum input token length will return an error.
- - `START`: Will discard the start of the input until the remaining input is exactly the maximum input token length for the model.
- - `END`: Will discard the end of the input until the remaining input is exactly the maximum input token length for the model.
-
-### Model Capabilities
-
-| Model | Embedding Dimensions |
-| ------------------------------- | -------------------- |
-| `embed-english-v3.0` | 1024 |
-| `embed-multilingual-v3.0` | 1024 |
-| `embed-english-light-v3.0` | 384 |
-| `embed-multilingual-light-v3.0` | 384 |
-| `embed-english-v2.0` | 4096 |
-| `embed-english-light-v2.0` | 1024 |
-| `embed-multilingual-v2.0` | 768 |
-
-## Reranking Models
-
-You can create models that call the [Cohere rerank API](https://docs.cohere.com/v2/reference/rerank)
-using the `.reranking()` factory method.
-
-```ts
-const model = cohere.reranking('rerank-v3.5');
-```
-
-You can use Cohere reranking models to rerank documents with the `rerank` function:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { rerank } from 'ai';
-
-const documents = [
- 'sunny day at the beach',
- 'rainy afternoon in the city',
- 'snowy night in the mountains',
-];
-
-const { ranking } = await rerank({
- model: cohere.reranking('rerank-v3.5'),
- documents,
- query: 'talk about rain',
- topN: 2,
-});
-
-console.log(ranking);
-// [
-// { originalIndex: 1, score: 0.9, document: 'rainy afternoon in the city' },
-// { originalIndex: 0, score: 0.3, document: 'sunny day at the beach' }
-// ]
-```
-
-Cohere reranking models support additional provider options that can be passed via `providerOptions.cohere`:
-
-```ts
-import { cohere } from '@ai-sdk/cohere';
-import { rerank } from 'ai';
-
-const { ranking } = await rerank({
- model: cohere.reranking('rerank-v3.5'),
- documents: ['sunny day at the beach', 'rainy afternoon in the city'],
- query: 'talk about rain',
- providerOptions: {
- cohere: {
- maxTokensPerDoc: 1000,
- priority: 1,
- },
- },
-});
-```
-
-The following provider options are available:
-
-- **maxTokensPerDoc** _number_
-
- Maximum number of tokens per document. Default is `4096`.
-
-- **priority** _number_
-
- Priority of the request. Default is `0`.
-
-### Model Capabilities
-
-| Model |
-| -------------------------- |
-| `rerank-v3.5` |
-| `rerank-english-v3.0` |
-| `rerank-multilingual-v3.0` |
-
----
-title: Fireworks
-description: Learn how to use Fireworks models with the AI SDK.
----
-
-# Fireworks Provider
-
-[Fireworks](https://fireworks.ai/) is a platform for running and testing LLMs through their [API](https://readme.fireworks.ai/).
-
-## Setup
-
-The Fireworks provider is available via the `@ai-sdk/fireworks` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `fireworks` from `@ai-sdk/fireworks`:
-
-```ts
-import { fireworks } from '@ai-sdk/fireworks';
-```
-
-If you need a customized setup, you can import `createFireworks` from `@ai-sdk/fireworks`
-and create a provider instance with your settings:
-
-```ts
-import { createFireworks } from '@ai-sdk/fireworks';
-
-const fireworks = createFireworks({
- apiKey: process.env.FIREWORKS_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Fireworks provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.fireworks.ai/inference/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `FIREWORKS_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Language Models
-
-You can create [Fireworks models](https://fireworks.ai/models) using a provider instance.
-The first argument is the model id, e.g. `accounts/fireworks/models/firefunction-v1`:
-
-```ts
-const model = fireworks('accounts/fireworks/models/firefunction-v1');
-```
-
-### Reasoning Models
-
-Fireworks exposes the thinking of `deepseek-r1` in the generated text using the `` tag.
-You can use the `extractReasoningMiddleware` to extract this reasoning and expose it as a `reasoning` property on the result:
-
-```ts
-import { fireworks } from '@ai-sdk/fireworks';
-import { wrapLanguageModel, extractReasoningMiddleware } from 'ai';
-
-const enhancedModel = wrapLanguageModel({
- model: fireworks('accounts/fireworks/models/deepseek-r1'),
- middleware: extractReasoningMiddleware({ tagName: 'think' }),
-});
-```
-
-You can then use that enhanced model in functions like `generateText` and `streamText`.
-
-### Example
-
-You can use Fireworks language models to generate text with the `generateText` function:
-
-```ts
-import { fireworks } from '@ai-sdk/fireworks';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: fireworks('accounts/fireworks/models/firefunction-v1'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Fireworks language models can also be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-### Completion Models
-
-You can create models that call the Fireworks completions API using the `.completion()` factory method:
-
-```ts
-const model = fireworks.completion('accounts/fireworks/models/firefunction-v1');
-```
-
-### Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| ---------------------------------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `accounts/fireworks/models/firefunction-v1` | | | | |
-| `accounts/fireworks/models/deepseek-r1` | | | | |
-| `accounts/fireworks/models/deepseek-v3` | | | | |
-| `accounts/fireworks/models/llama-v3p1-405b-instruct` | | | | |
-| `accounts/fireworks/models/llama-v3p1-8b-instruct` | | | | |
-| `accounts/fireworks/models/llama-v3p2-3b-instruct` | | | | |
-| `accounts/fireworks/models/llama-v3p3-70b-instruct` | | | | |
-| `accounts/fireworks/models/mixtral-8x7b-instruct` | | | | |
-| `accounts/fireworks/models/mixtral-8x7b-instruct-hf` | | | | |
-| `accounts/fireworks/models/mixtral-8x22b-instruct` | | | | |
-| `accounts/fireworks/models/qwen2p5-coder-32b-instruct` | | | | |
-| `accounts/fireworks/models/qwen2p5-72b-instruct` | | | | |
-| `accounts/fireworks/models/qwen-qwq-32b-preview` | | | | |
-| `accounts/fireworks/models/qwen2-vl-72b-instruct` | | | | |
-| `accounts/fireworks/models/llama-v3p2-11b-vision-instruct` | | | | |
-| `accounts/fireworks/models/qwq-32b` | | | | |
-| `accounts/fireworks/models/yi-large` | | | | |
-| `accounts/fireworks/models/kimi-k2-instruct` | | | | |
-
-
- The table above lists popular models. Please see the [Fireworks models
- page](https://fireworks.ai/models) for a full list of available models.
-
-
-## Embedding Models
-
-You can create models that call the Fireworks embeddings API using the `.textEmbedding()` factory method:
-
-```ts
-const model = fireworks.textEmbedding('nomic-ai/nomic-embed-text-v1.5');
-```
-
-You can use Fireworks embedding models to generate embeddings with the `embed` function:
-
-```ts
-import { fireworks } from '@ai-sdk/fireworks';
-import { embed } from 'ai';
-
-const { embedding } = await embed({
- model: fireworks.textEmbedding('nomic-ai/nomic-embed-text-v1.5'),
- value: 'sunny day at the beach',
-});
-```
-
-### Model Capabilities
-
-| Model | Dimensions | Max Tokens |
-| -------------------------------- | ---------- | ---------- |
-| `nomic-ai/nomic-embed-text-v1.5` | 768 | 8192 |
-
-
- For more embedding models, see the [Fireworks models
- page](https://fireworks.ai/models) for a full list of available models.
-
-
-## Image Models
-
-You can create Fireworks image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-```ts
-import { fireworks } from '@ai-sdk/fireworks';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: fireworks.image('accounts/fireworks/models/flux-1-dev-fp8'),
- prompt: 'A futuristic cityscape at sunset',
- aspectRatio: '16:9',
-});
-```
-
-
- Model support for `size` and `aspectRatio` parameters varies. See the [Model
- Capabilities](#model-capabilities-1) section below for supported dimensions,
- or check the model's documentation on [Fireworks models
- page](https://fireworks.ai/models) for more details.
-
-
-### Model Capabilities
-
-For all models supporting aspect ratios, the following aspect ratios are supported:
-
-`1:1 (default), 2:3, 3:2, 4:5, 5:4, 16:9, 9:16, 9:21, 21:9`
-
-For all models supporting size, the following sizes are supported:
-
-`640 x 1536, 768 x 1344, 832 x 1216, 896 x 1152, 1024x1024 (default), 1152 x 896, 1216 x 832, 1344 x 768, 1536 x 640`
-
-| Model | Dimensions Specification |
-| ------------------------------------------------------------ | ------------------------ |
-| `accounts/fireworks/models/flux-1-dev-fp8` | Aspect Ratio |
-| `accounts/fireworks/models/flux-1-schnell-fp8` | Aspect Ratio |
-| `accounts/fireworks/models/playground-v2-5-1024px-aesthetic` | Size |
-| `accounts/fireworks/models/japanese-stable-diffusion-xl` | Size |
-| `accounts/fireworks/models/playground-v2-1024px-aesthetic` | Size |
-| `accounts/fireworks/models/SSD-1B` | Size |
-| `accounts/fireworks/models/stable-diffusion-xl-1024-v1-0` | Size |
-
-For more details, see the [Fireworks models page](https://fireworks.ai/models).
-
-#### Stability AI Models
-
-Fireworks also presents several Stability AI models backed by Stability AI API
-keys and endpoint. The AI SDK Fireworks provider does not currently include
-support for these models:
-
-| Model ID |
-| -------------------------------------- |
-| `accounts/stability/models/sd3-turbo` |
-| `accounts/stability/models/sd3-medium` |
-| `accounts/stability/models/sd3` |
-
----
-title: DeepSeek
-description: Learn how to use DeepSeek's models with the AI SDK.
----
-
-# DeepSeek Provider
-
-The [DeepSeek](https://www.deepseek.com) provider offers access to powerful language models through the DeepSeek API, including their [DeepSeek-V3 model](https://github.com/deepseek-ai/DeepSeek-V3).
-
-API keys can be obtained from the [DeepSeek Platform](https://platform.deepseek.com/api_keys).
-
-## Setup
-
-The DeepSeek provider is available via the `@ai-sdk/deepseek` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `deepseek` from `@ai-sdk/deepseek`:
-
-```ts
-import { deepseek } from '@ai-sdk/deepseek';
-```
-
-For custom configuration, you can import `createDeepSeek` and create a provider instance with your settings:
-
-```ts
-import { createDeepSeek } from '@ai-sdk/deepseek';
-
-const deepseek = createDeepSeek({
- apiKey: process.env.DEEPSEEK_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the DeepSeek provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls.
- The default prefix is `https://api.deepseek.com/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `DEEPSEEK_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Language Models
-
-You can create language models using a provider instance:
-
-```ts
-import { deepseek } from '@ai-sdk/deepseek';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: deepseek('deepseek-chat'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-You can also use the `.chat()` or `.languageModel()` factory methods:
-
-```ts
-const model = deepseek.chat('deepseek-chat');
-// or
-const model = deepseek.languageModel('deepseek-chat');
-```
-
-DeepSeek language models can be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-### Reasoning
-
-DeepSeek has reasoning support for the `deepseek-reasoner` model. The reasoning is exposed through streaming:
-
-```ts
-import { deepseek } from '@ai-sdk/deepseek';
-import { streamText } from 'ai';
-
-const result = streamText({
- model: deepseek('deepseek-reasoner'),
- prompt: 'How many "r"s are in the word "strawberry"?',
-});
-
-for await (const part of result.fullStream) {
- if (part.type === 'reasoning') {
- // This is the reasoning text
- console.log('Reasoning:', part.text);
- } else if (part.type === 'text') {
- // This is the final answer
- console.log('Answer:', part.text);
- }
-}
-```
-
-See [AI SDK UI: Chatbot](/docs/ai-sdk-ui/chatbot#reasoning) for more details
-on how to integrate reasoning into your chatbot.
-
-### Cache Token Usage
-
-DeepSeek provides context caching on disk technology that can significantly reduce token costs for repeated content. You can access the cache hit/miss metrics through the `providerMetadata` property in the response:
-
-```ts
-import { deepseek } from '@ai-sdk/deepseek';
-import { generateText } from 'ai';
-
-const result = await generateText({
- model: deepseek('deepseek-chat'),
- prompt: 'Your prompt here',
-});
-
-console.log(result.providerMetadata);
-// Example output: { deepseek: { promptCacheHitTokens: 1856, promptCacheMissTokens: 5 } }
-```
-
-The metrics include:
-
-- `promptCacheHitTokens`: Number of input tokens that were cached
-- `promptCacheMissTokens`: Number of input tokens that were not cached
-
-
- For more details about DeepSeek's caching system, see the [DeepSeek caching
- documentation](https://api-docs.deepseek.com/guides/kv_cache#checking-cache-hit-status).
-
-
-## Model Capabilities
-
-| Model | Text Generation | Object Generation | Image Input | Tool Usage | Tool Streaming |
-| ------------------- | ------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `deepseek-chat` | | | | | |
-| `deepseek-reasoner` | | | | | |
-
-
- Please see the [DeepSeek docs](https://api-docs.deepseek.com) for a full list
- of available models. You can also pass any available provider model ID as a
- string if needed.
-
-
----
-title: Cerebras
-description: Learn how to use Cerebras's models with the AI SDK.
----
-
-# Cerebras Provider
-
-The [Cerebras](https://cerebras.ai) provider offers access to powerful language models through the Cerebras API, including their high-speed inference capabilities powered by Wafer-Scale Engines and CS-3 systems.
-
-API keys can be obtained from the [Cerebras Platform](https://cloud.cerebras.ai).
-
-## Setup
-
-The Cerebras provider is available via the `@ai-sdk/cerebras` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `cerebras` from `@ai-sdk/cerebras`:
-
-```ts
-import { cerebras } from '@ai-sdk/cerebras';
-```
-
-For custom configuration, you can import `createCerebras` and create a provider instance with your settings:
-
-```ts
-import { createCerebras } from '@ai-sdk/cerebras';
-
-const cerebras = createCerebras({
- apiKey: process.env.CEREBRAS_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Cerebras provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls.
- The default prefix is `https://api.cerebras.ai/v1`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `CEREBRAS_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Language Models
-
-You can create language models using a provider instance:
-
-```ts
-import { cerebras } from '@ai-sdk/cerebras';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: cerebras('llama3.1-8b'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-Cerebras language models can be used in the `streamText` function
-(see [AI SDK Core](/docs/ai-sdk-core)).
-
-You can create Cerebras language models using a provider instance. The first argument is the model ID, e.g. `llama-3.3-70b`:
-
-```ts
-const model = cerebras('llama-3.3-70b');
-```
-
-You can also use the `.languageModel()` and `.chat()` methods:
-
-```ts
-const model = cerebras.languageModel('llama-3.3-70b');
-const model = cerebras.chat('llama-3.3-70b');
-```
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| -------------------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `llama3.1-8b` | | | | |
-| `llama-3.3-70b` | | | | |
-| `gpt-oss-120b` | | | | |
-| `qwen-3-32b` | | | | |
-| `qwen-3-235b-a22b-instruct-2507` | | | | |
-| `qwen-3-235b-a22b-thinking-2507` | | | | |
-| `zai-glm-4.6` | | | | |
-
-
- Please see the [Cerebras
- docs](https://inference-docs.cerebras.ai/introduction) for more details about
- the available models. Note that context windows are temporarily limited to
- 8192 tokens in the Free Tier. You can also pass any available provider model
- ID as a string if needed.
-
-
----
-title: Replicate
-description: Learn how to use Replicate models with the AI SDK.
----
-
-# Replicate Provider
-
-[Replicate](https://replicate.com/) is a platform for running open-source AI models.
-It is a popular choice for running image generation models.
-
-## Setup
-
-The Replicate provider is available via the `@ai-sdk/replicate` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `replicate` from `@ai-sdk/replicate`:
-
-```ts
-import { replicate } from '@ai-sdk/replicate';
-```
-
-If you need a customized setup, you can import `createReplicate` from `@ai-sdk/replicate`
-and create a provider instance with your settings:
-
-```ts
-import { createReplicate } from '@ai-sdk/replicate';
-
-const replicate = createReplicate({
- apiToken: process.env.REPLICATE_API_TOKEN ?? '',
-});
-```
-
-You can use the following optional settings to customize the Replicate provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.replicate.com/v1`.
-
-- **apiToken** _string_
-
- API token that is being sent using the `Authorization` header. It defaults to
- the `REPLICATE_API_TOKEN` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Image Models
-
-You can create Replicate image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-
- Model support for `size` and other parameters varies by model. Check the
- model's documentation on [Replicate](https://replicate.com/explore) for
- supported options and additional parameters that can be passed via
- `providerOptions.replicate`.
-
-
-### Supported Image Models
-
-The following image models are currently supported by the Replicate provider:
-
-- [black-forest-labs/flux-1.1-pro-ultra](https://replicate.com/black-forest-labs/flux-1.1-pro-ultra)
-- [black-forest-labs/flux-1.1-pro](https://replicate.com/black-forest-labs/flux-1.1-pro)
-- [black-forest-labs/flux-dev](https://replicate.com/black-forest-labs/flux-dev)
-- [black-forest-labs/flux-pro](https://replicate.com/black-forest-labs/flux-pro)
-- [black-forest-labs/flux-schnell](https://replicate.com/black-forest-labs/flux-schnell)
-- [bytedance/sdxl-lightning-4step](https://replicate.com/bytedance/sdxl-lightning-4step)
-- [fofr/aura-flow](https://replicate.com/fofr/aura-flow)
-- [fofr/latent-consistency-model](https://replicate.com/fofr/latent-consistency-model)
-- [fofr/realvisxl-v3-multi-controlnet-lora](https://replicate.com/fofr/realvisxl-v3-multi-controlnet-lora)
-- [fofr/sdxl-emoji](https://replicate.com/fofr/sdxl-emoji)
-- [fofr/sdxl-multi-controlnet-lora](https://replicate.com/fofr/sdxl-multi-controlnet-lora)
-- [ideogram-ai/ideogram-v2-turbo](https://replicate.com/ideogram-ai/ideogram-v2-turbo)
-- [ideogram-ai/ideogram-v2](https://replicate.com/ideogram-ai/ideogram-v2)
-- [lucataco/dreamshaper-xl-turbo](https://replicate.com/lucataco/dreamshaper-xl-turbo)
-- [lucataco/open-dalle-v1.1](https://replicate.com/lucataco/open-dalle-v1.1)
-- [lucataco/realvisxl-v2.0](https://replicate.com/lucataco/realvisxl-v2.0)
-- [lucataco/realvisxl2-lcm](https://replicate.com/lucataco/realvisxl2-lcm)
-- [luma/photon-flash](https://replicate.com/luma/photon-flash)
-- [luma/photon](https://replicate.com/luma/photon)
-- [nvidia/sana](https://replicate.com/nvidia/sana)
-- [playgroundai/playground-v2.5-1024px-aesthetic](https://replicate.com/playgroundai/playground-v2.5-1024px-aesthetic)
-- [recraft-ai/recraft-v3-svg](https://replicate.com/recraft-ai/recraft-v3-svg)
-- [recraft-ai/recraft-v3](https://replicate.com/recraft-ai/recraft-v3)
-- [stability-ai/stable-diffusion-3.5-large-turbo](https://replicate.com/stability-ai/stable-diffusion-3.5-large-turbo)
-- [stability-ai/stable-diffusion-3.5-large](https://replicate.com/stability-ai/stable-diffusion-3.5-large)
-- [stability-ai/stable-diffusion-3.5-medium](https://replicate.com/stability-ai/stable-diffusion-3.5-medium)
-- [tstramer/material-diffusion](https://replicate.com/tstramer/material-diffusion)
-
-You can also use [versioned models](https://replicate.com/docs/topics/models/versions).
-The id for versioned models is the Replicate model id followed by a colon and the version ID (`$modelId:$versionId`), e.g.
-`bytedance/sdxl-lightning-4step:5599ed30703defd1d160a25a63321b4dec97101d98b4674bcc56e41f62f35637`.
-
-
- You can also pass any available Replicate model ID as a string if needed.
-
-
-### Basic Usage
-
-```ts
-import { replicate } from '@ai-sdk/replicate';
-import { experimental_generateImage as generateImage } from 'ai';
-import { writeFile } from 'node:fs/promises';
-
-const { image } = await generateImage({
- model: replicate.image('black-forest-labs/flux-schnell'),
- prompt: 'The Loch Ness Monster getting a manicure',
- aspectRatio: '16:9',
-});
-
-await writeFile('image.webp', image.uint8Array);
-
-console.log('Image saved as image.webp');
-```
-
-### Model-specific options
-
-```ts highlight="9-11"
-import { replicate } from '@ai-sdk/replicate';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: replicate.image('recraft-ai/recraft-v3'),
- prompt: 'The Loch Ness Monster getting a manicure',
- size: '1365x1024',
- providerOptions: {
- replicate: {
- style: 'realistic_image',
- },
- },
-});
-```
-
-### Versioned Models
-
-```ts
-import { replicate } from '@ai-sdk/replicate';
-import { experimental_generateImage as generateImage } from 'ai';
-
-const { image } = await generateImage({
- model: replicate.image(
- 'bytedance/sdxl-lightning-4step:5599ed30703defd1d160a25a63321b4dec97101d98b4674bcc56e41f62f35637',
- ),
- prompt: 'The Loch Ness Monster getting a manicure',
-});
-```
-
-For more details, see the [Replicate models page](https://replicate.com/explore).
-
----
-title: Perplexity
-description: Learn how to use Perplexity's Sonar API with the AI SDK.
----
-
-# Perplexity Provider
-
-The [Perplexity](https://sonar.perplexity.ai) provider offers access to Sonar API - a language model that uniquely combines real-time web search with natural language processing. Each response is grounded in current web data and includes detailed citations, making it ideal for research, fact-checking, and obtaining up-to-date information.
-
-API keys can be obtained from the [Perplexity Platform](https://docs.perplexity.ai).
-
-## Setup
-
-The Perplexity provider is available via the `@ai-sdk/perplexity` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `perplexity` from `@ai-sdk/perplexity`:
-
-```ts
-import { perplexity } from '@ai-sdk/perplexity';
-```
-
-For custom configuration, you can import `createPerplexity` and create a provider instance with your settings:
-
-```ts
-import { createPerplexity } from '@ai-sdk/perplexity';
-
-const perplexity = createPerplexity({
- apiKey: process.env.PERPLEXITY_API_KEY ?? '',
-});
-```
-
-You can use the following optional settings to customize the Perplexity provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls.
- The default prefix is `https://api.perplexity.ai`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header. It defaults to
- the `PERPLEXITY_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
-
-## Language Models
-
-You can create Perplexity models using a provider instance:
-
-```ts
-import { perplexity } from '@ai-sdk/perplexity';
-import { generateText } from 'ai';
-
-const { text } = await generateText({
- model: perplexity('sonar-pro'),
- prompt: 'What are the latest developments in quantum computing?',
-});
-```
-
-### Sources
-
-Websites that have been used to generate the response are included in the `sources` property of the result:
-
-```ts
-import { perplexity } from '@ai-sdk/perplexity';
-import { generateText } from 'ai';
-
-const { text, sources } = await generateText({
- model: perplexity('sonar-pro'),
- prompt: 'What are the latest developments in quantum computing?',
-});
-
-console.log(sources);
-```
-
-### Provider Options & Metadata
-
-The Perplexity provider includes additional metadata in the response through `providerMetadata`.
-Additional configuration options are available through `providerOptions`.
-
-```ts
-const result = await generateText({
- model: perplexity('sonar-pro'),
- prompt: 'What are the latest developments in quantum computing?',
- providerOptions: {
- perplexity: {
- return_images: true, // Enable image responses (Tier-2 Perplexity users only)
- },
- },
-});
-
-console.log(result.providerMetadata);
-// Example output:
-// {
-// perplexity: {
-// usage: { citationTokens: 5286, numSearchQueries: 1 },
-// images: [
-// { imageUrl: "https://example.com/image1.jpg", originUrl: "https://elsewhere.com/page1", height: 1280, width: 720 },
-// { imageUrl: "https://example.com/image2.jpg", originUrl: "https://elsewhere.com/page2", height: 1280, width: 720 }
-// ]
-// },
-// }
-```
-
-The metadata includes:
-
-- `usage`: Object containing `citationTokens` and `numSearchQueries` metrics
-- `images`: Array of image URLs when `return_images` is enabled (Tier-2 users only)
-
-You can enable image responses by setting `return_images: true` in the provider options. This feature is only available to Perplexity Tier-2 users and above.
-
-### PDF Support
-
-The Perplexity provider supports reading PDF files.
-You can pass PDF files as part of the message content using the `file` type:
-
-```ts
-const result = await generateText({
- model: perplexity('sonar-pro'),
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: 'What is this document about?',
- },
- {
- type: 'file',
- data: fs.readFileSync('./data/ai.pdf'),
- mediaType: 'application/pdf',
- filename: 'ai.pdf', // optional
- },
- ],
- },
- ],
-});
-```
-
-You can also pass the URL of a PDF:
-
-```ts
-{
- type: 'file',
- data: new URL('https://example.com/document.pdf'),
- mediaType: 'application/pdf',
- filename: 'document.pdf', // optional
-}
-```
-
-The model will have access to the contents of the PDF file and
-respond to questions about it.
-
-
- For more details about Perplexity's capabilities, see the [Perplexity chat
- completion docs](https://docs.perplexity.ai/api-reference/chat-completions).
-
-
-## Model Capabilities
-
-| Model | Image Input | Object Generation | Tool Usage | Tool Streaming |
-| --------------------- | ------------------- | ------------------- | ------------------- | ------------------- |
-| `sonar-deep-research` | | | | |
-| `sonar-reasoning-pro` | | | | |
-| `sonar-reasoning` | | | | |
-| `sonar-pro` | | | | |
-| `sonar` | | | | |
-
-
- Please see the [Perplexity docs](https://docs.perplexity.ai) for detailed API
- documentation and the latest updates.
-
-
----
-title: Luma
-description: Learn how to use Luma AI models with the AI SDK.
----
-
-# Luma Provider
-
-[Luma AI](https://lumalabs.ai/) provides state-of-the-art image generation models through their Dream Machine platform. Their models offer ultra-high quality image generation with superior prompt understanding and unique capabilities like character consistency and multi-image reference support.
-
-## Setup
-
-The Luma provider is available via the `@ai-sdk/luma` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `luma` from `@ai-sdk/luma`:
-
-```ts
-import { luma } from '@ai-sdk/luma';
-```
-
-If you need a customized setup, you can import `createLuma` and create a provider instance with your settings:
-
-```ts
-import { createLuma } from '@ai-sdk/luma';
-
-const luma = createLuma({
- apiKey: 'your-api-key', // optional, defaults to LUMA_API_KEY environment variable
- baseURL: 'custom-url', // optional
- headers: {
- /* custom headers */
- }, // optional
-});
-```
-
-You can use the following optional settings to customize the Luma provider instance:
-
-- **baseURL** _string_
-
- Use a different URL prefix for API calls, e.g. to use proxy servers.
- The default prefix is `https://api.lumalabs.ai`.
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `LUMA_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Image Models
-
-You can create Luma image models using the `.image()` factory method.
-For more on image generation with the AI SDK see [generateImage()](/docs/reference/ai-sdk-core/generate-image).
-
-### Basic Usage
-
-```ts
-import { luma } from '@ai-sdk/luma';
-import { experimental_generateImage as generateImage } from 'ai';
-import fs from 'fs';
-
-const { image } = await generateImage({
- model: luma.image('photon-1'),
- prompt: 'A serene mountain landscape at sunset',
- aspectRatio: '16:9',
-});
-
-const filename = `image-${Date.now()}.png`;
-fs.writeFileSync(filename, image.uint8Array);
-console.log(`Image saved to ${filename}`);
-```
-
-### Image Model Settings
-
-You can customize the generation behavior with optional settings:
-
-```ts
-const { image } = await generateImage({
- model: luma.image('photon-1'),
- prompt: 'A serene mountain landscape at sunset',
- aspectRatio: '16:9',
- maxImagesPerCall: 1, // Maximum number of images to generate per API call
- providerOptions: {
- luma: {
- pollIntervalMillis: 5000, // How often to check for completed images (in ms)
- maxPollAttempts: 10, // Maximum number of polling attempts before timeout
- },
- },
-});
-```
-
-Since Luma processes images through an asynchronous queue system, these settings allow you to tune the polling behavior:
-
-- **maxImagesPerCall** _number_
-
- Override the maximum number of images generated per API call. Defaults to 1.
-
-- **pollIntervalMillis** _number_
-
- Control how frequently the API is checked for completed images while they are
- being processed. Defaults to 500ms.
-
-- **maxPollAttempts** _number_
-
- Limit how long to wait for results before timing out, since image generation
- is queued asynchronously. Defaults to 120 attempts.
-
-### Model Capabilities
-
-Luma offers two main models:
-
-| Model | Description |
-| ---------------- | ---------------------------------------------------------------- |
-| `photon-1` | High-quality image generation with superior prompt understanding |
-| `photon-flash-1` | Faster generation optimized for speed while maintaining quality |
-
-Both models support the following aspect ratios:
-
-- 1:1
-- 3:4
-- 4:3
-- 9:16
-- 16:9 (default)
-- 9:21
-- 21:9
-
-For more details about supported aspect ratios, see the [Luma Image Generation documentation](https://docs.lumalabs.ai/docs/image-generation).
-
-Key features of Luma models include:
-
-- Ultra-high quality image generation
-- 10x higher cost efficiency compared to similar models
-- Superior prompt understanding and adherence
-- Unique character consistency capabilities from single reference images
-- Multi-image reference support for precise style matching
-
-### Advanced Options
-
-Luma models support several advanced features through the `providerOptions.luma` parameter.
-
-#### Image Reference
-
-Use up to 4 reference images to guide your generation. Useful for creating variations or visualizing complex concepts. Adjust the `weight` (0-1) to control the influence of reference images.
-
-```ts
-// Example: Generate a salamander with reference
-await generateImage({
- model: luma.image('photon-1'),
- prompt: 'A salamander at dusk in a forest pond, in the style of ukiyo-e',
- providerOptions: {
- luma: {
- image_ref: [
- {
- url: 'https://example.com/reference.jpg',
- weight: 0.85,
- },
- ],
- },
- },
-});
-```
-
-#### Style Reference
-
-Apply specific visual styles to your generations using reference images. Control the style influence using the `weight` parameter.
-
-```ts
-// Example: Generate with style reference
-await generateImage({
- model: luma.image('photon-1'),
- prompt: 'A blue cream Persian cat launching its website on Vercel',
- providerOptions: {
- luma: {
- style_ref: [
- {
- url: 'https://example.com/style.jpg',
- weight: 0.8,
- },
- ],
- },
- },
-});
-```
-
-#### Character Reference
-
-Create consistent and personalized characters using up to 4 reference images of the same subject. More reference images improve character representation.
-
-```ts
-// Example: Generate character-based image
-await generateImage({
- model: luma.image('photon-1'),
- prompt: 'A woman with a cat riding a broomstick in a forest',
- providerOptions: {
- luma: {
- character_ref: {
- identity0: {
- images: ['https://example.com/character.jpg'],
- },
- },
- },
- },
-});
-```
-
-#### Modify Image
-
-Transform existing images using text prompts. Use the `weight` parameter to control how closely the result matches the input image (higher weight = closer to input but less creative).
-
-
- For color changes, it's recommended to use a lower weight value (0.0-0.1).
-
-
-```ts
-// Example: Modify existing image
-await generateImage({
- model: luma.image('photon-1'),
- prompt: 'transform the bike to a boat',
- providerOptions: {
- luma: {
- modify_image_ref: {
- url: 'https://example.com/image.jpg',
- weight: 1.0,
- },
- },
- },
-});
-```
-
-For more details about Luma's capabilities and features, visit the [Luma Image Generation documentation](https://docs.lumalabs.ai/docs/image-generation).
-
----
-title: ElevenLabs
-description: Learn how to use the ElevenLabs provider for the AI SDK.
----
-
-# ElevenLabs Provider
-
-The [ElevenLabs](https://elevenlabs.io/) provider contains language model support for the ElevenLabs transcription and speech generation APIs.
-
-## Setup
-
-The ElevenLabs provider is available in the `@ai-sdk/elevenlabs` module. You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-You can import the default provider instance `elevenlabs` from `@ai-sdk/elevenlabs`:
-
-```ts
-import { elevenlabs } from '@ai-sdk/elevenlabs';
-```
-
-If you need a customized setup, you can import `createElevenLabs` from `@ai-sdk/elevenlabs` and create a provider instance with your settings:
-
-```ts
-import { createElevenLabs } from '@ai-sdk/elevenlabs';
-
-const elevenlabs = createElevenLabs({
- // custom settings, e.g.
- fetch: customFetch,
-});
-```
-
-You can use the following optional settings to customize the ElevenLabs provider instance:
-
-- **apiKey** _string_
-
- API key that is being sent using the `Authorization` header.
- It defaults to the `ELEVENLABS_API_KEY` environment variable.
-
-- **headers** _Record<string,string>_
-
- Custom headers to include in the requests.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-## Speech Models
-
-You can create models that call the [ElevenLabs speech API](https://elevenlabs.io/text-to-speech)
-using the `.speech()` factory method.
-
-The first argument is the model id e.g. `eleven_multilingual_v2`.
-
-```ts
-const model = elevenlabs.speech('eleven_multilingual_v2');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying a voice to use for the generated audio.
-
-```ts highlight="6"
-import { experimental_generateSpeech as generateSpeech } from 'ai';
-import { elevenlabs } from '@ai-sdk/elevenlabs';
-
-const result = await generateSpeech({
- model: elevenlabs.speech('eleven_multilingual_v2'),
- text: 'Hello, world!',
- providerOptions: { elevenlabs: {} },
-});
-```
-
-- **language_code** _string or null_
- Optional. Language code (ISO 639-1) used to enforce a language for the model. Currently, only Turbo v2.5 and Flash v2.5 support language enforcement. For other models, providing a language code will result in an error.
-
-- **voice_settings** _object or null_
- Optional. Voice settings that override stored settings for the given voice. These are applied only to the current request.
-
- - **stability** _double or null_
- Optional. Determines how stable the voice is and the randomness between each generation. Lower values introduce broader emotional range; higher values result in a more monotonous voice.
- - **use_speaker_boost** _boolean or null_
- Optional. Boosts similarity to the original speaker. Increases computational load and latency.
- - **similarity_boost** _double or null_
- Optional. Controls how closely the AI should adhere to the original voice.
- - **style** _double or null_
- Optional. Amplifies the style of the original speaker. May increase latency if set above 0.
-
-- **pronunciation_dictionary_locators** _array of objects or null_
- Optional. A list of pronunciation dictionary locators to apply to the text, in order. Up to 3 locators per request.
- Each locator object:
-
- - **pronunciation_dictionary_id** _string_ (required)
- The ID of the pronunciation dictionary.
- - **version_id** _string or null_ (optional)
- The version ID of the dictionary. If not provided, the latest version is used.
-
-- **seed** _integer or null_
- Optional. If specified, the system will attempt to sample deterministically. Must be between 0 and 4294967295. Determinism is not guaranteed.
-
-- **previous_text** _string or null_
- Optional. The text that came before the current request's text. Can improve continuity when concatenating generations or influence current generation continuity.
-
-- **next_text** _string or null_
- Optional. The text that comes after the current request's text. Can improve continuity when concatenating generations or influence current generation continuity.
-
-- **previous_request_ids** _array of strings or null_
- Optional. List of request IDs for samples generated before this one. Improves continuity when splitting large tasks. Max 3 IDs. If both `previous_text` and `previous_request_ids` are sent, `previous_text` is ignored.
-
-- **next_request_ids** _array of strings or null_
- Optional. List of request IDs for samples generated after this one. Useful for maintaining continuity when regenerating a sample. Max 3 IDs. If both `next_text` and `next_request_ids` are sent, `next_text` is ignored.
-
-- **apply_text_normalization** _enum_
- Optional. Controls text normalization.
- Allowed values: `'auto'` (default), `'on'`, `'off'`.
-
- - `'auto'`: System decides whether to apply normalization (e.g., spelling out numbers).
- - `'on'`: Always apply normalization.
- - `'off'`: Never apply normalization.
- For `eleven_turbo_v2_5` and `eleven_flash_v2_5`, can only be enabled with Enterprise plans.
-
-- **apply_language_text_normalization** _boolean_
- Optional. Defaults to `false`. Controls language text normalization, which helps with proper pronunciation in some supported languages (currently only Japanese). May significantly increase latency.
-
-### Model Capabilities
-
-| Model | Instructions |
-| ------------------------ | ------------------- |
-| `eleven_v3` | |
-| `eleven_multilingual_v2` | |
-| `eleven_flash_v2_5` | |
-| `eleven_flash_v2` | |
-| `eleven_turbo_v2_5` | |
-| `eleven_turbo_v2` | |
-| `eleven_monolingual_v1` | |
-| `eleven_multilingual_v1` | |
-
-## Transcription Models
-
-You can create models that call the [ElevenLabs transcription API](https://elevenlabs.io/speech-to-text)
-using the `.transcription()` factory method.
-
-The first argument is the model id e.g. `scribe_v1`.
-
-```ts
-const model = elevenlabs.transcription('scribe_v1');
-```
-
-You can also pass additional provider-specific options using the `providerOptions` argument. For example, supplying the input language in ISO-639-1 (e.g. `en`) format can sometimes improve transcription performance if known beforehand.
-
-```ts highlight="6"
-import { experimental_transcribe as transcribe } from 'ai';
-import { elevenlabs } from '@ai-sdk/elevenlabs';
-
-const result = await transcribe({
- model: elevenlabs.transcription('scribe_v1'),
- audio: new Uint8Array([1, 2, 3, 4]),
- providerOptions: { elevenlabs: { languageCode: 'en' } },
-});
-```
-
-The following provider options are available:
-
-- **languageCode** _string_
-
- An ISO-639-1 or ISO-639-3 language code corresponding to the language of the audio file.
- Can sometimes improve transcription performance if known beforehand.
- Defaults to `null`, in which case the language is predicted automatically.
-
-- **tagAudioEvents** _boolean_
-
- Whether to tag audio events like (laughter), (footsteps), etc. in the transcription.
- Defaults to `true`.
-
-- **numSpeakers** _integer_
-
- The maximum amount of speakers talking in the uploaded file.
- Can help with predicting who speaks when.
- The maximum amount of speakers that can be predicted is 32.
- Defaults to `null`, in which case the amount of speakers is set to the maximum value the model supports.
-
-- **timestampsGranularity** _enum_
-
- The granularity of the timestamps in the transcription.
- Defaults to `'word'`.
- Allowed values: `'none'`, `'word'`, `'character'`.
-
-- **diarize** _boolean_
-
- Whether to annotate which speaker is currently talking in the uploaded file.
- Defaults to `true`.
-
-- **fileFormat** _enum_
-
- The format of input audio.
- Defaults to `'other'`.
- Allowed values: `'pcm_s16le_16'`, `'other'`.
- For `'pcm_s16le_16'`, the input audio must be 16-bit PCM at a 16kHz sample rate, single channel (mono), and little-endian byte order.
- Latency will be lower than with passing an encoded waveform.
-
-### Model Capabilities
-
-| Model | Transcription | Duration | Segments | Language |
-| ------------------------ | ------------------- | ------------------- | ------------------- | ------------------- |
-| `scribe_v1` | | | | |
-| `scribe_v1_experimental` | | | | |
-
----
-title: LM Studio
-description: Use the LM Studio OpenAI compatible API with the AI SDK.
----
-
-# LM Studio Provider
-
-[LM Studio](https://lmstudio.ai/) is a user interface for running local models.
-
-It contains an OpenAI compatible API server that you can use with the AI SDK.
-You can start the local server under the [Local Server tab](https://lmstudio.ai/docs/basics/server) in the LM Studio UI ("Start Server" button).
-
-## Setup
-
-The LM Studio provider is available via the `@ai-sdk/openai-compatible` module as it is compatible with the OpenAI API.
-You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-To use LM Studio, you can create a custom provider instance with the `createOpenAICompatible` function from `@ai-sdk/openai-compatible`:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-
-const lmstudio = createOpenAICompatible({
- name: 'lmstudio',
- baseURL: 'http://localhost:1234/v1',
-});
-```
-
-
- LM Studio uses port `1234` by default, but you can change in the [app's Local
- Server tab](https://lmstudio.ai/docs/basics/server).
-
-
-## Language Models
-
-You can interact with local LLMs in [LM Studio](https://lmstudio.ai/docs/basics/server#endpoints-overview) using a provider instance.
-The first argument is the model id, e.g. `llama-3.2-1b`.
-
-```ts
-const model = lmstudio('llama-3.2-1b');
-```
-
-###### To be able to use a model, you need to [download it first](https://lmstudio.ai/docs/basics/download-model).
-
-### Example
-
-You can use LM Studio language models to generate text with the `generateText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { generateText } from 'ai';
-
-const lmstudio = createOpenAICompatible({
- name: 'lmstudio',
- baseURL: 'https://localhost:1234/v1',
-});
-
-const { text } = await generateText({
- model: lmstudio('llama-3.2-1b'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
- maxRetries: 1, // immediately error if the server is not running
-});
-```
-
-LM Studio language models can also be used with `streamText`.
-
-## Embedding Models
-
-You can create models that call the [LM Studio embeddings API](https://lmstudio.ai/docs/basics/server#endpoints-overview)
-using the `.textEmbeddingModel()` factory method.
-
-```ts
-const model = lmstudio.textEmbeddingModel(
- 'text-embedding-nomic-embed-text-v1.5',
-);
-```
-
-### Example - Embedding a Single Value
-
-```tsx
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { embed } from 'ai';
-
-const lmstudio = createOpenAICompatible({
- name: 'lmstudio',
- baseURL: 'https://localhost:1234/v1',
-});
-
-// 'embedding' is a single embedding object (number[])
-const { embedding } = await embed({
- model: lmstudio.textEmbeddingModel('text-embedding-nomic-embed-text-v1.5'),
- value: 'sunny day at the beach',
-});
-```
-
-### Example - Embedding Many Values
-
-When loading data, e.g. when preparing a data store for retrieval-augmented generation (RAG),
-it is often useful to embed many values at once (batch embedding).
-
-The AI SDK provides the [`embedMany`](/docs/reference/ai-sdk-core/embed-many) function for this purpose.
-Similar to `embed`, you can use it with embeddings models,
-e.g. `lmstudio.textEmbeddingModel('text-embedding-nomic-embed-text-v1.5')` or `lmstudio.textEmbeddingModel('text-embedding-bge-small-en-v1.5')`.
-
-```tsx
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { embedMany } from 'ai';
-
-const lmstudio = createOpenAICompatible({
- name: 'lmstudio',
- baseURL: 'https://localhost:1234/v1',
-});
-
-// 'embeddings' is an array of embedding objects (number[][]).
-// It is sorted in the same order as the input values.
-const { embeddings } = await embedMany({
- model: lmstudio.textEmbeddingModel('text-embedding-nomic-embed-text-v1.5'),
- values: [
- 'sunny day at the beach',
- 'rainy afternoon in the city',
- 'snowy night in the mountains',
- ],
-});
-```
-
----
-title: NVIDIA NIM
-description: Use NVIDIA NIM OpenAI compatible API with the AI SDK.
----
-
-# NVIDIA NIM Provider
-
-[NVIDIA NIM](https://www.nvidia.com/en-us/ai/) provides optimized inference microservices for deploying foundation models. It offers an OpenAI-compatible API that you can use with the AI SDK.
-
-## Setup
-
-The NVIDIA NIM provider is available via the `@ai-sdk/openai-compatible` module as it is compatible with the OpenAI API.
-You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-To use NVIDIA NIM, you can create a custom provider instance with the `createOpenAICompatible` function from `@ai-sdk/openai-compatible`:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-
-const nim = createOpenAICompatible({
- name: 'nim',
- baseURL: 'https://integrate.api.nvidia.com/v1',
- headers: {
- Authorization: `Bearer ${process.env.NIM_API_KEY}`,
- },
-});
-```
-
-
- You can obtain an API key and free credits by registering at [NVIDIA
- Build](https://build.nvidia.com/explore/discover). New users receive 1,000
- inference credits to get started.
-
-
-## Language Models
-
-You can interact with NIM models using a provider instance. For example, to use [DeepSeek-R1](https://build.nvidia.com/deepseek-ai/deepseek-r1), a powerful open-source language model:
-
-```ts
-const model = nim.chatModel('deepseek-ai/deepseek-r1');
-```
-
-### Example - Generate Text
-
-You can use NIM language models to generate text with the `generateText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { generateText } from 'ai';
-
-const nim = createOpenAICompatible({
- name: 'nim',
- baseURL: 'https://integrate.api.nvidia.com/v1',
- headers: {
- Authorization: `Bearer ${process.env.NIM_API_KEY}`,
- },
-});
-
-const { text, usage, finishReason } = await generateText({
- model: nim.chatModel('deepseek-ai/deepseek-r1'),
- prompt: 'Tell me the history of the San Francisco Mission-style burrito.',
-});
-
-console.log(text);
-console.log('Token usage:', usage);
-console.log('Finish reason:', finishReason);
-```
-
-### Example - Stream Text
-
-NIM language models can also generate text in a streaming fashion with the `streamText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { streamText } from 'ai';
-
-const nim = createOpenAICompatible({
- name: 'nim',
- baseURL: 'https://integrate.api.nvidia.com/v1',
- headers: {
- Authorization: `Bearer ${process.env.NIM_API_KEY}`,
- },
-});
-
-const result = streamText({
- model: nim.chatModel('deepseek-ai/deepseek-r1'),
- prompt: 'Tell me the history of the Northern White Rhino.',
-});
-
-for await (const textPart of result.textStream) {
- process.stdout.write(textPart);
-}
-
-console.log();
-console.log('Token usage:', await result.usage);
-console.log('Finish reason:', await result.finishReason);
-```
-
-NIM language models can also be used with other AI SDK functions like `generateObject` and `streamObject`.
-
-
- Model support for tool calls and structured object generation varies. For
- example, the
- [`meta/llama-3.3-70b-instruct`](https://build.nvidia.com/meta/llama-3_3-70b-instruct)
- model supports object generation capabilities. Check each model's
- documentation on NVIDIA Build for specific supported features.
-
-
----
-title: Heroku
-description: Use a Heroku OpenAI compatible API with the AI SDK.
----
-
-# Heroku Provider
-
-[Heroku](https://heroku.com/) is a cloud platform that allows you to deploy and run applications, including AI models with OpenAI API compatibility.
-You can deploy models that are OpenAI API compatible and use them with the AI SDK.
-
-## Setup
-
-The Heroku provider is available via the `@ai-sdk/openai-compatible` module as it is compatible with the OpenAI API.
-You can install it with
-
-
-
-
-
-
-
-
-
-
-
-
-
-### Heroku Setup
-
-1. Create a test app in Heroku:
-
-```bash
-heroku create
-```
-
-2. Inference using claude-3-5-haiku:
-
-```bash
-heroku ai:models:create -a $APP_NAME claude-3-5-haiku
-```
-
-3. Export Variables:
-
-```bash
-export INFERENCE_KEY=$(heroku config:get INFERENCE_KEY -a $APP_NAME)
-export INFERENCE_MODEL_ID=$(heroku config:get INFERENCE_MODEL_ID -a $APP_NAME)
-export INFERENCE_URL=$(heroku config:get INFERENCE_URL -a $APP_NAME)
-```
-
-## Provider Instance
-
-To use Heroku, you can create a custom provider instance with the `createOpenAICompatible` function from `@ai-sdk/openai-compatible`:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-
-const heroku = createOpenAICompatible({
- name: 'heroku',
- baseURL: process.env.INFERENCE_URL + '/v1',
- apiKey: process.env.INFERENCE_KEY,
-});
-```
-
-Be sure to have your `INFERENCE_KEY`, `INFERENCE_MODEL_ID`, and `INFERENCE_URL` set in your environment variables.
-
-## Language Models
-
-You can create Heroku models using a provider instance.
-The first argument is the served model name, e.g. `claude-3-5-haiku`.
-
-```ts
-const model = heroku('claude-3-5-haiku');
-```
-
-### Example
-
-You can use Heroku language models to generate text with the `generateText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { generateText } from 'ai';
-
-const heroku = createOpenAICompatible({
- name: 'heroku',
- baseURL: process.env.INFERENCE_URL + '/v1',
- apiKey: process.env.INFERENCE_KEY,
-});
-
-const { text } = await generateText({
- model: heroku('claude-3-5-haiku'),
- prompt: 'Tell me about yourself in one sentence',
-});
-
-console.log(text);
-```
-
-Heroku language models are also able to generate text in a streaming fashion with the `streamText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { streamText } from 'ai';
-
-const heroku = createOpenAICompatible({
- name: 'heroku',
- baseURL: process.env.INFERENCE_URL + '/v1',
- apiKey: process.env.INFERENCE_KEY,
-});
-
-const result = streamText({
- model: heroku('claude-3-5-haiku'),
- prompt: 'Tell me about yourself in one sentence',
-});
-
-for await (const message of result.textStream) {
- console.log(message);
-}
-```
-
-Heroku language models can also be used in the `generateObject`, and `streamObject` functions.
-
----
-title: OpenAI Compatible Providers
-description: Use OpenAI compatible providers with the AI SDK.
----
-
-# OpenAI Compatible Providers
-
-You can use the [OpenAI Compatible Provider](https://www.npmjs.com/package/@ai-sdk/openai-compatible) package to use language model providers that implement the OpenAI API.
-
-Below we focus on the general setup and provider instance creation. You can also [write a custom provider package leveraging the OpenAI Compatible package](/providers/openai-compatible-providers/custom-providers).
-
-We provide detailed documentation for the following OpenAI compatible providers:
-
-- [LM Studio](/providers/openai-compatible-providers/lmstudio)
-- [NIM](/providers/openai-compatible-providers/nim)
-- [Heroku](/providers/openai-compatible-providers/heroku)
-
-The general setup and provider instance creation is the same for all of these providers.
-
-## Setup
-
-The OpenAI Compatible provider is available via the `@ai-sdk/openai-compatible` module. You can install it with:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-## Provider Instance
-
-To use an OpenAI compatible provider, you can create a custom provider instance with the `createOpenAICompatible` function from `@ai-sdk/openai-compatible`:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-
-const provider = createOpenAICompatible({
- name: 'provider-name',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.provider.com/v1',
- includeUsage: true, // Include usage information in streaming responses
-});
-```
-
-You can use the following optional settings to customize the provider instance:
-
-- **baseURL** _string_
-
- Set the URL prefix for API calls.
-
-- **apiKey** _string_
-
- API key for authenticating requests. If specified, adds an `Authorization`
- header to request headers with the value `Bearer `. This will be added
- before any headers potentially specified in the `headers` option.
-
-- **headers** _Record<string,string>_
-
- Optional custom headers to include in requests. These will be added to request headers
- after any headers potentially added by use of the `apiKey` option.
-
-- **queryParams** _Record<string,string>_
-
- Optional custom url query parameters to include in request urls.
-
-- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
-
- Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
- Defaults to the global `fetch` function.
- You can use it as a middleware to intercept requests,
- or to provide a custom fetch implementation for e.g. testing.
-
-- **includeUsage** _boolean_
-
- Include usage information in streaming responses. When enabled, usage data will be included in the response metadata for streaming requests. Defaults to `undefined` (`false`).
-
-- **supportsStructuredOutputs** _boolean_
-
- Set to true if the provider supports structured outputs. Only relevant for `provider()`, `provider.chatModel()`, and `provider.languageModel()`.
-
-## Language Models
-
-You can create provider models using a provider instance.
-The first argument is the model id, e.g. `model-id`.
-
-```ts
-const model = provider('model-id');
-```
-
-### Example
-
-You can use provider language models to generate text with the `generateText` function:
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { generateText } from 'ai';
-
-const provider = createOpenAICompatible({
- name: 'provider-name',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.provider.com/v1',
-});
-
-const { text } = await generateText({
- model: provider('model-id'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-### Including model ids for auto-completion
-
-```ts
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-import { generateText } from 'ai';
-
-type ExampleChatModelIds =
- | 'meta-llama/Llama-3-70b-chat-hf'
- | 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo'
- | (string & {});
-
-type ExampleCompletionModelIds =
- | 'codellama/CodeLlama-34b-Instruct-hf'
- | 'Qwen/Qwen2.5-Coder-32B-Instruct'
- | (string & {});
-
-type ExampleEmbeddingModelIds =
- | 'BAAI/bge-large-en-v1.5'
- | 'bert-base-uncased'
- | (string & {});
-
-const model = createOpenAICompatible<
- ExampleChatModelIds,
- ExampleCompletionModelIds,
- ExampleEmbeddingModelIds
->({
- name: 'example',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.example.com/v1',
-});
-
-// Subsequent calls to e.g. `model.chatModel` will auto-complete the model id
-// from the list of `ExampleChatModelIds` while still allowing free-form
-// strings as well.
-
-const { text } = await generateText({
- model: model.chatModel('meta-llama/Llama-3-70b-chat-hf'),
- prompt: 'Write a vegetarian lasagna recipe for 4 people.',
-});
-```
-
-### Custom query parameters
-
-Some providers may require custom query parameters. An example is the [Azure AI
-Model Inference
-API](https://learn.microsoft.com/en-us/azure/machine-learning/reference-model-inference-chat-completions?view=azureml-api-2)
-which requires an `api-version` query parameter.
-
-You can set these via the optional `queryParams` provider setting. These will be
-added to all requests made by the provider.
-
-```ts highlight="7-9"
-import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
-
-const provider = createOpenAICompatible({
- name: 'provider-name',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.provider.com/v1',
- queryParams: {
- 'api-version': '1.0.0',
- },
-});
-```
-
-For example, with the above configuration, API requests would include the query parameter in the URL like:
-`https://api.provider.com/v1/chat/completions?api-version=1.0.0`.
-
-## Provider-specific options
-
-The OpenAI Compatible provider supports adding provider-specific options to the request body. These are specified with the `providerOptions` field in the request body.
-
-For example, if you create a provider instance with the name `provider-name`, you can add a `custom-option` field to the request body like this:
-
-```ts
-const provider = createOpenAICompatible({
- name: 'provider-name',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.provider.com/v1',
-});
-
-const { text } = await generateText({
- model: provider('model-id'),
- prompt: 'Hello',
- providerOptions: {
- 'provider-name': { customOption: 'magic-value' },
- },
-});
-```
-
-The request body sent to the provider will include the `customOption` field with the value `magic-value`. This gives you an easy way to add provider-specific options to requests without having to modify the provider or AI SDK code.
-
-## Custom Metadata Extraction
-
-The OpenAI Compatible provider supports extracting provider-specific metadata from API responses through metadata extractors.
-These extractors allow you to capture additional information returned by the provider beyond the standard response format.
-
-Metadata extractors receive the raw, unprocessed response data from the provider, giving you complete flexibility
-to extract any custom fields or experimental features that the provider may include.
-This is particularly useful when:
-
-- Working with providers that include non-standard response fields
-- Experimenting with beta or preview features
-- Capturing provider-specific metrics or debugging information
-- Supporting rapid provider API evolution without SDK changes
-
-Metadata extractors work with both streaming and non-streaming chat completions and consist of two main components:
-
-1. A function to extract metadata from complete responses
-2. A streaming extractor that can accumulate metadata across chunks in a streaming response
-
-Here's an example metadata extractor that captures both standard and custom provider data:
-
-```typescript
-const myMetadataExtractor: MetadataExtractor = {
- // Process complete, non-streaming responses
- extractMetadata: ({ parsedBody }) => {
- // You have access to the complete raw response
- // Extract any fields the provider includes
- return {
- myProvider: {
- standardUsage: parsedBody.usage,
- experimentalFeatures: parsedBody.beta_features,
- customMetrics: {
- processingTime: parsedBody.server_timing?.total_ms,
- modelVersion: parsedBody.model_version,
- // ... any other provider-specific data
- },
- },
- };
- },
-
- // Process streaming responses
- createStreamExtractor: () => {
- let accumulatedData = {
- timing: [],
- customFields: {},
- };
-
- return {
- // Process each chunk's raw data
- processChunk: parsedChunk => {
- if (parsedChunk.server_timing) {
- accumulatedData.timing.push(parsedChunk.server_timing);
- }
- if (parsedChunk.custom_data) {
- Object.assign(accumulatedData.customFields, parsedChunk.custom_data);
- }
- },
- // Build final metadata from accumulated data
- buildMetadata: () => ({
- myProvider: {
- streamTiming: accumulatedData.timing,
- customData: accumulatedData.customFields,
- },
- }),
- };
- },
-};
-```
-
-You can provide a metadata extractor when creating your provider instance:
-
-```typescript
-const provider = createOpenAICompatible({
- name: 'my-provider',
- apiKey: process.env.PROVIDER_API_KEY,
- baseURL: 'https://api.provider.com/v1',
- metadataExtractor: myMetadataExtractor,
-});
-```
-
-The extracted metadata will be included in the response under the `providerMetadata` field:
-
-```typescript
-const { text, providerMetadata } = await generateText({
- model: provider('model-id'),
- prompt: 'Hello',
-});
-
-console.log(providerMetadata.myProvider.customMetric);
-```
-
-This allows you to access provider-specific information while maintaining a consistent interface across different providers.
diff --git a/apps/web/public/.well-known/security.txt b/apps/web/public/.well-known/security.txt
index 184e4e1..9186c97 100644
--- a/apps/web/public/.well-known/security.txt
+++ b/apps/web/public/.well-known/security.txt
@@ -1,4 +1,4 @@
-Contact: mailto:thomasalwyndavis@gmail.com
+Contact: mailto:hello@tpmjs.com
Preferred-Languages: en
Canonical: https://tpmjs.com/.well-known/security.txt
Policy: https://tpmjs.com/.well-known/security.txt
diff --git a/apps/web/src/app/faq/page.tsx b/apps/web/src/app/faq/page.tsx
index 3f223de..0c85fc3 100644
--- a/apps/web/src/app/faq/page.tsx
+++ b/apps/web/src/app/faq/page.tsx
@@ -302,11 +302,8 @@ export default function FAQPage(): React.ReactElement {
For security issues: Email us
directly at{' '}
-
- thomasalwyndavis@gmail.com
+
+ hello@tpmjs.com
@@ -370,11 +367,8 @@ export default function FAQPage(): React.ReactElement {
Email: Contact us at{' '}
-
- thomasalwyndavis@gmail.com
+
+ hello@tpmjs.com
{' '}
for private inquiries
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 278a7da..692e697 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -26,7 +26,7 @@ export const metadata: Metadata = {
template: '%s | TPMJS',
},
description:
- 'The registry for AI tools. Discover, share, and integrate tools that give your agents superpowers.',
+ 'Discover and use npm packages as AI agent tools. No config files, automatic discovery, works with any framework.',
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
@@ -56,7 +56,7 @@ export const metadata: Metadata = {
siteName: 'TPMJS',
title: 'TPMJS - Tool Package Manager for AI Agents',
description:
- 'The registry for AI tools. Discover, share, and integrate tools that give your agents superpowers.',
+ 'Discover and use npm packages as AI agent tools. No config files, automatic discovery, works with any framework.',
images: [
{
url: '/og-image.png',
@@ -72,7 +72,7 @@ export const metadata: Metadata = {
creator: '@tpmjs_registry',
title: 'TPMJS - Tool Package Manager for AI Agents',
description:
- 'The registry for AI tools. Discover, share, and integrate tools that give your agents superpowers.',
+ 'Discover and use npm packages as AI agent tools. No config files, automatic discovery, works with any framework.',
images: ['/og-image.png'],
},
robots: {
@@ -100,7 +100,7 @@ export default function RootLayout({
url: 'https://tpmjs.com',
logo: 'https://tpmjs.com/logo.png',
description:
- 'The registry for AI tools. Discover, share, and integrate tools that give your agents superpowers.',
+ 'Discover and use npm packages as AI agent tools. No config files, automatic discovery, works with any framework.',
sameAs: ['https://github.com/tpmjs/tpmjs', 'https://x.com/tpmjs_registry'],
};
@@ -110,7 +110,7 @@ export default function RootLayout({
name: 'TPMJS',
url: 'https://tpmjs.com',
description:
- 'The registry for AI tools. Discover, share, and integrate tools that give your agents superpowers.',
+ 'Discover and use npm packages as AI agent tools. No config files, automatic discovery, works with any framework.',
potentialAction: {
'@type': 'SearchAction',
target: {
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 53293a5..f179a49 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -83,6 +83,76 @@ export default async function HomePage(): Promise {
{/* Hero Section - Dithered Design */}
+ {/* What is TPMJS? Explainer Section */}
+
+
+
+
+ What is TPMJS?
+
+
+
+ {/* The Problem */}
+
+
+ ✗ The Problem
+
+
+ AI agents need tools (web scraping, file processing, API calls) but developers
+ must manually import and configure each one. As tooling grows, this becomes
+ unmanageable—hundreds of imports, version conflicts, and static capabilities.
+
+
+
+ {/* The Solution */}
+
+
+ ✓ The Solution
+
+
+ TPMJS is a registry that automatically discovers npm packages built for AI
+ agents. Your agent searches by description and loads tools at runtime—no config
+ files, no manual imports, always up-to-date.
+
+
+
+
+ {/* How it works - 3 steps */}
+
+
+
+ 1
+
+
Publish to npm
+
+ Add tpmjs-tool keyword to your
+ package.json and publish normally
+
+
+
+
+ 2
+
+
Auto-indexed
+
+ TPMJS discovers your package within 15 minutes and extracts tool schemas
+ automatically
+
+
+
+
+ 3
+
+
Agents discover it
+
+ Any AI agent can now find and use your tool by describing what they need
+
+
+
+
+
+
+
{/* Featured Tools Section */}
@@ -173,7 +243,7 @@ export default async function HomePage(): Promise {
Share your tool with the AI community. Automatic discovery, quality scoring, and
- seamless integration with popular AI frameworks.
+ integration with Vercel AI SDK, LangChain, and more.
{/* Generator Highlight Box */}
diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx
index 909a93a..2d99051 100644
--- a/apps/web/src/app/privacy/page.tsx
+++ b/apps/web/src/app/privacy/page.tsx
@@ -20,7 +20,7 @@ export default function PrivacyPage(): React.ReactElement {
How we collect, use, and protect your data
- Last updated: December 14, 2025
+ Last updated: December 2024
{/* Introduction */}
@@ -432,11 +432,8 @@ export default function PrivacyPage(): React.ReactElement {
To exercise any of these rights, contact us at{' '}
-
- thomasalwyndavis@gmail.com
+
+ hello@tpmjs.com
. We will respond within 30 days.
@@ -542,11 +539,8 @@ export default function PrivacyPage(): React.ReactElement {
diff --git a/apps/web/src/app/sdk/page.tsx b/apps/web/src/app/sdk/page.tsx
index 4b26f9a..9ebce91 100644
--- a/apps/web/src/app/sdk/page.tsx
+++ b/apps/web/src/app/sdk/page.tsx
@@ -578,8 +578,8 @@ Use registrySearch to find tools, then registryExecute to run them.\`,
We're building the{' '}
npm for AI tools. Just as npm
- revolutionized JavaScript package sharing, TPMJS aims to create a universal
- ecosystem where AI agents can discover, share, and execute tools seamlessly.
+ changed how developers share JavaScript packages, TPMJS aims to do the same for AI
+ agent tools—a universal ecosystem where agents discover and use tools on-demand.
The registrySearch and{' '}
diff --git a/apps/web/src/app/terms/page.tsx b/apps/web/src/app/terms/page.tsx
index b285cd6..6c55c31 100644
--- a/apps/web/src/app/terms/page.tsx
+++ b/apps/web/src/app/terms/page.tsx
@@ -19,7 +19,7 @@ export default function TermsPage(): React.ReactElement {
Terms of Service
-
Last updated: December 14, 2025
+
Last updated: December 2024
{/* Content */}
@@ -372,10 +372,10 @@ export default function TermsPage(): React.ReactElement {
- thomasalwyndavis@gmail.com
+ hello@tpmjs.com
@@ -416,7 +416,7 @@ export default function TermsPage(): React.ReactElement {
We're here to help. Reach out if you need clarification on anything.
diff --git a/apps/web/src/components/AppFooter.tsx b/apps/web/src/components/AppFooter.tsx
index 179d4e1..9afab96 100644
--- a/apps/web/src/components/AppFooter.tsx
+++ b/apps/web/src/components/AppFooter.tsx
@@ -11,7 +11,7 @@ export function AppFooter(): React.ReactElement {
© 2025 TPMJS. All rights reserved.
Contact
diff --git a/apps/web/src/components/SDKFlowDiagram.tsx b/apps/web/src/components/SDKFlowDiagram.tsx
index 752b233..ebca63e 100644
--- a/apps/web/src/components/SDKFlowDiagram.tsx
+++ b/apps/web/src/components/SDKFlowDiagram.tsx
@@ -530,7 +530,7 @@ export function SDKFlowDiagram(): React.ReactElement {
Your existing tools — Any AI
SDK tools you've already built or installed. These work alongside the registry
- tools seamlessly.
+ tools without conflicts.
)}
{hoveredNode === 'registry-search' && (
diff --git a/apps/web/src/components/home/HeroSection.tsx b/apps/web/src/components/home/HeroSection.tsx
index e95e075..998bd08 100644
--- a/apps/web/src/components/home/HeroSection.tsx
+++ b/apps/web/src/components/home/HeroSection.tsx
@@ -62,14 +62,23 @@ export function HeroSection({ stats }: HeroSectionProps): React.ReactElement {
{/* Main Heading */}
- TOOL REGISTRY FOR AI AGENTS
+ NPM PACKAGES YOUR AI AGENT CAN DISCOVER
+ {/* Clear value prop */}
+
+ TPMJS indexes npm packages as tools that AI agents can find and use at runtime.
+
+
+ No config files. No manual imports. Just describe what you need.
+
+
+
{/* Live Metrics Strip */}
-
+
{formatNumber(stats.packageCount)}
PACKAGES
@@ -81,13 +90,6 @@ export function HeroSection({ stats }: HeroSectionProps): React.ReactElement {
- {/* Subheading */}
-
- Discover, share, and integrate tools that give your agents superpowers.
-
- The registry for AI tools.
-
-
{/* Brutalist Search Interface */}
diff --git a/apps/web/src/components/home/VisionSection.tsx b/apps/web/src/components/home/VisionSection.tsx
index 3f91f7b..341c2b7 100644
--- a/apps/web/src/components/home/VisionSection.tsx
+++ b/apps/web/src/components/home/VisionSection.tsx
@@ -63,7 +63,7 @@ export function VisionSection(): React.ReactElement {
- The registry that gives agents superpowers
+ One registry. Thousands of tools. Zero configuration.
diff --git a/broken-tools.md b/broken-tools.md
deleted file mode 100644
index 8f0d95e..0000000
--- a/broken-tools.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Broken Tools Classification
-
-This document tracks the different types of tool failures encountered in the TPMJS executor and the strategies for handling them.
-
-## Error Categories
-
-| Error Type | Example | Root Cause | Strategy |
-|------------|---------|------------|----------|
-| **Invalid structure** | `fish-joke-generator`, `@tpmjs/text-transformer` | Not an AI SDK tool (missing `description` or `execute`) | Mark as BROKEN, filter from search results |
-| **Module not found** | `@thomasdavis/cows@0.0.1` | Package doesn't exist on npm/esm.sh | Mark as BROKEN, consider removing from registry |
-| **Factory function** | `@tavily/ai-sdk/tavilySearch` | Tool is a factory that needs config to initialize | Need to detect and call with appropriate config |
-| **Missing env var** | `@exalabs/ai-sdk/webSearch` | Requires API key (e.g., `EXA_API_KEY`) not provided | Import: HEALTHY, Execution: BROKEN with clear error message |
-| **Missing execution context** | `@parallel-web/ai-sdk-tools/extractTool` | Tool expects `{ abortSignal }` as 2nd arg to `execute()` | Fix executor to pass execution context |
-
-## Detailed Examples
-
-### Invalid Structure
-```
-❌ Invalid AI SDK tool structure: {
- hasDescription: false,
- hasExecute: false,
- hasInputSchema: false,
- keys: ["FishJokeSchema", "fishJoker", "createFishJoker", ...]
-}
-```
-These packages export utility functions or schemas, not AI SDK tools.
-
-### Module Not Found
-```
-❌ Failed to load tool: TypeError: Module not found "https://esm.sh/@thomasdavis/cows@0.0.1"
-```
-Package was registered but doesn't exist on npm or was unpublished.
-
-### Factory Function
-```
-❌ Tool "tavilySearch" is a factory function but couldn't be initialized.
- Tried: no-args, config object, and single-arg patterns.
- Hint: This tool may require specific configuration. Check package documentation.
-```
-Tool exports a factory like `tavilySearch({ apiKey })` instead of a ready-to-use tool object.
-
-### Missing Env Var
-```
-❌ EXA_API_KEY is required. Set it in environment variables or pass it in config.
-```
-Tool loaded successfully but execution fails without required credentials.
-
-### Missing Execution Context
-```
-❌ Tool execution failed: TypeError: Cannot destructure property 'abortSignal' of 'undefined' as it is undefined.
- at Object.execute (https://esm.sh/@parallel-web/ai-sdk-tools@0.1.6/...)
-```
-AI SDK tools expect `execute(params, { abortSignal, ... })` but executor only passes params.
-
-## Resolution Status
-
-- [x] Invalid structure - Health check marks as BROKEN ✓
-- [x] Module not found - Health check marks as BROKEN ✓
-- [ ] Factory function - Partial support (tries common patterns)
-- [x] Missing env var - Import: HEALTHY, Execution: HEALTHY (config issue, not broken)
-- [x] Missing execution context - **Fixed in executor** (commit 0804f1b)
diff --git a/package.json b/package.json
index 1af9e20..699928b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,18 @@
{
"name": "@tpmjs/monorepo",
"version": "0.0.0",
+ "description": "Tool Package Manager for AI Agents - discover, share, and integrate tools",
+ "author": "TPMJS",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tpmjs/tpmjs.git"
+ },
+ "homepage": "https://tpmjs.com",
+ "bugs": {
+ "url": "https://github.com/tpmjs/tpmjs/issues"
+ },
+ "keywords": ["tpmjs", "ai", "agents", "tools", "mcp", "typescript", "npm"],
"private": true,
"type": "module",
"engines": {
diff --git a/packages/env/package.json b/packages/env/package.json
index 0bbbd9d..9e4817c 100644
--- a/packages/env/package.json
+++ b/packages/env/package.json
@@ -1,6 +1,16 @@
{
"name": "@tpmjs/env",
"version": "0.1.1",
+ "description": "Environment variable validation with Zod for TPMJS",
+ "author": "TPMJS",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tpmjs/tpmjs.git",
+ "directory": "packages/env"
+ },
+ "homepage": "https://tpmjs.com",
+ "keywords": ["tpmjs", "typescript", "environment", "zod", "validation"],
"type": "module",
"exports": {
".": {
diff --git a/packages/types/package.json b/packages/types/package.json
index 4cfd4ce..690aa87 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,16 @@
{
"name": "@tpmjs/types",
"version": "0.1.1",
+ "description": "Shared TypeScript types and Zod schemas for TPMJS",
+ "author": "TPMJS",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tpmjs/tpmjs.git",
+ "directory": "packages/types"
+ },
+ "homepage": "https://tpmjs.com",
+ "keywords": ["tpmjs", "typescript", "types", "zod", "schemas"],
"type": "module",
"exports": {
"./tool": {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index ba4c0e9..b80d6a5 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,16 @@
{
"name": "@tpmjs/ui",
"version": "0.1.3",
+ "description": "React component library for TPMJS applications",
+ "author": "TPMJS",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tpmjs/tpmjs.git",
+ "directory": "packages/ui"
+ },
+ "homepage": "https://tpmjs.com",
+ "keywords": ["tpmjs", "react", "components", "typescript", "ui"],
"type": "module",
"exports": {
"./Button/Button": {
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 6cfdc8e..000ebb8 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,16 @@
{
"name": "@tpmjs/utils",
"version": "0.1.1",
+ "description": "Utility functions for TPMJS applications",
+ "author": "TPMJS",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tpmjs/tpmjs.git",
+ "directory": "packages/utils"
+ },
+ "homepage": "https://tpmjs.com",
+ "keywords": ["tpmjs", "typescript", "utilities", "tailwind"],
"type": "module",
"exports": {
"./cn": {