{"_id":"@chutes-ai/ai-sdk-provider","_rev":"3-4ace26f9aeda9473359ece8acbc275f2","name":"@chutes-ai/ai-sdk-provider","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@chutes-ai/ai-sdk-provider","version":"0.1.0","keywords":["ai","ai-sdk","provider","chutes","llm","openai","anthropic"],"author":{"name":"Chutes.ai"},"license":"MIT","_id":"@chutes-ai/ai-sdk-provider@0.1.0","maintainers":[{"name":"chutesdev","email":"packages@chutes.ai"}],"homepage":"https://github.com/tududes/ai-sdk-provider-chutes","bugs":{"url":"https://github.com/tududes/ai-sdk-provider-chutes/issues"},"dist":{"shasum":"b4498aa4b452b1bf00782c93c91d9f952d947b0e","tarball":"https://registry.npmjs.org/@chutes-ai/ai-sdk-provider/-/ai-sdk-provider-0.1.0.tgz","fileCount":9,"integrity":"sha512-XDCsBdu5qnJo2un6+ExR7W0D6IpzBfp0yAsfGit5GuG1N7d+7qdAasF7RYHAMTfE37C4LdwKk0r2jpfOWtggTQ==","signatures":[{"sig":"MEQCIAb47LAR55Kng5iNwgjPLrpygv0ZfUxjJqyArQ7qlK5iAiADWjfeQYScrIGaHrO8SJHdcokNpp4+S4KaFMQDCI98fg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":682347},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"0202d891be1daa322b3814e63e82e17cc3960e65","scripts":{"dev":"tsup --watch","test":"vitest run tests/unit","build":"tsup","release":"release-it","test:ui":"vitest --ui","test:all":"vitest run","test:slow":"vitest run tests/slow","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","release:dry":"release-it --dry-run","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build","test:integration":"vitest run tests/integration"},"_npmUser":{"name":"chutesdev","email":"packages@chutes.ai"},"repository":{"url":"git+https://github.com/tududes/ai-sdk-provider-chutes.git","type":"git"},"_npmVersion":"10.9.2","description":"Chutes.ai provider for the Vercel AI SDK","directories":{},"_nodeVersion":"22.14.0","dependencies":{"@ai-sdk/provider":"^2.0.0","@ai-sdk/provider-utils":"^3.0.17"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.1","dotenv":"^17.2.3","vitest":"^4.0.8","@vitest/ui":"^4.0.8","release-it":"^19.0.6","typescript":"^5.9.3","@types/node":"^24.10.1","@release-it/conventional-changelog":"^10.0.2"},"peerDependencies":{"ai":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-provider_0.1.0_1764803231771_0.9737829149294639","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@chutes-ai/ai-sdk-provider","version":"0.1.1","keywords":["ai","ai-sdk","provider","chutes","llm","openai","anthropic"],"author":{"name":"Chutes.ai"},"license":"MIT","_id":"@chutes-ai/ai-sdk-provider@0.1.1","maintainers":[{"name":"chutesdev","email":"packages@chutes.ai"}],"homepage":"https://github.com/tududes/ai-sdk-provider-chutes","bugs":{"url":"https://github.com/tududes/ai-sdk-provider-chutes/issues"},"dist":{"shasum":"5fe5a01055dce8e4111ab1fcb76fa14e69bae34a","tarball":"https://registry.npmjs.org/@chutes-ai/ai-sdk-provider/-/ai-sdk-provider-0.1.1.tgz","fileCount":9,"integrity":"sha512-bUHMDzqSX4/sSGbp5BWbzQ1PZUM3f67062SrnBkzNtcK21C3DV2y4KI+kJyZI/5a1a8KQXzU66CL7D+e1Ab/Rg==","signatures":[{"sig":"MEQCIC4Stlcr3hkjBsco7mdNzOTJ7QjhQUcKG8Gu/+VUvuFVAiAVBH+CYuftrGCgWd9FY+ooTAphe62vzCAdMD/D72K6Sg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":682347},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"afe714c43a4557164f3164ac41d0b5f53bcc911a","scripts":{"dev":"tsup --watch","test":"vitest run tests/unit","build":"tsup","release":"release-it","test:ui":"vitest --ui","test:all":"vitest run","test:slow":"vitest run tests/slow","test:unit":"vitest run tests/unit","typecheck":"tsc --noEmit","test:watch":"vitest","release:dry":"release-it --dry-run","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build","test:integration":"vitest run tests/integration"},"_npmUser":{"name":"chutesdev","email":"packages@chutes.ai"},"repository":{"url":"git+https://github.com/tududes/ai-sdk-provider-chutes.git","type":"git"},"_npmVersion":"10.9.2","description":"Chutes.ai provider for the Vercel AI SDK","directories":{},"_nodeVersion":"22.14.0","dependencies":{"@ai-sdk/provider":"^2.0.0","@ai-sdk/provider-utils":"^3.0.17"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","tsup":"^8.5.1","dotenv":"^17.2.3","vitest":"^4.0.8","@vitest/ui":"^4.0.8","release-it":"^19.0.6","typescript":"^5.9.3","@types/node":"^24.10.1","@release-it/conventional-changelog":"^10.0.2"},"peerDependencies":{"ai":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk-provider_0.1.1_1764806794678_0.5526642492751368","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@chutes-ai/ai-sdk-provider","version":"0.1.2","description":"Chutes.ai provider for the Vercel AI SDK","keywords":["ai","ai-sdk","provider","chutes","llm","openai","anthropic"],"homepage":"https://github.com/chutesai/ai-sdk-provider-chutes","bugs":{"url":"https://github.com/chutesai/ai-sdk-provider-chutes/issues"},"repository":{"type":"git","url":"git+https://github.com/chutesai/ai-sdk-provider-chutes.git"},"license":"MIT","author":{"name":"Chutes.ai"},"main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run tests/unit","test:unit":"vitest run tests/unit","test:integration":"vitest run tests/integration","test:slow":"vitest run tests/slow","test:all":"vitest run","test:watch":"vitest","test:ui":"vitest --ui","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","release":"release-it","release:dry":"release-it --dry-run"},"peerDependencies":{"ai":">=4.0.0"},"dependencies":{"@ai-sdk/provider":"^2.0.0","@ai-sdk/provider-utils":"^3.0.17"},"devDependencies":{"@release-it/conventional-changelog":"^10.0.2","@types/node":"^24.10.1","@vitest/ui":"^4.0.8","dotenv":"^17.2.3","release-it":"^19.0.6","tsup":"^8.5.1","tsx":"^4.20.6","typescript":"^5.9.3","vitest":"^4.0.8"},"engines":{"node":">=18"},"_id":"@chutes-ai/ai-sdk-provider@0.1.2","gitHead":"935e1b21a3917f490d3d194267bad8895edcb001","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-CwPqVTM/nlCHOKltXcFP3Y5ifje3PsbdhogZHHNuUvr8VwdJgtwsXi3CwkwHXX15dEwF5PqRu0T8ngCYayYdaA==","shasum":"8981032435d082730fbd390b58f79621575beaa4","tarball":"https://registry.npmjs.org/@chutes-ai/ai-sdk-provider/-/ai-sdk-provider-0.1.2.tgz","fileCount":9,"unpackedSize":682344,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFyZ1hqURZL+B0g2RpajwvcWdKoYsIctJ5NJtuE7Zw6TAiBySERnjlpQWSau/iEtB+u6yrADkXyH8hy7oNf6uSu+TA=="}]},"_npmUser":{"name":"chutesdev","email":"packages@chutes.ai"},"directories":{},"maintainers":[{"name":"chutesdev","email":"packages@chutes.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-sdk-provider_0.1.2_1764862756506_0.7534624271396011"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-03T23:07:11.663Z","modified":"2025-12-04T15:39:16.937Z","0.1.0":"2025-12-03T23:07:11.930Z","0.1.1":"2025-12-04T00:06:34.901Z","0.1.2":"2025-12-04T15:39:16.681Z"},"bugs":{"url":"https://github.com/chutesai/ai-sdk-provider-chutes/issues"},"author":{"name":"Chutes.ai"},"license":"MIT","homepage":"https://github.com/chutesai/ai-sdk-provider-chutes","keywords":["ai","ai-sdk","provider","chutes","llm","openai","anthropic"],"repository":{"type":"git","url":"git+https://github.com/chutesai/ai-sdk-provider-chutes.git"},"description":"Chutes.ai provider for the Vercel AI SDK","maintainers":[{"name":"chutesdev","email":"packages@chutes.ai"}],"readme":"# Chutes.ai Provider for Vercel AI SDK\r\n\r\nA production-ready provider for using **open-source AI models** hosted on [Chutes.ai](https://chutes.ai) with the Vercel AI SDK.\r\n\r\n[![npm version](https://img.shields.io/npm/v/@chutes-ai/ai-sdk-provider)](https://www.npmjs.com/package/@chutes-ai/ai-sdk-provider)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\n## Features\r\n\r\n✅ **Language Models** - Complete support for chat and text completion  \r\n✅ **Streaming** - Real-time Server-Sent Events (SSE) streaming  \r\n✅ **Tool Calling** - Full function/tool calling support  \r\n✅ **Embeddings** - Text embedding generation with batch support  \r\n✅ **Image Generation** - AI-powered image creation  \r\n✅ **Video Generation** - Text-to-video and image-to-video creation  \r\n✅ **Text-to-Speech** - Natural voice synthesis with 54 pre-defined voices  \r\n✅ **Speech-to-Text** - Audio transcription and recognition  \r\n✅ **Music Generation** - AI-powered music composition  \r\n✅ **Content Moderation** - Automated content safety analysis  \r\n✅ **Custom Inference** - Flexible prediction and batch processing  \r\n✅ **Chute Warmup (Therm)** - Pre-warm chutes for instant response times  \r\n✅ **Dynamic Discovery** - Automatic model discovery from Chutes.ai API  \r\n✅ **Open Source Only** - Built exclusively for open-source models  \r\n✅ **TypeScript** - Fully typed for excellent IDE support  \r\n✅ **Error Handling** - Comprehensive error mapping and retry logic  \r\n✅ **Test Coverage** - 327+ tests with comprehensive coverage\r\n\r\n## Versioning\r\n\r\nThis project follows [Semantic Versioning 2.0.0](https://semver.org/):\r\n- **MAJOR** version for incompatible API changes\r\n- **MINOR** version for new functionality in a backwards compatible manner\r\n- **PATCH** version for backwards compatible bug fixes\r\n\r\nAll releases are tagged and available on the [releases page](https://github.com/chutesai/ai-sdk-provider-chutes/releases).\r\n\r\n## Changelog\r\n\r\nAll notable changes are documented in the [CHANGELOG.md](./CHANGELOG.md) file. The changelog is automatically generated using [Conventional Commits](https://www.conventionalcommits.org/).\r\n\r\n## Why Chutes.ai?\r\n\r\nChutes.ai provides easy access to **open-source AI models** like:\r\n- **DeepSeek-V3** & **DeepSeek-R1** - State-of-the-art reasoning models\r\n- **Llama 3.1** - Meta's powerful open-source LLM\r\n- **Qwen 2.5** - Alibaba's multilingual model\r\n- **Mistral** - High-performance European AI models\r\n- **FLUX** - Advanced open-source image generation\r\n\r\nUnlike other providers, Chutes focuses exclusively on open-source models, giving you full transparency and control.\r\n\r\n## Installation\r\n\r\n### From npm (Published Package)\r\n\r\n```bash\r\nnpm install @chutes-ai/ai-sdk-provider ai\r\n```\r\n\r\n**Note:** This package works with AI SDK v4 and v5. For Next.js projects with TypeScript, **AI SDK v5 is recommended**:\r\n\r\n```bash\r\nnpm install @chutes-ai/ai-sdk-provider ai@^5.0.0\r\n```\r\n\r\n### From GitHub (Private Access or Development)\r\n\r\n**For team members or beta testers:** Install directly from the GitHub repository (works with Vercel deployments):\r\n\r\n```bash\r\n# Install from GitHub repository\r\nnpm install git+https://github.com/YOUR_USERNAME/ai-sdk-provider-chutes.git\r\n\r\n# Or install a specific version/branch/commit\r\nnpm install git+https://github.com/YOUR_USERNAME/ai-sdk-provider-chutes.git#v0.1.0\r\nnpm install git+https://github.com/YOUR_USERNAME/ai-sdk-provider-chutes.git#main\r\n```\r\n\r\n**In your `package.json`:**\r\n```json\r\n{\r\n  \"dependencies\": {\r\n    \"@chutes-ai/ai-sdk-provider\": \"git+https://github.com/YOUR_USERNAME/ai-sdk-provider-chutes.git\",\r\n    \"ai\": \"latest\"\r\n  }\r\n}\r\n```\r\n\r\n**For Vercel deployments:**\r\n- Add the dependency to `package.json` as shown above\r\n- Set `CHUTES_API_KEY` in your Vercel project's environment variables\r\n- Vercel will automatically install from GitHub during build\r\n- No additional configuration needed!\r\n\r\n### From Tarball (Offline or Private Distribution)\r\n\r\n```bash\r\n# Install from a local tarball file\r\nnpm install ./chutes-ai-ai-sdk-provider-0.1.0.tgz\r\n\r\n# Or from a hosted tarball URL\r\nnpm install https://example.com/path/to/package.tgz\r\n```\r\n\r\nTo create a tarball for distribution:\r\n```bash\r\nnpm run build\r\nnpm pack\r\n# This creates: chutes-ai-ai-sdk-provider-0.1.0.tgz\r\n```\r\n\r\n### Local Development and Testing\r\n\r\nFor local development and testing with `npm link`, see [TESTING.md](./TESTING.md).\r\n\r\n## Quick Start\r\n\r\n### Setup\r\n\r\nGet your API key from [Chutes.ai](https://chutes.ai) and set it as an environment variable:\r\n\r\n```bash\r\nexport CHUTES_API_KEY=your-api-key-here\r\n```\r\n\r\n### Basic Usage\r\n\r\n```typescript\r\nimport { chutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\n// Use a chute URL (recommended)\r\nconst model = chutes('https://chutes-deepseek-ai-deepseek-v3.chutes.ai');\r\n\r\n// Or use a chute slug\r\nconst model2 = chutes('chutes-deepseek-v3');\r\n\r\n// Generate text\r\nconst result = await model.doGenerate({\r\n  inputFormat: 'prompt',\r\n  mode: { type: 'regular' },\r\n  prompt: [\r\n    {\r\n      role: 'user',\r\n      content: [{ type: 'text', text: 'Explain quantum computing in simple terms' }],\r\n    },\r\n  ],\r\n});\r\n\r\nconsole.log(result.text);\r\n```\r\n\r\n### Using a Default Model (Lazy Discovery)\r\n\r\nIf you don't want to specify a model ID every time, you can configure a default model or let the provider automatically discover one:\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\n// Option 1: Configure a default model explicitly\r\nconst chutes = createChutes({ \r\n  apiKey: process.env.CHUTES_API_KEY,\r\n  defaultModel: 'https://chutes-deepseek-ai-deepseek-v3.chutes.ai'\r\n});\r\n\r\n// Now you can call without a model ID\r\nconst model = await chutes(); // Uses the configured default\r\nconst result = await model.doGenerate({ /* ... */ });\r\n\r\n// Option 2: Set via environment variable (recommended for production)\r\nprocess.env.CHUTES_DEFAULT_MODEL = 'https://chutes-deepseek-ai-deepseek-v3.chutes.ai';\r\n\r\nconst chutes2 = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\nconst model2 = await chutes2(); // Uses CHUTES_DEFAULT_MODEL\r\n\r\n// Option 3: Lazy discovery (automatically finds first available LLM)\r\n// If no default is set, the provider will:\r\n// 1. Warn that no default is configured\r\n// 2. Discover the first available LLM chute\r\n// 3. Store it in process.env.CHUTES_DEFAULT_MODEL for the session\r\nconst chutes3 = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\nconst model3 = await chutes3(); // ⚠️  Discovers and warns on first use\r\n\r\n// Explicit model ID always takes precedence\r\nconst explicitModel = chutes('https://chutes-custom.chutes.ai'); // No default used\r\n```\r\n\r\n**Best Practice:** Set `CHUTES_DEFAULT_MODEL` in your environment to avoid discovery delays:\r\n\r\n```bash\r\nexport CHUTES_DEFAULT_MODEL=https://chutes-deepseek-ai-deepseek-v3.chutes.ai\r\n```\r\n\r\n## Discovering Available Models\r\n\r\nThe provider supports dynamic model discovery to help you find and inspect available chutes:\r\n\r\n### List All Models\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst chutes = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\n\r\n// Get all available models\r\nconst allModels = await chutes.listModels();\r\nconsole.log(`Found ${allModels.length} models`);\r\n\r\n// Filter by type\r\nconst llmModels = await chutes.listModels('llm');\r\nconst imageModels = await chutes.listModels('image');\r\nconst embeddingModels = await chutes.listModels('embedding');\r\n```\r\n\r\n### Get Model Capabilities\r\n\r\n```typescript\r\n// By slug\r\nconst capabilities = await chutes.getModelCapabilities('chutes-deepseek-v3');\r\n\r\n// By URL\r\nconst capabilities = await chutes.getModelCapabilities('https://chutes-deepseek-v3.chutes.ai');\r\n\r\n// By chute_id (UUID)\r\nconst capabilities = await chutes.getModelCapabilities('4f82321e-3e58-55da-ba44-051686ddbfe5');\r\n\r\nconsole.log(capabilities);\r\n// {\r\n//   chat: true,\r\n//   streaming: true,\r\n//   tools: true,\r\n//   functionCalling: true,\r\n//   contextWindow: 64000,\r\n//   inputModalities: ['text'],\r\n//   outputModalities: ['text'],\r\n//   ...\r\n// }\r\n```\r\n\r\n### Supported Model Types\r\n\r\n- `llm` - Language models (DeepSeek, Llama, Qwen, Mistral, etc.)\r\n- `image` - Image generation (Flux, Stable Diffusion, etc.)\r\n- `embedding` - Text embeddings\r\n- `video` - Video generation\r\n- `tts` - Text-to-speech\r\n- `stt` - Speech-to-text\r\n- `music` - Music generation\r\n\r\n## Understanding Chutes\r\n\r\nA **chute** is a deployed open-source model instance on Chutes.ai. Each chute has:\r\n- A unique URL: `https://{slug}.chutes.ai`\r\n- An OpenAI-compatible API endpoint\r\n- Specific model capabilities\r\n\r\n### Finding Available Chutes\r\n\r\n```typescript\r\nimport { createChutes, ChutesModelRegistry } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst provider = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\n\r\n// Create registry\r\nconst registry = new ChutesModelRegistry({\r\n  provider: 'chutes',\r\n  baseURL: 'https://api.chutes.ai',\r\n  headers: () => ({ 'Authorization': `Bearer ${process.env.CHUTES_API_KEY}` }),\r\n});\r\n\r\n// Fetch available chutes\r\nconst chutes = await registry.fetchAvailableChutes();\r\nconsole.log(`Found ${chutes.length} chutes`);\r\n\r\n// Filter by type\r\nconst llmChutes = registry.getLLMChutes();\r\nconst imageChutes = registry.getImageChutes();\r\n```\r\n\r\n## Usage Examples\r\n\r\n📂 **Complete Examples**: See the [`examples/`](./examples) folder for full working examples:\r\n- [`basic-chat.ts`](./examples/basic-chat.ts) - Language model basics\r\n- [`list-models.ts`](./examples/list-models.ts) - Dynamic model discovery\r\n- [`streaming.ts`](./examples/streaming.ts) - Streaming responses\r\n- [`tool-calling.ts`](./examples/tool-calling.ts) - Function calling\r\n- [`embeddings.ts`](./examples/embeddings.ts) - Text embeddings\r\n- [`image-generation.ts`](./examples/image-generation.ts) - Image generation\r\n- [`video-generation.ts`](./examples/video-generation.ts) - Video generation\r\n- [`text-to-speech.ts`](./examples/text-to-speech.ts) - Text-to-speech\r\n- [`speech-to-text.ts`](./examples/speech-to-text.ts) - Speech-to-text\r\n- [`music-generation.ts`](./examples/music-generation.ts) - Music generation\r\n- [`content-moderation.ts`](./examples/content-moderation.ts) - Content moderation\r\n- [`custom-inference.ts`](./examples/custom-inference.ts) - Custom inference\r\n- [`chute-warmup.ts`](./examples/chute-warmup.ts) - Chute warmup (Therm) ⚡\r\n\r\n### Language Models\r\n\r\n#### Text Generation\r\n\r\n```typescript\r\nimport { chutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst model = chutes('https://chutes-qwen-qwen2-5-72b-instruct.chutes.ai');\r\n\r\nconst result = await model.doGenerate({\r\n  inputFormat: 'prompt',\r\n  mode: { type: 'regular' },\r\n  prompt: [\r\n    { role: 'system', content: 'You are a helpful assistant' },\r\n    { role: 'user', content: [{ type: 'text', text: 'Hello!' }] },\r\n  ],\r\n  temperature: 0.7,\r\n  maxTokens: 500,\r\n});\r\n\r\nconsole.log(result.text);\r\n```\r\n\r\n#### Streaming\r\n\r\n```typescript\r\nconst model = chutes('https://chutes-meta-llama-llama-3-1-70b-instruct.chutes.ai');\r\n\r\nconst stream = await model.doStream({\r\n  inputFormat: 'prompt',\r\n  mode: { type: 'regular' },\r\n  prompt: [\r\n    { role: 'user', content: [{ type: 'text', text: 'Count from 1 to 10' }] },\r\n  ],\r\n});\r\n\r\nfor await (const chunk of stream) {\r\n  if (chunk.type === 'text-delta') {\r\n    process.stdout.write(chunk.textDelta);\r\n  }\r\n}\r\n```\r\n\r\n#### Tool Calling\r\n\r\n```typescript\r\nconst model = chutes('https://chutes-deepseek-ai-deepseek-v3.chutes.ai');\r\n\r\nconst tools = [\r\n  {\r\n    type: 'function' as const,\r\n    name: 'get_weather',\r\n    description: 'Get current weather',\r\n    parameters: {\r\n      type: 'object',\r\n      properties: {\r\n        location: { type: 'string', description: 'City name' },\r\n      },\r\n      required: ['location'],\r\n    },\r\n  },\r\n];\r\n\r\nconst result = await model.doGenerate({\r\n  inputFormat: 'prompt',\r\n  mode: { type: 'regular', tools },\r\n  prompt: [\r\n    { role: 'user', content: [{ type: 'text', text: 'What is the weather in Paris?' }] },\r\n  ],\r\n});\r\n\r\nif (result.toolCalls && result.toolCalls.length > 0) {\r\n  console.log('Tool called:', result.toolCalls[0].toolName);\r\n  console.log('Arguments:', result.toolCalls[0].args);\r\n}\r\n```\r\n\r\n### Embeddings\r\n\r\n```typescript\r\nconst embeddingModel = chutes.textEmbeddingModel('text-embedding-3-small');\r\n\r\nconst result = await embeddingModel.doEmbed({\r\n  values: [\r\n    'The quick brown fox jumps over the lazy dog',\r\n    'Machine learning is a subset of artificial intelligence',\r\n  ],\r\n});\r\n\r\nconsole.log(`Generated ${result.embeddings.length} embeddings`);\r\nconsole.log(`Dimension: ${result.embeddings[0].length}`);\r\n```\r\n\r\n### Image Generation\r\n\r\n```typescript\r\nconst imageModel = chutes.imageModel('flux-dev');\r\n\r\nconst result = await imageModel.doGenerate({\r\n  prompt: 'A serene mountain landscape at sunset',\r\n  n: 1,\r\n  size: '1024x1024',\r\n});\r\n\r\nconsole.log('Generated image:', result.images[0].url);\r\n```\r\n\r\n### Video Generation\r\n\r\nGenerate videos from text prompts or animate existing images:\r\n\r\n```typescript\r\nconst videoModel = chutes.videoModel('your-video-chute-id');\r\n\r\n// Text-to-Video\r\nconst videoResult = await videoModel.generateVideo({\r\n  prompt: 'A serene sunset over mountains with birds flying',\r\n  resolution: '1024x576',\r\n  fps: 24,\r\n  steps: 30,\r\n  outputFormat: 'buffer', // or 'base64'\r\n});\r\n\r\n// Save video\r\nimport * as fs from 'fs';\r\nif (Buffer.isBuffer(videoResult.video)) {\r\n  fs.writeFileSync('output.mp4', videoResult.video);\r\n}\r\n\r\n// Image-to-Video (animate an image)\r\nconst animatedResult = await videoModel.animateImage({\r\n  prompt: 'Make the image come alive with gentle movement',\r\n  image: 'https://example.com/image.jpg', // URL, base64, or Buffer\r\n  fps: 24,\r\n  steps: 25,\r\n});\r\n```\r\n\r\n**Video Settings:**\r\n- `resolution`: Video dimensions (e.g., '1024x576', '512x512')\r\n- `fps`: Frames per second (12-30)\r\n- `steps`: Generation quality (more steps = higher quality)\r\n- `frames`: Total frames to generate\r\n- `seed`: For deterministic generation\r\n- `outputFormat`: 'base64' (data URI) or 'buffer' (binary)\r\n\r\n### Text-to-Speech (TTS)\r\n\r\nConvert text to natural-sounding speech with 54 pre-defined voices:\r\n\r\n```typescript\r\nconst audioModel = chutes.audioModel('your-tts-chute-id');\r\n\r\n// Basic TTS\r\nconst speechResult = await audioModel.textToSpeech({\r\n  text: 'Hello! This is a test of the text-to-speech system.',\r\n  voice: 'af_bella', // American Female - Bella\r\n  speed: 1.0,\r\n  outputFormat: 'buffer',\r\n});\r\n\r\n// Save audio\r\nimport * as fs from 'fs';\r\nif (Buffer.isBuffer(speechResult.audio)) {\r\n  fs.writeFileSync('output.mp3', speechResult.audio);\r\n}\r\n```\r\n\r\n**Available Voice Categories:**\r\n- 🇺🇸 **American English**: 20 voices (11 female, 9 male)\r\n- 🇬🇧 **British English**: 8 voices (4 female, 4 male)\r\n- 🇪🇸 **Spanish**: 3 voices\r\n- 🇫🇷 **French**: 1 voice\r\n- 🇮🇳 **Hindi**: 4 voices\r\n- 🇮🇹 **Italian**: 2 voices\r\n- 🇯🇵 **Japanese**: 5 voices\r\n- 🇧🇷 **Portuguese (BR)**: 3 voices\r\n- 🇨🇳 **Mandarin Chinese**: 8 voices\r\n\r\n**Voice Discovery:**\r\n```typescript\r\nimport { listAvailableVoices, getVoicesByLanguage } from '@chutes-ai/ai-sdk-provider';\r\n\r\n// List all 54 voices\r\nconst allVoices = listAvailableVoices();\r\n\r\n// Get voices by language\r\nconst englishVoices = getVoicesByLanguage('en-US');\r\n```\r\n\r\n**Popular Voices:**\r\n- `af_bella` - Warm, friendly American female\r\n- `am_adam` - Professional American male\r\n- `bf_emma` - Clear British female\r\n- `bm_george` - Authoritative British male\r\n\r\n### Speech-to-Text (STT)\r\n\r\nTranscribe audio to text with high accuracy:\r\n\r\n```typescript\r\nconst audioModel = chutes.audioModel('your-stt-chute-id');\r\n\r\n// From audio file (Buffer)\r\nimport * as fs from 'fs';\r\nconst audioBuffer = fs.readFileSync('audio.mp3');\r\n\r\nconst transcription = await audioModel.speechToText({\r\n  audio: audioBuffer,\r\n  language: 'en', // Optional: specify language\r\n});\r\n\r\nconsole.log('Transcription:', transcription.text);\r\nconsole.log('Language:', transcription.metadata?.language);\r\nconsole.log('Duration:', transcription.metadata?.duration);\r\n\r\n// From URL\r\nconst urlTranscription = await audioModel.speechToText({\r\n  audio: 'https://example.com/audio.mp3',\r\n});\r\n\r\n// From base64\r\nconst base64Transcription = await audioModel.speechToText({\r\n  audio: audioBase64String,\r\n});\r\n```\r\n\r\n**Input Formats:**\r\n- Buffer (from file)\r\n- base64 string\r\n- URL (HTTP/HTTPS)\r\n- Supports: MP3, WAV, M4A, FLAC, and more\r\n\r\n### Music Generation\r\n\r\nGenerate AI-powered music from text descriptions:\r\n\r\n```typescript\r\nconst audioModel = chutes.audioModel('your-music-chute-id');\r\n\r\nconst musicResult = await audioModel.generateMusic({\r\n  prompt: 'Upbeat electronic dance music with synthesizers',\r\n  duration: 10, // seconds\r\n  outputFormat: 'buffer',\r\n});\r\n\r\n// Save music\r\nimport * as fs from 'fs';\r\nif (Buffer.isBuffer(musicResult.audio)) {\r\n  fs.writeFileSync('generated-music.mp3', musicResult.audio);\r\n}\r\n```\r\n\r\n**Music Styles:**\r\n```typescript\r\n// Classical\r\nawait audioModel.generateMusic({\r\n  prompt: 'Classical baroque harpsichord composition',\r\n  duration: 15,\r\n});\r\n\r\n// Rock\r\nawait audioModel.generateMusic({\r\n  prompt: 'Energetic rock guitar with drums, powerful and intense',\r\n  duration: 20,\r\n});\r\n\r\n// Jazz\r\nawait audioModel.generateMusic({\r\n  prompt: 'Smooth jazz saxophone with double bass, sophisticated and mellow',\r\n  duration: 15,\r\n});\r\n\r\n// Ambient\r\nawait audioModel.generateMusic({\r\n  prompt: 'Ambient electronic soundscape, atmospheric and ethereal',\r\n  duration: 30,\r\n});\r\n```\r\n\r\n### Content Moderation\r\n\r\nAnalyze content for safety and compliance:\r\n\r\n```typescript\r\nconst moderationModel = chutes.moderationModel('your-moderation-chute-id');\r\n\r\nconst moderationResult = await moderationModel.analyzeContent({\r\n  content: 'Text to analyze for moderation',\r\n  categories: ['hate', 'violence', 'sexual', 'self-harm'], // Optional\r\n});\r\n\r\nconsole.log('Flagged:', moderationResult.flagged);\r\n\r\nmoderationResult.categories.forEach(category => {\r\n  console.log(`${category.category}: ${category.flagged ? 'FLAGGED' : 'OK'}`);\r\n  console.log(`  Confidence: ${(category.score * 100).toFixed(2)}%`);\r\n});\r\n```\r\n\r\n**Moderation Categories:**\r\n- `hate` - Hate speech and discrimination\r\n- `violence` - Violent content and threats\r\n- `sexual` - Sexual or adult content\r\n- `self-harm` - Self-harm or suicide content\r\n- Custom categories based on your moderation model\r\n\r\n**Custom Thresholds:**\r\n```typescript\r\nconst result = await moderationModel.analyzeContent({\r\n  content: 'Content to check',\r\n});\r\n\r\n// Apply custom threshold (e.g., 30%)\r\nconst customThreshold = 0.3;\r\nconst customFlagged = result.categories.some(cat => cat.score > customThreshold);\r\n\r\nif (customFlagged) {\r\n  console.log('Content flagged by custom threshold');\r\n}\r\n```\r\n\r\n### Custom Inference\r\n\r\nFlexible inference for custom models and workflows:\r\n\r\n```typescript\r\nconst inferenceModel = chutes.inferenceModel('your-inference-chute-id');\r\n\r\n// Single prediction\r\nconst prediction = await inferenceModel.predict({\r\n  modelId: 'your-model-id',\r\n  input: {\r\n    text: 'Input data',\r\n    parameters: {\r\n      temperature: 0.7,\r\n      max_tokens: 100,\r\n    },\r\n  },\r\n});\r\n\r\nconsole.log('Result:', prediction.output);\r\n\r\n// Batch inference\r\nconst batchResult = await inferenceModel.batch({\r\n  modelId: 'your-model-id',\r\n  inputs: [\r\n    { text: 'First input', id: 1 },\r\n    { text: 'Second input', id: 2 },\r\n    { text: 'Third input', id: 3 },\r\n  ],\r\n});\r\n\r\nconsole.log('Job ID:', batchResult.jobId);\r\nconsole.log('Results:', batchResult.outputs);\r\n\r\n// Check job status\r\nconst status = await inferenceModel.getStatus({\r\n  jobId: batchResult.jobId!,\r\n});\r\n\r\nconsole.log('Status:', status.status); // 'pending', 'processing', 'completed', 'failed'\r\nconsole.log('Result:', status.result);\r\n```\r\n\r\n**Webhook Integration:**\r\n```typescript\r\n// Get results via webhook instead of polling\r\nconst result = await inferenceModel.predict({\r\n  modelId: 'your-model-id',\r\n  input: { text: 'Input' },\r\n  webhookUrl: 'https://your-domain.com/webhook/results',\r\n  priority: 'high', // 'low', 'normal', 'high'\r\n});\r\n\r\nconsole.log('Job submitted:', result.jobId);\r\n// Results will be POSTed to your webhook when ready\r\n```\r\n\r\n**Priority Processing:**\r\n- `low` - Best effort processing\r\n- `normal` - Standard queue (default)\r\n- `high` - Priority processing\r\n\r\n### Chute Warmup (Therm)\r\n\r\nPre-warm chutes to eliminate cold start latency and ensure instant response times. The \"therm\" feature (named after thermals that gliders use to gain altitude) proactively spins up chute infrastructure before you need it.\r\n\r\n#### Why Warmup?\r\n\r\nWhen a chute is \"cold\" (no running instances), your first request may experience latency while infrastructure spins up. By warming up a chute in advance, you ensure it's ready for immediate use.\r\n\r\n#### Basic Usage\r\n\r\n```typescript\r\nimport { createChutes, warmUpChute } from '@chutes-ai/ai-sdk-provider';\r\n\r\n// Standalone function\r\nconst result = await warmUpChute('your-chute-id', process.env.CHUTES_API_KEY!);\r\n\r\nconsole.log(result.isHot);         // true - chute is ready!\r\nconsole.log(result.status);        // 'hot', 'warming', 'cold', or 'unknown'\r\nconsole.log(result.instanceCount); // 2 - number of available instances\r\nconsole.log(result.log);           // 'chute is hot, 2 instances available'\r\n\r\n// Or via provider\r\nconst chutes = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\nconst warmupResult = await chutes.therm.warmup('your-chute-id');\r\n```\r\n\r\n#### Warmup Response Fields\r\n\r\n| Field | Type | Description |\r\n|-------|------|-------------|\r\n| `success` | `boolean` | Whether the warmup request succeeded |\r\n| `chuteId` | `string` | The chute ID that was warmed up |\r\n| `isHot` | `boolean` | `true` if chute is ready for immediate use |\r\n| `status` | `ChuteStatus` | `'hot'`, `'warming'`, `'cold'`, or `'unknown'` |\r\n| `instanceCount` | `number` | Number of instances currently available |\r\n| `log` | `string?` | Status message from the API |\r\n| `data` | `unknown?` | Raw API response data |\r\n\r\n#### Status-Based Logic\r\n\r\n```typescript\r\nconst result = await chutes.therm.warmup('your-chute-id');\r\n\r\n// Simple boolean check\r\nif (result.isHot) {\r\n  // Proceed immediately - chute is ready\r\n  const response = await generateText({ model: chutes('your-chute-id'), prompt });\r\n}\r\n\r\n// Status-based handling\r\nswitch (result.status) {\r\n  case 'hot':\r\n    console.log(`Ready with ${result.instanceCount} instances`);\r\n    break;\r\n  case 'warming':\r\n    console.log('Warming up, try again in a few seconds');\r\n    break;\r\n  case 'cold':\r\n    console.log('Cold start initiated, wait longer');\r\n    break;\r\n  case 'unknown':\r\n    console.log('Status unknown, proceed with caution');\r\n    break;\r\n}\r\n\r\n// Check for high throughput capacity\r\nif (result.instanceCount >= 3) {\r\n  console.log('Multiple instances available for parallel requests');\r\n}\r\n```\r\n\r\n#### Pre-Warming Before Requests\r\n\r\n```typescript\r\n// Warm up before making requests\r\nasync function ensureWarm(chuteId: string) {\r\n  const result = await chutes.therm.warmup(chuteId);\r\n  \r\n  if (!result.isHot) {\r\n    // Wait and retry\r\n    await new Promise(resolve => setTimeout(resolve, 5000));\r\n    return chutes.therm.warmup(chuteId);\r\n  }\r\n  \r\n  return result;\r\n}\r\n\r\n// Usage\r\nawait ensureWarm('your-chute-id');\r\nconst response = await generateText({ model: chutes('your-chute-id'), prompt });\r\n```\r\n\r\n#### Scheduled Warmup\r\n\r\nKeep chutes warm during business hours:\r\n\r\n```typescript\r\n// Example: Run every 5 minutes during business hours\r\nasync function keepWarm() {\r\n  const criticalChutes = [\r\n    'chute-id-1',\r\n    'chute-id-2',\r\n  ];\r\n  \r\n  for (const chuteId of criticalChutes) {\r\n    try {\r\n      const result = await chutes.therm.warmup(chuteId);\r\n      console.log(`${chuteId}: ${result.status} (${result.instanceCount} instances)`);\r\n    } catch (error) {\r\n      console.error(`Failed to warm ${chuteId}:`, error.message);\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n#### Thermal Monitor (Non-Blocking)\r\n\r\nFor long-running applications, use a `ThermalMonitor` to track chute status without blocking. The monitor polls in the background, automatically stops when the chute becomes hot, and can be restarted with `reheat()`.\r\n\r\n```typescript\r\nimport { createChutes, createThermalMonitor } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst chutes = createChutes({ apiKey: process.env.CHUTES_API_KEY });\r\n\r\n// Create a monitor - starts polling immediately\r\nconst monitor = chutes.therm.monitor('your-chute-id');\r\n\r\n// Check status anytime (non-blocking, no API call)\r\nconsole.log(monitor.status);     // 'cold' | 'warming' | 'hot' | 'unknown'\r\nconsole.log(monitor.isPolling);  // true - actively polling\r\n\r\n// Subscribe to status changes\r\nconst unsubscribe = monitor.onStatusChange((status) => {\r\n  if (status === 'hot') {\r\n    console.log('🔥 Chute is ready!');\r\n  }\r\n});\r\n\r\n// Optional: Wait until hot (blocking)\r\nawait monitor.waitUntilHot(60000); // 60 second timeout\r\n\r\n// Later, if you suspect it went cold, restart polling\r\nmonitor.reheat();\r\n\r\n// Cleanup when done\r\nunsubscribe();\r\nmonitor.stop();\r\n```\r\n\r\n##### Monitor Options\r\n\r\n```typescript\r\nconst monitor = chutes.therm.monitor('chute-id', {\r\n  pollInterval: 30000, // Poll every 30 seconds (default)\r\n  autoStart: true,     // Start polling immediately (default)\r\n});\r\n```\r\n\r\n##### Monitor Properties & Methods\r\n\r\n| Property/Method | Type | Description |\r\n|-----------------|------|-------------|\r\n| `status` | `ChuteStatus` | Current thermal status (no API call) |\r\n| `chuteId` | `string` | The chute being monitored |\r\n| `isPolling` | `boolean` | Whether actively polling |\r\n| `reheat()` | `void` | Signal to restart polling (no-op if already polling) |\r\n| `stop()` | `void` | Stop polling and cleanup |\r\n| `waitUntilHot(timeout?)` | `Promise<void>` | Block until hot or timeout |\r\n| `onStatusChange(cb)` | `() => void` | Subscribe to changes, returns unsubscribe |\r\n\r\n##### Standalone Factory\r\n\r\nYou can also create monitors without a provider:\r\n\r\n```typescript\r\nimport { createThermalMonitor } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst monitor = createThermalMonitor('chute-id', process.env.CHUTES_API_KEY!, {\r\n  pollInterval: 15000,\r\n});\r\n```\r\n\r\n## Configuration\r\n\r\n### Provider Settings\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\nconst provider = createChutes({\r\n  // Required: Your Chutes.ai API key\r\n  apiKey: process.env.CHUTES_API_KEY,\r\n  \r\n  // Optional: Custom base URL for management API\r\n  baseURL: 'https://api.chutes.ai',\r\n  \r\n  // Optional: Default model for lazy calls (also reads CHUTES_DEFAULT_MODEL env var)\r\n  defaultModel: 'https://chutes-deepseek-ai-deepseek-v3.chutes.ai',\r\n  \r\n  // Optional: Custom headers\r\n  headers: {\r\n    'X-Custom-Header': 'value',\r\n  },\r\n  \r\n  // Optional: Custom fetch implementation\r\n  fetch: customFetch,\r\n});\r\n```\r\n\r\n### Model Settings\r\n\r\n```typescript\r\nconst model = chutes('chute-url', {\r\n  // Generation settings\r\n  temperature: 0.7,\r\n  maxTokens: 1000,\r\n  topP: 0.9,\r\n  frequencyPenalty: 0.5,\r\n  presencePenalty: 0.5,\r\n  stopSequences: ['STOP', 'END'],\r\n  seed: 42,\r\n});\r\n```\r\n\r\n## Common Open-Source Chutes\r\n\r\n| Model | Chute URL | Best For |\r\n|-------|-----------|----------|\r\n| **DeepSeek-V3** | `https://chutes-deepseek-ai-deepseek-v3.chutes.ai` | Advanced reasoning, coding |\r\n| **DeepSeek-R1** | `https://chutes-deepseek-ai-deepseek-r1.chutes.ai` | Complex problem solving |\r\n| **Llama 3.1 70B** | `https://chutes-meta-llama-llama-3-1-70b-instruct.chutes.ai` | General purpose, chat |\r\n| **Qwen 2.5 72B** | `https://chutes-qwen-qwen2-5-72b-instruct.chutes.ai` | Multilingual, reasoning |\r\n\r\nFind more chutes at [chutes.ai/playground](https://chutes.ai/playground)\r\n\r\n## Common Patterns\r\n\r\n### Pattern 1: Streaming Chat with Vercel AI SDK\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\nimport { streamText } from 'ai';\r\n\r\nconst chutes = createChutes({\r\n  apiKey: process.env.CHUTES_API_KEY,\r\n});\r\n\r\nconst result = await streamText({\r\n  model: chutes('https://chutes-deepseek-v3.chutes.ai'),\r\n  messages: [\r\n    { role: 'system', content: 'You are a helpful assistant.' },\r\n    { role: 'user', content: 'Explain quantum computing in simple terms.' }\r\n  ],\r\n  temperature: 0.7,\r\n  maxTokens: 500,\r\n});\r\n\r\nfor await (const chunk of result.textStream) {\r\n  process.stdout.write(chunk);\r\n}\r\n```\r\n\r\n### Pattern 2: Tool Calling with Weather Function\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\nimport { generateText } from 'ai';\r\nimport { z } from 'zod';\r\n\r\nconst chutes = createChutes();\r\n\r\nconst result = await generateText({\r\n  model: chutes('https://chutes-deepseek-v3.chutes.ai'),\r\n  tools: {\r\n    getWeather: {\r\n      description: 'Get the current weather for a location',\r\n      parameters: z.object({\r\n        location: z.string().describe('The city name'),\r\n        unit: z.enum(['celsius', 'fahrenheit']).optional(),\r\n      }),\r\n      execute: async ({ location, unit = 'celsius' }) => {\r\n        // Your weather API call here\r\n        return {\r\n          location,\r\n          temperature: 22,\r\n          unit,\r\n          conditions: 'Partly cloudy',\r\n        };\r\n      },\r\n    },\r\n  },\r\n  prompt: 'What is the weather in Tokyo?',\r\n});\r\n\r\nconsole.log(result.text);\r\n```\r\n\r\n### Pattern 3: Batch Embeddings for Semantic Search\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\nimport { embedMany } from 'ai';\r\n\r\nconst chutes = createChutes();\r\nconst embeddingModel = chutes.textEmbeddingModel('text-embedding-3-small');\r\n\r\nconst documents = [\r\n  'The quick brown fox jumps over the lazy dog',\r\n  'Machine learning is a subset of artificial intelligence',\r\n  'TypeScript is a typed superset of JavaScript',\r\n  'Open source software is publicly accessible code',\r\n];\r\n\r\nconst { embeddings } = await embedMany({\r\n  model: embeddingModel,\r\n  values: documents,\r\n});\r\n\r\n// Each embedding is a vector you can store in a vector database\r\nconsole.log(`Generated ${embeddings.length} embeddings`);\r\nconsole.log(`Dimension: ${embeddings[0].length}`);\r\n```\r\n\r\n### Pattern 4: Image Generation with Error Handling\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\nimport * as fs from 'fs';\r\n\r\nconst chutes = createChutes();\r\nconst imageModel = chutes.imageModel('flux-dev');\r\n\r\ntry {\r\n  const result = await imageModel.doGenerate({\r\n    prompt: 'A serene mountain landscape at sunset with a lake reflection',\r\n    size: '1024x1024',\r\n    n: 1,\r\n  });\r\n\r\n  // Save base64 image to file\r\n  const base64Data = result.images[0].split(',')[1];\r\n  const buffer = Buffer.from(base64Data, 'base64');\r\n  fs.writeFileSync('generated-image.png', buffer);\r\n  \r\n  console.log('Image saved to generated-image.png');\r\n} catch (error) {\r\n  console.error('Image generation failed:', error.message);\r\n}\r\n```\r\n\r\n### Pattern 5: Multi-Turn Conversation with Context\r\n\r\n```typescript\r\nimport { createChutes } from '@chutes-ai/ai-sdk-provider';\r\nimport { generateText } from 'ai';\r\n\r\nconst chutes = createChutes();\r\nconst model = chutes('https://chutes-deepseek-v3.chutes.ai');\r\n\r\nconst messages = [\r\n  { role: 'user', content: 'What is TypeScript?' },\r\n];\r\n\r\n// First turn\r\nconst response1 = await generateText({\r\n  model,\r\n  messages,\r\n});\r\n\r\nconsole.log('Assistant:', response1.text);\r\n\r\n// Add response and continue conversation\r\nmessages.push({ role: 'assistant', content: response1.text });\r\nmessages.push({ role: 'user', content: 'How does it differ from JavaScript?' });\r\n\r\n// Second turn with context\r\nconst response2 = await generateText({\r\n  model,\r\n  messages,\r\n});\r\n\r\nconsole.log('Assistant:', response2.text);\r\n```\r\n\r\n## API Reference\r\n\r\n### Provider Methods\r\n\r\n| Method | Parameters | Returns | Description |\r\n|--------|------------|---------|-------------|\r\n| `chutes(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesModelSettings` | `LanguageModelV2` | Create a language model instance |\r\n| `chutes.textEmbeddingModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesEmbeddingSettings` | `EmbeddingModelV2<string>` | Create an embedding model |\r\n| `chutes.imageModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesImageSettings` | `ImageModelV2` | Create an image generation model |\r\n| `chutes.videoModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesVideoSettings` | `VideoModel` | Create a video generation model |\r\n| `chutes.audioModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesAudioSettings` | `AudioModel` | Create an audio model (TTS/STT/Music) |\r\n| `chutes.moderationModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesModerationSettings` | `ModerationModel` | Create a content moderation model |\r\n| `chutes.inferenceModel(modelId, settings?)` | `modelId: string`<br/>`settings?: ChutesInferenceSettings` | `InferenceModel` | Create a custom inference model |\r\n| `chutes.listModels(type?)` | `type?: 'llm' \\| 'image' \\| 'embedding' \\| 'video' \\| 'tts' \\| 'stt' \\| 'music'` | `Promise<ChuteInfo[]>` | List available models/chutes |\r\n| `chutes.getModelCapabilities(modelId)` | `modelId: string` | `Promise<ModelCapabilities>` | Get model capabilities and features |\r\n| `chutes.therm.warmup(chuteId)` | `chuteId: string` | `Promise<WarmupResult>` | Pre-warm a chute for instant response times |\r\n\r\n### Therm (Warmup) Utility Functions\r\n\r\n| Function | Parameters | Returns | Description |\r\n|----------|------------|---------|-------------|\r\n| `warmUpChute(chuteId, apiKey, options?)` | `chuteId: string`<br/>`apiKey: string`<br/>`options?: WarmupOptions` | `Promise<WarmupResult>` | Standalone warmup function |\r\n\r\n### Audio/Voice Utility Functions\r\n\r\n| Function | Parameters | Returns | Description |\r\n|----------|------------|---------|-------------|\r\n| `listAvailableVoices()` | - | `Voice[]` | Get all 54 available TTS voices |\r\n| `getVoicesByLanguage(lang)` | `lang: string` | `Voice[]` | Filter voices by language code (e.g., 'en-US') |\r\n| `getVoicesByRegion(region)` | `region: string` | `Voice[]` | Filter voices by region (e.g., 'american', 'british') |\r\n| `isValidVoice(id)` | `id: string` | `boolean` | Check if a voice ID is valid |\r\n| `getVoice(id)` | `id: string` | `Voice \\| undefined` | Get voice details by ID |\r\n\r\n### Model Registry\r\n\r\n| Method | Parameters | Returns | Description |\r\n|--------|------------|---------|-------------|\r\n| `registry.fetchAvailableChutes()` | - | `Promise<ChuteInfo[]>` | Fetch all available chutes from API |\r\n| `registry.getLLMChutes()` | - | `ChuteInfo[]` | Get language model chutes |\r\n| `registry.getImageChutes()` | - | `ChuteInfo[]` | Get image generation chutes |\r\n| `registry.getEmbeddingChutes()` | - | `ChuteInfo[]` | Get embedding model chutes |\r\n\r\n### Settings Interfaces\r\n\r\n#### ChutesProviderSettings\r\n```typescript\r\n{\r\n  apiKey?: string;           // Your Chutes.ai API key\r\n  baseURL?: string;          // Custom API base URL (default: 'https://api.chutes.ai')\r\n  headers?: Record<string, string>;  // Custom headers\r\n  fetch?: typeof fetch;      // Custom fetch implementation\r\n  defaultModel?: string;     // Default model for lazy calls (also reads CHUTES_DEFAULT_MODEL env var)\r\n}\r\n```\r\n\r\n#### ChutesModelSettings\r\n```typescript\r\n{\r\n  temperature?: number;      // 0.0 to 2.0 (default: 1.0)\r\n  maxTokens?: number;        // Maximum tokens to generate\r\n  topP?: number;             // 0.0 to 1.0 (default: 1.0)\r\n  frequencyPenalty?: number; // -2.0 to 2.0 (default: 0)\r\n  presencePenalty?: number;  // -2.0 to 2.0 (default: 0)\r\n  stopSequences?: string[];  // Stop generation at these sequences\r\n  seed?: number;             // For deterministic generation\r\n  chuteId?: string;          // Chute UUID for error tracking\r\n}\r\n```\r\n\r\n#### ChutesVideoSettings\r\n```typescript\r\n{\r\n  resolution?: string;       // e.g., '1024x576', '512x512'\r\n  fps?: number;              // Frames per second (12-30)\r\n  steps?: number;            // Generation steps (more = higher quality)\r\n  frames?: number;           // Total frames to generate\r\n  seed?: number;             // For deterministic generation\r\n}\r\n```\r\n\r\n#### ChutesAudioSettings\r\n```typescript\r\n{\r\n  voice?: string;            // Voice ID for TTS (e.g., 'af_bella')\r\n  speed?: number;            // Playback speed (0.5-2.0)\r\n  language?: string;         // Language code for STT\r\n}\r\n```\r\n\r\n#### WarmupOptions\r\n```typescript\r\n{\r\n  baseURL?: string;          // Custom API base URL (default: 'https://api.chutes.ai')\r\n  headers?: Record<string, string>;  // Custom headers\r\n  fetch?: typeof fetch;      // Custom fetch implementation\r\n}\r\n```\r\n\r\n#### WarmupResult\r\n```typescript\r\n{\r\n  success: boolean;          // Whether the warmup request succeeded\r\n  chuteId: string;           // The chute ID that was warmed up\r\n  isHot: boolean;            // true if chute is ready for immediate use\r\n  status: ChuteStatus;       // 'hot' | 'warming' | 'cold' | 'unknown'\r\n  instanceCount: number;     // Number of available instances\r\n  log?: string;              // Status message from API\r\n  data?: unknown;            // Raw API response\r\n}\r\n```\r\n\r\n## Testing\r\n\r\n```bash\r\n# Set your API key\r\nexport CHUTES_API_KEY=your-key\r\n\r\n# Run all tests\r\nnpm test\r\n\r\n# Run specific test suites\r\nnpm test tests/unit/\r\nnpm test tests/integration/\r\n\r\n# Run with coverage\r\nnpm test -- --coverage\r\n```\r\n\r\n## Development\r\n\r\n```bash\r\n# Install dependencies\r\nnpm install\r\n\r\n# Run tests in watch mode\r\nnpm test -- --watch\r\n\r\n# Build the package\r\nnpm run build\r\n\r\n# Type check\r\nnpm run typecheck\r\n```\r\n\r\n## Architecture\r\n\r\n### Project Structure\r\n\r\n```\r\nsrc/\r\n├── api/\r\n│   └── errors.ts           # Error handling and mapping\r\n├── constants/\r\n│   └── voices.ts           # TTS voice library (54 voices)\r\n├── converters/\r\n│   └── messages.ts         # Message format conversion\r\n├── models/\r\n│   ├── language-model.ts   # Language model implementation\r\n│   ├── embedding-model.ts  # Embedding model implementation\r\n│   ├── image-model.ts      # Image generation model\r\n│   ├── video-model.ts      # Video generation model (T2V, I2V)\r\n│   ├── audio-model.ts      # Audio model (TTS, STT, Music)\r\n│   ├── moderation-model.ts # Content moderation model\r\n│   └── inference-model.ts  # Custom inference model\r\n├── registry/\r\n│   └── models.ts           # Dynamic model discovery\r\n├── types/\r\n│   └── index.ts            # TypeScript type definitions\r\n├── utils/\r\n│   ├── chute-discovery.ts  # Model type filtering\r\n│   └── therm.ts            # Chute warmup utilities\r\n├── chutes-provider.ts      # Main provider factory\r\n└── index.ts                # Public API exports\r\n```\r\n\r\n### How It Works\r\n\r\n1. **Chute Discovery**: The provider fetches available chutes from `https://api.chutes.ai/chutes/`\r\n2. **Request Routing**: Each chute has its own subdomain (`https://{slug}.chutes.ai`)\r\n3. **API Compatibility**: Chutes implement OpenAI-compatible APIs (`/v1/chat/completions`, `/v1/embeddings`, etc.)\r\n4. **Message Conversion**: AI SDK prompts are converted to OpenAI format\r\n5. **Response Parsing**: Responses are parsed and mapped back to AI SDK V2 format\r\n\r\n## Error Handling\r\n\r\nThe provider includes comprehensive error handling:\r\n\r\n```typescript\r\nimport { ChutesError, ChutesAPIError } from '@chutes-ai/ai-sdk-provider';\r\n\r\ntry {\r\n  const result = await model.doGenerate({ /* ... */ });\r\n} catch (error) {\r\n  if (error instanceof ChutesAPIError) {\r\n    console.error('API Error:', error.statusCode, error.message);\r\n  } else if (error instanceof ChutesError) {\r\n    console.error('Chutes Error:', error.message);\r\n  }\r\n}\r\n```\r\n\r\n## Migration Guide\r\n\r\n### From OpenAI\r\n\r\n```diff\r\n- import { openai } from '@ai-sdk/openai';\r\n+ import { chutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\n- const model = openai('gpt-4');\r\n+ const model = chutes('https://chutes-deepseek-v3.chutes.ai');\r\n```\r\n\r\n### From OpenRouter\r\n\r\n```diff\r\n- import { createOpenRouter } from '@openrouter/ai-sdk-provider';\r\n+ import { createChutes } from '@chutes-ai/ai-sdk-provider';\r\n\r\n- const provider = createOpenRouter({ apiKey: key });\r\n+ const provider = createChutes({ apiKey: key });\r\n\r\n- const model = provider('meta-llama/llama-3.1-70b-instruct');\r\n+ const model = provider('https://chutes-meta-llama-llama-3-1-70b-instruct.chutes.ai');\r\n```\r\n\r\n## FAQ\r\n\r\n**Q: What models are available?**  \r\nA: All open-source models hosted on Chutes.ai, including language models, image generation, video generation, TTS, STT, music generation, content moderation, and custom inference models. Use `ChutesModelRegistry.fetchAvailableChutes()` to see what's currently available.\r\n\r\n**Q: Can I use closed-source models like GPT-4 or Claude?**  \r\nA: No, Chutes.ai exclusively hosts open-source models. For closed-source models, use their official providers.\r\n\r\n**Q: How do I find the right chute URL?**  \r\nA: Visit [chutes.ai/playground](https://chutes.ai/playground) or use the model registry API to discover available chutes.\r\n\r\n**Q: Does this work with Next.js?**  \r\nA: Yes! This provider works with any framework that supports the Vercel AI SDK.\r\n\r\n**Q: Are there rate limits?**  \r\nA: Rate limits depend on your Chutes.ai account tier. The provider handles 429 errors automatically with retry logic.\r\n\r\n**Q: Can I deploy my own models?**  \r\nA: Yes, Chutes.ai allows you to deploy custom open-source models. Once deployed, they'll work automatically with this provider.\r\n\r\n**Q: What voice languages are supported for TTS?**  \r\nA: 54 voices across 9 languages: American English, British English, Spanish, French, Hindi, Italian, Japanese, Portuguese (Brazilian), and Mandarin Chinese. Use `listAvailableVoices()` to see all options.\r\n\r\n**Q: What audio formats are supported for STT?**  \r\nA: Most common formats including MP3, WAV, M4A, FLAC, and more. Input can be a Buffer, base64 string, or URL.\r\n\r\n**Q: Can I generate long-form videos or music?**  \r\nA: Yes, but generation time increases with duration. Video generation typically supports 5-30 seconds, while music can go up to 30+ seconds depending on the model.\r\n\r\n## Troubleshooting\r\n\r\n### Common Errors\r\n\r\n#### \"No matching cord found!\" (404 Error)\r\n\r\n**Cause**: The chute URL is incorrect, the chute is not deployed, or the chute is not accessible with your API key.\r\n\r\n**Solution**:\r\n- Verify the chute URL is correct\r\n- Check that the chute exists at [chutes.ai/playground](https://chutes.ai/playground)\r\n- Ensure the chute is deployed and running\r\n- Verify your API key has access to the chute\r\n\r\n```typescript\r\n// Use model discovery to find available chutes\r\nconst chutes = createChutes();\r\nconst availableModels = await chutes.listModels('llm');\r\nconsole.log('Available chutes:', availableModels.map(m => m.slug));\r\n```\r\n\r\n#### \"Invalid API key\" (401 Error)\r\n\r\n**Cause**: The `CHUTES_API_KEY` environment variable is not set, is invalid, or has expired.\r\n\r\n**Solution**:\r\n- Get your API key from [chutes.ai](https://chutes.ai)\r\n- Set it in your environment:\r\n  ```bash\r\n  export CHUTES_API_KEY=your-api-key-here\r\n  ```\r\n- In Next.js, add it to `.env.local`:\r\n  ```\r\n  CHUTES_API_KEY=your-api-key-here\r\n  ```\r\n- Verify the key is loaded:\r\n  ```typescript\r\n  console.log('API Key set:', !!process.env.CHUTES_API_KEY);\r\n  ```\r\n\r\n#### \"Rate limit exceeded\" (429 Error)\r\n\r\n**Cause**: Too many requests to the API in a short time period.\r\n\r\n**Solution**:\r\n- The provider automatically retries with exponential backoff\r\n- If persistent, upgrade your Chutes.ai account tier\r\n- Implement request throttling in your application:\r\n  ```typescript\r\n  import pLimit from 'p-limit';\r\n  \r\n  const limit = pLimit(5); // Max 5 concurrent requests\r\n  const results = await Promise.all(\r\n    prompts.map(prompt => limit(() => generateText({ model, prompt })))\r\n  );\r\n  ```\r\n\r\n#### \"Cannot find module '@chutes-ai/ai-sdk-provider'\"\r\n\r\n**Cause**: Package not installed or npm link not set up correctly.\r\n\r\n**Solution**:\r\n- For npm: `npm install @chutes-ai/ai-sdk-provider ai`\r\n- For local development:\r\n  ```bash\r\n  # In provider package directory\r\n  npm link\r\n  \r\n  # In your project\r\n  npm link @chutes-ai/ai-sdk-provider\r\n  ```\r\n\r\n#### TypeScript Errors with AI SDK v4\r\n\r\n**Cause**: AI SDK v4 has some TypeScript compatibility issues with strict mode.\r\n\r\n**Solution**:\r\n- Upgrade to AI SDK v5: `npm install ai@^5.0.0`\r\n- Or disable strict mode in `tsconfig.json`:\r\n  ```json\r\n  {\r\n    \"compilerOptions\": {\r\n      \"strict\": false\r\n    }\r\n  }\r\n  ```\r\n\r\n#### Tool Calling Not Working\r\n\r\n**Cause**: Not all models support tool calling, or the tool schema is invalid.\r\n\r\n**Solution**:\r\n- Verify the model supports tools:\r\n  ```typescript\r\n  const capabilities = await chutes.getModelCapabilities('your-model-id');\r\n  console.log('Supports tools:', capabilities.tools);\r\n  ```\r\n- Use a model known to support tools:\r\n  - DeepSeek-V3: `https://chutes-deepseek-v3.chutes.ai`\r\n  - Qwen 2.5 72B: `https://chutes-qwen-qwen2-5-72b-instruct.chutes.ai`\r\n- Ensure tool parameters use valid Zod schemas\r\n\r\n#### Streaming Not Producing Output\r\n\r\n**Cause**: Incorrect stream handling or model doesn't support streaming.\r\n\r\n**Solution**:\r\n- Ensure you're iterating the stream correctly:\r\n  ```typescript\r\n  const result = await streamText({ model, prompt: '...' });\r\n  \r\n  // Correct way\r\n  for await (const chunk of result.textStream) {\r\n    process.stdout.write(chunk);\r\n  }\r\n  \r\n  // Or use fullStream for more control\r\n  for await (const part of result.fullStream) {\r\n    if (part.type === 'text-delta') {\r\n      process.stdout.write(part.textDelta);\r\n    }\r\n  }\r\n  ```\r\n\r\n#### Image Generation Returns Empty Result\r\n\r\n**Cause**: The image generation chute may not support the requested size or parameters.\r\n\r\n**Solution**:\r\n- Use standard sizes: `1024x1024`, `1024x1792`, `1792x1024`\r\n- Check chute capabilities for supported dimensions\r\n- Try with minimal parameters first:\r\n  ```typescript\r\n  const result = await imageModel.doGenerate({\r\n    prompt: 'A simple test image',\r\n    size: '1024x1024',\r\n  });\r\n  ```\r\n\r\n#### Video/Audio Generation Timeouts\r\n\r\n**Cause**: Video and audio generation can take 30-120 seconds depending on complexity.\r\n\r\n**Solution**:\r\n- Increase timeout in your HTTP client\r\n- For Next.js API routes, use:\r\n  ```typescript\r\n  export const maxDuration = 120; // 120 seconds\r\n  ```\r\n- Consider using webhook callbacks for long-running jobs:\r\n  ```typescript\r\n  const result = await inferenceModel.predict({\r\n    modelId: 'your-model',\r\n    input: { /* ... */ },\r\n    webhookUrl: 'https://your-domain.com/webhook',\r\n  });\r\n  ```\r\n\r\n### Getting Help\r\n\r\nIf you encounter issues not covered here:\r\n\r\n1. **Check the examples**: See the [`examples/`](./examples) directory for working code\r\n2. **Review tests**: Integration tests in [`tests/integration/`](./tests/integration) show real usage\r\n3. **GitHub Issues**: [Report a bug](https://github.com/chutesai/ai-sdk-provider-chutes/issues)\r\n4. **Chutes.ai Discord**: [Join the community](https://discord.gg/chutes)\r\n5. **Email Support**: support@chutes.ai\r\n\r\n## Contributing\r\n\r\nContributions are welcome! Please follow these guidelines:\r\n\r\n1. Follow TDD principles (test first!)\r\n2. Maintain >90% test coverage\r\n3. Follow the existing code style\r\n4. Update documentation for new features\r\n\r\n## License\r\n\r\nMIT © [Chutes.ai](https://chutes.ai)\r\n\r\n## Links\r\n\r\n- [Chutes.ai Website](https://chutes.ai)\r\n- [Vercel AI SDK Documentation](https://sdk.vercel.ai/docs)\r\n- [GitHub Repository](https://github.com/chutesai/ai-sdk-provider-chutes)\r\n- [npm Package](https://www.npmjs.com/package/@chutes-ai/ai-sdk-provider)\r\n\r\n## Support\r\n\r\n- GitHub Issues: [Report a bug](https://github.com/chutesai/ai-sdk-provider-chutes/issues)\r\n- Chutes.ai Discord: [Join the community](https://discord.gg/chutes)\r\n- Email: support@chutes.ai\r\n\r\n---\r\n\r\n**Built with ❤️ for the open-source AI community**\r\n","readmeFilename":"README.md"}