UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

135 lines (134 loc) 5.49 kB
import { i as streamSimple } from "./stream-4H1GSjKe.js"; import "./llm-B2byLsni.js"; import { b as createToolStreamWrapper, s as createPlainTextToolCallCompatWrapper, t as composeProviderStreamWrappers } from "./provider-stream-shared-BIV_zuwB.js"; //#region extensions/xai/stream.ts const XAI_FAST_MODEL_IDS = new Map([ ["grok-3", "grok-3-fast"], ["grok-3-mini", "grok-3-mini-fast"], ["grok-4", "grok-4-fast"], ["grok-4-0709", "grok-4-fast"] ]); function resolveXaiFastModelId(modelId) { if (typeof modelId !== "string") return; return XAI_FAST_MODEL_IDS.get(modelId.trim()); } function stripUnsupportedStrictFlag(tool) { if (!tool || typeof tool !== "object") return tool; const toolObj = tool; const fn = toolObj.function; if (!fn || typeof fn !== "object") return tool; const fnObj = fn; if (typeof fnObj.strict !== "boolean") return tool; const nextFunction = { ...fnObj }; delete nextFunction.strict; return { ...toolObj, function: nextFunction }; } function supportsExplicitImageInput(model) { return Array.isArray(model.input) && model.input.includes("image"); } function supportsReasoningControls(model) { const compat = model.compat && typeof model.compat === "object" ? model.compat : void 0; return model.reasoning === true && compat?.supportsReasoningEffort !== false; } const TOOL_RESULT_IMAGE_REPLAY_TEXT = "Attached image(s) from tool result:"; function isReplayableInputImagePart(part) { if (part.type !== "input_image") return false; if (typeof part.image_url === "string") return true; if (!part.source || typeof part.source !== "object") return false; const source = part.source; if (source.type === "url") return typeof source.url === "string"; return source.type === "base64" && typeof source.media_type === "string" && typeof source.data === "string"; } function normalizeXaiResponsesFunctionCallOutput(item, includeImages) { if (!item || typeof item !== "object") return { normalizedItem: item, imageParts: [] }; const itemObj = item; if (itemObj.type !== "function_call_output" || !Array.isArray(itemObj.output)) return { normalizedItem: itemObj, imageParts: [] }; const outputParts = itemObj.output; const textOutput = outputParts.filter((part) => part.type === "input_text" && typeof part.text === "string").map((part) => part.text).join(""); const imageParts = includeImages ? outputParts.filter((part) => isReplayableInputImagePart(part)) : []; const hadNonTextParts = outputParts.some((part) => part.type !== "input_text"); return { normalizedItem: { ...itemObj, output: textOutput || (hadNonTextParts ? "(see attached image)" : "") }, imageParts }; } function normalizeXaiResponsesToolResultPayload(payloadObj, model) { if (model.api !== "openai-responses" || !Array.isArray(payloadObj.input)) return; const includeImages = supportsExplicitImageInput(model); const normalizedInput = []; const collectedImageParts = []; for (const item of payloadObj.input) { const normalized = normalizeXaiResponsesFunctionCallOutput(item, includeImages); normalizedInput.push(normalized.normalizedItem); collectedImageParts.push(...normalized.imageParts); } if (collectedImageParts.length > 0) normalizedInput.push({ type: "message", role: "user", content: [{ type: "input_text", text: TOOL_RESULT_IMAGE_REPLAY_TEXT }, ...collectedImageParts] }); payloadObj.input = normalizedInput; } function createXaiToolPayloadCompatibilityWrapper(baseStreamFn) { const underlying = baseStreamFn ?? streamSimple; return (model, context, options) => { const originalOnPayload = options?.onPayload; return underlying(model, context, { ...options, onPayload: (payload) => { if (payload && typeof payload === "object") { const payloadObj = payload; if (Array.isArray(payloadObj.tools)) payloadObj.tools = payloadObj.tools.map((tool) => stripUnsupportedStrictFlag(tool)); normalizeXaiResponsesToolResultPayload(payloadObj, model); if (!supportsReasoningControls(model)) { delete payloadObj.reasoning; delete payloadObj.reasoningEffort; delete payloadObj.reasoning_effort; } } return originalOnPayload?.(payload, model); } }); }; } function createXaiFastModeWrapper(baseStreamFn, fastMode) { const underlying = baseStreamFn ?? streamSimple; return (model, context, options) => { const supportsFastAliasTransport = model.api === "openai-completions" || model.api === "openai-responses"; if (!fastMode || !supportsFastAliasTransport || model.provider !== "xai") return underlying(model, context, options); const fastModelId = resolveXaiFastModelId(model.id); if (!fastModelId) return underlying(model, context, options); return underlying({ ...model, id: fastModelId }, context, options); }; } function wrapXaiProviderStream(ctx) { const extraParams = ctx.extraParams; const fastMode = extraParams?.fastMode; const toolStreamEnabled = extraParams?.tool_stream !== false; return composeProviderStreamWrappers(ctx.streamFn, (streamFn) => { let wrappedStreamFn = createXaiToolPayloadCompatibilityWrapper(streamFn); if (typeof fastMode === "boolean") wrappedStreamFn = createXaiFastModeWrapper(wrappedStreamFn, fastMode); wrappedStreamFn = createPlainTextToolCallCompatWrapper(wrappedStreamFn); return createToolStreamWrapper(wrappedStreamFn, toolStreamEnabled); }); } //#endregion export { createXaiToolPayloadCompatibilityWrapper as n, wrapXaiProviderStream as r, createXaiFastModeWrapper as t };