@hiper2d/ai-agents 0.6.0 → 0.8.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 +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +96 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +96 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -805,6 +805,7 @@ var LLM_CONSTANTS = {
|
|
|
805
805
|
GLM: "glm",
|
|
806
806
|
GLM_FLASH: "glm-flash",
|
|
807
807
|
FUGU_ULTRA: "fugu-ultra",
|
|
808
|
+
FUGU_MAX: "fugu-max",
|
|
808
809
|
// Qwen (QwenCloud/DashScope). Stable picker ids without the version, matching the gpt/gemini
|
|
809
810
|
// pattern, so future repoints don't orphan persisted ids.
|
|
810
811
|
QWEN_MAX: "qwen-max",
|
|
@@ -826,9 +827,13 @@ var SupportedAiModels = {
|
|
|
826
827
|
tags: ["expensive"]
|
|
827
828
|
},
|
|
828
829
|
// Claude models — thinking-only entries (non-thinking variants retired 2026-08-05)
|
|
830
|
+
// Opus moved 5 → 5.5 on 2026-09-22 (`claude-opus-5-5`, verified against GET /v1/models).
|
|
831
|
+
// Cheaper than Opus 5 ($4/$20 vs $5/$25) and the same 1M context / 128K output. Thinking is
|
|
832
|
+
// always on, as on Opus 5. Its API default effort is `medium`, but we keep the catalog-wide
|
|
833
|
+
// `high` pin so bot answers don't get shallower than every other model in the lobby.
|
|
829
834
|
[LLM_CONSTANTS.CLAUDE_OPUS]: {
|
|
830
|
-
displayName: "Claude 5 Opus",
|
|
831
|
-
modelApiName: "claude-opus-5",
|
|
835
|
+
displayName: "Claude 5.5 Opus",
|
|
836
|
+
modelApiName: "claude-opus-5-5",
|
|
832
837
|
apiKeyName: API_KEY_CONSTANTS.ANTHROPIC,
|
|
833
838
|
hasThinking: true,
|
|
834
839
|
reasoningEffort: "high",
|
|
@@ -1050,13 +1055,37 @@ var SupportedAiModels = {
|
|
|
1050
1055
|
// so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
|
|
1051
1056
|
// zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
|
|
1052
1057
|
// prefix reuse. Ultra costs the same and is predictable.
|
|
1058
|
+
//
|
|
1059
|
+
// reasoningEffort is PINNED on both entries because the server default differs per model:
|
|
1060
|
+
// fugu-ultra defaults to `xhigh` (console.sakana.ai/models), fugu-max to `high`. Until
|
|
1061
|
+
// 2026-09-20 the agent sent no effort at all, so every Ultra turn ran at xhigh. Measured that
|
|
1062
|
+
// day on a 2k-token three-day game prompt (two samples each, chat completions, json mode):
|
|
1063
|
+
// ultra xhigh 148s / 265s $0.34 / $0.50 a turn (orchestration tokens included)
|
|
1064
|
+
// ultra high 65s / 78s $0.16 / $0.17
|
|
1065
|
+
// max high 6s / 10s $0.006 / $0.008
|
|
1066
|
+
// max xhigh 41s / 47s $0.02
|
|
1067
|
+
// `max_tokens` is not a latency lever for Ultra: Sakana applies it to the final response only,
|
|
1068
|
+
// the orchestrator "still uses maximum token limit". Effort is the only knob.
|
|
1053
1069
|
[LLM_CONSTANTS.FUGU_ULTRA]: {
|
|
1054
1070
|
displayName: "Sakana Fugu Ultra",
|
|
1055
1071
|
modelApiName: "fugu-ultra",
|
|
1056
1072
|
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1057
1073
|
hasThinking: false,
|
|
1074
|
+
reasoningEffort: "high",
|
|
1058
1075
|
tags: ["extremely-slow", "expensive"]
|
|
1059
1076
|
},
|
|
1077
|
+
// Fugu Max (2026-09-20, fugu-max → fugu-max-v1.0): Sakana's "largest pool of models on the
|
|
1078
|
+
// cost–performance Pareto frontier" — the everyday Fugu at 5x less than Ultra's output rate
|
|
1079
|
+
// and a fraction of its latency. It reasons (reasoning_tokens 80-500 a turn) but, like
|
|
1080
|
+
// Ultra, never returns reasoning_content, hence hasThinking: false. No orchestration
|
|
1081
|
+
// tokens observed on any call.
|
|
1082
|
+
[LLM_CONSTANTS.FUGU_MAX]: {
|
|
1083
|
+
displayName: "Sakana Fugu Max",
|
|
1084
|
+
modelApiName: "fugu-max",
|
|
1085
|
+
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1086
|
+
hasThinking: false,
|
|
1087
|
+
reasoningEffort: "high"
|
|
1088
|
+
},
|
|
1060
1089
|
// Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
|
|
1061
1090
|
// thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
|
|
1062
1091
|
// thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
|
|
@@ -1277,9 +1306,13 @@ var MODEL_PRICING = {
|
|
|
1277
1306
|
cacheHitPrice: 0.25
|
|
1278
1307
|
},
|
|
1279
1308
|
[SupportedAiModels[LLM_CONSTANTS.CLAUDE_OPUS].modelApiName]: {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1309
|
+
// Opus 5.5 (2026-09-22): $4/$20, down from Opus 5's $5/$25. Cache reads are 5% of input
|
|
1310
|
+
// here, not the usual 10% — $0.20, not $0.40. Cache WRITES ($5 at 5m, $8 at 1h) are not
|
|
1311
|
+
// modelled: calculateCost bills every uncached prompt token at inputPrice, so a write is
|
|
1312
|
+
// under-billed by the 1.25x/2x premium. Pre-existing for every Anthropic model.
|
|
1313
|
+
inputPrice: 4,
|
|
1314
|
+
outputPrice: 20,
|
|
1315
|
+
cacheHitPrice: 0.2
|
|
1283
1316
|
},
|
|
1284
1317
|
[SupportedAiModels[LLM_CONSTANTS.CLAUDE_SONNET].modelApiName]: {
|
|
1285
1318
|
inputPrice: 2,
|
|
@@ -1344,9 +1377,12 @@ var MODEL_PRICING = {
|
|
|
1344
1377
|
extendedContextCacheHitPrice: 1,
|
|
1345
1378
|
extendedContextThresholdTokens: 2e5
|
|
1346
1379
|
},
|
|
1347
|
-
// Sakana Fugu models. Base `fugu` was retired
|
|
1348
|
-
// measured out at these same ultra rates, so it
|
|
1349
|
-
//
|
|
1380
|
+
// Sakana Fugu models (console.sakana.ai/pricing, read 2026-09-20). Base `fugu` was retired
|
|
1381
|
+
// 2026-08-04 — it had no published price and measured out at these same ultra rates, so it
|
|
1382
|
+
// has no pricing entry. fugu-ultra: above 272K context the rates roughly double. Ultra also
|
|
1383
|
+
// reports "orchestration" tokens (its internal expert calls) in prompt_tokens_details /
|
|
1384
|
+
// completion_tokens_details, billed at these same input/output rates — FuguAgent folds them
|
|
1385
|
+
// into the token counts before pricing.
|
|
1350
1386
|
[SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
|
|
1351
1387
|
inputPrice: 5,
|
|
1352
1388
|
outputPrice: 30,
|
|
@@ -1356,6 +1392,12 @@ var MODEL_PRICING = {
|
|
|
1356
1392
|
extendedContextCacheHitPrice: 1,
|
|
1357
1393
|
extendedContextThresholdTokens: 272e3
|
|
1358
1394
|
},
|
|
1395
|
+
// fugu-max: flat rates, no context tiers.
|
|
1396
|
+
[SupportedAiModels[LLM_CONSTANTS.FUGU_MAX].modelApiName]: {
|
|
1397
|
+
inputPrice: 2,
|
|
1398
|
+
outputPrice: 6,
|
|
1399
|
+
cacheHitPrice: 0.25
|
|
1400
|
+
},
|
|
1359
1401
|
// Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
|
|
1360
1402
|
// 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
|
|
1361
1403
|
// qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
|
|
@@ -1472,7 +1514,7 @@ var ANTHROPIC_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
1472
1514
|
var GEMINI_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
1473
1515
|
var GLM_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1474
1516
|
var DEEPSEEK_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1475
|
-
var FUGU_REASONING_EFFORTS = ["high", "xhigh"];
|
|
1517
|
+
var FUGU_REASONING_EFFORTS = ["high", "xhigh", "max"];
|
|
1476
1518
|
var META_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1477
1519
|
var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
1478
1520
|
function clampReasoningEffort(effort, allowed) {
|
|
@@ -2428,6 +2470,7 @@ ${instruction}`);
|
|
|
2428
2470
|
|
|
2429
2471
|
// src/agents/anthropic-agent.ts
|
|
2430
2472
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
2473
|
+
var rejectsDisabledThinking = (model) => model.includes("fable") || model.includes("opus-5-5");
|
|
2431
2474
|
var ClaudeAgent = class extends AbstractAgent {
|
|
2432
2475
|
client;
|
|
2433
2476
|
/**
|
|
@@ -2644,7 +2687,9 @@ ${schemaDescription}`;
|
|
|
2644
2687
|
params.temperature = 1;
|
|
2645
2688
|
}
|
|
2646
2689
|
} else if (usesAdaptiveThinking) {
|
|
2647
|
-
|
|
2690
|
+
if (!rejectsDisabledThinking(this.model)) {
|
|
2691
|
+
params.thinking = { type: "disabled" };
|
|
2692
|
+
}
|
|
2648
2693
|
} else {
|
|
2649
2694
|
params.temperature = this.temperature;
|
|
2650
2695
|
}
|
|
@@ -2738,7 +2783,9 @@ ${schemaDescription}`;
|
|
|
2738
2783
|
params.temperature = 1;
|
|
2739
2784
|
}
|
|
2740
2785
|
} else if (usesAdaptiveThinking) {
|
|
2741
|
-
|
|
2786
|
+
if (!rejectsDisabledThinking(this.model)) {
|
|
2787
|
+
params.thinking = { type: "disabled" };
|
|
2788
|
+
}
|
|
2742
2789
|
} else {
|
|
2743
2790
|
params.temperature = this.temperature;
|
|
2744
2791
|
}
|
|
@@ -4240,13 +4287,15 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4240
4287
|
client;
|
|
4241
4288
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
4242
4289
|
// initializer would snapshot the default and silently ignore the override.
|
|
4290
|
+
// `reasoning_effort` is re-declared as string: the OpenAI SDK's union lacks Sakana's 'max'.
|
|
4243
4291
|
get defaultParams() {
|
|
4244
4292
|
return {
|
|
4245
4293
|
model: this.model,
|
|
4246
4294
|
stream: false,
|
|
4247
|
-
// Caps
|
|
4248
|
-
//
|
|
4249
|
-
max_tokens: this.maxOutputTokens
|
|
4295
|
+
// Caps the final reply only. Per Sakana's docs the orchestrator "still uses maximum
|
|
4296
|
+
// token limit", so this bounds neither Ultra's latency nor its orchestration spend.
|
|
4297
|
+
max_tokens: this.maxOutputTokens,
|
|
4298
|
+
reasoning_effort: toFuguEffort(this.reasoningEffort ?? "high")
|
|
4250
4299
|
};
|
|
4251
4300
|
}
|
|
4252
4301
|
logTemplates = {
|
|
@@ -4271,33 +4320,6 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4271
4320
|
content: msg.content
|
|
4272
4321
|
}));
|
|
4273
4322
|
}
|
|
4274
|
-
// ───────────────────────────────────────────────────────────────────────────────────────
|
|
4275
|
-
// TEMPORARY (cost calibration). Base `fugu` is a dynamic router with no published per-token
|
|
4276
|
-
// price and Sakana returns NO cost field in the response — only token counts. Crucially the
|
|
4277
|
-
// response also reports "orchestration tokens" (billed at input/output rates per Sakana's
|
|
4278
|
-
// pricing page) that our standard TokenUsage drops. This logs the full raw breakdown to
|
|
4279
|
-
// BetterStack under a distinctive tag so we can sum real tokens per game and, combined with
|
|
4280
|
-
// the Sakana billing dashboard total, derive the true per-token rate. REMOVE AFTER CALIBRATION.
|
|
4281
|
-
logRawUsageForCalibration(completion) {
|
|
4282
|
-
const usage = completion?.usage;
|
|
4283
|
-
if (!usage) return;
|
|
4284
|
-
logger.info("FUGU_COST_CALIBRATION", {
|
|
4285
|
-
tag: "FUGU_COST_CALIBRATION",
|
|
4286
|
-
model: this.model,
|
|
4287
|
-
agentName: this.name,
|
|
4288
|
-
gameId: this.gameId,
|
|
4289
|
-
userId: this.userId,
|
|
4290
|
-
promptTokens: usage.prompt_tokens ?? 0,
|
|
4291
|
-
completionTokens: usage.completion_tokens ?? 0,
|
|
4292
|
-
totalTokens: usage.total_tokens ?? 0,
|
|
4293
|
-
cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
|
|
4294
|
-
orchestrationInputTokens: usage.prompt_tokens_details?.orchestration_input_tokens ?? 0,
|
|
4295
|
-
orchestrationInputCachedTokens: usage.prompt_tokens_details?.orchestration_input_cached_tokens ?? 0,
|
|
4296
|
-
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
4297
|
-
orchestrationOutputTokens: usage.completion_tokens_details?.orchestration_output_tokens ?? 0,
|
|
4298
|
-
rawUsage: usage
|
|
4299
|
-
});
|
|
4300
|
-
}
|
|
4301
4323
|
extractThinkingAndUsage(completion) {
|
|
4302
4324
|
let thinkingContent = "";
|
|
4303
4325
|
const message = completion.choices[0]?.message;
|
|
@@ -4305,21 +4327,14 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4305
4327
|
thinkingContent = message.reasoning_content;
|
|
4306
4328
|
this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
|
|
4307
4329
|
}
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
totalTokens: usageResult.usage.totalTokens,
|
|
4315
|
-
costUSD: usageResult.cost,
|
|
4316
|
-
...usageResult.usage.cacheHitTokens !== void 0 ? { cachedInputTokens: usageResult.usage.cacheHitTokens } : {}
|
|
4317
|
-
};
|
|
4318
|
-
if (usageResult.usage.reasoningTokens) {
|
|
4319
|
-
const reasoningTokens = usageResult.usage.reasoningTokens;
|
|
4320
|
-
const finalAnswerTokens = Math.max(0, tokenUsage.outputTokens - reasoningTokens);
|
|
4330
|
+
const tokenUsage = extractFuguTokenUsage(this.model, completion);
|
|
4331
|
+
if (tokenUsage) {
|
|
4332
|
+
const details = completion.usage ?? {};
|
|
4333
|
+
const orchestrationIn = details.prompt_tokens_details?.orchestration_input_tokens ?? 0;
|
|
4334
|
+
const orchestrationOut = details.completion_tokens_details?.orchestration_output_tokens ?? 0;
|
|
4335
|
+
if (tokenUsage.reasoningTokens || orchestrationIn || orchestrationOut) {
|
|
4321
4336
|
this.logger(
|
|
4322
|
-
`
|
|
4337
|
+
`Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
|
|
4323
4338
|
);
|
|
4324
4339
|
}
|
|
4325
4340
|
}
|
|
@@ -4361,7 +4376,6 @@ ${schemaDescription}`;
|
|
|
4361
4376
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4362
4377
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4363
4378
|
}
|
|
4364
|
-
this.logRawUsageForCalibration(completion);
|
|
4365
4379
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4366
4380
|
if (!rawReply) {
|
|
4367
4381
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4405,7 +4419,6 @@ ${schemaDescription}`;
|
|
|
4405
4419
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4406
4420
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4407
4421
|
}
|
|
4408
|
-
this.logRawUsageForCalibration(completion);
|
|
4409
4422
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4410
4423
|
if (!rawReply) {
|
|
4411
4424
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4424,6 +4437,31 @@ ${schemaDescription}`;
|
|
|
4424
4437
|
}
|
|
4425
4438
|
}
|
|
4426
4439
|
};
|
|
4440
|
+
function extractFuguTokenUsage(modelApiName, completion) {
|
|
4441
|
+
const usage = completion?.usage;
|
|
4442
|
+
if (!usage) {
|
|
4443
|
+
return void 0;
|
|
4444
|
+
}
|
|
4445
|
+
const promptDetails = usage.prompt_tokens_details ?? {};
|
|
4446
|
+
const completionDetails = usage.completion_tokens_details ?? {};
|
|
4447
|
+
const inputTokens = (usage.prompt_tokens ?? 0) + (promptDetails.orchestration_input_tokens ?? 0);
|
|
4448
|
+
const outputTokens = (usage.completion_tokens ?? 0) + (completionDetails.orchestration_output_tokens ?? 0);
|
|
4449
|
+
const cachedInputTokens = (promptDetails.cached_tokens ?? 0) + (promptDetails.orchestration_input_cached_tokens ?? 0);
|
|
4450
|
+
const reasoningTokens = completionDetails.reasoning_tokens ?? 0;
|
|
4451
|
+
return {
|
|
4452
|
+
inputTokens,
|
|
4453
|
+
outputTokens,
|
|
4454
|
+
totalTokens: inputTokens + outputTokens,
|
|
4455
|
+
cachedInputTokens,
|
|
4456
|
+
// Omitted rather than 0: consumers persist the object as-is (see TokenUsage).
|
|
4457
|
+
...reasoningTokens > 0 ? { reasoningTokens } : {},
|
|
4458
|
+
costUSD: calculateModelCost(modelApiName, inputTokens, outputTokens, {
|
|
4459
|
+
cacheHitTokens: cachedInputTokens,
|
|
4460
|
+
// Context-tier selection: the request's own size, not the orchestrator's traffic.
|
|
4461
|
+
contextTokens: usage.total_tokens ?? inputTokens
|
|
4462
|
+
})
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4427
4465
|
|
|
4428
4466
|
// src/agents/qwen-agent.ts
|
|
4429
4467
|
import { OpenAI as OpenAI9 } from "openai";
|
|
@@ -5084,6 +5122,7 @@ var AgentFactory = class {
|
|
|
5084
5122
|
return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
|
|
5085
5123
|
// Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
|
|
5086
5124
|
case LLM_CONSTANTS.FUGU_ULTRA:
|
|
5125
|
+
case LLM_CONSTANTS.FUGU_MAX:
|
|
5087
5126
|
return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
|
|
5088
5127
|
// Qwen models — thinking-only (enable_thinking always sent)
|
|
5089
5128
|
case LLM_CONSTANTS.QWEN_MAX:
|