ai.matey.backend 0.2.0

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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/index.js +60 -0
  3. package/dist/cjs/index.js.map +1 -0
  4. package/dist/cjs/providers/ai21.js +331 -0
  5. package/dist/cjs/providers/ai21.js.map +1 -0
  6. package/dist/cjs/providers/anthropic.js +664 -0
  7. package/dist/cjs/providers/anthropic.js.map +1 -0
  8. package/dist/cjs/providers/anyscale.js +338 -0
  9. package/dist/cjs/providers/anyscale.js.map +1 -0
  10. package/dist/cjs/providers/aws-bedrock.js +374 -0
  11. package/dist/cjs/providers/aws-bedrock.js.map +1 -0
  12. package/dist/cjs/providers/azure-openai.js +406 -0
  13. package/dist/cjs/providers/azure-openai.js.map +1 -0
  14. package/dist/cjs/providers/cerebras.js +356 -0
  15. package/dist/cjs/providers/cerebras.js.map +1 -0
  16. package/dist/cjs/providers/cloudflare.js +359 -0
  17. package/dist/cjs/providers/cloudflare.js.map +1 -0
  18. package/dist/cjs/providers/cohere.js +368 -0
  19. package/dist/cjs/providers/cohere.js.map +1 -0
  20. package/dist/cjs/providers/deepinfra.js +343 -0
  21. package/dist/cjs/providers/deepinfra.js.map +1 -0
  22. package/dist/cjs/providers/deepseek.js +104 -0
  23. package/dist/cjs/providers/deepseek.js.map +1 -0
  24. package/dist/cjs/providers/fireworks.js +363 -0
  25. package/dist/cjs/providers/fireworks.js.map +1 -0
  26. package/dist/cjs/providers/gemini.js +292 -0
  27. package/dist/cjs/providers/gemini.js.map +1 -0
  28. package/dist/cjs/providers/groq.js +143 -0
  29. package/dist/cjs/providers/groq.js.map +1 -0
  30. package/dist/cjs/providers/huggingface.js +392 -0
  31. package/dist/cjs/providers/huggingface.js.map +1 -0
  32. package/dist/cjs/providers/lmstudio.js +144 -0
  33. package/dist/cjs/providers/lmstudio.js.map +1 -0
  34. package/dist/cjs/providers/mistral.js +288 -0
  35. package/dist/cjs/providers/mistral.js.map +1 -0
  36. package/dist/cjs/providers/nvidia.js +167 -0
  37. package/dist/cjs/providers/nvidia.js.map +1 -0
  38. package/dist/cjs/providers/ollama.js +257 -0
  39. package/dist/cjs/providers/ollama.js.map +1 -0
  40. package/dist/cjs/providers/openai.js +640 -0
  41. package/dist/cjs/providers/openai.js.map +1 -0
  42. package/dist/cjs/providers/openrouter.js +379 -0
  43. package/dist/cjs/providers/openrouter.js.map +1 -0
  44. package/dist/cjs/providers/perplexity.js +372 -0
  45. package/dist/cjs/providers/perplexity.js.map +1 -0
  46. package/dist/cjs/providers/replicate.js +340 -0
  47. package/dist/cjs/providers/replicate.js.map +1 -0
  48. package/dist/cjs/providers/together-ai.js +341 -0
  49. package/dist/cjs/providers/together-ai.js.map +1 -0
  50. package/dist/cjs/providers/xai.js +339 -0
  51. package/dist/cjs/providers/xai.js.map +1 -0
  52. package/dist/cjs/shared.js +279 -0
  53. package/dist/cjs/shared.js.map +1 -0
  54. package/dist/esm/index.js +44 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/esm/providers/ai21.js +327 -0
  57. package/dist/esm/providers/ai21.js.map +1 -0
  58. package/dist/esm/providers/anthropic.js +660 -0
  59. package/dist/esm/providers/anthropic.js.map +1 -0
  60. package/dist/esm/providers/anyscale.js +334 -0
  61. package/dist/esm/providers/anyscale.js.map +1 -0
  62. package/dist/esm/providers/aws-bedrock.js +370 -0
  63. package/dist/esm/providers/aws-bedrock.js.map +1 -0
  64. package/dist/esm/providers/azure-openai.js +402 -0
  65. package/dist/esm/providers/azure-openai.js.map +1 -0
  66. package/dist/esm/providers/cerebras.js +352 -0
  67. package/dist/esm/providers/cerebras.js.map +1 -0
  68. package/dist/esm/providers/cloudflare.js +355 -0
  69. package/dist/esm/providers/cloudflare.js.map +1 -0
  70. package/dist/esm/providers/cohere.js +364 -0
  71. package/dist/esm/providers/cohere.js.map +1 -0
  72. package/dist/esm/providers/deepinfra.js +339 -0
  73. package/dist/esm/providers/deepinfra.js.map +1 -0
  74. package/dist/esm/providers/deepseek.js +99 -0
  75. package/dist/esm/providers/deepseek.js.map +1 -0
  76. package/dist/esm/providers/fireworks.js +359 -0
  77. package/dist/esm/providers/fireworks.js.map +1 -0
  78. package/dist/esm/providers/gemini.js +288 -0
  79. package/dist/esm/providers/gemini.js.map +1 -0
  80. package/dist/esm/providers/groq.js +138 -0
  81. package/dist/esm/providers/groq.js.map +1 -0
  82. package/dist/esm/providers/huggingface.js +387 -0
  83. package/dist/esm/providers/huggingface.js.map +1 -0
  84. package/dist/esm/providers/lmstudio.js +139 -0
  85. package/dist/esm/providers/lmstudio.js.map +1 -0
  86. package/dist/esm/providers/mistral.js +284 -0
  87. package/dist/esm/providers/mistral.js.map +1 -0
  88. package/dist/esm/providers/nvidia.js +162 -0
  89. package/dist/esm/providers/nvidia.js.map +1 -0
  90. package/dist/esm/providers/ollama.js +253 -0
  91. package/dist/esm/providers/ollama.js.map +1 -0
  92. package/dist/esm/providers/openai.js +636 -0
  93. package/dist/esm/providers/openai.js.map +1 -0
  94. package/dist/esm/providers/openrouter.js +375 -0
  95. package/dist/esm/providers/openrouter.js.map +1 -0
  96. package/dist/esm/providers/perplexity.js +368 -0
  97. package/dist/esm/providers/perplexity.js.map +1 -0
  98. package/dist/esm/providers/replicate.js +336 -0
  99. package/dist/esm/providers/replicate.js.map +1 -0
  100. package/dist/esm/providers/together-ai.js +337 -0
  101. package/dist/esm/providers/together-ai.js.map +1 -0
  102. package/dist/esm/providers/xai.js +335 -0
  103. package/dist/esm/providers/xai.js.map +1 -0
  104. package/dist/esm/shared.js +272 -0
  105. package/dist/esm/shared.js.map +1 -0
  106. package/dist/types/index.d.ts +38 -0
  107. package/dist/types/index.d.ts.map +1 -0
  108. package/dist/types/providers/ai21.d.ts +106 -0
  109. package/dist/types/providers/ai21.d.ts.map +1 -0
  110. package/dist/types/providers/anthropic.d.ts +194 -0
  111. package/dist/types/providers/anthropic.d.ts.map +1 -0
  112. package/dist/types/providers/anyscale.d.ts +109 -0
  113. package/dist/types/providers/anyscale.d.ts.map +1 -0
  114. package/dist/types/providers/aws-bedrock.d.ts +152 -0
  115. package/dist/types/providers/aws-bedrock.d.ts.map +1 -0
  116. package/dist/types/providers/azure-openai.d.ts +142 -0
  117. package/dist/types/providers/azure-openai.d.ts.map +1 -0
  118. package/dist/types/providers/cerebras.d.ts +130 -0
  119. package/dist/types/providers/cerebras.d.ts.map +1 -0
  120. package/dist/types/providers/cloudflare.d.ts +125 -0
  121. package/dist/types/providers/cloudflare.d.ts.map +1 -0
  122. package/dist/types/providers/cohere.d.ts +114 -0
  123. package/dist/types/providers/cohere.d.ts.map +1 -0
  124. package/dist/types/providers/deepinfra.d.ts +118 -0
  125. package/dist/types/providers/deepinfra.d.ts.map +1 -0
  126. package/dist/types/providers/deepseek.d.ts +68 -0
  127. package/dist/types/providers/deepseek.d.ts.map +1 -0
  128. package/dist/types/providers/fireworks.d.ts +127 -0
  129. package/dist/types/providers/fireworks.d.ts.map +1 -0
  130. package/dist/types/providers/gemini.d.ts +71 -0
  131. package/dist/types/providers/gemini.d.ts.map +1 -0
  132. package/dist/types/providers/groq.d.ts +83 -0
  133. package/dist/types/providers/groq.d.ts.map +1 -0
  134. package/dist/types/providers/huggingface.d.ts +154 -0
  135. package/dist/types/providers/huggingface.d.ts.map +1 -0
  136. package/dist/types/providers/lmstudio.d.ts +88 -0
  137. package/dist/types/providers/lmstudio.d.ts.map +1 -0
  138. package/dist/types/providers/mistral.d.ts +65 -0
  139. package/dist/types/providers/mistral.d.ts.map +1 -0
  140. package/dist/types/providers/nvidia.d.ts +100 -0
  141. package/dist/types/providers/nvidia.d.ts.map +1 -0
  142. package/dist/types/providers/ollama.d.ts +59 -0
  143. package/dist/types/providers/ollama.d.ts.map +1 -0
  144. package/dist/types/providers/openai.d.ts +205 -0
  145. package/dist/types/providers/openai.d.ts.map +1 -0
  146. package/dist/types/providers/openrouter.d.ts +135 -0
  147. package/dist/types/providers/openrouter.d.ts.map +1 -0
  148. package/dist/types/providers/perplexity.d.ts +116 -0
  149. package/dist/types/providers/perplexity.d.ts.map +1 -0
  150. package/dist/types/providers/replicate.d.ts +91 -0
  151. package/dist/types/providers/replicate.d.ts.map +1 -0
  152. package/dist/types/providers/together-ai.d.ts +118 -0
  153. package/dist/types/providers/together-ai.d.ts.map +1 -0
  154. package/dist/types/providers/xai.d.ts +119 -0
  155. package/dist/types/providers/xai.d.ts.map +1 -0
  156. package/dist/types/shared.d.ts +116 -0
  157. package/dist/types/shared.d.ts.map +1 -0
  158. package/package.json +327 -0
  159. package/readme.md +86 -0
@@ -0,0 +1,664 @@
1
+ "use strict";
2
+ /**
3
+ * Anthropic Backend Adapter
4
+ *
5
+ * Adapts Universal IR to Anthropic Messages API.
6
+ * Handles Anthropic's separate system parameter and SSE streaming format.
7
+ *
8
+ * @module
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.AnthropicBackendAdapter = void 0;
12
+ const ai_matey_errors_1 = require("ai.matey.errors");
13
+ const ai_matey_utils_1 = require("ai.matey.utils");
14
+ const ai_matey_utils_2 = require("ai.matey.utils");
15
+ const shared_js_1 = require("../shared.js");
16
+ // ============================================================================
17
+ // Default Anthropic Models
18
+ // ============================================================================
19
+ /**
20
+ * Default list of Anthropic Claude models.
21
+ * Used when no custom model list is provided in config.
22
+ */
23
+ const DEFAULT_ANTHROPIC_MODELS = [
24
+ {
25
+ id: 'claude-3-5-sonnet-20241022',
26
+ name: 'Claude 3.5 Sonnet (Oct 2024)',
27
+ description: 'Most intelligent model with excellent reasoning and analysis',
28
+ ownedBy: 'anthropic',
29
+ capabilities: {
30
+ maxTokens: 8192,
31
+ contextWindow: 200000,
32
+ supportsStreaming: true,
33
+ supportsVision: true,
34
+ supportsTools: true,
35
+ supportsJSON: false,
36
+ },
37
+ },
38
+ {
39
+ id: 'claude-3-5-haiku-20241022',
40
+ name: 'Claude 3.5 Haiku (Oct 2024)',
41
+ description: 'Fastest and most compact model for high-throughput tasks',
42
+ ownedBy: 'anthropic',
43
+ capabilities: {
44
+ maxTokens: 8192,
45
+ contextWindow: 200000,
46
+ supportsStreaming: true,
47
+ supportsVision: false,
48
+ supportsTools: true,
49
+ supportsJSON: false,
50
+ },
51
+ },
52
+ {
53
+ id: 'claude-3-opus-20240229',
54
+ name: 'Claude 3 Opus (Feb 2024)',
55
+ description: 'Previous top-tier model with strong performance',
56
+ ownedBy: 'anthropic',
57
+ capabilities: {
58
+ maxTokens: 4096,
59
+ contextWindow: 200000,
60
+ supportsStreaming: true,
61
+ supportsVision: true,
62
+ supportsTools: true,
63
+ supportsJSON: false,
64
+ },
65
+ },
66
+ {
67
+ id: 'claude-3-sonnet-20240229',
68
+ name: 'Claude 3 Sonnet (Feb 2024)',
69
+ description: 'Balanced intelligence and speed',
70
+ ownedBy: 'anthropic',
71
+ capabilities: {
72
+ maxTokens: 4096,
73
+ contextWindow: 200000,
74
+ supportsStreaming: true,
75
+ supportsVision: true,
76
+ supportsTools: true,
77
+ supportsJSON: false,
78
+ },
79
+ },
80
+ {
81
+ id: 'claude-3-haiku-20240307',
82
+ name: 'Claude 3 Haiku (Mar 2024)',
83
+ description: 'Fast and compact model',
84
+ ownedBy: 'anthropic',
85
+ capabilities: {
86
+ maxTokens: 4096,
87
+ contextWindow: 200000,
88
+ supportsStreaming: true,
89
+ supportsVision: false,
90
+ supportsTools: false,
91
+ supportsJSON: false,
92
+ },
93
+ },
94
+ ];
95
+ // ============================================================================
96
+ // Anthropic Backend Adapter
97
+ // ============================================================================
98
+ /**
99
+ * Backend adapter for Anthropic Messages API.
100
+ */
101
+ class AnthropicBackendAdapter {
102
+ metadata;
103
+ config;
104
+ baseURL;
105
+ constructor(config) {
106
+ this.config = config;
107
+ this.baseURL = config.baseURL || 'https://api.anthropic.com/v1';
108
+ this.metadata = {
109
+ name: 'anthropic-backend',
110
+ version: '1.0.0',
111
+ provider: 'Anthropic',
112
+ capabilities: {
113
+ streaming: true,
114
+ multiModal: true,
115
+ tools: true,
116
+ maxContextTokens: 200000,
117
+ systemMessageStrategy: 'separate-parameter',
118
+ supportsMultipleSystemMessages: false, // Anthropic merges multiple system messages
119
+ supportsTemperature: true,
120
+ supportsTopP: true,
121
+ supportsTopK: true,
122
+ supportsSeed: false,
123
+ supportsFrequencyPenalty: false,
124
+ supportsPresencePenalty: false,
125
+ maxStopSequences: 4,
126
+ },
127
+ config: {
128
+ baseURL: this.baseURL,
129
+ },
130
+ };
131
+ }
132
+ /**
133
+ * Execute non-streaming chat completion request.
134
+ */
135
+ async execute(request, signal) {
136
+ try {
137
+ // Convert IR to Anthropic format
138
+ const anthropicRequest = this.fromIR(request);
139
+ // Make HTTP request
140
+ const startTime = Date.now();
141
+ const response = await this.makeRequest(anthropicRequest, signal);
142
+ // Convert response to IR
143
+ const irResponse = this.toIR(response, request, Date.now() - startTime);
144
+ return irResponse;
145
+ }
146
+ catch (error) {
147
+ // Re-throw adapter errors
148
+ if (error instanceof ai_matey_errors_1.AdapterConversionError ||
149
+ error instanceof ai_matey_errors_1.NetworkError ||
150
+ error instanceof ai_matey_errors_1.ProviderError) {
151
+ throw error;
152
+ }
153
+ // Wrap unknown errors
154
+ throw new ai_matey_errors_1.ProviderError({
155
+ code: ai_matey_errors_1.ErrorCode.PROVIDER_ERROR,
156
+ message: `Anthropic request failed: ${error instanceof Error ? error.message : String(error)}`,
157
+ isRetryable: true,
158
+ provenance: {
159
+ backend: this.metadata.name,
160
+ },
161
+ cause: error instanceof Error ? error : undefined,
162
+ });
163
+ }
164
+ }
165
+ /**
166
+ * Execute streaming chat completion request.
167
+ */
168
+ async *executeStream(request, signal) {
169
+ try {
170
+ // Convert IR to Anthropic format
171
+ const anthropicRequest = this.fromIR(request);
172
+ anthropicRequest.stream = true;
173
+ // Get effective streaming configuration
174
+ const streamingConfig = (0, ai_matey_utils_2.mergeStreamingConfig)(this.config.streaming);
175
+ const effectiveMode = (0, ai_matey_utils_2.getEffectiveStreamMode)(request.streamMode, undefined, streamingConfig);
176
+ const includeBoth = streamingConfig.includeBoth || effectiveMode === 'accumulated';
177
+ // Make streaming HTTP request
178
+ const response = await fetch(`${this.baseURL}/messages`, {
179
+ method: 'POST',
180
+ headers: this.getHeaders(),
181
+ body: JSON.stringify(anthropicRequest),
182
+ signal,
183
+ });
184
+ if (!response.ok) {
185
+ const errorBody = await response.text();
186
+ throw (0, ai_matey_errors_1.createErrorFromHttpResponse)(response.status, response.statusText, errorBody, {
187
+ backend: this.metadata.name,
188
+ });
189
+ }
190
+ if (!response.body) {
191
+ throw new ai_matey_errors_1.StreamError({
192
+ code: ai_matey_errors_1.ErrorCode.STREAM_INTERRUPTED,
193
+ message: 'Response body is null',
194
+ provenance: {
195
+ backend: this.metadata.name,
196
+ },
197
+ });
198
+ }
199
+ // Parse SSE stream
200
+ let sequence = 0;
201
+ let contentBuffer = '';
202
+ let messageId = '';
203
+ let model = '';
204
+ let usage;
205
+ // Read stream
206
+ const reader = response.body.getReader();
207
+ const decoder = new TextDecoder();
208
+ let buffer = '';
209
+ try {
210
+ while (true) {
211
+ const { done, value } = await reader.read();
212
+ if (done) {
213
+ break;
214
+ }
215
+ buffer += decoder.decode(value, { stream: true });
216
+ const lines = buffer.split('\n');
217
+ buffer = lines.pop() || '';
218
+ for (const line of lines) {
219
+ // Parse SSE format: "event: <type>" or "data: <json>"
220
+ if (line.startsWith('event:')) {
221
+ // Event type line (Anthropic doesn't always use this)
222
+ continue;
223
+ }
224
+ if (line.startsWith('data:')) {
225
+ const data = line.slice(5).trim();
226
+ if (!data) {
227
+ continue;
228
+ }
229
+ try {
230
+ const event = JSON.parse(data);
231
+ // Handle different event types
232
+ switch (event.type) {
233
+ case 'message_start':
234
+ // Extract message ID and model
235
+ messageId = event.message.id;
236
+ model = event.message.model;
237
+ usage = {
238
+ promptTokens: event.message.usage.input_tokens,
239
+ completionTokens: 0,
240
+ totalTokens: event.message.usage.input_tokens,
241
+ };
242
+ // Yield start chunk
243
+ yield {
244
+ type: 'start',
245
+ sequence: sequence++,
246
+ metadata: {
247
+ ...request.metadata,
248
+ requestId: messageId,
249
+ provenance: {
250
+ ...request.metadata.provenance,
251
+ backend: this.metadata.name,
252
+ },
253
+ custom: {
254
+ ...request.metadata.custom,
255
+ anthropicMessageId: messageId,
256
+ model,
257
+ },
258
+ },
259
+ };
260
+ break;
261
+ case 'content_block_start':
262
+ // Content block started (we'll handle deltas)
263
+ break;
264
+ case 'content_block_delta':
265
+ // Content delta
266
+ if (event.delta.type === 'text_delta') {
267
+ contentBuffer += event.delta.text;
268
+ // Build content chunk with optional accumulated field
269
+ const contentChunk = {
270
+ type: 'content',
271
+ sequence: sequence++,
272
+ delta: event.delta.text,
273
+ role: 'assistant',
274
+ };
275
+ // Add accumulated field if configured
276
+ if (includeBoth) {
277
+ contentChunk.accumulated = contentBuffer;
278
+ }
279
+ yield contentChunk;
280
+ }
281
+ else if (event.delta.type === 'input_json_delta') {
282
+ // Tool use delta (not implemented yet)
283
+ // TODO: Handle tool use deltas in Phase 5
284
+ }
285
+ break;
286
+ case 'content_block_stop':
287
+ // Content block completed
288
+ break;
289
+ case 'message_delta':
290
+ // Message metadata delta (stop reason, usage)
291
+ if (event.delta.stop_reason && usage) {
292
+ usage.completionTokens = event.usage.output_tokens;
293
+ usage.totalTokens = usage.promptTokens + event.usage.output_tokens;
294
+ }
295
+ break;
296
+ case 'message_stop': {
297
+ // Stream complete
298
+ const finishReason = this.mapStopReason(contentBuffer ? 'end_turn' : 'stop');
299
+ // Build final message
300
+ const message = {
301
+ role: 'assistant',
302
+ content: contentBuffer,
303
+ };
304
+ yield {
305
+ type: 'done',
306
+ sequence: sequence++,
307
+ finishReason,
308
+ usage,
309
+ message,
310
+ };
311
+ break;
312
+ }
313
+ case 'ping':
314
+ // Keep-alive ping, ignore
315
+ break;
316
+ case 'error':
317
+ // Error event
318
+ yield {
319
+ type: 'error',
320
+ sequence: sequence++,
321
+ error: {
322
+ code: event.error.type,
323
+ message: event.error.message,
324
+ },
325
+ };
326
+ break;
327
+ }
328
+ }
329
+ catch (parseError) {
330
+ // Skip malformed chunks
331
+ console.warn('Failed to parse SSE event:', data, parseError);
332
+ continue;
333
+ }
334
+ }
335
+ }
336
+ }
337
+ }
338
+ finally {
339
+ reader.releaseLock();
340
+ }
341
+ }
342
+ catch (error) {
343
+ // Yield error chunk
344
+ yield {
345
+ type: 'error',
346
+ sequence: 0,
347
+ error: {
348
+ code: error instanceof Error ? error.name : 'UNKNOWN_ERROR',
349
+ message: error instanceof Error ? error.message : String(error),
350
+ },
351
+ };
352
+ }
353
+ }
354
+ /**
355
+ * Health check to verify Anthropic API is accessible.
356
+ */
357
+ async healthCheck() {
358
+ try {
359
+ // Anthropic doesn't have a dedicated health endpoint
360
+ // We'll try a minimal request to verify auth
361
+ const response = await fetch(`${this.baseURL}/messages`, {
362
+ method: 'POST',
363
+ headers: this.getHeaders(),
364
+ body: JSON.stringify({
365
+ model: 'claude-3-haiku-20240307',
366
+ max_tokens: 1,
367
+ messages: [{ role: 'user', content: 'test' }],
368
+ }),
369
+ signal: AbortSignal.timeout(5000),
370
+ });
371
+ return response.ok || response.status === 400; // 400 is acceptable (validation error)
372
+ }
373
+ catch {
374
+ return false;
375
+ }
376
+ }
377
+ /**
378
+ * Estimate cost for a request (rough heuristic).
379
+ */
380
+ estimateCost(request) {
381
+ // Use shared token estimation utility
382
+ const estimatedInputTokens = (0, shared_js_1.estimateTokens)(request);
383
+ // Rough cost: $0.015 per 1000 tokens for Claude 3.5 Sonnet
384
+ return Promise.resolve((estimatedInputTokens / 1000) * 0.015);
385
+ }
386
+ /**
387
+ * List available Anthropic models.
388
+ *
389
+ * Since Anthropic doesn't have a public models endpoint, this uses:
390
+ * 1. Static config (config.models) - if provided
391
+ * 2. Default model list - built-in list of Claude models
392
+ */
393
+ listModels(options) {
394
+ // 1. Check static config first
395
+ if (this.config.models) {
396
+ return Promise.resolve((0, shared_js_1.buildStaticResult)(this.config.models, 'anthropic'));
397
+ }
398
+ // 2. Use default Anthropic models
399
+ const result = {
400
+ models: [...DEFAULT_ANTHROPIC_MODELS],
401
+ source: 'static',
402
+ fetchedAt: Date.now(),
403
+ isComplete: true,
404
+ };
405
+ // 3. Apply filter if requested
406
+ return Promise.resolve((0, shared_js_1.applyModelFilter)(result, options?.filter));
407
+ }
408
+ // ==========================================================================
409
+ // Private Helper Methods
410
+ // ==========================================================================
411
+ /**
412
+ * Convert IR request to Anthropic format.
413
+ *
414
+ * Public method for testing and debugging - see what will be sent to Anthropic.
415
+ */
416
+ fromIR(request) {
417
+ try {
418
+ // Normalize system messages (Anthropic uses separate-parameter strategy)
419
+ const { systemParameter, messages } = (0, ai_matey_utils_1.normalizeSystemMessages)(request.messages, this.metadata.capabilities.systemMessageStrategy, this.metadata.capabilities.supportsMultipleSystemMessages);
420
+ // Convert messages
421
+ const anthropicMessages = messages.map((msg) => this.convertMessageToAnthropic(msg));
422
+ // Validate max_tokens is present (required by Anthropic)
423
+ const maxTokens = request.parameters?.maxTokens || 4096;
424
+ // Build Anthropic request
425
+ const anthropicRequest = {
426
+ model: request.parameters?.model || this.config.defaultModel || 'claude-3-5-sonnet-20241022',
427
+ messages: anthropicMessages,
428
+ system: systemParameter || undefined,
429
+ max_tokens: maxTokens,
430
+ temperature: request.parameters?.temperature,
431
+ top_p: request.parameters?.topP,
432
+ top_k: request.parameters?.topK,
433
+ stop_sequences: request.parameters?.stopSequences
434
+ ? [...request.parameters.stopSequences].slice(0, 4) // Anthropic max is 4
435
+ : undefined,
436
+ stream: request.stream,
437
+ metadata: request.metadata.custom?.userId !== undefined &&
438
+ (typeof request.metadata.custom.userId === 'string' ||
439
+ typeof request.metadata.custom.userId === 'number')
440
+ ? { user_id: String(request.metadata.custom.userId) }
441
+ : undefined,
442
+ };
443
+ return anthropicRequest;
444
+ }
445
+ catch (error) {
446
+ throw new ai_matey_errors_1.AdapterConversionError({
447
+ code: ai_matey_errors_1.ErrorCode.ADAPTER_CONVERSION_ERROR,
448
+ message: `Failed to convert IR to Anthropic format: ${error instanceof Error ? error.message : String(error)}`,
449
+ provenance: {
450
+ backend: this.metadata.name,
451
+ },
452
+ cause: error instanceof Error ? error : undefined,
453
+ });
454
+ }
455
+ }
456
+ /**
457
+ * Convert Anthropic response to IR format.
458
+ *
459
+ * Public method for testing and debugging - parse Anthropic responses manually.
460
+ */
461
+ toIR(response, originalRequest, latencyMs) {
462
+ try {
463
+ // Extract text content from content blocks
464
+ let textContent = '';
465
+ const contentBlocks = [];
466
+ for (const block of response.content) {
467
+ if (block.type === 'text') {
468
+ textContent += block.text;
469
+ contentBlocks.push({ type: 'text', text: block.text });
470
+ }
471
+ else if (block.type === 'tool_use') {
472
+ contentBlocks.push({
473
+ type: 'tool_use',
474
+ id: block.id,
475
+ name: block.name,
476
+ input: block.input,
477
+ });
478
+ }
479
+ }
480
+ // Build message (use simple string if only text, otherwise structured content)
481
+ const firstBlock = contentBlocks[0];
482
+ const message = {
483
+ role: 'assistant',
484
+ content: contentBlocks.length === 1 && firstBlock?.type === 'text' ? textContent : contentBlocks,
485
+ };
486
+ // Map stop reason
487
+ const finishReason = this.mapStopReason(response.stop_reason || 'end_turn');
488
+ // Build IR response
489
+ const irResponse = {
490
+ message,
491
+ finishReason,
492
+ usage: {
493
+ promptTokens: response.usage.input_tokens,
494
+ completionTokens: response.usage.output_tokens,
495
+ totalTokens: response.usage.input_tokens + response.usage.output_tokens,
496
+ },
497
+ metadata: {
498
+ ...originalRequest.metadata,
499
+ providerResponseId: response.id, // Anthropic's msg_xxx ID
500
+ provenance: {
501
+ ...originalRequest.metadata.provenance,
502
+ backend: this.metadata.name,
503
+ },
504
+ custom: {
505
+ ...originalRequest.metadata.custom,
506
+ anthropicMessageId: response.id,
507
+ latencyMs,
508
+ },
509
+ },
510
+ raw: response,
511
+ };
512
+ return irResponse;
513
+ }
514
+ catch (error) {
515
+ throw new ai_matey_errors_1.AdapterConversionError({
516
+ code: ai_matey_errors_1.ErrorCode.ADAPTER_CONVERSION_ERROR,
517
+ message: `Failed to convert Anthropic response to IR: ${error instanceof Error ? error.message : String(error)}`,
518
+ provenance: {
519
+ backend: this.metadata.name,
520
+ },
521
+ cause: error instanceof Error ? error : undefined,
522
+ });
523
+ }
524
+ }
525
+ /**
526
+ * Convert IR message to Anthropic message.
527
+ */
528
+ convertMessageToAnthropic(message) {
529
+ // Anthropic only supports user/assistant roles (system handled separately)
530
+ if (message.role === 'system') {
531
+ throw new ai_matey_errors_1.AdapterConversionError({
532
+ code: ai_matey_errors_1.ErrorCode.ADAPTER_CONVERSION_ERROR,
533
+ message: 'System messages should be extracted before conversion to Anthropic format',
534
+ provenance: {
535
+ backend: this.metadata.name,
536
+ },
537
+ });
538
+ }
539
+ // Convert content
540
+ let content;
541
+ if (typeof message.content === 'string') {
542
+ content = message.content;
543
+ }
544
+ else {
545
+ // Convert content blocks
546
+ content = message.content.map((block) => {
547
+ switch (block.type) {
548
+ case 'text':
549
+ return { type: 'text', text: block.text };
550
+ case 'image':
551
+ if (block.source.type === 'url') {
552
+ return {
553
+ type: 'image',
554
+ source: { type: 'url', url: block.source.url },
555
+ };
556
+ }
557
+ else {
558
+ return {
559
+ type: 'image',
560
+ source: {
561
+ type: 'base64',
562
+ media_type: block.source.mediaType,
563
+ data: block.source.data,
564
+ },
565
+ };
566
+ }
567
+ case 'tool_use':
568
+ return {
569
+ type: 'tool_use',
570
+ id: block.id,
571
+ name: block.name,
572
+ input: block.input,
573
+ };
574
+ case 'tool_result':
575
+ return {
576
+ type: 'tool_result',
577
+ tool_use_id: block.toolUseId,
578
+ content: typeof block.content === 'string'
579
+ ? block.content
580
+ : block.content.map((c) => c.type === 'text'
581
+ ? { type: 'text', text: c.text }
582
+ : { type: 'text', text: '' }),
583
+ };
584
+ default:
585
+ // Fallback to text for unsupported types
586
+ return { type: 'text', text: JSON.stringify(block) };
587
+ }
588
+ });
589
+ }
590
+ return {
591
+ role: message.role,
592
+ content,
593
+ };
594
+ }
595
+ /**
596
+ * Map Anthropic stop reason to IR finish reason.
597
+ */
598
+ mapStopReason(stopReason) {
599
+ switch (stopReason) {
600
+ case 'end_turn':
601
+ return 'stop';
602
+ case 'max_tokens':
603
+ return 'length';
604
+ case 'stop_sequence':
605
+ return 'stop';
606
+ case 'tool_use':
607
+ return 'tool_calls';
608
+ default:
609
+ return 'stop';
610
+ }
611
+ }
612
+ /**
613
+ * Make HTTP request to Anthropic API.
614
+ */
615
+ async makeRequest(request, signal) {
616
+ try {
617
+ const response = await fetch(`${this.baseURL}/messages`, {
618
+ method: 'POST',
619
+ headers: this.getHeaders(),
620
+ body: JSON.stringify(request),
621
+ signal,
622
+ });
623
+ if (!response.ok) {
624
+ const errorBody = await response.text();
625
+ throw (0, ai_matey_errors_1.createErrorFromHttpResponse)(response.status, response.statusText, errorBody, {
626
+ backend: this.metadata.name,
627
+ });
628
+ }
629
+ const data = (await response.json());
630
+ return data;
631
+ }
632
+ catch (error) {
633
+ if (error instanceof TypeError && error.message.includes('fetch')) {
634
+ throw new ai_matey_errors_1.NetworkError({
635
+ code: ai_matey_errors_1.ErrorCode.NETWORK_ERROR,
636
+ message: `Network request failed: ${error.message}`,
637
+ provenance: {
638
+ backend: this.metadata.name,
639
+ },
640
+ cause: error,
641
+ });
642
+ }
643
+ throw error;
644
+ }
645
+ }
646
+ /**
647
+ * Get HTTP headers for Anthropic API requests.
648
+ */
649
+ getHeaders() {
650
+ const headers = {
651
+ 'Content-Type': 'application/json',
652
+ 'x-api-key': this.config.apiKey,
653
+ 'anthropic-version': '2023-06-01',
654
+ };
655
+ // Add dangerous browser access header if browserMode is enabled
656
+ if (this.config.browserMode) {
657
+ headers['anthropic-dangerous-direct-browser-access'] = 'true';
658
+ }
659
+ // Merge with custom headers (custom headers can override)
660
+ return { ...headers, ...this.config.headers };
661
+ }
662
+ }
663
+ exports.AnthropicBackendAdapter = AnthropicBackendAdapter;
664
+ //# sourceMappingURL=anthropic.js.map