UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

351 lines (350 loc) • 15.2 kB
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { f as runPluginStreamConsumer } from "./plugin-instance-scope-DEzoxqBj.mjs"; import { a as sanitizeExtraParamsRecord, i as resolveModelExtraParamSources, r as resolveAliasedParamValue } from "./model-extra-params-C1g6s41K.mjs"; import { t as DEFAULT_MISSING_TOOL_RESULT_TEXT } from "./types-CQc6JNG4.mjs"; import { r as loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime-B-yS8AQL.mjs"; import { l as isSyntheticMissingToolResult, n as SYNTHETIC_MISSING_TOOL_RESULT_DETAIL_KEY } from "./tool-result-pairing-DxxMHOQj.mjs"; import { i as resolveProviderRequestCapabilities } from "./provider-attribution-DCsKu1Yv.mjs"; import { a as getModelProviderRequestRouteFacts, f as resolveProviderRequestPolicyConfig, o as getModelProviderRequestTransport, r as attachModelProviderRequestTransport, s as inheritModelProviderRequestRouteFacts } from "./provider-request-config-By9R9pyt.mjs"; import { f as wrapProviderSimpleCompletionStreamFn, r as getModelProviderRuntimePluginHandle } from "./provider-hook-runtime-CV4YC-3T.mjs"; import { D as resolveProviderStreamFn, j as resolveProviderTransportTurnStateWithPlugin } from "./provider-runtime-D-Xv0Y4z.mjs"; import { o as getModelProviderLocalService, t as attachModelProviderLocalService } from "./provider-local-service-ucCbK8nV.mjs"; import { t as repairToolUseResultPairing } from "./session-transcript-repair-CgGD_n-Q.mjs"; import "./ai-transport-host-DgKeCN07.mjs"; import { t as event_stream_exports } from "./event-stream-xGBL9CyU.mjs"; import { t as log } from "./logger-BpegRAAz.mjs"; import { n as buildStreamErrorAssistantMessage } from "./stream-message-shared-CYuIJ_XF.mjs"; import { t as streamWithPayloadPatch } from "./stream-payload-utils-BoUixBWV.mjs"; import { c as hasCopilotVisionInput, o as buildCopilotDynamicHeaders } from "./copilot-dynamic-headers-C8eJEO7a.mjs"; import { FAILED_ASSISTANT_REPLAY_TEXT, isReasoningOnlyLengthAssistantTurn, resolveFailedAssistantReplay } from "@openclaw/ai/internal/shared"; import { configureAiTransportHost, getDefaultAiTransportHost } from "@openclaw/ai"; import { OPENAI_RESPONSES_APIS } from "@openclaw/ai/internal/openai-responses-payload-policy"; import { resolveModelBoundThinkingReplayMode } from "@openclaw/ai/internal/anthropic"; //#region src/agents/anthropic-vertex-stream.ts /** * Anthropic Vertex stream facade. * Keeps Vertex-specific provider implementation in the bundled provider plugin * while core imports a small stable factory. */ function loadAnthropicVertexStreamFacade() { return loadBundledPluginPublicSurfaceModuleSync({ dirName: "anthropic-vertex", artifactBasename: "api.js" }); } /** Creates an Anthropic Vertex stream function through the bundled provider facade. */ function createAnthropicVertexStreamFnForModel(model, env = process.env) { return loadAnthropicVertexStreamFacade().createAnthropicVertexStreamFnForModel(model, env); } //#endregion //#region src/agents/custom-api-registry.ts const CUSTOM_API_SOURCE_PREFIX = "openclaw-custom-api:"; /** Returns the registry source id used for a custom API stream function. */ function getCustomApiRegistrySourceId(api) { return `${CUSTOM_API_SOURCE_PREFIX}${api}`; } function adaptCustomStream(model, stream) { if (!(stream instanceof Promise)) return stream; const adapted = (0, event_stream_exports.createAssistantMessageEventStream)(); (async () => { try { await runPluginStreamConsumer(stream, async () => { const resolved = await stream; for await (const event of resolved) adapted.push(event); adapted.end(await resolved.result()); }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const message = buildStreamErrorAssistantMessage({ model, errorMessage }); adapted.push({ type: "error", reason: "error", error: message }); } })(); return adapted; } /** Registers a custom API stream function when no provider already owns it. */ function ensureCustomApiRegistered(registry, api, streamFn) { if (registry.getApiProvider(api)) return false; registry.registerApiProvider({ api, stream: (model, context, options) => adaptCustomStream(model, streamFn(model, context, options)), streamSimple: (model, context, options) => adaptCustomStream(model, streamFn(model, context, options)) }, getCustomApiRegistrySourceId(api)); return true; } //#endregion //#region src/agents/openai-completions-payload-policy.ts function shouldStripOpenAICompletionsStore(model) { if (model.api !== "openai-completions") return false; const compat = model.compat && typeof model.compat === "object" ? Object.fromEntries(Object.entries(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 resolveExtraBodyRecord(value, param) { if (value === void 0 || value === null) return; if (typeof value !== "object" || Array.isArray(value)) { log.warn(`ignoring invalid ${param} param: ${typeof value === "string" ? value : typeof value}`); return; } const record = Object.fromEntries(Object.entries(sanitizeExtraParamsRecord(value) ?? {}).filter(([, entry]) => entry !== void 0)); return Object.keys(record).length > 0 ? record : void 0; } const FRAMEWORK_MANAGED_EXTRA_BODY_KEYS = /* @__PURE__ */ new Set([ "messages", "model", "stream" ]); /** Apply configured payload fields before removing store from non-native completion routes. */ function createOpenAICompletionsPayloadPolicyWrapper(streamFn, sources) { const configuredChatTemplateKwargs = resolveExtraBodyRecord(resolveAliasedParamValue(sources, ["chat_template_kwargs", "chatTemplateKwargs"]), "chat_template_kwargs"); const extraBody = resolveExtraBodyRecord(resolveAliasedParamValue(sources, ["extra_body", "extraBody"]), "extra_body"); return (model, context, options) => { const stripStore = shouldStripOpenAICompletionsStore(model); if (model.api !== "openai-completions" || !configuredChatTemplateKwargs && !extraBody && !stripStore) return streamFn(model, context, options); return streamWithPayloadPatch(streamFn, model, context, options, (payload) => { if (configuredChatTemplateKwargs) { const existing = payload.chat_template_kwargs; payload.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing, ...configuredChatTemplateKwargs } : configuredChatTemplateKwargs; } if (extraBody) { const clobberedManagedKeys = Object.keys(extraBody).filter((key) => Object.hasOwn(payload, key) && FRAMEWORK_MANAGED_EXTRA_BODY_KEYS.has(key)); if (clobberedManagedKeys.length > 0) log.warn(`extra_body overrides framework-managed request keys: ${clobberedManagedKeys.join(", ")}`); Object.assign(payload, extraBody); } if (stripStore) delete payload.store; }); }; } //#endregion //#region src/agents/transport-message-transform.ts /** * Normalizes transcript messages before provider transport replay. It drops * unsafe failed turns, maps tool-call ids across model boundaries, and fills * strict provider tool-result gaps when supported. */ const SYNTHETIC_TOOL_RESULT_APIS = /* @__PURE__ */ new Set([ "anthropic-messages", "openclaw-anthropic-messages-transport", "bedrock-converse-stream", "google-generative-ai", "openclaw-google-generative-ai-transport", ...OPENAI_RESPONSES_APIS ]); /** Transforms transcript messages into a provider-safe replay context. */ function transformTransportMessages(messages, model, normalizeToolCallId, options) { const syntheticToolResultText = OPENAI_RESPONSES_APIS.has(model.api) ? "aborted" : DEFAULT_MISSING_TOOL_RESULT_TEXT; const toolCallIdMap = /* @__PURE__ */ new Map(); let hasCrossModelAsyncCalls = false; const transformed = messages.map((msg) => { if (msg.role === "toolResult") { const result = isSyntheticMissingToolResult(msg) && (msg.content.length !== 1 || msg.content[0]?.type !== "text" || msg.content[0].text !== syntheticToolResultText) ? { ...msg, content: [{ type: "text", text: syntheticToolResultText }], details: { ...isRecord(msg.details) ? msg.details : {}, [SYNTHETIC_MISSING_TOOL_RESULT_DETAIL_KEY]: true } } : msg; const normalizedId = toolCallIdMap.get(msg.toolCallId); return normalizedId && normalizedId !== msg.toolCallId ? { ...result, toolCallId: normalizedId } : result; } if (msg.role !== "assistant") return msg; const modelBoundThinkingReplayMode = resolveModelBoundThinkingReplayMode({ source: { provider: msg.provider, api: msg.api, modelId: msg.model, responseModelId: msg.responseModel }, target: { provider: model.provider, api: model.api, modelId: model.id, modelParams: model.params } }); const isSameModel = modelBoundThinkingReplayMode === "preserve" || msg.provider === model.provider && msg.api === model.api && msg.model === model.id; const sourceContent = Array.isArray(msg.content) ? msg.content : msg.content != null && typeof msg.content === "object" ? [msg.content] : []; const content = []; for (const block of sourceContent) { if (block.type === "thinking") { if (modelBoundThinkingReplayMode === "drop") continue; if (block.redacted) { if (isSameModel) content.push(block); continue; } if (isSameModel && block.thinkingSignature) { content.push(block); continue; } if (!block.thinking.trim()) continue; content.push(isSameModel ? block : { type: "text", text: block.thinking }); continue; } if (block.type === "text") { content.push(isSameModel ? block : { type: "text", text: block.text }); continue; } if (block.type !== "toolCall") { content.push(block); continue; } let normalizedToolCall = block; if (!isSameModel && block.async) { hasCrossModelAsyncCalls = true; normalizedToolCall = { ...normalizedToolCall }; delete normalizedToolCall.async; } if (!isSameModel && block.thoughtSignature && options?.preserveCrossModelToolCallThoughtSignature !== true) { normalizedToolCall = { ...normalizedToolCall }; delete normalizedToolCall.thoughtSignature; } if ((!isSameModel || options?.normalizeSameModelToolCallIds === true) && normalizeToolCallId) { const normalizedId = normalizeToolCallId(block.id, model, msg); if (normalizedId !== block.id) { toolCallIdMap.set(block.id, normalizedId); normalizedToolCall = { ...normalizedToolCall, id: normalizedId }; } } content.push(normalizedToolCall); } return { ...msg, content }; }); const requiresPairing = SYNTHETIC_TOOL_RESULT_APIS.has(model.api) || hasCrossModelAsyncCalls; let replayLength = 0; transformed.forEach((msg, index) => { const original = messages[index]; if (original && isReasoningOnlyLengthAssistantTurn(original)) return; const replay = original ? resolveFailedAssistantReplay(original, { pairingAware: requiresPairing }) : "keep"; if (replay !== "drop") transformed[replayLength++] = replay === "marker" ? { ...msg, content: [{ type: "text", text: FAILED_ASSISTANT_REPLAY_TEXT }] } : msg; }); transformed.length = replayLength; if (!requiresPairing) return transformed; return repairToolUseResultPairing(transformed, { erroredAssistantResultPolicy: "drop", missingToolResultText: syntheticToolResultText, preserveUnframedToolResults: options?.preserveUnframedToolResults }).messages; } //#endregion //#region src/agents/ai-transport-runtime-host.ts let configured = false; /** Installs the agent and plugin ports only on paths that execute provider runtime. */ function configureAiTransportRuntimeHost() { if (configured) return; const host = getDefaultAiTransportHost(); configureAiTransportHost({ ...host, plugin: { ...host.plugin, resolveProviderStream: (params) => resolveProviderStreamFn({ ...params, config: params.config, runtimeHandle: getModelProviderRuntimePluginHandle(params.context.model), context: { ...params.context, config: params.context.config, model: params.context.model } }), resolveTransportTurnState: (params) => resolveProviderTransportTurnStateWithPlugin({ ...params, config: params.config, runtimeHandle: getModelProviderRuntimePluginHandle(params.context.model), context: { ...params.context, model: params.context.model } }), wrapSimpleCompletionStream: (params) => { const config = params.config; const providerStreamFn = wrapProviderSimpleCompletionStreamFn({ ...params, config, runtimeHandle: getModelProviderRuntimePluginHandle(params.context.model), context: { ...params.context, config: params.context.config, model: params.context.model } }); const baseStreamFn = providerStreamFn ?? params.context.streamFn; if ((params.context.sourceApi ?? params.context.model.api) !== "openai-completions") return providerStreamFn; const { defaultParams, modelParams, agentModelParams, agentParams } = resolveModelExtraParamSources({ config, provider: params.provider, modelId: params.context.modelId, agentId: params.context.agentId }); return createOpenAICompletionsPayloadPolicyWrapper(baseStreamFn, [ defaultParams, modelParams, agentModelParams, agentParams ]); }, createAnthropicVertexStream: createAnthropicVertexStreamFnForModel }, buildCopilotDynamicHeaders: (messages) => buildCopilotDynamicHeaders({ messages, hasImages: hasCopilotVisionInput(messages) }), resolveProviderRequestCapabilities: (input) => getModelProviderRequestRouteFacts(input.model ?? {})?.capabilities ?? resolveProviderRequestCapabilities(input), resolveProviderRequestHeaders: (input) => resolveProviderRequestPolicyConfig({ ...input, routeFacts: getModelProviderRequestRouteFacts(input.model ?? {}), capability: "llm", transport: "stream" }).headers, requiresManagedTransport: (model) => { const request = getModelProviderRequestTransport(model); return Boolean(request?.proxy || request?.tls || getModelProviderLocalService(model)); }, inheritManagedTransport: (source, target) => inheritModelProviderRequestRouteFacts(source, attachModelProviderLocalService(attachModelProviderRequestTransport(target, getModelProviderRequestTransport(source)), getModelProviderLocalService(source))), transformTransportMessages, registerCustomApi: ensureCustomApiRegistered }); configured = true; } configureAiTransportRuntimeHost(); //#endregion export { createAnthropicVertexStreamFnForModel as a, ensureCustomApiRegistered as i, transformTransportMessages as n, createOpenAICompletionsPayloadPolicyWrapper as r, configureAiTransportRuntimeHost as t };