ai-providers 2.0.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
-
2
- 
3
- > ai-providers@2.0.1 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-providers
4
- > tsc -p tsconfig.json
5
-
1
+
2
+ > ai-providers@2.1.3 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-providers
3
+ > tsc -p tsconfig.json
4
+
@@ -0,0 +1,58 @@
1
+
2
+ > ai-providers@2.1.1 test /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-providers
3
+ > vitest
4
+
5
+
6
+ DEV v2.1.9 /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-providers
7
+
8
+ ✓ src/llm.do.test.ts (42 tests) 29ms
9
+ ❯ src/providers/cloudflare.test.ts (45 tests | 2 failed) 10ms
10
+ × cloudflareEmbedding > model properties > has correct specification version 4ms
11
+ → expected 'v2' to be 'v1' // Object.is equality
12
+ × type safety > returns EmbeddingModel interface 0ms
13
+ → expected 'v2' to be 'v1' // Object.is equality
14
+ ✓ src/registry.test.ts (51 tests) 47ms
15
+ ✓ src/index.test.ts (37 tests) 5ms
16
+ ✓ src/integration.test.ts (26 tests | 6 skipped) 6ms
17
+
18
+ ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
19
+
20
+ FAIL src/providers/cloudflare.test.ts > cloudflareEmbedding > model properties > has correct specification version
21
+ AssertionError: expected 'v2' to be 'v1' // Object.is equality
22
+
23
+ Expected: "v1"
24
+ Received: "v2"
25
+
26
+ ❯ src/providers/cloudflare.test.ts:58:42
27
+ 56| it('has correct specification version', () => {
28
+ 57| const model = cloudflareEmbedding()
29
+ 58| expect(model.specificationVersion).toBe('v1')
30
+ | ^
31
+ 59| })
32
+ 60|
33
+
34
+ ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
35
+
36
+ FAIL src/providers/cloudflare.test.ts > type safety > returns EmbeddingModel interface
37
+ AssertionError: expected 'v2' to be 'v1' // Object.is equality
38
+
39
+ Expected: "v1"
40
+ Received: "v2"
41
+
42
+ ❯ src/providers/cloudflare.test.ts:559:40
43
+ 557| it('returns EmbeddingModel interface', () => {
44
+ 558| const model = cloudflareEmbedding()
45
+ 559| expect(model.specificationVersion).toBe('v1')
46
+ | ^
47
+ 560| expect(typeof model.doEmbed).toBe('function')
48
+ 561| })
49
+
50
+ ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
51
+
52
+ Test Files 1 failed | 4 passed (5)
53
+ Tests 2 failed | 193 passed | 6 skipped (201)
54
+ Start at 06:45:31
55
+ Duration 438ms (transform 81ms, setup 0ms, collect 155ms, tests 97ms, environment 0ms, prepare 38ms)
56
+
57
+ FAIL Tests failed. Watching for file changes...
58
+ press h to show help, press q to quit
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # ai-providers
2
2
 
3
+ ## 2.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Documentation and testing improvements
8
+ - Add deterministic AI testing suite with self-validating patterns
9
+ - Apply StoryBrand narrative to all package READMEs
10
+ - Update TESTING.md with four principles of deterministic AI testing
11
+ - Fix duplicate examples package name conflict
12
+
13
+ - Updated dependencies
14
+ - language-models@2.1.3
15
+
16
+ ## 2.1.1
17
+
18
+ ### Patch Changes
19
+
20
+ - language-models@2.1.1
21
+
22
+ ## 2.0.3
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies
27
+ - rpc.do@0.2.0
28
+ - language-models@2.0.3
29
+
3
30
  ## 2.0.2
4
31
 
5
32
  ### Patch Changes
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 .org.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,112 +1,157 @@
1
1
  # ai-providers
2
2
 
3
- Unified AI provider registry with Cloudflare AI Gateway support.
3
+ **Stop juggling API keys. Start building.**
4
4
 
5
- ## Installation
5
+ You're building AI features, not managing provider configurations. But every model needs its own SDK, its own API key, its own quirks. OpenAI, Anthropic, Google, Llama, Mistral... each one is another dependency to install, another secret to manage, another authentication pattern to remember.
6
6
 
7
- ```bash
8
- pnpm add ai-providers
7
+ What if you could just say `model('sonnet')` and it worked?
8
+
9
+ ## The Problem
10
+
11
+ ```typescript
12
+ // Before: Provider chaos
13
+ import Anthropic from '@anthropic-ai/sdk'
14
+ import OpenAI from 'openai'
15
+ import { GoogleGenerativeAI } from '@google/generative-ai'
16
+
17
+ const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
18
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
19
+ const google = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY)
20
+
21
+ // Different APIs, different patterns, different headaches
9
22
  ```
10
23
 
11
- ## Quick Start
24
+ ## The Solution
12
25
 
13
26
  ```typescript
27
+ // After: One import, any model
14
28
  import { model } from 'ai-providers'
15
29
  import { generateText } from 'ai'
16
30
 
17
- // Simple aliases - just works
18
31
  const { text } = await generateText({
19
- model: await model('sonnet'), // anthropic/claude-sonnet-4.5
32
+ model: await model('sonnet'), // Just works
20
33
  prompt: 'Hello!'
21
34
  })
22
35
 
23
- // All these work too
24
- await model('opus') // anthropic/claude-opus-4.5
25
- await model('gpt-4o') // openai/gpt-4o
26
- await model('gemini') // google/gemini-2.5-flash
27
- await model('llama-70b') // meta-llama/llama-3.3-70b-instruct
28
- await model('mistral') // mistralai/mistral-large-2411
29
- await model('deepseek') // deepseek/deepseek-chat
36
+ // Switch models in seconds
37
+ await model('opus') // Anthropic Claude Opus 4.5
38
+ await model('gpt-4o') // OpenAI GPT-4o
39
+ await model('gemini') // Google Gemini 2.5 Flash
40
+ await model('llama-70b') // Meta Llama 3.3 70B
41
+ await model('deepseek') // DeepSeek Chat
42
+ await model('mistral') // Mistral Large
30
43
  ```
31
44
 
32
- ## How It Works
33
-
34
- ### Smart Routing
35
-
36
- The `model()` function uses intelligent routing based on model data from OpenRouter:
37
-
38
- 1. **Direct Provider Routing** - When `provider_model_id` is available and the provider matches (openai, anthropic, google), routes directly to the provider's native SDK. This enables provider-specific features like:
39
- - Anthropic: MCP (Model Context Protocol), extended thinking
40
- - OpenAI: Function calling, JSON mode, vision
41
- - Google: Grounding, code execution
45
+ ## Quick Start
42
46
 
43
- 2. **OpenRouter Fallback** - All other models route through OpenRouter, which provides:
44
- - 200+ models from all major providers
45
- - Unified API with consistent model ID format
46
- - Automatic model ID translation
47
- - Fallback routing if a provider is down
47
+ ### 1. Install
48
48
 
49
- Model aliases are resolved by the `language-models` package, which includes `provider_model_id` data from OpenRouter's API for direct routing when available.
49
+ ```bash
50
+ pnpm add ai-providers ai
51
+ ```
50
52
 
51
- ## Configuration
53
+ ### 2. Configure (choose one)
52
54
 
53
- ### Cloudflare AI Gateway (Recommended)
55
+ **Option A: Cloudflare AI Gateway (Recommended)**
54
56
 
55
- Set up a Cloudflare AI Gateway with stored secrets for each provider:
57
+ One gateway, all providers, zero API key management:
56
58
 
57
59
  ```bash
58
60
  export AI_GATEWAY_URL=https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_name}
59
61
  export AI_GATEWAY_TOKEN=your-gateway-auth-token
60
62
  ```
61
63
 
62
- The gateway handles authentication - you don't need individual API keys.
64
+ **Option B: Direct API Keys**
63
65
 
64
- ### Direct API Keys (Fallback)
66
+ ```bash
67
+ export OPENROUTER_API_KEY=sk-or-... # Access 200+ models
68
+ ```
65
69
 
66
- If not using a gateway:
70
+ ### 3. Build
67
71
 
68
- ```bash
69
- export OPENROUTER_API_KEY=sk-or-...
70
- export OPENAI_API_KEY=sk-... # for embeddings
71
- export CLOUDFLARE_ACCOUNT_ID=... # for CF embeddings
72
- export CLOUDFLARE_API_TOKEN=... # for CF embeddings
72
+ ```typescript
73
+ import { model } from 'ai-providers'
74
+ import { generateText } from 'ai'
75
+
76
+ const { text } = await generateText({
77
+ model: await model('sonnet'),
78
+ prompt: 'What is the meaning of life?'
79
+ })
73
80
  ```
74
81
 
75
- ## API
82
+ That's it. No provider-specific SDKs. No authentication dance. Just AI.
76
83
 
77
- ### `model(id: string)`
84
+ ## How It Works
78
85
 
79
- Get a language model by alias or full ID.
86
+ `ai-providers` is your guide through the AI provider landscape:
80
87
 
81
- ```typescript
82
- import { model } from 'ai-providers'
88
+ ```
89
+ Your Code
90
+
91
+
92
+ ┌─────────────────┐
93
+ │ ai-providers │ Resolves aliases, routes intelligently
94
+ └────────┬────────┘
95
+
96
+ ┌────┴────┐
97
+ ▼ ▼
98
+ ┌───────┐ ┌──────────┐
99
+ │Direct │ │OpenRouter│
100
+ │SDK │ │ │
101
+ └───────┘ └──────────┘
102
+ │ │
103
+ ▼ ▼
104
+ Anthropic 200+ models
105
+ OpenAI from any
106
+ Google provider
107
+ ```
83
108
 
84
- // Aliases (requires language-models package)
85
- await model('opus') // anthropic/claude-opus-4.5
86
- await model('sonnet') // anthropic/claude-sonnet-4.5
87
- await model('gpt-4o') // openai/gpt-4o
88
- await model('llama') // meta-llama/llama-4-maverick
109
+ **Smart routing** gives you the best of both worlds:
110
+ - **Direct SDK access** for OpenAI, Anthropic, and Google - enabling provider-specific features like MCP, extended thinking, and structured outputs
111
+ - **OpenRouter fallback** for everything else - 200+ models with automatic failover
89
112
 
90
- // Full IDs always work
113
+ ## Model Aliases
114
+
115
+ Simple names that just work:
116
+
117
+ | Alias | Model |
118
+ |-------|-------|
119
+ | `opus` | Claude Opus 4.5 |
120
+ | `sonnet` | Claude Sonnet 4.5 |
121
+ | `haiku` | Claude Haiku 4.5 |
122
+ | `gpt-4o` | GPT-4o |
123
+ | `o1`, `o3` | OpenAI o1, o3 |
124
+ | `gemini` | Gemini 2.5 Flash |
125
+ | `llama` | Llama 4 Maverick |
126
+ | `deepseek`, `r1` | DeepSeek Chat, R1 |
127
+ | `mistral` | Mistral Large |
128
+ | `qwen` | Qwen3 235B |
129
+ | `grok` | Grok 3 |
130
+
131
+ Or use full model IDs:
132
+
133
+ ```typescript
91
134
  await model('anthropic/claude-opus-4.5')
92
135
  await model('mistralai/codestral-2501')
93
136
  await model('meta-llama/llama-3.3-70b-instruct')
94
137
  ```
95
138
 
96
- ### `embeddingModel(id: string)`
97
-
98
- Get an embedding model.
139
+ ## Embeddings
99
140
 
100
141
  ```typescript
101
142
  import { embeddingModel } from 'ai-providers'
143
+ import { embed } from 'ai'
102
144
 
103
- await embeddingModel('openai:text-embedding-3-small')
104
- await embeddingModel('cloudflare:@cf/baai/bge-m3')
145
+ const model = await embeddingModel('openai:text-embedding-3-small')
146
+ const { embedding } = await embed({ model, value: 'Hello world' })
147
+
148
+ // Or use Cloudflare Workers AI
149
+ const cfModel = await embeddingModel('cloudflare:@cf/baai/bge-m3')
105
150
  ```
106
151
 
107
- ### `createRegistry(config?)`
152
+ ## Advanced Usage
108
153
 
109
- Create a custom provider registry.
154
+ ### Custom Registry
110
155
 
111
156
  ```typescript
112
157
  import { createRegistry } from 'ai-providers'
@@ -116,89 +161,42 @@ const registry = await createRegistry({
116
161
  gatewayToken: 'your-token'
117
162
  })
118
163
 
119
- // Use registry directly
120
- const model = registry.languageModel('openrouter:anthropic/claude-sonnet-4.5')
164
+ const model = registry.languageModel('anthropic:claude-sonnet-4-5-20251101')
121
165
  ```
122
166
 
123
- ### `getRegistry()`
167
+ ### Direct Provider Access
124
168
 
125
- Get the default singleton registry (lazily created).
169
+ When you need provider-specific features:
126
170
 
127
171
  ```typescript
128
- import { getRegistry } from 'ai-providers'
172
+ // Bedrock with bearer token auth
173
+ await model('bedrock:us.anthropic.claude-3-5-sonnet-20241022-v2:0')
129
174
 
130
- const registry = await getRegistry()
175
+ // Direct provider routing
176
+ await model('openai:gpt-4o')
177
+ await model('anthropic:claude-sonnet-4-5-20251101')
178
+ await model('google:gemini-2.5-flash')
131
179
  ```
132
180
 
133
- ## Model Aliases
134
-
135
- When `language-models` is installed, these aliases work:
136
-
137
- | Alias | Model ID |
138
- |-------|----------|
139
- | `opus` | anthropic/claude-opus-4.5 |
140
- | `sonnet` | anthropic/claude-sonnet-4.5 |
141
- | `haiku` | anthropic/claude-haiku-4.5 |
142
- | `gpt`, `gpt-4o` | openai/gpt-4o |
143
- | `o1`, `o3` | openai/o1, openai/o3 |
144
- | `gemini`, `flash` | google/gemini-2.5-flash |
145
- | `llama`, `llama-4` | meta-llama/llama-4-maverick |
146
- | `mistral` | mistralai/mistral-large-2411 |
147
- | `deepseek`, `r1` | deepseek/deepseek-chat, deepseek/deepseek-r1 |
148
- | `qwen` | qwen/qwen3-235b-a22b |
149
- | `grok` | x-ai/grok-3 |
150
-
151
- ## Cloudflare Embeddings
152
-
153
- The package includes a Cloudflare Workers AI embedding provider:
154
-
155
- ```typescript
156
- import { cloudflareEmbedding } from 'ai-providers/cloudflare'
157
- import { embed } from 'ai'
158
-
159
- const model = cloudflareEmbedding('@cf/baai/bge-m3')
160
- const { embedding } = await embed({ model, value: 'Hello world' })
161
- ```
181
+ ## Why Cloudflare AI Gateway?
162
182
 
163
- Available models:
164
- - `@cf/baai/bge-m3` (default, multilingual)
165
- - `@cf/baai/bge-base-en-v1.5`
166
- - `@cf/baai/bge-large-en-v1.5`
167
- - `@cf/baai/bge-small-en-v1.5`
183
+ When configured with AI Gateway:
168
184
 
169
- ## Architecture
185
+ 1. **One token** authenticates everything - gateway injects provider keys from its secrets
186
+ 2. **Unified logging** - see all AI calls in one dashboard
187
+ 3. **Rate limiting** - protect your budget across providers
188
+ 4. **Caching** - reduce costs with intelligent response caching
189
+ 5. **Fallback routing** - automatic failover if a provider is down
170
190
 
171
- ```
172
- ┌─────────────────┐
173
- │ ai-functions │ Uses model() for generation
174
- └────────┬────────┘
175
-
176
- ┌────────▼────────┐
177
- │ ai-providers │ Resolves aliases, smart routing
178
- └────────┬────────┘
179
-
180
- ┌────────▼────────┐
181
- │ language-models │ Model data with provider_model_id
182
- └────────┬────────┘
183
-
184
- ┌────┴────┐
185
- │ │
186
- ┌───▼───┐ ┌───▼───┐
187
- │Direct │ │OpenRou│ Direct: openai, anthropic, google
188
- │SDK │ │ter │ OpenRouter: all other providers
189
- └───────┘ └───────┘
190
- ```
191
+ No gateway? No problem. Set individual API keys and `ai-providers` works the same way.
191
192
 
192
- ## Gateway Authentication
193
+ ## What You Get
193
194
 
194
- When using Cloudflare AI Gateway with stored secrets:
195
+ With `ai-providers`, you can:
195
196
 
196
- 1. Gateway URL points to your gateway endpoint
197
- 2. `AI_GATEWAY_TOKEN` authenticates with the gateway
198
- 3. Gateway injects provider API keys from its stored secrets
199
- 4. No individual API keys needed in your app
197
+ - **Ship faster** - one import, any model, zero config
198
+ - **Stay flexible** - switch providers without code changes
199
+ - **Build with confidence** - production-ready with Cloudflare AI Gateway
200
+ - **Access everything** - 200+ models through OpenRouter, native SDK features through direct routing
200
201
 
201
- The package automatically:
202
- - Strips SDK-added API key headers
203
- - Adds `cf-aig-authorization` header for gateway auth
204
- - Lets the gateway inject the real API keys
202
+ Stop wrestling with provider APIs. Start building AI features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-providers",
3
- "version": "2.0.2",
3
+ "version": "2.1.3",
4
4
  "description": "Unified AI provider registry with Cloudflare AI Gateway support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,18 +15,9 @@
15
15
  "types": "./dist/providers/cloudflare.d.ts"
16
16
  }
17
17
  },
18
- "scripts": {
19
- "build": "tsc -p tsconfig.json",
20
- "dev": "tsc -p tsconfig.json --watch",
21
- "test": "vitest",
22
- "typecheck": "tsc --noEmit",
23
- "lint": "eslint .",
24
- "clean": "rm -rf dist"
25
- },
26
18
  "dependencies": {
27
19
  "ai": "^5.0.0",
28
- "language-models": "2.0.2",
29
- "rpc.do": "^0.1.0"
20
+ "language-models": "2.1.3"
30
21
  },
31
22
  "optionalDependencies": {
32
23
  "@ai-sdk/amazon-bedrock": "^3.0.0",
@@ -49,5 +40,13 @@
49
40
  "gateway",
50
41
  "primitives"
51
42
  ],
52
- "license": "MIT"
53
- }
43
+ "license": "MIT",
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "dev": "tsc -p tsconfig.json --watch",
47
+ "test": "vitest",
48
+ "typecheck": "tsc --noEmit",
49
+ "lint": "eslint .",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }
package/src/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * ai-providers - Unified AI Provider Registry
3
+ *
4
+ * Access multiple AI providers via simple string identifiers.
5
+ * Supports Cloudflare AI Gateway for unified routing and auth.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { createRegistry, getRegistry, configureRegistry, model, embeddingModel, DIRECT_PROVIDERS } from './registry.js';
10
+ // Export llm.do WebSocket transport
11
+ export { LLM, getLLM, createLLMFetch } from './llm.do.js';