@pentoshi/clai 4.2.1 → 4.3.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/README.md +14 -16
- package/dist/agent/compaction-summary.d.ts +1 -1
- package/dist/agent/compaction-summary.js +1 -1
- package/dist/agent/compaction-summary.js.map +1 -1
- package/dist/agent/runner.js +4 -4
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/tool-output-formatting.d.ts +6 -0
- package/dist/agent/tool-output-formatting.js +126 -40
- package/dist/agent/tool-output-formatting.js.map +1 -1
- package/dist/app/adapters/agent-event-adapter.d.ts +4 -0
- package/dist/app/adapters/agent-event-adapter.js +28 -3
- package/dist/app/adapters/agent-event-adapter.js.map +1 -1
- package/dist/app/commands/catalog.js +4 -21
- package/dist/app/commands/catalog.js.map +1 -1
- package/dist/app/controllers/session-controller.js +7 -0
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/app/events/app-event.d.ts +2 -0
- package/dist/app/events/app-event.js.map +1 -1
- package/dist/classic/app/use-feed.js +2 -1
- package/dist/classic/app/use-feed.js.map +1 -1
- package/dist/commands/providers.js +0 -4
- package/dist/commands/providers.js.map +1 -1
- package/dist/llm/adapters/openai-tools.d.ts +1 -0
- package/dist/llm/adapters/openai-tools.js +4 -1
- package/dist/llm/adapters/openai-tools.js.map +1 -1
- package/dist/llm/agentrouter.js +2 -0
- package/dist/llm/agentrouter.js.map +1 -1
- package/dist/llm/aws-mantle.js +2 -0
- package/dist/llm/aws-mantle.js.map +1 -1
- package/dist/llm/bynara.js +2 -0
- package/dist/llm/bynara.js.map +1 -1
- package/dist/llm/capabilities.d.ts +19 -0
- package/dist/llm/capabilities.js +165 -79
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/catalog-facts.d.ts +23 -0
- package/dist/llm/catalog-facts.js +253 -0
- package/dist/llm/catalog-facts.js.map +1 -0
- package/dist/llm/catalog-prefetch.d.ts +5 -0
- package/dist/llm/catalog-prefetch.js +38 -0
- package/dist/llm/catalog-prefetch.js.map +1 -0
- package/dist/llm/context-windows.d.ts +2 -0
- package/dist/llm/context-windows.js +20 -6
- package/dist/llm/context-windows.js.map +1 -1
- package/dist/llm/custom-profile-resolver.d.ts +5 -0
- package/dist/llm/custom-profile-resolver.js +8 -0
- package/dist/llm/custom-profile-resolver.js.map +1 -0
- package/dist/llm/custom-provider-profile.d.ts +7 -2
- package/dist/llm/custom-provider-profile.js +38 -8
- package/dist/llm/custom-provider-profile.js.map +1 -1
- package/dist/llm/custom-providers.js +2 -0
- package/dist/llm/custom-providers.js.map +1 -1
- package/dist/llm/effort-fallback.js +3 -0
- package/dist/llm/effort-fallback.js.map +1 -1
- package/dist/llm/fireworks.js +2 -0
- package/dist/llm/fireworks.js.map +1 -1
- package/dist/llm/free.js +2 -0
- package/dist/llm/free.js.map +1 -1
- package/dist/llm/hetzner.js +2 -0
- package/dist/llm/hetzner.js.map +1 -1
- package/dist/llm/http.d.ts +21 -2
- package/dist/llm/http.js +100 -141
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/learned-capabilities.d.ts +22 -0
- package/dist/llm/learned-capabilities.js +147 -0
- package/dist/llm/learned-capabilities.js.map +1 -0
- package/dist/llm/lightning.js +2 -0
- package/dist/llm/lightning.js.map +1 -1
- package/dist/llm/modal.js +2 -0
- package/dist/llm/modal.js.map +1 -1
- package/dist/llm/model-families.d.ts +20 -0
- package/dist/llm/model-families.js +227 -19
- package/dist/llm/model-families.js.map +1 -1
- package/dist/llm/nvidia.js +2 -0
- package/dist/llm/nvidia.js.map +1 -1
- package/dist/llm/openai.js +2 -0
- package/dist/llm/openai.js.map +1 -1
- package/dist/llm/openrouter.js +2 -0
- package/dist/llm/openrouter.js.map +1 -1
- package/dist/llm/orcarouter.js +2 -0
- package/dist/llm/orcarouter.js.map +1 -1
- package/dist/llm/provider-model-layers.d.ts +3 -0
- package/dist/llm/provider-model-layers.js +522 -0
- package/dist/llm/provider-model-layers.js.map +1 -0
- package/dist/llm/provider-profile-layers.d.ts +4 -1
- package/dist/llm/provider-profile-layers.js +8 -523
- package/dist/llm/provider-profile-layers.js.map +1 -1
- package/dist/llm/provider-profile.d.ts +13 -1
- package/dist/llm/provider-profile.js +18 -0
- package/dist/llm/provider-profile.js.map +1 -1
- package/dist/llm/provider-profiles.d.ts +5 -1
- package/dist/llm/provider-profiles.js +177 -4
- package/dist/llm/provider-profiles.js.map +1 -1
- package/dist/llm/provider.js +0 -15
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/qwen-cloud.js +2 -0
- package/dist/llm/qwen-cloud.js.map +1 -1
- package/dist/llm/reasoning-artifacts.d.ts +1 -0
- package/dist/llm/reasoning-artifacts.js +7 -5
- package/dist/llm/reasoning-artifacts.js.map +1 -1
- package/dist/llm/reasoning-controls.d.ts +16 -0
- package/dist/llm/reasoning-controls.js +210 -0
- package/dist/llm/reasoning-controls.js.map +1 -0
- package/dist/llm/reasoning-errors.d.ts +4 -0
- package/dist/llm/reasoning-errors.js +45 -0
- package/dist/llm/reasoning-errors.js.map +1 -0
- package/dist/llm/request-plan.d.ts +8 -6
- package/dist/llm/request-plan.js +47 -13
- package/dist/llm/request-plan.js.map +1 -1
- package/dist/llm/route-dialect-registry.d.ts +8 -0
- package/dist/llm/route-dialect-registry.js +33 -0
- package/dist/llm/route-dialect-registry.js.map +1 -0
- package/dist/llm/route-vocabulary.d.ts +2 -0
- package/dist/llm/route-vocabulary.js +23 -0
- package/dist/llm/route-vocabulary.js.map +1 -0
- package/dist/llm/router.d.ts +2 -0
- package/dist/llm/router.js +70 -20
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/tokenrouter.js +2 -0
- package/dist/llm/tokenrouter.js.map +1 -1
- package/dist/store/config.d.ts +11 -0
- package/dist/store/config.js +21 -3
- package/dist/store/config.js.map +1 -1
- package/dist/store/keys.d.ts +1 -1
- package/dist/store/keys.js +14 -3
- package/dist/store/keys.js.map +1 -1
- package/dist/store/session-workspace.d.ts +1 -0
- package/dist/store/session-workspace.js +21 -1
- package/dist/store/session-workspace.js.map +1 -1
- package/dist/tools/definitions.js +5 -10
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/output-selection.js +47 -3
- package/dist/tools/output-selection.js.map +1 -1
- package/dist/tools/web/content-encoding.d.ts +6 -0
- package/dist/tools/web/content-encoding.js +57 -0
- package/dist/tools/web/content-encoding.js.map +1 -0
- package/dist/tools/web/fetch-core.js +23 -1
- package/dist/tools/web/fetch-core.js.map +1 -1
- package/dist/tui-v2/components/picker/picker.js +1 -1
- package/dist/tui-v2/components/picker/picker.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/dist/ui-core/commands/command-handlers.js +2 -3
- package/dist/ui-core/commands/command-handlers.js.map +1 -1
- package/dist/ui-core/commands/picker-commands.js +34 -11
- package/dist/ui-core/commands/picker-commands.js.map +1 -1
- package/dist/ui-core/commands/session-commands.d.ts +2 -3
- package/dist/ui-core/commands/session-commands.js +44 -17
- package/dist/ui-core/commands/session-commands.js.map +1 -1
- package/dist/ui-core/rendering/format-help.js +1 -1
- package/dist/ui-core/rendering/format-help.js.map +1 -1
- package/dist/ui-core/state/transcript-reducer.js +78 -19
- package/dist/ui-core/state/transcript-reducer.js.map +1 -1
- package/dist/ui-core/state/transcript-types.d.ts +1 -0
- package/dist/ui-core/state/transcript-types.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/package.json +1 -2
- package/dist/llm/groq.d.ts +0 -24
- package/dist/llm/groq.js +0 -155
- package/dist/llm/groq.js.map +0 -1
- package/dist/llm/kimchi.d.ts +0 -2
- package/dist/llm/kimchi.js +0 -96
- package/dist/llm/kimchi.js.map +0 -1
package/dist/llm/http.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { modelAcceptsImages, modelSupportsThinking, isReasoningUnsupported, modelReasoningEfforts, learnModelEmitsReasoning, registerModelCatalog, } from "./capabilities.js";
|
|
2
|
+
import { catalogEffortList, catalogEntriesFromPayload, parseCatalogFacts, } from "./catalog-facts.js";
|
|
2
3
|
import { resolveSampling } from "./sampling.js";
|
|
3
4
|
import { accumulateOpenAiToolCallDelta, finalizeOpenAiToolCalls, fromWireName, parseOpenAiMessageToolCalls, } from "./tool-protocol.js";
|
|
4
5
|
import { openAiToolBodyFields, toOpenAiToolMessages, } from "./adapters/openai-tools.js";
|
|
@@ -7,6 +8,9 @@ import { generationFetch } from "./operation-usage.js";
|
|
|
7
8
|
import { isOperationPolicyError } from "./operation-ledger.js";
|
|
8
9
|
import { emitStreamReasoningArtifacts, emitStreamReasoningDelta, } from "./stream-events.js";
|
|
9
10
|
import { CHAT_COMPLETIONS_STREAM_TERMINAL, requireTerminalProof, } from "./stream-terminal.js";
|
|
11
|
+
import { modelMaxOutputTokens } from "./context-windows.js";
|
|
12
|
+
import { inBandBadRequestStatus, isMissingReasoningContentError, } from "./reasoning-errors.js";
|
|
13
|
+
import { emitReasoningControls, } from "./reasoning-controls.js";
|
|
10
14
|
import { createReasoningArtifact, createReasoningArtifactProvenance, } from "./reasoning-artifacts.js";
|
|
11
15
|
import { classifyBynaraModel, classifyNvidiaModel, } from "./model-families.js";
|
|
12
16
|
import { compileRequestPlan, } from "./request-plan.js";
|
|
@@ -68,116 +72,26 @@ function statusCodeHint(status) {
|
|
|
68
72
|
}
|
|
69
73
|
return "";
|
|
70
74
|
}
|
|
71
|
-
function modalitiesDeclareImage(value) {
|
|
72
|
-
if (Array.isArray(value)) {
|
|
73
|
-
const items = value.filter((item) => typeof item === "string");
|
|
74
|
-
if (items.length === 0)
|
|
75
|
-
return undefined;
|
|
76
|
-
return items.some((item) => /image|vision/i.test(item));
|
|
77
|
-
}
|
|
78
|
-
if (typeof value === "string") {
|
|
79
|
-
if (!/text|image|audio|video/i.test(value))
|
|
80
|
-
return undefined;
|
|
81
|
-
return /image|vision/i.test(value);
|
|
82
|
-
}
|
|
83
|
-
if (value && typeof value === "object") {
|
|
84
|
-
const nested = value;
|
|
85
|
-
if (typeof nested.vision === "boolean")
|
|
86
|
-
return nested.vision;
|
|
87
|
-
if (typeof nested.image === "boolean")
|
|
88
|
-
return nested.image;
|
|
89
|
-
if (nested.input !== undefined)
|
|
90
|
-
return modalitiesDeclareImage(nested.input);
|
|
91
|
-
}
|
|
92
|
-
return undefined;
|
|
93
|
-
}
|
|
94
75
|
export function catalogEntryVision(entry) {
|
|
95
|
-
|
|
96
|
-
return undefined;
|
|
97
|
-
const raw = entry;
|
|
98
|
-
for (const flag of [raw.vision, raw.supports_vision, raw.multimodal]) {
|
|
99
|
-
if (typeof flag === "boolean")
|
|
100
|
-
return flag;
|
|
101
|
-
}
|
|
102
|
-
for (const candidate of [
|
|
103
|
-
raw.architecture?.input_modalities,
|
|
104
|
-
raw.architecture?.modality,
|
|
105
|
-
raw.input_modalities,
|
|
106
|
-
raw.modalities,
|
|
107
|
-
raw.capabilities,
|
|
108
|
-
raw.features,
|
|
109
|
-
]) {
|
|
110
|
-
const declared = modalitiesDeclareImage(candidate);
|
|
111
|
-
if (declared !== undefined)
|
|
112
|
-
return declared;
|
|
113
|
-
}
|
|
114
|
-
return undefined;
|
|
115
|
-
}
|
|
116
|
-
const REASONING_FEATURE_RE = /^(?:reasoning|reasoning_effort|include_reasoning|thinking|reasoning_content)$/i;
|
|
117
|
-
function catalogEntryReasoning(entry) {
|
|
118
|
-
const raw = entry;
|
|
119
|
-
if (typeof raw?.reasoning === "boolean")
|
|
120
|
-
return raw.reasoning;
|
|
121
|
-
if (Array.isArray(raw?.reasoning_options) && raw.reasoning_options.length > 0) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
for (const container of [
|
|
125
|
-
raw?.supported_features,
|
|
126
|
-
raw?.supported_parameters,
|
|
127
|
-
raw?.features,
|
|
128
|
-
raw?.capabilities,
|
|
129
|
-
]) {
|
|
130
|
-
if (!Array.isArray(container))
|
|
131
|
-
continue;
|
|
132
|
-
const names = container.filter((value) => typeof value === "string");
|
|
133
|
-
if (names.length === 0)
|
|
134
|
-
continue;
|
|
135
|
-
return names.some((name) => REASONING_FEATURE_RE.test(name.trim()));
|
|
136
|
-
}
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
function catalogEntryReasoningEfforts(entry) {
|
|
140
|
-
const raw = entry;
|
|
141
|
-
const collected = [];
|
|
142
|
-
const pushValues = (values) => {
|
|
143
|
-
if (!Array.isArray(values))
|
|
144
|
-
return;
|
|
145
|
-
for (const value of values) {
|
|
146
|
-
if (typeof value === "string" && value.trim())
|
|
147
|
-
collected.push(value.trim());
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
if (Array.isArray(raw?.reasoning_options)) {
|
|
151
|
-
for (const option of raw.reasoning_options) {
|
|
152
|
-
const shaped = option;
|
|
153
|
-
if (shaped?.type !== undefined && shaped.type !== "effort")
|
|
154
|
-
continue;
|
|
155
|
-
pushValues(shaped?.values);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
pushValues(raw?.supported_reasoning_efforts);
|
|
159
|
-
if (raw?.reasoning_effort && typeof raw.reasoning_effort === "object") {
|
|
160
|
-
pushValues(raw.reasoning_effort.values);
|
|
161
|
-
}
|
|
162
|
-
return collected.length > 0 ? collected : undefined;
|
|
76
|
+
return parseCatalogFacts(entry)?.vision;
|
|
163
77
|
}
|
|
164
78
|
export function ingestModelCatalogEntries(provider, entries) {
|
|
165
79
|
const models = [];
|
|
166
80
|
const seen = new Set();
|
|
167
81
|
for (const entry of entries) {
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
82
|
+
const facts = parseCatalogFacts(entry);
|
|
83
|
+
if (!facts)
|
|
170
84
|
continue;
|
|
171
|
-
const id =
|
|
172
|
-
if (
|
|
85
|
+
const id = facts.id;
|
|
86
|
+
if (seen.has(id))
|
|
173
87
|
continue;
|
|
174
88
|
seen.add(id);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const reasoningEfforts = typeof entry === "string" ? undefined : catalogEntryReasoningEfforts(entry);
|
|
89
|
+
const reasoning = facts.reasoning?.supported;
|
|
90
|
+
const reasoningEfforts = catalogEffortList(facts.reasoning?.supportedEfforts);
|
|
178
91
|
models.push({
|
|
179
92
|
id,
|
|
180
|
-
|
|
93
|
+
facts,
|
|
94
|
+
...(facts.vision === undefined ? {} : { vision: facts.vision }),
|
|
181
95
|
...(reasoning === undefined ? {} : { reasoning }),
|
|
182
96
|
...(reasoningEfforts === undefined ? {} : { reasoningEfforts }),
|
|
183
97
|
});
|
|
@@ -187,15 +101,7 @@ export function ingestModelCatalogEntries(provider, entries) {
|
|
|
187
101
|
return models.map((model) => model.id).sort();
|
|
188
102
|
}
|
|
189
103
|
export function ingestOpenAiModelCatalog(provider, payload) {
|
|
190
|
-
|
|
191
|
-
const entries = Array.isArray(payload)
|
|
192
|
-
? payload
|
|
193
|
-
: Array.isArray(container?.data)
|
|
194
|
-
? container.data
|
|
195
|
-
: Array.isArray(container?.models)
|
|
196
|
-
? container.models
|
|
197
|
-
: [];
|
|
198
|
-
return ingestModelCatalogEntries(provider, entries);
|
|
104
|
+
return ingestModelCatalogEntries(provider, catalogEntriesFromPayload(payload));
|
|
199
105
|
}
|
|
200
106
|
export async function readJson(response, signal) {
|
|
201
107
|
const text = await readBodyCapped(response, MAX_JSON_RESPONSE_BYTES, signal);
|
|
@@ -552,6 +458,15 @@ const DEFAULT_COMPATIBLE_REASONING_ARTIFACT_POLICY = {
|
|
|
552
458
|
scope: "all-history",
|
|
553
459
|
persistence: "tool-turn",
|
|
554
460
|
};
|
|
461
|
+
const PRESERVED_FINAL_TURN_ARTIFACT_POLICY = {
|
|
462
|
+
scope: "all-history",
|
|
463
|
+
persistence: "all-turns",
|
|
464
|
+
};
|
|
465
|
+
export function compatibleArtifactPolicyFor(preservation) {
|
|
466
|
+
return preservation === "required"
|
|
467
|
+
? PRESERVED_FINAL_TURN_ARTIFACT_POLICY
|
|
468
|
+
: DEFAULT_COMPATIBLE_REASONING_ARTIFACT_POLICY;
|
|
469
|
+
}
|
|
555
470
|
function artifactRaw(value) {
|
|
556
471
|
if (typeof value === "string")
|
|
557
472
|
return value;
|
|
@@ -726,7 +641,16 @@ function pickAdvertisedEffort(effort, advertised) {
|
|
|
726
641
|
return (order.find((candidate) => advertised.includes(candidate)) ??
|
|
727
642
|
advertised[advertised.length - 1]);
|
|
728
643
|
}
|
|
729
|
-
export function buildReasoningPayload(reasoning, style, model, providerId) {
|
|
644
|
+
export function buildReasoningPayload(reasoning, style, model, providerId, control) {
|
|
645
|
+
if (control) {
|
|
646
|
+
return {
|
|
647
|
+
...emitReasoningControls({
|
|
648
|
+
profile: control.profile,
|
|
649
|
+
preference: reasoning,
|
|
650
|
+
willReplayReasoning: control.willReplayReasoning,
|
|
651
|
+
}),
|
|
652
|
+
};
|
|
653
|
+
}
|
|
730
654
|
if (style === "none")
|
|
731
655
|
return {};
|
|
732
656
|
const enabled = Boolean(reasoning?.enabled);
|
|
@@ -866,18 +790,6 @@ export function buildReasoningPayload(reasoning, style, model, providerId) {
|
|
|
866
790
|
// off, so compaction would still buy hidden reasoning tokens. vLLM's
|
|
867
791
|
// StepFun template honours this explicit per-request switch.
|
|
868
792
|
return { chat_template_kwargs: { enable_thinking: enabled } };
|
|
869
|
-
case "groq": {
|
|
870
|
-
const m = (model ?? "").toLowerCase();
|
|
871
|
-
if (/qwen\/qwen3-32b/.test(m)) {
|
|
872
|
-
return { reasoning_effort: enabled ? "default" : "none" };
|
|
873
|
-
}
|
|
874
|
-
if (/openai\/gpt-oss-(?:20b|120b)/.test(m)) {
|
|
875
|
-
return enabled
|
|
876
|
-
? { reasoning_effort: clampEffort(effort), include_reasoning: true }
|
|
877
|
-
: { reasoning_effort: "low", include_reasoning: false };
|
|
878
|
-
}
|
|
879
|
-
return {};
|
|
880
|
-
}
|
|
881
793
|
case "nvidia": {
|
|
882
794
|
const kind = classifyNvidiaModel(model ?? "");
|
|
883
795
|
switch (kind) {
|
|
@@ -964,6 +876,8 @@ export function buildReasoningPayload(reasoning, style, model, providerId) {
|
|
|
964
876
|
* payload and retries so an unsupported option never fails the whole request.
|
|
965
877
|
*/
|
|
966
878
|
export function isReasoningUnsupportedError(error) {
|
|
879
|
+
if (isMissingReasoningContentError(error))
|
|
880
|
+
return false;
|
|
967
881
|
const status = error && typeof error === "object" && "status" in error
|
|
968
882
|
? Number(error.status)
|
|
969
883
|
: undefined;
|
|
@@ -1043,30 +957,46 @@ export function isOpenAiReasoningModel(model) {
|
|
|
1043
957
|
/(?:^|\/)o[134](?:\.|-|$)/.test(m) ||
|
|
1044
958
|
/muse-spark/.test(m));
|
|
1045
959
|
}
|
|
960
|
+
const DEFAULT_REASONING_OUTPUT_FLOOR = 16_384;
|
|
961
|
+
function outputBudgetWithReasoning(requested, options) {
|
|
962
|
+
const floor = options.control?.profile.reasoning.minOutputTokens ??
|
|
963
|
+
DEFAULT_REASONING_OUTPUT_FLOOR;
|
|
964
|
+
const ceiling = modelMaxOutputTokens(options.providerId, options.model, options.outputTokenLimit);
|
|
965
|
+
const floored = Math.max(requested, floor);
|
|
966
|
+
return ceiling !== undefined ? Math.min(floored, ceiling) : floored;
|
|
967
|
+
}
|
|
1046
968
|
function emitChatCompletionsBody(options) {
|
|
1047
969
|
// Skip reasoning knobs entirely for models observed to reject them this
|
|
1048
970
|
// session (see isReasoningUnsupportedError). This is how thinking degrades
|
|
1049
971
|
// gracefully: the request still runs, just without the unsupported option.
|
|
1050
|
-
const capabilityDeniesThinking = options.
|
|
972
|
+
const capabilityDeniesThinking = options.control === undefined &&
|
|
973
|
+
options.providerId !== undefined &&
|
|
1051
974
|
Boolean(options.reasoning?.enabled) &&
|
|
1052
975
|
!modelSupportsThinking(options.providerId, options.model);
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
976
|
+
const legacyControlDenied = options.control === undefined &&
|
|
977
|
+
options.providerId !== undefined &&
|
|
978
|
+
isReasoningUnsupported(options.providerId, options.model);
|
|
979
|
+
const reasoning = legacyControlDenied ||
|
|
980
|
+
capabilityDeniesThinking ||
|
|
981
|
+
options.control?.suppressed === true
|
|
1056
982
|
? {}
|
|
1057
|
-
: buildReasoningPayload(options.reasoning, options.reasoningStyle ?? "none", options.model, options.providerId);
|
|
983
|
+
: buildReasoningPayload(options.reasoning, options.reasoningStyle ?? "none", options.model, options.providerId, options.control);
|
|
1058
984
|
const reasoningOn = Boolean(options.reasoning?.enabled);
|
|
1059
985
|
// Kimchi exposes this model as `minimax-m3`; NVIDIA uses the longer
|
|
1060
986
|
// `minimaxai/minimax-m3` ID. Both need the larger default output budget.
|
|
1061
987
|
const isMinimaxM3 = /minimax-m3/i.test(options.model);
|
|
1062
988
|
const defaultMaxTokens = isMinimaxM3 ? 8_192 : reasoningOn ? 8_192 : 4_096;
|
|
1063
989
|
// One declarative sampling policy; explicit caller value wins.
|
|
1064
|
-
const sampling =
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
990
|
+
const sampling = options.resolvedSampling ?? {
|
|
991
|
+
...resolveSampling({
|
|
992
|
+
model: options.model,
|
|
993
|
+
reasoningEnabled: reasoningOn,
|
|
994
|
+
requestedTemperature: options.temperature,
|
|
995
|
+
}),
|
|
996
|
+
};
|
|
1069
997
|
const reasoningModel = isOpenAiReasoningModel(options.model);
|
|
998
|
+
const emitTemperature = sampling.temperature !== undefined &&
|
|
999
|
+
(options.resolvedSampling !== undefined || !reasoningModel);
|
|
1070
1000
|
// Claude extended thinking via AgentRouter maps reasoning_effort to an
|
|
1071
1001
|
// Anthropic `thinking.budget_tokens`, and the gateway (Bedrock) rejects the
|
|
1072
1002
|
// request with HTTP 400 unless `max_tokens > budget_tokens`. Some upstream
|
|
@@ -1077,15 +1007,20 @@ function emitChatCompletionsBody(options) {
|
|
|
1077
1007
|
const claudeThinking = reasoningOn &&
|
|
1078
1008
|
options.reasoningStyle === "agentrouter" &&
|
|
1079
1009
|
/claude/i.test(options.model);
|
|
1080
|
-
const
|
|
1081
|
-
|
|
1082
|
-
|
|
1010
|
+
const requestedMaxTokens = options.maxTokens ?? defaultMaxTokens;
|
|
1011
|
+
const claudeFloored = claudeThinking
|
|
1012
|
+
? Math.max(requestedMaxTokens, 32_000)
|
|
1013
|
+
: requestedMaxTokens;
|
|
1014
|
+
const effectiveMaxTokens = reasoningOn
|
|
1015
|
+
? outputBudgetWithReasoning(claudeFloored, options)
|
|
1016
|
+
: claudeFloored;
|
|
1083
1017
|
const body = {
|
|
1084
1018
|
model: options.model,
|
|
1085
1019
|
messages: toOpenAiMessages(singleLeadingSystemMessages(options.messages), options.supportsVision, options.replayTarget
|
|
1086
1020
|
? {
|
|
1087
1021
|
target: options.replayTarget,
|
|
1088
1022
|
observe: options.reasoningArtifactReplayObserver,
|
|
1023
|
+
...(options.forceReasoningReplay ? { forceScope: true } : {}),
|
|
1089
1024
|
}
|
|
1090
1025
|
: undefined),
|
|
1091
1026
|
stream: options.stream,
|
|
@@ -1098,7 +1033,7 @@ function emitChatCompletionsBody(options) {
|
|
|
1098
1033
|
// gpt-5.x / o1 / o3 / o4 only accept the default temperature (1) and
|
|
1099
1034
|
// reject any explicit value — omit the field entirely rather than send
|
|
1100
1035
|
// our 0.2 default and get a 400.
|
|
1101
|
-
...(
|
|
1036
|
+
...(emitTemperature ? { temperature: sampling.temperature } : {}),
|
|
1102
1037
|
...reasoning,
|
|
1103
1038
|
...openAiToolBodyFields({
|
|
1104
1039
|
tools: options.tools,
|
|
@@ -1106,7 +1041,7 @@ function emitChatCompletionsBody(options) {
|
|
|
1106
1041
|
parallelToolCalls: options.parallelToolCalls,
|
|
1107
1042
|
}),
|
|
1108
1043
|
};
|
|
1109
|
-
if (
|
|
1044
|
+
if (emitTemperature && sampling.topP !== undefined) {
|
|
1110
1045
|
body.top_p = sampling.topP;
|
|
1111
1046
|
}
|
|
1112
1047
|
// OpenAI + many OpenAI-compatible gateways attach usage on the final SSE
|
|
@@ -1136,6 +1071,21 @@ export function chatCompletionsBodyFromPlan(plan, extras = {}) {
|
|
|
1136
1071
|
reasoning: plan.controls.reasoning,
|
|
1137
1072
|
reasoningStyle: extras.reasoningStyle,
|
|
1138
1073
|
supportsVision: plan.images.visionAccepted,
|
|
1074
|
+
control: {
|
|
1075
|
+
profile: {
|
|
1076
|
+
reasoning: plan.policy.reasoning,
|
|
1077
|
+
capabilities: { acceptedParameters: plan.policy.acceptedParameters },
|
|
1078
|
+
},
|
|
1079
|
+
willReplayReasoning: plan.replay.decisions.some((entry) => entry.decision.action === "replayed"),
|
|
1080
|
+
suppressed: plan.controls.controlSuppression !== undefined,
|
|
1081
|
+
},
|
|
1082
|
+
resolvedSampling: {
|
|
1083
|
+
temperature: plan.controls.temperature,
|
|
1084
|
+
topP: plan.controls.topP,
|
|
1085
|
+
},
|
|
1086
|
+
...(plan.policy.limits.outputTokens !== undefined
|
|
1087
|
+
? { outputTokenLimit: plan.policy.limits.outputTokens }
|
|
1088
|
+
: {}),
|
|
1139
1089
|
tools: plan.tools.definitions.length
|
|
1140
1090
|
? [...plan.tools.definitions]
|
|
1141
1091
|
: undefined,
|
|
@@ -1143,6 +1093,7 @@ export function chatCompletionsBodyFromPlan(plan, extras = {}) {
|
|
|
1143
1093
|
parallelToolCalls: plan.tools.parallelToolCalls,
|
|
1144
1094
|
replayTarget: plan.replay.target,
|
|
1145
1095
|
reasoningArtifactReplayObserver: extras.reasoningArtifactReplayObserver,
|
|
1096
|
+
...(extras.forceReasoningReplay ? { forceReasoningReplay: true } : {}),
|
|
1146
1097
|
});
|
|
1147
1098
|
}
|
|
1148
1099
|
function sentReasoningEffort(requestBody) {
|
|
@@ -1185,6 +1136,7 @@ export async function openAiCompatibleComplete(options) {
|
|
|
1185
1136
|
const requestBody = chatCompletionsBodyFromPlan(plan, {
|
|
1186
1137
|
reasoningStyle: options.reasoningStyle,
|
|
1187
1138
|
reasoningArtifactReplayObserver: options.reasoningArtifactReplayObserver,
|
|
1139
|
+
...(options.forceReasoningReplay ? { forceReasoningReplay: true } : {}),
|
|
1188
1140
|
});
|
|
1189
1141
|
let response;
|
|
1190
1142
|
try {
|
|
@@ -1244,7 +1196,8 @@ export async function openAiCompatibleComplete(options) {
|
|
|
1244
1196
|
model: options.model,
|
|
1245
1197
|
baseUrl: options.baseUrl,
|
|
1246
1198
|
toolCalls,
|
|
1247
|
-
policy: options.reasoningArtifactPolicy
|
|
1199
|
+
policy: options.reasoningArtifactPolicy ??
|
|
1200
|
+
compatibleArtifactPolicyFor(plan.policy.reasoning.finalTurnPreservation),
|
|
1248
1201
|
...(typeof reasoning === "string" && reasoning
|
|
1249
1202
|
? { reasoning: { text: reasoning, sequence: 0 } }
|
|
1250
1203
|
: {}),
|
|
@@ -1374,6 +1327,7 @@ export async function openAiCompatibleStream(options) {
|
|
|
1374
1327
|
reasoningStyle: options.reasoningStyle,
|
|
1375
1328
|
includeStreamUsage: options.includeStreamUsage,
|
|
1376
1329
|
reasoningArtifactReplayObserver: options.reasoningArtifactReplayObserver,
|
|
1330
|
+
...(options.forceReasoningReplay ? { forceReasoningReplay: true } : {}),
|
|
1377
1331
|
});
|
|
1378
1332
|
let response;
|
|
1379
1333
|
try {
|
|
@@ -1441,7 +1395,8 @@ export async function openAiCompatibleStream(options) {
|
|
|
1441
1395
|
model: options.model,
|
|
1442
1396
|
baseUrl: options.baseUrl,
|
|
1443
1397
|
toolCalls,
|
|
1444
|
-
policy: options.reasoningArtifactPolicy
|
|
1398
|
+
policy: options.reasoningArtifactPolicy ??
|
|
1399
|
+
compatibleArtifactPolicyFor(plan.policy.reasoning.finalTurnPreservation),
|
|
1445
1400
|
...(typeof reasoning === "string" && reasoning
|
|
1446
1401
|
? { reasoning: { text: reasoning, sequence: 0 } }
|
|
1447
1402
|
: {}),
|
|
@@ -1509,7 +1464,10 @@ export async function openAiCompatibleStream(options) {
|
|
|
1509
1464
|
let reasoningWireSeen = "";
|
|
1510
1465
|
let finishReason;
|
|
1511
1466
|
let terminalSignal;
|
|
1512
|
-
const terminalPolicy = options.streamTerminal ??
|
|
1467
|
+
const terminalPolicy = options.streamTerminal ??
|
|
1468
|
+
(plan.policy.terminal.proofs.length > 0
|
|
1469
|
+
? plan.policy.terminal
|
|
1470
|
+
: CHAT_COMPLETIONS_STREAM_TERMINAL);
|
|
1513
1471
|
const emittedByteCounts = () => {
|
|
1514
1472
|
let toolArgumentBytes = 0;
|
|
1515
1473
|
for (const state of toolCallState.values()) {
|
|
@@ -1545,7 +1503,8 @@ export async function openAiCompatibleStream(options) {
|
|
|
1545
1503
|
model: options.model,
|
|
1546
1504
|
baseUrl: options.baseUrl,
|
|
1547
1505
|
toolCalls,
|
|
1548
|
-
policy: options.reasoningArtifactPolicy
|
|
1506
|
+
policy: options.reasoningArtifactPolicy ??
|
|
1507
|
+
compatibleArtifactPolicyFor(plan.policy.reasoning.finalTurnPreservation),
|
|
1549
1508
|
...(reasoningSeen
|
|
1550
1509
|
? {
|
|
1551
1510
|
reasoning: {
|
|
@@ -1763,7 +1722,7 @@ export async function openAiCompatibleStream(options) {
|
|
|
1763
1722
|
const detail = typeof parsed.error === "string"
|
|
1764
1723
|
? parsed.error
|
|
1765
1724
|
: (parsed.error.message ?? parsed.error.type ?? "unknown error");
|
|
1766
|
-
throw new ProviderError(`${options.provider} stream error: ${detail}`,
|
|
1725
|
+
throw new ProviderError(`${options.provider} stream error: ${detail}`, inBandBadRequestStatus(parsed.error), payload.slice(0, 500));
|
|
1767
1726
|
}
|
|
1768
1727
|
{
|
|
1769
1728
|
const chunkUsage = options.providerId === "fireworks"
|