openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
728 lines (727 loc) • 33.4 kB
JavaScript
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { m as resolveModelExtraParamSources } from "./openai-routing-Ex_3E9TF.js";
import { c as resolveProviderExtraParamsForTransport, h as wrapProviderStreamFn, i as prepareProviderExtraParams } from "./provider-hook-runtime-DgKv_Z8O.js";
import { a as getModelProviderRequestRouteFacts, f as resolveProviderRequestPolicyConfig } from "./provider-request-config-DIOYidiO.js";
import { i as streamSimple } from "./stream-Ci9pOvq7.js";
import { t as log } from "./logger-C3TD2wdV.js";
import { T as resolveAnthropicCacheRetentionFamily, h as createOpenAIStringContentWrapper, l as createOpenAICompletionsStrictMessageKeysWrapper, n as createOpenRouterSystemCacheWrapper, o as createMinimaxThinkingDisabledWrapper, p as createOpenAIResponsesContextManagementWrapper, u as createOpenAICompletionsToolsCompatWrapper } from "./proxy-WJH2VPqG.js";
import { I as streamWithPayloadPatch, a as createDeepSeekV4OpenAICompatibleThinkingWrapper, f as createThinkingOnlyFinalTextWrapper, s as createGoogleThinkingPayloadWrapper } from "./provider-stream-shared-DakgNCd0.js";
import { i as isNativeWebSearchAllowedByToolPolicy } from "./codex-native-web-search-core-Du_GEtBK.js";
import { canonicalizeMaxTokensParam, detectOpenAICompletionsCompat, resolveMaxTokensParam, resolveOpenAICompletionsCompat } from "@openclaw/ai/transports";
//#region src/agents/embedded-agent-runner/prompt-cache-retention.ts
/**
* Resolves provider/model prompt-cache retention behavior.
*/
function parseCacheRetention(value) {
return value === "none" || value === "short" || value === "long" ? value : void 0;
}
function isGooglePromptCacheEligible(params) {
if (params.modelApi !== "google-generative-ai") return false;
const normalizedModelId = normalizeLowercaseStringOrEmpty(params.modelId);
return normalizedModelId.startsWith("gemini-2.5") || normalizedModelId.startsWith("gemini-3");
}
function resolveCacheRetention(extraParams, provider, modelApi, modelId, compat) {
const hasExplicitCacheConfig = extraParams?.cacheRetention !== void 0 || extraParams?.cacheControlTtl !== void 0;
const family = resolveAnthropicCacheRetentionFamily({
provider,
modelApi,
modelId,
hasExplicitCacheConfig
});
const googleEligible = isGooglePromptCacheEligible({
modelApi,
modelId
});
const compatEligible = compat?.supportsPromptCacheKey === true || compat?.cacheControlFormat === "anthropic";
if (!family && !googleEligible && !compatEligible) return;
const newVal = parseCacheRetention(extraParams?.cacheRetention);
if (newVal) return newVal;
const legacy = extraParams?.cacheControlTtl;
if (legacy === "5m" && (family || googleEligible)) return "short";
if (legacy === "1h" && (family || googleEligible)) return "long";
return family === "anthropic-direct" ? "short" : void 0;
}
//#endregion
//#region src/llm/providers/stream-wrappers/moonshot.ts
/** Detects SiliconFlow Pro models that require thinking=null instead of thinking="off". */
function shouldApplySiliconFlowThinkingOffCompat(params) {
return params.provider === "siliconflow" && params.thinkingLevel === "off" && params.modelId.startsWith("Pro/");
}
/** Wraps Moonshot-compatible requests to rewrite SiliconFlow thinking-off payloads. */
function createSiliconFlowThinkingWrapper(baseStreamFn) {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, options) => streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
if (payloadObj.thinking === "off") payloadObj.thinking = null;
});
}
//#endregion
//#region src/agents/embedded-agent-runner/extra-params.ts
function requireBaseStreamFn(streamFn) {
if (!streamFn) throw new Error("Cannot apply stream policy without a lifecycle-owned base stream.");
return streamFn;
}
const defaultProviderRuntimeDeps = {
prepareProviderExtraParams,
resolveProviderExtraParamsForTransport,
wrapProviderStreamFn
};
const providerRuntimeDeps = { ...defaultProviderRuntimeDeps };
let preparedExtraParamsCache = /* @__PURE__ */ new WeakMap();
const REQUEST_SCOPED_EXTRA_PARAM_KEYS = /* @__PURE__ */ new Set([
"response_format",
"responseFormat",
"stop"
]);
const GPT_PARALLEL_TOOL_CALLS_APIS = /* @__PURE__ */ new Set([
"openai-completions",
"openai-responses",
"openai-chatgpt-responses",
"azure-openai-responses"
]);
/** True when a provider API accepts GPT parallel-tool-call payload settings. */
function supportsGptParallelToolCallsPayload(api) {
return typeof api === "string" && GPT_PARALLEL_TOOL_CALLS_APIS.has(api);
}
const testing = {
setProviderRuntimeDepsForTest(deps) {
providerRuntimeDeps.prepareProviderExtraParams = deps?.prepareProviderExtraParams ?? defaultProviderRuntimeDeps.prepareProviderExtraParams;
providerRuntimeDeps.resolveProviderExtraParamsForTransport = deps?.resolveProviderExtraParamsForTransport ?? defaultProviderRuntimeDeps.resolveProviderExtraParamsForTransport;
providerRuntimeDeps.wrapProviderStreamFn = deps?.wrapProviderStreamFn ?? defaultProviderRuntimeDeps.wrapProviderStreamFn;
},
resetProviderRuntimeDepsForTest() {
clearPreparedExtraParamsCache();
providerRuntimeDeps.prepareProviderExtraParams = defaultProviderRuntimeDeps.prepareProviderExtraParams;
providerRuntimeDeps.resolveProviderExtraParamsForTransport = defaultProviderRuntimeDeps.resolveProviderExtraParamsForTransport;
providerRuntimeDeps.wrapProviderStreamFn = defaultProviderRuntimeDeps.wrapProviderStreamFn;
}
};
if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.extraParamsTestApi")] = testing;
/**
* Resolve provider-specific extra params from model config.
* Used to pass through stream params like temperature/maxTokens.
*
* @internal Exported for testing only
*/
function resolveExtraParams(params) {
const { defaultParams, modelParams, agentParams } = resolveModelExtraParamSources({
config: params.cfg,
provider: params.provider,
modelId: params.modelId,
agentId: params.agentId
});
const globalParams = modelParams ? { ...modelParams } : void 0;
const merged = Object.assign({}, defaultParams, globalParams, agentParams);
const resolvedParallelToolCalls = resolveAliasedParamValue([
defaultParams,
globalParams,
agentParams
], "parallel_tool_calls", "parallelToolCalls");
if (resolvedParallelToolCalls !== void 0) {
merged.parallel_tool_calls = resolvedParallelToolCalls;
delete merged.parallelToolCalls;
}
const resolvedTextVerbosity = resolveAliasedParamValue([globalParams, agentParams], "text_verbosity", "textVerbosity");
if (resolvedTextVerbosity !== void 0) {
merged.text_verbosity = resolvedTextVerbosity;
delete merged.textVerbosity;
}
const resolvedResponseFormat = resolveAliasedParamValue([
defaultParams,
globalParams,
agentParams
], "response_format", "responseFormat");
if (resolvedResponseFormat !== void 0) {
merged.response_format = resolvedResponseFormat;
delete merged.responseFormat;
}
canonicalizeMaxTokensParam({
merged,
sources: [
defaultParams,
globalParams,
agentParams
]
});
const resolvedCachedContent = resolveAliasedParamValue([
defaultParams,
globalParams,
agentParams
], "cached_content", "cachedContent");
if (resolvedCachedContent !== void 0) {
merged.cachedContent = resolvedCachedContent;
delete merged.cached_content;
}
if (params.provider === "openrouter") canonicalizeOpenRouterResponseCacheParams(merged, [
defaultParams,
globalParams,
agentParams
]);
applyDefaultOpenAIGptRuntimeParams(params, merged);
return Object.keys(merged).length > 0 ? merged : void 0;
}
function resolveSupportedTransport(value) {
return value === "sse" || value === "websocket" || value === "websocket-cached" || value === "auto" ? value : void 0;
}
function hasExplicitTransportSetting(settings) {
return Object.hasOwn(settings, "transport");
}
function clearPreparedExtraParamsCache() {
preparedExtraParamsCache = /* @__PURE__ */ new WeakMap();
}
function fingerprintPreparedExtraParamsModel(model) {
if (!model) return null;
return {
api: model.api,
provider: model.provider,
id: model.id,
name: model.name,
baseUrl: model.baseUrl,
reasoning: model.reasoning,
input: model.input,
cost: model.cost,
compat: Reflect.get(model, "compat") ?? null,
contextWindow: model.contextWindow,
contextTokens: model.contextTokens ?? null,
headers: Reflect.get(model, "headers") ?? null,
maxTokens: model.maxTokens,
maxTokensSource: model.maxTokensSource ?? null,
params: model.params ?? null,
requestTimeoutMs: model.requestTimeoutMs ?? null
};
}
function resolvePreparedExtraParamsCacheKey(params) {
return JSON.stringify({
provider: params.provider,
modelId: params.modelId,
agentId: params.agentId ?? "",
agentDir: params.agentDir ?? "",
workspaceDir: params.workspaceDir ?? "",
thinkingLevel: params.thinkingLevel ?? "",
resolvedTransport: params.resolvedTransport ?? "",
extraParamsOverride: stripRequestScopedExtraParams(sanitizeExtraParamsRecord(params.extraParamsOverride)) ?? null,
resolvedExtraParams: params.resolvedExtraParams ?? null,
model: fingerprintPreparedExtraParamsModel(params.model)
});
}
function resolvePreparedExtraParams(params) {
const resolvedExtraParams = params.resolvedExtraParams ?? resolveExtraParams({
cfg: params.cfg,
provider: params.provider,
modelId: params.modelId,
agentId: params.agentId
});
const override = params.extraParamsOverride && Object.keys(params.extraParamsOverride).length > 0 ? stripRequestScopedExtraParams(sanitizeExtraParamsRecord(Object.fromEntries(Object.entries(params.extraParamsOverride).filter(([, value]) => value !== void 0)))) : void 0;
const merged = {
...sanitizeExtraParamsRecord(resolvedExtraParams),
...override
};
canonicalizeMaxTokensParam({
merged,
sources: [resolvedExtraParams, override]
});
const resolvedCachedContent = resolveAliasedParamValue([resolvedExtraParams, override], "cached_content", "cachedContent");
if (resolvedCachedContent !== void 0) {
merged.cachedContent = resolvedCachedContent;
delete merged.cached_content;
}
if (params.provider === "openrouter") canonicalizeOpenRouterResponseCacheParams(merged, [resolvedExtraParams, override]);
const cfg = params.cfg;
const cacheKey = cfg && !hasFunctionExtraParamValue(params.extraParamsOverride) ? resolvePreparedExtraParamsCacheKey(params) : void 0;
if (cacheKey) {
const cached = preparedExtraParamsCache.get(cfg)?.get(cacheKey);
if (cached) return cached;
}
const prepared = providerRuntimeDeps.prepareProviderExtraParams({
provider: params.provider,
config: params.cfg,
workspaceDir: params.workspaceDir,
runtimeHandle: params.providerRuntimeHandle,
context: {
config: params.cfg,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
provider: params.provider,
modelId: params.modelId,
model: params.model,
extraParams: merged,
thinkingLevel: params.thinkingLevel
}
}) ?? merged;
const transportPatch = providerRuntimeDeps.resolveProviderExtraParamsForTransport({
provider: params.provider,
config: params.cfg,
workspaceDir: params.workspaceDir,
runtimeHandle: params.providerRuntimeHandle,
context: {
config: params.cfg,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
provider: params.provider,
modelId: params.modelId,
extraParams: prepared,
thinkingLevel: params.thinkingLevel,
model: params.model,
transport: params.resolvedTransport ?? resolveSupportedTransport(prepared.transport)
}
})?.patch;
const result = transportPatch ? {
...prepared,
...transportPatch
} : prepared;
canonicalizeMaxTokensParam({
merged: result,
sources: [prepared, transportPatch ?? void 0]
});
if (cacheKey) {
let bucket = preparedExtraParamsCache.get(cfg);
if (!bucket) {
bucket = /* @__PURE__ */ new Map();
preparedExtraParamsCache.set(cfg, bucket);
}
bucket.set(cacheKey, result);
}
return result;
}
function sanitizeExtraParamsRecord(value) {
if (!value) return;
return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "__proto__" && key !== "prototype" && key !== "constructor"));
}
function stripRequestScopedExtraParams(value) {
if (!value) return;
const filtered = Object.fromEntries(Object.entries(value).filter(([key]) => !REQUEST_SCOPED_EXTRA_PARAM_KEYS.has(key)));
return Object.keys(filtered).length > 0 ? filtered : void 0;
}
function hasRequestScopedExtraParams(value) {
if (!value) return false;
return [...REQUEST_SCOPED_EXTRA_PARAM_KEYS].some((key) => Object.hasOwn(value, key));
}
function hasFunctionExtraParamValue(value) {
return Boolean(value && Object.values(value).some((item) => typeof item === "function"));
}
function shouldApplyDefaultOpenAIGptRuntimeParams(params) {
if (params.provider !== "openai") return false;
return /^gpt-5(?:[.-]|$)/i.test(params.modelId);
}
function applyDefaultOpenAIGptRuntimeParams(params, merged) {
if (!shouldApplyDefaultOpenAIGptRuntimeParams(params)) return;
if (!Object.hasOwn(merged, "parallel_tool_calls") && !Object.hasOwn(merged, "parallelToolCalls")) merged.parallel_tool_calls = true;
if (!Object.hasOwn(merged, "text_verbosity") && !Object.hasOwn(merged, "textVerbosity")) merged.text_verbosity = "low";
}
function resolveAgentTransportOverride(params) {
const globalSettings = params.settingsManager.getGlobalSettings();
const projectSettings = params.settingsManager.getProjectSettings();
if (hasExplicitTransportSetting(globalSettings) || hasExplicitTransportSetting(projectSettings)) return;
return resolveSupportedTransport(params.effectiveExtraParams?.transport);
}
function resolveExplicitSettingsTransport(params) {
const globalSettings = params.settingsManager.getGlobalSettings();
const projectSettings = params.settingsManager.getProjectSettings();
if (!hasExplicitTransportSetting(globalSettings) && !hasExplicitTransportSetting(projectSettings)) return;
return resolveSupportedTransport(params.sessionTransport);
}
function normalizeStopSequences(value) {
const list = typeof value === "string" ? [value] : Array.isArray(value) ? value : void 0;
if (!list) return;
const sequences = list.filter((item) => typeof item === "string" && item.length > 0);
return sequences.length > 0 ? sequences : void 0;
}
function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, model) {
if (!extraParams || Object.keys(extraParams).length === 0) return;
if (Object.hasOwn(extraParams, "cacheRetention") && parseCacheRetention(extraParams.cacheRetention) === void 0) log.warn("ignoring invalid cacheRetention param; expected \"none\", \"short\", or \"long\"");
const streamParams = {};
if (typeof extraParams.temperature === "number") streamParams.temperature = extraParams.temperature;
if (typeof extraParams.topP === "number") streamParams.topP = extraParams.topP;
const maxTokens = resolveMaxTokensParam(extraParams);
if (maxTokens !== void 0) streamParams.maxTokens = maxTokens;
const resolvedResponseFormat = resolveAliasedParamValue([extraParams], "response_format", "responseFormat");
if (resolvedResponseFormat && typeof resolvedResponseFormat === "object" && !Array.isArray(resolvedResponseFormat)) streamParams.responseFormat = resolvedResponseFormat;
const transport = resolveSupportedTransport(extraParams.transport);
if (transport) streamParams.transport = transport;
else if (extraParams.transport != null) {
const transportSummary = typeof extraParams.transport === "string" ? extraParams.transport : typeof extraParams.transport;
log.warn(`ignoring invalid transport param: ${transportSummary}`);
}
const cachedContent = typeof extraParams.cachedContent === "string" ? extraParams.cachedContent : typeof extraParams.cached_content === "string" ? extraParams.cached_content : void 0;
if (typeof cachedContent === "string" && cachedContent.trim()) streamParams.cachedContent = cachedContent.trim();
const resolvedFrequencyPenalty = resolveAliasedParamValueFromKeys([extraParams], ["frequencyPenalty", "frequency_penalty"]);
const resolvedPresencePenalty = resolveAliasedParamValueFromKeys([extraParams], ["presencePenalty", "presence_penalty"]);
const resolvedSeed = extraParams.seed;
if (typeof resolvedFrequencyPenalty === "number") streamParams.frequencyPenalty = resolvedFrequencyPenalty;
if (typeof resolvedPresencePenalty === "number") streamParams.presencePenalty = resolvedPresencePenalty;
if (typeof resolvedSeed === "number") streamParams.seed = resolvedSeed;
const resolvedStop = normalizeStopSequences(extraParams.stop);
if (resolvedStop) streamParams.stop = resolvedStop;
const readCacheCompat = (m) => m?.api === "openai-completions" ? resolveOpenAICompletionsCompat(m) : m?.compat;
const initialCacheRetention = resolveCacheRetention(extraParams, provider, typeof model?.api === "string" ? model.api : void 0, typeof model?.id === "string" ? model.id : void 0, readCacheCompat(model));
if (Object.keys(streamParams).length > 0 || initialCacheRetention) {
const debugParams = initialCacheRetention ? {
...streamParams,
cacheRetention: initialCacheRetention
} : streamParams;
log.debug(`creating streamFn wrapper with params: ${JSON.stringify(debugParams)}`);
}
const underlying = requireBaseStreamFn(baseStreamFn);
const wrappedStreamFn = (callModel, context, options) => {
const cacheRetention = resolveCacheRetention(extraParams, provider, typeof callModel.api === "string" ? callModel.api : void 0, typeof callModel.id === "string" ? callModel.id : void 0, readCacheCompat(callModel));
if (Object.keys(streamParams).length === 0 && !cacheRetention) return underlying(callModel, context, options);
const effectiveCacheRetention = options?.cacheRetention ?? cacheRetention;
return underlying(callModel, context, {
...streamParams,
...options,
...effectiveCacheRetention ? { cacheRetention: effectiveCacheRetention } : {}
});
};
return wrappedStreamFn;
}
function resolveAliasedParamValue(sources, snakeCaseKey, camelCaseKey) {
return resolveAliasedParamValueFromKeys(sources, [snakeCaseKey, camelCaseKey]);
}
function resolveAliasedParamValueFromKeys(sources, keys) {
let resolved = void 0;
let seen = false;
for (const source of sources) {
if (!source) continue;
for (const key of keys) {
if (!Object.hasOwn(source, key)) continue;
resolved = source[key];
seen = true;
break;
}
}
return seen ? resolved : void 0;
}
function applyCanonicalAliasedParamValue(params) {
const resolved = resolveAliasedParamValueFromKeys(params.sources, params.keys);
if (resolved === void 0) return;
for (const key of params.keys) delete params.merged[key];
params.merged[params.canonicalKey] = resolved;
}
function canonicalizeOpenRouterResponseCacheParams(merged, sources) {
applyCanonicalAliasedParamValue({
merged,
sources,
keys: ["responseCache", "response_cache"],
canonicalKey: "responseCache"
});
applyCanonicalAliasedParamValue({
merged,
sources,
keys: [
"responseCacheTtlSeconds",
"response_cache_ttl_seconds",
"responseCacheTtl",
"response_cache_ttl"
],
canonicalKey: "responseCacheTtlSeconds"
});
applyCanonicalAliasedParamValue({
merged,
sources,
keys: ["responseCacheClear", "response_cache_clear"],
canonicalKey: "responseCacheClear"
});
}
function createParallelToolCallsWrapper(baseStreamFn, enabled) {
const underlying = requireBaseStreamFn(baseStreamFn);
return (model, context, options) => {
if (!supportsGptParallelToolCallsPayload(model.api)) return underlying(model, context, options);
log.debug(`applying parallel_tool_calls=${enabled} for ${model.provider ?? "unknown"}/${model.id ?? "unknown"} api=${model.api}`);
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
payloadObj.parallel_tool_calls = enabled;
});
};
}
function shouldStripOpenAICompletionsStore(model) {
if (model.api !== "openai-completions") return false;
const compat = model.compat && typeof model.compat === "object" ? model.compat : void 0;
return !(getModelProviderRequestRouteFacts(model)?.capabilities ?? resolveProviderRequestPolicyConfig({
provider: typeof model.provider === "string" ? model.provider : void 0,
api: model.api,
baseUrl: typeof model.baseUrl === "string" ? model.baseUrl : void 0,
compat,
capability: "llm",
transport: "stream"
}).capabilities).usesKnownNativeOpenAIRoute;
}
function createOpenAICompletionsStoreCompatWrapper(baseStreamFn) {
const underlying = requireBaseStreamFn(baseStreamFn);
return (model, context, options) => {
if (!shouldStripOpenAICompletionsStore(model)) return underlying(model, context, options);
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
delete payloadObj.store;
});
};
}
function sanitizeExtraBodyRecord(value) {
return Object.fromEntries(Object.entries(sanitizeExtraParamsRecord(value) ?? {}).filter(([, entry]) => entry !== void 0));
}
function resolveExtraBodyParam(rawExtraBody) {
if (rawExtraBody === void 0 || rawExtraBody === null) return;
if (typeof rawExtraBody !== "object" || Array.isArray(rawExtraBody)) {
const summary = typeof rawExtraBody === "string" ? rawExtraBody : typeof rawExtraBody;
log.warn(`ignoring invalid extra_body param: ${summary}`);
return;
}
const extraBody = sanitizeExtraBodyRecord(rawExtraBody);
return Object.keys(extraBody).length > 0 ? extraBody : void 0;
}
function resolveChatTemplateKwargsParam(rawChatTemplateKwargs) {
if (rawChatTemplateKwargs === void 0 || rawChatTemplateKwargs === null) return;
if (typeof rawChatTemplateKwargs !== "object" || Array.isArray(rawChatTemplateKwargs)) {
const summary = typeof rawChatTemplateKwargs === "string" ? rawChatTemplateKwargs : typeof rawChatTemplateKwargs;
log.warn(`ignoring invalid chat_template_kwargs param: ${summary}`);
return;
}
const chatTemplateKwargs = sanitizeExtraBodyRecord(rawChatTemplateKwargs);
return Object.keys(chatTemplateKwargs).length > 0 ? chatTemplateKwargs : void 0;
}
function createOpenAICompletionsChatTemplateKwargsWrapper(params) {
const underlying = requireBaseStreamFn(params.baseStreamFn);
return (model, context, options) => {
if (model.api !== "openai-completions") return underlying(model, context, options);
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
const existing = payloadObj.chat_template_kwargs;
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
payloadObj.chat_template_kwargs = {
...existing,
...params.configured
};
return;
}
payloadObj.chat_template_kwargs = params.configured;
});
};
}
function createOpenAICompletionsExtraBodyWrapper(baseStreamFn, extraBody) {
const underlying = requireBaseStreamFn(baseStreamFn);
return (model, context, options) => {
if (model.api !== "openai-completions") return underlying(model, context, options);
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
const collisions = Object.keys(extraBody).filter((key) => Object.hasOwn(payloadObj, key));
if (collisions.length > 0) log.warn(`extra_body overwriting request payload keys: ${collisions.join(", ")}`);
Object.assign(payloadObj, extraBody);
});
};
}
function applyPrePluginStreamWrappers(ctx) {
const baseExtraParams = ctx.override && hasRequestScopedExtraParams(ctx.override) ? stripRequestScopedExtraParams(ctx.effectiveExtraParams) : ctx.effectiveExtraParams;
const streamParams = ctx.override ? {
...baseExtraParams,
...ctx.override
} : baseExtraParams;
const wrappedStreamFn = createStreamFnWithExtraParams(ctx.agent.streamFn, streamParams, ctx.provider, ctx.model);
if (wrappedStreamFn) {
log.debug(`applying extraParams to agent streamFn for ${ctx.provider}/${ctx.modelId}`);
ctx.agent.streamFn = wrappedStreamFn;
}
if (shouldApplySiliconFlowThinkingOffCompat({
provider: ctx.provider,
modelId: ctx.modelId,
thinkingLevel: ctx.thinkingLevel
})) {
log.debug(`normalizing thinking=off to thinking=null for SiliconFlow compatibility (${ctx.provider}/${ctx.modelId})`);
ctx.agent.streamFn = createSiliconFlowThinkingWrapper(ctx.agent.streamFn);
}
}
function applyPostPluginStreamWrappers(ctx) {
const streamParams = ctx.override ? {
...ctx.effectiveExtraParams,
...ctx.override
} : ctx.effectiveExtraParams;
ctx.agent.streamFn = createOpenRouterSystemCacheWrapper(ctx.agent.streamFn, streamParams);
ctx.agent.streamFn = createOpenAIStringContentWrapper(ctx.agent.streamFn);
ctx.agent.streamFn = createOpenAICompletionsStrictMessageKeysWrapper(ctx.agent.streamFn);
ctx.agent.streamFn = createOpenAICompletionsToolsCompatWrapper(ctx.agent.streamFn);
if (!ctx.providerWrapperHandled) {
ctx.agent.streamFn = createDeepSeekV4OpenAICompatibleThinkingWrapper({
baseStreamFn: ctx.agent.streamFn,
thinkingLevel: ctx.thinkingLevel,
shouldPatchModel: (model) => isDeepSeekV4OpenAICompatibleModel(model) && deepSeekV4NativeThinkingAllowedByCompat(model)
});
ctx.agent.streamFn = createDeepSeekV4NonNativeCompatSanitizerWrapper(ctx.agent.streamFn);
ctx.agent.streamFn = createDeepSeekV4OpenAICompatibleThinkingWrapper({
baseStreamFn: ctx.agent.streamFn,
thinkingLevel: ctx.thinkingLevel,
shouldPatchModel: isMiMoReasoningOpenAICompatibleModel
});
ctx.agent.streamFn = createThinkingOnlyFinalTextWrapper({
baseStreamFn: ctx.agent.streamFn,
shouldPatchModel: isMiMoReasoningAsVisibleTextOpenAICompatibleModel
});
ctx.agent.streamFn = createGoogleThinkingPayloadWrapper(ctx.agent.streamFn, ctx.thinkingLevel);
ctx.agent.streamFn = createOpenAIResponsesContextManagementWrapper(ctx.agent.streamFn, ctx.effectiveExtraParams);
}
ctx.agent.streamFn = createMinimaxThinkingDisabledWrapper(ctx.agent.streamFn, ctx.thinkingLevel);
const configuredChatTemplateKwargs = resolveChatTemplateKwargsParam(resolveAliasedParamValue([ctx.effectiveExtraParams, ctx.override], "chat_template_kwargs", "chatTemplateKwargs"));
if (configuredChatTemplateKwargs) ctx.agent.streamFn = createOpenAICompletionsChatTemplateKwargsWrapper({
baseStreamFn: ctx.agent.streamFn,
configured: configuredChatTemplateKwargs
});
const extraBody = resolveExtraBodyParam(resolveAliasedParamValue([ctx.effectiveExtraParams, ctx.override], "extra_body", "extraBody"));
if (extraBody) ctx.agent.streamFn = createOpenAICompletionsExtraBodyWrapper(ctx.agent.streamFn, extraBody);
ctx.agent.streamFn = createOpenAICompletionsStoreCompatWrapper(ctx.agent.streamFn);
const rawParallelToolCalls = resolveAliasedParamValue([ctx.effectiveExtraParams, ctx.override], "parallel_tool_calls", "parallelToolCalls");
if (rawParallelToolCalls === void 0) return;
if (typeof rawParallelToolCalls === "boolean") {
ctx.agent.streamFn = createParallelToolCallsWrapper(ctx.agent.streamFn, rawParallelToolCalls);
return;
}
if (rawParallelToolCalls === null) {
log.debug("parallel_tool_calls suppressed by null override, skipping injection");
return;
}
const summary = typeof rawParallelToolCalls === "string" ? rawParallelToolCalls : typeof rawParallelToolCalls;
log.warn(`ignoring invalid parallel_tool_calls param: ${summary}`);
}
function normalizeDeepSeekV4CandidateId(modelId) {
if (typeof modelId !== "string") return;
const normalized = modelId.trim().toLowerCase();
const suffixIndex = normalized.indexOf(":");
return (suffixIndex === -1 ? normalized : normalized.slice(0, suffixIndex)).split("/").pop();
}
function isDeepSeekV4OpenAICompatibleModel(model) {
return isDeepSeekV4OpenAICompletionsModel(model) && !isMicrosoftFoundryProviderId(model.provider);
}
function isDeepSeekV4OpenAICompletionsModel(model) {
const normalizedModelId = normalizeDeepSeekV4CandidateId(model.id);
return model.api === "openai-completions" && (normalizedModelId === "deepseek-v4-flash" || normalizedModelId === "deepseek-v4-pro");
}
function isMicrosoftFoundryProviderId(provider) {
if (typeof provider !== "string") return false;
const normalizedProvider = provider.trim().toLowerCase();
return normalizedProvider === "microsoft-foundry" || normalizedProvider.startsWith("microsoft-foundry-");
}
/**
* The DeepSeek V4 wrapper emits the deepseek-native `thinking: { type }` wire
* format (plus `reasoning_effort`). Honor an explicit `compat.thinkingFormat`
* override that selects a different reasoning format: some OpenAI-compatible
* deployments — notably Azure AI Foundry DeepSeek V4 — reject the `thinking`
* parameter outright, even `thinking: { type: "disabled" }`. When no override
* exists, honor provider-level detection for non-native formats such as
* OpenRouter while keeping id-based fallback for unknown DeepSeek-compatible
* proxy routes.
*/
function deepSeekV4NativeThinkingAllowedByCompat(model) {
const thinkingFormat = resolveDeepSeekV4ThinkingFormatOverride(model);
return thinkingFormat === void 0 || thinkingFormat === "deepseek";
}
function resolveDeepSeekV4ThinkingFormatOverride(model) {
const compat = model.compat;
const configured = compat && typeof compat === "object" ? compat.thinkingFormat : void 0;
if (typeof configured === "string") return configured;
const detected = detectOpenAICompletionsCompat(model).defaults.thinkingFormat;
return detected === "openrouter" || detected === "together" || detected === "zai" ? detected : void 0;
}
function createDeepSeekV4NonNativeCompatSanitizerWrapper(baseStreamFn) {
if (!baseStreamFn) return;
return (model, context, options) => {
if (!shouldSanitizeDeepSeekV4NonNativeFields(model)) return baseStreamFn(model, context, options);
return streamWithPayloadPatch(baseStreamFn, model, context, options, (payload) => {
delete payload.thinking;
stripDeepSeekV4ReasoningContent(payload);
});
};
}
function shouldSanitizeDeepSeekV4NonNativeFields(model) {
return isDeepSeekV4OpenAICompletionsModel(model) && (isMicrosoftFoundryProviderId(model.provider) || !deepSeekV4NativeThinkingAllowedByCompat(model));
}
function stripDeepSeekV4ReasoningContent(payload) {
if (!Array.isArray(payload.messages)) return;
for (const message of payload.messages) {
if (!message || typeof message !== "object") continue;
delete message.reasoning_content;
}
}
const MIMO_REASONING_OPENAI_COMPATIBLE_MODEL_IDS = /* @__PURE__ */ new Set([
"mimo-v2-pro",
"mimo-v2-omni",
"mimo-v2.5",
"mimo-v2.5-pro",
"mimo-v2.6-pro"
]);
const MIMO_REASONING_AS_VISIBLE_TEXT_MODEL_IDS = /* @__PURE__ */ new Set(["mimo-v2-pro", "mimo-v2-omni"]);
function isMiMoReasoningOpenAICompatibleModel(model) {
const normalizedModelId = normalizeDeepSeekV4CandidateId(model.id);
return model.api === "openai-completions" && normalizedModelId !== void 0 && MIMO_REASONING_OPENAI_COMPATIBLE_MODEL_IDS.has(normalizedModelId);
}
function isMiMoReasoningAsVisibleTextOpenAICompatibleModel(model) {
const normalizedModelId = normalizeDeepSeekV4CandidateId(model.id);
return model.api === "openai-completions" && normalizedModelId !== void 0 && MIMO_REASONING_AS_VISIBLE_TEXT_MODEL_IDS.has(normalizedModelId);
}
/**
* Apply extra params (like temperature) to an agent's streamFn.
* Also applies verified provider-specific request wrappers, such as OpenRouter attribution.
*
* @internal Exported for testing
*/
function applyExtraParamsToAgent(agent, cfg, provider, modelId, extraParamsOverride, thinkingLevel, agentId, workspaceDir, model, agentDir, resolvedTransport, options) {
const resolvedExtraParams = resolveExtraParams({
cfg,
provider,
modelId,
agentId
});
const override = extraParamsOverride && Object.keys(extraParamsOverride).length > 0 ? sanitizeExtraParamsRecord(Object.fromEntries(Object.entries(extraParamsOverride).filter(([, value]) => value !== void 0))) : void 0;
const effectiveExtraParams = options?.preparedExtraParams ?? resolvePreparedExtraParams({
cfg,
provider,
modelId,
extraParamsOverride,
thinkingLevel,
agentId,
agentDir,
workspaceDir,
resolvedExtraParams,
model,
resolvedTransport
});
const wrapperContext = {
agent,
cfg,
provider,
modelId,
agentDir,
workspaceDir,
thinkingLevel,
model,
effectiveExtraParams,
resolvedExtraParams,
override
};
const providerStreamBase = agent.streamFn;
const nativeWebSearchAllowedByToolPolicy = options?.nativeWebSearchPolicyContext ? isNativeWebSearchAllowedByToolPolicy({
config: cfg,
modelProvider: model?.provider,
modelId: model?.id,
agentId,
...options.nativeWebSearchPolicyContext
}) : void 0;
const pluginWrappedStreamFn = providerRuntimeDeps.wrapProviderStreamFn({
provider,
config: cfg,
workspaceDir,
context: {
config: cfg,
agentDir,
workspaceDir,
agentId,
nativeWebSearchAllowedByToolPolicy,
provider,
modelId,
extraParams: effectiveExtraParams,
thinkingLevel,
model,
streamFn: providerStreamBase
}
});
agent.streamFn = pluginWrappedStreamFn ?? providerStreamBase;
applyPrePluginStreamWrappers(wrapperContext);
const providerWrapperHandled = pluginWrappedStreamFn !== void 0 && pluginWrappedStreamFn !== providerStreamBase;
applyPostPluginStreamWrappers({
...wrapperContext,
providerWrapperHandled
});
return { effectiveExtraParams };
}
//#endregion
export { resolvePreparedExtraParams as a, resolveExtraParams as i, resolveAgentTransportOverride as n, isGooglePromptCacheEligible as o, resolveExplicitSettingsTransport as r, resolveCacheRetention as s, applyExtraParamsToAgent as t };