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