genai-lite 0.10.0 → 0.12.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 (33) hide show
  1. package/README.md +30 -2
  2. package/dist/adapters/image/GenaiElectronImageAdapter.d.ts +1 -0
  3. package/dist/adapters/image/GenaiElectronImageAdapter.js +26 -9
  4. package/dist/adapters/image/MockImageAdapter.d.ts +1 -0
  5. package/dist/adapters/image/OpenAIImageAdapter.d.ts +1 -0
  6. package/dist/adapters/image/OpenAIImageAdapter.js +22 -7
  7. package/dist/image/ImageService.d.ts +11 -0
  8. package/dist/image/ImageService.js +80 -32
  9. package/dist/image/config.js +5 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/llm/LLMService.d.ts +23 -1
  12. package/dist/llm/LLMService.js +326 -186
  13. package/dist/llm/clients/AnthropicClientAdapter.d.ts +6 -1
  14. package/dist/llm/clients/AnthropicClientAdapter.js +271 -87
  15. package/dist/llm/clients/GeminiClientAdapter.d.ts +8 -1
  16. package/dist/llm/clients/GeminiClientAdapter.js +216 -59
  17. package/dist/llm/clients/LlamaCppClientAdapter.d.ts +7 -1
  18. package/dist/llm/clients/LlamaCppClientAdapter.js +303 -125
  19. package/dist/llm/clients/MistralClientAdapter.d.ts +8 -1
  20. package/dist/llm/clients/MistralClientAdapter.js +222 -41
  21. package/dist/llm/clients/MockClientAdapter.d.ts +2 -1
  22. package/dist/llm/clients/MockClientAdapter.js +51 -0
  23. package/dist/llm/clients/OpenAIClientAdapter.d.ts +6 -1
  24. package/dist/llm/clients/OpenAIClientAdapter.js +200 -72
  25. package/dist/llm/clients/OpenRouterClientAdapter.d.ts +6 -1
  26. package/dist/llm/clients/OpenRouterClientAdapter.js +242 -109
  27. package/dist/llm/clients/types.d.ts +9 -1
  28. package/dist/llm/types.d.ts +28 -0
  29. package/dist/llm/types.js +1 -0
  30. package/dist/shared/adapters/errorUtils.d.ts +2 -1
  31. package/dist/shared/adapters/errorUtils.js +56 -18
  32. package/dist/types/image.d.ts +33 -0
  33. package/package.json +7 -4
@@ -85,116 +85,23 @@ class LLMService {
85
85
  async sendMessage(request, callOptions) {
86
86
  this.logger.info(`LLMService.sendMessage called with presetId: ${request.presetId}, provider: ${request.providerId}, model: ${request.modelId}`);
87
87
  try {
88
- // Resolve model information from preset or direct IDs
89
- const resolved = await this.modelResolver.resolve(request);
90
- if (resolved.error) {
91
- return resolved.error;
92
- }
93
- const { providerId, modelId, modelInfo, settings: resolvedSettings } = resolved;
94
- // Create a proper LLMChatRequest with resolved values
95
- const resolvedRequest = {
96
- ...request,
97
- providerId: providerId,
98
- modelId: modelId,
99
- };
100
- // Validate basic request structure
101
- const structureValidationResult = this.requestValidator.validateRequestStructure(resolvedRequest);
102
- if (structureValidationResult) {
103
- return structureValidationResult;
104
- }
105
- // Validate settings if provided
106
- const combinedSettings = { ...resolvedSettings, ...request.settings };
107
- if (combinedSettings) {
108
- const settingsValidation = this.requestValidator.validateSettings(combinedSettings, providerId, modelId);
109
- if (settingsValidation) {
110
- return settingsValidation;
111
- }
112
- }
113
- // Apply model-specific defaults and merge with user settings
114
- const finalSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, combinedSettings, modelInfo);
115
- // Validate reasoning settings for model capabilities
116
- const reasoningValidation = this.requestValidator.validateReasoningSettings(modelInfo, finalSettings.reasoning, resolvedRequest);
117
- if (reasoningValidation) {
118
- return reasoningValidation;
88
+ const preparedResult = await this.prepareRequest(request, callOptions);
89
+ if ("error" in preparedResult) {
90
+ return preparedResult.error;
119
91
  }
120
- // Validate structured output settings for model capabilities
121
- const structuredOutputValidation = this.requestValidator.validateStructuredOutputSettings(modelInfo, finalSettings.structuredOutput, resolvedRequest);
122
- if (structuredOutputValidation) {
123
- return structuredOutputValidation;
124
- }
125
- // Get provider info for parameter filtering
126
- const providerInfo = (0, config_1.getProviderById)(providerId);
127
- if (!providerInfo) {
128
- return {
129
- provider: providerId,
130
- model: modelId,
131
- error: {
132
- message: `Provider information not found: ${providerId}`,
133
- code: "PROVIDER_ERROR",
134
- type: "server_error",
135
- },
136
- object: "error",
137
- };
138
- }
139
- // Filter out unsupported parameters
140
- const filteredSettings = this.settingsManager.filterUnsupportedParameters(finalSettings, modelInfo, providerInfo);
141
- const internalRequest = {
142
- ...resolvedRequest,
143
- settings: filteredSettings,
144
- };
145
- this.logger.debug(`Processing LLM request with (potentially filtered) settings:`, {
146
- provider: providerId,
147
- model: modelId,
148
- settings: filteredSettings,
149
- messageCount: resolvedRequest.messages.length,
150
- });
151
- // Get client adapter
152
- const clientAdapter = this.adapterRegistry.getAdapter(providerId);
153
- // Use ApiKeyProvider to get the API key and make the request
92
+ const prepared = preparedResult.prepared;
154
93
  try {
155
- const apiKey = await this.getApiKey(providerId);
156
- if (!apiKey) {
157
- return {
158
- provider: providerId,
159
- model: modelId,
160
- error: {
161
- message: `API key for provider '${providerId}' could not be retrieved. Ensure your ApiKeyProvider is configured correctly.`,
162
- code: "API_KEY_ERROR",
163
- type: "authentication_error",
164
- },
165
- object: "error",
166
- };
167
- }
168
- // Validate API key format if adapter supports it
169
- if (clientAdapter.validateApiKey && !clientAdapter.validateApiKey(apiKey)) {
170
- return {
171
- provider: providerId,
172
- model: modelId,
173
- error: {
174
- message: `Invalid API key format for provider '${providerId}'. Please check your API key.`,
175
- code: "INVALID_API_KEY",
176
- type: "authentication_error",
177
- },
178
- object: "error",
179
- };
180
- }
181
- this.logger.info(`Making LLM request with ${clientAdapter.constructor.name} for provider: ${providerId}`);
94
+ this.logger.info(`Making LLM request with ${prepared.clientAdapter.constructor.name} for provider: ${prepared.providerId}`);
182
95
  // Unified retry layer: adapters never throw, so retry decisions are made on
183
96
  // RETURNED failure responses. SDK-internal retries are disabled (maxRetries: 0
184
97
  // at client construction) — this loop is the single owner of retry behavior.
185
- const adapterOptions = {
186
- ...(callOptions?.signal && { signal: callOptions.signal }),
187
- ...((callOptions?.timeoutMs ?? this.defaultTimeoutMs) !== undefined && {
188
- timeoutMs: callOptions?.timeoutMs ?? this.defaultTimeoutMs,
189
- }),
190
- };
191
98
  const retryOnTimeout = this.retryOptions?.retryOnTimeout ?? true;
192
99
  const retryableCodes = new Set([
193
100
  types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED,
194
101
  types_1.ADAPTER_ERROR_CODES.NETWORK_ERROR,
195
102
  ...(retryOnTimeout ? [types_1.ADAPTER_ERROR_CODES.REQUEST_TIMEOUT] : []),
196
103
  ]);
197
- const result = await (0, withRetry_1.withRetry)(() => clientAdapter.sendMessage(internalRequest, apiKey, adapterOptions), (res) => {
104
+ const result = await (0, withRetry_1.withRetry)(() => prepared.clientAdapter.sendMessage(prepared.internalRequest, prepared.apiKey, prepared.adapterOptions), (res) => {
198
105
  if (res.object !== 'error') {
199
106
  return { retry: false };
200
107
  }
@@ -212,99 +119,19 @@ class LLMService {
212
119
  }),
213
120
  signal: callOptions?.signal,
214
121
  logger: this.logger,
215
- label: `${providerId}/${modelId}`,
122
+ label: `${prepared.providerId}/${prepared.modelId}`,
216
123
  });
217
- // Post-process for thinking tag fallback
218
- // This feature extracts reasoning from XML tags when native reasoning is not active.
219
- // It's a fallback mechanism for models without native reasoning or when native is disabled.
220
- const fallbackSettings = internalRequest.settings.thinkingTagFallback;
221
- if (result.object === 'chat.completion' && fallbackSettings && fallbackSettings.enabled !== false) {
222
- const tagName = fallbackSettings.tagName || 'thinking';
223
- // Check if native reasoning is active for this request
224
- const isNativeReasoningActive = modelInfo.reasoning?.supported === true &&
225
- (internalRequest.settings.reasoning?.enabled === true ||
226
- (modelInfo.reasoning?.enabledByDefault === true &&
227
- internalRequest.settings.reasoning?.enabled !== false) ||
228
- modelInfo.reasoning?.canDisable === false);
229
- // Process the response - extract thinking tags if present
230
- const choice = result.choices[0];
231
- if (choice?.message?.content) {
232
- const { extracted, remaining } = (0, parser_1.extractInitialTaggedContent)(choice.message.content, tagName);
233
- if (extracted !== null) {
234
- // Success: thinking tag found
235
- this.logger.debug(`Extracted <${tagName}> block from response.`);
236
- // Handle the edge case: append to existing reasoning if present (e.g., native reasoning + thinking tags)
237
- const existingReasoning = choice.reasoning || '';
238
- if (existingReasoning) {
239
- // Use a neutral markdown header that works for any consumer (human or AI)
240
- choice.reasoning = `${existingReasoning}\n\n#### Additional Reasoning\n\n${extracted}`;
241
- }
242
- else {
243
- // No existing reasoning, just use the extracted content directly
244
- choice.reasoning = extracted;
245
- }
246
- choice.message.content = remaining;
247
- }
248
- else {
249
- // Tag was not found
250
- // Enforce only if: (1) enforce: true AND (2) native reasoning is NOT active
251
- if (fallbackSettings.enforce === true && !isNativeReasoningActive) {
252
- const nativeReasoningCapable = modelInfo.reasoning?.supported === true;
253
- return {
254
- provider: providerId,
255
- model: modelId,
256
- error: {
257
- message: `Model response missing required <${tagName}> tags.`,
258
- code: "THINKING_TAGS_MISSING",
259
- type: "validation_error",
260
- param: nativeReasoningCapable && !isNativeReasoningActive
261
- ? `You disabled native reasoning for this model (${modelId}). ` +
262
- `To see its reasoning, you must prompt it to use <${tagName}> tags. ` +
263
- `Example: "Write your step-by-step reasoning in <${tagName}> tags before answering."`
264
- : `This model (${modelId}) does not support native reasoning. ` +
265
- `To get reasoning, you must prompt it to use <${tagName}> tags. ` +
266
- `Example: "Write your step-by-step reasoning in <${tagName}> tags before answering."`,
267
- },
268
- object: "error",
269
- partialResponse: {
270
- id: result.id,
271
- provider: result.provider,
272
- model: result.model,
273
- created: result.created,
274
- choices: result.choices,
275
- usage: result.usage
276
- }
277
- };
278
- }
279
- // If enforce: false or native reasoning is active, do nothing
280
- }
281
- }
282
- }
283
- // Post-process for structured output auto-parsing
284
- // Parse JSON response when structuredOutput is enabled and autoParse is not disabled
285
- const structuredOutputSettings = internalRequest.settings.structuredOutput;
286
- if (result.object === 'chat.completion' && structuredOutputSettings &&
287
- structuredOutputSettings.enabled !== false && structuredOutputSettings.autoParse !== false) {
288
- for (const choice of result.choices) {
289
- if (choice.message?.content) {
290
- try {
291
- choice.parsedContent = JSON.parse(choice.message.content);
292
- }
293
- catch (e) {
294
- choice.parseError = `JSON parse failed: ${e instanceof Error ? e.message : String(e)}`;
295
- this.logger.warn(`Failed to parse structured output for choice ${choice.index}: ${choice.parseError}`);
296
- }
297
- }
298
- }
124
+ const processedResult = this.postProcessResponse(result, prepared);
125
+ if (processedResult.object === "chat.completion") {
126
+ this.logger.info(`LLM request completed successfully for model: ${prepared.modelId}`);
299
127
  }
300
- this.logger.info(`LLM request completed successfully for model: ${modelId}`);
301
- return result;
128
+ return processedResult;
302
129
  }
303
130
  catch (error) {
304
131
  this.logger.error("Error in LLMService.sendMessage:", error);
305
132
  return {
306
- provider: providerId,
307
- model: modelId,
133
+ provider: prepared.providerId,
134
+ model: prepared.modelId,
308
135
  error: {
309
136
  message: error instanceof Error
310
137
  ? error.message
@@ -334,6 +161,319 @@ class LLMService {
334
161
  };
335
162
  }
336
163
  }
164
+ /**
165
+ * Streams a chat message from an LLM provider.
166
+ *
167
+ * The final `complete` event contains the same normalized response shape as
168
+ * sendMessage(). Validation/API key failures and unsupported streaming adapters
169
+ * are yielded as a single `error` event.
170
+ */
171
+ async *streamMessage(request, callOptions) {
172
+ this.logger.info(`LLMService.streamMessage called with presetId: ${request.presetId}, provider: ${request.providerId}, model: ${request.modelId}`);
173
+ try {
174
+ const preparedResult = await this.prepareRequest(request, callOptions);
175
+ if ("error" in preparedResult) {
176
+ yield { type: "error", error: preparedResult.error };
177
+ return;
178
+ }
179
+ const prepared = preparedResult.prepared;
180
+ if (!prepared.clientAdapter.streamMessage) {
181
+ yield {
182
+ type: "error",
183
+ error: {
184
+ provider: prepared.providerId,
185
+ model: prepared.modelId,
186
+ error: {
187
+ message: `Streaming is not supported for provider '${prepared.providerId}'.`,
188
+ code: "PROVIDER_ERROR",
189
+ type: "unsupported_feature",
190
+ },
191
+ object: "error",
192
+ },
193
+ };
194
+ return;
195
+ }
196
+ try {
197
+ for await (const event of prepared.clientAdapter.streamMessage(prepared.internalRequest, prepared.apiKey, prepared.adapterOptions)) {
198
+ if (event.type === "complete") {
199
+ const processedResult = this.postProcessResponse(event.response, prepared);
200
+ if (processedResult.object === "error") {
201
+ yield { type: "error", error: processedResult };
202
+ }
203
+ else {
204
+ yield { type: "complete", response: processedResult };
205
+ }
206
+ continue;
207
+ }
208
+ yield event;
209
+ }
210
+ }
211
+ catch (error) {
212
+ this.logger.error("Error in LLMService.streamMessage:", error);
213
+ yield {
214
+ type: "error",
215
+ error: {
216
+ provider: prepared.providerId,
217
+ model: prepared.modelId,
218
+ error: {
219
+ message: error instanceof Error
220
+ ? error.message
221
+ : "An unknown error occurred during message streaming.",
222
+ code: "PROVIDER_ERROR",
223
+ type: "server_error",
224
+ providerError: error,
225
+ },
226
+ object: "error",
227
+ },
228
+ };
229
+ }
230
+ }
231
+ catch (error) {
232
+ this.logger.error("Error in LLMService.streamMessage (outer):", error);
233
+ yield {
234
+ type: "error",
235
+ error: {
236
+ provider: request.providerId || request.presetId || 'unknown',
237
+ model: request.modelId || request.presetId || 'unknown',
238
+ error: {
239
+ message: error instanceof Error
240
+ ? error.message
241
+ : "An unknown error occurred.",
242
+ code: "UNEXPECTED_ERROR",
243
+ type: "server_error",
244
+ providerError: error,
245
+ },
246
+ object: "error",
247
+ },
248
+ };
249
+ }
250
+ }
251
+ async prepareRequest(request, callOptions) {
252
+ // Resolve model information from preset or direct IDs
253
+ const resolved = await this.modelResolver.resolve(request);
254
+ if (resolved.error) {
255
+ return { error: resolved.error };
256
+ }
257
+ const { providerId, modelId, modelInfo, settings: resolvedSettings } = resolved;
258
+ // Create a proper LLMChatRequest with resolved values
259
+ const resolvedRequest = {
260
+ ...request,
261
+ providerId: providerId,
262
+ modelId: modelId,
263
+ };
264
+ // Validate basic request structure
265
+ const structureValidationResult = this.requestValidator.validateRequestStructure(resolvedRequest);
266
+ if (structureValidationResult) {
267
+ return { error: structureValidationResult };
268
+ }
269
+ // Validate settings if provided
270
+ const combinedSettings = { ...resolvedSettings, ...request.settings };
271
+ if (combinedSettings) {
272
+ const settingsValidation = this.requestValidator.validateSettings(combinedSettings, providerId, modelId);
273
+ if (settingsValidation) {
274
+ return { error: settingsValidation };
275
+ }
276
+ }
277
+ // Apply model-specific defaults and merge with user settings
278
+ const finalSettings = this.settingsManager.mergeSettingsForModel(modelId, providerId, combinedSettings, modelInfo);
279
+ // Validate reasoning settings for model capabilities
280
+ const reasoningValidation = this.requestValidator.validateReasoningSettings(modelInfo, finalSettings.reasoning, resolvedRequest);
281
+ if (reasoningValidation) {
282
+ return { error: reasoningValidation };
283
+ }
284
+ // Validate structured output settings for model capabilities
285
+ const structuredOutputValidation = this.requestValidator.validateStructuredOutputSettings(modelInfo, finalSettings.structuredOutput, resolvedRequest);
286
+ if (structuredOutputValidation) {
287
+ return { error: structuredOutputValidation };
288
+ }
289
+ // Get provider info for parameter filtering
290
+ const providerInfo = (0, config_1.getProviderById)(providerId);
291
+ if (!providerInfo) {
292
+ return {
293
+ error: {
294
+ provider: providerId,
295
+ model: modelId,
296
+ error: {
297
+ message: `Provider information not found: ${providerId}`,
298
+ code: "PROVIDER_ERROR",
299
+ type: "server_error",
300
+ },
301
+ object: "error",
302
+ },
303
+ };
304
+ }
305
+ // Filter out unsupported parameters
306
+ const filteredSettings = this.settingsManager.filterUnsupportedParameters(finalSettings, modelInfo, providerInfo);
307
+ const internalRequest = {
308
+ ...resolvedRequest,
309
+ settings: filteredSettings,
310
+ };
311
+ this.logger.debug(`Processing LLM request with (potentially filtered) settings:`, {
312
+ provider: providerId,
313
+ model: modelId,
314
+ settings: filteredSettings,
315
+ messageCount: resolvedRequest.messages.length,
316
+ });
317
+ // Get client adapter
318
+ const clientAdapter = this.adapterRegistry.getAdapter(providerId);
319
+ try {
320
+ const apiKey = await this.getApiKey(providerId);
321
+ if (!apiKey) {
322
+ return {
323
+ error: {
324
+ provider: providerId,
325
+ model: modelId,
326
+ error: {
327
+ message: `API key for provider '${providerId}' could not be retrieved. Ensure your ApiKeyProvider is configured correctly.`,
328
+ code: "API_KEY_ERROR",
329
+ type: "authentication_error",
330
+ },
331
+ object: "error",
332
+ },
333
+ };
334
+ }
335
+ // Validate API key format if adapter supports it
336
+ if (clientAdapter.validateApiKey && !clientAdapter.validateApiKey(apiKey)) {
337
+ return {
338
+ error: {
339
+ provider: providerId,
340
+ model: modelId,
341
+ error: {
342
+ message: `Invalid API key format for provider '${providerId}'. Please check your API key.`,
343
+ code: "INVALID_API_KEY",
344
+ type: "authentication_error",
345
+ },
346
+ object: "error",
347
+ },
348
+ };
349
+ }
350
+ const adapterOptions = {
351
+ ...(callOptions?.signal && { signal: callOptions.signal }),
352
+ ...((callOptions?.timeoutMs ?? this.defaultTimeoutMs) !== undefined && {
353
+ timeoutMs: callOptions?.timeoutMs ?? this.defaultTimeoutMs,
354
+ }),
355
+ };
356
+ return {
357
+ prepared: {
358
+ providerId: providerId,
359
+ modelId: modelId,
360
+ modelInfo: modelInfo,
361
+ resolvedRequest,
362
+ internalRequest,
363
+ clientAdapter,
364
+ apiKey,
365
+ adapterOptions,
366
+ },
367
+ };
368
+ }
369
+ catch (error) {
370
+ this.logger.error("Error preparing LLM request:", error);
371
+ return {
372
+ error: {
373
+ provider: providerId,
374
+ model: modelId,
375
+ error: {
376
+ message: error instanceof Error
377
+ ? error.message
378
+ : "An unknown error occurred during request preparation.",
379
+ code: "PROVIDER_ERROR",
380
+ type: "server_error",
381
+ providerError: error,
382
+ },
383
+ object: "error",
384
+ },
385
+ };
386
+ }
387
+ }
388
+ postProcessResponse(result, prepared) {
389
+ if (result.object === "error") {
390
+ return result;
391
+ }
392
+ // Post-process for thinking tag fallback
393
+ // This feature extracts reasoning from XML tags when native reasoning is not active.
394
+ // It's a fallback mechanism for models without native reasoning or when native is disabled.
395
+ const fallbackSettings = prepared.internalRequest.settings.thinkingTagFallback;
396
+ if (fallbackSettings && fallbackSettings.enabled !== false) {
397
+ const tagName = fallbackSettings.tagName || 'thinking';
398
+ // Check if native reasoning is active for this request
399
+ const isNativeReasoningActive = prepared.modelInfo.reasoning?.supported === true &&
400
+ (prepared.internalRequest.settings.reasoning?.enabled === true ||
401
+ (prepared.modelInfo.reasoning?.enabledByDefault === true &&
402
+ prepared.internalRequest.settings.reasoning?.enabled !== false) ||
403
+ prepared.modelInfo.reasoning?.canDisable === false);
404
+ // Process the response - extract thinking tags if present
405
+ const choice = result.choices[0];
406
+ if (choice?.message?.content) {
407
+ const { extracted, remaining } = (0, parser_1.extractInitialTaggedContent)(choice.message.content, tagName);
408
+ if (extracted !== null) {
409
+ // Success: thinking tag found
410
+ this.logger.debug(`Extracted <${tagName}> block from response.`);
411
+ // Handle the edge case: append to existing reasoning if present (e.g., native reasoning + thinking tags)
412
+ const existingReasoning = choice.reasoning || '';
413
+ if (existingReasoning) {
414
+ // Use a neutral markdown header that works for any consumer (human or AI)
415
+ choice.reasoning = `${existingReasoning}\n\n#### Additional Reasoning\n\n${extracted}`;
416
+ }
417
+ else {
418
+ // No existing reasoning, just use the extracted content directly
419
+ choice.reasoning = extracted;
420
+ }
421
+ choice.message.content = remaining;
422
+ }
423
+ else {
424
+ // Tag was not found
425
+ // Enforce only if: (1) enforce: true AND (2) native reasoning is NOT active
426
+ if (fallbackSettings.enforce === true && !isNativeReasoningActive) {
427
+ const nativeReasoningCapable = prepared.modelInfo.reasoning?.supported === true;
428
+ return {
429
+ provider: prepared.providerId,
430
+ model: prepared.modelId,
431
+ error: {
432
+ message: `Model response missing required <${tagName}> tags.`,
433
+ code: "THINKING_TAGS_MISSING",
434
+ type: "validation_error",
435
+ param: nativeReasoningCapable && !isNativeReasoningActive
436
+ ? `You disabled native reasoning for this model (${prepared.modelId}). ` +
437
+ `To see its reasoning, you must prompt it to use <${tagName}> tags. ` +
438
+ `Example: "Write your step-by-step reasoning in <${tagName}> tags before answering."`
439
+ : `This model (${prepared.modelId}) does not support native reasoning. ` +
440
+ `To get reasoning, you must prompt it to use <${tagName}> tags. ` +
441
+ `Example: "Write your step-by-step reasoning in <${tagName}> tags before answering."`,
442
+ },
443
+ object: "error",
444
+ partialResponse: {
445
+ id: result.id,
446
+ provider: result.provider,
447
+ model: result.model,
448
+ created: result.created,
449
+ choices: result.choices,
450
+ usage: result.usage
451
+ }
452
+ };
453
+ }
454
+ // If enforce: false or native reasoning is active, do nothing
455
+ }
456
+ }
457
+ }
458
+ // Post-process for structured output auto-parsing
459
+ // Parse JSON response when structuredOutput is enabled and autoParse is not disabled
460
+ const structuredOutputSettings = prepared.internalRequest.settings.structuredOutput;
461
+ if (structuredOutputSettings &&
462
+ structuredOutputSettings.enabled !== false && structuredOutputSettings.autoParse !== false) {
463
+ for (const choice of result.choices) {
464
+ if (choice.message?.content) {
465
+ try {
466
+ choice.parsedContent = JSON.parse(choice.message.content);
467
+ }
468
+ catch (e) {
469
+ choice.parseError = `JSON parse failed: ${e instanceof Error ? e.message : String(e)}`;
470
+ this.logger.warn(`Failed to parse structured output for choice ${choice.index}: ${choice.parseError}`);
471
+ }
472
+ }
473
+ }
474
+ }
475
+ return result;
476
+ }
337
477
  /**
338
478
  * Gets all configured model presets
339
479
  *
@@ -1,4 +1,4 @@
1
- import type { LLMResponse, LLMFailureResponse } from "../types";
1
+ import type { LLMResponse, LLMFailureResponse, LLMStreamEvent } from "../types";
2
2
  import type { ILLMClientAdapter, InternalLLMChatRequest, AdapterRequestOptions } from "./types";
3
3
  import type { Logger } from "../../logging/types";
4
4
  /**
@@ -33,6 +33,7 @@ export declare class AnthropicClientAdapter implements ILLMClientAdapter {
33
33
  * @returns Promise resolving to success or failure response
34
34
  */
35
35
  sendMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): Promise<LLMResponse | LLMFailureResponse>;
36
+ streamMessage(request: InternalLLMChatRequest, apiKey: string, options?: AdapterRequestOptions): AsyncIterable<LLMStreamEvent>;
36
37
  /**
37
38
  * Validates Anthropic API key format
38
39
  *
@@ -48,6 +49,10 @@ export declare class AnthropicClientAdapter implements ILLMClientAdapter {
48
49
  name: string;
49
50
  version: string;
50
51
  };
52
+ private prepareMessageRequest;
53
+ private mapAnthropicUsage;
54
+ private mergeAnthropicUsage;
55
+ private createSyntheticMessage;
51
56
  /**
52
57
  * Recursively adds additionalProperties: false to all object schemas
53
58
  * Required by Anthropic's strict mode for structured outputs