@absolutejs/ai 0.2.3 → 0.2.4

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.
@@ -582,17 +582,70 @@ async function* tapStream(source, params, providerName) {
582
582
  }
583
583
  }
584
584
 
585
+ // src/ai/models/reasoning.ts
586
+ var profile = (kind, efforts, description, source) => ({ kind, efforts, description, source });
587
+ var claude = profile("effort", ["low", "medium", "high", "xhigh", "max"], "More thinking for harder problems; higher settings can take longer and use more credits.", "https://platform.claude.com/docs/en/build-with-claude/effort");
588
+ var claude46 = {
589
+ ...claude,
590
+ efforts: ["low", "medium", "high", "max"]
591
+ };
592
+ var haiku = profile("budget", ["none", "low", "medium", "high"], "Thinking-token budget: Low 2,048; Medium 8,192; High 16,384. Actual usage can be lower.", "https://platform.claude.com/docs/en/build-with-claude/extended-thinking");
593
+ var gpt = profile("effort", ["none", "low", "medium", "high", "xhigh", "max"], claude.description, "https://developers.openai.com/api/docs/models/gpt-5.6-sol");
594
+ var gemini = profile("effort", ["low", "medium", "high"], claude.description, "https://ai.google.dev/gemini-api/docs/openai");
595
+ var deepseek = profile("effort", ["none", "low", "high", "max"], claude.description, "https://api-docs.deepseek.com/guides/thinking_mode/");
596
+ var mistral = profile("effort", ["none", "high"], "Minimal thinking or full reasoning. This model does not offer intermediate effort levels.", "https://docs.mistral.ai/studio/conversations/reasoning");
597
+ var qwen = profile("budget", ["none", "low", "medium", "high"], haiku.description, "https://help.aliyun.com/en/model-studio/deep-thinking");
598
+ var MODEL_REASONING = {
599
+ "anthropic:claude-fable-5-1": claude,
600
+ "anthropic:claude-opus-5": claude,
601
+ "anthropic:claude-sonnet-5": claude,
602
+ "anthropic:claude-sonnet-4-6": claude46,
603
+ "anthropic:claude-opus-4-6": claude46,
604
+ "anthropic:claude-haiku-4-5": haiku,
605
+ "openai:gpt-6-astra": {
606
+ ...gpt,
607
+ efforts: ["low", "medium", "high", "xhigh", "max"],
608
+ source: "https://developers.openai.com/api/docs/models/gpt-6-astra"
609
+ },
610
+ "openai:gpt-5.6-sol": gpt,
611
+ "openai:gpt-5.6-terra": gpt,
612
+ "openai:gpt-5.6-luna": gpt,
613
+ "google:gemini-3.8-flash": gemini,
614
+ "google:gemini-3.1-pro-preview": gemini,
615
+ "google:gemini-2.5-flash": profile("budget", ["none", "low", "medium", "high"], "Thinking-token budget: Low 1,024; Medium 8,192; High 24,576.", gemini.source),
616
+ "xai:grok-4.6": profile("effort", ["low", "medium", "high", "xhigh"], "Always reasons. Choose how much thinking it should do.", "https://docs.x.ai/developers/model-capabilities/text/reasoning"),
617
+ "deepseek:deepseek-flash": deepseek,
618
+ "deepseek:deepseek-v4-pro": deepseek,
619
+ "mistral:mistral-medium-3-5": mistral,
620
+ "mistral:mistral-small-latest": mistral,
621
+ "alibaba:qwen3.8-max": qwen,
622
+ "alibaba:qwen3.7-plus": qwen,
623
+ "moonshot:kimi-k3": profile("effort", ["low", "high", "max"], "Always reasons. Choose how much thinking it should do.", "https://platform.kimi.ai/docs/guide/kimi-k3-quickstart"),
624
+ "moonshot:kimi-k2.7-code": profile("fixed", [], "Thinking is always enabled; this model has no documented adjustable effort control.", "https://www.kimi.ai/resources/kimi-k2-7-code")
625
+ };
626
+ var getModelReasoning = (provider, model) => MODEL_REASONING[`${provider}:${model}`];
627
+
585
628
  // src/ai/providers/reasoning.ts
586
629
  var EFFORT_ORDER = [
630
+ "none",
587
631
  "minimal",
588
632
  "low",
589
633
  "medium",
590
634
  "high",
635
+ "xhigh",
591
636
  "max"
592
637
  ];
593
- var ANTHROPIC_EFFORT = [/opus-4-[5-8]/, /sonnet-4-6/, /fable-5/, /mythos-5/];
638
+ var ANTHROPIC_EFFORT = [
639
+ /opus-4-[5-8]/,
640
+ /sonnet-4-6/,
641
+ /fable-5/,
642
+ /mythos-5/,
643
+ /opus-5/,
644
+ /sonnet-5/
645
+ ];
594
646
  var ANTHROPIC_ADAPTIVE_ONLY = [];
595
647
  var ANTHROPIC_LEGACY_THINKING = [
648
+ /haiku-4-5/,
596
649
  /sonnet-4-5/,
597
650
  /sonnet-4-0/,
598
651
  /sonnet-4-2025/,
@@ -601,14 +654,20 @@ var ANTHROPIC_LEGACY_THINKING = [
601
654
  /opus-4-2025/,
602
655
  /3-7-sonnet/
603
656
  ];
604
- var ANTHROPIC_NO_SAMPLING = [/opus-4-[78]/, /fable-5/, /mythos-5/];
657
+ var ANTHROPIC_NO_SAMPLING = [
658
+ /opus-4-[78]/,
659
+ /fable-5/,
660
+ /mythos-5/,
661
+ /opus-5/,
662
+ /sonnet-5/
663
+ ];
605
664
  var ANTHROPIC_MAX_EFFORT = [
606
665
  /opus-4-[678]/,
607
666
  /sonnet-4-6/,
608
667
  /fable-5/,
609
668
  /mythos-5/
610
669
  ];
611
- var OPENAI_REASONING = [/(^|[^a-z])o[1345](-|$)/, /gpt-5/];
670
+ var OPENAI_REASONING = [/(^|[^a-z])o[1345](-|$)/, /gpt-[56]/];
612
671
  var OPENAI_MINIMAL_EFFORT = [/gpt-5/];
613
672
  var matches = (model, patterns) => patterns.some((pattern) => pattern.test(model));
614
673
  var anthropicReasoningMode = (model) => {
@@ -623,6 +682,8 @@ var anthropicReasoningMode = (model) => {
623
682
  var anthropicSupportsSampling = (model) => !matches(model, ANTHROPIC_NO_SAMPLING);
624
683
  var isOpenAIReasoningModel = (model) => matches(model, OPENAI_REASONING);
625
684
  var EFFORT_BUDGET = {
685
+ none: 0,
686
+ xhigh: 24576,
626
687
  high: 16384,
627
688
  low: 2048,
628
689
  max: 32768,
@@ -668,6 +729,9 @@ var anthropicEffortValue = (model, reasoning) => {
668
729
  const effort = resolveEffort(reasoning);
669
730
  if (!effort)
670
731
  return null;
732
+ const known = getModelReasoning("anthropic", model);
733
+ if (known?.kind === "effort")
734
+ return clampEffort(effort, [...known.efforts]);
671
735
  const allowed = matches(model, ANTHROPIC_MAX_EFFORT) ? ["low", "medium", "high", "max"] : ["low", "medium", "high"];
672
736
  return clampEffort(effort, allowed);
673
737
  };
@@ -677,10 +741,50 @@ var openaiEffortValue = (model, reasoning) => {
677
741
  const effort = resolveEffort(reasoning);
678
742
  if (!effort)
679
743
  return null;
744
+ const known = getModelReasoning("openai", model);
745
+ if (known)
746
+ return clampEffort(effort, [...known.efforts]);
680
747
  const allowed = matches(model, OPENAI_MINIMAL_EFFORT) ? ["minimal", "low", "medium", "high"] : ["low", "medium", "high"];
681
748
  const requested = effort === "max" ? "high" : effort;
682
749
  return clampEffort(requested, allowed);
683
750
  };
751
+ var compatibleReasoningBody = (provider, model, reasoning) => {
752
+ if (!reasoning)
753
+ return {};
754
+ const known = getModelReasoning(provider, model);
755
+ const effort = resolveEffort(reasoning);
756
+ if (!known || !effort)
757
+ return {};
758
+ if (!known.efforts.includes(effort))
759
+ throw new Error(`Unsupported reasoning effort ${effort} for ${provider}:${model}`);
760
+ if (provider === "alibaba")
761
+ return effort === "none" ? { enable_thinking: false } : {
762
+ enable_thinking: true,
763
+ thinking_budget: resolveBudgetTokens(reasoning)
764
+ };
765
+ if (provider === "deepseek")
766
+ return effort === "none" ? { thinking: { type: "disabled" } } : { thinking: { type: "enabled" }, reasoning_effort: effort };
767
+ return { reasoning_effort: effort };
768
+ };
769
+ var geminiThinkingConfig = (model, reasoning) => {
770
+ if (!reasoning)
771
+ return;
772
+ const profile2 = getModelReasoning("google", model);
773
+ const effort = resolveEffort(reasoning);
774
+ if (!profile2 || !effort)
775
+ return;
776
+ if (!profile2.efforts.includes(effort))
777
+ throw new Error(`Unsupported reasoning effort ${effort} for ${model}`);
778
+ if (profile2.kind === "effort")
779
+ return { thinkingLevel: effort };
780
+ const budgets = {
781
+ none: 0,
782
+ low: 1024,
783
+ medium: 8192,
784
+ high: 24576
785
+ };
786
+ return { thinkingBudget: reasoning.budgetTokens ?? budgets[effort] };
787
+ };
684
788
 
685
789
  // src/ai/providers/openaiResponses.ts
686
790
  var h2IfHttps = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
@@ -1314,15 +1418,28 @@ var mapOpenAIContent = (msg) => {
1314
1418
  }
1315
1419
  const hasMedia = msg.content.some((block) => block.type === "image" || block.type === "document" || block.type === "audio" || block.type === "video");
1316
1420
  if (!hasMedia) {
1317
- return null;
1421
+ return msg.content.filter((block) => block.type === "text").map((block) => block.content).join("") || null;
1318
1422
  }
1319
1423
  return msg.content.map(mapContentBlockToOpenAI).filter((mapped) => mapped !== null);
1320
1424
  };
1321
- var buildRequestBody = (params, capabilityModel = params.model) => {
1425
+ var buildRequestBody = (params, capabilityModel = params.model, providerName = "openai") => {
1322
1426
  const messages = params.messages.flatMap((msg) => {
1323
- if (hasArrayContent(msg) && msg.content.some((block) => block.type === "tool_use" || block.type === "tool_result"))
1324
- return buildToolMessages(msg.content);
1325
- return [{ content: mapOpenAIContent(msg), role: msg.role }];
1427
+ const mapped = hasArrayContent(msg) && msg.content.some((block) => block.type === "tool_use" || block.type === "tool_result") ? buildToolMessages(msg.content) : [{ content: mapOpenAIContent(msg), role: msg.role }];
1428
+ if (hasArrayContent(msg) && msg.role === "assistant") {
1429
+ const assistant = mapped.find((message) => message.role === "assistant");
1430
+ if (assistant) {
1431
+ assistant.content = mapOpenAIContent(msg);
1432
+ const thinking = msg.content.filter((block) => block.type === "thinking").map((block) => block.thinking).join("");
1433
+ if (thinking && ["deepseek", "moonshot", "alibaba"].includes(providerName))
1434
+ assistant.reasoning_content = thinking;
1435
+ if (thinking && providerName === "mistral")
1436
+ assistant.content = [
1437
+ { type: "thinking", thinking: [{ type: "text", text: thinking }] },
1438
+ ...typeof assistant.content === "string" && assistant.content ? [{ type: "text", text: assistant.content }] : []
1439
+ ];
1440
+ }
1441
+ }
1442
+ return mapped;
1326
1443
  });
1327
1444
  if (params.systemPrompt) {
1328
1445
  messages.unshift({ content: params.systemPrompt, role: "system" });
@@ -1365,6 +1482,8 @@ var buildRequestBody = (params, capabilityModel = params.model) => {
1365
1482
  if (typeof params.maxTokens === "number")
1366
1483
  body.max_tokens = params.maxTokens;
1367
1484
  }
1485
+ if (providerName !== "openai")
1486
+ Object.assign(body, compatibleReasoningBody(providerName, params.model, params.reasoning));
1368
1487
  if (params.stopSequences && params.stopSequences.length > 0)
1369
1488
  body.stop = params.stopSequences;
1370
1489
  if (typeof params.seed === "number")
@@ -1468,6 +1587,21 @@ var processToolCallDeltas = (toolCalls, pendingToolCalls) => {
1468
1587
  }
1469
1588
  };
1470
1589
  var processDelta = function* (delta, pendingToolCalls) {
1590
+ if (typeof delta.reasoning_content === "string")
1591
+ yield { type: "thinking", content: delta.reasoning_content };
1592
+ if (Array.isArray(delta.content)) {
1593
+ for (const part of delta.content) {
1594
+ if (!isRecord2(part))
1595
+ continue;
1596
+ if (part.type === "text" && typeof part.text === "string")
1597
+ yield { type: "text", content: part.text };
1598
+ if (part.type === "thinking" && Array.isArray(part.thinking)) {
1599
+ for (const thought of part.thinking)
1600
+ if (isRecord2(thought) && typeof thought.text === "string")
1601
+ yield { type: "thinking", content: thought.text };
1602
+ }
1603
+ }
1604
+ }
1471
1605
  if (typeof delta.content === "string") {
1472
1606
  yield { content: delta.content, type: "text" };
1473
1607
  }
@@ -1730,7 +1864,7 @@ var openai = (config2) => {
1730
1864
  body: (params) => buildResponsesRequestBody(params, false, config2.modelForCapabilities?.(params.model) ?? params.model)
1731
1865
  }),
1732
1866
  stream: (params) => {
1733
- const openaiBody = buildRequestBody(params, config2.modelForCapabilities?.(params.model) ?? params.model);
1867
+ const openaiBody = buildRequestBody(params, config2.modelForCapabilities?.(params.model) ?? params.model, providerName);
1734
1868
  const body = config2.transformRequestBody ? config2.transformRequestBody(openaiBody, params) : openaiBody;
1735
1869
  return async function* () {
1736
1870
  const [apiKey, headers] = await Promise.all([
@@ -1931,7 +2065,9 @@ var buildRequestBody2 = (params, configuredMax, configCaching) => {
1931
2065
  if (params.stopSequences && params.stopSequences.length > 0) {
1932
2066
  body.stop_sequences = params.stopSequences;
1933
2067
  }
1934
- const mode = params.reasoning ? anthropicReasoningMode(params.model) : "none";
2068
+ const mode = params.reasoning && params.reasoning.effort !== "none" ? anthropicReasoningMode(params.model) : "none";
2069
+ if (params.reasoning?.effort === "none")
2070
+ body.thinking = { type: "disabled" };
1935
2071
  const thinkingActive = mode !== "none";
1936
2072
  if (!thinkingActive && anthropicSupportsSampling(params.model)) {
1937
2073
  if (typeof params.temperature === "number") {
@@ -3320,5 +3456,5 @@ export {
3320
3456
  verifyOpenRouterWebhookSignature
3321
3457
  };
3322
3458
 
3323
- //# debugId=9CCBCF47C3223B0164756E2164756E21
3459
+ //# debugId=0300DC4312EEC35964756E2164756E21
3324
3460
  //# sourceMappingURL=openrouter.js.map