@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.
Files changed (136) hide show
  1. package/README.md +363 -37
  2. package/dist/anthropic/index.d.ts +3 -2
  3. package/dist/anthropic/index.js +7 -5
  4. package/dist/anthropic/index.js.map +1 -1
  5. package/dist/cerebras/index.d.ts +3 -2
  6. package/dist/cerebras/index.js +7 -5
  7. package/dist/cerebras/index.js.map +1 -1
  8. package/dist/chunk-3Q5VELKG.js +124 -0
  9. package/dist/chunk-3Q5VELKG.js.map +1 -0
  10. package/dist/{chunk-WU4U6IHF.js → chunk-6QCV4WXF.js} +4 -13
  11. package/dist/chunk-6QCV4WXF.js.map +1 -0
  12. package/dist/{chunk-LTEMH3CI.js → chunk-AC3VHSZJ.js} +6 -4
  13. package/dist/{chunk-LTEMH3CI.js.map → chunk-AC3VHSZJ.js.map} +1 -1
  14. package/dist/{chunk-YQLR3XOA.js → chunk-BIBMNP7Y.js} +1 -75
  15. package/dist/chunk-BIBMNP7Y.js.map +1 -0
  16. package/dist/{chunk-CRP6Y7NF.js → chunk-CWGTARDE.js} +2 -2
  17. package/dist/{chunk-ZRVNAET3.js → chunk-DI47UY2H.js} +6 -3
  18. package/dist/chunk-DI47UY2H.js.map +1 -0
  19. package/dist/{chunk-7GTWHZY2.js → chunk-EHR3LIPS.js} +5 -3
  20. package/dist/{chunk-7GTWHZY2.js.map → chunk-EHR3LIPS.js.map} +1 -1
  21. package/dist/chunk-EY2LLDGY.js +94 -0
  22. package/dist/chunk-EY2LLDGY.js.map +1 -0
  23. package/dist/{chunk-MJI74VEJ.js → chunk-F5ENANMJ.js} +18 -2
  24. package/dist/chunk-F5ENANMJ.js.map +1 -0
  25. package/dist/chunk-IKJH5ZSJ.js +1 -0
  26. package/dist/chunk-IKJH5ZSJ.js.map +1 -0
  27. package/dist/{chunk-4RX4VQCB.js → chunk-KBI45OXI.js} +2 -2
  28. package/dist/{chunk-5IWHCXKN.js → chunk-KVUOTFYZ.js} +2 -2
  29. package/dist/{chunk-EPB3GQNL.js → chunk-L6QWKFGE.js} +13 -3
  30. package/dist/chunk-L6QWKFGE.js.map +1 -0
  31. package/dist/{chunk-BDXH6NQS.js → chunk-N4LAFGLX.js} +7 -7
  32. package/dist/{chunk-ZKNPQBIE.js → chunk-R3T2IYOU.js} +5 -3
  33. package/dist/{chunk-ZKNPQBIE.js.map → chunk-R3T2IYOU.js.map} +1 -1
  34. package/dist/chunk-U2G5PHHL.js +25 -0
  35. package/dist/chunk-U2G5PHHL.js.map +1 -0
  36. package/dist/{chunk-SBGZJVTJ.js → chunk-VQZPADW6.js} +100 -33
  37. package/dist/chunk-VQZPADW6.js.map +1 -0
  38. package/dist/{chunk-FYSZFIZS.js → chunk-XTWBAL42.js} +5 -3
  39. package/dist/{chunk-FYSZFIZS.js.map → chunk-XTWBAL42.js.map} +1 -1
  40. package/dist/{chunk-2YXFLRQ6.js → chunk-ZMESKGUY.js} +2 -2
  41. package/dist/chunk-ZSZVWLGE.js +83 -0
  42. package/dist/chunk-ZSZVWLGE.js.map +1 -0
  43. package/dist/{embedding-CwZ1ZNWv.d.ts → embedding-ts1npsDg.d.ts} +1 -1
  44. package/dist/google/index.d.ts +38 -3
  45. package/dist/google/index.js +5 -4
  46. package/dist/google/index.js.map +1 -1
  47. package/dist/groq/index.d.ts +3 -2
  48. package/dist/groq/index.js +7 -5
  49. package/dist/groq/index.js.map +1 -1
  50. package/dist/http/index.d.ts +5 -4
  51. package/dist/http/index.js +19 -22
  52. package/dist/{image-stream-CeQHtjxS.d.ts → image-stream-BPml2YZZ.d.ts} +1 -1
  53. package/dist/index.d.ts +8 -7
  54. package/dist/index.js +316 -113
  55. package/dist/index.js.map +1 -1
  56. package/dist/{llm-DS_-l71X.d.ts → llm-BWLaTzzY.d.ts} +89 -36
  57. package/dist/middleware/logging/index.d.ts +3 -2
  58. package/dist/middleware/logging/index.js +3 -0
  59. package/dist/middleware/logging/index.js.map +1 -1
  60. package/dist/middleware/parsed-object/index.d.ts +3 -2
  61. package/dist/middleware/parsed-object/index.js +5 -1
  62. package/dist/middleware/parsed-object/index.js.map +1 -1
  63. package/dist/middleware/persistence/index.d.ts +3 -2
  64. package/dist/middleware/persistence/index.js +3 -2
  65. package/dist/middleware/persistence/index.js.map +1 -1
  66. package/dist/middleware/pipeline/index.d.ts +195 -0
  67. package/dist/middleware/pipeline/index.js +61 -0
  68. package/dist/middleware/pipeline/index.js.map +1 -0
  69. package/dist/middleware/pubsub/index.d.ts +13 -10
  70. package/dist/middleware/pubsub/index.js +78 -6
  71. package/dist/middleware/pubsub/index.js.map +1 -1
  72. package/dist/middleware/pubsub/server/express/index.d.ts +3 -2
  73. package/dist/middleware/pubsub/server/express/index.js +2 -2
  74. package/dist/middleware/pubsub/server/fastify/index.d.ts +3 -2
  75. package/dist/middleware/pubsub/server/fastify/index.js +2 -2
  76. package/dist/middleware/pubsub/server/h3/index.d.ts +3 -2
  77. package/dist/middleware/pubsub/server/h3/index.js +2 -2
  78. package/dist/middleware/pubsub/server/index.d.ts +50 -8
  79. package/dist/middleware/pubsub/server/index.js +5 -5
  80. package/dist/middleware/pubsub/server/index.js.map +1 -1
  81. package/dist/middleware/pubsub/server/webapi/index.d.ts +3 -2
  82. package/dist/middleware/pubsub/server/webapi/index.js +2 -2
  83. package/dist/moonshot/index.d.ts +3 -2
  84. package/dist/moonshot/index.js +7 -5
  85. package/dist/moonshot/index.js.map +1 -1
  86. package/dist/ollama/index.d.ts +24 -3
  87. package/dist/ollama/index.js +5 -4
  88. package/dist/ollama/index.js.map +1 -1
  89. package/dist/openai/index.d.ts +65 -3
  90. package/dist/openai/index.js +7 -5
  91. package/dist/openai/index.js.map +1 -1
  92. package/dist/openrouter/index.d.ts +4 -3
  93. package/dist/openrouter/index.js +7 -5
  94. package/dist/openrouter/index.js.map +1 -1
  95. package/dist/proxy/index.d.ts +5 -4
  96. package/dist/proxy/index.js +20 -17
  97. package/dist/proxy/index.js.map +1 -1
  98. package/dist/proxy/server/express/index.d.ts +8 -8
  99. package/dist/proxy/server/express/index.js +5 -3
  100. package/dist/proxy/server/fastify/index.d.ts +8 -8
  101. package/dist/proxy/server/fastify/index.js +5 -3
  102. package/dist/proxy/server/h3/index.d.ts +22 -21
  103. package/dist/proxy/server/h3/index.js +5 -3
  104. package/dist/proxy/server/index.d.ts +5 -4
  105. package/dist/proxy/server/index.js +15 -13
  106. package/dist/proxy/server/webapi/index.d.ts +8 -8
  107. package/dist/proxy/server/webapi/index.js +5 -3
  108. package/dist/responses/index.d.ts +3 -2
  109. package/dist/responses/index.js +7 -5
  110. package/dist/responses/index.js.map +1 -1
  111. package/dist/retry-DVfdPLIB.d.ts +322 -0
  112. package/dist/{stream-sXhBtWjl.d.ts → stream-bBd_4Ipu.d.ts} +29 -419
  113. package/dist/tool-BmAfKNBq.d.ts +507 -0
  114. package/dist/{types-Cr4F0tVy.d.ts → types-nTwlpyJE.d.ts} +28 -3
  115. package/dist/utils/index.d.ts +129 -1
  116. package/dist/utils/index.js +28 -1
  117. package/dist/xai/index.d.ts +3 -2
  118. package/dist/xai/index.js +7 -5
  119. package/dist/xai/index.js.map +1 -1
  120. package/package.json +20 -3
  121. package/dist/chunk-ARVM24K2.js +0 -128
  122. package/dist/chunk-ARVM24K2.js.map +0 -1
  123. package/dist/chunk-EPB3GQNL.js.map +0 -1
  124. package/dist/chunk-MJI74VEJ.js.map +0 -1
  125. package/dist/chunk-SBGZJVTJ.js.map +0 -1
  126. package/dist/chunk-WU4U6IHF.js.map +0 -1
  127. package/dist/chunk-Y5H7C5J4.js +0 -263
  128. package/dist/chunk-Y5H7C5J4.js.map +0 -1
  129. package/dist/chunk-YQLR3XOA.js.map +0 -1
  130. package/dist/chunk-ZRVNAET3.js.map +0 -1
  131. package/dist/retry-CgoBNa51.d.ts +0 -531
  132. /package/dist/{chunk-CRP6Y7NF.js.map → chunk-CWGTARDE.js.map} +0 -0
  133. /package/dist/{chunk-4RX4VQCB.js.map → chunk-KBI45OXI.js.map} +0 -0
  134. /package/dist/{chunk-5IWHCXKN.js.map → chunk-KVUOTFYZ.js.map} +0 -0
  135. /package/dist/{chunk-BDXH6NQS.js.map → chunk-N4LAFGLX.js.map} +0 -0
  136. /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: new RoundRobinKeys(['sk-key1', 'sk-key2']),
515
+ apiKey: roundRobinKeys(['sk-key1', 'sk-key2']),
446
516
  timeout: 30000,
447
- retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
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 { RoundRobinKeys, WeightedKeys, DynamicKey } from '@providerprotocol/ai/http';
575
+ import { roundRobinKeys, weightedKeys, dynamicKey } from '@providerprotocol/ai/http';
506
576
 
507
577
  // Cycle through keys evenly
508
- new RoundRobinKeys(['sk-1', 'sk-2', 'sk-3'])
578
+ roundRobinKeys(['sk-1', 'sk-2', 'sk-3'])
509
579
 
510
580
  // Weighted selection (70% key1, 30% key2)
511
- new WeightedKeys([
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
- new DynamicKey(async () => fetchKeyFromVault())
587
+ dynamicKey(async () => fetchKeyFromVault())
518
588
  ```
519
589
 
520
590
  ### Retry Strategies
521
591
 
522
592
  ```typescript
523
593
  import {
524
- ExponentialBackoff,
525
- LinearBackoff,
526
- NoRetry,
527
- TokenBucket,
528
- RetryAfterStrategy,
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
- new ExponentialBackoff({
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
- new LinearBackoff({ maxAttempts: 3, delay: 1000 })
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
- new RetryAfterStrategy({ maxAttempts: 3, fallbackDelay: 5000 })
612
+ retryAfterStrategy({ maxAttempts: 3, fallbackDelay: 5000 })
547
613
 
548
614
  // No retries
549
- new NoRetry()
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
- app.post('/api/ai/reconnect', (req, res) => {
723
- const { streamId } = req.body;
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
- app.post('/api/ai/reconnect', (request, reply) => {
730
- const { streamId } = request.body;
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
- async remove(streamId) { /* notify onComplete then delete */ },
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: new RoundRobinKeys([process.env.ANTHROPIC_KEY_1!, process.env.ANTHROPIC_KEY_2!]),
833
- retryStrategy: new ExponentialBackoff({ maxAttempts: 3 }),
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: new ExponentialBackoff({ maxAttempts: 3 }),
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.streamSSE(claude.stream(messages, { system }), event);
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
@@ -1,5 +1,6 @@
1
- import { e as Provider } from '../llm-DS_-l71X.js';
2
- import '../stream-sXhBtWjl.js';
1
+ import { e as Provider } from '../llm-BWLaTzzY.js';
2
+ import '../stream-bBd_4Ipu.js';
3
+ import '../tool-BmAfKNBq.js';
3
4
 
4
5
  /**
5
6
  * @fileoverview Anthropic API type definitions.
@@ -6,7 +6,7 @@ import {
6
6
  } from "../chunk-TUTYMOBL.js";
7
7
  import {
8
8
  resolveApiKey
9
- } from "../chunk-ARVM24K2.js";
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-SBGZJVTJ.js";
17
+ } from "../chunk-VQZPADW6.js";
18
18
  import {
19
19
  StreamEventType,
20
20
  objectDelta
21
- } from "../chunk-MJI74VEJ.js";
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-WU4U6IHF.js";
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";