@providerprotocol/ai 0.0.21 → 0.0.23

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 (53) hide show
  1. package/README.md +188 -6
  2. package/dist/anthropic/index.d.ts +1 -1
  3. package/dist/anthropic/index.js +115 -39
  4. package/dist/anthropic/index.js.map +1 -1
  5. package/dist/{chunk-Y3GBJNA2.js → chunk-55X3W2MN.js} +4 -3
  6. package/dist/chunk-55X3W2MN.js.map +1 -0
  7. package/dist/chunk-73IIE3QT.js +120 -0
  8. package/dist/chunk-73IIE3QT.js.map +1 -0
  9. package/dist/{chunk-M4BMM5IB.js → chunk-MF5ETY5O.js} +13 -4
  10. package/dist/chunk-MF5ETY5O.js.map +1 -0
  11. package/dist/{chunk-SKY2JLA7.js → chunk-MKDLXV4O.js} +1 -1
  12. package/dist/chunk-MKDLXV4O.js.map +1 -0
  13. package/dist/{chunk-Z7RBRCRN.js → chunk-NWS5IKNR.js} +37 -11
  14. package/dist/chunk-NWS5IKNR.js.map +1 -0
  15. package/dist/{chunk-EDENPF3E.js → chunk-QNJO7DSD.js} +152 -53
  16. package/dist/chunk-QNJO7DSD.js.map +1 -0
  17. package/dist/{chunk-Z4ILICF5.js → chunk-SBCATNHA.js} +43 -14
  18. package/dist/chunk-SBCATNHA.js.map +1 -0
  19. package/dist/chunk-Z6DKC37J.js +50 -0
  20. package/dist/chunk-Z6DKC37J.js.map +1 -0
  21. package/dist/google/index.d.ts +22 -7
  22. package/dist/google/index.js +286 -85
  23. package/dist/google/index.js.map +1 -1
  24. package/dist/http/index.d.ts +3 -3
  25. package/dist/http/index.js +4 -4
  26. package/dist/index.d.ts +10 -6
  27. package/dist/index.js +331 -204
  28. package/dist/index.js.map +1 -1
  29. package/dist/ollama/index.d.ts +5 -2
  30. package/dist/ollama/index.js +87 -28
  31. package/dist/ollama/index.js.map +1 -1
  32. package/dist/openai/index.d.ts +1 -1
  33. package/dist/openai/index.js +226 -81
  34. package/dist/openai/index.js.map +1 -1
  35. package/dist/openrouter/index.d.ts +1 -1
  36. package/dist/openrouter/index.js +199 -64
  37. package/dist/openrouter/index.js.map +1 -1
  38. package/dist/{provider-DGQHYE6I.d.ts → provider-DR1yins0.d.ts} +159 -53
  39. package/dist/proxy/index.d.ts +2 -2
  40. package/dist/proxy/index.js +178 -17
  41. package/dist/proxy/index.js.map +1 -1
  42. package/dist/{retry-Pcs3hnbu.d.ts → retry-DJiqAslw.d.ts} +11 -2
  43. package/dist/{stream-Di9acos2.d.ts → stream-BuTrqt_j.d.ts} +103 -41
  44. package/dist/xai/index.d.ts +1 -1
  45. package/dist/xai/index.js +189 -75
  46. package/dist/xai/index.js.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/chunk-EDENPF3E.js.map +0 -1
  49. package/dist/chunk-M4BMM5IB.js.map +0 -1
  50. package/dist/chunk-SKY2JLA7.js.map +0 -1
  51. package/dist/chunk-Y3GBJNA2.js.map +0 -1
  52. package/dist/chunk-Z4ILICF5.js.map +0 -1
  53. package/dist/chunk-Z7RBRCRN.js.map +0 -1
package/README.md CHANGED
@@ -44,6 +44,31 @@ for await (const event of stream) {
44
44
  const turn = await stream.turn;
45
45
  ```
46
46
 
47
+ **Stream Control:**
48
+
49
+ ```typescript
50
+ const stream = claude.stream('Write a long story');
51
+
52
+ // Abort the stream at any time
53
+ setTimeout(() => stream.abort(), 5000);
54
+
55
+ for await (const event of stream) {
56
+ // Process events until abort
57
+ }
58
+ ```
59
+
60
+ **Stream Events:**
61
+
62
+ | Event | Description |
63
+ |-------|-------------|
64
+ | `text_delta` | Incremental text output |
65
+ | `reasoning_delta` | Incremental reasoning/thinking output |
66
+ | `tool_call_delta` | Tool call arguments being streamed |
67
+ | `tool_execution_start` | Tool execution has started |
68
+ | `tool_execution_end` | Tool execution has completed |
69
+ | `message_start` / `message_stop` | Message boundaries |
70
+ | `content_block_start` / `content_block_stop` | Content block boundaries |
71
+
47
72
  ### Multi-turn Conversations
48
73
 
49
74
  ```typescript
@@ -104,6 +129,60 @@ const img = await Image.fromPath('./photo.png');
104
129
  const turn = await claude.generate([img, 'What is in this image?']);
105
130
  ```
106
131
 
132
+ ## Anthropic Beta Features
133
+
134
+ Anthropic provides beta features through the `betas` export. Enable them at the model level:
135
+
136
+ ```typescript
137
+ import { anthropic, betas } from '@providerprotocol/ai/anthropic';
138
+ import { llm } from '@providerprotocol/ai';
139
+
140
+ // Native structured outputs with guaranteed JSON schema conformance
141
+ const model = llm({
142
+ model: anthropic('claude-sonnet-4-20250514', {
143
+ betas: [betas.structuredOutputs],
144
+ }),
145
+ structure: {
146
+ type: 'object',
147
+ properties: { answer: { type: 'string' } },
148
+ required: ['answer'],
149
+ },
150
+ });
151
+
152
+ // Extended thinking with interleaved tool calls
153
+ const thinker = llm({
154
+ model: anthropic('claude-sonnet-4-20250514', {
155
+ betas: [betas.interleavedThinking],
156
+ }),
157
+ params: {
158
+ thinking: { type: 'enabled', budget_tokens: 10000 },
159
+ },
160
+ });
161
+ ```
162
+
163
+ **Available Beta Features:**
164
+
165
+ | Beta | Description |
166
+ |------|-------------|
167
+ | `structuredOutputs` | Guaranteed JSON schema conformance for responses |
168
+ | `interleavedThinking` | Claude can think between tool calls |
169
+ | `devFullThinking` | Developer mode for full thinking visibility |
170
+ | `effort` | Control response thoroughness vs efficiency (Opus 4.5) |
171
+ | `computerUse` | Mouse, keyboard, screenshot control (Claude 4) |
172
+ | `codeExecution` | Python/Bash sandbox execution |
173
+ | `tokenEfficientTools` | Up to 70% token reduction for tool calls |
174
+ | `fineGrainedToolStreaming` | Stream tool args without buffering |
175
+ | `output128k` | 128K token output length |
176
+ | `context1m` | 1 million token context window (Sonnet 4) |
177
+ | `promptCaching` | Reduced latency and costs via caching |
178
+ | `extendedCacheTtl` | 1-hour cache TTL (vs 5-minute default) |
179
+ | `advancedToolUse` | Tool Search, Programmatic Tool Calling |
180
+ | `mcpClient` | Connect to remote MCP servers |
181
+ | `filesApi` | Upload and manage files |
182
+ | `pdfs` | PDF document support |
183
+ | `messageBatches` | Async batch processing at 50% cost |
184
+ | `skills` | Agent Skills (PowerPoint, Excel, Word, PDF) |
185
+
107
186
  ## Embeddings
108
187
 
109
188
  ```typescript
@@ -198,6 +277,51 @@ const instance = llm({
198
277
  });
199
278
  ```
200
279
 
280
+ ### System Prompts
281
+
282
+ System prompts can be a simple string or a provider-specific array for advanced features:
283
+
284
+ ```typescript
285
+ // Simple string (all providers)
286
+ const simple = llm({
287
+ model: anthropic('claude-sonnet-4-20250514'),
288
+ system: 'You are a helpful assistant.',
289
+ });
290
+
291
+ // Anthropic cache_control format
292
+ import { anthropic, betas } from '@providerprotocol/ai/anthropic';
293
+
294
+ const cached = llm({
295
+ model: anthropic('claude-sonnet-4-20250514', {
296
+ betas: [betas.promptCaching],
297
+ }),
298
+ system: [
299
+ { type: 'text', text: 'Large context document...', cache_control: { type: 'ephemeral' } },
300
+ { type: 'text', text: 'Instructions...' },
301
+ ],
302
+ });
303
+ ```
304
+
305
+ ### Provider Config Options
306
+
307
+ ```typescript
308
+ interface ProviderConfig {
309
+ apiKey?: string | (() => Promise<string>) | KeyStrategy; // API key, async getter, or strategy
310
+ baseUrl?: string; // Custom API endpoint
311
+ timeout?: number; // Per-attempt timeout (ms)
312
+ retryStrategy?: RetryStrategy; // Retry behavior
313
+ headers?: Record<string, string>; // Custom headers (merged with provider defaults)
314
+ fetch?: typeof fetch; // Custom fetch implementation
315
+ apiVersion?: string; // API version override
316
+ retryAfterMaxSeconds?: number; // Cap for Retry-After header (default: 3600)
317
+ }
318
+ ```
319
+
320
+ **Notes:**
321
+ - `timeout` applies per attempt; total time can exceed this with retries
322
+ - `headers` are merged with model-level headers (explicit config takes precedence)
323
+ - `retryAfterMaxSeconds` prevents honoring excessively long Retry-After values
324
+
201
325
  ### Key Strategies
202
326
 
203
327
  ```typescript
@@ -227,8 +351,13 @@ import {
227
351
  RetryAfterStrategy,
228
352
  } from '@providerprotocol/ai/http';
229
353
 
230
- // Exponential: 1s, 2s, 4s... (default)
231
- new ExponentialBackoff({ maxAttempts: 5, baseDelay: 1000, maxDelay: 30000 })
354
+ // Exponential: 1s, 2s, 4s...
355
+ new ExponentialBackoff({
356
+ maxAttempts: 5,
357
+ baseDelay: 1000,
358
+ maxDelay: 30000,
359
+ jitter: true, // Randomize delays to prevent thundering herd (default: true)
360
+ })
232
361
 
233
362
  // Linear: 1s, 2s, 3s...
234
363
  new LinearBackoff({ maxAttempts: 3, delay: 1000 })
@@ -243,6 +372,8 @@ new RetryAfterStrategy({ maxAttempts: 3, fallbackDelay: 5000 })
243
372
  new NoRetry()
244
373
  ```
245
374
 
375
+ **Retryable Errors:** `RATE_LIMITED`, `NETWORK_ERROR`, `TIMEOUT`, `PROVIDER_ERROR`
376
+
246
377
  ## Tool Execution Control
247
378
 
248
379
  ```typescript
@@ -294,6 +425,12 @@ try {
294
425
  await claude.generate('Hello');
295
426
  } catch (error) {
296
427
  if (error instanceof UPPError) {
428
+ console.log(error.code); // 'RATE_LIMITED'
429
+ console.log(error.provider); // 'anthropic'
430
+ console.log(error.modality); // 'llm'
431
+ console.log(error.statusCode); // 429
432
+ console.log(error.cause); // Original error (if any)
433
+
297
434
  switch (error.code) {
298
435
  case 'RATE_LIMITED':
299
436
  // Wait and retry
@@ -385,7 +522,7 @@ Server adapters for Express, Fastify, and Nuxt/H3:
385
522
 
386
523
  ```typescript
387
524
  // Express
388
- import { express as expressAdapter } from '@providerprotocol/ai/proxy/server';
525
+ import { express as expressAdapter, parseBody } from '@providerprotocol/ai/proxy';
389
526
  app.post('/ai', authMiddleware, async (req, res) => {
390
527
  const { messages, system, params } = parseBody(req.body);
391
528
  if (params?.stream) {
@@ -396,7 +533,7 @@ app.post('/ai', authMiddleware, async (req, res) => {
396
533
  });
397
534
 
398
535
  // Fastify
399
- import { fastify as fastifyAdapter } from '@providerprotocol/ai/proxy/server';
536
+ import { fastify as fastifyAdapter, parseBody } from '@providerprotocol/ai/proxy';
400
537
  app.post('/ai', async (request, reply) => {
401
538
  const { messages, system, params } = parseBody(request.body);
402
539
  if (params?.stream) {
@@ -406,7 +543,7 @@ app.post('/ai', async (request, reply) => {
406
543
  });
407
544
 
408
545
  // Nuxt/H3 (server/api/ai.post.ts)
409
- import { h3 as h3Adapter } from '@providerprotocol/ai/proxy/server';
546
+ import { h3 as h3Adapter, parseBody } from '@providerprotocol/ai/proxy';
410
547
  export default defineEventHandler(async (event) => {
411
548
  const { messages, system, params } = parseBody(await readBody(event));
412
549
  if (params?.stream) {
@@ -441,23 +578,68 @@ xai('grok-3-fast', { api: 'responses' })
441
578
  xai('grok-3-fast', { api: 'messages' })
442
579
  ```
443
580
 
581
+ ## Alternative Import Style
582
+
583
+ Use the `ai` namespace for a grouped import style:
584
+
585
+ ```typescript
586
+ import { ai } from '@providerprotocol/ai';
587
+ import { openai } from '@providerprotocol/ai/openai';
588
+
589
+ const model = ai.llm({ model: openai('gpt-4o') });
590
+ const embedder = ai.embedding({ model: openai('text-embedding-3-small') });
591
+ const dalle = ai.image({ model: openai('dall-e-3') });
592
+ ```
593
+
444
594
  ## TypeScript
445
595
 
446
596
  Full type safety with no `any` types. All provider parameters are typed:
447
597
 
448
598
  ```typescript
449
599
  import type {
600
+ // Core types
450
601
  Turn,
451
602
  Message,
452
603
  Tool,
453
- UPPError,
454
604
  TokenUsage,
605
+
606
+ // Streaming
455
607
  StreamEvent,
608
+ StreamResult,
609
+
610
+ // Modality results
456
611
  EmbeddingResult,
457
612
  ImageResult,
613
+
614
+ // Errors
615
+ UPPError,
616
+ ErrorCode,
617
+
618
+ // Configuration
619
+ ProviderConfig,
620
+ KeyStrategy,
621
+ RetryStrategy,
622
+ LLMCapabilities,
458
623
  } from '@providerprotocol/ai';
459
624
  ```
460
625
 
626
+ ### Custom Providers
627
+
628
+ Build custom providers with `createProvider`:
629
+
630
+ ```typescript
631
+ import { createProvider } from '@providerprotocol/ai';
632
+
633
+ const myProvider = createProvider({
634
+ name: 'my-provider',
635
+ version: '1.0.0',
636
+ handlers: {
637
+ llm: myLLMHandler,
638
+ embedding: myEmbeddingHandler,
639
+ },
640
+ });
641
+ ```
642
+
461
643
  ## License
462
644
 
463
645
  MIT
@@ -1,4 +1,4 @@
1
- import { g as Provider } from '../provider-DGQHYE6I.js';
1
+ import { g as Provider } from '../provider-DR1yins0.js';
2
2
 
3
3
  /**
4
4
  * @fileoverview Anthropic API type definitions.
@@ -1,22 +1,32 @@
1
+ import {
2
+ parseJsonResponse
3
+ } from "../chunk-Z6DKC37J.js";
4
+ import {
5
+ StreamEventType
6
+ } from "../chunk-73IIE3QT.js";
1
7
  import {
2
8
  AssistantMessage,
3
9
  createProvider,
10
+ generateId,
4
11
  isAssistantMessage,
5
12
  isToolResultMessage,
6
13
  isUserMessage
7
- } from "../chunk-M4BMM5IB.js";
14
+ } from "../chunk-MF5ETY5O.js";
8
15
  import {
9
16
  parseSSEStream
10
- } from "../chunk-Z7RBRCRN.js";
17
+ } from "../chunk-NWS5IKNR.js";
11
18
  import {
12
19
  resolveApiKey
13
- } from "../chunk-Y3GBJNA2.js";
20
+ } from "../chunk-55X3W2MN.js";
14
21
  import {
22
+ ErrorCode,
23
+ ModalityType,
15
24
  UPPError,
16
25
  doFetch,
17
26
  doStreamFetch,
18
- normalizeHttpError
19
- } from "../chunk-EDENPF3E.js";
27
+ normalizeHttpError,
28
+ toError
29
+ } from "../chunk-QNJO7DSD.js";
20
30
 
21
31
  // src/providers/anthropic/types.ts
22
32
  var betas = {
@@ -151,8 +161,9 @@ function transformRequest(request, modelId, useNativeStructuredOutput = false) {
151
161
  model: modelId,
152
162
  messages: request.messages.map(transformMessage)
153
163
  };
154
- if (request.system) {
155
- anthropicRequest.system = request.system;
164
+ const normalizedSystem = normalizeSystem(request.system);
165
+ if (normalizedSystem !== void 0) {
166
+ anthropicRequest.system = normalizedSystem;
156
167
  }
157
168
  const allTools = [];
158
169
  if (request.tools && request.tools.length > 0) {
@@ -184,7 +195,8 @@ function transformRequest(request, modelId, useNativeStructuredOutput = false) {
184
195
  input_schema: {
185
196
  type: "object",
186
197
  properties: request.structure.properties,
187
- required: request.structure.required
198
+ required: request.structure.required,
199
+ ...request.structure.additionalProperties !== void 0 ? { additionalProperties: request.structure.additionalProperties } : {}
188
200
  }
189
201
  };
190
202
  anthropicRequest.tools = [...anthropicRequest.tools ?? [], structuredTool];
@@ -193,6 +205,57 @@ function transformRequest(request, modelId, useNativeStructuredOutput = false) {
193
205
  }
194
206
  return anthropicRequest;
195
207
  }
208
+ function normalizeSystem(system) {
209
+ if (system === void 0 || system === null) return void 0;
210
+ if (typeof system === "string") return system;
211
+ if (!Array.isArray(system)) {
212
+ throw new UPPError(
213
+ "System prompt must be a string or an array of text blocks",
214
+ ErrorCode.InvalidRequest,
215
+ "anthropic",
216
+ ModalityType.LLM
217
+ );
218
+ }
219
+ const blocks = [];
220
+ for (const block of system) {
221
+ if (!block || typeof block !== "object") {
222
+ throw new UPPError(
223
+ 'System prompt array must contain objects with type "text"',
224
+ ErrorCode.InvalidRequest,
225
+ "anthropic",
226
+ ModalityType.LLM
227
+ );
228
+ }
229
+ const candidate = block;
230
+ if (candidate.type !== "text" || typeof candidate.text !== "string") {
231
+ throw new UPPError(
232
+ 'Anthropic system blocks must be of type "text" with a string text field',
233
+ ErrorCode.InvalidRequest,
234
+ "anthropic",
235
+ ModalityType.LLM
236
+ );
237
+ }
238
+ if (candidate.cache_control !== void 0 && !isValidCacheControl(candidate.cache_control)) {
239
+ throw new UPPError(
240
+ "Invalid cache_control for Anthropic system prompt",
241
+ ErrorCode.InvalidRequest,
242
+ "anthropic",
243
+ ModalityType.LLM
244
+ );
245
+ }
246
+ blocks.push(block);
247
+ }
248
+ return blocks.length > 0 ? blocks : void 0;
249
+ }
250
+ function isValidCacheControl(value) {
251
+ if (!value || typeof value !== "object") return false;
252
+ const candidate = value;
253
+ if (candidate.type !== "ephemeral") return false;
254
+ if (candidate.ttl !== void 0 && candidate.ttl !== "5m" && candidate.ttl !== "1h") {
255
+ return false;
256
+ }
257
+ return true;
258
+ }
196
259
  function filterValidContent(content) {
197
260
  return content.filter((c) => c && typeof c.type === "string");
198
261
  }
@@ -312,7 +375,8 @@ function transformTool(tool) {
312
375
  input_schema: {
313
376
  type: "object",
314
377
  properties: tool.parameters.properties,
315
- required: tool.parameters.required
378
+ required: tool.parameters.required,
379
+ ...tool.parameters.additionalProperties !== void 0 ? { additionalProperties: tool.parameters.additionalProperties } : {}
316
380
  },
317
381
  ...cacheControl ? { cache_control: cacheControl } : {}
318
382
  };
@@ -396,6 +460,7 @@ function createStreamState() {
396
460
  };
397
461
  }
398
462
  function transformStreamEvent(event, state) {
463
+ const events = [];
399
464
  switch (event.type) {
400
465
  case "message_start":
401
466
  state.messageId = event.message.id;
@@ -403,7 +468,8 @@ function transformStreamEvent(event, state) {
403
468
  state.inputTokens = event.message.usage.input_tokens;
404
469
  state.cacheReadTokens = event.message.usage.cache_read_input_tokens ?? 0;
405
470
  state.cacheWriteTokens = event.message.usage.cache_creation_input_tokens ?? 0;
406
- return { type: "message_start", index: 0, delta: {} };
471
+ events.push({ type: StreamEventType.MessageStart, index: 0, delta: {} });
472
+ break;
407
473
  case "content_block_start":
408
474
  if (event.content_block.type === "text") {
409
475
  state.content[event.index] = { type: "text", text: "" };
@@ -436,56 +502,63 @@ function transformStreamEvent(event, state) {
436
502
  fileContent: resultBlock.content?.content ?? ""
437
503
  };
438
504
  }
439
- return { type: "content_block_start", index: event.index, delta: {} };
505
+ events.push({ type: StreamEventType.ContentBlockStart, index: event.index, delta: {} });
506
+ break;
440
507
  case "content_block_delta": {
441
508
  const delta = event.delta;
442
509
  if (delta.type === "text_delta") {
443
510
  if (state.content[event.index]) {
444
511
  state.content[event.index].text = (state.content[event.index].text ?? "") + delta.text;
445
512
  }
446
- return {
447
- type: "text_delta",
513
+ events.push({
514
+ type: StreamEventType.TextDelta,
448
515
  index: event.index,
449
516
  delta: { text: delta.text }
450
- };
517
+ });
518
+ break;
451
519
  }
452
520
  if (delta.type === "input_json_delta") {
453
521
  if (state.content[event.index]) {
454
522
  state.content[event.index].input = (state.content[event.index].input ?? "") + delta.partial_json;
455
523
  }
456
- return {
457
- type: "tool_call_delta",
524
+ events.push({
525
+ type: StreamEventType.ToolCallDelta,
458
526
  index: event.index,
459
527
  delta: {
460
528
  argumentsJson: delta.partial_json,
461
529
  toolCallId: state.content[event.index]?.id,
462
530
  toolName: state.content[event.index]?.name
463
531
  }
464
- };
532
+ });
533
+ break;
465
534
  }
466
535
  if (delta.type === "thinking_delta") {
467
- return {
468
- type: "reasoning_delta",
536
+ events.push({
537
+ type: StreamEventType.ReasoningDelta,
469
538
  index: event.index,
470
539
  delta: { text: delta.thinking }
471
- };
540
+ });
541
+ break;
472
542
  }
473
- return null;
543
+ break;
474
544
  }
475
545
  case "content_block_stop":
476
- return { type: "content_block_stop", index: event.index, delta: {} };
546
+ events.push({ type: StreamEventType.ContentBlockStop, index: event.index, delta: {} });
547
+ break;
477
548
  case "message_delta":
478
549
  state.stopReason = event.delta.stop_reason;
479
550
  state.outputTokens = event.usage.output_tokens;
480
- return null;
551
+ return [];
481
552
  case "message_stop":
482
- return { type: "message_stop", index: 0, delta: {} };
553
+ events.push({ type: StreamEventType.MessageStop, index: 0, delta: {} });
554
+ break;
483
555
  case "ping":
484
556
  case "error":
485
- return null;
557
+ return [];
486
558
  default:
487
- return null;
559
+ break;
488
560
  }
561
+ return events;
489
562
  }
490
563
  function buildResponseFromState(state, useNativeStructuredOutput = false) {
491
564
  const textContent = [];
@@ -529,11 +602,12 @@ ${block.fileContent}\`\`\`
529
602
  ` });
530
603
  }
531
604
  }
605
+ const messageId = state.messageId || generateId();
532
606
  const message = new AssistantMessage(
533
607
  textContent,
534
608
  toolCalls.length > 0 ? toolCalls : void 0,
535
609
  {
536
- id: state.messageId,
610
+ id: messageId,
537
611
  metadata: {
538
612
  anthropic: {
539
613
  stop_reason: state.stopReason,
@@ -588,9 +662,9 @@ function createLLMHandler() {
588
662
  if (!providerRef) {
589
663
  throw new UPPError(
590
664
  "Provider reference not set. Handler must be used with createProvider().",
591
- "INVALID_REQUEST",
665
+ ErrorCode.InvalidRequest,
592
666
  "anthropic",
593
- "llm"
667
+ ModalityType.LLM
594
668
  );
595
669
  }
596
670
  const model = {
@@ -636,7 +710,7 @@ function createLLMHandler() {
636
710
  "anthropic",
637
711
  "llm"
638
712
  );
639
- const data = await response.json();
713
+ const data = await parseJsonResponse(response, "anthropic", "llm");
640
714
  return transformResponse(data, useNativeStructuredOutput);
641
715
  },
642
716
  stream(request) {
@@ -665,7 +739,8 @@ function createLLMHandler() {
665
739
  const headers = {
666
740
  "Content-Type": "application/json",
667
741
  "x-api-key": apiKey,
668
- "anthropic-version": request.config.apiVersion ?? ANTHROPIC_VERSION
742
+ "anthropic-version": request.config.apiVersion ?? ANTHROPIC_VERSION,
743
+ Accept: "text/event-stream"
669
744
  };
670
745
  if (request.config.headers) {
671
746
  for (const [key, value] of Object.entries(request.config.headers)) {
@@ -694,9 +769,9 @@ function createLLMHandler() {
694
769
  if (!response.body) {
695
770
  const error = new UPPError(
696
771
  "No response body for streaming request",
697
- "PROVIDER_ERROR",
772
+ ErrorCode.ProviderError,
698
773
  "anthropic",
699
- "llm"
774
+ ModalityType.LLM
700
775
  );
701
776
  responseReject(error);
702
777
  throw error;
@@ -707,23 +782,24 @@ function createLLMHandler() {
707
782
  if (event.type === "error") {
708
783
  const error = new UPPError(
709
784
  event.error.message,
710
- "PROVIDER_ERROR",
785
+ ErrorCode.ProviderError,
711
786
  "anthropic",
712
- "llm"
787
+ ModalityType.LLM
713
788
  );
714
789
  responseReject(error);
715
790
  throw error;
716
791
  }
717
- const uppEvent = transformStreamEvent(event, state);
718
- if (uppEvent) {
792
+ const uppEvents = transformStreamEvent(event, state);
793
+ for (const uppEvent of uppEvents) {
719
794
  yield uppEvent;
720
795
  }
721
796
  }
722
797
  }
723
798
  responseResolve(buildResponseFromState(state, useNativeStructuredOutput));
724
799
  } catch (error) {
725
- responseReject(error);
726
- throw error;
800
+ const err = toError(error);
801
+ responseReject(err);
802
+ throw err;
727
803
  }
728
804
  }
729
805
  return {