@providerprotocol/ai 0.0.34 → 0.0.36
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.
- package/README.md +546 -3
- package/dist/anthropic/index.d.ts +2 -1
- package/dist/anthropic/index.js +151 -145
- package/dist/anthropic/index.js.map +1 -1
- package/dist/cerebras/index.d.ts +392 -0
- package/dist/cerebras/index.js +648 -0
- package/dist/cerebras/index.js.map +1 -0
- package/dist/chunk-2YXFLRQ6.js +49 -0
- package/dist/chunk-2YXFLRQ6.js.map +1 -0
- package/dist/chunk-4OGB7JZA.js +157 -0
- package/dist/chunk-4OGB7JZA.js.map +1 -0
- package/dist/chunk-4RX4VQCB.js +31 -0
- package/dist/chunk-4RX4VQCB.js.map +1 -0
- package/dist/chunk-5IWHCXKN.js +30 -0
- package/dist/chunk-5IWHCXKN.js.map +1 -0
- package/dist/{chunk-3C7O2RNO.js → chunk-A2IM7PGT.js} +6 -4
- package/dist/{chunk-3C7O2RNO.js.map → chunk-A2IM7PGT.js.map} +1 -1
- package/dist/{chunk-3D6XGGVG.js → chunk-ARVM24K2.js} +2 -2
- package/dist/{chunk-4J6OFUKX.js → chunk-AY55T37A.js} +70 -162
- package/dist/chunk-AY55T37A.js.map +1 -0
- package/dist/{chunk-ILR2D5PN.js → chunk-BRP5XJ6Q.js} +2 -86
- package/dist/chunk-BRP5XJ6Q.js.map +1 -0
- package/dist/chunk-C4JP64VW.js +298 -0
- package/dist/chunk-C4JP64VW.js.map +1 -0
- package/dist/chunk-COS4ON4G.js +111 -0
- package/dist/chunk-COS4ON4G.js.map +1 -0
- package/dist/chunk-CRP6Y7NF.js +31 -0
- package/dist/chunk-CRP6Y7NF.js.map +1 -0
- package/dist/chunk-EPB3GQNL.js +118 -0
- package/dist/chunk-EPB3GQNL.js.map +1 -0
- package/dist/chunk-ETBFOLQN.js +34 -0
- package/dist/chunk-ETBFOLQN.js.map +1 -0
- package/dist/chunk-I53CI6ZZ.js +142 -0
- package/dist/chunk-I53CI6ZZ.js.map +1 -0
- package/dist/chunk-IDZOVWP3.js +29 -0
- package/dist/chunk-IDZOVWP3.js.map +1 -0
- package/dist/chunk-JA3UZALR.js +88 -0
- package/dist/chunk-JA3UZALR.js.map +1 -0
- package/dist/{chunk-WAKD3OO5.js → chunk-N5DX5JW3.js} +31 -31
- package/dist/chunk-N5DX5JW3.js.map +1 -0
- package/dist/chunk-OIEWDFQU.js +97 -0
- package/dist/chunk-OIEWDFQU.js.map +1 -0
- package/dist/{chunk-TOJCZMVU.js → chunk-PMK5LZ5Z.js} +40 -40
- package/dist/chunk-PMK5LZ5Z.js.map +1 -0
- package/dist/{chunk-6S222DHN.js → chunk-RJGTRQ47.js} +20 -1
- package/dist/chunk-RJGTRQ47.js.map +1 -0
- package/dist/chunk-UFFJDYCE.js +94 -0
- package/dist/chunk-UFFJDYCE.js.map +1 -0
- package/dist/chunk-VGKZIGVI.js +222 -0
- package/dist/chunk-VGKZIGVI.js.map +1 -0
- package/dist/{chunk-KUPF5KHT.js → chunk-Y5H7C5J4.js} +2 -2
- package/dist/{embedding-D2BYIehX.d.ts → embedding-BXA72PlJ.d.ts} +1 -1
- package/dist/google/index.d.ts +2 -1
- package/dist/google/index.js +202 -199
- package/dist/google/index.js.map +1 -1
- package/dist/groq/index.d.ts +410 -0
- package/dist/groq/index.js +649 -0
- package/dist/groq/index.js.map +1 -0
- package/dist/http/index.d.ts +3 -2
- package/dist/http/index.js +5 -4
- package/dist/image-stream-CCgwB7ve.d.ts +11 -0
- package/dist/index.d.ts +8 -118
- package/dist/index.js +520 -769
- package/dist/index.js.map +1 -1
- package/dist/{llm-BQJZj3cD.d.ts → llm-ByUFPcFH.d.ts} +12 -1632
- package/dist/middleware/logging/index.d.ts +76 -0
- package/dist/middleware/logging/index.js +74 -0
- package/dist/middleware/logging/index.js.map +1 -0
- package/dist/middleware/parsed-object/index.d.ts +45 -0
- package/dist/middleware/parsed-object/index.js +73 -0
- package/dist/middleware/parsed-object/index.js.map +1 -0
- package/dist/middleware/pubsub/index.d.ts +97 -0
- package/dist/middleware/pubsub/index.js +160 -0
- package/dist/middleware/pubsub/index.js.map +1 -0
- package/dist/middleware/pubsub/server/express/index.d.ts +66 -0
- package/dist/middleware/pubsub/server/express/index.js +11 -0
- package/dist/middleware/pubsub/server/express/index.js.map +1 -0
- package/dist/middleware/pubsub/server/fastify/index.d.ts +67 -0
- package/dist/middleware/pubsub/server/fastify/index.js +11 -0
- package/dist/middleware/pubsub/server/fastify/index.js.map +1 -0
- package/dist/middleware/pubsub/server/h3/index.d.ts +70 -0
- package/dist/middleware/pubsub/server/h3/index.js +11 -0
- package/dist/middleware/pubsub/server/h3/index.js.map +1 -0
- package/dist/middleware/pubsub/server/index.d.ts +78 -0
- package/dist/middleware/pubsub/server/index.js +34 -0
- package/dist/middleware/pubsub/server/index.js.map +1 -0
- package/dist/middleware/pubsub/server/webapi/index.d.ts +63 -0
- package/dist/middleware/pubsub/server/webapi/index.js +11 -0
- package/dist/middleware/pubsub/server/webapi/index.js.map +1 -0
- package/dist/ollama/index.d.ts +2 -1
- package/dist/ollama/index.js +48 -45
- package/dist/ollama/index.js.map +1 -1
- package/dist/openai/index.d.ts +2 -1
- package/dist/openai/index.js +319 -313
- package/dist/openai/index.js.map +1 -1
- package/dist/openrouter/index.d.ts +2 -1
- package/dist/openrouter/index.js +381 -385
- package/dist/openrouter/index.js.map +1 -1
- package/dist/proxy/index.d.ts +10 -914
- package/dist/proxy/index.js +275 -1007
- package/dist/proxy/index.js.map +1 -1
- package/dist/proxy/server/express/index.d.ts +161 -0
- package/dist/proxy/server/express/index.js +24 -0
- package/dist/proxy/server/express/index.js.map +1 -0
- package/dist/proxy/server/fastify/index.d.ts +162 -0
- package/dist/proxy/server/fastify/index.js +24 -0
- package/dist/proxy/server/fastify/index.js.map +1 -0
- package/dist/proxy/server/h3/index.d.ts +189 -0
- package/dist/proxy/server/h3/index.js +28 -0
- package/dist/proxy/server/h3/index.js.map +1 -0
- package/dist/proxy/server/index.d.ts +151 -0
- package/dist/proxy/server/index.js +48 -0
- package/dist/proxy/server/index.js.map +1 -0
- package/dist/proxy/server/webapi/index.d.ts +278 -0
- package/dist/proxy/server/webapi/index.js +32 -0
- package/dist/proxy/server/webapi/index.js.map +1 -0
- package/dist/responses/index.d.ts +650 -0
- package/dist/responses/index.js +930 -0
- package/dist/responses/index.js.map +1 -0
- package/dist/{retry-8Ch-WWgX.d.ts → retry-BDMo4AVu.d.ts} +1 -1
- package/dist/stream-S7nwQRqM.d.ts +1643 -0
- package/dist/types-CE4B7pno.d.ts +96 -0
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/xai/index.d.ts +2 -1
- package/dist/xai/index.js +310 -310
- package/dist/xai/index.js.map +1 -1
- package/package.json +82 -4
- package/dist/chunk-4J6OFUKX.js.map +0 -1
- package/dist/chunk-6S222DHN.js.map +0 -1
- package/dist/chunk-ILR2D5PN.js.map +0 -1
- package/dist/chunk-TOJCZMVU.js.map +0 -1
- package/dist/chunk-WAKD3OO5.js.map +0 -1
- /package/dist/{chunk-3D6XGGVG.js.map → chunk-ARVM24K2.js.map} +0 -0
- /package/dist/{chunk-KUPF5KHT.js.map → chunk-Y5H7C5J4.js.map} +0 -0
package/README.md
CHANGED
|
@@ -26,9 +26,12 @@ console.log(turn.response.text);
|
|
|
26
26
|
| Google | `@providerprotocol/ai/google` | ✓ | ✓ | ✓ |
|
|
27
27
|
| xAI | `@providerprotocol/ai/xai` | ✓ | | ✓ |
|
|
28
28
|
| Ollama | `@providerprotocol/ai/ollama` | ✓ | ✓ | |
|
|
29
|
-
| OpenRouter | `@providerprotocol/ai/openrouter` | ✓ | ✓ | |
|
|
29
|
+
| OpenRouter | `@providerprotocol/ai/openrouter` | ✓ | ✓ | ✓ |
|
|
30
|
+
| Groq | `@providerprotocol/ai/groq` | ✓ | | |
|
|
31
|
+
| Cerebras | `@providerprotocol/ai/cerebras` | ✓ | | |
|
|
32
|
+
| OpenResponses | `@providerprotocol/ai/responses` | ✓ | | |
|
|
30
33
|
|
|
31
|
-
API keys are loaded automatically from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.).
|
|
34
|
+
API keys are loaded automatically from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, `CEREBRAS_API_KEY`, etc.).
|
|
32
35
|
|
|
33
36
|
## LLM
|
|
34
37
|
|
|
@@ -44,6 +47,12 @@ for await (const event of stream) {
|
|
|
44
47
|
const turn = await stream.turn;
|
|
45
48
|
```
|
|
46
49
|
|
|
50
|
+
Stream results are PromiseLike, so you can also await the stream directly to auto-drain:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const turn = await claude.stream('Count to 5');
|
|
54
|
+
```
|
|
55
|
+
|
|
47
56
|
**Stream Control:**
|
|
48
57
|
|
|
49
58
|
```typescript
|
|
@@ -63,6 +72,7 @@ for await (const event of stream) {
|
|
|
63
72
|
|-------|-------------|
|
|
64
73
|
| `text_delta` | Incremental text output |
|
|
65
74
|
| `reasoning_delta` | Incremental reasoning/thinking output |
|
|
75
|
+
| `object_delta` | Incremental structured output JSON |
|
|
66
76
|
| `tool_call_delta` | Tool call arguments being streamed |
|
|
67
77
|
| `tool_execution_start` | Tool execution has started |
|
|
68
78
|
| `tool_execution_end` | Tool execution has completed |
|
|
@@ -123,12 +133,36 @@ console.log(turn.data); // { name: 'John', age: 30 }
|
|
|
123
133
|
### Multimodal Input
|
|
124
134
|
|
|
125
135
|
```typescript
|
|
126
|
-
import { Image } from '@providerprotocol/ai';
|
|
136
|
+
import { Image, Document, Audio, Video } from '@providerprotocol/ai';
|
|
127
137
|
|
|
138
|
+
// Images
|
|
128
139
|
const img = await Image.fromPath('./photo.png');
|
|
129
140
|
const turn = await claude.generate([img, 'What is in this image?']);
|
|
141
|
+
|
|
142
|
+
// Documents (PDF, text)
|
|
143
|
+
const doc = await Document.fromPath('./report.pdf', 'Annual Report');
|
|
144
|
+
const docTurn = await claude.generate([doc.toBlock(), 'Summarize this document']);
|
|
145
|
+
|
|
146
|
+
// Audio (Google, OpenRouter)
|
|
147
|
+
const audio = await Audio.fromPath('./recording.mp3');
|
|
148
|
+
const audioTurn = await gemini.generate([audio.toBlock(), 'Transcribe this audio']);
|
|
149
|
+
|
|
150
|
+
// Video (Google, OpenRouter)
|
|
151
|
+
const video = await Video.fromPath('./clip.mp4');
|
|
152
|
+
const videoTurn = await gemini.generate([video.toBlock(), 'Describe this video']);
|
|
130
153
|
```
|
|
131
154
|
|
|
155
|
+
**Multimodal Support by Provider:**
|
|
156
|
+
|
|
157
|
+
| Provider | Image | Document | Audio | Video |
|
|
158
|
+
|----------|:-----:|:--------:|:-----:|:-----:|
|
|
159
|
+
| Anthropic | ✓ | PDF, Text | | |
|
|
160
|
+
| OpenAI | ✓ | PDF, Text | | |
|
|
161
|
+
| Google | ✓ | PDF, Text | ✓ | ✓ |
|
|
162
|
+
| OpenRouter | ✓ | PDF, Text | ✓ | ✓ |
|
|
163
|
+
| xAI | ✓ | | | |
|
|
164
|
+
| Groq | ✓ | | | |
|
|
165
|
+
|
|
132
166
|
## Anthropic Beta Features
|
|
133
167
|
|
|
134
168
|
Anthropic provides beta features through the `betas` export. Enable them at the model level:
|
|
@@ -168,21 +202,164 @@ const thinker = llm({
|
|
|
168
202
|
| `interleavedThinking` | Claude can think between tool calls |
|
|
169
203
|
| `devFullThinking` | Developer mode for full thinking visibility |
|
|
170
204
|
| `effort` | Control response thoroughness vs efficiency (Opus 4.5) |
|
|
205
|
+
| `computerUseLegacy` | Computer use for Claude 3.x models |
|
|
171
206
|
| `computerUse` | Mouse, keyboard, screenshot control (Claude 4) |
|
|
207
|
+
| `computerUseOpus` | Computer use with extra commands (Opus 4.5) |
|
|
172
208
|
| `codeExecution` | Python/Bash sandbox execution |
|
|
173
209
|
| `tokenEfficientTools` | Up to 70% token reduction for tool calls |
|
|
174
210
|
| `fineGrainedToolStreaming` | Stream tool args without buffering |
|
|
211
|
+
| `maxTokens35Sonnet` | 8,192 output tokens for Claude 3.5 Sonnet |
|
|
175
212
|
| `output128k` | 128K token output length |
|
|
176
213
|
| `context1m` | 1 million token context window (Sonnet 4) |
|
|
177
214
|
| `promptCaching` | Reduced latency and costs via caching |
|
|
178
215
|
| `extendedCacheTtl` | 1-hour cache TTL (vs 5-minute default) |
|
|
216
|
+
| `contextManagement` | Automatic tool call clearing for context |
|
|
217
|
+
| `modelContextWindowExceeded` | Handle exceeded context windows |
|
|
179
218
|
| `advancedToolUse` | Tool Search, Programmatic Tool Calling |
|
|
180
219
|
| `mcpClient` | Connect to remote MCP servers |
|
|
220
|
+
| `mcpClientLatest` | Updated MCP client |
|
|
181
221
|
| `filesApi` | Upload and manage files |
|
|
182
222
|
| `pdfs` | PDF document support |
|
|
223
|
+
| `tokenCounting` | Token counting endpoint |
|
|
183
224
|
| `messageBatches` | Async batch processing at 50% cost |
|
|
184
225
|
| `skills` | Agent Skills (PowerPoint, Excel, Word, PDF) |
|
|
185
226
|
|
|
227
|
+
## Anthropic Built-in Tools
|
|
228
|
+
|
|
229
|
+
Use Anthropic's built-in tools directly with the `tools` export:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { anthropic, betas, tools } from '@providerprotocol/ai/anthropic';
|
|
233
|
+
import { llm } from '@providerprotocol/ai';
|
|
234
|
+
|
|
235
|
+
// Web search with optional user location
|
|
236
|
+
const model = llm({
|
|
237
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
238
|
+
params: {
|
|
239
|
+
tools: [tools.webSearch({ max_results: 5 })],
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Computer use (requires beta)
|
|
244
|
+
const computerModel = llm({
|
|
245
|
+
model: anthropic('claude-sonnet-4-20250514', {
|
|
246
|
+
betas: [betas.computerUse],
|
|
247
|
+
}),
|
|
248
|
+
params: {
|
|
249
|
+
tools: [tools.computer({ display_width: 1920, display_height: 1080, display_number: 1 })],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Code execution (requires beta)
|
|
254
|
+
const codeModel = llm({
|
|
255
|
+
model: anthropic('claude-sonnet-4-20250514', {
|
|
256
|
+
betas: [betas.codeExecution],
|
|
257
|
+
}),
|
|
258
|
+
params: {
|
|
259
|
+
tools: [tools.codeExecution()],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Available Built-in Tools:**
|
|
265
|
+
|
|
266
|
+
| Tool | Description |
|
|
267
|
+
|------|-------------|
|
|
268
|
+
| `tools.webSearch()` | Search the web with optional max results and location |
|
|
269
|
+
| `tools.computer()` | Mouse, keyboard, and screenshot control |
|
|
270
|
+
| `tools.textEditor()` | Edit text files programmatically |
|
|
271
|
+
| `tools.bash()` | Execute bash commands |
|
|
272
|
+
| `tools.codeExecution()` | Run code in a sandboxed environment |
|
|
273
|
+
| `tools.toolSearch()` | Search through available tools |
|
|
274
|
+
|
|
275
|
+
## Reasoning / Extended Thinking
|
|
276
|
+
|
|
277
|
+
Access model reasoning and extended thinking across providers with a unified API.
|
|
278
|
+
|
|
279
|
+
### Anthropic
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { llm } from '@providerprotocol/ai';
|
|
283
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
284
|
+
|
|
285
|
+
const claude = llm({
|
|
286
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
287
|
+
params: {
|
|
288
|
+
max_tokens: 16000,
|
|
289
|
+
thinking: {
|
|
290
|
+
type: 'enabled',
|
|
291
|
+
budget_tokens: 5000,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const turn = await claude.generate('Solve this complex problem...');
|
|
297
|
+
console.log(turn.response.reasoning); // Reasoning blocks
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### OpenAI
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { llm } from '@providerprotocol/ai';
|
|
304
|
+
import { openai } from '@providerprotocol/ai/openai';
|
|
305
|
+
|
|
306
|
+
const gpt = llm({
|
|
307
|
+
model: openai('o3-mini'),
|
|
308
|
+
params: {
|
|
309
|
+
max_output_tokens: 4000,
|
|
310
|
+
reasoning: {
|
|
311
|
+
effort: 'medium',
|
|
312
|
+
summary: 'detailed',
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Google Gemini
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { llm } from '@providerprotocol/ai';
|
|
322
|
+
import { google } from '@providerprotocol/ai/google';
|
|
323
|
+
|
|
324
|
+
const gemini = llm({
|
|
325
|
+
model: google('gemini-2.5-flash'),
|
|
326
|
+
params: {
|
|
327
|
+
maxOutputTokens: 4000,
|
|
328
|
+
thinkingConfig: {
|
|
329
|
+
thinkingBudget: -1, // Dynamic
|
|
330
|
+
includeThoughts: true,
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Cerebras
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { llm } from '@providerprotocol/ai';
|
|
340
|
+
import { cerebras } from '@providerprotocol/ai/cerebras';
|
|
341
|
+
|
|
342
|
+
const model = llm({
|
|
343
|
+
model: cerebras('gpt-oss-120b'),
|
|
344
|
+
params: {
|
|
345
|
+
reasoning_effort: 'high',
|
|
346
|
+
reasoning_format: 'parsed',
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Streaming Reasoning
|
|
352
|
+
|
|
353
|
+
All providers emit `ReasoningDelta` events during streaming:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
for await (const event of stream) {
|
|
357
|
+
if (event.type === 'reasoning_delta') {
|
|
358
|
+
process.stdout.write(event.delta.text);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
186
363
|
## Embeddings
|
|
187
364
|
|
|
188
365
|
```typescript
|
|
@@ -414,6 +591,161 @@ localStorage.setItem('conversation', JSON.stringify(json));
|
|
|
414
591
|
const restored = Thread.fromJSON(JSON.parse(localStorage.getItem('conversation')));
|
|
415
592
|
```
|
|
416
593
|
|
|
594
|
+
## Middleware
|
|
595
|
+
|
|
596
|
+
Compose request/response/stream transformations with the middleware system. Middleware is imported from dedicated entry points.
|
|
597
|
+
|
|
598
|
+
### Parsed Object Middleware
|
|
599
|
+
|
|
600
|
+
Automatically parse streaming JSON from structured output and tool call events:
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
import { llm } from '@providerprotocol/ai';
|
|
604
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
605
|
+
import { parsedObjectMiddleware } from '@providerprotocol/ai/middleware/parsed-object';
|
|
606
|
+
|
|
607
|
+
const model = llm({
|
|
608
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
609
|
+
structure: {
|
|
610
|
+
type: 'object',
|
|
611
|
+
properties: {
|
|
612
|
+
city: { type: 'string' },
|
|
613
|
+
country: { type: 'string' },
|
|
614
|
+
population: { type: 'number' },
|
|
615
|
+
},
|
|
616
|
+
required: ['city', 'country', 'population'],
|
|
617
|
+
},
|
|
618
|
+
middleware: [parsedObjectMiddleware()],
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
for await (const event of model.stream('What is the capital of France?')) {
|
|
622
|
+
if (event.type === 'object_delta') {
|
|
623
|
+
// Access incrementally parsed structured data
|
|
624
|
+
console.log(event.delta.parsed);
|
|
625
|
+
// { city: "Par" } -> { city: "Paris" } -> { city: "Paris", country: "Fr" } -> ...
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Logging Middleware
|
|
631
|
+
|
|
632
|
+
Add visibility into request lifecycle:
|
|
633
|
+
|
|
634
|
+
```typescript
|
|
635
|
+
import { llm } from '@providerprotocol/ai';
|
|
636
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
637
|
+
import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';
|
|
638
|
+
|
|
639
|
+
const model = llm({
|
|
640
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
641
|
+
middleware: [loggingMiddleware({ level: 'debug' })],
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Logs: [PP] [anthropic] Starting llm request (streaming)
|
|
645
|
+
// Logs: [PP] [anthropic] Completed in 1234ms
|
|
646
|
+
const result = await model.generate('Hello');
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
### Pub-Sub Middleware (Stream Resumption)
|
|
650
|
+
|
|
651
|
+
Enable reconnecting clients to catch up on missed events during active generation. The middleware buffers events, publishes them to subscribers, and removes streams on completion/abort/error.
|
|
652
|
+
If a stream never reaches those hooks (for example, a process crash), the adapter may retain the entry. Custom adapters should invoke `onComplete` when `remove()` runs so subscriber streams can terminate.
|
|
653
|
+
Streams are created lazily on first `append()` or `subscribe()` call.
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
import { llm } from '@providerprotocol/ai';
|
|
657
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
658
|
+
import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
|
|
659
|
+
import { webapi } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
660
|
+
|
|
661
|
+
// Create a shared adapter instance
|
|
662
|
+
const adapter = memoryAdapter({ maxStreams: 1000 });
|
|
663
|
+
|
|
664
|
+
// Server route handling both new requests and reconnections
|
|
665
|
+
Bun.serve({
|
|
666
|
+
port: 3000,
|
|
667
|
+
async fetch(req) {
|
|
668
|
+
const { messages, streamId } = await req.json();
|
|
669
|
+
const exists = await adapter.exists(streamId);
|
|
670
|
+
|
|
671
|
+
if (!exists) {
|
|
672
|
+
// Start background generation (fire and forget)
|
|
673
|
+
// Stream is created lazily on first append()
|
|
674
|
+
const model = llm({
|
|
675
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
676
|
+
middleware: [pubsubMiddleware({ adapter, streamId })],
|
|
677
|
+
});
|
|
678
|
+
model.stream(messages).then(turn => { /* save to DB */ });
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Both new and reconnect: subscribe to events
|
|
682
|
+
return new Response(webapi.createSubscriberStream(streamId, adapter), {
|
|
683
|
+
headers: { 'Content-Type': 'text/event-stream' },
|
|
684
|
+
});
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
**Framework Adapters:**
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
// Express
|
|
693
|
+
import { express } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
694
|
+
app.post('/api/ai/reconnect', (req, res) => {
|
|
695
|
+
const { streamId } = req.body;
|
|
696
|
+
express.streamSubscriber(streamId, adapter, res);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// Fastify
|
|
700
|
+
import { fastify } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
701
|
+
app.post('/api/ai/reconnect', (request, reply) => {
|
|
702
|
+
const { streamId } = request.body;
|
|
703
|
+
return fastify.streamSubscriber(streamId, adapter, reply);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// H3/Nuxt
|
|
707
|
+
import { h3 } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
708
|
+
export default defineEventHandler(async (event) => {
|
|
709
|
+
const { streamId } = await readBody(event);
|
|
710
|
+
return h3.streamSubscriber(streamId, adapter, event);
|
|
711
|
+
});
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**Custom Adapters:**
|
|
715
|
+
|
|
716
|
+
Implement `PubSubAdapter` for custom backends (Redis, etc.):
|
|
717
|
+
|
|
718
|
+
```typescript
|
|
719
|
+
import type { PubSubAdapter } from '@providerprotocol/ai/middleware/pubsub';
|
|
720
|
+
|
|
721
|
+
const redisAdapter: PubSubAdapter = {
|
|
722
|
+
async exists(streamId) { /* check if stream exists */ },
|
|
723
|
+
async append(streamId, event) { /* append event, create lazily */ },
|
|
724
|
+
async getEvents(streamId) { /* return events or [] */ },
|
|
725
|
+
subscribe(streamId, onEvent, onComplete) { /* subscribe to live events */ },
|
|
726
|
+
publish(streamId, event) { /* broadcast to subscribers */ },
|
|
727
|
+
async remove(streamId) { /* notify onComplete then delete */ },
|
|
728
|
+
};
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### Combining Middleware
|
|
732
|
+
|
|
733
|
+
```typescript
|
|
734
|
+
import { llm } from '@providerprotocol/ai';
|
|
735
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
736
|
+
import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';
|
|
737
|
+
import { parsedObjectMiddleware } from '@providerprotocol/ai/middleware/parsed-object';
|
|
738
|
+
|
|
739
|
+
const model = llm({
|
|
740
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
741
|
+
structure: mySchema,
|
|
742
|
+
middleware: [
|
|
743
|
+
loggingMiddleware({ level: 'info' }),
|
|
744
|
+
parsedObjectMiddleware(),
|
|
745
|
+
],
|
|
746
|
+
});
|
|
747
|
+
```
|
|
748
|
+
|
|
417
749
|
## Error Handling
|
|
418
750
|
|
|
419
751
|
All errors are normalized to `UPPError` with consistent error codes:
|
|
@@ -561,6 +893,74 @@ export default defineEventHandler(async (event) => {
|
|
|
561
893
|
- Request/response logging, content filtering
|
|
562
894
|
- Double-layer retry: client retries to proxy, server retries to AI provider
|
|
563
895
|
|
|
896
|
+
## OpenAI API Modes
|
|
897
|
+
|
|
898
|
+
OpenAI supports two API endpoints. The Responses API is the default and recommended approach:
|
|
899
|
+
|
|
900
|
+
```typescript
|
|
901
|
+
import { openai } from '@providerprotocol/ai/openai';
|
|
902
|
+
|
|
903
|
+
// Responses API (default, recommended)
|
|
904
|
+
openai('gpt-4o')
|
|
905
|
+
|
|
906
|
+
// Chat Completions API (legacy)
|
|
907
|
+
openai('gpt-4o', { api: 'completions' })
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
The Responses API supports built-in tools and stateful conversations. Use completions for backward compatibility.
|
|
911
|
+
|
|
912
|
+
## OpenAI Built-in Tools
|
|
913
|
+
|
|
914
|
+
With the Responses API, use OpenAI's built-in tools directly:
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
import { llm } from '@providerprotocol/ai';
|
|
918
|
+
import { openai, tools } from '@providerprotocol/ai/openai';
|
|
919
|
+
|
|
920
|
+
// Web search
|
|
921
|
+
const model = llm({
|
|
922
|
+
model: openai('gpt-4o'),
|
|
923
|
+
params: {
|
|
924
|
+
tools: [tools.webSearch()],
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// File search with vector stores
|
|
929
|
+
const researchModel = llm({
|
|
930
|
+
model: openai('gpt-4o'),
|
|
931
|
+
params: {
|
|
932
|
+
tools: [tools.fileSearch({ vector_store_ids: ['vs_abc123'] })],
|
|
933
|
+
},
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
// Code interpreter
|
|
937
|
+
const codeModel = llm({
|
|
938
|
+
model: openai('gpt-4o'),
|
|
939
|
+
params: {
|
|
940
|
+
tools: [tools.codeInterpreter()],
|
|
941
|
+
},
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
// Image generation
|
|
945
|
+
const creativeModel = llm({
|
|
946
|
+
model: openai('gpt-4o'),
|
|
947
|
+
params: {
|
|
948
|
+
tools: [tools.imageGeneration()],
|
|
949
|
+
},
|
|
950
|
+
});
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
**Available Built-in Tools:**
|
|
954
|
+
|
|
955
|
+
| Tool | Description |
|
|
956
|
+
|------|-------------|
|
|
957
|
+
| `tools.webSearch()` | Search the web with optional user location |
|
|
958
|
+
| `tools.fileSearch()` | Search uploaded files in vector stores |
|
|
959
|
+
| `tools.codeInterpreter()` | Execute code in a sandboxed environment |
|
|
960
|
+
| `tools.computer()` | Computer use with display configuration |
|
|
961
|
+
| `tools.imageGeneration()` | Generate images via DALL-E |
|
|
962
|
+
| `tools.mcp()` | Connect to MCP servers |
|
|
963
|
+
|
|
564
964
|
## xAI API Modes
|
|
565
965
|
|
|
566
966
|
xAI supports multiple API compatibility modes:
|
|
@@ -578,6 +978,119 @@ xai('grok-3-fast', { api: 'responses' })
|
|
|
578
978
|
xai('grok-3-fast', { api: 'messages' })
|
|
579
979
|
```
|
|
580
980
|
|
|
981
|
+
## Groq
|
|
982
|
+
|
|
983
|
+
Fast inference with Llama, Gemma, and Mixtral models:
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
import { llm } from '@providerprotocol/ai';
|
|
987
|
+
import { groq } from '@providerprotocol/ai/groq';
|
|
988
|
+
|
|
989
|
+
const model = llm({
|
|
990
|
+
model: groq('llama-3.3-70b-versatile'),
|
|
991
|
+
params: { max_tokens: 1000 },
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
const turn = await model.generate('Hello!');
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
**With web search:**
|
|
998
|
+
|
|
999
|
+
```typescript
|
|
1000
|
+
const searchModel = llm({
|
|
1001
|
+
model: groq('llama-3.3-70b-versatile'),
|
|
1002
|
+
params: {
|
|
1003
|
+
search_settings: { mode: 'auto' },
|
|
1004
|
+
},
|
|
1005
|
+
});
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
**With RAG documents:**
|
|
1009
|
+
|
|
1010
|
+
```typescript
|
|
1011
|
+
const ragModel = llm({
|
|
1012
|
+
model: groq('llama-3.3-70b-versatile'),
|
|
1013
|
+
params: {
|
|
1014
|
+
documents: [
|
|
1015
|
+
{ title: 'Doc 1', content: 'Document content here...' },
|
|
1016
|
+
{ title: 'Doc 2', content: 'More content...' },
|
|
1017
|
+
],
|
|
1018
|
+
citation_options: { include: true },
|
|
1019
|
+
},
|
|
1020
|
+
});
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**Capabilities:** Streaming, tool calling, structured output, image input (Llama 4 preview), web search, RAG with citations.
|
|
1024
|
+
|
|
1025
|
+
**Environment:** `GROQ_API_KEY`
|
|
1026
|
+
|
|
1027
|
+
## Cerebras
|
|
1028
|
+
|
|
1029
|
+
Ultra-fast inference with Llama, Qwen, and GPT-OSS models:
|
|
1030
|
+
|
|
1031
|
+
```typescript
|
|
1032
|
+
import { llm } from '@providerprotocol/ai';
|
|
1033
|
+
import { cerebras } from '@providerprotocol/ai/cerebras';
|
|
1034
|
+
|
|
1035
|
+
const model = llm({
|
|
1036
|
+
model: cerebras('llama-3.3-70b'),
|
|
1037
|
+
params: { max_completion_tokens: 1000 },
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
const turn = await model.generate('Hello!');
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
**With reasoning (GPT-OSS):**
|
|
1044
|
+
|
|
1045
|
+
```typescript
|
|
1046
|
+
const model = llm({
|
|
1047
|
+
model: cerebras('gpt-oss-120b'),
|
|
1048
|
+
params: {
|
|
1049
|
+
reasoning_effort: 'high',
|
|
1050
|
+
reasoning_format: 'parsed',
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
**Capabilities:** Streaming, tool calling, structured output, reasoning parameters.
|
|
1056
|
+
|
|
1057
|
+
**Environment:** `CEREBRAS_API_KEY`
|
|
1058
|
+
|
|
1059
|
+
## OpenResponses Provider
|
|
1060
|
+
|
|
1061
|
+
Connect to any server implementing the [OpenResponses specification](https://www.openresponses.org):
|
|
1062
|
+
|
|
1063
|
+
```typescript
|
|
1064
|
+
import { llm } from '@providerprotocol/ai';
|
|
1065
|
+
import { responses } from '@providerprotocol/ai/responses';
|
|
1066
|
+
|
|
1067
|
+
// Using with OpenAI
|
|
1068
|
+
const model = llm({
|
|
1069
|
+
model: responses('gpt-5.2', {
|
|
1070
|
+
host: 'https://api.openai.com/v1',
|
|
1071
|
+
apiKeyEnv: 'OPENAI_API_KEY',
|
|
1072
|
+
}),
|
|
1073
|
+
params: { max_output_tokens: 1000 },
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
// Using with OpenRouter
|
|
1077
|
+
const routerModel = llm({
|
|
1078
|
+
model: responses('openai/gpt-4o', {
|
|
1079
|
+
host: 'https://openrouter.ai/api/v1',
|
|
1080
|
+
apiKeyEnv: 'OPENROUTER_API_KEY',
|
|
1081
|
+
}),
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
// Using with self-hosted server
|
|
1085
|
+
const localModel = llm({
|
|
1086
|
+
model: responses('llama-3.3-70b', {
|
|
1087
|
+
host: 'http://localhost:8080/v1',
|
|
1088
|
+
}),
|
|
1089
|
+
});
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
**Capabilities:** Full multimodal support, streaming, tool calling, structured output, reasoning summaries.
|
|
1093
|
+
|
|
581
1094
|
## Alternative Import Style
|
|
582
1095
|
|
|
583
1096
|
Use the `ai` namespace for a grouped import style:
|
|
@@ -607,6 +1120,14 @@ import type {
|
|
|
607
1120
|
StreamEvent,
|
|
608
1121
|
StreamResult,
|
|
609
1122
|
|
|
1123
|
+
// Content blocks
|
|
1124
|
+
TextBlock,
|
|
1125
|
+
ImageBlock,
|
|
1126
|
+
ReasoningBlock,
|
|
1127
|
+
DocumentBlock,
|
|
1128
|
+
AudioBlock,
|
|
1129
|
+
VideoBlock,
|
|
1130
|
+
|
|
610
1131
|
// Modality results
|
|
611
1132
|
EmbeddingResult,
|
|
612
1133
|
ImageResult,
|
|
@@ -620,9 +1141,31 @@ import type {
|
|
|
620
1141
|
KeyStrategy,
|
|
621
1142
|
RetryStrategy,
|
|
622
1143
|
LLMCapabilities,
|
|
1144
|
+
|
|
1145
|
+
// Middleware
|
|
1146
|
+
Middleware,
|
|
1147
|
+
MiddlewareContext,
|
|
1148
|
+
StreamContext,
|
|
623
1149
|
} from '@providerprotocol/ai';
|
|
624
1150
|
```
|
|
625
1151
|
|
|
1152
|
+
**Type-Safe Enums:**
|
|
1153
|
+
|
|
1154
|
+
```typescript
|
|
1155
|
+
import {
|
|
1156
|
+
StreamEventType,
|
|
1157
|
+
ErrorCode,
|
|
1158
|
+
ContentBlockType,
|
|
1159
|
+
MessageRole,
|
|
1160
|
+
ModalityType,
|
|
1161
|
+
} from '@providerprotocol/ai';
|
|
1162
|
+
|
|
1163
|
+
// Use instead of magic strings
|
|
1164
|
+
if (event.type === StreamEventType.TextDelta) { ... }
|
|
1165
|
+
if (error.code === ErrorCode.RateLimited) { ... }
|
|
1166
|
+
if (block.type === ContentBlockType.Text) { ... }
|
|
1167
|
+
```
|
|
1168
|
+
|
|
626
1169
|
### Custom Providers
|
|
627
1170
|
|
|
628
1171
|
Build custom providers with `createProvider`:
|