@jaypie/llm 1.3.11 → 1.3.12

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.
package/dist/esm/index.js CHANGED
@@ -34,6 +34,14 @@ const MODEL = {
34
34
  SONNET: "claude-sonnet-5",
35
35
  HAIKU: "claude-haiku-4-5",
36
36
  MYTHOS: "claude-mythos-5",
37
+ // Fireworks (serverless open models; ids provided by operator)
38
+ FIREWORKS: {
39
+ DEEPSEEK: "accounts/fireworks/models/deepseek-v4-pro",
40
+ GLM: "accounts/fireworks/models/glm-5p2",
41
+ KIMI: "accounts/fireworks/models/kimi-k2p7-code",
42
+ MINIMAX: "accounts/fireworks/models/minimax-m2p7",
43
+ QWEN: "accounts/fireworks/models/qwen3p7-plus",
44
+ },
37
45
  // Google
38
46
  GEMINI_FLASH: "gemini-3.5-flash",
39
47
  GEMINI_FLASH_LITE: "gemini-3.1-flash-lite",
@@ -138,6 +146,14 @@ const PROVIDER = {
138
146
  SCHEMA_VERSION: "v2",
139
147
  },
140
148
  },
149
+ FIREWORKS: {
150
+ // https://docs.fireworks.ai/
151
+ API_KEY: "FIREWORKS_API_KEY",
152
+ BASE_URL: "https://api.fireworks.ai/inference/v1",
153
+ DEFAULT: MODEL.FIREWORKS.GLM,
154
+ MODEL_MATCH_WORDS: ["fireworks"],
155
+ NAME: "fireworks",
156
+ },
141
157
  /** @deprecated Use PROVIDER.GOOGLE — "Google" is the provider; Gemini is the model family */
142
158
  GEMINI: GOOGLE_PROVIDER,
143
159
  GOOGLE: GOOGLE_PROVIDER,
@@ -275,6 +291,14 @@ function determineModelProvider(input) {
275
291
  provider: PROVIDER.OPENROUTER.NAME,
276
292
  };
277
293
  }
294
+ // Check for explicit fireworks: prefix
295
+ if (input.startsWith("fireworks:")) {
296
+ const model = input.slice("fireworks:".length);
297
+ return {
298
+ model,
299
+ provider: PROVIDER.FIREWORKS.NAME,
300
+ };
301
+ }
278
302
  // Check for explicit bedrock: prefix
279
303
  if (input.startsWith("bedrock:")) {
280
304
  const model = input.slice("bedrock:".length);
@@ -290,6 +314,12 @@ function determineModelProvider(input) {
290
314
  provider: PROVIDER.BEDROCK.NAME,
291
315
  };
292
316
  }
317
+ if (input === PROVIDER.FIREWORKS.NAME) {
318
+ return {
319
+ model: PROVIDER.FIREWORKS.DEFAULT,
320
+ provider: PROVIDER.FIREWORKS.NAME,
321
+ };
322
+ }
293
323
  if (input === PROVIDER.ANTHROPIC.NAME) {
294
324
  return {
295
325
  model: PROVIDER.ANTHROPIC.DEFAULT,
@@ -322,6 +352,18 @@ function determineModelProvider(input) {
322
352
  }
323
353
  // Exact model ids are classified by the "/" rule and MODEL_MATCH_WORDS below,
324
354
  // so no per-provider id catalog is consulted here.
355
+ // Check Fireworks match words before the "/" rule — Fireworks ids contain
356
+ // "/" (e.g., "accounts/fireworks/models/glm-5p2") and would otherwise route
357
+ // to OpenRouter
358
+ const lowerInputEarly = input.toLowerCase();
359
+ for (const matchWord of PROVIDER.FIREWORKS.MODEL_MATCH_WORDS) {
360
+ if (lowerInputEarly.includes(matchWord)) {
361
+ return {
362
+ model: input,
363
+ provider: PROVIDER.FIREWORKS.NAME,
364
+ };
365
+ }
366
+ }
325
367
  // Assume OpenRouter for models containing "/" (e.g., "openai/gpt-4", "anthropic/claude-3-opus")
326
368
  // This check must come before match words so that "openai/gpt-4" is not matched by "openai" keyword
327
369
  if (input.includes("/")) {
@@ -573,6 +615,19 @@ const GEMINI_THINKING_BUDGET = {
573
615
  function toGeminiThinkingBudget(effort) {
574
616
  return { papered: false, value: GEMINI_THINKING_BUDGET[effort] };
575
617
  }
618
+ // Fireworks `reasoning_effort` — low | medium | high. Accepted on every model
619
+ // (the API no-ops where unsupported). No sub-low or top rung, so `lowest`
620
+ // collapses onto `low` and `highest` onto `high`.
621
+ const FIREWORKS_EFFORT = {
622
+ [EFFORT.LOWEST]: { papered: true, value: "low" },
623
+ [EFFORT.LOW]: { papered: false, value: "low" },
624
+ [EFFORT.MEDIUM]: { papered: false, value: "medium" },
625
+ [EFFORT.HIGH]: { papered: false, value: "high" },
626
+ [EFFORT.HIGHEST]: { papered: true, value: "high" },
627
+ };
628
+ function toFireworksEffort(effort) {
629
+ return FIREWORKS_EFFORT[effort];
630
+ }
576
631
  // OpenRouter `reasoning.effort` — accepts the full minimal..xhigh ladder and
577
632
  // maps to the routed provider's nearest supported level itself, so nothing is
578
633
  // papered here.
@@ -1059,7 +1114,7 @@ function jsonSchemaToOpenApi3(schema) {
1059
1114
  return result;
1060
1115
  }
1061
1116
 
1062
- const getLogger$6 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
1117
+ const getLogger$7 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
1063
1118
 
1064
1119
  //
1065
1120
  //
@@ -1715,7 +1770,7 @@ function classifyProviderError(error) {
1715
1770
  //
1716
1771
  // Constants
1717
1772
  //
1718
- const STRUCTURED_OUTPUT_TOOL_NAME$3 = "structured_output";
1773
+ const STRUCTURED_OUTPUT_TOOL_NAME$4 = "structured_output";
1719
1774
  /**
1720
1775
  * Whether `output_config.effort` may be sent for this model. Anthropic added
1721
1776
  * the effort control on the Claude 4.5 line and up (the 5 family); older models
@@ -1951,7 +2006,7 @@ const MODELS_WITHOUT_TEMPERATURE$2 = [
1951
2006
  /^claude-opus-4-[789]/,
1952
2007
  /^claude-opus-[5-9]/,
1953
2008
  ];
1954
- function isTemperatureDeprecationError$3(error) {
2009
+ function isTemperatureDeprecationError$4(error) {
1955
2010
  if (!error || typeof error !== "object")
1956
2011
  return false;
1957
2012
  const err = error;
@@ -1970,7 +2025,7 @@ function isTemperatureDeprecationError$3(error) {
1970
2025
  * field) is also explicitly excluded — that's a code-path bug, not a model
1971
2026
  * gap; it should propagate so we notice and fix it.
1972
2027
  */
1973
- function isStructuredOutputUnsupportedError$1(error) {
2028
+ function isStructuredOutputUnsupportedError$2(error) {
1974
2029
  if (!error || typeof error !== "object")
1975
2030
  return false;
1976
2031
  const err = error;
@@ -2103,7 +2158,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2103
2158
  if (useFallbackStructuredOutput && request.format) {
2104
2159
  log$2.warn(`[AnthropicAdapter] Using legacy structured_output tool fallback for model ${anthropicRequest.model}; native output_config previously rejected for this model.`);
2105
2160
  allTools.push({
2106
- name: STRUCTURED_OUTPUT_TOOL_NAME$3,
2161
+ name: STRUCTURED_OUTPUT_TOOL_NAME$4,
2107
2162
  description: "Output a structured JSON object, " +
2108
2163
  "use this before your final response to give structured outputs to the user",
2109
2164
  parameters: request.format,
@@ -2220,7 +2275,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2220
2275
  return undefined;
2221
2276
  // If the model rejected `temperature`, cache it and retry without the param
2222
2277
  if (anthropicRequest.temperature !== undefined &&
2223
- isTemperatureDeprecationError$3(error)) {
2278
+ isTemperatureDeprecationError$4(error)) {
2224
2279
  this.rememberModelRejectsTemperature(anthropicRequest.model);
2225
2280
  const retryRequest = { ...anthropicRequest };
2226
2281
  delete retryRequest.temperature;
@@ -2232,7 +2287,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2232
2287
  }
2233
2288
  // If the model rejected native structured output, cache it and retry
2234
2289
  // via the legacy fake-tool emulation path.
2235
- if (wantsStructuredOutput && isStructuredOutputUnsupportedError$1(error)) {
2290
+ if (wantsStructuredOutput && isStructuredOutputUnsupportedError$2(error)) {
2236
2291
  const model = anthropicRequest.model;
2237
2292
  this.rememberModelRejectsStructuredOutput(model);
2238
2293
  log$2.warn(`[AnthropicAdapter] Model ${model} rejected native output_config; falling back to legacy structured_output tool emulation.`);
@@ -2257,7 +2312,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2257
2312
  fallbackRequest.output_config = { effort: output_config.effort };
2258
2313
  }
2259
2314
  const fakeTool = {
2260
- name: STRUCTURED_OUTPUT_TOOL_NAME$3,
2315
+ name: STRUCTURED_OUTPUT_TOOL_NAME$4,
2261
2316
  description: "Output a structured JSON object, " +
2262
2317
  "use this before your final response to give structured outputs to the user",
2263
2318
  input_schema: {
@@ -2285,7 +2340,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2285
2340
  }
2286
2341
  catch (error) {
2287
2342
  if (streamRequest.temperature !== undefined &&
2288
- isTemperatureDeprecationError$3(error)) {
2343
+ isTemperatureDeprecationError$4(error)) {
2289
2344
  this.rememberModelRejectsTemperature(streamRequest.model);
2290
2345
  streamRequest = {
2291
2346
  ...streamRequest,
@@ -2548,7 +2603,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2548
2603
  // runtime has cached as not supporting `output_format`.
2549
2604
  const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
2550
2605
  return (lastBlock?.type === "tool_use" &&
2551
- lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$3);
2606
+ lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$4);
2552
2607
  }
2553
2608
  extractStructuredOutput(response) {
2554
2609
  const anthropicResponse = response;
@@ -2574,7 +2629,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
2574
2629
  // Fallback path: legacy fake-tool emulation
2575
2630
  const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
2576
2631
  if (lastBlock?.type === "tool_use" &&
2577
- lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$3) {
2632
+ lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$4) {
2578
2633
  return lastBlock.input;
2579
2634
  }
2580
2635
  return undefined;
@@ -2663,12 +2718,12 @@ function convertContentToBedrock(content) {
2663
2718
  //
2664
2719
  // Constants / helpers
2665
2720
  //
2666
- const STRUCTURED_OUTPUT_TOOL_NAME$2 = "structured_output";
2721
+ const STRUCTURED_OUTPUT_TOOL_NAME$3 = "structured_output";
2667
2722
  function isOutputConfigUnsupportedError(error) {
2668
2723
  const msg = error?.message ?? "";
2669
2724
  return /outputConfig|output_config/i.test(msg);
2670
2725
  }
2671
- function isTemperatureDeprecationError$2(error) {
2726
+ function isTemperatureDeprecationError$3(error) {
2672
2727
  const msg = error?.message ?? "";
2673
2728
  return /temperature.*deprecated|deprecated.*temperature/i.test(msg);
2674
2729
  }
@@ -2815,7 +2870,7 @@ class BedrockAdapter extends BaseProviderAdapter {
2815
2870
  if (this.useFakeToolForStructuredOutput(model)) {
2816
2871
  const fakeTool = {
2817
2872
  toolSpec: {
2818
- name: STRUCTURED_OUTPUT_TOOL_NAME$2,
2873
+ name: STRUCTURED_OUTPUT_TOOL_NAME$3,
2819
2874
  description: "REQUIRED: You MUST call this tool to provide your final response. " +
2820
2875
  "After gathering all necessary information (including results from other tools), " +
2821
2876
  "call this tool with the structured data to complete the request.",
@@ -2884,7 +2939,7 @@ class BedrockAdapter extends BaseProviderAdapter {
2884
2939
  }
2885
2940
  catch (error) {
2886
2941
  if (bedrockRequest.inferenceConfig?.temperature !== undefined &&
2887
- isTemperatureDeprecationError$2(error)) {
2942
+ isTemperatureDeprecationError$3(error)) {
2888
2943
  this.rememberModelRejectsTemperature(bedrockRequest.modelId || this.defaultModel);
2889
2944
  const retryRequest = {
2890
2945
  ...bedrockRequest,
@@ -2918,7 +2973,7 @@ class BedrockAdapter extends BaseProviderAdapter {
2918
2973
  }
2919
2974
  const fakeTool = {
2920
2975
  toolSpec: {
2921
- name: STRUCTURED_OUTPUT_TOOL_NAME$2,
2976
+ name: STRUCTURED_OUTPUT_TOOL_NAME$3,
2922
2977
  description: "REQUIRED: You MUST call this tool to provide your final response. " +
2923
2978
  "After gathering all necessary information (including results from other tools), " +
2924
2979
  "call this tool with the structured data to complete the request.",
@@ -3007,7 +3062,7 @@ class BedrockAdapter extends BaseProviderAdapter {
3007
3062
  // Don't surface structured_output fake tool as a real tool call
3008
3063
  const allToolUses = (bedrockResponse.output?.message?.content ?? []).filter((b) => "toolUse" in b);
3009
3064
  const hasOnlyStructuredOutputTool = allToolUses.length > 0 &&
3010
- allToolUses.every((b) => b.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
3065
+ allToolUses.every((b) => b.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$3);
3011
3066
  const hasToolCalls = bedrockResponse.stopReason === "tool_use" && !hasOnlyStructuredOutputTool;
3012
3067
  return {
3013
3068
  content,
@@ -3162,7 +3217,7 @@ class BedrockAdapter extends BaseProviderAdapter {
3162
3217
  const last = content[content.length - 1];
3163
3218
  return (!!last &&
3164
3219
  "toolUse" in last &&
3165
- last.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
3220
+ last.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$3);
3166
3221
  }
3167
3222
  extractStructuredOutput(response) {
3168
3223
  const bedrockResponse = response;
@@ -3180,7 +3235,7 @@ class BedrockAdapter extends BaseProviderAdapter {
3180
3235
  const last = content[content.length - 1];
3181
3236
  if (last &&
3182
3237
  "toolUse" in last &&
3183
- last.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$2) {
3238
+ last.toolUse.name === STRUCTURED_OUTPUT_TOOL_NAME$3) {
3184
3239
  return last.toolUse.input;
3185
3240
  }
3186
3241
  return undefined;
@@ -3209,128 +3264,965 @@ const bedrockAdapter = new BedrockAdapter();
3209
3264
  //
3210
3265
  // Constants
3211
3266
  //
3212
- const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
3213
- // Gemini 3 family supports combining tools (function calling) with native
3214
- // structured output via `responseJsonSchema`. Earlier Gemini families
3215
- // (including 2.5 thinking) do not support the combo and fall back to the
3216
- // legacy `structured_output` fake-tool emulation.
3217
- const GEMINI_3_PATTERN = /^gemini-3/;
3218
- // Reasoning-effort control differs by generation: Gemini 3.x takes the
3219
- // `thinkingLevel` enum, Gemini 2.5 takes an integer `thinkingBudget`. Sending
3220
- // the wrong one errors, and models outside these families have no thinking
3221
- // control, so effort is only applied when one of these matches.
3222
- const GEMINI_25_PATTERN = /^gemini-2\.5/;
3267
+ const STRUCTURED_OUTPUT_TOOL_NAME$2 = "structured_output";
3268
+ const STRUCTURED_OUTPUT_SCHEMA_NAME$1 = "response";
3223
3269
  /**
3224
- * Detect 4xx errors that indicate the model itself does not support the
3225
- * `responseJsonSchema` + tools combo. Triggers the runtime fallback to the
3226
- * fake-tool emulation path. Other 400s propagate.
3270
+ * Detect 4xx errors that indicate the model itself does not support
3271
+ * `response_format: json_schema`. We only trigger the fake-tool fallback when
3272
+ * the failure is plausibly a capability gap, not a generic 400.
3227
3273
  */
3228
- function isStructuredOutputComboUnsupportedError(error) {
3274
+ function isStructuredOutputUnsupportedError$1(error) {
3229
3275
  if (!error || typeof error !== "object")
3230
3276
  return false;
3231
3277
  const err = error;
3232
- const status = err.status ?? err.code;
3278
+ const status = err.status ?? err.statusCode;
3279
+ if (status !== 400 && status !== 422)
3280
+ return false;
3281
+ const messages = [err.message, err.error?.message].filter((m) => typeof m === "string");
3282
+ return messages.some((m) => /response[_ ]format|json[_ ]schema|structured[_ ]output/i.test(m));
3283
+ }
3284
+ function isTemperatureDeprecationError$2(error) {
3285
+ if (!error || typeof error !== "object")
3286
+ return false;
3287
+ const err = error;
3288
+ const status = err.status ?? err.statusCode;
3233
3289
  if (status !== 400)
3234
3290
  return false;
3235
3291
  const messages = [err.message, err.error?.message].filter((m) => typeof m === "string");
3236
- return messages.some((m) => /response[_ ]?json[_ ]?schema|response[_ ]?schema|response[_ ]?mime|function[_ ]?call|tools/i.test(m));
3292
+ return messages.some((m) => m.toLowerCase().includes("temperature"));
3293
+ }
3294
+ /**
3295
+ * Convert standardized content items to Fireworks format. Images become
3296
+ * `image_url` parts (vision models only; non-vision models 4xx — a
3297
+ * model-capability mismatch surfaced by the call). File inputs are warned
3298
+ * and discarded: Fireworks has no `file` part and rejects document `data:`
3299
+ * URIs outright, so there is no wire shape that can carry them.
3300
+ */
3301
+ function convertContentToFireworks(content) {
3302
+ if (typeof content === "string") {
3303
+ return content;
3304
+ }
3305
+ const parts = [];
3306
+ for (const item of content) {
3307
+ if (item.type === LlmMessageType.InputText) {
3308
+ parts.push({ type: "text", text: item.text });
3309
+ continue;
3310
+ }
3311
+ if (item.type === LlmMessageType.InputImage) {
3312
+ const url = item.image_url ?? "";
3313
+ if (!url) {
3314
+ log$2.warn("Fireworks image content missing image_url; image discarded");
3315
+ continue;
3316
+ }
3317
+ parts.push({ type: "image_url", imageUrl: { url } });
3318
+ continue;
3319
+ }
3320
+ if (item.type === LlmMessageType.InputFile) {
3321
+ log$2.warn({ filename: item.filename }, "Fireworks does not support file inputs (no file part; document data: URIs rejected); file discarded");
3322
+ continue;
3323
+ }
3324
+ // Unknown type - warn and skip
3325
+ log$2.warn({ item }, "Unknown content type for Fireworks; discarded");
3326
+ }
3327
+ // If no parts remain, return empty string to avoid empty array
3328
+ if (parts.length === 0) {
3329
+ return "";
3330
+ }
3331
+ return parts;
3332
+ }
3333
+ /**
3334
+ * Convert internal content parts to the OpenAI-compatible wire shape. The
3335
+ * internal representation uses camelCase keys (`imageUrl`); the REST API wants
3336
+ * snake_case (`image_url`).
3337
+ */
3338
+ function contentToWire$1(content) {
3339
+ if (content === null ||
3340
+ content === undefined ||
3341
+ typeof content === "string") {
3342
+ return content;
3343
+ }
3344
+ return content.map((part) => {
3345
+ if (part.type === "image_url") {
3346
+ return { type: "image_url", image_url: part.imageUrl };
3347
+ }
3348
+ return part;
3349
+ });
3350
+ }
3351
+ /**
3352
+ * Serialize an internal message to the OpenAI-compatible wire shape, mapping
3353
+ * camelCase tool fields (`toolCalls`, `toolCallId`) to snake_case. Tool-call
3354
+ * objects are already wire-shaped (`{ id, type, function: { name, arguments } }`).
3355
+ */
3356
+ function messageToWire$1(message) {
3357
+ const wire = { role: message.role };
3358
+ if (message.content !== undefined) {
3359
+ wire.content = contentToWire$1(message.content);
3360
+ }
3361
+ if (message.toolCalls) {
3362
+ wire.tool_calls = message.toolCalls;
3363
+ }
3364
+ if (message.toolCallId) {
3365
+ wire.tool_call_id = message.toolCallId;
3366
+ }
3367
+ return wire;
3368
+ }
3369
+ // Fireworks error types based on HTTP status codes
3370
+ const RETRYABLE_STATUS_CODES$2 = [408, 500, 502, 503, 524, 529];
3371
+ const RATE_LIMIT_STATUS_CODE$1 = 429;
3372
+ /**
3373
+ * Walk the JSON schema and force `additionalProperties: false` on every
3374
+ * object node. Required by the OpenAI-style json_schema response_format when
3375
+ * `strict: true`.
3376
+ */
3377
+ function enforceAdditionalPropertiesFalse$1(schema) {
3378
+ const stack = [schema];
3379
+ while (stack.length > 0) {
3380
+ const node = stack.pop();
3381
+ if (node.type === "object") {
3382
+ node.additionalProperties = false;
3383
+ }
3384
+ for (const value of Object.values(node)) {
3385
+ if (Array.isArray(value)) {
3386
+ for (const entry of value) {
3387
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
3388
+ stack.push(entry);
3389
+ }
3390
+ }
3391
+ }
3392
+ else if (value && typeof value === "object") {
3393
+ stack.push(value);
3394
+ }
3395
+ }
3396
+ }
3237
3397
  }
3238
- // Gemini uses HTTP status codes for error classification
3239
- // Documented at: https://ai.google.dev/api/rest/v1beta/Status
3240
- const RETRYABLE_STATUS_CODES$1 = [
3241
- 408, // Request Timeout
3242
- 429, // Too Many Requests (Rate Limit)
3243
- 500, // Internal Server Error
3244
- 502, // Bad Gateway
3245
- 503, // Service Unavailable
3246
- 504, // Gateway Timeout
3247
- ];
3248
- const NOT_RETRYABLE_STATUS_CODES = [
3249
- 400, // Bad Request
3250
- 401, // Unauthorized
3251
- 403, // Forbidden
3252
- 404, // Not Found
3253
- 409, // Conflict
3254
- 422, // Unprocessable Entity
3255
- ];
3256
3398
  //
3257
3399
  //
3258
3400
  // Main
3259
3401
  //
3260
3402
  /**
3261
- * GoogleAdapter implements the ProviderAdapter interface for Google's Gemini API.
3262
- * It handles request building, response parsing, and error classification
3263
- * specific to Gemini's generateContent API.
3403
+ * FireworksAdapter implements the ProviderAdapter interface for Fireworks AI's
3404
+ * OpenAI-compatible Chat Completions API. It handles request building,
3405
+ * response parsing, and error classification specific to Fireworks.
3264
3406
  */
3265
- class GoogleAdapter extends BaseProviderAdapter {
3407
+ class FireworksAdapter extends BaseProviderAdapter {
3266
3408
  constructor() {
3267
3409
  super(...arguments);
3268
- this.name = PROVIDER.GOOGLE.NAME;
3269
- this.defaultModel = PROVIDER.GOOGLE.DEFAULT;
3270
- // Session-level cache of Gemini 3 models observed to reject the native
3271
- // `responseJsonSchema` + tools combo. When a model is in this set,
3272
- // buildRequest engages the legacy fake-tool path instead.
3273
- this.runtimeNoStructuredOutputComboModels = new Set();
3410
+ this.name = PROVIDER.FIREWORKS.NAME;
3411
+ this.defaultModel = PROVIDER.FIREWORKS.DEFAULT;
3412
+ // Session-level cache of models observed to reject native
3413
+ // `response_format: json_schema`. When a model is in this set, buildRequest
3414
+ // engages the legacy fake-tool path instead of native structured output.
3415
+ this.runtimeNoStructuredOutputModels = new Set();
3416
+ // Session-level cache of models observed to reject `temperature`. Populated
3417
+ // by executeRequest on 400 errors so repeat calls skip the param.
3418
+ this.runtimeNoTemperatureModels = new Set();
3274
3419
  }
3275
- rememberModelRejectsStructuredOutputCombo(model) {
3276
- this.runtimeNoStructuredOutputComboModels.add(model);
3420
+ rememberModelRejectsStructuredOutput(model) {
3421
+ this.runtimeNoStructuredOutputModels.add(model);
3277
3422
  }
3278
- clearRuntimeNoStructuredOutputComboModels() {
3279
- this.runtimeNoStructuredOutputComboModels.clear();
3423
+ clearRuntimeNoStructuredOutputModels() {
3424
+ this.runtimeNoStructuredOutputModels.clear();
3280
3425
  }
3281
- supportsStructuredOutputCombo(model) {
3282
- if (this.runtimeNoStructuredOutputComboModels.has(model))
3283
- return false;
3284
- return GEMINI_3_PATTERN.test(model);
3426
+ supportsStructuredOutput(model) {
3427
+ return !this.runtimeNoStructuredOutputModels.has(model);
3428
+ }
3429
+ rememberModelRejectsTemperature(model) {
3430
+ this.runtimeNoTemperatureModels.add(model);
3431
+ }
3432
+ clearRuntimeNoTemperatureModels() {
3433
+ this.runtimeNoTemperatureModels.clear();
3434
+ }
3435
+ supportsTemperature(model) {
3436
+ return !this.runtimeNoTemperatureModels.has(model);
3285
3437
  }
3286
3438
  //
3287
3439
  // Request Building
3288
3440
  //
3289
3441
  buildRequest(request) {
3290
- // Convert messages to Gemini format (Content[])
3291
- const contents = this.convertMessagesToContents(request.messages);
3292
- const geminiRequest = {
3442
+ // Convert messages to Fireworks format (OpenAI-compatible)
3443
+ const messages = this.convertMessagesToFireworks(request.messages, request.system);
3444
+ // Append instructions to last message if provided
3445
+ if (request.instructions && messages.length > 0) {
3446
+ const lastMsg = messages[messages.length - 1];
3447
+ if (lastMsg.content && typeof lastMsg.content === "string") {
3448
+ lastMsg.content = lastMsg.content + "\n\n" + request.instructions;
3449
+ }
3450
+ }
3451
+ const fireworksRequest = {
3293
3452
  model: request.model || this.defaultModel,
3294
- contents,
3453
+ messages,
3295
3454
  };
3296
- // Add system instruction if provided
3297
- if (request.system) {
3298
- geminiRequest.config = {
3299
- ...geminiRequest.config,
3300
- systemInstruction: request.system,
3301
- };
3302
- }
3303
- // Append instructions to the last user message if provided
3304
- if (request.instructions && contents.length > 0) {
3305
- const lastContent = contents[contents.length - 1];
3306
- if (lastContent.role === "user" && lastContent.parts) {
3307
- const lastPart = lastContent.parts[lastContent.parts.length - 1];
3308
- if (lastPart.text) {
3309
- lastPart.text = lastPart.text + "\n\n" + request.instructions;
3310
- }
3311
- }
3455
+ if (request.user) {
3456
+ fireworksRequest.user = request.user;
3312
3457
  }
3313
- const hasUserTools = !!(request.tools && request.tools.length > 0);
3314
- const useNativeCombo = Boolean(request.format) &&
3315
- hasUserTools &&
3316
- this.supportsStructuredOutputCombo(geminiRequest.model);
3317
- // When tools+format are combined and the model does not support the native
3318
- // combo, inject the legacy `structured_output` fake tool here so the model
3319
- // is forced to call it before its final answer.
3458
+ // Fireworks rejects `response_format` combined with `tools` ("You cannot
3459
+ // specify response format and function call at the same time"), so a
3460
+ // request with both preemptively uses the structured_output tool
3461
+ // emulation — same approach as Gemini 2.5.
3462
+ const hasCallerTools = Boolean(request.tools?.length);
3463
+ const useFallbackStructuredOutput = Boolean(request.format) &&
3464
+ (hasCallerTools ||
3465
+ !this.supportsStructuredOutput(fireworksRequest.model));
3320
3466
  const allTools = request.tools
3321
3467
  ? [...request.tools]
3322
3468
  : [];
3323
- if (request.format && hasUserTools && !useNativeCombo) {
3324
- log$2.warn(`[GoogleAdapter] Using legacy structured_output tool fallback for model ${geminiRequest.model}; native responseJsonSchema + tools combo is only available on Gemini 3.`);
3469
+ if (useFallbackStructuredOutput && request.format) {
3470
+ log$2.warn(hasCallerTools
3471
+ ? `[FireworksAdapter] Fireworks does not support response_format combined with tools; using structured_output tool emulation for model ${fireworksRequest.model}.`
3472
+ : `[FireworksAdapter] Using legacy structured_output tool fallback for model ${fireworksRequest.model}; native response_format previously rejected for this model.`);
3325
3473
  allTools.push({
3326
- name: STRUCTURED_OUTPUT_TOOL_NAME$1,
3327
- description: "Output a structured JSON object, " +
3328
- "use this before your final response to give structured outputs to the user",
3474
+ name: STRUCTURED_OUTPUT_TOOL_NAME$2,
3475
+ description: "REQUIRED: You MUST call this tool to provide your final response. " +
3476
+ "After gathering all necessary information (including results from other tools), " +
3477
+ "call this tool with the structured data to complete the request.",
3329
3478
  parameters: request.format,
3330
3479
  });
3331
3480
  }
3332
3481
  if (allTools.length > 0) {
3333
- const functionDeclarations = allTools.map((tool) => ({
3482
+ fireworksRequest.tools = allTools.map((tool) => ({
3483
+ type: "function",
3484
+ function: {
3485
+ name: tool.name,
3486
+ description: tool.description,
3487
+ parameters: tool.parameters,
3488
+ },
3489
+ }));
3490
+ // Use "auto" for tool_choice - not all Fireworks models support "required"
3491
+ // The structured_output tool prompt already emphasizes it must be called
3492
+ fireworksRequest.tool_choice = "auto";
3493
+ }
3494
+ // Native structured output: send schema as `response_format`. The legacy
3495
+ // tool-emulation path is engaged only as a runtime fallback for models the
3496
+ // API has flagged as not supporting native json_schema.
3497
+ if (request.format && !useFallbackStructuredOutput) {
3498
+ fireworksRequest.response_format = {
3499
+ type: "json_schema",
3500
+ json_schema: {
3501
+ name: STRUCTURED_OUTPUT_SCHEMA_NAME$1,
3502
+ schema: request.format,
3503
+ strict: true,
3504
+ },
3505
+ };
3506
+ }
3507
+ if (request.providerOptions) {
3508
+ Object.assign(fireworksRequest, request.providerOptions);
3509
+ }
3510
+ // Normalized reasoning effort -> reasoning_effort. Fireworks accepts the
3511
+ // param on every model and no-ops where unsupported, so no per-model
3512
+ // gating. First-class effort wins over providerOptions.
3513
+ if (request.effort) {
3514
+ const mapping = toFireworksEffort(request.effort);
3515
+ if (mapping.papered) {
3516
+ log$2.debug(paperedEffortMessage({
3517
+ model: fireworksRequest.model,
3518
+ provider: this.name,
3519
+ requested: request.effort,
3520
+ value: mapping.value,
3521
+ }));
3522
+ }
3523
+ fireworksRequest.reasoning_effort = mapping.value;
3524
+ }
3525
+ // First-class temperature takes precedence over providerOptions
3526
+ if (request.temperature !== undefined) {
3527
+ fireworksRequest.temperature =
3528
+ request.temperature;
3529
+ }
3530
+ // Strip temperature for models the runtime has cached as rejecting it
3531
+ const requestRecord = fireworksRequest;
3532
+ if (requestRecord.temperature !== undefined &&
3533
+ !this.supportsTemperature(fireworksRequest.model)) {
3534
+ delete requestRecord.temperature;
3535
+ }
3536
+ return fireworksRequest;
3537
+ }
3538
+ formatTools(toolkit,
3539
+ // outputSchema is part of the interface contract but Fireworks uses native
3540
+ // `response_format` (set in buildRequest); the legacy fake-tool injection
3541
+ // happens in buildRequest only as a runtime fallback.
3542
+ _outputSchema) {
3543
+ return toolkit.tools.map((tool) => ({
3544
+ name: tool.name,
3545
+ description: tool.description,
3546
+ parameters: tool.parameters,
3547
+ }));
3548
+ }
3549
+ formatOutputSchema(schema) {
3550
+ let jsonSchema;
3551
+ // Check if schema is already a JsonObject — either the OpenAI-style
3552
+ // `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
3553
+ if ((typeof schema === "object" &&
3554
+ schema !== null &&
3555
+ !Array.isArray(schema) &&
3556
+ schema.type === "json_schema") ||
3557
+ isJsonSchema$1(schema)) {
3558
+ jsonSchema = structuredClone(schema);
3559
+ jsonSchema.type = "object"; // Normalize type
3560
+ }
3561
+ else {
3562
+ // Convert NaturalSchema to JSON schema through Zod. Re-spread into a
3563
+ // plain object to drop Zod v4's non-enumerable `~standard` marker.
3564
+ const zodSchema = schema instanceof z.ZodType
3565
+ ? schema
3566
+ : naturalZodSchema(schema);
3567
+ jsonSchema = { ...z.toJSONSchema(zodSchema) };
3568
+ }
3569
+ // Remove $schema property (can cause issues with some providers)
3570
+ if (jsonSchema.$schema) {
3571
+ delete jsonSchema.$schema;
3572
+ }
3573
+ // Strict json_schema response_format requires additionalProperties: false
3574
+ // on every object.
3575
+ enforceAdditionalPropertiesFalse$1(jsonSchema);
3576
+ return jsonSchema;
3577
+ }
3578
+ //
3579
+ // API Execution
3580
+ //
3581
+ async executeRequest(client, request, signal) {
3582
+ const fireworks = client;
3583
+ const fireworksRequest = request;
3584
+ const wantsStructuredOutput = Boolean(fireworksRequest.response_format);
3585
+ try {
3586
+ const response = (await fireworks.chatCompletion(this.toWireBody(fireworksRequest), signal ? { signal } : undefined));
3587
+ if (wantsStructuredOutput) {
3588
+ response.__jaypieStructuredOutput = true;
3589
+ }
3590
+ return response;
3591
+ }
3592
+ catch (error) {
3593
+ if (signal?.aborted)
3594
+ return undefined;
3595
+ // If the model rejected `temperature`, cache it and retry without the param.
3596
+ const requestRecord = fireworksRequest;
3597
+ if (requestRecord.temperature !== undefined &&
3598
+ isTemperatureDeprecationError$2(error)) {
3599
+ this.rememberModelRejectsTemperature(fireworksRequest.model);
3600
+ const retryRequest = { ...fireworksRequest };
3601
+ delete retryRequest.temperature;
3602
+ const response = (await fireworks.chatCompletion(this.toWireBody(retryRequest), signal ? { signal } : undefined));
3603
+ if (wantsStructuredOutput) {
3604
+ response.__jaypieStructuredOutput = true;
3605
+ }
3606
+ return response;
3607
+ }
3608
+ // If the model rejected `response_format`, cache it and retry with the
3609
+ // legacy fake-tool emulation path.
3610
+ if (wantsStructuredOutput && isStructuredOutputUnsupportedError$1(error)) {
3611
+ const model = fireworksRequest.model;
3612
+ this.rememberModelRejectsStructuredOutput(model);
3613
+ log$2.warn(`[FireworksAdapter] Model ${model} rejected native response_format; falling back to legacy structured_output tool emulation.`);
3614
+ const fallbackRequest = this.toFallbackStructuredOutputRequest(fireworksRequest);
3615
+ return (await fireworks.chatCompletion(this.toWireBody(fallbackRequest), signal ? { signal } : undefined));
3616
+ }
3617
+ throw error;
3618
+ }
3619
+ }
3620
+ /**
3621
+ * Serialize the internal request into the OpenAI-compatible wire body for
3622
+ * Fireworks' Chat Completions endpoint. Top-level fields (model, tools,
3623
+ * tool_choice, response_format, reasoning_effort, user, temperature, and any
3624
+ * providerOptions) are already wire-shaped (snake_case); only messages carry
3625
+ * camelCase tool fields that must become snake_case on the wire.
3626
+ */
3627
+ toWireBody(fireworksRequest) {
3628
+ return {
3629
+ ...fireworksRequest,
3630
+ messages: fireworksRequest.messages.map(messageToWire$1),
3631
+ };
3632
+ }
3633
+ /**
3634
+ * Rebuild a structured-output request without `response_format`, swapping in
3635
+ * the legacy fake-tool emulation. Used as a runtime fallback when a model
3636
+ * rejects native json_schema.
3637
+ */
3638
+ toFallbackStructuredOutputRequest(request) {
3639
+ if (!request.response_format ||
3640
+ request.response_format.type !== "json_schema") {
3641
+ return request;
3642
+ }
3643
+ const { response_format, ...rest } = request;
3644
+ const fallbackRequest = { ...rest };
3645
+ const schema = response_format.json_schema.schema;
3646
+ const fakeTool = {
3647
+ type: "function",
3648
+ function: {
3649
+ name: STRUCTURED_OUTPUT_TOOL_NAME$2,
3650
+ description: "REQUIRED: You MUST call this tool to provide your final response. " +
3651
+ "After gathering all necessary information (including results from other tools), " +
3652
+ "call this tool with the structured data to complete the request.",
3653
+ parameters: schema,
3654
+ },
3655
+ };
3656
+ fallbackRequest.tools = [...(fallbackRequest.tools ?? []), fakeTool];
3657
+ fallbackRequest.tool_choice = "auto";
3658
+ return fallbackRequest;
3659
+ }
3660
+ async *executeStreamRequest(client, request, signal) {
3661
+ const fireworks = client;
3662
+ const fireworksRequest = request;
3663
+ // streamChatCompletion adds `stream: true` + `stream_options.include_usage`
3664
+ // and yields decoded SSE chunks in OpenAI-compatible (snake_case) shape.
3665
+ const stream = fireworks.streamChatCompletion(this.toWireBody(fireworksRequest), signal ? { signal } : undefined);
3666
+ // Track current tool call being built
3667
+ let currentToolCall = null;
3668
+ // Track usage for final chunk
3669
+ let inputTokens = 0;
3670
+ let outputTokens = 0;
3671
+ const model = fireworksRequest.model || this.defaultModel;
3672
+ for await (const chunk of stream) {
3673
+ const typedChunk = chunk;
3674
+ const choices = typedChunk.choices;
3675
+ if (choices && choices.length > 0) {
3676
+ const delta = choices[0].delta;
3677
+ // Handle text content
3678
+ if (delta?.content) {
3679
+ yield {
3680
+ type: LlmStreamChunkType.Text,
3681
+ content: delta.content,
3682
+ };
3683
+ }
3684
+ // Handle tool calls
3685
+ if (delta?.tool_calls && delta.tool_calls.length > 0) {
3686
+ for (const toolCallDelta of delta.tool_calls) {
3687
+ if (toolCallDelta.id) {
3688
+ // New tool call starting
3689
+ if (currentToolCall) {
3690
+ // Emit the previous tool call
3691
+ yield {
3692
+ type: LlmStreamChunkType.ToolCall,
3693
+ toolCall: {
3694
+ id: currentToolCall.id,
3695
+ name: currentToolCall.name,
3696
+ arguments: currentToolCall.arguments,
3697
+ },
3698
+ };
3699
+ }
3700
+ currentToolCall = {
3701
+ id: toolCallDelta.id,
3702
+ name: toolCallDelta.function?.name || "",
3703
+ arguments: toolCallDelta.function?.arguments || "",
3704
+ };
3705
+ }
3706
+ else if (currentToolCall) {
3707
+ // Continuing existing tool call
3708
+ if (toolCallDelta.function?.name) {
3709
+ currentToolCall.name += toolCallDelta.function.name;
3710
+ }
3711
+ if (toolCallDelta.function?.arguments) {
3712
+ currentToolCall.arguments += toolCallDelta.function.arguments;
3713
+ }
3714
+ }
3715
+ }
3716
+ }
3717
+ // Check for finish reason
3718
+ if (choices[0].finish_reason) {
3719
+ // Emit any pending tool call
3720
+ if (currentToolCall) {
3721
+ yield {
3722
+ type: LlmStreamChunkType.ToolCall,
3723
+ toolCall: {
3724
+ id: currentToolCall.id,
3725
+ name: currentToolCall.name,
3726
+ arguments: currentToolCall.arguments,
3727
+ },
3728
+ };
3729
+ currentToolCall = null;
3730
+ }
3731
+ }
3732
+ }
3733
+ // Extract usage if present (usually in the final chunk)
3734
+ if (typedChunk.usage) {
3735
+ inputTokens =
3736
+ typedChunk.usage.prompt_tokens || typedChunk.usage.promptTokens || 0;
3737
+ outputTokens =
3738
+ typedChunk.usage.completion_tokens ||
3739
+ typedChunk.usage.completionTokens ||
3740
+ 0;
3741
+ }
3742
+ }
3743
+ // Emit done chunk with final usage
3744
+ yield {
3745
+ type: LlmStreamChunkType.Done,
3746
+ usage: [
3747
+ {
3748
+ input: inputTokens,
3749
+ output: outputTokens,
3750
+ reasoning: 0,
3751
+ total: inputTokens + outputTokens,
3752
+ provider: this.name,
3753
+ model,
3754
+ },
3755
+ ],
3756
+ };
3757
+ }
3758
+ //
3759
+ // Response Parsing
3760
+ //
3761
+ parseResponse(response, _options) {
3762
+ const fireworksResponse = response;
3763
+ const choice = fireworksResponse.choices[0];
3764
+ const content = this.extractContent(fireworksResponse);
3765
+ const hasToolCalls = this.hasToolCalls(fireworksResponse);
3766
+ const stopReason = choice?.finishReason ?? choice?.finish_reason ?? undefined;
3767
+ return {
3768
+ content,
3769
+ hasToolCalls,
3770
+ stopReason,
3771
+ usage: this.extractUsage(fireworksResponse, fireworksResponse.model),
3772
+ raw: fireworksResponse,
3773
+ };
3774
+ }
3775
+ extractToolCalls(response) {
3776
+ const fireworksResponse = response;
3777
+ const toolCalls = [];
3778
+ const choice = fireworksResponse.choices[0];
3779
+ if (!choice?.message?.toolCalls) {
3780
+ return toolCalls;
3781
+ }
3782
+ for (const toolCall of choice.message.toolCalls) {
3783
+ toolCalls.push({
3784
+ callId: toolCall.id,
3785
+ name: toolCall.function.name,
3786
+ arguments: toolCall.function.arguments,
3787
+ raw: toolCall,
3788
+ });
3789
+ }
3790
+ return toolCalls;
3791
+ }
3792
+ extractUsage(response, model) {
3793
+ const fireworksResponse = response;
3794
+ if (!fireworksResponse.usage) {
3795
+ return {
3796
+ input: 0,
3797
+ output: 0,
3798
+ reasoning: 0,
3799
+ total: 0,
3800
+ provider: this.name,
3801
+ model,
3802
+ };
3803
+ }
3804
+ const usage = fireworksResponse.usage;
3805
+ return {
3806
+ input: usage.promptTokens || usage.prompt_tokens || 0,
3807
+ output: usage.completionTokens || usage.completion_tokens || 0,
3808
+ reasoning: usage.completionTokensDetails?.reasoningTokens || 0,
3809
+ total: usage.totalTokens || usage.total_tokens || 0,
3810
+ provider: this.name,
3811
+ model,
3812
+ };
3813
+ }
3814
+ //
3815
+ // Tool Result Handling
3816
+ //
3817
+ formatToolResult(toolCall, result) {
3818
+ return {
3819
+ role: "tool",
3820
+ toolCallId: toolCall.callId,
3821
+ content: result.output,
3822
+ };
3823
+ }
3824
+ appendToolResult(request, toolCall, result) {
3825
+ const fireworksRequest = request;
3826
+ const toolCallRaw = toolCall.raw;
3827
+ // Add assistant message with the tool call
3828
+ fireworksRequest.messages.push({
3829
+ role: "assistant",
3830
+ content: null,
3831
+ toolCalls: [toolCallRaw],
3832
+ });
3833
+ // Add tool result message
3834
+ fireworksRequest.messages.push(this.formatToolResult(toolCall, result));
3835
+ return fireworksRequest;
3836
+ }
3837
+ //
3838
+ // History Management
3839
+ //
3840
+ responseToHistoryItems(response) {
3841
+ const fireworksResponse = response;
3842
+ const historyItems = [];
3843
+ const choice = fireworksResponse.choices[0];
3844
+ if (!choice?.message) {
3845
+ return historyItems;
3846
+ }
3847
+ // Check if this is a tool use response
3848
+ if (choice.message.toolCalls && choice.message.toolCalls.length > 0) {
3849
+ // Don't add to history yet - will be added after tool execution
3850
+ return historyItems;
3851
+ }
3852
+ // Extract text content for non-tool responses
3853
+ if (choice.message.content) {
3854
+ const historyItem = {
3855
+ content: choice.message.content,
3856
+ role: LlmMessageRole.Assistant,
3857
+ type: LlmMessageType.Message,
3858
+ };
3859
+ // Preserve reasoning if present. Fireworks reasoning models return it in
3860
+ // reasoning_content; some routes use reasoning.
3861
+ const reasoning = choice.message.reasoning_content ?? choice.message.reasoning;
3862
+ if (reasoning) {
3863
+ historyItem.reasoning = reasoning;
3864
+ }
3865
+ historyItems.push(historyItem);
3866
+ }
3867
+ return historyItems;
3868
+ }
3869
+ //
3870
+ // Error Classification
3871
+ //
3872
+ classifyError(error) {
3873
+ // Shared first pass: retryable structured-output timeouts (#422),
3874
+ // quota exhaustion, and billing failures classify the same across providers.
3875
+ const shared = classifyProviderError(error);
3876
+ if (shared)
3877
+ return shared;
3878
+ // Check if error has a status code (HTTP error)
3879
+ const errorWithStatus = error;
3880
+ const statusCode = errorWithStatus.status || errorWithStatus.statusCode;
3881
+ if (statusCode) {
3882
+ // Rate limit error
3883
+ if (statusCode === RATE_LIMIT_STATUS_CODE$1) {
3884
+ return {
3885
+ error,
3886
+ category: ErrorCategory.RateLimit,
3887
+ shouldRetry: false,
3888
+ suggestedDelayMs: 60000,
3889
+ };
3890
+ }
3891
+ // Retryable errors (server errors, timeouts, etc.)
3892
+ if (RETRYABLE_STATUS_CODES$2.includes(statusCode)) {
3893
+ return {
3894
+ error,
3895
+ category: ErrorCategory.Retryable,
3896
+ shouldRetry: true,
3897
+ };
3898
+ }
3899
+ // Client errors (4xx except 429) are unrecoverable
3900
+ if (statusCode >= 400 && statusCode < 500) {
3901
+ return {
3902
+ error,
3903
+ category: ErrorCategory.Unrecoverable,
3904
+ shouldRetry: false,
3905
+ };
3906
+ }
3907
+ }
3908
+ // Check error message for rate limit indicators
3909
+ const errorMessage = error instanceof Error ? error.message.toLowerCase() : "";
3910
+ if (errorMessage.includes("rate limit") ||
3911
+ errorMessage.includes("too many requests")) {
3912
+ return {
3913
+ error,
3914
+ category: ErrorCategory.RateLimit,
3915
+ shouldRetry: false,
3916
+ suggestedDelayMs: 60000,
3917
+ };
3918
+ }
3919
+ // Check for transient network errors (ECONNRESET, etc.)
3920
+ if (isTransientNetworkError(error)) {
3921
+ return {
3922
+ error,
3923
+ category: ErrorCategory.Retryable,
3924
+ shouldRetry: true,
3925
+ };
3926
+ }
3927
+ // Unknown error - treat as potentially retryable
3928
+ return {
3929
+ error,
3930
+ category: ErrorCategory.Unknown,
3931
+ shouldRetry: true,
3932
+ };
3933
+ }
3934
+ //
3935
+ // Provider-Specific Features
3936
+ //
3937
+ isComplete(response) {
3938
+ const fireworksResponse = response;
3939
+ const choice = fireworksResponse.choices[0];
3940
+ // Complete if no tool calls
3941
+ if (!choice?.message?.toolCalls?.length) {
3942
+ return true;
3943
+ }
3944
+ return false;
3945
+ }
3946
+ hasStructuredOutput(response) {
3947
+ const fireworksResponse = response;
3948
+ // Native path: executeRequest annotates the response when we sent
3949
+ // `response_format`, so we can detect intent statelessly.
3950
+ if (fireworksResponse.__jaypieStructuredOutput) {
3951
+ return this.extractStructuredOutput(response) !== undefined;
3952
+ }
3953
+ // Fallback path: legacy fake-tool emulation, kept for models that the
3954
+ // runtime has cached as not supporting native `response_format`.
3955
+ const choice = fireworksResponse.choices[0];
3956
+ if (!choice?.message?.toolCalls?.length) {
3957
+ return false;
3958
+ }
3959
+ // Check if the last tool call is structured_output
3960
+ const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
3961
+ return lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME$2;
3962
+ }
3963
+ extractStructuredOutput(response) {
3964
+ const fireworksResponse = response;
3965
+ if (fireworksResponse.__jaypieStructuredOutput) {
3966
+ const choice = fireworksResponse.choices[0];
3967
+ const content = choice?.message?.content;
3968
+ if (typeof content !== "string" || content.length === 0) {
3969
+ return undefined;
3970
+ }
3971
+ try {
3972
+ return JSON.parse(content);
3973
+ }
3974
+ catch {
3975
+ return undefined;
3976
+ }
3977
+ }
3978
+ // Fallback path: legacy fake-tool emulation
3979
+ const choice = fireworksResponse.choices[0];
3980
+ if (!choice?.message?.toolCalls?.length) {
3981
+ return undefined;
3982
+ }
3983
+ const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
3984
+ if (lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME$2) {
3985
+ try {
3986
+ return JSON.parse(lastToolCall.function.arguments);
3987
+ }
3988
+ catch {
3989
+ return undefined;
3990
+ }
3991
+ }
3992
+ return undefined;
3993
+ }
3994
+ //
3995
+ // Private Helpers
3996
+ //
3997
+ hasToolCalls(response) {
3998
+ const choice = response.choices[0];
3999
+ return (choice?.message?.toolCalls?.length ?? 0) > 0;
4000
+ }
4001
+ extractContent(response) {
4002
+ // Check for structured output first
4003
+ if (this.hasStructuredOutput(response)) {
4004
+ return this.extractStructuredOutput(response);
4005
+ }
4006
+ const choice = response.choices[0];
4007
+ return choice?.message?.content ?? undefined;
4008
+ }
4009
+ convertMessagesToFireworks(messages, system) {
4010
+ const fireworksMessages = [];
4011
+ // Add system message if provided
4012
+ if (system) {
4013
+ fireworksMessages.push({
4014
+ role: "system",
4015
+ content: system,
4016
+ });
4017
+ }
4018
+ for (const msg of messages) {
4019
+ const message = msg;
4020
+ // Handle different message types
4021
+ if (message.role === "system") {
4022
+ fireworksMessages.push({
4023
+ role: "system",
4024
+ content: message.content,
4025
+ });
4026
+ }
4027
+ else if (message.role === "user") {
4028
+ fireworksMessages.push({
4029
+ role: "user",
4030
+ content: convertContentToFireworks(message.content),
4031
+ });
4032
+ }
4033
+ else if (message.role === "assistant") {
4034
+ const assistantMsg = {
4035
+ role: "assistant",
4036
+ content: message.content || null,
4037
+ };
4038
+ // Include toolCalls if present (check both camelCase and snake_case for compatibility)
4039
+ if (message.toolCalls) {
4040
+ assistantMsg.toolCalls = message.toolCalls;
4041
+ }
4042
+ else if (message.tool_calls) {
4043
+ assistantMsg.toolCalls = message.tool_calls;
4044
+ }
4045
+ fireworksMessages.push(assistantMsg);
4046
+ }
4047
+ else if (message.role === "tool") {
4048
+ fireworksMessages.push({
4049
+ role: "tool",
4050
+ toolCallId: message.toolCallId || message.tool_call_id,
4051
+ content: message.content,
4052
+ });
4053
+ }
4054
+ else if (message.type === LlmMessageType.Message) {
4055
+ // Handle internal message format
4056
+ const role = message.role?.toLowerCase();
4057
+ if (role === "assistant") {
4058
+ fireworksMessages.push({
4059
+ role: "assistant",
4060
+ content: message.content,
4061
+ });
4062
+ }
4063
+ else {
4064
+ fireworksMessages.push({
4065
+ role: "user",
4066
+ content: convertContentToFireworks(message.content),
4067
+ });
4068
+ }
4069
+ }
4070
+ else if (message.type === LlmMessageType.FunctionCall) {
4071
+ fireworksMessages.push({
4072
+ role: "assistant",
4073
+ content: null,
4074
+ toolCalls: [
4075
+ {
4076
+ id: message.call_id,
4077
+ type: "function",
4078
+ function: {
4079
+ name: message.name,
4080
+ arguments: message.arguments || "{}",
4081
+ },
4082
+ },
4083
+ ],
4084
+ });
4085
+ }
4086
+ else if (message.type === LlmMessageType.FunctionCallOutput) {
4087
+ fireworksMessages.push({
4088
+ role: "tool",
4089
+ toolCallId: message.call_id,
4090
+ content: message.output || "",
4091
+ });
4092
+ }
4093
+ }
4094
+ return fireworksMessages;
4095
+ }
4096
+ }
4097
+ // Export singleton instance
4098
+ const fireworksAdapter = new FireworksAdapter();
4099
+
4100
+ //
4101
+ //
4102
+ // Constants
4103
+ //
4104
+ const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
4105
+ // Gemini 3 family supports combining tools (function calling) with native
4106
+ // structured output via `responseJsonSchema`. Earlier Gemini families
4107
+ // (including 2.5 thinking) do not support the combo and fall back to the
4108
+ // legacy `structured_output` fake-tool emulation.
4109
+ const GEMINI_3_PATTERN = /^gemini-3/;
4110
+ // Reasoning-effort control differs by generation: Gemini 3.x takes the
4111
+ // `thinkingLevel` enum, Gemini 2.5 takes an integer `thinkingBudget`. Sending
4112
+ // the wrong one errors, and models outside these families have no thinking
4113
+ // control, so effort is only applied when one of these matches.
4114
+ const GEMINI_25_PATTERN = /^gemini-2\.5/;
4115
+ /**
4116
+ * Detect 4xx errors that indicate the model itself does not support the
4117
+ * `responseJsonSchema` + tools combo. Triggers the runtime fallback to the
4118
+ * fake-tool emulation path. Other 400s propagate.
4119
+ */
4120
+ function isStructuredOutputComboUnsupportedError(error) {
4121
+ if (!error || typeof error !== "object")
4122
+ return false;
4123
+ const err = error;
4124
+ const status = err.status ?? err.code;
4125
+ if (status !== 400)
4126
+ return false;
4127
+ const messages = [err.message, err.error?.message].filter((m) => typeof m === "string");
4128
+ return messages.some((m) => /response[_ ]?json[_ ]?schema|response[_ ]?schema|response[_ ]?mime|function[_ ]?call|tools/i.test(m));
4129
+ }
4130
+ // Gemini uses HTTP status codes for error classification
4131
+ // Documented at: https://ai.google.dev/api/rest/v1beta/Status
4132
+ const RETRYABLE_STATUS_CODES$1 = [
4133
+ 408, // Request Timeout
4134
+ 429, // Too Many Requests (Rate Limit)
4135
+ 500, // Internal Server Error
4136
+ 502, // Bad Gateway
4137
+ 503, // Service Unavailable
4138
+ 504, // Gateway Timeout
4139
+ ];
4140
+ const NOT_RETRYABLE_STATUS_CODES = [
4141
+ 400, // Bad Request
4142
+ 401, // Unauthorized
4143
+ 403, // Forbidden
4144
+ 404, // Not Found
4145
+ 409, // Conflict
4146
+ 422, // Unprocessable Entity
4147
+ ];
4148
+ //
4149
+ //
4150
+ // Main
4151
+ //
4152
+ /**
4153
+ * GoogleAdapter implements the ProviderAdapter interface for Google's Gemini API.
4154
+ * It handles request building, response parsing, and error classification
4155
+ * specific to Gemini's generateContent API.
4156
+ */
4157
+ class GoogleAdapter extends BaseProviderAdapter {
4158
+ constructor() {
4159
+ super(...arguments);
4160
+ this.name = PROVIDER.GOOGLE.NAME;
4161
+ this.defaultModel = PROVIDER.GOOGLE.DEFAULT;
4162
+ // Session-level cache of Gemini 3 models observed to reject the native
4163
+ // `responseJsonSchema` + tools combo. When a model is in this set,
4164
+ // buildRequest engages the legacy fake-tool path instead.
4165
+ this.runtimeNoStructuredOutputComboModels = new Set();
4166
+ }
4167
+ rememberModelRejectsStructuredOutputCombo(model) {
4168
+ this.runtimeNoStructuredOutputComboModels.add(model);
4169
+ }
4170
+ clearRuntimeNoStructuredOutputComboModels() {
4171
+ this.runtimeNoStructuredOutputComboModels.clear();
4172
+ }
4173
+ supportsStructuredOutputCombo(model) {
4174
+ if (this.runtimeNoStructuredOutputComboModels.has(model))
4175
+ return false;
4176
+ return GEMINI_3_PATTERN.test(model);
4177
+ }
4178
+ //
4179
+ // Request Building
4180
+ //
4181
+ buildRequest(request) {
4182
+ // Convert messages to Gemini format (Content[])
4183
+ const contents = this.convertMessagesToContents(request.messages);
4184
+ const geminiRequest = {
4185
+ model: request.model || this.defaultModel,
4186
+ contents,
4187
+ };
4188
+ // Add system instruction if provided
4189
+ if (request.system) {
4190
+ geminiRequest.config = {
4191
+ ...geminiRequest.config,
4192
+ systemInstruction: request.system,
4193
+ };
4194
+ }
4195
+ // Append instructions to the last user message if provided
4196
+ if (request.instructions && contents.length > 0) {
4197
+ const lastContent = contents[contents.length - 1];
4198
+ if (lastContent.role === "user" && lastContent.parts) {
4199
+ const lastPart = lastContent.parts[lastContent.parts.length - 1];
4200
+ if (lastPart.text) {
4201
+ lastPart.text = lastPart.text + "\n\n" + request.instructions;
4202
+ }
4203
+ }
4204
+ }
4205
+ const hasUserTools = !!(request.tools && request.tools.length > 0);
4206
+ const useNativeCombo = Boolean(request.format) &&
4207
+ hasUserTools &&
4208
+ this.supportsStructuredOutputCombo(geminiRequest.model);
4209
+ // When tools+format are combined and the model does not support the native
4210
+ // combo, inject the legacy `structured_output` fake tool here so the model
4211
+ // is forced to call it before its final answer.
4212
+ const allTools = request.tools
4213
+ ? [...request.tools]
4214
+ : [];
4215
+ if (request.format && hasUserTools && !useNativeCombo) {
4216
+ log$2.warn(`[GoogleAdapter] Using legacy structured_output tool fallback for model ${geminiRequest.model}; native responseJsonSchema + tools combo is only available on Gemini 3.`);
4217
+ allTools.push({
4218
+ name: STRUCTURED_OUTPUT_TOOL_NAME$1,
4219
+ description: "Output a structured JSON object, " +
4220
+ "use this before your final response to give structured outputs to the user",
4221
+ parameters: request.format,
4222
+ });
4223
+ }
4224
+ if (allTools.length > 0) {
4225
+ const functionDeclarations = allTools.map((tool) => ({
3334
4226
  name: tool.name,
3335
4227
  description: tool.description,
3336
4228
  parameters: tool.parameters,
@@ -6035,8 +6927,8 @@ async function withLlmObsSpan(options, fn) {
6035
6927
  if (started) {
6036
6928
  throw error;
6037
6929
  }
6038
- getLogger$6().warn("[llmobs] span emission failed");
6039
- getLogger$6().var({ error });
6930
+ getLogger$7().warn("[llmobs] span emission failed");
6931
+ getLogger$7().var({ error });
6040
6932
  return fn();
6041
6933
  }
6042
6934
  }
@@ -6052,8 +6944,8 @@ function annotateLlmObs(annotation) {
6052
6944
  sdk.annotate(undefined, annotation);
6053
6945
  }
6054
6946
  catch (error) {
6055
- getLogger$6().warn("[llmobs] annotate failed");
6056
- getLogger$6().var({ error });
6947
+ getLogger$7().warn("[llmobs] annotate failed");
6948
+ getLogger$7().var({ error });
6057
6949
  }
6058
6950
  }
6059
6951
  /**
@@ -6090,8 +6982,8 @@ function openLlmObsSpan(options) {
6090
6982
  sdk.annotate(capturedSpan, annotation);
6091
6983
  }
6092
6984
  catch (error) {
6093
- getLogger$6().warn("[llmobs] annotate failed");
6094
- getLogger$6().var({ error });
6985
+ getLogger$7().warn("[llmobs] annotate failed");
6986
+ getLogger$7().var({ error });
6095
6987
  }
6096
6988
  },
6097
6989
  finish: () => {
@@ -6104,8 +6996,8 @@ function openLlmObsSpan(options) {
6104
6996
  };
6105
6997
  }
6106
6998
  catch (error) {
6107
- getLogger$6().warn("[llmobs] span emission failed");
6108
- getLogger$6().var({ error });
6999
+ getLogger$7().warn("[llmobs] span emission failed");
7000
+ getLogger$7().var({ error });
6109
7001
  return null;
6110
7002
  }
6111
7003
  }
@@ -6583,7 +7475,7 @@ async function emitProgress({ event, onProgress, }) {
6583
7475
  await onProgress(event);
6584
7476
  }
6585
7477
  catch (error) {
6586
- const log = getLogger$6();
7478
+ const log = getLogger$7();
6587
7479
  log.warn(`[operate] onProgress callback threw on "${event.type}" event`);
6588
7480
  log.var({ error });
6589
7481
  }
@@ -6833,7 +7725,7 @@ function matchesCaughtError(reason, caught) {
6833
7725
  * (e.g. undici `TypeError: terminated`) emitted between attempts.
6834
7726
  */
6835
7727
  function createStaleRejectionGuard() {
6836
- const log = getLogger$6();
7728
+ const log = getLogger$7();
6837
7729
  const caughtErrors = new Set();
6838
7730
  let listener;
6839
7731
  return {
@@ -7020,7 +7912,7 @@ class RetryExecutor {
7020
7912
  * @throws BadGatewayError if all retries are exhausted or error is not retryable
7021
7913
  */
7022
7914
  async execute(operation, options) {
7023
- const log = getLogger$6();
7915
+ const log = getLogger$7();
7024
7916
  let attempt = 0;
7025
7917
  // Guard against stale rejections firing on a subsequent microtask after
7026
7918
  // the retry layer has already caught the originating error: undici socket
@@ -7138,7 +8030,7 @@ class OperateLoop {
7138
8030
  * Execute the operate loop for multi-turn conversations with tool calling.
7139
8031
  */
7140
8032
  async execute(input, options = {}) {
7141
- const log = getLogger$6();
8033
+ const log = getLogger$7();
7142
8034
  // Log what was passed to operate
7143
8035
  log.trace("[operate] Starting operate loop");
7144
8036
  log.trace.var({ "operate.input": input });
@@ -7293,7 +8185,7 @@ class OperateLoop {
7293
8185
  };
7294
8186
  }
7295
8187
  async executeOneTurn(request, state, context, options) {
7296
- const log = getLogger$6();
8188
+ const log = getLogger$7();
7297
8189
  // Create error classifier from adapter
7298
8190
  const errorClassifier = createErrorClassifier(this.adapter);
7299
8191
  // Create retry executor for this turn
@@ -7759,7 +8651,7 @@ class StreamLoop {
7759
8651
  * Yields stream chunks as they become available.
7760
8652
  */
7761
8653
  async *execute(input, options = {}) {
7762
- const log = getLogger$6();
8654
+ const log = getLogger$7();
7763
8655
  // Verify adapter supports streaming
7764
8656
  if (!this.adapter.executeStreamRequest) {
7765
8657
  throw new BadGatewayError(`Provider ${this.adapter.name} does not support streaming`);
@@ -7887,7 +8779,7 @@ class StreamLoop {
7887
8779
  };
7888
8780
  }
7889
8781
  async *executeOneStreamingTurn(request, state, context, options) {
7890
- const log = getLogger$6();
8782
+ const log = getLogger$7();
7891
8783
  // Build provider-specific request
7892
8784
  const providerRequest = this.adapter.buildRequest(request);
7893
8785
  // Execute beforeEachModelRequest hook
@@ -8044,7 +8936,7 @@ class StreamLoop {
8044
8936
  return { shouldContinue: false };
8045
8937
  }
8046
8938
  async *processToolCalls(toolCalls, state, context) {
8047
- const log = getLogger$6();
8939
+ const log = getLogger$7();
8048
8940
  for (const toolCall of toolCalls) {
8049
8941
  state.toolCallNames.push(toolCall.name);
8050
8942
  // Resolved once per call; never throws (undefined when tool has no message)
@@ -8327,10 +9219,10 @@ class AnthropicClient {
8327
9219
  }
8328
9220
 
8329
9221
  // Logger
8330
- const getLogger$5 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
9222
+ const getLogger$6 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
8331
9223
  // Client initialization
8332
- async function initializeClient$5({ apiKey, } = {}) {
8333
- const logger = getLogger$5();
9224
+ async function initializeClient$6({ apiKey, } = {}) {
9225
+ const logger = getLogger$6();
8334
9226
  const resolvedApiKey = apiKey || (await getEnvSecret("ANTHROPIC_API_KEY"));
8335
9227
  if (!resolvedApiKey) {
8336
9228
  throw new ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
@@ -8340,12 +9232,12 @@ async function initializeClient$5({ apiKey, } = {}) {
8340
9232
  return client;
8341
9233
  }
8342
9234
  // Message formatting functions
8343
- function formatSystemMessage$2(systemPrompt, { data, placeholders: placeholders$1 } = {}) {
9235
+ function formatSystemMessage$3(systemPrompt, { data, placeholders: placeholders$1 } = {}) {
8344
9236
  return placeholders$1?.system === false
8345
9237
  ? systemPrompt
8346
9238
  : placeholders(systemPrompt, data);
8347
9239
  }
8348
- function formatUserMessage$3(message, { data, placeholders: placeholders$1 } = {}) {
9240
+ function formatUserMessage$4(message, { data, placeholders: placeholders$1 } = {}) {
8349
9241
  const content = placeholders$1?.message === false
8350
9242
  ? message
8351
9243
  : placeholders(message, data);
@@ -8354,11 +9246,11 @@ function formatUserMessage$3(message, { data, placeholders: placeholders$1 } = {
8354
9246
  content,
8355
9247
  };
8356
9248
  }
8357
- function prepareMessages$3(message, { data, placeholders } = {}) {
8358
- const logger = getLogger$5();
9249
+ function prepareMessages$4(message, { data, placeholders } = {}) {
9250
+ const logger = getLogger$6();
8359
9251
  const messages = [];
8360
9252
  // Add user message (necessary for all requests)
8361
- const userMessage = formatUserMessage$3(message, { data, placeholders });
9253
+ const userMessage = formatUserMessage$4(message, { data, placeholders });
8362
9254
  messages.push(userMessage);
8363
9255
  logger.trace(`User message: ${userMessage.content.length} characters`);
8364
9256
  return messages;
@@ -8440,7 +9332,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
8440
9332
  // Main class implementation
8441
9333
  class AnthropicProvider {
8442
9334
  constructor(model = PROVIDER.ANTHROPIC.DEFAULT, { apiKey } = {}) {
8443
- this.log = getLogger$5();
9335
+ this.log = getLogger$6();
8444
9336
  this.conversationHistory = [];
8445
9337
  this.model = model;
8446
9338
  this.apiKey = apiKey;
@@ -8449,7 +9341,7 @@ class AnthropicProvider {
8449
9341
  if (this._client) {
8450
9342
  return this._client;
8451
9343
  }
8452
- this._client = await initializeClient$5({ apiKey: this.apiKey });
9344
+ this._client = await initializeClient$6({ apiKey: this.apiKey });
8453
9345
  return this._client;
8454
9346
  }
8455
9347
  async getOperateLoop() {
@@ -8477,12 +9369,12 @@ class AnthropicProvider {
8477
9369
  // Main send method
8478
9370
  async send(message, options) {
8479
9371
  const client = await this.getClient();
8480
- const messages = prepareMessages$3(message, options || {});
9372
+ const messages = prepareMessages$4(message, options || {});
8481
9373
  const modelToUse = options?.model || this.model;
8482
9374
  // Process system message if provided
8483
9375
  let systemMessage;
8484
9376
  if (options?.system) {
8485
- systemMessage = formatSystemMessage$2(options.system, {
9377
+ systemMessage = formatSystemMessage$3(options.system, {
8486
9378
  data: options.data,
8487
9379
  placeholders: options.placeholders,
8488
9380
  });
@@ -8536,9 +9428,9 @@ async function loadSdk() {
8536
9428
  throw new ConfigurationError("@aws-sdk/client-bedrock-runtime is required but not installed. Run: npm install @aws-sdk/client-bedrock-runtime");
8537
9429
  }
8538
9430
  }
8539
- const getLogger$4 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
8540
- async function initializeClient$4({ region, } = {}) {
8541
- const logger = getLogger$4();
9431
+ const getLogger$5 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
9432
+ async function initializeClient$5({ region, } = {}) {
9433
+ const logger = getLogger$5();
8542
9434
  const resolvedRegion = region || process.env.AWS_REGION || "us-east-1";
8543
9435
  const sdk = await loadSdk();
8544
9436
  const client = new sdk.BedrockRuntimeClient({ region: resolvedRegion });
@@ -8548,7 +9440,7 @@ async function initializeClient$4({ region, } = {}) {
8548
9440
 
8549
9441
  class BedrockProvider {
8550
9442
  constructor(model = PROVIDER.BEDROCK.DEFAULT, { region } = {}) {
8551
- this.log = getLogger$4();
9443
+ this.log = getLogger$5();
8552
9444
  this.conversationHistory = [];
8553
9445
  this.model = model;
8554
9446
  this.region = region;
@@ -8556,7 +9448,7 @@ class BedrockProvider {
8556
9448
  async getClient() {
8557
9449
  if (this._client)
8558
9450
  return this._client;
8559
- this._client = await initializeClient$4({ region: this.region });
9451
+ this._client = await initializeClient$5({ region: this.region });
8560
9452
  return this._client;
8561
9453
  }
8562
9454
  async getOperateLoop() {
@@ -8605,6 +9497,278 @@ class BedrockProvider {
8605
9497
  }
8606
9498
  }
8607
9499
 
9500
+ /**
9501
+ * HTTP error carrying the upstream status and parsed API message. The
9502
+ * FireworksAdapter classifies errors by reading `.status` / `.statusCode` and
9503
+ * `.message` / `.error.message`.
9504
+ */
9505
+ class FireworksHttpError extends Error {
9506
+ constructor(status, message, error) {
9507
+ super(message);
9508
+ this.name = "FireworksHttpError";
9509
+ this.status = status;
9510
+ this.statusCode = status;
9511
+ this.error = error;
9512
+ }
9513
+ }
9514
+ //
9515
+ //
9516
+ // Helpers
9517
+ //
9518
+ /**
9519
+ * Normalize the snake_case wire response into the camelCase shape the adapter
9520
+ * readers expect. Only protocol fields are touched — user content (schema
9521
+ * property names, tool argument JSON) is left untouched.
9522
+ */
9523
+ function normalizeResponse$1(json) {
9524
+ const choices = json.choices;
9525
+ if (Array.isArray(choices)) {
9526
+ for (const choice of choices) {
9527
+ if (choice.finish_reason !== undefined &&
9528
+ choice.finishReason === undefined) {
9529
+ choice.finishReason = choice.finish_reason;
9530
+ }
9531
+ const message = choice.message;
9532
+ if (message?.tool_calls !== undefined &&
9533
+ message.toolCalls === undefined) {
9534
+ message.toolCalls = message.tool_calls;
9535
+ }
9536
+ }
9537
+ }
9538
+ const usage = json.usage;
9539
+ if (usage) {
9540
+ if (usage.promptTokens === undefined)
9541
+ usage.promptTokens = usage.prompt_tokens;
9542
+ if (usage.completionTokens === undefined) {
9543
+ usage.completionTokens = usage.completion_tokens;
9544
+ }
9545
+ if (usage.totalTokens === undefined)
9546
+ usage.totalTokens = usage.total_tokens;
9547
+ const details = usage.completion_tokens_details;
9548
+ if (details?.reasoning_tokens !== undefined &&
9549
+ !usage.completionTokensDetails) {
9550
+ usage.completionTokensDetails = {
9551
+ reasoningTokens: details.reasoning_tokens,
9552
+ };
9553
+ }
9554
+ }
9555
+ return json;
9556
+ }
9557
+ //
9558
+ //
9559
+ // Main
9560
+ //
9561
+ /**
9562
+ * Minimal `fetch`-based client for Fireworks AI's OpenAI-compatible Chat
9563
+ * Completions endpoint. The adapter only needs a single POST (streaming and
9564
+ * non-streaming), header auth, and HTTP error surfacing.
9565
+ */
9566
+ class FireworksClient {
9567
+ constructor({ apiKey, baseURL = PROVIDER.FIREWORKS.BASE_URL, }) {
9568
+ this.apiKey = apiKey;
9569
+ this.baseURL = baseURL;
9570
+ }
9571
+ headers() {
9572
+ return {
9573
+ Authorization: `Bearer ${this.apiKey}`,
9574
+ "Content-Type": "application/json",
9575
+ };
9576
+ }
9577
+ async toError(response) {
9578
+ let message = `Fireworks request failed with status ${response.status}`;
9579
+ let error;
9580
+ try {
9581
+ const body = (await response.json());
9582
+ if (body?.error?.message) {
9583
+ message = body.error.message;
9584
+ error = body.error;
9585
+ }
9586
+ }
9587
+ catch {
9588
+ // Non-JSON error body; keep the status-based message.
9589
+ }
9590
+ return new FireworksHttpError(response.status, message, error);
9591
+ }
9592
+ async chatCompletion(body, { signal } = {}) {
9593
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
9594
+ method: "POST",
9595
+ headers: this.headers(),
9596
+ body: JSON.stringify(body),
9597
+ signal,
9598
+ });
9599
+ if (!response.ok)
9600
+ throw await this.toError(response);
9601
+ const json = (await response.json());
9602
+ return normalizeResponse$1(json);
9603
+ }
9604
+ async *streamChatCompletion(body, { signal } = {}) {
9605
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
9606
+ method: "POST",
9607
+ headers: { ...this.headers(), Accept: "text/event-stream" },
9608
+ // OpenAI-style streams only include usage when explicitly requested.
9609
+ body: JSON.stringify({
9610
+ ...body,
9611
+ stream: true,
9612
+ stream_options: { include_usage: true },
9613
+ }),
9614
+ signal,
9615
+ });
9616
+ if (!response.ok)
9617
+ throw await this.toError(response);
9618
+ if (!response.body)
9619
+ return;
9620
+ yield* parseSseStream(response.body);
9621
+ }
9622
+ }
9623
+
9624
+ // Logger
9625
+ const getLogger$4 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
9626
+ // Client initialization
9627
+ async function initializeClient$4({ apiKey, } = {}) {
9628
+ const logger = getLogger$4();
9629
+ const resolvedApiKey = apiKey || (await getEnvSecret(PROVIDER.FIREWORKS.API_KEY));
9630
+ if (!resolvedApiKey) {
9631
+ throw new ConfigurationError("The application could not resolve the requested keys");
9632
+ }
9633
+ const client = new FireworksClient({ apiKey: resolvedApiKey });
9634
+ logger.trace("Initialized Fireworks client");
9635
+ return client;
9636
+ }
9637
+ // Get default model from environment or constants
9638
+ function getDefaultModel$1() {
9639
+ return process.env.FIREWORKS_MODEL || PROVIDER.FIREWORKS.DEFAULT;
9640
+ }
9641
+ function formatSystemMessage$2(systemPrompt, { data, placeholders: placeholders$1 } = {}) {
9642
+ const content = placeholders$1?.system === false
9643
+ ? systemPrompt
9644
+ : placeholders(systemPrompt, data);
9645
+ return {
9646
+ role: "system",
9647
+ content,
9648
+ };
9649
+ }
9650
+ function formatUserMessage$3(message, { data, placeholders: placeholders$1 } = {}) {
9651
+ const content = placeholders$1?.message === false
9652
+ ? message
9653
+ : placeholders(message, data);
9654
+ return {
9655
+ role: "user",
9656
+ content,
9657
+ };
9658
+ }
9659
+ function prepareMessages$3(message, { system, data, placeholders } = {}) {
9660
+ const logger = getLogger$4();
9661
+ const messages = [];
9662
+ if (system) {
9663
+ const systemMessage = formatSystemMessage$2(system, { data, placeholders });
9664
+ messages.push(systemMessage);
9665
+ logger.trace(`System message: ${systemMessage.content?.length} characters`);
9666
+ }
9667
+ const userMessage = formatUserMessage$3(message, { data, placeholders });
9668
+ messages.push(userMessage);
9669
+ logger.trace(`User message: ${userMessage.content?.length} characters`);
9670
+ return messages;
9671
+ }
9672
+
9673
+ class FireworksProvider {
9674
+ constructor(model = getDefaultModel$1(), { apiKey } = {}) {
9675
+ this.log = getLogger$4();
9676
+ this.conversationHistory = [];
9677
+ this.model = model;
9678
+ this.apiKey = apiKey;
9679
+ }
9680
+ async getClient() {
9681
+ if (this._client) {
9682
+ return this._client;
9683
+ }
9684
+ this._client = await initializeClient$4({ apiKey: this.apiKey });
9685
+ return this._client;
9686
+ }
9687
+ async getOperateLoop() {
9688
+ if (this._operateLoop) {
9689
+ return this._operateLoop;
9690
+ }
9691
+ const client = await this.getClient();
9692
+ this._operateLoop = createOperateLoop({
9693
+ adapter: fireworksAdapter,
9694
+ client,
9695
+ });
9696
+ return this._operateLoop;
9697
+ }
9698
+ async getStreamLoop() {
9699
+ if (this._streamLoop) {
9700
+ return this._streamLoop;
9701
+ }
9702
+ const client = await this.getClient();
9703
+ this._streamLoop = createStreamLoop({
9704
+ adapter: fireworksAdapter,
9705
+ client,
9706
+ });
9707
+ return this._streamLoop;
9708
+ }
9709
+ async send(message, options) {
9710
+ const client = await this.getClient();
9711
+ const messages = prepareMessages$3(message, options);
9712
+ const modelToUse = options?.model || this.model;
9713
+ // OpenAI-compatible Chat Completions body; messages are already wire-shaped.
9714
+ const response = await client.chatCompletion({
9715
+ model: modelToUse,
9716
+ messages,
9717
+ });
9718
+ const choices = response.choices;
9719
+ const rawContent = choices?.[0]?.message?.content;
9720
+ // Extract text content - content could be string or array of content items
9721
+ const content = typeof rawContent === "string"
9722
+ ? rawContent
9723
+ : Array.isArray(rawContent)
9724
+ ? rawContent
9725
+ .filter((item) => item.type === "text")
9726
+ .map((item) => item.text)
9727
+ .join("")
9728
+ : "";
9729
+ this.log.trace(`Assistant reply: ${content?.length || 0} characters`);
9730
+ // If structured output was requested, try to parse the response
9731
+ if (options?.response && content) {
9732
+ try {
9733
+ return JSON.parse(content);
9734
+ }
9735
+ catch {
9736
+ return content || "";
9737
+ }
9738
+ }
9739
+ return content || "";
9740
+ }
9741
+ async operate(input, options = {}) {
9742
+ const operateLoop = await this.getOperateLoop();
9743
+ const mergedOptions = { ...options, model: options.model ?? this.model };
9744
+ // Create a merged history including both the tracked history and any explicitly provided history
9745
+ if (this.conversationHistory.length > 0) {
9746
+ mergedOptions.history = options.history
9747
+ ? [...this.conversationHistory, ...options.history]
9748
+ : [...this.conversationHistory];
9749
+ }
9750
+ // Execute operate loop
9751
+ const response = await operateLoop.execute(input, mergedOptions);
9752
+ // Update conversation history with the new history from the response
9753
+ if (response.history && response.history.length > 0) {
9754
+ this.conversationHistory = response.history;
9755
+ }
9756
+ return response;
9757
+ }
9758
+ async *stream(input, options = {}) {
9759
+ const streamLoop = await this.getStreamLoop();
9760
+ const mergedOptions = { ...options, model: options.model ?? this.model };
9761
+ // Create a merged history including both the tracked history and any explicitly provided history
9762
+ if (this.conversationHistory.length > 0) {
9763
+ mergedOptions.history = options.history
9764
+ ? [...this.conversationHistory, ...options.history]
9765
+ : [...this.conversationHistory];
9766
+ }
9767
+ // Execute stream loop
9768
+ yield* streamLoop.execute(input, mergedOptions);
9769
+ }
9770
+ }
9771
+
8608
9772
  //
8609
9773
  //
8610
9774
  // Constants
@@ -9518,6 +10682,10 @@ class Llm {
9518
10682
  });
9519
10683
  case PROVIDER.BEDROCK.NAME:
9520
10684
  return new BedrockProvider(model || PROVIDER.BEDROCK.DEFAULT);
10685
+ case PROVIDER.FIREWORKS.NAME:
10686
+ return new FireworksProvider(model || PROVIDER.FIREWORKS.DEFAULT, {
10687
+ apiKey,
10688
+ });
9521
10689
  case PROVIDER.GOOGLE.NAME:
9522
10690
  return new GoogleProvider(model || PROVIDER.GOOGLE.DEFAULT, {
9523
10691
  apiKey,
@@ -9813,7 +10981,7 @@ const roll = {
9813
10981
  },
9814
10982
  type: "function",
9815
10983
  call: ({ number = 1, sides = 6 } = {}) => {
9816
- const log = getLogger$6();
10984
+ const log = getLogger$7();
9817
10985
  const rng = random$1();
9818
10986
  const rolls = [];
9819
10987
  let total = 0;
@@ -10011,5 +11179,5 @@ const toolkit = new JaypieToolkit(tools);
10011
11179
  [LlmMessageRole.Developer]: "user",
10012
11180
  });
10013
11181
 
10014
- export { BedrockProvider, ErrorCategory, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmError, LlmMessageRole, LlmMessageType, LlmProgressEventType, LlmQuotaError, LlmRateLimitError, LlmStreamChunkType, LlmTransientError, LlmUnrecoverableError, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, jsonSchemaToNaturalSchema, naturalSchemaToJsonSchema, toolkit, tools };
11182
+ export { BedrockProvider, ErrorCategory, FireworksProvider, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmError, LlmMessageRole, LlmMessageType, LlmProgressEventType, LlmQuotaError, LlmRateLimitError, LlmStreamChunkType, LlmTransientError, LlmUnrecoverableError, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, jsonSchemaToNaturalSchema, naturalSchemaToJsonSchema, toolkit, tools };
10015
11183
  //# sourceMappingURL=index.js.map