@providerprotocol/agents 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +333 -6
  2. package/dist/checkpoint/index.d.ts +43 -0
  3. package/dist/checkpoint/index.js +64 -0
  4. package/dist/checkpoint/index.js.map +1 -0
  5. package/{src/execution/loop.ts → dist/chunk-4ESYN66B.js} +54 -162
  6. package/dist/chunk-4ESYN66B.js.map +1 -0
  7. package/dist/chunk-EKRXMSDX.js +8 -0
  8. package/dist/chunk-EKRXMSDX.js.map +1 -0
  9. package/dist/chunk-PHI5ULBV.js +427 -0
  10. package/dist/chunk-PHI5ULBV.js.map +1 -0
  11. package/dist/execution/index.d.ts +105 -0
  12. package/dist/execution/index.js +679 -0
  13. package/dist/execution/index.js.map +1 -0
  14. package/dist/index-qsPwbY86.d.ts +65 -0
  15. package/dist/index.d.ts +101 -0
  16. package/dist/index.js +218 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/middleware/index.d.ts +23 -0
  19. package/dist/middleware/index.js +82 -0
  20. package/dist/middleware/index.js.map +1 -0
  21. package/dist/thread-tree/index.d.ts +115 -0
  22. package/dist/thread-tree/index.js +4 -0
  23. package/dist/thread-tree/index.js.map +1 -0
  24. package/dist/types-2Vsthzyu.d.ts +163 -0
  25. package/dist/types-BhX9uD_d.d.ts +91 -0
  26. package/dist/types-DR02gtFv.d.ts +270 -0
  27. package/dist/types-NGQMdnaD.d.ts +65 -0
  28. package/package.json +40 -8
  29. package/.claude/settings.local.json +0 -27
  30. package/AGENTS.md +0 -681
  31. package/CLAUDE.md +0 -681
  32. package/bun.lock +0 -472
  33. package/eslint.config.js +0 -75
  34. package/index.ts +0 -1
  35. package/llms.md +0 -796
  36. package/specs/UAP-1.0.md +0 -2355
  37. package/src/agent/index.ts +0 -384
  38. package/src/agent/types.ts +0 -91
  39. package/src/checkpoint/file.ts +0 -126
  40. package/src/checkpoint/index.ts +0 -40
  41. package/src/checkpoint/types.ts +0 -95
  42. package/src/execution/index.ts +0 -37
  43. package/src/execution/plan.ts +0 -497
  44. package/src/execution/react.ts +0 -340
  45. package/src/execution/tool-ordering.ts +0 -186
  46. package/src/execution/types.ts +0 -315
  47. package/src/index.ts +0 -80
  48. package/src/middleware/index.ts +0 -7
  49. package/src/middleware/logging.ts +0 -123
  50. package/src/middleware/types.ts +0 -69
  51. package/src/state/index.ts +0 -301
  52. package/src/state/types.ts +0 -173
  53. package/src/thread-tree/index.ts +0 -249
  54. package/src/thread-tree/types.ts +0 -29
  55. package/src/utils/uuid.ts +0 -7
  56. package/tests/live/agent-anthropic.test.ts +0 -288
  57. package/tests/live/agent-strategy-hooks.test.ts +0 -268
  58. package/tests/live/checkpoint.test.ts +0 -243
  59. package/tests/live/execution-strategies.test.ts +0 -255
  60. package/tests/live/plan-strategy.test.ts +0 -160
  61. package/tests/live/subagent-events.live.test.ts +0 -249
  62. package/tests/live/thread-tree.test.ts +0 -186
  63. package/tests/unit/agent.test.ts +0 -703
  64. package/tests/unit/checkpoint.test.ts +0 -232
  65. package/tests/unit/execution/equivalence.test.ts +0 -402
  66. package/tests/unit/execution/loop.test.ts +0 -437
  67. package/tests/unit/execution/plan.test.ts +0 -590
  68. package/tests/unit/execution/react.test.ts +0 -604
  69. package/tests/unit/execution/subagent-events.test.ts +0 -235
  70. package/tests/unit/execution/tool-ordering.test.ts +0 -310
  71. package/tests/unit/middleware/logging.test.ts +0 -276
  72. package/tests/unit/state.test.ts +0 -573
  73. package/tests/unit/thread-tree.test.ts +0 -249
  74. package/tsconfig.json +0 -29
package/AGENTS.md DELETED
@@ -1,681 +0,0 @@
1
- 1. **100% TypeScript compliant** – All code must be fully TypeScript compliant with no use of `any`, implicit types, or unchecked assumptions.
2
- 2. **No complex abstractions around library data types** – Use library APIs as first-class application code; never truncate, shrink, or morph library data. Maintain full data stacks to accurately and transparently represent upstream libraries.
3
- 3. **High coding standards** – Follow modern best practices, prioritize readability, maintainability, and correctness, and write code that is clear in intent and behavior.
4
- 4. **AirBnB style compliance** – All code must strictly follow AirBnB’s TypeScript/JavaScript style guidelines and conventions.
5
- 5. **Lint cleanliness required** – Always ensure `bun lint` reports **zero errors** before code is considered complete or acceptable.
6
- 6. **Type-check cleanliness required** – Always ensure `bun typecheck` reports **zero errors** to guarantee full type safety and correctness.
7
- 7. **Electron-first environment** – This is a desktop **Electron** application. Web technologies are used for UI and runtime only; standard web application or hosted API security assumptions do not apply unless explicitly requested.
8
- 8. **No re-exports of library types or APIs** – Do **not** create re-exports, wrappers, or barrel files for third-party library types or functions (e.g., `export type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'`). Always import directly from the source library in every file that uses them. However, **do import and use library types** for proper typing—avoid `unknown` or `any` when a concrete type exists. The rule prohibits re-exporting, not importing for local use.
9
- 9. **Never disable tooling rules** – Do **not** suppress, disable, or bypass linting, TypeScript, or language rules (e.g. `eslint-disable`, `@ts-ignore`, `@ts-expect-error`, rule overrides, or inline suppressions). Code must be written to comply with the active ruleset rather than weakening or working around it.
10
- 10. **Shared UI components for reuse** – Create reusable components for shared styling and shared views (e.g., consistently styled dropdowns, standardized buttons, or complex UI elements used in multiple places like an AI input bar). Prefer composition and clear props so shared UI stays consistent, maintainable, and avoids duplicated styling/logic across the app.
11
- 11. **Small, maintainable files** – Aim to keep files small and easy to reason about. When a file grows overly large (≈300 lines of code, excluding whitespace), consider refactoring it into cohesive, maintainable modules **when it is realistic and beneficial**. Refactoring should improve clarity and structure—not split code arbitrarily or create artificial indirection.
12
- 12. **Use icon libraries only** – Use established icon libraries for all icons. Do **not** write or embed custom inline SVGs. Rely on library-provided icons to ensure consistency, readability, and easier maintenance across the codebase.
13
- 13. **Comprehensive, intentional testing discipline** – All features and changes must include both **static unit tests** and **live API/integration tests** where applicable. Every code change must explicitly consider its testing impact and ensure **all unit and live tests pass fully**. Tests should be written deliberately: validate real behavior, edge cases, and regressions while avoiding unnecessary duplication, over-mocking, or low-value assertions that create test bloat without increasing confidence.
14
-
15
- `.env` contains all AI provider keys.
16
-
17
- >Persist only atomic values; do not persist derived attributes. Calculated fields such as duration or netCost should be computed at runtime to prevent data inconsistency
18
-
19
- Default to using Bun instead of Node.js.
20
-
21
- - Use `bun <file>` instead of `node <file>` or `ts-node <file>`
22
- - Use `bun test` instead of `jest` or `vitest`
23
- - Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
24
- - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
25
- - Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
26
- - Use `bunx <package> <command>` instead of `npx <package> <command>`
27
- - Bun automatically loads .env, so don't use dotenv.
28
-
29
- ## APIs
30
-
31
- - `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
32
- - `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
33
- - `Bun.redis` for Redis. Don't use `ioredis`.
34
- - `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
35
- - `WebSocket` is built-in. Don't use `ws`.
36
- - Prefer `Bun.file` over `node:fs`'s readFile/writeFile
37
- - Bun.$`ls` instead of execa.
38
-
39
- ## Testing
40
-
41
- Use `bun test` to run tests.
42
-
43
- ```ts#index.test.ts
44
- import { test, expect } from "bun:test";
45
-
46
- test("hello world", () => {
47
- expect(1).toBe(1);
48
- });
49
- ```
50
-
51
- ## Frontend
52
-
53
- Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
54
-
55
- Server:
56
-
57
- ```ts#index.ts
58
- import index from "./index.html"
59
-
60
- Bun.serve({
61
- routes: {
62
- "/": index,
63
- "/api/users/:id": {
64
- GET: (req) => {
65
- return new Response(JSON.stringify({ id: req.params.id }));
66
- },
67
- },
68
- },
69
- // optional websocket support
70
- websocket: {
71
- open: (ws) => {
72
- ws.send("Hello, world!");
73
- },
74
- message: (ws, message) => {
75
- ws.send(message);
76
- },
77
- close: (ws) => {
78
- // handle close
79
- }
80
- },
81
- development: {
82
- hmr: true,
83
- console: true,
84
- }
85
- })
86
- ```
87
-
88
- HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
89
-
90
- ```html#index.html
91
- <html>
92
- <body>
93
- <h1>Hello, world!</h1>
94
- <script type="module" src="./frontend.tsx"></script>
95
- </body>
96
- </html>
97
- ```
98
-
99
- With the following `frontend.tsx`:
100
-
101
- ```tsx#frontend.tsx
102
- import React from "react";
103
- import { createRoot } from "react-dom/client";
104
-
105
- // import .css files directly and it works
106
- import './index.css';
107
-
108
- const root = createRoot(document.body);
109
-
110
- export default function Frontend() {
111
- return <h1>Hello, world!</h1>;
112
- }
113
-
114
- root.render(<Frontend />);
115
- ```
116
-
117
- Then, run index.ts
118
-
119
- ```sh
120
- bun --hot ./index.ts
121
- ```
122
-
123
- For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
124
-
125
- # How to Use @providerprotocol/ai
126
-
127
- This library provides a unified SDK for AI inference across multiple LLM providers (Anthropic, OpenAI, Google, Ollama, OpenRouter, xAI). It implements the Unified Provider Protocol (UPP-1.2).
128
-
129
- ## Quick Start
130
-
131
- ```ts
132
- import { llm } from '@providerprotocol/ai';
133
- import { anthropic } from '@providerprotocol/ai/anthropic';
134
-
135
- const claude = llm({
136
- model: anthropic('claude-sonnet-4-20250514'),
137
- params: { max_tokens: 1000 },
138
- });
139
-
140
- const turn = await claude.generate('Hello!');
141
- console.log(turn.response.text);
142
- ```
143
-
144
- ## Provider Setup
145
-
146
- Each provider has a factory function. Import from the provider's path:
147
-
148
- ```ts
149
- // Anthropic
150
- import { anthropic } from '@providerprotocol/ai/anthropic';
151
- import type { AnthropicLLMParams } from '@providerprotocol/ai/anthropic';
152
-
153
- // OpenAI
154
- import { openai } from '@providerprotocol/ai/openai';
155
- import type { OpenAIResponsesParams, OpenAICompletionsParams } from '@providerprotocol/ai/openai';
156
-
157
- // Google Gemini
158
- import { google } from '@providerprotocol/ai/google';
159
- import type { GoogleLLMParams } from '@providerprotocol/ai/google';
160
-
161
- // Ollama (local models)
162
- import { ollama } from '@providerprotocol/ai/ollama';
163
- import type { OllamaLLMParams } from '@providerprotocol/ai/ollama';
164
-
165
- // OpenRouter
166
- import { openrouter } from '@providerprotocol/ai/openrouter';
167
-
168
- // xAI (Grok)
169
- import { xai } from '@providerprotocol/ai/xai';
170
- ```
171
-
172
- ## Creating LLM Instances
173
-
174
- Use `llm()` with provider-specific params type for type safety:
175
-
176
- ```ts
177
- // Anthropic
178
- const claude = llm<AnthropicLLMParams>({
179
- model: anthropic('claude-3-5-haiku-latest'),
180
- params: { max_tokens: 100 },
181
- });
182
-
183
- // OpenAI (Responses API - default)
184
- const gpt = llm<OpenAIResponsesParams>({
185
- model: openai('gpt-5.2'),
186
- params: { max_output_tokens: 100 },
187
- });
188
-
189
- // OpenAI (Completions API)
190
- const gptCompletions = llm<OpenAICompletionsParams>({
191
- model: openai('gpt-5.2', { api: 'completions' }),
192
- params: { max_completion_tokens: 100 },
193
- });
194
-
195
- // Google Gemini
196
- const gemini = llm<GoogleLLMParams>({
197
- model: google('gemini-3-flash-preview'),
198
- params: { maxOutputTokens: 500 },
199
- });
200
-
201
- // Ollama (local)
202
- const local = llm<OllamaLLMParams>({
203
- model: ollama('gemma3:4b'),
204
- params: { num_predict: 100 },
205
- });
206
- ```
207
-
208
- ## Basic Generation
209
-
210
- ```ts
211
- const turn = await model.generate('What is 2+2?');
212
-
213
- // Access response
214
- console.log(turn.response.text); // The text response
215
- console.log(turn.cycles); // Number of inference cycles (>1 if tools used)
216
- console.log(turn.usage.totalTokens); // Total tokens used
217
- console.log(turn.usage.inputTokens); // Input tokens
218
- console.log(turn.usage.outputTokens); // Output tokens
219
- ```
220
-
221
- ## Streaming
222
-
223
- ```ts
224
- const stream = model.stream('Count from 1 to 5.');
225
-
226
- // Iterate over events
227
- for await (const event of stream) {
228
- if (event.type === 'text_delta' && event.delta.text) {
229
- process.stdout.write(event.delta.text);
230
- }
231
- }
232
-
233
- // Get final turn after stream completes
234
- const turn = await stream.turn;
235
- console.log(turn.response.text);
236
- ```
237
-
238
- ### Stream Event Types
239
-
240
- - `text_delta` - Text chunk: `event.delta.text`
241
- - `tool_call_delta` - Tool call info: `event.delta.toolCallId`, `event.delta.toolName`, `event.delta.argumentsJson`
242
- - `message_start` - Message started
243
- - `message_stop` - Message complete
244
- - `content_block_start` - Content block started
245
- - `content_block_stop` - Content block complete
246
-
247
- ## System Prompts
248
-
249
- ```ts
250
- const model = llm({
251
- model: anthropic('claude-3-5-haiku-latest'),
252
- params: { max_tokens: 100 },
253
- system: 'You are a helpful assistant who speaks like a pirate.',
254
- });
255
- ```
256
-
257
- ## Multi-turn Conversations
258
-
259
- ### Manual History Management
260
-
261
- ```ts
262
- const model = llm<AnthropicLLMParams>({
263
- model: anthropic('claude-3-5-haiku-latest'),
264
- params: { max_tokens: 100 },
265
- });
266
-
267
- const history: any[] = [];
268
-
269
- // First turn
270
- const turn1 = await model.generate(history, 'My name is Alice.');
271
- history.push(...turn1.messages);
272
-
273
- // Second turn (model remembers context)
274
- const turn2 = await model.generate(history, 'What is my name?');
275
- // turn2.response.text will contain "Alice"
276
- ```
277
-
278
- ### Using Thread Class
279
-
280
- ```ts
281
- import { Thread, UserMessage, AssistantMessage } from '@providerprotocol/ai';
282
-
283
- const thread = new Thread();
284
-
285
- // Add messages
286
- thread.user('Hello');
287
- thread.assistant('Hi there!');
288
- thread.push(new UserMessage('How are you?'));
289
-
290
- // Use with generate
291
- const turn = await model.generate(thread.messages, 'Tell me a joke.');
292
- thread.append(turn); // Appends turn.messages to thread
293
-
294
- // Thread utilities
295
- thread.filter('user'); // Get only user messages
296
- thread.tail(5); // Get last 5 messages
297
- thread.slice(0, 10); // Get messages 0-9
298
- thread.clear(); // Clear all messages
299
-
300
- // Serialization
301
- const json = thread.toJSON();
302
- const restored = Thread.fromJSON(json);
303
- ```
304
-
305
- ## Tool Calling
306
-
307
- Define tools with JSON Schema parameters and a `run` function:
308
-
309
- ```ts
310
- const getWeather = {
311
- name: 'getWeather',
312
- description: 'Get the weather for a location',
313
- parameters: {
314
- type: 'object' as const,
315
- properties: {
316
- location: { type: 'string' as const, description: 'The city name' },
317
- },
318
- required: ['location'],
319
- },
320
- run: async (params: { location: string }) => {
321
- // Your implementation here
322
- return `The weather in ${params.location} is 72°F and sunny.`;
323
- },
324
- };
325
-
326
- const model = llm<AnthropicLLMParams>({
327
- model: anthropic('claude-3-5-haiku-latest'),
328
- params: { max_tokens: 200 },
329
- tools: [getWeather],
330
- });
331
-
332
- const turn = await model.generate('What is the weather in Tokyo?');
333
-
334
- // Tool executions are tracked
335
- console.log(turn.toolExecutions);
336
- // [{ toolName: 'getWeather', arguments: { location: 'Tokyo' }, result: '...', duration: 123 }]
337
- ```
338
-
339
- ### Tool with Approval
340
-
341
- ```ts
342
- const deleteTool = {
343
- name: 'deleteFile',
344
- description: 'Delete a file',
345
- parameters: {
346
- type: 'object' as const,
347
- properties: { path: { type: 'string' as const } },
348
- required: ['path'],
349
- },
350
- run: async (params: { path: string }) => {
351
- // Delete logic
352
- },
353
- approval: async (params: { path: string }) => {
354
- // Return true to allow, false to deny
355
- return confirm(`Delete ${params.path}?`);
356
- },
357
- };
358
- ```
359
-
360
- ### Tool Use Strategy (Hooks)
361
-
362
- ```ts
363
- const model = llm({
364
- model: anthropic('claude-3-5-haiku-latest'),
365
- tools: [myTool],
366
- toolStrategy: {
367
- maxIterations: 5, // Max tool execution rounds (default: 10)
368
- onToolCall: (tool, params) => console.log(`Calling ${tool.name}`),
369
- onBeforeCall: (tool, params) => true, // Return false to skip
370
- onAfterCall: (tool, params, result) => console.log('Result:', result),
371
- onError: (tool, params, error) => console.error(error),
372
- onMaxIterations: (n) => console.warn(`Reached ${n} iterations`),
373
- },
374
- });
375
- ```
376
-
377
- ### Streaming with Tools
378
-
379
- ```ts
380
- const stream = model.stream('What is 7 + 15?');
381
-
382
- for await (const event of stream) {
383
- if (event.type === 'tool_call_delta') {
384
- console.log('Tool:', event.delta.toolName, event.delta.argumentsJson);
385
- }
386
- if (event.type === 'text_delta') {
387
- process.stdout.write(event.delta.text ?? '');
388
- }
389
- }
390
-
391
- const turn = await stream.turn;
392
- console.log('Tool executions:', turn.toolExecutions);
393
- ```
394
-
395
- ## Structured Output
396
-
397
- Use JSON Schema to enforce response structure:
398
-
399
- ```ts
400
- const model = llm<AnthropicLLMParams>({
401
- model: anthropic('claude-3-5-haiku-latest'),
402
- params: { max_tokens: 200 },
403
- structure: {
404
- type: 'object',
405
- properties: {
406
- city: { type: 'string' },
407
- population: { type: 'number' },
408
- isCapital: { type: 'boolean' },
409
- },
410
- required: ['city', 'population', 'isCapital'],
411
- },
412
- });
413
-
414
- const turn = await model.generate('Tell me about Paris, France.');
415
-
416
- // Parsed data is available on turn.data
417
- console.log(turn.data);
418
- // { city: 'Paris', population: 2161000, isCapital: true }
419
- ```
420
-
421
- ### Streaming Structured Output
422
-
423
- Different providers stream structured output differently:
424
-
425
- ```ts
426
- // OpenAI/Google: Accumulate text_delta events
427
- const stream = gpt.stream('Tell me about Tokyo.');
428
- let json = '';
429
- for await (const event of stream) {
430
- if (event.type === 'text_delta' && event.delta.text) {
431
- json += event.delta.text;
432
- }
433
- }
434
- const data = JSON.parse(json);
435
-
436
- // Anthropic: Accumulate tool_call_delta events (tool-based approach)
437
- const stream = claude.stream('Tell me about Tokyo.');
438
- let json = '';
439
- for await (const event of stream) {
440
- if (event.type === 'tool_call_delta' && event.delta.argumentsJson) {
441
- json += event.delta.argumentsJson;
442
- }
443
- }
444
- const data = JSON.parse(json);
445
- ```
446
-
447
- ## Vision / Multimodal
448
-
449
- ### Using Images
450
-
451
- ```ts
452
- import { UserMessage } from '@providerprotocol/ai';
453
- import { readFileSync } from 'fs';
454
-
455
- // Base64 image
456
- const imageBase64 = readFileSync('./image.png').toString('base64');
457
-
458
- const message = new UserMessage([
459
- { type: 'text', text: 'What is in this image?' },
460
- {
461
- type: 'image',
462
- mimeType: 'image/png',
463
- source: { type: 'base64', data: imageBase64 },
464
- },
465
- ]);
466
-
467
- const turn = await model.generate([message]);
468
- console.log(turn.response.text);
469
- ```
470
-
471
- ### Image from URL
472
-
473
- ```ts
474
- const message = new UserMessage([
475
- { type: 'text', text: 'Describe this image.' },
476
- {
477
- type: 'image',
478
- mimeType: 'image/jpeg',
479
- source: { type: 'url', url: 'https://example.com/image.jpg' },
480
- },
481
- ]);
482
- ```
483
-
484
- ## OpenAI Built-in Tools
485
-
486
- OpenAI Responses API provides native tools:
487
-
488
- ```ts
489
- import { openai, tools } from '@providerprotocol/ai/openai';
490
-
491
- // Web Search
492
- const model = llm<OpenAIResponsesParams>({
493
- model: openai('gpt-4o'),
494
- params: {
495
- max_output_tokens: 500,
496
- tools: [tools.webSearch()],
497
- },
498
- });
499
-
500
- // Web Search with user location
501
- const modelWithLocation = llm<OpenAIResponsesParams>({
502
- model: openai('gpt-4o'),
503
- params: {
504
- max_output_tokens: 500,
505
- tools: [
506
- tools.webSearch({
507
- search_context_size: 'medium',
508
- user_location: {
509
- type: 'approximate',
510
- city: 'Tokyo',
511
- country: 'JP',
512
- },
513
- }),
514
- ],
515
- },
516
- });
517
-
518
- // Image Generation
519
- const imageModel = llm<OpenAIResponsesParams>({
520
- model: openai('gpt-4o'),
521
- params: {
522
- max_output_tokens: 1000,
523
- tools: [tools.imageGeneration({ quality: 'low', size: '1024x1024' })],
524
- },
525
- });
526
-
527
- const turn = await imageModel.generate('Generate an image of a red apple.');
528
- // Generated images are in turn.response.images
529
- const image = turn.response.images[0];
530
- ```
531
-
532
- ## Error Handling
533
-
534
- All errors are normalized to `UPPError`:
535
-
536
- ```ts
537
- import { UPPError } from '@providerprotocol/ai';
538
-
539
- try {
540
- await model.generate('Hello');
541
- } catch (error) {
542
- if (error instanceof UPPError) {
543
- console.log(error.code); // 'AUTHENTICATION_FAILED', 'RATE_LIMITED', etc.
544
- console.log(error.provider); // 'anthropic', 'openai', etc.
545
- console.log(error.modality); // 'llm', 'embedding', 'image'
546
- console.log(error.message); // Human-readable message
547
- }
548
- }
549
- ```
550
-
551
- ### Error Codes
552
-
553
- - `AUTHENTICATION_FAILED` - Invalid API key
554
- - `RATE_LIMITED` - Too many requests
555
- - `CONTEXT_LENGTH_EXCEEDED` - Input too long
556
- - `MODEL_NOT_FOUND` - Invalid model ID
557
- - `INVALID_REQUEST` - Malformed request
558
- - `SERVER_ERROR` - Provider error
559
- - `NETWORK_ERROR` - Connection failed
560
- - `TIMEOUT` - Request timeout
561
-
562
- ## Custom API Key / Config
563
-
564
- ```ts
565
- const model = llm({
566
- model: anthropic('claude-3-5-haiku-latest'),
567
- params: { max_tokens: 100 },
568
- config: {
569
- apiKey: 'sk-...', // Override env var
570
- baseUrl: 'https://custom-endpoint.com', // Custom endpoint
571
- },
572
- });
573
- ```
574
-
575
- ## Key Management Strategies
576
-
577
- For load balancing across multiple API keys:
578
-
579
- ```ts
580
- import { RoundRobinKeys, WeightedKeys, DynamicKey } from '@providerprotocol/ai';
581
-
582
- // Round robin
583
- const model = llm({
584
- model: anthropic('claude-3-5-haiku-latest'),
585
- config: {
586
- apiKey: new RoundRobinKeys(['key1', 'key2', 'key3']),
587
- },
588
- });
589
-
590
- // Weighted distribution
591
- const weighted = llm({
592
- model: anthropic('claude-3-5-haiku-latest'),
593
- config: {
594
- apiKey: new WeightedKeys([
595
- { key: 'primary-key', weight: 80 },
596
- { key: 'backup-key', weight: 20 },
597
- ]),
598
- },
599
- });
600
-
601
- // Dynamic key fetching
602
- const dynamic = llm({
603
- model: anthropic('claude-3-5-haiku-latest'),
604
- config: {
605
- apiKey: new DynamicKey(async () => {
606
- // Fetch from secrets manager, etc.
607
- return await getKeyFromVault();
608
- }),
609
- },
610
- });
611
- ```
612
-
613
- ## Retry Strategies
614
-
615
- ```ts
616
- import { ExponentialBackoff, LinearBackoff, NoRetry } from '@providerprotocol/ai';
617
-
618
- const model = llm({
619
- model: anthropic('claude-3-5-haiku-latest'),
620
- config: {
621
- retry: new ExponentialBackoff({
622
- maxRetries: 3,
623
- baseDelay: 1000,
624
- maxDelay: 30000,
625
- }),
626
- },
627
- });
628
- ```
629
-
630
- ## Provider-Specific Parameters
631
-
632
- Each provider has unique parameter names:
633
-
634
- | Feature | Anthropic | OpenAI Responses | OpenAI Completions | Google |
635
- |---------|-----------|------------------|-------------------|--------|
636
- | Max tokens | `max_tokens` | `max_output_tokens` | `max_completion_tokens` | `maxOutputTokens` |
637
- | Temperature | `temperature` | `temperature` | `temperature` | `temperature` |
638
-
639
- ## Message Types
640
-
641
- ```ts
642
- import { UserMessage, AssistantMessage, ToolResultMessage } from '@providerprotocol/ai';
643
-
644
- // User message with text
645
- const user = new UserMessage('Hello');
646
-
647
- // User message with content blocks
648
- const userMulti = new UserMessage([
649
- { type: 'text', text: 'Describe this:' },
650
- { type: 'image', mimeType: 'image/png', source: { type: 'base64', data: '...' } },
651
- ]);
652
-
653
- // Assistant message
654
- const assistant = new AssistantMessage('Hi there!');
655
-
656
- // Assistant message with tool calls
657
- const assistantWithTools = new AssistantMessage('Let me check...', [
658
- { toolCallId: 'call_123', toolName: 'getWeather', arguments: { city: 'Tokyo' } },
659
- ]);
660
-
661
- // Tool result
662
- const toolResult = new ToolResultMessage([
663
- { toolCallId: 'call_123', result: '72°F and sunny' },
664
- ]);
665
-
666
- // Type guards
667
- import { isUserMessage, isAssistantMessage, isToolResultMessage } from '@providerprotocol/ai';
668
- if (isUserMessage(msg)) { /* ... */ }
669
- ```
670
-
671
- ## Environment Variables
672
-
673
- Set API keys as environment variables (Bun auto-loads `.env`):
674
-
675
- ```
676
- ANTHROPIC_API_KEY=sk-ant-...
677
- OPENAI_API_KEY=sk-...
678
- GOOGLE_API_KEY=AI...
679
- XAI_API_KEY=xai-...
680
- OPENROUTER_API_KEY=sk-or-...
681
- ```