UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

141 lines (140 loc) 7.75 kB
import { a as normalizeLowercaseStringOrEmpty, i as normalizeFastMode, p as readStringValue } from "./string-coerce-mnp54Vah.js"; import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js"; import { i as streamSimple } from "./stream-4H1GSjKe.js"; import "./llm-B2byLsni.js"; import { n as createAnthropicThinkingPrefillPayloadWrapper, t as composeProviderStreamWrappers, y as stripTrailingAnthropicAssistantPrefillWhenThinking } from "./provider-stream-shared-BIV_zuwB.js"; import { i as streamWithPayloadPatch } from "./moonshot-thinking-RT-IFIsx.js"; import { i as resolveAnthropicPayloadPolicy, n as applyAnthropicPayloadPolicyToParams } from "./anthropic-payload-policy-ZFzBdZJv.js"; import "./runtime-env-D_2q8-VK.js"; import "./string-coerce-runtime-CEGJWkQ_.js"; //#region extensions/anthropic/stream-wrappers.ts const log = createSubsystemLogger("anthropic-stream"); const ANTHROPIC_CONTEXT_1M_BETA_LEGACY = "context-1m-2025-08-07"; const ANTHROPIC_GA_1M_MODEL_PREFIXES = [ "claude-opus-4-8", "claude-opus-4.8", "claude-opus-4-6", "claude-opus-4.6", "claude-opus-4-7", "claude-opus-4.7", "claude-sonnet-4-6", "claude-sonnet-4.6" ]; const OPENCLAW_DEFAULT_ANTHROPIC_BETAS = ["fine-grained-tool-streaming-2025-05-14", "interleaved-thinking-2025-05-14"]; const OPENCLAW_OAUTH_ANTHROPIC_BETAS = [ "claude-code-20250219", "oauth-2025-04-20", ...OPENCLAW_DEFAULT_ANTHROPIC_BETAS ]; function isAnthropic1MModel(modelId) { const normalized = normalizeLowercaseStringOrEmpty(modelId); return ANTHROPIC_GA_1M_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix)); } function parseHeaderList(value) { if (typeof value !== "string") return []; return value.split(",").map((item) => item.trim()).filter(Boolean); } function mergeAnthropicBetaHeader(headers, betas) { const merged = { ...headers }; const existingKey = Object.keys(merged).find((key) => normalizeLowercaseStringOrEmpty(key) === "anthropic-beta"); const existing = existingKey ? parseHeaderList(merged[existingKey]) : []; const values = Array.from(new Set([...existing, ...betas])); const key = existingKey ?? "anthropic-beta"; merged[key] = values.join(","); return merged; } function isAnthropicOAuthApiKey(apiKey) { return typeof apiKey === "string" && apiKey.includes("sk-ant-oat"); } function resolveAnthropicFastServiceTier(enabled) { return enabled ? "auto" : "standard_only"; } function normalizeAnthropicServiceTier(value) { if (typeof value !== "string") return; const normalized = normalizeLowercaseStringOrEmpty(value); if (normalized === "auto" || normalized === "standard_only") return normalized; } function hasConfiguredAnthropicBeta(extraParams) { const configured = extraParams?.anthropicBeta; if (typeof configured === "string") return configured.trim().length > 0; if (!Array.isArray(configured)) return false; return configured.some((beta) => typeof beta === "string" && beta.trim().length > 0); } /** Resolve configured Anthropic beta headers from extra model params. */ function resolveAnthropicBetas(extraParams, _modelId) { const betas = /* @__PURE__ */ new Set(); const configured = extraParams?.anthropicBeta; if (typeof configured === "string" && configured.trim()) for (const beta of parseHeaderList(configured)) betas.add(beta); else if (Array.isArray(configured)) { for (const beta of configured) if (typeof beta === "string" && beta.trim()) for (const betaValue of parseHeaderList(beta)) betas.add(betaValue); } betas.delete(ANTHROPIC_CONTEXT_1M_BETA_LEGACY); return betas.size > 0 ? [...betas] : void 0; } /** Wrap a stream function to merge OpenClaw and configured Anthropic beta headers. */ function createAnthropicBetaHeadersWrapper(baseStreamFn, betas) { const underlying = baseStreamFn ?? streamSimple; return (model, context, options) => { const isOauth = isAnthropicOAuthApiKey(options?.apiKey); const effectiveBetas = betas.filter((beta) => beta !== ANTHROPIC_CONTEXT_1M_BETA_LEGACY); const allBetas = [...new Set([...isOauth ? OPENCLAW_OAUTH_ANTHROPIC_BETAS : OPENCLAW_DEFAULT_ANTHROPIC_BETAS, ...effectiveBetas])]; return underlying(model, context, { ...options, headers: mergeAnthropicBetaHeader(options?.headers, allBetas) }); }; } /** Wrap a stream function with the Anthropic fast-mode service tier. */ function createAnthropicFastModeWrapper(baseStreamFn, enabled) { return createAnthropicServiceTierWrapper(baseStreamFn, resolveAnthropicFastServiceTier(enabled)); } /** Wrap a stream function with an explicit Anthropic service tier when allowed. */ function createAnthropicServiceTierWrapper(baseStreamFn, serviceTier) { const underlying = baseStreamFn ?? streamSimple; return (model, context, options) => { if (isAnthropicOAuthApiKey(options?.apiKey)) return underlying(model, context, options); const payloadPolicy = resolveAnthropicPayloadPolicy({ provider: readStringValue(model.provider), api: readStringValue(model.api), baseUrl: readStringValue(model.baseUrl), serviceTier }); if (!payloadPolicy.allowsServiceTier) return underlying(model, context, options); return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => applyAnthropicPayloadPolicyToParams(payloadObj, payloadPolicy)); }; } /** Wrap a stream function to strip trailing assistant prefill before thinking requests. */ function createAnthropicThinkingPrefillWrapper(baseStreamFn) { return createAnthropicThinkingPrefillPayloadWrapper(baseStreamFn, (stripped) => { log.warn(`removed ${stripped} trailing assistant prefill message${stripped === 1 ? "" : "s"} because Anthropic extended thinking requires conversations to end with a user turn`); }); } /** Resolve Anthropic fast-mode setting from model extra params. */ function resolveAnthropicFastMode(extraParams) { return normalizeFastMode(extraParams?.fastMode ?? extraParams?.fast_mode); } /** Resolve Anthropic service tier from model extra params. */ function resolveAnthropicServiceTier(extraParams) { const raw = extraParams?.serviceTier ?? extraParams?.service_tier; const normalized = normalizeAnthropicServiceTier(raw); if (raw !== void 0 && normalized === void 0) { const rawSummary = typeof raw === "string" ? raw : typeof raw; log.warn(`ignoring invalid Anthropic service tier param: ${rawSummary}`); } return normalized; } /** Compose all Anthropic stream wrappers for one provider/model context. */ function wrapAnthropicProviderStream(ctx) { const anthropicBetas = resolveAnthropicBetas(ctx.extraParams, ctx.modelId); const needsAnthropicBetaWrapper = anthropicBetas !== void 0 || hasConfiguredAnthropicBeta(ctx.extraParams) || ctx.extraParams?.context1m === true && isAnthropic1MModel(ctx.modelId); const serviceTier = resolveAnthropicServiceTier(ctx.extraParams); const fastMode = resolveAnthropicFastMode(ctx.extraParams); return composeProviderStreamWrappers(ctx.streamFn, needsAnthropicBetaWrapper ? (streamFn) => createAnthropicBetaHeadersWrapper(streamFn, anthropicBetas ?? []) : void 0, serviceTier ? (streamFn) => createAnthropicServiceTierWrapper(streamFn, serviceTier) : void 0, fastMode !== void 0 ? (streamFn) => createAnthropicFastModeWrapper(streamFn, fastMode) : void 0, (streamFn) => createAnthropicThinkingPrefillWrapper(streamFn)); } /** Test-only hooks for Anthropic stream wrapper behavior. */ const testing = { log, stripTrailingAssistantPrefillWhenThinking: stripTrailingAnthropicAssistantPrefillWhenThinking }; //#endregion export { resolveAnthropicBetas as a, testing as c, createAnthropicThinkingPrefillWrapper as i, wrapAnthropicProviderStream as l, createAnthropicFastModeWrapper as n, resolveAnthropicFastMode as o, createAnthropicServiceTierWrapper as r, resolveAnthropicServiceTier as s, createAnthropicBetaHeadersWrapper as t };