@providerprotocol/ai 0.0.38 → 0.0.40
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 +363 -37
- package/dist/anthropic/index.d.ts +3 -2
- package/dist/anthropic/index.js +7 -5
- package/dist/anthropic/index.js.map +1 -1
- package/dist/cerebras/index.d.ts +3 -2
- package/dist/cerebras/index.js +7 -5
- package/dist/cerebras/index.js.map +1 -1
- package/dist/chunk-3Q5VELKG.js +124 -0
- package/dist/chunk-3Q5VELKG.js.map +1 -0
- package/dist/{chunk-WU4U6IHF.js → chunk-6QCV4WXF.js} +4 -13
- package/dist/chunk-6QCV4WXF.js.map +1 -0
- package/dist/{chunk-LTEMH3CI.js → chunk-AC3VHSZJ.js} +6 -4
- package/dist/{chunk-LTEMH3CI.js.map → chunk-AC3VHSZJ.js.map} +1 -1
- package/dist/{chunk-YQLR3XOA.js → chunk-BIBMNP7Y.js} +1 -75
- package/dist/chunk-BIBMNP7Y.js.map +1 -0
- package/dist/{chunk-CRP6Y7NF.js → chunk-CWGTARDE.js} +2 -2
- package/dist/{chunk-ZRVNAET3.js → chunk-DI47UY2H.js} +6 -3
- package/dist/chunk-DI47UY2H.js.map +1 -0
- package/dist/{chunk-7GTWHZY2.js → chunk-EHR3LIPS.js} +5 -3
- package/dist/{chunk-7GTWHZY2.js.map → chunk-EHR3LIPS.js.map} +1 -1
- package/dist/chunk-EY2LLDGY.js +94 -0
- package/dist/chunk-EY2LLDGY.js.map +1 -0
- package/dist/{chunk-MJI74VEJ.js → chunk-F5ENANMJ.js} +18 -2
- package/dist/chunk-F5ENANMJ.js.map +1 -0
- package/dist/chunk-IKJH5ZSJ.js +1 -0
- package/dist/chunk-IKJH5ZSJ.js.map +1 -0
- package/dist/{chunk-4RX4VQCB.js → chunk-KBI45OXI.js} +2 -2
- package/dist/{chunk-5IWHCXKN.js → chunk-KVUOTFYZ.js} +2 -2
- package/dist/{chunk-EPB3GQNL.js → chunk-L6QWKFGE.js} +13 -3
- package/dist/chunk-L6QWKFGE.js.map +1 -0
- package/dist/{chunk-BDXH6NQS.js → chunk-N4LAFGLX.js} +7 -7
- package/dist/{chunk-ZKNPQBIE.js → chunk-R3T2IYOU.js} +5 -3
- package/dist/{chunk-ZKNPQBIE.js.map → chunk-R3T2IYOU.js.map} +1 -1
- package/dist/chunk-U2G5PHHL.js +25 -0
- package/dist/chunk-U2G5PHHL.js.map +1 -0
- package/dist/{chunk-SBGZJVTJ.js → chunk-VQZPADW6.js} +100 -33
- package/dist/chunk-VQZPADW6.js.map +1 -0
- package/dist/{chunk-FYSZFIZS.js → chunk-XTWBAL42.js} +5 -3
- package/dist/{chunk-FYSZFIZS.js.map → chunk-XTWBAL42.js.map} +1 -1
- package/dist/{chunk-2YXFLRQ6.js → chunk-ZMESKGUY.js} +2 -2
- package/dist/chunk-ZSZVWLGE.js +83 -0
- package/dist/chunk-ZSZVWLGE.js.map +1 -0
- package/dist/{embedding-CwZ1ZNWv.d.ts → embedding-ts1npsDg.d.ts} +1 -1
- package/dist/google/index.d.ts +38 -3
- package/dist/google/index.js +5 -4
- package/dist/google/index.js.map +1 -1
- package/dist/groq/index.d.ts +3 -2
- package/dist/groq/index.js +7 -5
- package/dist/groq/index.js.map +1 -1
- package/dist/http/index.d.ts +5 -4
- package/dist/http/index.js +19 -22
- package/dist/{image-stream-CeQHtjxS.d.ts → image-stream-BPml2YZZ.d.ts} +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +316 -113
- package/dist/index.js.map +1 -1
- package/dist/{llm-DS_-l71X.d.ts → llm-BWLaTzzY.d.ts} +89 -36
- package/dist/middleware/logging/index.d.ts +3 -2
- package/dist/middleware/logging/index.js +3 -0
- package/dist/middleware/logging/index.js.map +1 -1
- package/dist/middleware/parsed-object/index.d.ts +3 -2
- package/dist/middleware/parsed-object/index.js +5 -1
- package/dist/middleware/parsed-object/index.js.map +1 -1
- package/dist/middleware/persistence/index.d.ts +3 -2
- package/dist/middleware/persistence/index.js +3 -2
- package/dist/middleware/persistence/index.js.map +1 -1
- package/dist/middleware/pipeline/index.d.ts +195 -0
- package/dist/middleware/pipeline/index.js +61 -0
- package/dist/middleware/pipeline/index.js.map +1 -0
- package/dist/middleware/pubsub/index.d.ts +13 -10
- package/dist/middleware/pubsub/index.js +78 -6
- package/dist/middleware/pubsub/index.js.map +1 -1
- package/dist/middleware/pubsub/server/express/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/express/index.js +2 -2
- package/dist/middleware/pubsub/server/fastify/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/fastify/index.js +2 -2
- package/dist/middleware/pubsub/server/h3/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/h3/index.js +2 -2
- package/dist/middleware/pubsub/server/index.d.ts +50 -8
- package/dist/middleware/pubsub/server/index.js +5 -5
- package/dist/middleware/pubsub/server/index.js.map +1 -1
- package/dist/middleware/pubsub/server/webapi/index.d.ts +3 -2
- package/dist/middleware/pubsub/server/webapi/index.js +2 -2
- package/dist/moonshot/index.d.ts +3 -2
- package/dist/moonshot/index.js +7 -5
- package/dist/moonshot/index.js.map +1 -1
- package/dist/ollama/index.d.ts +24 -3
- package/dist/ollama/index.js +5 -4
- package/dist/ollama/index.js.map +1 -1
- package/dist/openai/index.d.ts +65 -3
- package/dist/openai/index.js +7 -5
- package/dist/openai/index.js.map +1 -1
- package/dist/openrouter/index.d.ts +4 -3
- package/dist/openrouter/index.js +7 -5
- package/dist/openrouter/index.js.map +1 -1
- package/dist/proxy/index.d.ts +5 -4
- package/dist/proxy/index.js +20 -17
- package/dist/proxy/index.js.map +1 -1
- package/dist/proxy/server/express/index.d.ts +8 -8
- package/dist/proxy/server/express/index.js +5 -3
- package/dist/proxy/server/fastify/index.d.ts +8 -8
- package/dist/proxy/server/fastify/index.js +5 -3
- package/dist/proxy/server/h3/index.d.ts +22 -21
- package/dist/proxy/server/h3/index.js +5 -3
- package/dist/proxy/server/index.d.ts +5 -4
- package/dist/proxy/server/index.js +15 -13
- package/dist/proxy/server/webapi/index.d.ts +8 -8
- package/dist/proxy/server/webapi/index.js +5 -3
- package/dist/responses/index.d.ts +3 -2
- package/dist/responses/index.js +7 -5
- package/dist/responses/index.js.map +1 -1
- package/dist/retry-DVfdPLIB.d.ts +322 -0
- package/dist/{stream-sXhBtWjl.d.ts → stream-bBd_4Ipu.d.ts} +29 -419
- package/dist/tool-BmAfKNBq.d.ts +507 -0
- package/dist/{types-Cr4F0tVy.d.ts → types-nTwlpyJE.d.ts} +28 -3
- package/dist/utils/index.d.ts +129 -1
- package/dist/utils/index.js +28 -1
- package/dist/xai/index.d.ts +3 -2
- package/dist/xai/index.js +7 -5
- package/dist/xai/index.js.map +1 -1
- package/package.json +20 -3
- package/dist/chunk-ARVM24K2.js +0 -128
- package/dist/chunk-ARVM24K2.js.map +0 -1
- package/dist/chunk-EPB3GQNL.js.map +0 -1
- package/dist/chunk-MJI74VEJ.js.map +0 -1
- package/dist/chunk-SBGZJVTJ.js.map +0 -1
- package/dist/chunk-WU4U6IHF.js.map +0 -1
- package/dist/chunk-Y5H7C5J4.js +0 -263
- package/dist/chunk-Y5H7C5J4.js.map +0 -1
- package/dist/chunk-YQLR3XOA.js.map +0 -1
- package/dist/chunk-ZRVNAET3.js.map +0 -1
- package/dist/retry-CgoBNa51.d.ts +0 -531
- /package/dist/{chunk-CRP6Y7NF.js.map → chunk-CWGTARDE.js.map} +0 -0
- /package/dist/{chunk-4RX4VQCB.js.map → chunk-KBI45OXI.js.map} +0 -0
- /package/dist/{chunk-5IWHCXKN.js.map → chunk-KVUOTFYZ.js.map} +0 -0
- /package/dist/{chunk-BDXH6NQS.js.map → chunk-N4LAFGLX.js.map} +0 -0
- /package/dist/{chunk-2YXFLRQ6.js.map → chunk-ZMESKGUY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -29,9 +29,10 @@ console.log(turn.response.text);
|
|
|
29
29
|
| OpenRouter | `@providerprotocol/ai/openrouter` | ✓ | ✓ | ✓ |
|
|
30
30
|
| Groq | `@providerprotocol/ai/groq` | ✓ | | |
|
|
31
31
|
| Cerebras | `@providerprotocol/ai/cerebras` | ✓ | | |
|
|
32
|
+
| Moonshot | `@providerprotocol/ai/moonshot` | ✓ | | |
|
|
32
33
|
| OpenResponses | `@providerprotocol/ai/responses` | ✓ | | |
|
|
33
34
|
|
|
34
|
-
API keys are loaded automatically from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, `CEREBRAS_API_KEY`, etc.).
|
|
35
|
+
API keys are loaded automatically from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, `CEREBRAS_API_KEY`, `MOONSHOT_API_KEY`, etc.).
|
|
35
36
|
|
|
36
37
|
## LLM
|
|
37
38
|
|
|
@@ -91,6 +92,21 @@ const t2 = await claude.generate(history, 'What is my name?');
|
|
|
91
92
|
// Response: "Your name is Alice"
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
### System-Only Inference
|
|
96
|
+
|
|
97
|
+
Both `generate()` and `stream()` can be called with no arguments for system-prompt-only inference:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const assistant = llm({
|
|
101
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
102
|
+
system: 'You are a haiku generator. Generate a haiku about coding.',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// No user input needed
|
|
106
|
+
const turn = await assistant.generate();
|
|
107
|
+
console.log(turn.response.text);
|
|
108
|
+
```
|
|
109
|
+
|
|
94
110
|
### Tools
|
|
95
111
|
|
|
96
112
|
```typescript
|
|
@@ -108,6 +124,27 @@ const turn = await claude.generate({
|
|
|
108
124
|
}, 'What is the weather in Tokyo?');
|
|
109
125
|
```
|
|
110
126
|
|
|
127
|
+
#### Tools with Zod Parameters
|
|
128
|
+
|
|
129
|
+
Tool parameters also accept Zod schemas:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { z } from 'zod';
|
|
133
|
+
|
|
134
|
+
const model = llm({
|
|
135
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
136
|
+
tools: [{
|
|
137
|
+
name: 'get_weather',
|
|
138
|
+
description: 'Get weather for a location',
|
|
139
|
+
parameters: z.object({
|
|
140
|
+
location: z.string().describe('City name'),
|
|
141
|
+
units: z.enum(['celsius', 'fahrenheit']).optional(),
|
|
142
|
+
}),
|
|
143
|
+
run: async ({ location, units }) => fetchWeather(location, units),
|
|
144
|
+
}],
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
111
148
|
### Structured Output
|
|
112
149
|
|
|
113
150
|
```typescript
|
|
@@ -130,6 +167,37 @@ const turn = await extractor.generate('John is 30 years old');
|
|
|
130
167
|
console.log(turn.data); // { name: 'John', age: 30 }
|
|
131
168
|
```
|
|
132
169
|
|
|
170
|
+
#### Zod Schema Support
|
|
171
|
+
|
|
172
|
+
Structured output and tool parameters accept Zod schemas directly, with automatic conversion to JSON Schema:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { llm } from '@providerprotocol/ai';
|
|
176
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
177
|
+
import { z } from 'zod';
|
|
178
|
+
|
|
179
|
+
const extractor = llm({
|
|
180
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
181
|
+
structure: z.object({
|
|
182
|
+
name: z.string(),
|
|
183
|
+
age: z.number(),
|
|
184
|
+
tags: z.array(z.string()),
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const turn = await extractor.generate('Extract: John Doe, 30 years old, likes coding');
|
|
189
|
+
console.log(turn.data); // { name: "John Doe", age: 30, tags: ["coding"] }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Requirements:**
|
|
193
|
+
- Zod schemas must be object schemas (`z.object()`). Non-object schemas will throw an error.
|
|
194
|
+
- Zod is an optional peer dependency - install only if using Zod schemas:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
bun add zod # v4+ for native JSON Schema conversion
|
|
198
|
+
bun add zod zod-to-json-schema # v3 requires additional package
|
|
199
|
+
```
|
|
200
|
+
|
|
133
201
|
### Multimodal Input
|
|
134
202
|
|
|
135
203
|
```typescript
|
|
@@ -162,6 +230,9 @@ const videoTurn = await gemini.generate([video.toBlock(), 'Describe this video']
|
|
|
162
230
|
| OpenRouter | ✓ | PDF, Text | ✓ | ✓ |
|
|
163
231
|
| xAI | ✓ | | | |
|
|
164
232
|
| Groq | ✓ | | | |
|
|
233
|
+
| Moonshot | ✓ | | | ✓* |
|
|
234
|
+
|
|
235
|
+
\* Moonshot video input is experimental.
|
|
165
236
|
|
|
166
237
|
## Anthropic Beta Features
|
|
167
238
|
|
|
@@ -435,16 +506,15 @@ const result = await editor.edit({
|
|
|
435
506
|
## Configuration
|
|
436
507
|
|
|
437
508
|
```typescript
|
|
438
|
-
import { llm } from '@providerprotocol/ai';
|
|
509
|
+
import { llm, exponentialBackoff, roundRobinKeys } from '@providerprotocol/ai';
|
|
439
510
|
import { openai } from '@providerprotocol/ai/openai';
|
|
440
|
-
import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
441
511
|
|
|
442
512
|
const instance = llm({
|
|
443
513
|
model: openai('gpt-4o'),
|
|
444
514
|
config: {
|
|
445
|
-
apiKey:
|
|
515
|
+
apiKey: roundRobinKeys(['sk-key1', 'sk-key2']),
|
|
446
516
|
timeout: 30000,
|
|
447
|
-
retryStrategy:
|
|
517
|
+
retryStrategy: exponentialBackoff({ maxAttempts: 3 }),
|
|
448
518
|
},
|
|
449
519
|
params: {
|
|
450
520
|
temperature: 0.7,
|
|
@@ -502,34 +572,33 @@ interface ProviderConfig {
|
|
|
502
572
|
### Key Strategies
|
|
503
573
|
|
|
504
574
|
```typescript
|
|
505
|
-
import {
|
|
575
|
+
import { roundRobinKeys, weightedKeys, dynamicKey } from '@providerprotocol/ai/http';
|
|
506
576
|
|
|
507
577
|
// Cycle through keys evenly
|
|
508
|
-
|
|
578
|
+
roundRobinKeys(['sk-1', 'sk-2', 'sk-3'])
|
|
509
579
|
|
|
510
580
|
// Weighted selection (70% key1, 30% key2)
|
|
511
|
-
|
|
581
|
+
weightedKeys([
|
|
512
582
|
{ key: 'sk-1', weight: 70 },
|
|
513
583
|
{ key: 'sk-2', weight: 30 },
|
|
514
584
|
])
|
|
515
585
|
|
|
516
586
|
// Dynamic fetching (secrets manager, etc.)
|
|
517
|
-
|
|
587
|
+
dynamicKey(async () => fetchKeyFromVault())
|
|
518
588
|
```
|
|
519
589
|
|
|
520
590
|
### Retry Strategies
|
|
521
591
|
|
|
522
592
|
```typescript
|
|
523
593
|
import {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
} from '@providerprotocol/ai/http';
|
|
594
|
+
exponentialBackoff,
|
|
595
|
+
linearBackoff,
|
|
596
|
+
noRetry,
|
|
597
|
+
retryAfterStrategy,
|
|
598
|
+
} from '@providerprotocol/ai';
|
|
530
599
|
|
|
531
600
|
// Exponential: 1s, 2s, 4s...
|
|
532
|
-
|
|
601
|
+
exponentialBackoff({
|
|
533
602
|
maxAttempts: 5,
|
|
534
603
|
baseDelay: 1000,
|
|
535
604
|
maxDelay: 30000,
|
|
@@ -537,20 +606,19 @@ new ExponentialBackoff({
|
|
|
537
606
|
})
|
|
538
607
|
|
|
539
608
|
// Linear: 1s, 2s, 3s...
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// Rate limiting with token bucket
|
|
543
|
-
new TokenBucket({ maxTokens: 10, refillRate: 1 })
|
|
609
|
+
linearBackoff({ maxAttempts: 3, delay: 1000 })
|
|
544
610
|
|
|
545
611
|
// Respect server Retry-After headers
|
|
546
|
-
|
|
612
|
+
retryAfterStrategy({ maxAttempts: 3, fallbackDelay: 5000 })
|
|
547
613
|
|
|
548
614
|
// No retries
|
|
549
|
-
|
|
615
|
+
noRetry()
|
|
550
616
|
```
|
|
551
617
|
|
|
552
618
|
**Retryable Errors:** `RATE_LIMITED`, `NETWORK_ERROR`, `TIMEOUT`, `PROVIDER_ERROR`
|
|
553
619
|
|
|
620
|
+
**Streaming Retry:** Retry strategies work with both `.generate()` and `.stream()`. During streaming, `stream_retry` events are emitted to notify consumers of retry attempts, and middleware can use the `onRetry` hook to reset accumulated state.
|
|
621
|
+
|
|
554
622
|
## Tool Execution Control
|
|
555
623
|
|
|
556
624
|
```typescript
|
|
@@ -719,22 +787,55 @@ Bun.serve({
|
|
|
719
787
|
```typescript
|
|
720
788
|
// Express
|
|
721
789
|
import { express } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
722
|
-
|
|
723
|
-
|
|
790
|
+
|
|
791
|
+
app.post('/api/ai', async (req, res) => {
|
|
792
|
+
const { messages, streamId } = req.body;
|
|
793
|
+
|
|
794
|
+
// Guard: prevent duplicate generations on reconnect
|
|
795
|
+
if (!await adapter.exists(streamId)) {
|
|
796
|
+
const model = llm({
|
|
797
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
798
|
+
middleware: [pubsubMiddleware({ adapter, streamId })],
|
|
799
|
+
});
|
|
800
|
+
model.stream(messages).then(turn => { /* save to DB */ });
|
|
801
|
+
}
|
|
802
|
+
|
|
724
803
|
express.streamSubscriber(streamId, adapter, res);
|
|
725
804
|
});
|
|
726
805
|
|
|
727
806
|
// Fastify
|
|
728
807
|
import { fastify } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
729
|
-
|
|
730
|
-
|
|
808
|
+
|
|
809
|
+
app.post('/api/ai', async (request, reply) => {
|
|
810
|
+
const { messages, streamId } = request.body;
|
|
811
|
+
|
|
812
|
+
// Guard: prevent duplicate generations on reconnect
|
|
813
|
+
if (!await adapter.exists(streamId)) {
|
|
814
|
+
const model = llm({
|
|
815
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
816
|
+
middleware: [pubsubMiddleware({ adapter, streamId })],
|
|
817
|
+
});
|
|
818
|
+
model.stream(messages).then(turn => { /* save to DB */ });
|
|
819
|
+
}
|
|
820
|
+
|
|
731
821
|
return fastify.streamSubscriber(streamId, adapter, reply);
|
|
732
822
|
});
|
|
733
823
|
|
|
734
824
|
// H3/Nuxt
|
|
735
825
|
import { h3 } from '@providerprotocol/ai/middleware/pubsub/server';
|
|
826
|
+
|
|
736
827
|
export default defineEventHandler(async (event) => {
|
|
737
|
-
const { streamId } = await readBody(event);
|
|
828
|
+
const { messages, streamId } = await readBody(event);
|
|
829
|
+
|
|
830
|
+
// Guard: prevent duplicate generations on reconnect
|
|
831
|
+
if (!await adapter.exists(streamId)) {
|
|
832
|
+
const model = llm({
|
|
833
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
834
|
+
middleware: [pubsubMiddleware({ adapter, streamId })],
|
|
835
|
+
});
|
|
836
|
+
model.stream(messages).then(turn => { /* save to DB */ });
|
|
837
|
+
}
|
|
838
|
+
|
|
738
839
|
return h3.streamSubscriber(streamId, adapter, event);
|
|
739
840
|
});
|
|
740
841
|
```
|
|
@@ -750,9 +851,10 @@ const redisAdapter: PubSubAdapter = {
|
|
|
750
851
|
async exists(streamId) { /* check if stream exists */ },
|
|
751
852
|
async append(streamId, event) { /* append event, create lazily */ },
|
|
752
853
|
async getEvents(streamId) { /* return events or [] */ },
|
|
753
|
-
subscribe(streamId, onEvent, onComplete) { /* subscribe to live events */ },
|
|
854
|
+
subscribe(streamId, onEvent, onComplete, onFinalData) { /* subscribe to live events */ },
|
|
754
855
|
publish(streamId, event) { /* broadcast to subscribers */ },
|
|
755
|
-
|
|
856
|
+
setFinalData(streamId, data) { /* store final Turn data */ },
|
|
857
|
+
async remove(streamId) { /* notify onFinalData, onComplete, then delete */ },
|
|
756
858
|
};
|
|
757
859
|
```
|
|
758
860
|
|
|
@@ -774,6 +876,89 @@ const model = llm({
|
|
|
774
876
|
});
|
|
775
877
|
```
|
|
776
878
|
|
|
879
|
+
### Pipeline Middleware (Post-Turn Processing)
|
|
880
|
+
|
|
881
|
+
Run async tasks (image generation, embeddings, slug creation, etc.) after the LLM completes, with progress events streamed to connected clients:
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
import { llm } from '@providerprotocol/ai';
|
|
885
|
+
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
886
|
+
import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
|
|
887
|
+
import { pipelineMiddleware, isPipelineStageEvent } from '@providerprotocol/ai/middleware/pipeline';
|
|
888
|
+
|
|
889
|
+
const adapter = memoryAdapter();
|
|
890
|
+
|
|
891
|
+
const model = llm({
|
|
892
|
+
model: anthropic('claude-sonnet-4-20250514'),
|
|
893
|
+
structure: BlogPostSchema,
|
|
894
|
+
middleware: [
|
|
895
|
+
pubsubMiddleware({ adapter, streamId: postId }),
|
|
896
|
+
pipelineMiddleware<BlogPost>({
|
|
897
|
+
stages: [
|
|
898
|
+
{
|
|
899
|
+
type: 'slug',
|
|
900
|
+
run: (turn, emit) => {
|
|
901
|
+
const slug = turn.data!.title.toLowerCase().replace(/\s+/g, '-');
|
|
902
|
+
(turn as { slug?: string }).slug = slug;
|
|
903
|
+
emit({ slug });
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
type: 'embedding',
|
|
908
|
+
run: async (turn, emit) => {
|
|
909
|
+
await vectorize(turn.data!);
|
|
910
|
+
emit({ embedded: true });
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
parallel: false, // Run stages sequentially (default)
|
|
915
|
+
continueOnError: false, // Stop on first error (default)
|
|
916
|
+
onStageError: ({ stage, error }) => {
|
|
917
|
+
console.error(`Stage ${stage.type} failed:`, error);
|
|
918
|
+
},
|
|
919
|
+
}),
|
|
920
|
+
],
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
// Stages run after streaming completes
|
|
924
|
+
model.stream(prompt).then(turn => {
|
|
925
|
+
const extended = turn as typeof turn & { slug?: string };
|
|
926
|
+
console.log(extended.slug);
|
|
927
|
+
});
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**Consuming Pipeline Events:**
|
|
931
|
+
|
|
932
|
+
```typescript
|
|
933
|
+
for await (const event of model.stream(prompt)) {
|
|
934
|
+
if (isPipelineStageEvent(event)) {
|
|
935
|
+
console.log(event.delta.stage, event.delta.payload);
|
|
936
|
+
// 'slug' { slug: 'my-blog-post' }
|
|
937
|
+
// 'embedding' { embedded: true }
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
**Middleware Order:** Place `pipelineMiddleware` after `pubsubMiddleware` in the array:
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
middleware: [
|
|
946
|
+
pubsubMiddleware({ ... }), // Setup runs first in onStart
|
|
947
|
+
pipelineMiddleware({ ... }), // Events run first in onTurn (reverse order)
|
|
948
|
+
]
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
This ensures pubsub sets up before pipeline stages execute, and pipeline events emit before pubsub cleanup.
|
|
952
|
+
|
|
953
|
+
**Pipeline Configuration:**
|
|
954
|
+
|
|
955
|
+
| Option | Type | Default | Description |
|
|
956
|
+
|--------|------|---------|-------------|
|
|
957
|
+
| `stages` | `PipelineStage[]` | required | Stages to run after turn completion |
|
|
958
|
+
| `parallel` | `boolean` | `false` | Run stages in parallel instead of sequential |
|
|
959
|
+
| `continueOnError` | `boolean` | `false` | Continue running subsequent stages if one fails |
|
|
960
|
+
| `onStageError` | `function` | - | Called when a stage throws an error |
|
|
961
|
+
|
|
777
962
|
## Error Handling
|
|
778
963
|
|
|
779
964
|
All errors are normalized to `UPPError` with consistent error codes:
|
|
@@ -820,17 +1005,16 @@ Build AI API gateways with your own authentication. Users authenticate with your
|
|
|
820
1005
|
### Server (Bun/Deno/Cloudflare Workers)
|
|
821
1006
|
|
|
822
1007
|
```typescript
|
|
823
|
-
import { llm } from '@providerprotocol/ai';
|
|
1008
|
+
import { llm, exponentialBackoff, roundRobinKeys } from '@providerprotocol/ai';
|
|
824
1009
|
import { anthropic } from '@providerprotocol/ai/anthropic';
|
|
825
|
-
import { ExponentialBackoff, RoundRobinKeys } from '@providerprotocol/ai/http';
|
|
826
1010
|
import { parseBody, toJSON, toSSE, toError } from '@providerprotocol/ai/proxy';
|
|
827
1011
|
|
|
828
1012
|
// Server manages AI provider keys - users never see them
|
|
829
1013
|
const claude = llm({
|
|
830
1014
|
model: anthropic('claude-sonnet-4-20250514'),
|
|
831
1015
|
config: {
|
|
832
|
-
apiKey:
|
|
833
|
-
retryStrategy:
|
|
1016
|
+
apiKey: roundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),
|
|
1017
|
+
retryStrategy: exponentialBackoff({ maxAttempts: 3 }),
|
|
834
1018
|
},
|
|
835
1019
|
});
|
|
836
1020
|
|
|
@@ -860,15 +1044,14 @@ Bun.serve({
|
|
|
860
1044
|
Clients authenticate with your platform token. They get automatic retry on network failures to your proxy.
|
|
861
1045
|
|
|
862
1046
|
```typescript
|
|
863
|
-
import { llm } from '@providerprotocol/ai';
|
|
1047
|
+
import { llm, exponentialBackoff } from '@providerprotocol/ai';
|
|
864
1048
|
import { proxy } from '@providerprotocol/ai/proxy';
|
|
865
|
-
import { ExponentialBackoff } from '@providerprotocol/ai/http';
|
|
866
1049
|
|
|
867
1050
|
const claude = llm({
|
|
868
1051
|
model: proxy('https://api.yourplatform.com/ai'),
|
|
869
1052
|
config: {
|
|
870
1053
|
headers: { 'Authorization': 'Bearer user-platform-token' },
|
|
871
|
-
retryStrategy:
|
|
1054
|
+
retryStrategy: exponentialBackoff({ maxAttempts: 3 }),
|
|
872
1055
|
timeout: 30000,
|
|
873
1056
|
},
|
|
874
1057
|
});
|
|
@@ -903,11 +1086,12 @@ app.post('/ai', async (request, reply) => {
|
|
|
903
1086
|
});
|
|
904
1087
|
|
|
905
1088
|
// Nuxt/H3 (server/api/ai.post.ts)
|
|
1089
|
+
import { sendStream } from 'h3';
|
|
906
1090
|
import { h3 as h3Adapter, parseBody } from '@providerprotocol/ai/proxy';
|
|
907
1091
|
export default defineEventHandler(async (event) => {
|
|
908
1092
|
const { messages, system, params } = parseBody(await readBody(event));
|
|
909
1093
|
if (params?.stream) {
|
|
910
|
-
return h3Adapter.
|
|
1094
|
+
return sendStream(event, h3Adapter.createSSEStream(claude.stream(messages, { system })));
|
|
911
1095
|
}
|
|
912
1096
|
return h3Adapter.sendJSON(await claude.generate(messages, { system }), event);
|
|
913
1097
|
});
|
|
@@ -1084,6 +1268,85 @@ const model = llm({
|
|
|
1084
1268
|
|
|
1085
1269
|
**Environment:** `CEREBRAS_API_KEY`
|
|
1086
1270
|
|
|
1271
|
+
## Moonshot
|
|
1272
|
+
|
|
1273
|
+
Kimi K2.5 with 256K context, thinking mode, vision, and server-side builtin tools:
|
|
1274
|
+
|
|
1275
|
+
```typescript
|
|
1276
|
+
import { llm } from '@providerprotocol/ai';
|
|
1277
|
+
import { moonshot, tools } from '@providerprotocol/ai/moonshot';
|
|
1278
|
+
|
|
1279
|
+
const model = llm({
|
|
1280
|
+
model: moonshot('kimi-k2.5'),
|
|
1281
|
+
params: { max_tokens: 1000 },
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
const turn = await model.generate('Hello!');
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
**With thinking mode (default for K2.5):**
|
|
1288
|
+
|
|
1289
|
+
```typescript
|
|
1290
|
+
const model = llm({
|
|
1291
|
+
model: moonshot('kimi-k2.5'),
|
|
1292
|
+
params: {
|
|
1293
|
+
max_tokens: 2000,
|
|
1294
|
+
temperature: 1.0,
|
|
1295
|
+
thinking: { type: 'enabled' },
|
|
1296
|
+
},
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
// Response includes reasoning in turn.response.reasoning
|
|
1300
|
+
const turn = await model.generate('Solve step by step: 2x + 5 = 13');
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
**With instant mode (disabled thinking):**
|
|
1304
|
+
|
|
1305
|
+
```typescript
|
|
1306
|
+
const model = llm({
|
|
1307
|
+
model: moonshot('kimi-k2.5'),
|
|
1308
|
+
params: {
|
|
1309
|
+
temperature: 0.6,
|
|
1310
|
+
thinking: { type: 'disabled' },
|
|
1311
|
+
},
|
|
1312
|
+
});
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
**With builtin tools:**
|
|
1316
|
+
|
|
1317
|
+
```typescript
|
|
1318
|
+
const model = llm({
|
|
1319
|
+
model: moonshot('kimi-k2.5'),
|
|
1320
|
+
params: {
|
|
1321
|
+
tools: [
|
|
1322
|
+
tools.webSearch(),
|
|
1323
|
+
tools.codeRunner(),
|
|
1324
|
+
tools.date(),
|
|
1325
|
+
],
|
|
1326
|
+
},
|
|
1327
|
+
});
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
**Available Builtin Tools:**
|
|
1331
|
+
|
|
1332
|
+
| Tool | Description |
|
|
1333
|
+
|------|-------------|
|
|
1334
|
+
| `tools.webSearch()` | Real-time internet search |
|
|
1335
|
+
| `tools.codeRunner()` | Python code execution with matplotlib/pandas |
|
|
1336
|
+
| `tools.quickjs()` | JavaScript execution via QuickJS engine |
|
|
1337
|
+
| `tools.fetch()` | URL content fetching with markdown extraction |
|
|
1338
|
+
| `tools.convert()` | Unit conversion (length, mass, temperature, currency) |
|
|
1339
|
+
| `tools.date()` | Date/time processing and timezone conversion |
|
|
1340
|
+
| `tools.base64Encode()` | Base64 encoding |
|
|
1341
|
+
| `tools.base64Decode()` | Base64 decoding |
|
|
1342
|
+
| `tools.memory()` | Memory storage and retrieval system |
|
|
1343
|
+
| `tools.rethink()` | Intelligent reasoning/reflection tool |
|
|
1344
|
+
| `tools.randomChoice()` | Random selection with optional weights |
|
|
1345
|
+
|
|
1346
|
+
**Capabilities:** Streaming, tool calling, structured output, thinking mode, image input, video input (experimental).
|
|
1347
|
+
|
|
1348
|
+
**Environment:** `MOONSHOT_API_KEY` or `KIMI_API_KEY`
|
|
1349
|
+
|
|
1087
1350
|
## OpenResponses Provider
|
|
1088
1351
|
|
|
1089
1352
|
Connect to any server implementing the [OpenResponses specification](https://www.openresponses.org):
|
|
@@ -1140,8 +1403,10 @@ Full type safety with no `any` types. All provider parameters are typed:
|
|
|
1140
1403
|
import type {
|
|
1141
1404
|
// Core types
|
|
1142
1405
|
Turn,
|
|
1406
|
+
TurnJSON,
|
|
1143
1407
|
Message,
|
|
1144
1408
|
Tool,
|
|
1409
|
+
ToolInput,
|
|
1145
1410
|
TokenUsage,
|
|
1146
1411
|
|
|
1147
1412
|
// Streaming
|
|
@@ -1174,9 +1439,70 @@ import type {
|
|
|
1174
1439
|
Middleware,
|
|
1175
1440
|
MiddlewareContext,
|
|
1176
1441
|
StreamContext,
|
|
1442
|
+
|
|
1443
|
+
// Schema types (Zod support)
|
|
1444
|
+
Structure,
|
|
1445
|
+
ZodLike,
|
|
1177
1446
|
} from '@providerprotocol/ai';
|
|
1178
1447
|
```
|
|
1179
1448
|
|
|
1449
|
+
**Zod Utilities:**
|
|
1450
|
+
|
|
1451
|
+
```typescript
|
|
1452
|
+
import {
|
|
1453
|
+
isZodSchema,
|
|
1454
|
+
isZodV4,
|
|
1455
|
+
zodToJSONSchema,
|
|
1456
|
+
zodToJSONSchemaSync,
|
|
1457
|
+
resolveStructure,
|
|
1458
|
+
resolveTools,
|
|
1459
|
+
} from '@providerprotocol/ai/utils';
|
|
1460
|
+
|
|
1461
|
+
// Type guard for Zod schemas
|
|
1462
|
+
if (isZodSchema(schema)) {
|
|
1463
|
+
const jsonSchema = zodToJSONSchemaSync(schema);
|
|
1464
|
+
}
|
|
1465
|
+
```
|
|
1466
|
+
|
|
1467
|
+
**Error & ID Utilities:**
|
|
1468
|
+
|
|
1469
|
+
```typescript
|
|
1470
|
+
import {
|
|
1471
|
+
toError,
|
|
1472
|
+
isCancelledError,
|
|
1473
|
+
generateId,
|
|
1474
|
+
generateShortId,
|
|
1475
|
+
} from '@providerprotocol/ai/utils';
|
|
1476
|
+
|
|
1477
|
+
// Convert unknown thrown values to Error instances
|
|
1478
|
+
const error = toError(unknownValue);
|
|
1479
|
+
|
|
1480
|
+
// Check if an error is a cancellation/abort error
|
|
1481
|
+
if (isCancelledError(error)) {
|
|
1482
|
+
console.log('Request was cancelled');
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Generate IDs
|
|
1486
|
+
const uuid = generateId(); // UUID v4: "a1b2c3d4-..."
|
|
1487
|
+
const shortId = generateShortId('req'); // "req_abc123xyz789"
|
|
1488
|
+
```
|
|
1489
|
+
|
|
1490
|
+
**Provider-Specific Types:**
|
|
1491
|
+
|
|
1492
|
+
```typescript
|
|
1493
|
+
// OpenAI
|
|
1494
|
+
import type { OpenAIHeaders, OpenAIImageParams } from '@providerprotocol/ai/openai';
|
|
1495
|
+
|
|
1496
|
+
// Google
|
|
1497
|
+
import type { GoogleImagenParams } from '@providerprotocol/ai/google';
|
|
1498
|
+
|
|
1499
|
+
// Ollama
|
|
1500
|
+
import type { OllamaHeaders } from '@providerprotocol/ai/ollama';
|
|
1501
|
+
|
|
1502
|
+
// OpenRouter
|
|
1503
|
+
import type { OpenRouterProviderOptions } from '@providerprotocol/ai/openrouter';
|
|
1504
|
+
```
|
|
1505
|
+
|
|
1180
1506
|
**Type-Safe Enums:**
|
|
1181
1507
|
|
|
1182
1508
|
```typescript
|
package/dist/anthropic/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../chunk-TUTYMOBL.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveApiKey
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-EY2LLDGY.js";
|
|
10
10
|
import {
|
|
11
11
|
createProvider
|
|
12
12
|
} from "../chunk-JA3UZALR.js";
|
|
@@ -14,18 +14,20 @@ import {
|
|
|
14
14
|
doFetch,
|
|
15
15
|
doStreamFetch,
|
|
16
16
|
normalizeHttpError
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-VQZPADW6.js";
|
|
18
18
|
import {
|
|
19
19
|
StreamEventType,
|
|
20
20
|
objectDelta
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-F5ENANMJ.js";
|
|
22
22
|
import {
|
|
23
23
|
AssistantMessage,
|
|
24
|
-
generateId,
|
|
25
24
|
isAssistantMessage,
|
|
26
25
|
isToolResultMessage,
|
|
27
26
|
isUserMessage
|
|
28
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-6QCV4WXF.js";
|
|
28
|
+
import {
|
|
29
|
+
generateId
|
|
30
|
+
} from "../chunk-U2G5PHHL.js";
|
|
29
31
|
import {
|
|
30
32
|
toError
|
|
31
33
|
} from "../chunk-GIDT7C6I.js";
|