openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
408 lines (407 loc) • 19.6 kB
JavaScript
import { a as sanitizeExtraParamsRecord, i as resolveModelExtraParamSources, r as resolveAliasedParamValue } from "./model-extra-params-BN1mxasE.mjs";
import { n as ensureProviderRuntimePluginHandle, r as getModelProviderRuntimePluginHandle } from "./provider-hook-runtime-DXt-lONr.mjs";
import { r as isNativeWebSearchAllowedByToolPolicy } from "./codex-native-web-search-core-CaxYwRH_.mjs";
import { i as streamSimple } from "./stream-B2a9zNQj.mjs";
import { t as log } from "./logger-Wi08e7sJ.mjs";
import { i as resolveExplicitCachedContent, n as parseCacheRetention, r as resolveCacheRetention } from "./prompt-cache-retention-BEbTNOXT.mjs";
import { t as streamWithPayloadPatch } from "./stream-payload-utils-BoUixBWV.mjs";
import { r as createOpenAICompletionsPayloadPolicyWrapper } from "./ai-transport-runtime-host-Dx1-1zBB.mjs";
import { h as createOpenAIStringContentWrapper, l as createOpenAICompletionsStrictMessageKeysWrapper, n as createOpenRouterSystemCacheWrapper, o as createMinimaxThinkingDisabledWrapper, p as createOpenAIResponsesContextManagementWrapper, u as createOpenAICompletionsToolsCompatWrapper } from "./proxy-C1UOLOq7.mjs";
import { c as createDeepSeekV4OpenAICompatibleThinkingWrapper, h as createThinkingOnlyFinalTextWrapper, u as createGoogleThinkingPayloadWrapper } from "./provider-stream-shared-BhBL5TrQ.mjs";
import { canonicalizeMaxTokensParam, detectOpenAICompletionsCompat, resolveMaxTokensParam, resolveOpenAICompletionsCompat } from "@openclaw/ai/transports";
//#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 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"
]);
function resolveExtraParams(params) {
const { defaultParams, modelParams, agentModelParams, agentParams } = resolveModelExtraParamSources({
config: params.cfg,
provider: params.provider,
modelId: params.modelId,
agentId: params.agentId
});
const sources = [
defaultParams,
modelParams,
agentModelParams,
agentParams
];
const merged = Object.assign({}, ...sources);
canonicalizeExtraParamAlias(merged, sources, ["parallel_tool_calls", "parallelToolCalls"]);
canonicalizeExtraParamAlias(merged, [
modelParams,
agentModelParams,
agentParams
], ["text_verbosity", "textVerbosity"]);
canonicalizeExtraParamAlias(merged, sources, ["response_format", "responseFormat"]);
canonicalizeMaxTokensParam({
merged,
sources
});
canonicalizeExtraParamAlias(merged, sources, ["cached_content", "cachedContent"], "cachedContent");
if (params.provider === "openrouter") canonicalizeOpenRouterResponseCacheParams(merged, sources);
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 resolvePreparedExtraParams(params) {
const resolvedExtraParams = params.resolvedExtraParams ?? resolveExtraParams(params);
const override = stripRequestScopedExtraParams(sanitizeExtraParamsOverride(params.extraParamsOverride));
const merged = {
...sanitizeExtraParamsRecord(resolvedExtraParams),
...override
};
canonicalizeMaxTokensParam({
merged,
sources: [resolvedExtraParams, override]
});
canonicalizeExtraParamAlias(merged, [resolvedExtraParams, override], ["cached_content", "cachedContent"], "cachedContent");
if (params.provider === "openrouter") canonicalizeOpenRouterResponseCacheParams(merged, [resolvedExtraParams, override]);
const { plugin } = ensureProviderRuntimePluginHandle({
provider: params.provider,
modelId: params.modelId,
config: params.cfg,
workspaceDir: params.workspaceDir,
runtimeHandle: params.providerRuntimeHandle ?? getModelProviderRuntimePluginHandle(params.model)
});
const context = {
config: params.cfg,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir,
provider: params.provider,
modelId: params.modelId,
model: params.model,
thinkingLevel: params.thinkingLevel,
auth: params.auth
};
const prepared = plugin?.prepareExtraParams?.({
...context,
extraParams: merged
}) ?? merged;
const transportPatch = plugin?.extraParamsForTransport?.({
...context,
extraParams: prepared,
transport: params.resolvedTransport ?? resolveSupportedTransport(prepared.transport)
})?.patch;
const result = transportPatch ? {
...prepared,
...transportPatch
} : prepared;
canonicalizeMaxTokensParam({
merged: result,
sources: [prepared, transportPatch ?? void 0]
});
return result;
}
function sanitizeExtraParamsOverride(value) {
return value && Object.keys(value).length > 0 ? sanitizeExtraParamsRecord(Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0))) : void 0;
}
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 applyDefaultOpenAIGptRuntimeParams(params, merged) {
if (params.provider !== "openai" || !/^gpt-5(?:[.-]|$)/i.test(params.modelId)) return;
for (const [canonical, alias, value] of [[
"parallel_tool_calls",
"parallelToolCalls",
true
], [
"text_verbosity",
"textVerbosity",
"low"
]]) if (!Object.hasOwn(merged, canonical) && !Object.hasOwn(merged, alias)) merged[canonical] = value;
}
function resolveAgentTransportOverride(params) {
const globalSettings = params.settingsManager.getGlobalSettings();
const projectSettings = params.settingsManager.getProjectSettings();
if (Object.hasOwn(globalSettings, "transport") || Object.hasOwn(projectSettings, "transport")) return;
return resolveSupportedTransport(params.effectiveExtraParams?.transport);
}
function resolveExplicitSettingsTransport(params) {
const globalSettings = params.settingsManager.getGlobalSettings();
const projectSettings = params.settingsManager.getProjectSettings();
if (!Object.hasOwn(globalSettings, "transport") && !Object.hasOwn(projectSettings, "transport")) 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 = {};
for (const key of ["temperature", "topP"]) if (typeof extraParams[key] === "number") streamParams[key] = extraParams[key];
if (typeof extraParams.streaming === "boolean") streamParams.streaming = extraParams.streaming;
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 = resolveExplicitCachedContent(extraParams);
if (cachedContent) streamParams.cachedContent = cachedContent;
for (const keys of [["frequencyPenalty", "frequency_penalty"], ["presencePenalty", "presence_penalty"]]) {
const value = resolveAliasedParamValue([extraParams], keys);
if (typeof value === "number") streamParams[keys[0]] = value;
}
if (typeof extraParams.seed === "number") streamParams.seed = extraParams.seed;
const resolvedStop = normalizeStopSequences(extraParams.stop);
if (resolvedStop) streamParams.stop = resolvedStop;
const resolveModelCacheRetention = (candidate) => resolveCacheRetention(extraParams, provider, typeof candidate?.api === "string" ? candidate.api : void 0, typeof candidate?.id === "string" ? candidate.id : void 0, candidate?.api === "openai-completions" ? resolveOpenAICompletionsCompat(candidate) : candidate?.compat, candidate?.baseUrl);
if (log.isEnabled("debug")) {
const initialCacheRetention = resolveModelCacheRetention(model);
if (Object.keys(streamParams).length > 0 || initialCacheRetention) {
const debugParams = {
...streamParams,
cacheRetention: initialCacheRetention
};
log.debug(`creating streamFn wrapper with params: ${JSON.stringify(debugParams)}`);
}
}
const underlying = requireBaseStreamFn(baseStreamFn);
return (callModel, context, options) => {
const cacheRetention = resolveModelCacheRetention(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 } : {}
});
};
}
function canonicalizeExtraParamAlias(merged, sources, keys, canonical = keys[0]) {
const resolved = resolveAliasedParamValue(sources, keys);
if (resolved !== void 0) {
merged[canonical] = resolved;
delete merged[keys[0] === canonical ? keys[1] : keys[0]];
}
}
const OPENROUTER_RESPONSE_CACHE_PARAM_ALIASES = [
["responseCache", "response_cache"],
[
"responseCacheTtlSeconds",
"response_cache_ttl_seconds",
"responseCacheTtl",
"response_cache_ttl"
],
["responseCacheClear", "response_cache_clear"]
];
function canonicalizeOpenRouterResponseCacheParams(merged, sources) {
for (const keys of OPENROUTER_RESPONSE_CACHE_PARAM_ALIASES) {
const resolved = resolveAliasedParamValue(sources, keys);
if (resolved === void 0) continue;
for (const key of keys) delete merged[key];
merged[keys[0]] = resolved;
}
}
function createParallelToolCallsWrapper(baseStreamFn, enabled) {
const underlying = requireBaseStreamFn(baseStreamFn);
return (model, context, options) => {
if (!GPT_PARALLEL_TOOL_CALLS_APIS.has(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;
});
};
}
const DEEPSEEK_V4_MODEL_IDS = /* @__PURE__ */ new Set([
"deepseek-flash",
"deepseek-v4-flash",
"deepseek-v4-pro"
]);
function normalizeCompatibleModelId(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 isOpenAICompletionsModel(model, modelIds) {
const normalizedModelId = normalizeCompatibleModelId(model.id);
return model.api === "openai-completions" && normalizedModelId !== void 0 && modelIds.has(normalizedModelId);
}
function isMicrosoftFoundryProviderId(provider) {
if (typeof provider !== "string") return false;
const normalizedProvider = provider.trim().toLowerCase();
return normalizedProvider === "microsoft-foundry" || normalizedProvider.startsWith("microsoft-foundry-");
}
/**
* Foundry and other non-native routes reject even thinking.type=disabled.
* Explicit compat wins, then detected non-native formats; unknown proxy routes
* retain the model-ID fallback to DeepSeek's native wire format.
*/
function deepSeekV4NativeThinkingAllowedByCompat(model) {
const compat = model.compat;
const configured = compat && typeof compat === "object" ? compat.thinkingFormat : void 0;
if (typeof configured === "string") return configured === "deepseek";
const detected = detectOpenAICompletionsCompat(model).defaults.thinkingFormat;
return detected !== "openrouter" && detected !== "together" && detected !== "zai";
}
function createDeepSeekV4NonNativeCompatSanitizerWrapper(baseStreamFn) {
if (!baseStreamFn) return;
return (model, context, options) => {
if (!isOpenAICompletionsModel(model, DEEPSEEK_V4_MODEL_IDS) || !isMicrosoftFoundryProviderId(model.provider) && deepSeekV4NativeThinkingAllowedByCompat(model)) return baseStreamFn(model, context, options);
return streamWithPayloadPatch(baseStreamFn, model, context, options, (payload) => {
delete payload.thinking;
if (Array.isArray(payload.messages)) {
for (const message of payload.messages) if (message && typeof message === "object") 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",
...[
"flash",
"pro",
"pro-ultraspeed"
].map((variant) => `mimo-v2.6-${variant}`)
]);
const MIMO_REASONING_AS_VISIBLE_TEXT_MODEL_IDS = /* @__PURE__ */ new Set(["mimo-v2-pro", "mimo-v2-omni"]);
function applyExtraParamsToAgent(agent, cfg, provider, modelId, extraParamsOverride, thinkingLevel, agentId, workspaceDir, model, agentDir, resolvedTransport, options) {
const selectedModel = {
provider,
modelId
};
const providerRuntimeHandle = ensureProviderRuntimePluginHandle({
...selectedModel,
config: cfg,
workspaceDir,
runtimeHandle: getModelProviderRuntimePluginHandle(model)
});
const override = sanitizeExtraParamsOverride(extraParamsOverride);
const effectiveExtraParams = options?.preparedExtraParams ?? resolvePreparedExtraParams({
cfg,
...selectedModel,
extraParamsOverride,
thinkingLevel,
agentId,
agentDir,
workspaceDir,
model,
resolvedTransport,
providerRuntimeHandle,
auth: options?.auth
});
const providerStreamBase = agent.streamFn;
const nativeWebSearchAllowedByToolPolicy = options?.nativeWebSearchPolicyContext ? isNativeWebSearchAllowedByToolPolicy({
config: cfg,
modelProvider: model?.provider,
modelId: model?.id,
agentId,
...options.nativeWebSearchPolicyContext
}) : void 0;
const pluginWrappedStreamFn = providerRuntimeHandle.plugin?.wrapStreamFn?.({
config: cfg,
agentDir,
workspaceDir,
agentId,
auth: options?.auth,
nativeWebSearchAllowedByToolPolicy,
...selectedModel,
extraParams: effectiveExtraParams,
thinkingLevel,
model,
streamFn: providerStreamBase
}) ?? void 0;
agent.streamFn = pluginWrappedStreamFn ?? providerStreamBase;
const baseExtraParams = override && [...REQUEST_SCOPED_EXTRA_PARAM_KEYS].some((key) => Object.hasOwn(override, key)) ? stripRequestScopedExtraParams(effectiveExtraParams) : effectiveExtraParams;
const streamParams = override ? {
...baseExtraParams,
...override
} : baseExtraParams;
const wrappedStreamFn = createStreamFnWithExtraParams(agent.streamFn, streamParams, provider, model);
if (wrappedStreamFn) {
log.debug(`applying extraParams to agent streamFn for ${provider}/${modelId}`);
agent.streamFn = wrappedStreamFn;
}
if (shouldApplySiliconFlowThinkingOffCompat({
...selectedModel,
thinkingLevel
})) {
log.debug(`normalizing thinking=off to thinking=null for SiliconFlow compatibility (${provider}/${modelId})`);
agent.streamFn = createSiliconFlowThinkingWrapper(agent.streamFn);
}
const providerWrapperHandled = pluginWrappedStreamFn !== void 0 && pluginWrappedStreamFn !== providerStreamBase;
const cacheStreamParams = override ? {
...effectiveExtraParams,
...override
} : effectiveExtraParams;
agent.streamFn = createOpenRouterSystemCacheWrapper(agent.streamFn, cacheStreamParams);
agent.streamFn = createOpenAIStringContentWrapper(agent.streamFn);
agent.streamFn = createOpenAICompletionsStrictMessageKeysWrapper(agent.streamFn);
agent.streamFn = createOpenAICompletionsToolsCompatWrapper(agent.streamFn);
if (!providerWrapperHandled) {
agent.streamFn = createDeepSeekV4OpenAICompatibleThinkingWrapper({
baseStreamFn: agent.streamFn,
thinkingLevel,
shouldPatchModel: (candidateModel) => isOpenAICompletionsModel(candidateModel, DEEPSEEK_V4_MODEL_IDS) && !isMicrosoftFoundryProviderId(candidateModel.provider) && deepSeekV4NativeThinkingAllowedByCompat(candidateModel)
});
agent.streamFn = createDeepSeekV4NonNativeCompatSanitizerWrapper(agent.streamFn);
agent.streamFn = createDeepSeekV4OpenAICompatibleThinkingWrapper({
baseStreamFn: agent.streamFn,
thinkingLevel,
shouldPatchModel: (candidateModel) => isOpenAICompletionsModel(candidateModel, MIMO_REASONING_OPENAI_COMPATIBLE_MODEL_IDS)
});
agent.streamFn = createThinkingOnlyFinalTextWrapper({
baseStreamFn: agent.streamFn,
shouldPatchModel: (candidateModel) => isOpenAICompletionsModel(candidateModel, MIMO_REASONING_AS_VISIBLE_TEXT_MODEL_IDS)
});
agent.streamFn = createGoogleThinkingPayloadWrapper(agent.streamFn, thinkingLevel);
agent.streamFn = createOpenAIResponsesContextManagementWrapper(agent.streamFn, effectiveExtraParams);
}
agent.streamFn = createMinimaxThinkingDisabledWrapper(agent.streamFn, thinkingLevel);
agent.streamFn = createOpenAICompletionsPayloadPolicyWrapper(requireBaseStreamFn(agent.streamFn), [effectiveExtraParams, override]);
const rawParallelToolCalls = resolveAliasedParamValue([effectiveExtraParams, override], ["parallel_tool_calls", "parallelToolCalls"]);
if (typeof rawParallelToolCalls === "boolean") agent.streamFn = createParallelToolCallsWrapper(agent.streamFn, rawParallelToolCalls);
else if (rawParallelToolCalls === null) log.debug("parallel_tool_calls suppressed by null override, skipping injection");
else if (rawParallelToolCalls !== void 0) {
const summary = typeof rawParallelToolCalls === "string" ? rawParallelToolCalls : typeof rawParallelToolCalls;
log.warn(`ignoring invalid parallel_tool_calls param: ${summary}`);
}
return {
effectiveExtraParams,
nativeWebSearchAllowedByToolPolicy
};
}
//#endregion
export { resolvePreparedExtraParams as a, resolveExtraParams as i, resolveAgentTransportOverride as n, resolveExplicitSettingsTransport as r, applyExtraParamsToAgent as t };