@hiper2d/ai-agents 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +46 -14
- package/dist/index.d.ts +46 -14
- package/dist/index.js +140 -97
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +138 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ __export(src_exports, {
|
|
|
59
59
|
LLM_CONSTANTS: () => LLM_CONSTANTS,
|
|
60
60
|
MESSAGE_ROLE: () => MESSAGE_ROLE,
|
|
61
61
|
META_REASONING_EFFORTS: () => META_REASONING_EFFORTS,
|
|
62
|
+
MISTRAL_REASONING_EFFORTS: () => MISTRAL_REASONING_EFFORTS,
|
|
62
63
|
MODEL_PRICING: () => MODEL_PRICING,
|
|
63
64
|
MetaAgent: () => MetaAgent,
|
|
64
65
|
MiniMaxAgent: () => MiniMaxAgent,
|
|
@@ -160,6 +161,7 @@ __export(src_exports, {
|
|
|
160
161
|
toGeminiEffort: () => toGeminiEffort,
|
|
161
162
|
toGlmEffort: () => toGlmEffort,
|
|
162
163
|
toMetaEffort: () => toMetaEffort,
|
|
164
|
+
toMistralEffort: () => toMistralEffort,
|
|
163
165
|
toOpenAIEffort: () => toOpenAIEffort,
|
|
164
166
|
transcribeWithGemini: () => transcribeWithGemini,
|
|
165
167
|
transcribeWithOpenAi: () => transcribeWithOpenAi,
|
|
@@ -967,15 +969,14 @@ var LLM_CONSTANTS = {
|
|
|
967
969
|
GEMINI_PRO: "gemini-pro",
|
|
968
970
|
GEMINI_FLASH: "gemini-flash",
|
|
969
971
|
GEMINI_LITE: "gemini-lite",
|
|
970
|
-
MISTRAL_LARGE: "mistral-large",
|
|
971
972
|
MISTRAL_MEDIUM: "mistral-medium",
|
|
972
973
|
MISTRAL_SMALL: "mistral-small",
|
|
973
|
-
MISTRAL_MAGISTRAL: "mistral-magistral",
|
|
974
974
|
GROK: "grok",
|
|
975
975
|
KIMI: "kimi",
|
|
976
976
|
GLM: "glm",
|
|
977
977
|
GLM_FLASH: "glm-flash",
|
|
978
978
|
FUGU_ULTRA: "fugu-ultra",
|
|
979
|
+
FUGU_MAX: "fugu-max",
|
|
979
980
|
// Qwen (QwenCloud/DashScope). Stable picker ids without the version, matching the gpt/gemini
|
|
980
981
|
// pattern, so future repoints don't orphan persisted ids.
|
|
981
982
|
QWEN_MAX: "qwen-max",
|
|
@@ -1128,36 +1129,40 @@ var SupportedAiModels = {
|
|
|
1128
1129
|
hasThinking: true,
|
|
1129
1130
|
temperature: 0.7
|
|
1130
1131
|
},
|
|
1131
|
-
// Mistral models
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1132
|
+
// Mistral models. Two hybrid entries since 2026-09-18: reasoning is off by default on the
|
|
1133
|
+
// API and switched on per request with `reasoning_effort` (see mistral-agent.ts); the trace
|
|
1134
|
+
// comes back as a `thinking` content chunk and works with json_schema structured output
|
|
1135
|
+
// (verified live 2026-09-18 on both). Pinned to 'high' — the only level Mistral's docs
|
|
1136
|
+
// describe ("full thinking chunk before the final answer"); Small is cheap enough that the
|
|
1137
|
+
// extra tokens don't matter, Medium's are priced into its free-tier band via the hybrid
|
|
1138
|
+
// multiplier. Live 2026-09-18 (one short schema ask each): Small 1.7s with a ~600-char
|
|
1139
|
+
// trace, Medium 3.8s with ~2000 chars — Medium's trace ran ~15x its answer length.
|
|
1140
|
+
//
|
|
1141
|
+
// Mistral Large 3 and Magistral Medium 1.2 were dropped 2026-09-18. Large 3 was retired by
|
|
1142
|
+
// Mistral on 2026-08-31 (the `-latest` alias still answered, but its capabilities say
|
|
1143
|
+
// reasoning: false and `reasoning_effort` is a 400 on it). Magistral was retired 2026-07-31
|
|
1144
|
+
// and `magistral-medium-latest` is now literally an alias of Medium 3.5 — the model list
|
|
1145
|
+
// returns `mistral-medium-3-5`, `mistral-medium-3`, `mistral-medium-latest` and
|
|
1146
|
+
// `magistral-medium-latest` as one entry — so Magistral seats had silently become Medium
|
|
1147
|
+
// seats already. Consumers map both retired ids onto the surviving pair.
|
|
1139
1148
|
[LLM_CONSTANTS.MISTRAL_MEDIUM]: {
|
|
1140
1149
|
displayName: "Mistral Medium 3.5",
|
|
1141
|
-
|
|
1150
|
+
// The docs' explicit 3.5 id. It was `mistral-medium-3` until 2026-09-18, which Mistral
|
|
1151
|
+
// now serves as an alias of the same model (Medium 3 itself retired 2026-08-31).
|
|
1152
|
+
modelApiName: "mistral-medium-3-5",
|
|
1142
1153
|
apiKeyName: API_KEY_CONSTANTS.MISTRAL,
|
|
1143
|
-
hasThinking:
|
|
1144
|
-
|
|
1154
|
+
hasThinking: true,
|
|
1155
|
+
reasoningEffort: "high",
|
|
1156
|
+
tags: ["fast", "expensive"]
|
|
1145
1157
|
},
|
|
1146
1158
|
[LLM_CONSTANTS.MISTRAL_SMALL]: {
|
|
1147
1159
|
displayName: "Mistral 4 Small",
|
|
1160
|
+
// Resolves to mistral-small-2603 (Small 4), which also carries the magistral-small alias.
|
|
1148
1161
|
modelApiName: "mistral-small-latest",
|
|
1149
1162
|
apiKeyName: API_KEY_CONSTANTS.MISTRAL,
|
|
1150
|
-
hasThinking: false,
|
|
1151
|
-
tags: ["very-fast", "cheap"]
|
|
1152
|
-
},
|
|
1153
|
-
[LLM_CONSTANTS.MISTRAL_MAGISTRAL]: {
|
|
1154
|
-
displayName: "Magistral Medium 1.2",
|
|
1155
|
-
modelApiName: "magistral-medium-latest",
|
|
1156
|
-
apiKeyName: API_KEY_CONSTANTS.MISTRAL,
|
|
1157
1163
|
hasThinking: true,
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
tags: ["very-fast"]
|
|
1164
|
+
reasoningEffort: "high",
|
|
1165
|
+
tags: ["very-fast", "cheap"]
|
|
1161
1166
|
},
|
|
1162
1167
|
// Kimi models. Single always-reasoning entry: K3 reasons by default and the only way to stop
|
|
1163
1168
|
// it is the undocumented K2-era `thinking: disabled` toggle, which we no longer rely on.
|
|
@@ -1217,13 +1222,37 @@ var SupportedAiModels = {
|
|
|
1217
1222
|
// so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
|
|
1218
1223
|
// zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
|
|
1219
1224
|
// prefix reuse. Ultra costs the same and is predictable.
|
|
1225
|
+
//
|
|
1226
|
+
// reasoningEffort is PINNED on both entries because the server default differs per model:
|
|
1227
|
+
// fugu-ultra defaults to `xhigh` (console.sakana.ai/models), fugu-max to `high`. Until
|
|
1228
|
+
// 2026-09-20 the agent sent no effort at all, so every Ultra turn ran at xhigh. Measured that
|
|
1229
|
+
// day on a 2k-token three-day game prompt (two samples each, chat completions, json mode):
|
|
1230
|
+
// ultra xhigh 148s / 265s $0.34 / $0.50 a turn (orchestration tokens included)
|
|
1231
|
+
// ultra high 65s / 78s $0.16 / $0.17
|
|
1232
|
+
// max high 6s / 10s $0.006 / $0.008
|
|
1233
|
+
// max xhigh 41s / 47s $0.02
|
|
1234
|
+
// `max_tokens` is not a latency lever for Ultra: Sakana applies it to the final response only,
|
|
1235
|
+
// the orchestrator "still uses maximum token limit". Effort is the only knob.
|
|
1220
1236
|
[LLM_CONSTANTS.FUGU_ULTRA]: {
|
|
1221
1237
|
displayName: "Sakana Fugu Ultra",
|
|
1222
1238
|
modelApiName: "fugu-ultra",
|
|
1223
1239
|
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1224
1240
|
hasThinking: false,
|
|
1241
|
+
reasoningEffort: "high",
|
|
1225
1242
|
tags: ["extremely-slow", "expensive"]
|
|
1226
1243
|
},
|
|
1244
|
+
// Fugu Max (2026-09-20, fugu-max → fugu-max-v1.0): Sakana's "largest pool of models on the
|
|
1245
|
+
// cost–performance Pareto frontier" — the everyday Fugu at 5x less than Ultra's output rate
|
|
1246
|
+
// and a fraction of its latency. It reasons (reasoning_tokens 80-500 a turn) but, like
|
|
1247
|
+
// Ultra, never returns reasoning_content, hence hasThinking: false. No orchestration
|
|
1248
|
+
// tokens observed on any call.
|
|
1249
|
+
[LLM_CONSTANTS.FUGU_MAX]: {
|
|
1250
|
+
displayName: "Sakana Fugu Max",
|
|
1251
|
+
modelApiName: "fugu-max",
|
|
1252
|
+
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1253
|
+
hasThinking: false,
|
|
1254
|
+
reasoningEffort: "high"
|
|
1255
|
+
},
|
|
1227
1256
|
// Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
|
|
1228
1257
|
// thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
|
|
1229
1258
|
// thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
|
|
@@ -1485,13 +1514,10 @@ var MODEL_PRICING = {
|
|
|
1485
1514
|
outputPrice: 1.5,
|
|
1486
1515
|
cacheHitPrice: 0.025
|
|
1487
1516
|
},
|
|
1488
|
-
// Mistral models. Cached tokens bill at 10% of
|
|
1489
|
-
//
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
outputPrice: 1.5,
|
|
1493
|
-
cacheHitPrice: 0.05
|
|
1494
|
-
},
|
|
1517
|
+
// Mistral models (mistral.ai/pricing/api, verified 2026-09-18). Cached tokens bill at 10% of
|
|
1518
|
+
// the input price (documented on the prompt_cache_key param; no per-model cached prices
|
|
1519
|
+
// published). Reasoning tokens are counted inside completion_tokens — no separate
|
|
1520
|
+
// reasoning_tokens field — so the output rate already covers the trace.
|
|
1495
1521
|
[SupportedAiModels[LLM_CONSTANTS.MISTRAL_MEDIUM].modelApiName]: {
|
|
1496
1522
|
inputPrice: 1.5,
|
|
1497
1523
|
outputPrice: 7.5,
|
|
@@ -1502,11 +1528,6 @@ var MODEL_PRICING = {
|
|
|
1502
1528
|
outputPrice: 0.6,
|
|
1503
1529
|
cacheHitPrice: 0.015
|
|
1504
1530
|
},
|
|
1505
|
-
[SupportedAiModels[LLM_CONSTANTS.MISTRAL_MAGISTRAL].modelApiName]: {
|
|
1506
|
-
inputPrice: 2,
|
|
1507
|
-
outputPrice: 5,
|
|
1508
|
-
cacheHitPrice: 0.2
|
|
1509
|
-
},
|
|
1510
1531
|
// Grok models. Cached price is per-model on xAI (not a uniform ratio):
|
|
1511
1532
|
// grok-4.6 is $0.50/M cached vs $2.00/M input, and all rates double for prompts
|
|
1512
1533
|
// >= 200K tokens, per docs.x.ai/developers/models (verified 2026-08-12).
|
|
@@ -1519,9 +1540,12 @@ var MODEL_PRICING = {
|
|
|
1519
1540
|
extendedContextCacheHitPrice: 1,
|
|
1520
1541
|
extendedContextThresholdTokens: 2e5
|
|
1521
1542
|
},
|
|
1522
|
-
// Sakana Fugu models. Base `fugu` was retired
|
|
1523
|
-
// measured out at these same ultra rates, so it
|
|
1524
|
-
//
|
|
1543
|
+
// Sakana Fugu models (console.sakana.ai/pricing, read 2026-09-20). Base `fugu` was retired
|
|
1544
|
+
// 2026-08-04 — it had no published price and measured out at these same ultra rates, so it
|
|
1545
|
+
// has no pricing entry. fugu-ultra: above 272K context the rates roughly double. Ultra also
|
|
1546
|
+
// reports "orchestration" tokens (its internal expert calls) in prompt_tokens_details /
|
|
1547
|
+
// completion_tokens_details, billed at these same input/output rates — FuguAgent folds them
|
|
1548
|
+
// into the token counts before pricing.
|
|
1525
1549
|
[SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
|
|
1526
1550
|
inputPrice: 5,
|
|
1527
1551
|
outputPrice: 30,
|
|
@@ -1531,6 +1555,12 @@ var MODEL_PRICING = {
|
|
|
1531
1555
|
extendedContextCacheHitPrice: 1,
|
|
1532
1556
|
extendedContextThresholdTokens: 272e3
|
|
1533
1557
|
},
|
|
1558
|
+
// fugu-max: flat rates, no context tiers.
|
|
1559
|
+
[SupportedAiModels[LLM_CONSTANTS.FUGU_MAX].modelApiName]: {
|
|
1560
|
+
inputPrice: 2,
|
|
1561
|
+
outputPrice: 6,
|
|
1562
|
+
cacheHitPrice: 0.25
|
|
1563
|
+
},
|
|
1534
1564
|
// Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
|
|
1535
1565
|
// 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
|
|
1536
1566
|
// qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
|
|
@@ -1581,7 +1611,11 @@ var HYBRID_THINKING_API_NAMES = /* @__PURE__ */ new Set([
|
|
|
1581
1611
|
// hybrid by the same definition: we force reasoning on, so they carry the multiplier.
|
|
1582
1612
|
SupportedAiModels[LLM_CONSTANTS.QWEN_MAX].modelApiName,
|
|
1583
1613
|
SupportedAiModels[LLM_CONSTANTS.QWEN_FLASH].modelApiName,
|
|
1584
|
-
SupportedAiModels[LLM_CONSTANTS.MINIMAX].modelApiName
|
|
1614
|
+
SupportedAiModels[LLM_CONSTANTS.MINIMAX].modelApiName,
|
|
1615
|
+
// Mistral reasons only when asked (`reasoning_effort`), and we always ask — hybrid by the
|
|
1616
|
+
// same definition as Qwen. Added 2026-09-18 when the two entries went thinking-on.
|
|
1617
|
+
SupportedAiModels[LLM_CONSTANTS.MISTRAL_MEDIUM].modelApiName,
|
|
1618
|
+
SupportedAiModels[LLM_CONSTANTS.MISTRAL_SMALL].modelApiName
|
|
1585
1619
|
]);
|
|
1586
1620
|
function isHybridThinkingModel(modelApiName) {
|
|
1587
1621
|
return HYBRID_THINKING_API_NAMES.has(modelApiName);
|
|
@@ -1643,8 +1677,9 @@ var ANTHROPIC_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
1643
1677
|
var GEMINI_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
1644
1678
|
var GLM_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1645
1679
|
var DEEPSEEK_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1646
|
-
var FUGU_REASONING_EFFORTS = ["high", "xhigh"];
|
|
1680
|
+
var FUGU_REASONING_EFFORTS = ["high", "xhigh", "max"];
|
|
1647
1681
|
var META_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1682
|
+
var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
1648
1683
|
function clampReasoningEffort(effort, allowed) {
|
|
1649
1684
|
const rank = REASONING_EFFORT_SCALE.indexOf(effort);
|
|
1650
1685
|
let best = allowed[0];
|
|
@@ -1665,6 +1700,7 @@ var toGlmEffort = (effort) => clampReasoningEffort(effort, GLM_REASONING_EFFORTS
|
|
|
1665
1700
|
var toDeepSeekEffort = (effort) => clampReasoningEffort(effort, DEEPSEEK_REASONING_EFFORTS);
|
|
1666
1701
|
var toMetaEffort = (effort) => clampReasoningEffort(effort, META_REASONING_EFFORTS);
|
|
1667
1702
|
var toFuguEffort = (effort) => clampReasoningEffort(effort, FUGU_REASONING_EFFORTS);
|
|
1703
|
+
var toMistralEffort = (effort) => clampReasoningEffort(effort, MISTRAL_REASONING_EFFORTS);
|
|
1668
1704
|
|
|
1669
1705
|
// src/pricing/token-usage-utils.ts
|
|
1670
1706
|
function extractTokenUsage(response) {
|
|
@@ -3397,6 +3433,9 @@ ${instruction}`);
|
|
|
3397
3433
|
const body = await request.clone().text();
|
|
3398
3434
|
const json = JSON.parse(body);
|
|
3399
3435
|
json.prompt_cache_key = promptCacheKey;
|
|
3436
|
+
if (this.enableThinking && this.reasoningEffort) {
|
|
3437
|
+
json.reasoning_effort = toMistralEffort(this.reasoningEffort);
|
|
3438
|
+
}
|
|
3400
3439
|
return new Request(request.url, {
|
|
3401
3440
|
method: request.method,
|
|
3402
3441
|
headers: request.headers,
|
|
@@ -3409,11 +3448,25 @@ ${instruction}`);
|
|
|
3409
3448
|
});
|
|
3410
3449
|
this.client = new import_mistralai.Mistral({ apiKey, httpClient });
|
|
3411
3450
|
}
|
|
3451
|
+
/**
|
|
3452
|
+
* History in Mistral's shape. A prior assistant turn that carries a stored trace is
|
|
3453
|
+
* replayed as [thinking, text] chunks, as Mistral's multi-turn guidance asks; every other
|
|
3454
|
+
* turn is a plain string.
|
|
3455
|
+
*/
|
|
3412
3456
|
convertToMistralMessages(messages) {
|
|
3413
|
-
return this.prepareMessages(messages).map((msg) =>
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3457
|
+
return this.prepareMessages(messages).map((msg) => {
|
|
3458
|
+
if (msg.role === "assistant" && msg.thinking) {
|
|
3459
|
+
const chunks = [
|
|
3460
|
+
{ type: "thinking", thinking: [{ type: "text", text: msg.thinking }] },
|
|
3461
|
+
{ type: "text", text: msg.content }
|
|
3462
|
+
];
|
|
3463
|
+
return { role: "assistant", content: chunks };
|
|
3464
|
+
}
|
|
3465
|
+
return {
|
|
3466
|
+
role: msg.role === "developer" ? "system" : msg.role,
|
|
3467
|
+
content: msg.content
|
|
3468
|
+
};
|
|
3469
|
+
});
|
|
3417
3470
|
}
|
|
3418
3471
|
processReply(response) {
|
|
3419
3472
|
const message = response?.choices?.[0]?.message;
|
|
@@ -3448,7 +3501,6 @@ ${instruction}`);
|
|
|
3448
3501
|
extractTokenUsage(response) {
|
|
3449
3502
|
const usage = extractMistralTokenUsage(response);
|
|
3450
3503
|
if (!usage) return void 0;
|
|
3451
|
-
this.logger(`MISTRAL_CACHE_CALIBRATION raw usage: ${JSON.stringify(response?.usage)}`);
|
|
3452
3504
|
if (usage.reasoningTokens && usage.reasoningTokens > 0) {
|
|
3453
3505
|
this.logger(`\u{1F9E0} Reasoning tokens used: ${usage.reasoningTokens}`);
|
|
3454
3506
|
}
|
|
@@ -3484,7 +3536,7 @@ ${instruction}`);
|
|
|
3484
3536
|
const convertedMessages = this.convertToMistralMessages(messages);
|
|
3485
3537
|
if (convertedMessages.length > 0) {
|
|
3486
3538
|
const lastMessage = convertedMessages[convertedMessages.length - 1];
|
|
3487
|
-
if (lastMessage && lastMessage.content) {
|
|
3539
|
+
if (lastMessage && typeof lastMessage.content === "string" && lastMessage.content) {
|
|
3488
3540
|
lastMessage.content += `
|
|
3489
3541
|
|
|
3490
3542
|
Your response must be a valid JSON object matching this schema:
|
|
@@ -3555,9 +3607,8 @@ ${schemaDescription}`
|
|
|
3555
3607
|
}
|
|
3556
3608
|
}
|
|
3557
3609
|
/**
|
|
3558
|
-
* Plain-text ask: no schema appended, no responseFormat.
|
|
3559
|
-
*
|
|
3560
|
-
* json_object, so unlike askWithZodSchema this path can surface thinking content.
|
|
3610
|
+
* Plain-text ask: no schema appended, no responseFormat. Reasoning (and the trace) works
|
|
3611
|
+
* the same way as on the schema path.
|
|
3561
3612
|
*/
|
|
3562
3613
|
async doAskText(messages) {
|
|
3563
3614
|
try {
|
|
@@ -4394,13 +4445,15 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4394
4445
|
client;
|
|
4395
4446
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
4396
4447
|
// initializer would snapshot the default and silently ignore the override.
|
|
4448
|
+
// `reasoning_effort` is re-declared as string: the OpenAI SDK's union lacks Sakana's 'max'.
|
|
4397
4449
|
get defaultParams() {
|
|
4398
4450
|
return {
|
|
4399
4451
|
model: this.model,
|
|
4400
4452
|
stream: false,
|
|
4401
|
-
// Caps
|
|
4402
|
-
//
|
|
4403
|
-
max_tokens: this.maxOutputTokens
|
|
4453
|
+
// Caps the final reply only. Per Sakana's docs the orchestrator "still uses maximum
|
|
4454
|
+
// token limit", so this bounds neither Ultra's latency nor its orchestration spend.
|
|
4455
|
+
max_tokens: this.maxOutputTokens,
|
|
4456
|
+
reasoning_effort: toFuguEffort(this.reasoningEffort ?? "high")
|
|
4404
4457
|
};
|
|
4405
4458
|
}
|
|
4406
4459
|
logTemplates = {
|
|
@@ -4425,33 +4478,6 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4425
4478
|
content: msg.content
|
|
4426
4479
|
}));
|
|
4427
4480
|
}
|
|
4428
|
-
// ───────────────────────────────────────────────────────────────────────────────────────
|
|
4429
|
-
// TEMPORARY (cost calibration). Base `fugu` is a dynamic router with no published per-token
|
|
4430
|
-
// price and Sakana returns NO cost field in the response — only token counts. Crucially the
|
|
4431
|
-
// response also reports "orchestration tokens" (billed at input/output rates per Sakana's
|
|
4432
|
-
// pricing page) that our standard TokenUsage drops. This logs the full raw breakdown to
|
|
4433
|
-
// BetterStack under a distinctive tag so we can sum real tokens per game and, combined with
|
|
4434
|
-
// the Sakana billing dashboard total, derive the true per-token rate. REMOVE AFTER CALIBRATION.
|
|
4435
|
-
logRawUsageForCalibration(completion) {
|
|
4436
|
-
const usage = completion?.usage;
|
|
4437
|
-
if (!usage) return;
|
|
4438
|
-
logger.info("FUGU_COST_CALIBRATION", {
|
|
4439
|
-
tag: "FUGU_COST_CALIBRATION",
|
|
4440
|
-
model: this.model,
|
|
4441
|
-
agentName: this.name,
|
|
4442
|
-
gameId: this.gameId,
|
|
4443
|
-
userId: this.userId,
|
|
4444
|
-
promptTokens: usage.prompt_tokens ?? 0,
|
|
4445
|
-
completionTokens: usage.completion_tokens ?? 0,
|
|
4446
|
-
totalTokens: usage.total_tokens ?? 0,
|
|
4447
|
-
cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
|
|
4448
|
-
orchestrationInputTokens: usage.prompt_tokens_details?.orchestration_input_tokens ?? 0,
|
|
4449
|
-
orchestrationInputCachedTokens: usage.prompt_tokens_details?.orchestration_input_cached_tokens ?? 0,
|
|
4450
|
-
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
4451
|
-
orchestrationOutputTokens: usage.completion_tokens_details?.orchestration_output_tokens ?? 0,
|
|
4452
|
-
rawUsage: usage
|
|
4453
|
-
});
|
|
4454
|
-
}
|
|
4455
4481
|
extractThinkingAndUsage(completion) {
|
|
4456
4482
|
let thinkingContent = "";
|
|
4457
4483
|
const message = completion.choices[0]?.message;
|
|
@@ -4459,21 +4485,14 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4459
4485
|
thinkingContent = message.reasoning_content;
|
|
4460
4486
|
this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
|
|
4461
4487
|
}
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
totalTokens: usageResult.usage.totalTokens,
|
|
4469
|
-
costUSD: usageResult.cost,
|
|
4470
|
-
...usageResult.usage.cacheHitTokens !== void 0 ? { cachedInputTokens: usageResult.usage.cacheHitTokens } : {}
|
|
4471
|
-
};
|
|
4472
|
-
if (usageResult.usage.reasoningTokens) {
|
|
4473
|
-
const reasoningTokens = usageResult.usage.reasoningTokens;
|
|
4474
|
-
const finalAnswerTokens = Math.max(0, tokenUsage.outputTokens - reasoningTokens);
|
|
4488
|
+
const tokenUsage = extractFuguTokenUsage(this.model, completion);
|
|
4489
|
+
if (tokenUsage) {
|
|
4490
|
+
const details = completion.usage ?? {};
|
|
4491
|
+
const orchestrationIn = details.prompt_tokens_details?.orchestration_input_tokens ?? 0;
|
|
4492
|
+
const orchestrationOut = details.completion_tokens_details?.orchestration_output_tokens ?? 0;
|
|
4493
|
+
if (tokenUsage.reasoningTokens || orchestrationIn || orchestrationOut) {
|
|
4475
4494
|
this.logger(
|
|
4476
|
-
`
|
|
4495
|
+
`Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
|
|
4477
4496
|
);
|
|
4478
4497
|
}
|
|
4479
4498
|
}
|
|
@@ -4515,7 +4534,6 @@ ${schemaDescription}`;
|
|
|
4515
4534
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4516
4535
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4517
4536
|
}
|
|
4518
|
-
this.logRawUsageForCalibration(completion);
|
|
4519
4537
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4520
4538
|
if (!rawReply) {
|
|
4521
4539
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4559,7 +4577,6 @@ ${schemaDescription}`;
|
|
|
4559
4577
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4560
4578
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4561
4579
|
}
|
|
4562
|
-
this.logRawUsageForCalibration(completion);
|
|
4563
4580
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4564
4581
|
if (!rawReply) {
|
|
4565
4582
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4578,6 +4595,31 @@ ${schemaDescription}`;
|
|
|
4578
4595
|
}
|
|
4579
4596
|
}
|
|
4580
4597
|
};
|
|
4598
|
+
function extractFuguTokenUsage(modelApiName, completion) {
|
|
4599
|
+
const usage = completion?.usage;
|
|
4600
|
+
if (!usage) {
|
|
4601
|
+
return void 0;
|
|
4602
|
+
}
|
|
4603
|
+
const promptDetails = usage.prompt_tokens_details ?? {};
|
|
4604
|
+
const completionDetails = usage.completion_tokens_details ?? {};
|
|
4605
|
+
const inputTokens = (usage.prompt_tokens ?? 0) + (promptDetails.orchestration_input_tokens ?? 0);
|
|
4606
|
+
const outputTokens = (usage.completion_tokens ?? 0) + (completionDetails.orchestration_output_tokens ?? 0);
|
|
4607
|
+
const cachedInputTokens = (promptDetails.cached_tokens ?? 0) + (promptDetails.orchestration_input_cached_tokens ?? 0);
|
|
4608
|
+
const reasoningTokens = completionDetails.reasoning_tokens ?? 0;
|
|
4609
|
+
return {
|
|
4610
|
+
inputTokens,
|
|
4611
|
+
outputTokens,
|
|
4612
|
+
totalTokens: inputTokens + outputTokens,
|
|
4613
|
+
cachedInputTokens,
|
|
4614
|
+
// Omitted rather than 0: consumers persist the object as-is (see TokenUsage).
|
|
4615
|
+
...reasoningTokens > 0 ? { reasoningTokens } : {},
|
|
4616
|
+
costUSD: calculateModelCost(modelApiName, inputTokens, outputTokens, {
|
|
4617
|
+
cacheHitTokens: cachedInputTokens,
|
|
4618
|
+
// Context-tier selection: the request's own size, not the orchestrator's traffic.
|
|
4619
|
+
contextTokens: usage.total_tokens ?? inputTokens
|
|
4620
|
+
})
|
|
4621
|
+
};
|
|
4622
|
+
}
|
|
4581
4623
|
|
|
4582
4624
|
// src/agents/qwen-agent.ts
|
|
4583
4625
|
var import_openai9 = require("openai");
|
|
@@ -5229,8 +5271,6 @@ var AgentFactory = class {
|
|
|
5229
5271
|
// Mistral models
|
|
5230
5272
|
case LLM_CONSTANTS.MISTRAL_MEDIUM:
|
|
5231
5273
|
case LLM_CONSTANTS.MISTRAL_SMALL:
|
|
5232
|
-
case LLM_CONSTANTS.MISTRAL_LARGE:
|
|
5233
|
-
case LLM_CONSTANTS.MISTRAL_MAGISTRAL:
|
|
5234
5274
|
return new MistralAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
|
|
5235
5275
|
case LLM_CONSTANTS.KIMI:
|
|
5236
5276
|
return new KimiAgent(name, instruction, model.modelApiName, key, 0, shouldEnableThinking);
|
|
@@ -5240,6 +5280,7 @@ var AgentFactory = class {
|
|
|
5240
5280
|
return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
|
|
5241
5281
|
// Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
|
|
5242
5282
|
case LLM_CONSTANTS.FUGU_ULTRA:
|
|
5283
|
+
case LLM_CONSTANTS.FUGU_MAX:
|
|
5243
5284
|
return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
|
|
5244
5285
|
// Qwen models — thinking-only (enable_thinking always sent)
|
|
5245
5286
|
case LLM_CONSTANTS.QWEN_MAX:
|
|
@@ -5294,6 +5335,7 @@ var AgentFactory = class {
|
|
|
5294
5335
|
LLM_CONSTANTS,
|
|
5295
5336
|
MESSAGE_ROLE,
|
|
5296
5337
|
META_REASONING_EFFORTS,
|
|
5338
|
+
MISTRAL_REASONING_EFFORTS,
|
|
5297
5339
|
MODEL_PRICING,
|
|
5298
5340
|
MetaAgent,
|
|
5299
5341
|
MiniMaxAgent,
|
|
@@ -5395,6 +5437,7 @@ var AgentFactory = class {
|
|
|
5395
5437
|
toGeminiEffort,
|
|
5396
5438
|
toGlmEffort,
|
|
5397
5439
|
toMetaEffort,
|
|
5440
|
+
toMistralEffort,
|
|
5398
5441
|
toOpenAIEffort,
|
|
5399
5442
|
transcribeWithGemini,
|
|
5400
5443
|
transcribeWithOpenAi,
|