@hiper2d/ai-agents 0.6.0 → 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 +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +76 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -50
- 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",
|
|
@@ -1050,13 +1051,37 @@ var SupportedAiModels = {
|
|
|
1050
1051
|
// so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
|
|
1051
1052
|
// zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
|
|
1052
1053
|
// prefix reuse. Ultra costs the same and is predictable.
|
|
1054
|
+
//
|
|
1055
|
+
// reasoningEffort is PINNED on both entries because the server default differs per model:
|
|
1056
|
+
// fugu-ultra defaults to `xhigh` (console.sakana.ai/models), fugu-max to `high`. Until
|
|
1057
|
+
// 2026-09-20 the agent sent no effort at all, so every Ultra turn ran at xhigh. Measured that
|
|
1058
|
+
// day on a 2k-token three-day game prompt (two samples each, chat completions, json mode):
|
|
1059
|
+
// ultra xhigh 148s / 265s $0.34 / $0.50 a turn (orchestration tokens included)
|
|
1060
|
+
// ultra high 65s / 78s $0.16 / $0.17
|
|
1061
|
+
// max high 6s / 10s $0.006 / $0.008
|
|
1062
|
+
// max xhigh 41s / 47s $0.02
|
|
1063
|
+
// `max_tokens` is not a latency lever for Ultra: Sakana applies it to the final response only,
|
|
1064
|
+
// the orchestrator "still uses maximum token limit". Effort is the only knob.
|
|
1053
1065
|
[LLM_CONSTANTS.FUGU_ULTRA]: {
|
|
1054
1066
|
displayName: "Sakana Fugu Ultra",
|
|
1055
1067
|
modelApiName: "fugu-ultra",
|
|
1056
1068
|
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1057
1069
|
hasThinking: false,
|
|
1070
|
+
reasoningEffort: "high",
|
|
1058
1071
|
tags: ["extremely-slow", "expensive"]
|
|
1059
1072
|
},
|
|
1073
|
+
// Fugu Max (2026-09-20, fugu-max → fugu-max-v1.0): Sakana's "largest pool of models on the
|
|
1074
|
+
// cost–performance Pareto frontier" — the everyday Fugu at 5x less than Ultra's output rate
|
|
1075
|
+
// and a fraction of its latency. It reasons (reasoning_tokens 80-500 a turn) but, like
|
|
1076
|
+
// Ultra, never returns reasoning_content, hence hasThinking: false. No orchestration
|
|
1077
|
+
// tokens observed on any call.
|
|
1078
|
+
[LLM_CONSTANTS.FUGU_MAX]: {
|
|
1079
|
+
displayName: "Sakana Fugu Max",
|
|
1080
|
+
modelApiName: "fugu-max",
|
|
1081
|
+
apiKeyName: API_KEY_CONSTANTS.FUGU,
|
|
1082
|
+
hasThinking: false,
|
|
1083
|
+
reasoningEffort: "high"
|
|
1084
|
+
},
|
|
1060
1085
|
// Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
|
|
1061
1086
|
// thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
|
|
1062
1087
|
// thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
|
|
@@ -1344,9 +1369,12 @@ var MODEL_PRICING = {
|
|
|
1344
1369
|
extendedContextCacheHitPrice: 1,
|
|
1345
1370
|
extendedContextThresholdTokens: 2e5
|
|
1346
1371
|
},
|
|
1347
|
-
// Sakana Fugu models. Base `fugu` was retired
|
|
1348
|
-
// measured out at these same ultra rates, so it
|
|
1349
|
-
//
|
|
1372
|
+
// Sakana Fugu models (console.sakana.ai/pricing, read 2026-09-20). Base `fugu` was retired
|
|
1373
|
+
// 2026-08-04 — it had no published price and measured out at these same ultra rates, so it
|
|
1374
|
+
// has no pricing entry. fugu-ultra: above 272K context the rates roughly double. Ultra also
|
|
1375
|
+
// reports "orchestration" tokens (its internal expert calls) in prompt_tokens_details /
|
|
1376
|
+
// completion_tokens_details, billed at these same input/output rates — FuguAgent folds them
|
|
1377
|
+
// into the token counts before pricing.
|
|
1350
1378
|
[SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
|
|
1351
1379
|
inputPrice: 5,
|
|
1352
1380
|
outputPrice: 30,
|
|
@@ -1356,6 +1384,12 @@ var MODEL_PRICING = {
|
|
|
1356
1384
|
extendedContextCacheHitPrice: 1,
|
|
1357
1385
|
extendedContextThresholdTokens: 272e3
|
|
1358
1386
|
},
|
|
1387
|
+
// fugu-max: flat rates, no context tiers.
|
|
1388
|
+
[SupportedAiModels[LLM_CONSTANTS.FUGU_MAX].modelApiName]: {
|
|
1389
|
+
inputPrice: 2,
|
|
1390
|
+
outputPrice: 6,
|
|
1391
|
+
cacheHitPrice: 0.25
|
|
1392
|
+
},
|
|
1359
1393
|
// Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
|
|
1360
1394
|
// 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
|
|
1361
1395
|
// qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
|
|
@@ -1472,7 +1506,7 @@ var ANTHROPIC_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
|
1472
1506
|
var GEMINI_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
1473
1507
|
var GLM_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1474
1508
|
var DEEPSEEK_REASONING_EFFORTS = ["low", "high", "max"];
|
|
1475
|
-
var FUGU_REASONING_EFFORTS = ["high", "xhigh"];
|
|
1509
|
+
var FUGU_REASONING_EFFORTS = ["high", "xhigh", "max"];
|
|
1476
1510
|
var META_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1477
1511
|
var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
1478
1512
|
function clampReasoningEffort(effort, allowed) {
|
|
@@ -4240,13 +4274,15 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4240
4274
|
client;
|
|
4241
4275
|
// A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
|
|
4242
4276
|
// initializer would snapshot the default and silently ignore the override.
|
|
4277
|
+
// `reasoning_effort` is re-declared as string: the OpenAI SDK's union lacks Sakana's 'max'.
|
|
4243
4278
|
get defaultParams() {
|
|
4244
4279
|
return {
|
|
4245
4280
|
model: this.model,
|
|
4246
4281
|
stream: false,
|
|
4247
|
-
// Caps
|
|
4248
|
-
//
|
|
4249
|
-
max_tokens: this.maxOutputTokens
|
|
4282
|
+
// Caps the final reply only. Per Sakana's docs the orchestrator "still uses maximum
|
|
4283
|
+
// token limit", so this bounds neither Ultra's latency nor its orchestration spend.
|
|
4284
|
+
max_tokens: this.maxOutputTokens,
|
|
4285
|
+
reasoning_effort: toFuguEffort(this.reasoningEffort ?? "high")
|
|
4250
4286
|
};
|
|
4251
4287
|
}
|
|
4252
4288
|
logTemplates = {
|
|
@@ -4271,33 +4307,6 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4271
4307
|
content: msg.content
|
|
4272
4308
|
}));
|
|
4273
4309
|
}
|
|
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
4310
|
extractThinkingAndUsage(completion) {
|
|
4302
4311
|
let thinkingContent = "";
|
|
4303
4312
|
const message = completion.choices[0]?.message;
|
|
@@ -4305,21 +4314,14 @@ var FuguAgent = class extends AbstractAgent {
|
|
|
4305
4314
|
thinkingContent = message.reasoning_content;
|
|
4306
4315
|
this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
|
|
4307
4316
|
}
|
|
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);
|
|
4317
|
+
const tokenUsage = extractFuguTokenUsage(this.model, completion);
|
|
4318
|
+
if (tokenUsage) {
|
|
4319
|
+
const details = completion.usage ?? {};
|
|
4320
|
+
const orchestrationIn = details.prompt_tokens_details?.orchestration_input_tokens ?? 0;
|
|
4321
|
+
const orchestrationOut = details.completion_tokens_details?.orchestration_output_tokens ?? 0;
|
|
4322
|
+
if (tokenUsage.reasoningTokens || orchestrationIn || orchestrationOut) {
|
|
4321
4323
|
this.logger(
|
|
4322
|
-
`
|
|
4324
|
+
`Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
|
|
4323
4325
|
);
|
|
4324
4326
|
}
|
|
4325
4327
|
}
|
|
@@ -4361,7 +4363,6 @@ ${schemaDescription}`;
|
|
|
4361
4363
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4362
4364
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4363
4365
|
}
|
|
4364
|
-
this.logRawUsageForCalibration(completion);
|
|
4365
4366
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4366
4367
|
if (!rawReply) {
|
|
4367
4368
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4405,7 +4406,6 @@ ${schemaDescription}`;
|
|
|
4405
4406
|
this.logger(this.logTemplates.error(this.name, apiError));
|
|
4406
4407
|
throw new Error(this.errorMessages.apiError(apiError));
|
|
4407
4408
|
}
|
|
4408
|
-
this.logRawUsageForCalibration(completion);
|
|
4409
4409
|
const rawReply = completion.choices[0]?.message?.content;
|
|
4410
4410
|
if (!rawReply) {
|
|
4411
4411
|
throw new Error(this.errorMessages.emptyResponse);
|
|
@@ -4424,6 +4424,31 @@ ${schemaDescription}`;
|
|
|
4424
4424
|
}
|
|
4425
4425
|
}
|
|
4426
4426
|
};
|
|
4427
|
+
function extractFuguTokenUsage(modelApiName, completion) {
|
|
4428
|
+
const usage = completion?.usage;
|
|
4429
|
+
if (!usage) {
|
|
4430
|
+
return void 0;
|
|
4431
|
+
}
|
|
4432
|
+
const promptDetails = usage.prompt_tokens_details ?? {};
|
|
4433
|
+
const completionDetails = usage.completion_tokens_details ?? {};
|
|
4434
|
+
const inputTokens = (usage.prompt_tokens ?? 0) + (promptDetails.orchestration_input_tokens ?? 0);
|
|
4435
|
+
const outputTokens = (usage.completion_tokens ?? 0) + (completionDetails.orchestration_output_tokens ?? 0);
|
|
4436
|
+
const cachedInputTokens = (promptDetails.cached_tokens ?? 0) + (promptDetails.orchestration_input_cached_tokens ?? 0);
|
|
4437
|
+
const reasoningTokens = completionDetails.reasoning_tokens ?? 0;
|
|
4438
|
+
return {
|
|
4439
|
+
inputTokens,
|
|
4440
|
+
outputTokens,
|
|
4441
|
+
totalTokens: inputTokens + outputTokens,
|
|
4442
|
+
cachedInputTokens,
|
|
4443
|
+
// Omitted rather than 0: consumers persist the object as-is (see TokenUsage).
|
|
4444
|
+
...reasoningTokens > 0 ? { reasoningTokens } : {},
|
|
4445
|
+
costUSD: calculateModelCost(modelApiName, inputTokens, outputTokens, {
|
|
4446
|
+
cacheHitTokens: cachedInputTokens,
|
|
4447
|
+
// Context-tier selection: the request's own size, not the orchestrator's traffic.
|
|
4448
|
+
contextTokens: usage.total_tokens ?? inputTokens
|
|
4449
|
+
})
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4427
4452
|
|
|
4428
4453
|
// src/agents/qwen-agent.ts
|
|
4429
4454
|
import { OpenAI as OpenAI9 } from "openai";
|
|
@@ -5084,6 +5109,7 @@ var AgentFactory = class {
|
|
|
5084
5109
|
return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
|
|
5085
5110
|
// Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
|
|
5086
5111
|
case LLM_CONSTANTS.FUGU_ULTRA:
|
|
5112
|
+
case LLM_CONSTANTS.FUGU_MAX:
|
|
5087
5113
|
return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
|
|
5088
5114
|
// Qwen models — thinking-only (enable_thinking always sent)
|
|
5089
5115
|
case LLM_CONSTANTS.QWEN_MAX:
|