@adaptic/lumic-utils 1.0.28 → 1.0.30

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.
Files changed (33) hide show
  1. package/dist/{apollo-client.client-CzTlfziA.js → apollo-client.client-CNrz3Xod.js} +3 -3
  2. package/dist/{apollo-client.client-CzTlfziA.js.map → apollo-client.client-CNrz3Xod.js.map} +1 -1
  3. package/dist/{apollo-client.client-KzZx5slz.js → apollo-client.client-DsFSBlst.js} +4 -4
  4. package/dist/{apollo-client.client-KzZx5slz.js.map → apollo-client.client-DsFSBlst.js.map} +1 -1
  5. package/dist/{apollo-client.server-DQPc4Iam.js → apollo-client.server-DMz8cEcx.js} +3 -3
  6. package/dist/{apollo-client.server-DQPc4Iam.js.map → apollo-client.server-DMz8cEcx.js.map} +1 -1
  7. package/dist/{apollo-client.server-Bv2rQsCw.js → apollo-client.server-YkSyk6OJ.js} +3 -3
  8. package/dist/{apollo-client.server-Bv2rQsCw.js.map → apollo-client.server-YkSyk6OJ.js.map} +1 -1
  9. package/dist/{index-I-0uzUiF.js → index-BZ9x9UfI.js} +734 -55
  10. package/dist/{index-FqzV71J4.js.map → index-BZ9x9UfI.js.map} +1 -1
  11. package/dist/{index-Cjy8RH6E.js → index-CGlW9JSL.js} +2 -2
  12. package/dist/{index-Cjy8RH6E.js.map → index-CGlW9JSL.js.map} +1 -1
  13. package/dist/{index-FqzV71J4.js → index-DlAW691N.js} +735 -54
  14. package/dist/{index-I-0uzUiF.js.map → index-DlAW691N.js.map} +1 -1
  15. package/dist/{index-QMP2OpYn.js → index-VsQMFi2M.js} +2 -2
  16. package/dist/{index-QMP2OpYn.js.map → index-VsQMFi2M.js.map} +1 -1
  17. package/dist/index.cjs +3 -1
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.mjs +1 -1
  20. package/dist/test.cjs +1 -1
  21. package/dist/test.mjs +1 -1
  22. package/dist/types/config/secrets.d.ts +10 -0
  23. package/dist/types/functions/__tests__/gpt56-model-release-layer.test.d.ts +1 -0
  24. package/dist/types/functions/__tests__/llm-openai-responses-routing.test.d.ts +1 -0
  25. package/dist/types/functions/llm-config.d.ts +8 -6
  26. package/dist/types/functions/llm-openai.d.ts +125 -2
  27. package/dist/types/index.d.ts +1 -1
  28. package/dist/types/schemas/openai-schemas.d.ts +30 -30
  29. package/dist/types/schemas/perplexity-schemas.d.ts +40 -40
  30. package/dist/types/types/openai-types.d.ts +44 -5
  31. package/dist/types/utils/config.d.ts +12 -1
  32. package/dist/types/utils/llm-cost-tracker.d.ts +2 -2
  33. package/package.json +2 -2
@@ -80,9 +80,14 @@ const OPENAI_COMPATIBLE_PROVIDERS = {
80
80
  */
81
81
  const SUPPORTED_MODELS = {
82
82
  // ── OpenAI GPT-5.6 series (current flagship) ─────────────────────────
83
- // sol / terra / luna are the 2026 flagship trio (developers.openai.com/
84
- // api/docs/models/all). Same GPT-5.x API surface + the temperature
85
- // constraint documented below.
83
+ // sol / terra / luna are the 2026 flagship reasoning trio
84
+ // (developers.openai.com/api/docs/models/all). They are REASONING models
85
+ // (isReasoningModel: true) and — like all reasoning models — do not accept a
86
+ // custom temperature. Critically, OpenAI rejects function tools for them on
87
+ // Chat Completions ("Function tools with reasoning_effort are not supported
88
+ // for gpt-5.6-sol in /v1/chat/completions"), so `requiresResponsesApiForTools`
89
+ // routes tool-bearing calls to the Responses API. Both flags are corrected
90
+ // here from their initial (registration-time) `false` values.
86
91
  'gpt-5.6-sol': {
87
92
  provider: 'openai',
88
93
  supportsTools: true,
@@ -90,7 +95,8 @@ const SUPPORTED_MODELS = {
90
95
  supportsStructuredOutput: true,
91
96
  supportsDeveloperPrompt: true,
92
97
  supportsTemperature: false,
93
- isReasoningModel: false,
98
+ isReasoningModel: true,
99
+ requiresResponsesApiForTools: true,
94
100
  },
95
101
  'gpt-5.6-terra': {
96
102
  provider: 'openai',
@@ -99,7 +105,8 @@ const SUPPORTED_MODELS = {
99
105
  supportsStructuredOutput: true,
100
106
  supportsDeveloperPrompt: true,
101
107
  supportsTemperature: false,
102
- isReasoningModel: false,
108
+ isReasoningModel: true,
109
+ requiresResponsesApiForTools: true,
103
110
  },
104
111
  'gpt-5.6-luna': {
105
112
  provider: 'openai',
@@ -108,7 +115,8 @@ const SUPPORTED_MODELS = {
108
115
  supportsStructuredOutput: true,
109
116
  supportsDeveloperPrompt: true,
110
117
  supportsTemperature: false,
111
- isReasoningModel: false,
118
+ isReasoningModel: true,
119
+ requiresResponsesApiForTools: true,
112
120
  },
113
121
  // ── OpenAI GPT-5.5 series ────────────────────────────────────────────
114
122
  // GPT-5.x series: OpenAI's 2026 API constraint — only the default
@@ -591,24 +599,33 @@ function getModelProvider(model) {
591
599
  return SUPPORTED_MODELS[model].provider;
592
600
  }
593
601
  /**
594
- * Default model tiers per provider for use with LLM_MODEL_MINI/NORMAL/ADVANCED env vars
602
+ * Default model tiers per provider for use with the
603
+ * LLM_MODEL_MINI/NORMAL/ADVANCED/CRITICAL env vars.
604
+ *
605
+ * Four tiers, increasing in capability/cost: `mini` (high-volume, cheap) →
606
+ * `normal` (default) → `advanced` → `critical` (most-capable). The tier a caller
607
+ * lands on is resolved in `../utils/config` (see `LLM_MODEL_*`).
595
608
  */
596
609
  const PROVIDER_DEFAULT_MODELS = {
597
- // advanced pins to gpt-5.5, NOT the newer gpt-5.6-sol, deliberately: gpt-5.6-sol is a
598
- // reasoning model and OpenAI rejects function tools on /v1/chat/completions for it
599
- // ("Function tools with reasoning_effort are not supported for gpt-5.6-sol in
600
- // /v1/chat/completions") it requires the Responses API. Consumers that call
601
- // makeOpenAIChatCompletionCall WITH tools (e.g. the engine's decision path) 400 on
602
- // every call. gpt-5.6-sol/terra/luna remain REGISTERED in SUPPORTED_MODELS for when a
603
- // Responses-API routing path lands; until then the advanced (tool-calling) tier pins
604
- // to gpt-5.5 — the newest model compatible with chat-completions + function tools.
605
- openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.4-mini', advanced: 'gpt-5.5' },
606
- anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7' },
607
- deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro' },
608
- kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6' },
609
- qwen: { mini: 'qwen3.5-flash', normal: 'qwen3.5-plus', advanced: 'qwen3-max' },
610
- xai: { mini: 'grok-4.1-fast', normal: 'grok-4-fast-non-reasoning', advanced: 'grok-4.3' },
611
- gemini: { mini: 'gemini-3.1-flash-lite-preview', normal: 'gemini-3-flash-preview', advanced: 'gemini-3.1-pro-preview' },
610
+ // OpenAI now leads with the GPT-5.6 reasoning trio. The historical blocker
611
+ // (gpt-5.6-* returning 400 on Chat-Completions + tools) is resolved by the
612
+ // Responses-API routing (`requiresResponsesApiForTools` +
613
+ // `makeOpenAIChatCompletionCall`), so the intended canary is live:
614
+ // normal → gpt-5.6-luna (cheapest of the trio)
615
+ // advanced gpt-5.6-terra (flips off the temporary gpt-5.5 pin)
616
+ // critical → gpt-5.6-sol (most capable; in/out priced like gpt-5.5)
617
+ // `mini` stays on gpt-5.4-nano — the high-volume summarizer tier where the
618
+ // trio's reasoning cost is not warranted.
619
+ openai: { mini: 'gpt-5.4-nano', normal: 'gpt-5.6-luna', advanced: 'gpt-5.6-terra', critical: 'gpt-5.6-sol' },
620
+ // Non-OpenAI providers: `critical` mirrors `advanced` (each provider's
621
+ // most-capable model), so the new tier is a no-op for them until a distinct
622
+ // critical-tier model is chosen.
623
+ anthropic: { mini: 'claude-haiku-4-5', normal: 'claude-sonnet-4-6', advanced: 'claude-opus-4-7', critical: 'claude-opus-4-7' },
624
+ deepseek: { mini: 'deepseek-v4-flash', normal: 'deepseek-v4-flash', advanced: 'deepseek-v4-pro', critical: 'deepseek-v4-pro' },
625
+ kimi: { mini: 'kimi-k2-0905-preview', normal: 'kimi-k2.5', advanced: 'kimi-k2.6', critical: 'kimi-k2.6' },
626
+ qwen: { mini: 'qwen3.5-flash', normal: 'qwen3.5-plus', advanced: 'qwen3-max', critical: 'qwen3-max' },
627
+ xai: { mini: 'grok-4.1-fast', normal: 'grok-4-fast-non-reasoning', advanced: 'grok-4.3', critical: 'grok-4.3' },
628
+ gemini: { mini: 'gemini-3.1-flash-lite-preview', normal: 'gemini-3-flash-preview', advanced: 'gemini-3.1-pro-preview', critical: 'gemini-3.1-pro-preview' },
612
629
  };
613
630
 
614
631
  // Default implementation using console (backward compatible)
@@ -683,9 +700,11 @@ const lumicSecretsSchema = z.object({
683
700
  llmMiniProvider: z.string().optional(),
684
701
  llmNormalProvider: z.string().optional(),
685
702
  llmAdvancedProvider: z.string().optional(),
703
+ llmCriticalProvider: z.string().optional(),
686
704
  llmModelMini: z.string().optional(),
687
705
  llmModelNormal: z.string().optional(),
688
706
  llmModelAdvanced: z.string().optional(),
707
+ llmModelCritical: z.string().optional(),
689
708
  lumicDebug: z.boolean().default(false),
690
709
  allowedUploadPaths: z.string().default('/tmp'),
691
710
  }),
@@ -748,9 +767,11 @@ function loadSecrets() {
748
767
  llmMiniProvider: process.env.LLM_MINI_PROVIDER,
749
768
  llmNormalProvider: process.env.LLM_NORMAL_PROVIDER,
750
769
  llmAdvancedProvider: process.env.LLM_ADVANCED_PROVIDER,
770
+ llmCriticalProvider: process.env.LLM_CRITICAL_PROVIDER,
751
771
  llmModelMini: process.env.LLM_MODEL_MINI,
752
772
  llmModelNormal: process.env.LLM_MODEL_NORMAL,
753
773
  llmModelAdvanced: process.env.LLM_MODEL_ADVANCED,
774
+ llmModelCritical: process.env.LLM_MODEL_CRITICAL,
754
775
  lumicDebug: process.env.LUMIC_DEBUG === 'true' ||
755
776
  process.env.lumic_debug === 'true',
756
777
  allowedUploadPaths: process.env.LUMIC_ALLOWED_UPLOAD_PATHS || '/tmp',
@@ -850,6 +871,7 @@ const LLM_DEFAULT_PROVIDER = parseProvider(getSecrets().config.llmDefaultProvide
850
871
  const LLM_MINI_PROVIDER = parseProvider(getSecrets().config.llmMiniProvider) || LLM_DEFAULT_PROVIDER;
851
872
  const LLM_NORMAL_PROVIDER = parseProvider(getSecrets().config.llmNormalProvider) || LLM_DEFAULT_PROVIDER;
852
873
  const LLM_ADVANCED_PROVIDER = parseProvider(getSecrets().config.llmAdvancedProvider) || LLM_DEFAULT_PROVIDER;
874
+ const LLM_CRITICAL_PROVIDER = parseProvider(getSecrets().config.llmCriticalProvider) || LLM_DEFAULT_PROVIDER;
853
875
  // Keep backward compat — LLM_PROVIDER is the default provider
854
876
  const LLM_PROVIDER = LLM_DEFAULT_PROVIDER;
855
877
  /**
@@ -869,13 +891,14 @@ const DEFAULT_MODEL = (() => {
869
891
  * Model tier values resolved from env vars.
870
892
  *
871
893
  * Resolution order per tier:
872
- * 1. LLM_MODEL_MINI / LLM_MODEL_NORMAL / LLM_MODEL_ADVANCED (explicit model override)
894
+ * 1. LLM_MODEL_MINI / LLM_MODEL_NORMAL / LLM_MODEL_ADVANCED / LLM_MODEL_CRITICAL (explicit model override)
873
895
  * 2. PROVIDER_DEFAULT_MODELS[tier-specific provider][tier] (provider default for tier)
874
896
  *
875
897
  * The tier-specific provider is:
876
898
  * LLM_MINI_PROVIDER → falls back to LLM_DEFAULT_PROVIDER
877
899
  * LLM_NORMAL_PROVIDER → falls back to LLM_DEFAULT_PROVIDER
878
900
  * LLM_ADVANCED_PROVIDER → falls back to LLM_DEFAULT_PROVIDER
901
+ * LLM_CRITICAL_PROVIDER → falls back to LLM_DEFAULT_PROVIDER
879
902
  */
880
903
  const LLM_MODEL_MINI = (() => {
881
904
  const env = getSecrets().config.llmModelMini;
@@ -904,6 +927,21 @@ const LLM_MODEL_ADVANCED = (() => {
904
927
  }
905
928
  return PROVIDER_DEFAULT_MODELS[LLM_ADVANCED_PROVIDER].advanced;
906
929
  })();
930
+ /**
931
+ * The most-capable ("critical") model tier — reserved for the highest-stakes
932
+ * calls. Resolves from LLM_MODEL_CRITICAL, else the critical-tier default of the
933
+ * resolved LLM_CRITICAL_PROVIDER (see PROVIDER_DEFAULT_MODELS). For OpenAI this
934
+ * is gpt-5.6-sol; for other providers it mirrors their advanced tier.
935
+ */
936
+ const LLM_MODEL_CRITICAL = (() => {
937
+ const env = getSecrets().config.llmModelCritical;
938
+ if (env) {
939
+ const resolved = resolveModelName(env);
940
+ if (resolved)
941
+ return resolved;
942
+ }
943
+ return PROVIDER_DEFAULT_MODELS[LLM_CRITICAL_PROVIDER].critical;
944
+ })();
907
945
  const PERPLEXITY_MODEL = getSecrets().config.perplexityModel || DEFAULT_PERPLEXITY_MODEL;
908
946
  const PERPLEXITY_API_URL = 'https://api.perplexity.ai/chat/completions';
909
947
 
@@ -916,15 +954,40 @@ const DEFAULT_DEVELOPER_PROMPT = `
916
954
  Respond only with final content (e.g. code, a json or yaml object, a formatted string, or a markdown document) and nothing else. Do not reply with a preamble, introduction, or conclusion.
917
955
  `;
918
956
  /**
919
- * Token costs in USD per token. Last updated May 2026.
957
+ * Token costs in USD per token. Last updated July 2026.
920
958
  *
921
- * `cacheHitCost` reflects OpenAI's cached-input billing rate (~50% of the
922
- * standard input rate per OpenAI's prompt caching documentation). When set,
923
- * `calculateCost` splits prompt tokens into cached vs non-cached buckets and
924
- * applies the discount; when omitted, cached tokens are billed at full input
925
- * rate (a silent ~50% cost overstatement for cache-friendly workloads).
959
+ * `cacheHitCost` reflects each model's cached-input billing rate. For most
960
+ * models this is ~50% of the standard input rate (per OpenAI's prompt-caching
961
+ * documentation); the GPT-5.6 reasoning trio discounts cached input more
962
+ * aggressively, to 10% of the input rate. When set, `calculateCost` splits
963
+ * prompt tokens into cached vs non-cached buckets and applies the discount; when
964
+ * omitted, cached tokens are billed at the full input rate (a silent cost
965
+ * overstatement for cache-friendly workloads).
926
966
  */
927
967
  const openAiModelCosts = {
968
+ // GPT-5.6 reasoning trio (sol/terra/luna). Tiered per-1M-token pricing,
969
+ // verified against OpenAI's published price sheet on 2026-07-13:
970
+ // sol — $5.00 in / $0.50 cached / $30.00 out (in/out identical to gpt-5.5)
971
+ // terra — $2.50 in / $0.25 cached / $15.00 out
972
+ // luna — $1.00 in / $0.10 cached / $6.00 out
973
+ // Cached input is 10% of the input rate across the trio. Reasoning tokens are
974
+ // a SUBSET of output tokens and are billed at the output rate inside
975
+ // `calculateCost`, so callers must not add them on top of the output count.
976
+ 'gpt-5.6-sol': {
977
+ inputCost: 5 / 1_000_000,
978
+ cacheHitCost: 0.5 / 1_000_000,
979
+ outputCost: 30 / 1_000_000,
980
+ },
981
+ 'gpt-5.6-terra': {
982
+ inputCost: 2.5 / 1_000_000,
983
+ cacheHitCost: 0.25 / 1_000_000,
984
+ outputCost: 15 / 1_000_000,
985
+ },
986
+ 'gpt-5.6-luna': {
987
+ inputCost: 1 / 1_000_000,
988
+ cacheHitCost: 0.1 / 1_000_000,
989
+ outputCost: 6 / 1_000_000,
990
+ },
928
991
  'gpt-5.5': {
929
992
  inputCost: 5 / 1_000_000,
930
993
  cacheHitCost: 2.5 / 1_000_000,
@@ -2256,12 +2319,18 @@ class LLMCostTracker {
2256
2319
  * @param provider - The LLM provider ('openai' or 'deepseek')
2257
2320
  * @param model - The model name (e.g., 'gpt-5', 'deepseek-chat')
2258
2321
  * @param inputTokens - Number of input/prompt tokens
2259
- * @param outputTokens - Number of output/completion tokens
2260
- * @param reasoningTokens - Number of reasoning tokens (default: 0)
2322
+ * @param outputTokens - Total output/completion tokens, INCLUDING reasoning tokens
2323
+ * @param reasoningTokens - Reasoning tokens, a subset of outputTokens (default: 0). Billed at the output rate, not on top of it.
2261
2324
  * @param cacheHitTokens - Number of cache hit tokens (default: 0)
2262
2325
  */
2263
2326
  trackUsage(provider, model, inputTokens, outputTokens, reasoningTokens = 0, cacheHitTokens = 0) {
2264
- const cost = calculateCost(provider, model, inputTokens, outputTokens, reasoningTokens, cacheHitTokens);
2327
+ // Reasoning tokens are a SUBSET of output tokens for every provider tracked
2328
+ // here (they are counted within completion/output tokens and billed at the
2329
+ // output rate). calculateCost bills output AND reasoning separately, so cost
2330
+ // the non-reasoning remainder to avoid billing reasoning twice; the stored
2331
+ // record keeps the full outputTokens/reasoningTokens for reporting. For
2332
+ // non-reasoning calls reasoningTokens is 0, so this is a no-op.
2333
+ const cost = calculateCost(provider, model, inputTokens, outputTokens - reasoningTokens, reasoningTokens, cacheHitTokens);
2265
2334
  const record = {
2266
2335
  provider,
2267
2336
  model,
@@ -2578,6 +2647,105 @@ function isReasoningModel(model) {
2578
2647
  }
2579
2648
  return getModelCapabilities(model).isReasoningModel;
2580
2649
  }
2650
+ /**
2651
+ * Checks whether a model must use the OpenAI Responses API (`/v1/responses`)
2652
+ * for function-tool calls rather than Chat Completions (`/v1/chat/completions`).
2653
+ *
2654
+ * Driven entirely by the {@link ModelCapabilities.requiresResponsesApiForTools}
2655
+ * flag in the model registry — there are no hardcoded model-name checks here.
2656
+ * Unknown models and models without the flag return `false` (stay on Chat
2657
+ * Completions).
2658
+ *
2659
+ * @param model The model to check.
2660
+ * @returns True when tool-bearing calls for this model must be routed to the
2661
+ * Responses API, false otherwise.
2662
+ */
2663
+ function requiresResponsesApiForTools(model) {
2664
+ if (!isValidModel(model)) {
2665
+ return false;
2666
+ }
2667
+ return getModelCapabilities(model).requiresResponsesApiForTools ?? false;
2668
+ }
2669
+ /**
2670
+ * OpenAI's `reasoning_effort` ladder, ordered from least to most effort. This is
2671
+ * the full 2026 ladder (developers.openai.com), including `'max'` — the OpenAI
2672
+ * SDK v6 `Shared.ReasoningEffort` type accepts this entire set on the wire.
2673
+ * Mirrors the widened {@link LLMOptions.reasoning_effort} union.
2674
+ */
2675
+ const REASONING_EFFORT_LADDER = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
2676
+ /**
2677
+ * Per-model `reasoning_effort` support window for OpenAI reasoning models,
2678
+ * expressed on {@link REASONING_EFFORT_LADDER}. `min`/`max` are the lowest and
2679
+ * highest effort each model's API accepts; a request below `min` clamps up and
2680
+ * above `max` clamps down, so an over/under-shoot degrades gracefully instead of
2681
+ * returning HTTP 400.
2682
+ *
2683
+ * Reasoning models ABSENT from this table omit `reasoning_effort` entirely —
2684
+ * notably `o1-mini`, which does not accept the parameter at all. The windows
2685
+ * record TRUE API support; the wire value is additionally narrowed to
2686
+ * {@link SdkReasoningEffort} in {@link toSdkReasoningEffort}, so when the SDK
2687
+ * widens only that mapping (not these windows) needs revisiting.
2688
+ */
2689
+ const MODEL_REASONING_EFFORT_SUPPORT = {
2690
+ // GPT-5.6 reasoning trio — full ladder (developers.openai.com/api/docs/models).
2691
+ 'gpt-5.6-sol': { min: 'minimal', max: 'xhigh' },
2692
+ 'gpt-5.6-terra': { min: 'minimal', max: 'xhigh' },
2693
+ 'gpt-5.6-luna': { min: 'minimal', max: 'xhigh' },
2694
+ // o-series reasoning models accept low|medium|high (no none/minimal/xhigh).
2695
+ o1: { min: 'low', max: 'high' },
2696
+ o3: { min: 'low', max: 'high' },
2697
+ 'o3-mini': { min: 'low', max: 'high' },
2698
+ 'o4-mini': { min: 'low', max: 'high' },
2699
+ // o1-mini is intentionally omitted — it rejects reasoning_effort outright.
2700
+ };
2701
+ /**
2702
+ * Identity pass-through from a full-ladder effort level to an SDK-sendable value.
2703
+ * Under OpenAI SDK v6 the wire type ({@link SdkReasoningEffort}) spans the entire
2704
+ * ladder, so every level is sendable as-is; the per-model
2705
+ * {@link MODEL_REASONING_EFFORT_SUPPORT} window (applied in
2706
+ * {@link clampReasoningEffort}) is what keeps a request within a model's range.
2707
+ * Retained as a named seam so a future SDK narrowing has one obvious place to
2708
+ * re-introduce folding.
2709
+ *
2710
+ * @param level A level on {@link REASONING_EFFORT_LADDER}.
2711
+ * @returns The same level, typed as SDK-sendable.
2712
+ */
2713
+ function toSdkReasoningEffort(level) {
2714
+ return level;
2715
+ }
2716
+ /**
2717
+ * Clamps a requested {@link LLMOptions.reasoning_effort} to a value the target
2718
+ * model — and the installed OpenAI SDK — will accept, degrading gracefully so an
2719
+ * unsupported (too-high or too-low) level never returns HTTP 400.
2720
+ *
2721
+ * Resolution:
2722
+ * 1. Models without an entry in {@link MODEL_REASONING_EFFORT_SUPPORT}
2723
+ * (non-reasoning models, or reasoning models like `o1-mini` that reject the
2724
+ * parameter) ⇒ `undefined`, i.e. omit `reasoning_effort` from the request.
2725
+ * 2. Clamp the requested level into the model's `[min, max]` window.
2726
+ * 3. Fold onto the SDK-sendable range via {@link toSdkReasoningEffort}.
2727
+ *
2728
+ * Shared by BOTH OpenAI transports so the Chat and Responses paths clamp
2729
+ * identically.
2730
+ *
2731
+ * @param requested The caller's requested effort (full ladder).
2732
+ * @param model The normalized model name the request targets.
2733
+ * @returns An SDK-safe effort, or `undefined` when the field must be omitted.
2734
+ */
2735
+ function clampReasoningEffort(requested, model) {
2736
+ const support = MODEL_REASONING_EFFORT_SUPPORT[model];
2737
+ if (!support) {
2738
+ return undefined;
2739
+ }
2740
+ const requestedIdx = REASONING_EFFORT_LADDER.indexOf(requested);
2741
+ if (requestedIdx < 0) {
2742
+ return undefined;
2743
+ }
2744
+ const minIdx = REASONING_EFFORT_LADDER.indexOf(support.min);
2745
+ const maxIdx = REASONING_EFFORT_LADDER.indexOf(support.max);
2746
+ const clampedIdx = Math.min(Math.max(requestedIdx, minIdx), maxIdx);
2747
+ return toSdkReasoningEffort(REASONING_EFFORT_LADDER[clampedIdx]);
2748
+ }
2581
2749
  /**
2582
2750
  * Normalizes the response format option based on model compatibility.
2583
2751
  * @param responseFormat The desired response format.
@@ -2709,6 +2877,18 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2709
2877
  if (options.max_completion_tokens !== undefined) {
2710
2878
  queryOptions.max_completion_tokens = options.max_completion_tokens;
2711
2879
  }
2880
+ // Forward reasoning_effort on the Chat path for reasoning models. Chat
2881
+ // Completions accepts reasoning_effort for reasoning models (only tools +
2882
+ // effort together 400 on GPT-5.6, and those tool-bearing calls are already
2883
+ // routed to the Responses API), so this is safe. The value is clamped per
2884
+ // model so an unsupported level degrades gracefully instead of 400-ing;
2885
+ // clampReasoningEffort returns undefined for models that reject the parameter.
2886
+ if (options.reasoning_effort !== undefined && isReasoningModel(normalizedModel)) {
2887
+ const effort = clampReasoningEffort(options.reasoning_effort, normalizedModel);
2888
+ if (effort !== undefined) {
2889
+ queryOptions.reasoning_effort = effort;
2890
+ }
2891
+ }
2712
2892
  // Only set response_format when it's not the default 'text' type
2713
2893
  // (sending response_format: {type: 'text'} is redundant and may cause issues)
2714
2894
  const responseFormatOption = getResponseFormatOption(responseFormat, normalizedModel);
@@ -2778,12 +2958,18 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2778
2958
  const response = {
2779
2959
  id: completion.id,
2780
2960
  content: completion.choices[0]?.message?.content || '',
2781
- tool_calls: completion.choices[0]?.message?.tool_calls,
2961
+ // OpenAI SDK v6 widened tool calls into a function/custom union; the engine
2962
+ // only emits + parses function calls, so narrow to the function variant to
2963
+ // preserve the byte-identical `{ id, type, function }` shape.
2964
+ tool_calls: completion.choices[0]?.message?.tool_calls?.filter((tc) => tc.type === 'function'),
2782
2965
  usage: {
2783
2966
  prompt_tokens: completion.usage?.prompt_tokens ?? 0,
2784
2967
  completion_tokens: completion.usage?.completion_tokens ?? 0,
2785
2968
  total_tokens: completion.usage?.total_tokens ?? 0,
2786
2969
  cached_tokens: cachedTokens,
2970
+ // Non-reasoning Chat-Completions models report no reasoning tokens; read
2971
+ // defensively so the field is always present and correct.
2972
+ reasoning_tokens: completion.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
2787
2973
  },
2788
2974
  system_fingerprint: completion.system_fingerprint,
2789
2975
  service_tier: options.service_tier,
@@ -2792,9 +2978,383 @@ async function createCompletion(content, responseFormat, options = DEFAULT_OPTIO
2792
2978
  };
2793
2979
  return response;
2794
2980
  }
2981
+ // ─────────────────────────────────────────────────────────────────────────────
2982
+ // Transparent Chat-Completions → Responses API routing (GPT-5.6 reasoning trio)
2983
+ //
2984
+ // OpenAI's GPT-5.6 reasoning models (sol/terra/luna) return HTTP 400 for
2985
+ // function tools on `/v1/chat/completions`
2986
+ // ("Function tools with reasoning_effort are not supported for gpt-5.6-sol in
2987
+ // /v1/chat/completions"); they require the Responses API (`/v1/responses`).
2988
+ // The helpers below convert a Chat-Completions-style request into a Responses
2989
+ // request and normalize the Responses result back into the SAME internal
2990
+ // {@link CompletionResponse}, so callers of {@link makeOpenAIChatCompletionCall}
2991
+ // receive a byte-identical {@link LLMResponse} envelope regardless of which
2992
+ // endpoint served the call. The routing decision is data-driven via
2993
+ // {@link requiresResponsesApiForTools}.
2994
+ // ─────────────────────────────────────────────────────────────────────────────
2995
+ /**
2996
+ * Coerces a Chat-Completions message `content` value (string, content-part
2997
+ * array, or null) into a plain string. Mirrors the text-extraction rule used
2998
+ * elsewhere in this module: array parts contribute their `text` field, all
2999
+ * other shapes contribute nothing.
3000
+ *
3001
+ * @param content The message content in any of its Chat-Completions shapes.
3002
+ * @returns The concatenated text, or an empty string when there is none.
3003
+ */
3004
+ function coerceMessageText(content) {
3005
+ if (typeof content === 'string') {
3006
+ return content;
3007
+ }
3008
+ if (Array.isArray(content)) {
3009
+ return content
3010
+ .map((part) => typeof part === 'object' && part !== null && 'text' in part &&
3011
+ typeof part.text === 'string'
3012
+ ? part.text
3013
+ : '')
3014
+ .join('');
3015
+ }
3016
+ return '';
3017
+ }
3018
+ /**
3019
+ * Converts Chat-Completions user content (string or multi-modal content parts)
3020
+ * into the Responses API content shape. String content passes through
3021
+ * unchanged; content-part arrays map `text` → `input_text` and `image_url` →
3022
+ * `input_image`. Unsupported part kinds (audio/file) are skipped rather than
3023
+ * emitted in an incompatible shape — no tool-calling caller uses them today.
3024
+ *
3025
+ * @param content The Chat-Completions user content.
3026
+ * @returns A string or an array of Responses input-content parts.
3027
+ */
3028
+ function chatContentToResponsesContent(content) {
3029
+ if (typeof content === 'string') {
3030
+ return content;
3031
+ }
3032
+ const parts = [];
3033
+ for (const part of content) {
3034
+ if (part.type === 'text') {
3035
+ parts.push({ type: 'input_text', text: part.text });
3036
+ }
3037
+ else if (part.type === 'image_url') {
3038
+ parts.push({
3039
+ type: 'input_image',
3040
+ image_url: part.image_url.url,
3041
+ detail: part.image_url.detail ?? 'auto',
3042
+ });
3043
+ }
3044
+ }
3045
+ return parts;
3046
+ }
3047
+ /**
3048
+ * Converts a single Chat-Completions message into zero or more Responses API
3049
+ * input items. Assistant tool calls become `function_call` items and `tool`
3050
+ * results become `function_call_output` items — both keyed by the SAME id the
3051
+ * chat envelope exposed as `tool_call.id` / `tool_call_id`, so a multi-turn
3052
+ * tool conversation round-trips correctly through the Responses transport.
3053
+ *
3054
+ * @param message The Chat-Completions message to convert.
3055
+ * @returns The equivalent Responses input items (may be empty).
3056
+ */
3057
+ function chatMessageToResponsesInput(message) {
3058
+ switch (message.role) {
3059
+ case 'system':
3060
+ return [{ role: 'system', content: coerceMessageText(message.content) }];
3061
+ case 'developer':
3062
+ return [{ role: 'developer', content: coerceMessageText(message.content) }];
3063
+ case 'user':
3064
+ return [{ role: 'user', content: chatContentToResponsesContent(message.content) }];
3065
+ case 'assistant': {
3066
+ const items = [];
3067
+ const text = coerceMessageText(message.content);
3068
+ if (text.length > 0) {
3069
+ items.push({ role: 'assistant', content: text });
3070
+ }
3071
+ if (message.tool_calls) {
3072
+ for (const toolCall of message.tool_calls) {
3073
+ if (toolCall.type === 'function') {
3074
+ items.push({
3075
+ type: 'function_call',
3076
+ call_id: toolCall.id,
3077
+ name: toolCall.function.name,
3078
+ arguments: toolCall.function.arguments,
3079
+ });
3080
+ }
3081
+ }
3082
+ }
3083
+ return items;
3084
+ }
3085
+ case 'tool':
3086
+ return [{
3087
+ type: 'function_call_output',
3088
+ call_id: message.tool_call_id,
3089
+ output: coerceMessageText(message.content),
3090
+ }];
3091
+ default:
3092
+ // Deprecated `function`-role messages carry no call_id to correlate; keep
3093
+ // the text so history stays coherent. No current caller emits these.
3094
+ return [{ role: 'assistant', content: coerceMessageText(message.content) }];
3095
+ }
3096
+ }
3097
+ /**
3098
+ * Converts a Chat-Completions function tool (`{ type:'function', function:{…} }`)
3099
+ * into a Responses API function tool (`{ type:'function', name, parameters,
3100
+ * strict, description }`). `strict` defaults to `false` to preserve Chat
3101
+ * Completions' effective default (non-strict) — the Responses API otherwise
3102
+ * defaults function tools to strict, which would silently change model
3103
+ * behaviour and could reject payloads the Chat path accepted.
3104
+ *
3105
+ * @param tool The Chat-Completions function tool.
3106
+ * @returns The equivalent Responses API function tool.
3107
+ */
3108
+ function chatToolToResponsesFunctionTool(tool) {
3109
+ return {
3110
+ type: 'function',
3111
+ name: tool.function.name,
3112
+ description: tool.function.description,
3113
+ parameters: tool.function.parameters ?? {},
3114
+ strict: tool.function.strict ?? false,
3115
+ };
3116
+ }
3117
+ /**
3118
+ * Maps an {@link OpenAIResponseFormat} onto the Responses API `text.format`
3119
+ * config, applying the same model-capability gates as the Chat path
3120
+ * ({@link getResponseFormatOption}): `'text'` yields `undefined` (omit
3121
+ * `text.format`), `'json'` yields `json_object` only when the model supports
3122
+ * JSON mode, and a `json_schema` request yields the flat Responses
3123
+ * `json_schema` format (name/schema at the top level, unlike Chat's nested
3124
+ * `json_schema` wrapper).
3125
+ *
3126
+ * @param responseFormat The requested response format.
3127
+ * @param normalizedModel The normalized model name.
3128
+ * @returns The `text.format` config, or `undefined` when none should be sent.
3129
+ * @throws Error when a `json_schema` format is requested for an incompatible model.
3130
+ */
3131
+ function getResponsesTextFormat(responseFormat, normalizedModel) {
3132
+ if (responseFormat === 'text') {
3133
+ return undefined;
3134
+ }
3135
+ if (responseFormat === 'json') {
3136
+ return supportsJsonMode(normalizedModel) ? { type: 'json_object' } : undefined;
3137
+ }
3138
+ if (typeof responseFormat === 'object' && responseFormat.type === 'json_schema') {
3139
+ if (!isStructuredOutputCompatibleModel(normalizedModel)) {
3140
+ throw new Error(`${normalizedModel} is not compatible with structured outputs / json object.`);
3141
+ }
3142
+ return {
3143
+ type: 'json_schema',
3144
+ name: 'Response',
3145
+ schema: {
3146
+ type: 'object',
3147
+ properties: responseFormat.schema.properties,
3148
+ required: responseFormat.schema.required || [],
3149
+ },
3150
+ };
3151
+ }
3152
+ throw new Error(`Invalid response format: ${String(responseFormat)}`);
3153
+ }
3154
+ /**
3155
+ * Builds an OpenAI Responses API request body from the exact same arguments a
3156
+ * caller passes to {@link makeOpenAIChatCompletionCall}. This is the request
3157
+ * half of the transparent routing layer.
3158
+ *
3159
+ * The mapping deliberately mirrors {@link createCompletion}'s Chat request
3160
+ * one-for-one so request semantics are preserved across transports:
3161
+ * - `developerPrompt` (+ context messages + user `content`) → `input` items
3162
+ * (chat `tool_calls`/`tool` messages become `function_call`/
3163
+ * `function_call_output` items).
3164
+ * - chat `tools` → Responses `tools` (function tools only).
3165
+ * - `temperature` → `temperature`, gated by {@link supportsTemperature} exactly
3166
+ * as the Chat path (dropped for GPT-5.6/o-series reasoning models).
3167
+ * - `max_completion_tokens` → `max_output_tokens`.
3168
+ * - {@link OpenAIResponseFormat} → `text.format`.
3169
+ * - `reasoning_effort` → `reasoning.effort`, clamped per-model via
3170
+ * {@link clampReasoningEffort} and gated on {@link isReasoningModel} exactly as
3171
+ * the Chat path, so the two transports clamp identically. Only set when the
3172
+ * caller explicitly supplies it — parity is preserved for callers (like the
3173
+ * engine decision path) that do not.
3174
+ *
3175
+ * Parameters the Chat path does not forward (`top_p`, `parallel_tool_calls`,
3176
+ * `tool_choice`, `metadata`, `store`) are intentionally omitted here too, so the
3177
+ * request semantics stay identical to today's production Chat behaviour.
3178
+ *
3179
+ * @param content The user content (string or multi-modal content parts).
3180
+ * @param responseFormat The desired response format.
3181
+ * @param options The Chat-Completions-style options bag.
3182
+ * @param normalizedModel The normalized model name to target.
3183
+ * @returns A ready-to-send non-streaming Responses API request body.
3184
+ * @throws Error when a `json_schema` format is requested for an incompatible model.
3185
+ */
3186
+ function buildResponsesRequestFromChatOptions(content, responseFormat, options, normalizedModel) {
3187
+ const input = [];
3188
+ if (options.developerPrompt && supportsDeveloperPrompt(normalizedModel)) {
3189
+ input.push({ role: 'developer', content: options.developerPrompt });
3190
+ }
3191
+ if (options.context) {
3192
+ for (const message of options.context) {
3193
+ input.push(...chatMessageToResponsesInput(message));
3194
+ }
3195
+ }
3196
+ input.push({ role: 'user', content: chatContentToResponsesContent(content) });
3197
+ const requestBody = {
3198
+ model: normalizedModel,
3199
+ input,
3200
+ };
3201
+ if (options.tools && options.tools.length > 0) {
3202
+ const functionTools = options.tools
3203
+ .filter((tool) => tool.type === 'function')
3204
+ .map(chatToolToResponsesFunctionTool);
3205
+ if (functionTools.length > 0) {
3206
+ requestBody.tools = functionTools;
3207
+ }
3208
+ }
3209
+ if (options.temperature !== undefined && supportsTemperature(normalizedModel)) {
3210
+ requestBody.temperature = options.temperature;
3211
+ }
3212
+ if (options.max_completion_tokens !== undefined) {
3213
+ requestBody.max_output_tokens = options.max_completion_tokens;
3214
+ }
3215
+ const textFormat = getResponsesTextFormat(responseFormat, normalizedModel);
3216
+ if (textFormat) {
3217
+ requestBody.text = { format: textFormat };
3218
+ }
3219
+ if (options.reasoning_effort !== undefined && isReasoningModel(normalizedModel)) {
3220
+ const effort = clampReasoningEffort(options.reasoning_effort, normalizedModel);
3221
+ if (effort !== undefined) {
3222
+ requestBody.reasoning = { effort };
3223
+ }
3224
+ }
3225
+ return requestBody;
3226
+ }
3227
+ /**
3228
+ * Normalizes an OpenAI Responses API result into the internal
3229
+ * {@link CompletionResponse} shape. This is the response half of the
3230
+ * transparent routing layer and is the single most correctness-critical mapping
3231
+ * in this module: the returned `tool_calls` MUST be byte-identical to what the
3232
+ * Chat path produces so downstream consumers (notably the engine decision path,
3233
+ * which reads `tool_call.function.arguments` as a JSON string) parse it
3234
+ * unchanged.
3235
+ *
3236
+ * Mapping:
3237
+ * - `function_call` output items → `{ id: call_id, type:'function',
3238
+ * function:{ name, arguments } }` with `arguments` kept as the raw JSON
3239
+ * string (never pre-parsed) — exactly the Chat `ChatCompletionMessageToolCall`
3240
+ * shape. `id` is sourced from `call_id` (not the item `id`) so it round-trips
3241
+ * as the `tool_call_id` in a multi-turn conversation.
3242
+ * - `message` output items → concatenated `output_text` parts (refusals and
3243
+ * non-text parts contribute nothing, matching the Chat path's null-content →
3244
+ * empty-string behaviour).
3245
+ * - usage: `input_tokens`→prompt, `output_tokens`→completion,
3246
+ * `input_tokens_details.cached_tokens`→cached,
3247
+ * `output_tokens_details.reasoning_tokens`→reasoning.
3248
+ *
3249
+ * `system_fingerprint` is `undefined` — the Responses API does not return one
3250
+ * (the Chat path forwards it; consumers already treat it as optional).
3251
+ *
3252
+ * @param response The raw OpenAI Responses API result.
3253
+ * @param normalizedModel The normalized model name the request targeted.
3254
+ * @param serviceTier The caller's requested service tier, echoed through as on
3255
+ * the Chat path.
3256
+ * @returns A {@link CompletionResponse} structurally identical to the Chat path's.
3257
+ */
3258
+ function normalizeResponsesToCompletion(response, normalizedModel, serviceTier) {
3259
+ const toolCalls = response.output
3260
+ ?.filter((item) => item.type === 'function_call')
3261
+ .map((toolCall) => ({
3262
+ id: toolCall.call_id,
3263
+ type: 'function',
3264
+ function: {
3265
+ name: toolCall.name,
3266
+ arguments: toolCall.arguments,
3267
+ },
3268
+ }));
3269
+ const content = response.output
3270
+ ?.filter((item) => item.type === 'message')
3271
+ .map((item) => item.content
3272
+ .filter((part) => part.type === 'output_text')
3273
+ .map((part) => part.text)
3274
+ .join(''))
3275
+ .join('') || '';
3276
+ return {
3277
+ id: response.id,
3278
+ content,
3279
+ tool_calls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
3280
+ usage: {
3281
+ prompt_tokens: response.usage?.input_tokens ?? 0,
3282
+ completion_tokens: response.usage?.output_tokens ?? 0,
3283
+ total_tokens: response.usage?.total_tokens ?? 0,
3284
+ cached_tokens: response.usage?.input_tokens_details?.cached_tokens ?? 0,
3285
+ reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens ?? 0,
3286
+ },
3287
+ system_fingerprint: undefined,
3288
+ service_tier: serviceTier,
3289
+ provider: 'openai',
3290
+ model: normalizedModel,
3291
+ };
3292
+ }
3293
+ /**
3294
+ * Executes an OpenAI Responses API call for a Chat-Completions-style request and
3295
+ * returns the internal {@link CompletionResponse}. Mirrors {@link createCompletion}'s
3296
+ * transport concerns exactly — API-key resolution, timeout, `AbortSignal`
3297
+ * plumbing, the shared retry policy, and metadata-only failure logging — so the
3298
+ * two producers differ ONLY in the endpoint they hit.
3299
+ *
3300
+ * @param content The user content (string or multi-modal content parts).
3301
+ * @param responseFormat The desired response format.
3302
+ * @param options The Chat-Completions-style options bag.
3303
+ * @returns The normalized completion, identical in shape to the Chat path.
3304
+ * @throws Error when the API key is missing or the API call ultimately fails.
3305
+ */
3306
+ async function createResponsesCompletion(content, responseFormat, options = DEFAULT_OPTIONS$1) {
3307
+ const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
3308
+ const apiKey = options.apiKey || getSecrets().openai.apiKey;
3309
+ if (!apiKey) {
3310
+ throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
3311
+ }
3312
+ const openai = new OpenAI({
3313
+ apiKey: apiKey,
3314
+ timeout: options.timeout ?? LLM_TIMEOUT_MS,
3315
+ });
3316
+ const requestBody = buildResponsesRequestFromChatOptions(content, responseFormat, options, normalizedModel);
3317
+ let response;
3318
+ try {
3319
+ response = await withRetry(
3320
+ // Same AbortSignal plumbing as the Chat path — a fired signal tears down
3321
+ // the in-flight Responses request and releases its body buffer.
3322
+ () => openai.responses.create(requestBody, options.signal ? { signal: options.signal } : undefined), {
3323
+ maxRetries: 3,
3324
+ baseDelayMs: 2000,
3325
+ maxDelayMs: 30000,
3326
+ retryableErrors: isRetryableLLMError,
3327
+ signal: options.signal,
3328
+ }, `OpenAI-Responses:${normalizedModel}`);
3329
+ }
3330
+ catch (error) {
3331
+ // Metadata-only failure snapshot (no message content, no API key), matching
3332
+ // the Chat path's defensive observability.
3333
+ const errorMessage = error instanceof Error ? error.message : String(error);
3334
+ getLumicLogger().error(`OpenAI Responses call failed for model ${normalizedModel}`, {
3335
+ model: normalizedModel,
3336
+ errorMessage,
3337
+ inputItemCount: Array.isArray(requestBody.input) ? requestBody.input.length : 1,
3338
+ toolCount: requestBody.tools?.length ?? 0,
3339
+ hasTemperature: requestBody.temperature !== undefined && requestBody.temperature !== null,
3340
+ hasTextFormat: requestBody.text?.format !== undefined,
3341
+ hasMaxOutputTokens: requestBody.max_output_tokens !== undefined && requestBody.max_output_tokens !== null,
3342
+ hasReasoningEffort: requestBody.reasoning?.effort !== undefined && requestBody.reasoning?.effort !== null,
3343
+ });
3344
+ throw error;
3345
+ }
3346
+ return normalizeResponsesToCompletion(response, normalizedModel, options.service_tier);
3347
+ }
2795
3348
  /**
2796
3349
  * Makes a call to the Lumic LLM interface, either to the default model or to one specified.
2797
3350
  *
3351
+ * Transport is chosen transparently: models flagged
3352
+ * {@link ModelCapabilities.requiresResponsesApiForTools} (the GPT-5.6 reasoning
3353
+ * trio) that are called WITH function tools are routed to the OpenAI Responses
3354
+ * API; every other call stays on Chat Completions. The returned
3355
+ * {@link LLMResponse} envelope — `response`, `tool_calls`, and `usage` — is
3356
+ * byte-identical across both transports, so callers require no changes.
3357
+ *
2798
3358
  * @param content The content to pass to the LLM.
2799
3359
  * @param responseFormat The format of the response. Defaults to 'text'.
2800
3360
  * @param options The options for the LLM call. Defaults to an empty object.
@@ -2805,11 +3365,28 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2805
3365
  ...DEFAULT_OPTIONS$1,
2806
3366
  ...options,
2807
3367
  };
2808
- const completion = await createCompletion(content, responseFormat, mergedOptions);
3368
+ // Transparent transport routing. GPT-5.6 reasoning models 400 on
3369
+ // Chat-Completions + function tools, so route tool-bearing calls for such
3370
+ // models to the Responses API. `createResponsesCompletion` returns the SAME
3371
+ // CompletionResponse shape, so all cost-tracking and envelope construction
3372
+ // below is shared and the caller-facing result is identical.
3373
+ const normalizedModel = normalizeModelName(mergedOptions.model || DEFAULT_MODEL);
3374
+ const hasTools = !!(mergedOptions.tools && mergedOptions.tools.length > 0);
3375
+ const completion = requiresResponsesApiForTools(normalizedModel) && hasTools
3376
+ ? await createResponsesCompletion(content, responseFormat, mergedOptions)
3377
+ : await createCompletion(content, responseFormat, mergedOptions);
2809
3378
  // Track cost in the global cost tracker. Pass cached tokens through so the
2810
3379
  // tracker applies the discounted cached-input rate (typically ~50% of the
2811
3380
  // standard input rate) instead of billing every input token at full price.
2812
- getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens);
3381
+ // reasoning_tokens is 0 on the Chat path and the real count on the Responses
3382
+ // path, so reasoning-heavy models are billed accurately.
3383
+ getLLMCostTracker().trackUsage('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, completion.usage.reasoning_tokens, completion.usage.cached_tokens);
3384
+ // Reasoning tokens are a SUBSET of completion (output) tokens. `calculateCost`
3385
+ // bills output AND reasoning at the output rate, so passing the full
3386
+ // completion_tokens alongside reasoning_tokens would bill reasoning twice.
3387
+ // Cost the non-reasoning remainder here and let calculateCost add reasoning
3388
+ // back once; the reported usage below keeps the full completion_tokens.
3389
+ const nonReasoningOutputTokens = completion.usage.completion_tokens - completion.usage.reasoning_tokens;
2813
3390
  // Handle tool calls differently
2814
3391
  if (completion.tool_calls && completion.tool_calls.length > 0) {
2815
3392
  const toolCallResponse = {
@@ -2824,11 +3401,11 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2824
3401
  usage: {
2825
3402
  prompt_tokens: completion.usage.prompt_tokens,
2826
3403
  completion_tokens: completion.usage.completion_tokens,
2827
- reasoning_tokens: 0,
3404
+ reasoning_tokens: completion.usage.reasoning_tokens,
2828
3405
  provider: 'openai',
2829
3406
  model: completion.model,
2830
3407
  cached_tokens: completion.usage.cached_tokens,
2831
- cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
3408
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, nonReasoningOutputTokens, completion.usage.reasoning_tokens, completion.usage.cached_tokens),
2832
3409
  },
2833
3410
  tool_calls: completion.tool_calls,
2834
3411
  };
@@ -2843,11 +3420,11 @@ const makeOpenAIChatCompletionCall = async (content, responseFormat = 'text', op
2843
3420
  usage: {
2844
3421
  prompt_tokens: completion.usage.prompt_tokens,
2845
3422
  completion_tokens: completion.usage.completion_tokens,
2846
- reasoning_tokens: 0,
3423
+ reasoning_tokens: completion.usage.reasoning_tokens,
2847
3424
  provider: 'openai',
2848
3425
  model: completion.model,
2849
3426
  cached_tokens: completion.usage.cached_tokens,
2850
- cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, completion.usage.completion_tokens, 0, completion.usage.cached_tokens),
3427
+ cost: calculateCost('openai', completion.model, completion.usage.prompt_tokens, nonReasoningOutputTokens, completion.usage.reasoning_tokens, completion.usage.cached_tokens),
2851
3428
  },
2852
3429
  tool_calls: completion.tool_calls,
2853
3430
  };
@@ -2906,8 +3483,14 @@ const makeResponsesAPICall = async (input, options = {}) => {
2906
3483
  // Responses API exposes cached input tokens under `input_tokens_details.cached_tokens`
2907
3484
  // (the equivalent of Chat Completions' `prompt_tokens_details.cached_tokens`).
2908
3485
  const responsesCachedTokens = response.usage?.input_tokens_details?.cached_tokens || 0;
3486
+ const responsesOutputTokens = response.usage?.output_tokens || 0;
3487
+ const responsesReasoningTokens = response.usage?.output_tokens_details?.reasoning_tokens || 0;
3488
+ // Reasoning tokens are a SUBSET of output tokens; cost the non-reasoning
3489
+ // remainder so calculateCost does not bill reasoning twice (same rationale as
3490
+ // makeOpenAIChatCompletionCall). Reported usage keeps the full output/reasoning counts.
3491
+ const responsesNonReasoningOutputTokens = responsesOutputTokens - responsesReasoningTokens;
2909
3492
  // Track cost in the global cost tracker
2910
- getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens);
3493
+ getLLMCostTracker().trackUsage('openai', normalizedModel, response.usage?.input_tokens || 0, responsesOutputTokens, responsesReasoningTokens, responsesCachedTokens);
2911
3494
  // Extract tool calls from the output
2912
3495
  const toolCalls = response.output
2913
3496
  ?.filter((item) => item.type === 'function_call')
@@ -2944,12 +3527,12 @@ const makeResponsesAPICall = async (input, options = {}) => {
2944
3527
  response: toolCallResponse,
2945
3528
  usage: {
2946
3529
  prompt_tokens: response.usage?.input_tokens || 0,
2947
- completion_tokens: response.usage?.output_tokens || 0,
2948
- reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
3530
+ completion_tokens: responsesOutputTokens,
3531
+ reasoning_tokens: responsesReasoningTokens,
2949
3532
  provider: 'openai',
2950
3533
  model: normalizedModel,
2951
3534
  cached_tokens: responsesCachedTokens,
2952
- cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
3535
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, responsesNonReasoningOutputTokens, responsesReasoningTokens, responsesCachedTokens),
2953
3536
  },
2954
3537
  tool_calls: toolCalls,
2955
3538
  ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
@@ -2977,12 +3560,12 @@ const makeResponsesAPICall = async (input, options = {}) => {
2977
3560
  response: parsedResponse,
2978
3561
  usage: {
2979
3562
  prompt_tokens: response.usage?.input_tokens || 0,
2980
- completion_tokens: response.usage?.output_tokens || 0,
2981
- reasoning_tokens: response.usage?.output_tokens_details?.reasoning_tokens || 0,
3563
+ completion_tokens: responsesOutputTokens,
3564
+ reasoning_tokens: responsesReasoningTokens,
2982
3565
  provider: 'openai',
2983
3566
  model: normalizedModel,
2984
3567
  cached_tokens: responsesCachedTokens,
2985
- cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, response.usage?.output_tokens || 0, response.usage?.output_tokens_details?.reasoning_tokens || 0, responsesCachedTokens),
3568
+ cost: calculateCost('openai', normalizedModel, response.usage?.input_tokens || 0, responsesNonReasoningOutputTokens, responsesReasoningTokens, responsesCachedTokens),
2986
3569
  },
2987
3570
  tool_calls: toolCalls,
2988
3571
  ...(codeInterpreterOutputs ? { code_interpreter_outputs: codeInterpreterOutputs } : {}),
@@ -8313,7 +8896,12 @@ const isRetryableAnthropicError = (error) => {
8313
8896
  * Anthropic: { name, description, input_schema }
8314
8897
  */
8315
8898
  function translateToolsToAnthropic(tools) {
8316
- return tools.map((tool) => ({
8899
+ // OpenAI SDK v6 widened `ChatCompletionTool` into a function/custom union.
8900
+ // lumic-utils only supports function tools, so narrow to the function variant
8901
+ // before reading `.function`. Custom tools (which lumic never emits) are skipped.
8902
+ return tools
8903
+ .filter((tool) => tool.type === 'function')
8904
+ .map((tool) => ({
8317
8905
  name: tool.function.name,
8318
8906
  description: tool.function.description || '',
8319
8907
  input_schema: (tool.function.parameters || { type: 'object', properties: {} }),
@@ -8707,8 +9295,11 @@ async function makeOpenAICompatibleCall(content, responseFormat = 'text', option
8707
9295
  const completionTokens = completion.usage?.completion_tokens || 0;
8708
9296
  // Track cost
8709
9297
  getLLMCostTracker().trackUsage(providerConfig.name, model, promptTokens, completionTokens);
8710
- // Handle tool calls
8711
- const toolCalls = completion.choices[0]?.message?.tool_calls;
9298
+ // Handle tool calls. OpenAI SDK v6 widened tool calls into a function/custom
9299
+ // union; these OpenAI-compatible providers only return function calls, so
9300
+ // narrow to the function variant to preserve the byte-identical
9301
+ // `function.{name,arguments}` shape the engine parses.
9302
+ const toolCalls = completion.choices[0]?.message?.tool_calls?.filter((tc) => tc.type === 'function');
8712
9303
  if (toolCalls && toolCalls.length > 0) {
8713
9304
  const toolCallResponse = {
8714
9305
  tool_calls: toolCalls.map((tc) => ({
@@ -9257,7 +9848,13 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
9257
9848
  // Handle tool calls similarly to OpenAI
9258
9849
  if (completion.tool_calls && completion.tool_calls.length > 0) {
9259
9850
  const toolCallResponse = {
9260
- tool_calls: completion.tool_calls.map((tc) => ({
9851
+ // OpenAI SDK v6 widened tool calls into a function/custom union. DeepSeek
9852
+ // (called via the OpenAI-compatible surface) only returns function calls,
9853
+ // so narrow to the function variant to preserve the byte-identical
9854
+ // `function.{name,arguments}` shape.
9855
+ tool_calls: completion.tool_calls
9856
+ .filter((tc) => tc.type === 'function')
9857
+ .map((tc) => ({
9261
9858
  id: tc.id,
9262
9859
  name: tc.function.name,
9263
9860
  arguments: JSON.parse(tc.function.arguments),
@@ -23211,11 +23808,11 @@ let poolConfig = DEFAULT_POOL_CONFIG;
23211
23808
  async function loadApolloModules() {
23212
23809
  if (typeof window === "undefined" || process.env.AWS_EXECUTION_ENV) {
23213
23810
  // Server-side (or Lambda): load the CommonJS‑based implementation.
23214
- return (await import('./apollo-client.server-Bv2rQsCw.js'));
23811
+ return (await import('./apollo-client.server-YkSyk6OJ.js'));
23215
23812
  }
23216
23813
  else {
23217
23814
  // Client-side: load the ESM‑based implementation.
23218
- return (await import('./apollo-client.client-KzZx5slz.js'));
23815
+ return (await import('./apollo-client.client-DsFSBlst.js'));
23219
23816
  }
23220
23817
  }
23221
23818
  /**
@@ -80466,11 +81063,93 @@ const tools = [
80466
81063
  }
80467
81064
  }
80468
81065
  },
81066
+ {
81067
+ "type": "function",
81068
+ "function": {
81069
+ "name": "requiresResponsesApiForTools",
81070
+ "description": "Checks whether a model must use the OpenAI Responses API (`/v1/responses`) for function-tool calls rather than Chat Completions (`/v1/chat/completions`). Driven entirely by the {@link ModelCapabilities.requiresResponsesApiForTools} flag in the model registry — there are no hardcoded model-name checks here. Unknown models and models without the flag return `false` (stay on Chat Completions).",
81071
+ "parameters": {
81072
+ "type": "object",
81073
+ "properties": {
81074
+ "model": {
81075
+ "type": "string",
81076
+ "description": "The model to check."
81077
+ }
81078
+ },
81079
+ "required": [
81080
+ "model"
81081
+ ]
81082
+ }
81083
+ }
81084
+ },
81085
+ {
81086
+ "type": "function",
81087
+ "function": {
81088
+ "name": "buildResponsesRequestFromChatOptions",
81089
+ "description": "Builds an OpenAI Responses API request body from the exact same arguments a caller passes to {@link makeOpenAIChatCompletionCall}. This is the request half of the transparent routing layer. The mapping deliberately mirrors {@link createCompletion}'s Chat request one-for-one so request semantics are preserved across transports: - `developerPrompt` (+ context messages + user `content`) → `input` items (chat `tool_calls`/`tool` messages become `function_call`/ `function_call_output` items). - chat `tools` → Responses `tools` (function tools only). - `temperature` → `temperature`, gated by {@link supportsTemperature} exactly as the Chat path (dropped for GPT-5.6/o-series reasoning models). - `max_completion_tokens` → `max_output_tokens`. - {@link OpenAIResponseFormat} → `text.format`. - `reasoning_effort` → `reasoning.effort`, clamped per-model via {@link clampReasoningEffort} and gated on {@link isReasoningModel} exactly as the Chat path, so the two transports clamp identically. Only set when the caller explicitly supplies it — parity is preserved for callers (like the engine decision path) that do not. Parameters the Chat path does not forward (`top_p`, `parallel_tool_calls`, `tool_choice`, `metadata`, `store`) are intentionally omitted here too, so the request semantics stay identical to today's production Chat behaviour.",
81090
+ "parameters": {
81091
+ "type": "object",
81092
+ "properties": {
81093
+ "content": {
81094
+ "type": "string",
81095
+ "description": "The user content (string or multi-modal content parts)."
81096
+ },
81097
+ "responseFormat": {
81098
+ "type": "string",
81099
+ "description": "The desired response format."
81100
+ },
81101
+ "options": {
81102
+ "type": "string",
81103
+ "description": "The Chat-Completions-style options bag."
81104
+ },
81105
+ "normalizedModel": {
81106
+ "type": "string",
81107
+ "description": "The normalized model name to target."
81108
+ }
81109
+ },
81110
+ "required": [
81111
+ "content",
81112
+ "responseFormat",
81113
+ "options",
81114
+ "normalizedModel"
81115
+ ]
81116
+ }
81117
+ }
81118
+ },
81119
+ {
81120
+ "type": "function",
81121
+ "function": {
81122
+ "name": "normalizeResponsesToCompletion",
81123
+ "description": "Normalizes an OpenAI Responses API result into the internal {@link CompletionResponse} shape. This is the response half of the transparent routing layer and is the single most correctness-critical mapping in this module: the returned `tool_calls` MUST be byte-identical to what the Chat path produces so downstream consumers (notably the engine decision path, which reads `tool_call.function.arguments` as a JSON string) parse it unchanged. Mapping: - `function_call` output items → `{ id: call_id, type:'function', function:{ name, arguments } }` with `arguments` kept as the raw JSON string (never pre-parsed) — exactly the Chat `ChatCompletionMessageToolCall` shape. `id` is sourced from `call_id` (not the item `id`) so it round-trips as the `tool_call_id` in a multi-turn conversation. - `message` output items → concatenated `output_text` parts (refusals and non-text parts contribute nothing, matching the Chat path's null-content → empty-string behaviour). - usage: `input_tokens`→prompt, `output_tokens`→completion, `input_tokens_details.cached_tokens`→cached, `output_tokens_details.reasoning_tokens`→reasoning. `system_fingerprint` is `undefined` — the Responses API does not return one (the Chat path forwards it; consumers already treat it as optional).",
81124
+ "parameters": {
81125
+ "type": "object",
81126
+ "properties": {
81127
+ "response": {
81128
+ "type": "string",
81129
+ "description": "The raw OpenAI Responses API result."
81130
+ },
81131
+ "normalizedModel": {
81132
+ "type": "string",
81133
+ "description": "The normalized model name the request targeted."
81134
+ },
81135
+ "serviceTier": {
81136
+ "type": "string",
81137
+ "description": "The caller's requested service tier, echoed through as on the Chat path."
81138
+ }
81139
+ },
81140
+ "required": [
81141
+ "response",
81142
+ "normalizedModel",
81143
+ "serviceTier"
81144
+ ]
81145
+ }
81146
+ }
81147
+ },
80469
81148
  {
80470
81149
  "type": "function",
80471
81150
  "function": {
80472
81151
  "name": "makeOpenAIChatCompletionCall",
80473
- "description": "Makes a call to the Lumic LLM interface, either to the default model or to one specified.",
81152
+ "description": "Makes a call to the Lumic LLM interface, either to the default model or to one specified. Transport is chosen transparently: models flagged {@link ModelCapabilities.requiresResponsesApiForTools} (the GPT-5.6 reasoning trio) that are called WITH function tools are routed to the OpenAI Responses API; every other call stays on Chat Completions. The returned {@link LLMResponse} envelope — `response`, `tool_calls`, and `usage` — is byte-identical across both transports, so callers require no changes.",
80474
81153
  "parameters": {
80475
81154
  "type": "object",
80476
81155
  "properties": {
@@ -81738,5 +82417,5 @@ const lumic = {
81738
82417
  }
81739
82418
  };
81740
82419
 
81741
- export { GraphQLInterfaceType as $, print as A, getNamedType as B, isInputType as C, isRequiredArgument as D, isNamedType as E, GraphQLError as F, GraphQLNonNull as G, isOutputType as H, isRequiredInputField as I, isCompositeType as J, Kind as K, getNullableType as L, getEnterLeaveForKind as M, isNode as N, OperationTypeNode as O, didYouMean as P, naturalCompare as Q, suggestionList as R, specifiedScalarTypes as S, keyMap as T, isType as U, isNullableType as V, visit as W, visitInParallel as X, keyValMap as Y, assertObjectType as Z, GraphQLScalarType as _, isListType as a, validateGoogleSheetsRange as a$, GraphQLUnionType as a0, GraphQLInputObjectType as a1, assertNullableType as a2, assertInterfaceType as a3, mapValue as a4, isSpecifiedScalarType as a5, isPrintableAsBlockString as a6, printBlockString as a7, BREAK as a8, GRAPHQL_MAX_INT as a9, printSourceLocation as aA, resolveObjMapThunk as aB, resolveReadonlyArrayThunk as aC, valueFromASTUntyped as aD, version$4 as aE, versionInfo as aF, getAugmentedNamespace as aG, isDigit$1 as aH, isNameStart as aI, dedentBlockStringLines as aJ, isNameContinue as aK, setLumicLogger as aL, getLumicLogger as aM, sanitizeForLog as aN, sanitizeError as aO, sanitizeAWSAuth as aP, sanitizeObject as aQ, getSecrets as aR, resetSecrets as aS, requireSecret as aT, withRetry as aU, CircuitBreaker as aV, CircuitBreakerState as aW, CircuitBreakerOpenError as aX, DEFAULT_CIRCUIT_BREAKER_CONFIG as aY, validateSlackChannel as aZ, validateS3Key as a_, GRAPHQL_MIN_INT as aa, GraphQLFloat as ab, GraphQLInt as ac, Location as ad, Token as ae, assertAbstractType as af, assertCompositeType as ag, assertEnumType as ah, assertEnumValueName as ai, assertInputObjectType as aj, assertInputType as ak, assertLeafType as al, assertListType as am, assertNamedType as an, assertNonNullType as ao, assertOutputType as ap, assertScalarType as aq, assertType as ar, assertUnionType as as, assertWrappingType as at, formatError as au, getLocation as av, getVisitFn as aw, isWrappingType as ax, printError as ay, printLocation as az, isAbstractType as b, PDFError as b$, LLMCostTracker as b0, getLLMCostTracker as b1, setLLMCostTracker as b2, resetLLMCostTracker as b3, setMetricsCollector as b4, getMetricsCollector as b5, resetMetricsCollector as b6, withMetrics as b7, generateCorrelationId as b8, getCorrelationId as b9, openAIChatCompletionSchema as bA, openAIImageResponseSchema as bB, validateOpenAIChatCompletion as bC, safeValidateOpenAIChatCompletion as bD, perplexityResponseSchema as bE, validatePerplexityResponse as bF, safeValidatePerplexityResponse as bG, googleSheetsValueRangeSchema as bH, validateGoogleSheetsResponse as bI, safeValidateGoogleSheetsResponse as bJ, s3ListObjectsSchema as bK, s3GetObjectSchema as bL, lambdaInvokeResponseSchema as bM, validateS3ListObjects as bN, safeValidateS3ListObjects as bO, validateLambdaResponse as bP, safeValidateLambdaResponse as bQ, createValidator as bR, createSafeValidator as bS, LumicError as bT, SlackError as bU, LLMError as bV, AWSLambdaError as bW, AWSS3Error as bX, GoogleSheetsError as bY, PerplexityError as bZ, JsonParseError as b_, getCorrelationContext as ba, withCorrelationId as bb, getCorrelationHeaders as bc, TokenBucketRateLimiter as bd, RATE_LIMIT_PROFILES as be, getRateLimiter as bf, resetAllRateLimiters as bg, withRateLimit as bh, checkIntegrationHealth as bi, SUPPORTED_MODELS as bj, isValidModel as bk, getModelCapabilities as bl, getModelProvider as bm, MODEL_ALIASES as bn, OPENAI_COMPATIBLE_PROVIDERS as bo, PROVIDER_DEFAULT_MODELS as bp, LLM_DEFAULT_PROVIDER as bq, LLM_MINI_PROVIDER as br, LLM_NORMAL_PROVIDER as bs, LLM_ADVANCED_PROVIDER as bt, LLM_PROVIDER as bu, LLM_MODEL_MINI as bv, LLM_MODEL_NORMAL as bw, LLM_MODEL_ADVANCED as bx, makeAnthropicCall as by, makeOpenAICompatibleCall as bz, isInterfaceType as c, ZipError as c0, SLACK_TIMEOUT_MS as c1, PERPLEXITY_TIMEOUT_MS as c2, GOOGLE_SHEETS_TIMEOUT_MS as c3, LLM_TIMEOUT_MS as c4, AWS_LAMBDA_TIMEOUT_MS as c5, AWS_S3_TIMEOUT_MS as c6, OPENWEATHER_TIMEOUT_MS as c7, GENERIC_FETCH_TIMEOUT_MS as c8, isObjectType as d, assertName as e, devAssert as f, isObjectLike as g, defineArguments as h, isNonNullType as i, argsToArgsConfig as j, GraphQLBoolean as k, lumic as l, GraphQLString as m, instanceOf as n, inspect as o, isInputObjectType as p, isLeafType as q, isEnumType as r, GraphQLID as s, toObjMap as t, invariant as u, GraphQLObjectType as v, GraphQLEnumType as w, GraphQLList as x, isScalarType as y, isUnionType as z };
81742
- //# sourceMappingURL=index-I-0uzUiF.js.map
82420
+ export { GraphQLInterfaceType as $, print as A, getNamedType as B, isInputType as C, isRequiredArgument as D, isNamedType as E, GraphQLError as F, GraphQLNonNull as G, isOutputType as H, isRequiredInputField as I, isCompositeType as J, Kind as K, getNullableType as L, getEnterLeaveForKind as M, isNode as N, OperationTypeNode as O, didYouMean as P, naturalCompare as Q, suggestionList as R, specifiedScalarTypes as S, keyMap as T, isType as U, isNullableType as V, visit as W, visitInParallel as X, keyValMap as Y, assertObjectType as Z, GraphQLScalarType as _, isListType as a, validateGoogleSheetsRange as a$, GraphQLUnionType as a0, GraphQLInputObjectType as a1, assertNullableType as a2, assertInterfaceType as a3, mapValue as a4, isSpecifiedScalarType as a5, isPrintableAsBlockString as a6, printBlockString as a7, BREAK as a8, GRAPHQL_MAX_INT as a9, printSourceLocation as aA, resolveObjMapThunk as aB, resolveReadonlyArrayThunk as aC, valueFromASTUntyped as aD, version$4 as aE, versionInfo as aF, getAugmentedNamespace as aG, isDigit$1 as aH, isNameStart as aI, dedentBlockStringLines as aJ, isNameContinue as aK, setLumicLogger as aL, getLumicLogger as aM, sanitizeForLog as aN, sanitizeError as aO, sanitizeAWSAuth as aP, sanitizeObject as aQ, getSecrets as aR, resetSecrets as aS, requireSecret as aT, withRetry as aU, CircuitBreaker as aV, CircuitBreakerState as aW, CircuitBreakerOpenError as aX, DEFAULT_CIRCUIT_BREAKER_CONFIG as aY, validateSlackChannel as aZ, validateS3Key as a_, GRAPHQL_MIN_INT as aa, GraphQLFloat as ab, GraphQLInt as ac, Location as ad, Token as ae, assertAbstractType as af, assertCompositeType as ag, assertEnumType as ah, assertEnumValueName as ai, assertInputObjectType as aj, assertInputType as ak, assertLeafType as al, assertListType as am, assertNamedType as an, assertNonNullType as ao, assertOutputType as ap, assertScalarType as aq, assertType as ar, assertUnionType as as, assertWrappingType as at, formatError as au, getLocation as av, getVisitFn as aw, isWrappingType as ax, printError as ay, printLocation as az, isAbstractType as b, PerplexityError as b$, LLMCostTracker as b0, getLLMCostTracker as b1, setLLMCostTracker as b2, resetLLMCostTracker as b3, setMetricsCollector as b4, getMetricsCollector as b5, resetMetricsCollector as b6, withMetrics as b7, generateCorrelationId as b8, getCorrelationId as b9, makeAnthropicCall as bA, makeOpenAICompatibleCall as bB, openAIChatCompletionSchema as bC, openAIImageResponseSchema as bD, validateOpenAIChatCompletion as bE, safeValidateOpenAIChatCompletion as bF, perplexityResponseSchema as bG, validatePerplexityResponse as bH, safeValidatePerplexityResponse as bI, googleSheetsValueRangeSchema as bJ, validateGoogleSheetsResponse as bK, safeValidateGoogleSheetsResponse as bL, s3ListObjectsSchema as bM, s3GetObjectSchema as bN, lambdaInvokeResponseSchema as bO, validateS3ListObjects as bP, safeValidateS3ListObjects as bQ, validateLambdaResponse as bR, safeValidateLambdaResponse as bS, createValidator as bT, createSafeValidator as bU, LumicError as bV, SlackError as bW, LLMError as bX, AWSLambdaError as bY, AWSS3Error as bZ, GoogleSheetsError as b_, getCorrelationContext as ba, withCorrelationId as bb, getCorrelationHeaders as bc, TokenBucketRateLimiter as bd, RATE_LIMIT_PROFILES as be, getRateLimiter as bf, resetAllRateLimiters as bg, withRateLimit as bh, checkIntegrationHealth as bi, SUPPORTED_MODELS as bj, isValidModel as bk, getModelCapabilities as bl, getModelProvider as bm, MODEL_ALIASES as bn, OPENAI_COMPATIBLE_PROVIDERS as bo, PROVIDER_DEFAULT_MODELS as bp, LLM_DEFAULT_PROVIDER as bq, LLM_MINI_PROVIDER as br, LLM_NORMAL_PROVIDER as bs, LLM_ADVANCED_PROVIDER as bt, LLM_CRITICAL_PROVIDER as bu, LLM_PROVIDER as bv, LLM_MODEL_MINI as bw, LLM_MODEL_NORMAL as bx, LLM_MODEL_ADVANCED as by, LLM_MODEL_CRITICAL as bz, isInterfaceType as c, JsonParseError as c0, PDFError as c1, ZipError as c2, SLACK_TIMEOUT_MS as c3, PERPLEXITY_TIMEOUT_MS as c4, GOOGLE_SHEETS_TIMEOUT_MS as c5, LLM_TIMEOUT_MS as c6, AWS_LAMBDA_TIMEOUT_MS as c7, AWS_S3_TIMEOUT_MS as c8, OPENWEATHER_TIMEOUT_MS as c9, GENERIC_FETCH_TIMEOUT_MS as ca, isObjectType as d, assertName as e, devAssert as f, isObjectLike as g, defineArguments as h, isNonNullType as i, argsToArgsConfig as j, GraphQLBoolean as k, lumic as l, GraphQLString as m, instanceOf as n, inspect as o, isInputObjectType as p, isLeafType as q, isEnumType as r, GraphQLID as s, toObjMap as t, invariant as u, GraphQLObjectType as v, GraphQLEnumType as w, GraphQLList as x, isScalarType as y, isUnionType as z };
82421
+ //# sourceMappingURL=index-BZ9x9UfI.js.map