@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 CHANGED
@@ -370,6 +370,7 @@ declare const LLM_CONSTANTS: {
370
370
  GLM: string;
371
371
  GLM_FLASH: string;
372
372
  FUGU_ULTRA: string;
373
+ FUGU_MAX: string;
373
374
  QWEN_MAX: string;
374
375
  QWEN_FLASH: string;
375
376
  MINIMAX: string;
@@ -527,7 +528,8 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
527
528
  * - Gemini 3.x thinkingLevel: minimal|low|medium|high (3.1 Pro and 3.7 Flash reject minimal)
528
529
  * - Z.AI GLM-5.3 / 5.3-Flash: low|high|max only
529
530
  * - DeepSeek V4: low|high|max (the API itself aliases medium → high)
530
- * - Sakana Fugu: high|xhigh
531
+ * - Sakana Fugu (verified live 2026-09-20): high|xhigh|max on every model ("low" → 400 naming
532
+ * exactly that enum). The server default is xhigh for fugu-ultra and high for the rest.
531
533
  * - Meta Muse Spark (verified 2026-09-12): minimal|low|medium|high|xhigh|max ("none" → 400; max is
532
534
  * Standard tier only)
533
535
  * - Mistral Small 4 / Medium 3.5 (verified 2026-09-18): the API enum is
@@ -543,7 +545,7 @@ type AnthropicReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
543
545
  type GeminiReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
544
546
  type GlmReasoningEffort = 'low' | 'high' | 'max';
545
547
  type DeepSeekReasoningEffort = 'low' | 'high' | 'max';
546
- type FuguReasoningEffort = 'high' | 'xhigh';
548
+ type FuguReasoningEffort = 'high' | 'xhigh' | 'max';
547
549
  type MetaReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
548
550
  type MistralReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
549
551
  /** The shared scale, lowest first. */
@@ -1651,7 +1653,6 @@ declare class FuguAgent extends AbstractAgent {
1651
1653
  private readonly errorMessages;
1652
1654
  constructor(name: string, instruction: string, model: string, apiKey: string, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
1653
1655
  private convertToOpenAIMessages;
1654
- private logRawUsageForCalibration;
1655
1656
  private extractThinkingAndUsage;
1656
1657
  private prependSystemInstruction;
1657
1658
  doAskWithZodSchema<T>(zodSchema: z.ZodSchema<T>, messages: AIMessage[]): Promise<[T, string, TokenUsage$1?, string?]>;
package/dist/index.d.ts CHANGED
@@ -370,6 +370,7 @@ declare const LLM_CONSTANTS: {
370
370
  GLM: string;
371
371
  GLM_FLASH: string;
372
372
  FUGU_ULTRA: string;
373
+ FUGU_MAX: string;
373
374
  QWEN_MAX: string;
374
375
  QWEN_FLASH: string;
375
376
  MINIMAX: string;
@@ -527,7 +528,8 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
527
528
  * - Gemini 3.x thinkingLevel: minimal|low|medium|high (3.1 Pro and 3.7 Flash reject minimal)
528
529
  * - Z.AI GLM-5.3 / 5.3-Flash: low|high|max only
529
530
  * - DeepSeek V4: low|high|max (the API itself aliases medium → high)
530
- * - Sakana Fugu: high|xhigh
531
+ * - Sakana Fugu (verified live 2026-09-20): high|xhigh|max on every model ("low" → 400 naming
532
+ * exactly that enum). The server default is xhigh for fugu-ultra and high for the rest.
531
533
  * - Meta Muse Spark (verified 2026-09-12): minimal|low|medium|high|xhigh|max ("none" → 400; max is
532
534
  * Standard tier only)
533
535
  * - Mistral Small 4 / Medium 3.5 (verified 2026-09-18): the API enum is
@@ -543,7 +545,7 @@ type AnthropicReasoningEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
543
545
  type GeminiReasoningEffort = 'minimal' | 'low' | 'medium' | 'high';
544
546
  type GlmReasoningEffort = 'low' | 'high' | 'max';
545
547
  type DeepSeekReasoningEffort = 'low' | 'high' | 'max';
546
- type FuguReasoningEffort = 'high' | 'xhigh';
548
+ type FuguReasoningEffort = 'high' | 'xhigh' | 'max';
547
549
  type MetaReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
548
550
  type MistralReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
549
551
  /** The shared scale, lowest first. */
@@ -1651,7 +1653,6 @@ declare class FuguAgent extends AbstractAgent {
1651
1653
  private readonly errorMessages;
1652
1654
  constructor(name: string, instruction: string, model: string, apiKey: string, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
1653
1655
  private convertToOpenAIMessages;
1654
- private logRawUsageForCalibration;
1655
1656
  private extractThinkingAndUsage;
1656
1657
  private prependSystemInstruction;
1657
1658
  doAskWithZodSchema<T>(zodSchema: z.ZodSchema<T>, messages: AIMessage[]): Promise<[T, string, TokenUsage$1?, string?]>;
package/dist/index.js CHANGED
@@ -976,6 +976,7 @@ var LLM_CONSTANTS = {
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",
@@ -1221,13 +1222,37 @@ var SupportedAiModels = {
1221
1222
  // so the rate is not even guaranteed stable, and its cache hit rate was 9.3% — effectively
1222
1223
  // zero, since every hit came from a duplicate call seconds apart rather than turn-to-turn
1223
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.
1224
1236
  [LLM_CONSTANTS.FUGU_ULTRA]: {
1225
1237
  displayName: "Sakana Fugu Ultra",
1226
1238
  modelApiName: "fugu-ultra",
1227
1239
  apiKeyName: API_KEY_CONSTANTS.FUGU,
1228
1240
  hasThinking: false,
1241
+ reasoningEffort: "high",
1229
1242
  tags: ["extremely-slow", "expensive"]
1230
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
+ },
1231
1256
  // Qwen models (QwenCloud, OpenAI-compatible endpoint). Added 2026-08-05 straight into the
1232
1257
  // thinking-only catalog: their API has an `enable_thinking` toggle, we always send true, and
1233
1258
  // thinking arrives in `reasoning_content` (verified live against all three, non-streaming).
@@ -1515,9 +1540,12 @@ var MODEL_PRICING = {
1515
1540
  extendedContextCacheHitPrice: 1,
1516
1541
  extendedContextThresholdTokens: 2e5
1517
1542
  },
1518
- // Sakana Fugu models. Base `fugu` was retired 2026-08-04 — it had no published price and
1519
- // measured out at these same ultra rates, so it has no pricing entry.
1520
- // fugu-ultra has published pricing. Above 272K context the rates roughly double.
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.
1521
1549
  [SupportedAiModels[LLM_CONSTANTS.FUGU_ULTRA].modelApiName]: {
1522
1550
  inputPrice: 5,
1523
1551
  outputPrice: 30,
@@ -1527,6 +1555,12 @@ var MODEL_PRICING = {
1527
1555
  extendedContextCacheHitPrice: 1,
1528
1556
  extendedContextThresholdTokens: 272e3
1529
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
+ },
1530
1564
  // Qwen models. Rates from the official pricing page (qwencloud.com/pricing/api, read
1531
1565
  // 2026-08-30 — the page is client-rendered, so it was read by eye, not WebFetch):
1532
1566
  // qwen3.8-max $2/$6 with implicit-cache hits at $0.25; qwen3.8-flash $0.15/$0.47, hits
@@ -1643,7 +1677,7 @@ 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"];
1648
1682
  var MISTRAL_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
1649
1683
  function clampReasoningEffort(effort, allowed) {
@@ -4411,13 +4445,15 @@ var FuguAgent = class extends AbstractAgent {
4411
4445
  client;
4412
4446
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
4413
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'.
4414
4449
  get defaultParams() {
4415
4450
  return {
4416
4451
  model: this.model,
4417
4452
  stream: false,
4418
- // Caps visible output only. Server-side orchestration/reasoning tokens are
4419
- // separate and unaffected by this.
4420
- 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")
4421
4457
  };
4422
4458
  }
4423
4459
  logTemplates = {
@@ -4442,33 +4478,6 @@ var FuguAgent = class extends AbstractAgent {
4442
4478
  content: msg.content
4443
4479
  }));
4444
4480
  }
4445
- // ───────────────────────────────────────────────────────────────────────────────────────
4446
- // TEMPORARY (cost calibration). Base `fugu` is a dynamic router with no published per-token
4447
- // price and Sakana returns NO cost field in the response — only token counts. Crucially the
4448
- // response also reports "orchestration tokens" (billed at input/output rates per Sakana's
4449
- // pricing page) that our standard TokenUsage drops. This logs the full raw breakdown to
4450
- // BetterStack under a distinctive tag so we can sum real tokens per game and, combined with
4451
- // the Sakana billing dashboard total, derive the true per-token rate. REMOVE AFTER CALIBRATION.
4452
- logRawUsageForCalibration(completion) {
4453
- const usage = completion?.usage;
4454
- if (!usage) return;
4455
- logger.info("FUGU_COST_CALIBRATION", {
4456
- tag: "FUGU_COST_CALIBRATION",
4457
- model: this.model,
4458
- agentName: this.name,
4459
- gameId: this.gameId,
4460
- userId: this.userId,
4461
- promptTokens: usage.prompt_tokens ?? 0,
4462
- completionTokens: usage.completion_tokens ?? 0,
4463
- totalTokens: usage.total_tokens ?? 0,
4464
- cachedTokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
4465
- orchestrationInputTokens: usage.prompt_tokens_details?.orchestration_input_tokens ?? 0,
4466
- orchestrationInputCachedTokens: usage.prompt_tokens_details?.orchestration_input_cached_tokens ?? 0,
4467
- reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0,
4468
- orchestrationOutputTokens: usage.completion_tokens_details?.orchestration_output_tokens ?? 0,
4469
- rawUsage: usage
4470
- });
4471
- }
4472
4481
  extractThinkingAndUsage(completion) {
4473
4482
  let thinkingContent = "";
4474
4483
  const message = completion.choices[0]?.message;
@@ -4476,21 +4485,14 @@ var FuguAgent = class extends AbstractAgent {
4476
4485
  thinkingContent = message.reasoning_content;
4477
4486
  this.logger(`Captured reasoning_content (${thinkingContent.length} characters)`);
4478
4487
  }
4479
- let tokenUsage;
4480
- const usageResult = extractUsageAndCalculateCost(this.model, completion);
4481
- if (usageResult) {
4482
- tokenUsage = {
4483
- inputTokens: usageResult.usage.promptTokens,
4484
- outputTokens: usageResult.usage.completionTokens,
4485
- totalTokens: usageResult.usage.totalTokens,
4486
- costUSD: usageResult.cost,
4487
- ...usageResult.usage.cacheHitTokens !== void 0 ? { cachedInputTokens: usageResult.usage.cacheHitTokens } : {}
4488
- };
4489
- if (usageResult.usage.reasoningTokens) {
4490
- const reasoningTokens = usageResult.usage.reasoningTokens;
4491
- 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) {
4492
4494
  this.logger(
4493
- `Output breakdown: ${reasoningTokens} reasoning tokens, ${finalAnswerTokens} final answer tokens`
4495
+ `Usage breakdown: ${tokenUsage.reasoningTokens ?? 0} reasoning tokens; orchestration ${orchestrationIn} in / ${orchestrationOut} out (billed, folded into the totals)`
4494
4496
  );
4495
4497
  }
4496
4498
  }
@@ -4532,7 +4534,6 @@ ${schemaDescription}`;
4532
4534
  this.logger(this.logTemplates.error(this.name, apiError));
4533
4535
  throw new Error(this.errorMessages.apiError(apiError));
4534
4536
  }
4535
- this.logRawUsageForCalibration(completion);
4536
4537
  const rawReply = completion.choices[0]?.message?.content;
4537
4538
  if (!rawReply) {
4538
4539
  throw new Error(this.errorMessages.emptyResponse);
@@ -4576,7 +4577,6 @@ ${schemaDescription}`;
4576
4577
  this.logger(this.logTemplates.error(this.name, apiError));
4577
4578
  throw new Error(this.errorMessages.apiError(apiError));
4578
4579
  }
4579
- this.logRawUsageForCalibration(completion);
4580
4580
  const rawReply = completion.choices[0]?.message?.content;
4581
4581
  if (!rawReply) {
4582
4582
  throw new Error(this.errorMessages.emptyResponse);
@@ -4595,6 +4595,31 @@ ${schemaDescription}`;
4595
4595
  }
4596
4596
  }
4597
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
+ }
4598
4623
 
4599
4624
  // src/agents/qwen-agent.ts
4600
4625
  var import_openai9 = require("openai");
@@ -5255,6 +5280,7 @@ var AgentFactory = class {
5255
5280
  return new GlmAgent(name, instruction, model.modelApiName, key, model.temperature, shouldEnableThinking);
5256
5281
  // Sakana Fugu models — always-on reasoning, no temperature (ignored by the model)
5257
5282
  case LLM_CONSTANTS.FUGU_ULTRA:
5283
+ case LLM_CONSTANTS.FUGU_MAX:
5258
5284
  return new FuguAgent(name, instruction, model.modelApiName, key, shouldEnableThinking);
5259
5285
  // Qwen models — thinking-only (enable_thinking always sent)
5260
5286
  case LLM_CONSTANTS.QWEN_MAX: