/** * chatCore upstream body preparation (Quality Gate v2 / Fase 9 — chatCore god-file decomposition, * #3501 — first internal sub-slice of executeProviderRequest). * * Extracted from handleChatCore's execute() closure: prepares the body actually sent upstream for a * given target model. Pins the model id, applies the configured payload rules, truncates the tool * list to the provider's effective limit, backfills a default `user` for Qwen OAuth requests, and * injects an OpenAI `prompt_cache_key` for caching-capable providers. Pure with respect to handler * state (returns a fresh body, only logs as a side effect); behaviour is byte-identical to the * previous inline block. Split into small private steps so each stays under the complexity cap. */ import { applyConfiguredPayloadRules, resolvePayloadRuleProtocols, } from "../../services/payloadRules.ts"; import { getEffectiveToolLimit, getKnownToolLimit } from "../../services/toolLimitDetector.ts"; import { providerSupportsCaching } from "../../utils/cacheControlPolicy.ts"; import { FORMATS } from "../../translator/formats.ts"; type LoggerLike = { debug?: (...args: unknown[]) => void } | null | undefined; type Body = Record; type CredentialsLike = { apiKey?: unknown; accessToken?: unknown } | null | undefined; function buildAppliedRulesSummary( applied: Array<{ type: string; path: string; value?: unknown }> ): string { return applied .map((rule) => { if (rule.type === "filter") return `${rule.type}:${rule.path}`; const serializedValue = JSON.stringify(rule.value); const safeValue = typeof serializedValue === "string" && serializedValue.length > 80 ? `${serializedValue.slice(0, 77)}...` : serializedValue; return `${rule.type}:${rule.path}=${safeValue}`; }) .join(", "); } function truncateToolList( bodyToSend: Body, provider: string | null | undefined, bypassDefaultToolLimit: boolean, log?: LoggerLike ): Body { if (!Array.isArray(bodyToSend.tools)) return bodyToSend; const knownLimit = getKnownToolLimit(provider); if (knownLimit !== null) { if (bodyToSend.tools.length > knownLimit) { const originalCount = bodyToSend.tools.length; const truncatedTools = bodyToSend.tools.slice(0, knownLimit); bodyToSend = { ...bodyToSend, tools: truncatedTools }; log?.debug?.( "TOOL_LIMIT", `Truncated ${originalCount} tools to ${knownLimit} for ${provider}` ); } return bodyToSend; } if (bypassDefaultToolLimit === true) return bodyToSend; const effectiveToolLimit = getEffectiveToolLimit(provider); if (bodyToSend.tools.length > effectiveToolLimit) { const originalCount = bodyToSend.tools.length; const truncatedTools = bodyToSend.tools.slice(0, effectiveToolLimit); bodyToSend = { ...bodyToSend, tools: truncatedTools }; log?.debug?.( "TOOL_LIMIT", `Truncated ${originalCount} tools to ${effectiveToolLimit} for ${provider}` ); } return bodyToSend; } // Qwen OAuth rejects requests without a non-empty `user` field. Some minimal OpenAI-compatible // clients omit it, so we backfill a stable default only for OAuth mode (API key mode is unaffected). function backfillQwenOAuthUser( bodyToSend: Body, provider: string | null | undefined, credentials: CredentialsLike, log?: LoggerLike ): Body { const hasValidQwenUser = typeof bodyToSend.user === "string" && bodyToSend.user.trim().length > 0; const isQwenOAuthRequest = provider === "qwen" && !credentials?.apiKey && typeof credentials?.accessToken === "string" && credentials.accessToken.trim().length > 0; if (isQwenOAuthRequest && !hasValidQwenUser) { bodyToSend = { ...bodyToSend, user: "omniroute-qwen-oauth" }; log?.debug?.("QWEN", "Injected fallback user for OAuth request"); } return bodyToSend; } // Inject prompt_cache_key only for providers that support it. async function injectPromptCacheKey( bodyToSend: Body, provider: string | null | undefined, targetFormat: string ): Promise { if ( targetFormat === FORMATS.OPENAI && providerSupportsCaching(provider) && !bodyToSend.prompt_cache_key && Array.isArray(bodyToSend.messages) && !["nvidia", "codex", "xai"].includes(provider) ) { const { generatePromptCacheKey } = await import("@/lib/promptCache"); const cacheKey = generatePromptCacheKey(bodyToSend.messages); if (cacheKey) { bodyToSend = { ...bodyToSend, prompt_cache_key: cacheKey }; } } return bodyToSend; } export async function prepareUpstreamBody(opts: { translatedBody: Body; modelToCall: string; provider: string | null | undefined; targetFormat: string; credentials: CredentialsLike; bypassDefaultToolLimit?: boolean; log?: LoggerLike; }): Promise { const { translatedBody, modelToCall, provider, targetFormat, credentials, bypassDefaultToolLimit = false, log, } = opts; let bodyToSend: Body = translatedBody.model === modelToCall ? translatedBody : { ...translatedBody, model: modelToCall }; const payloadRuleModel = typeof bodyToSend.model === "string" && bodyToSend.model.length > 0 ? bodyToSend.model : modelToCall; const payloadRuleProtocols = resolvePayloadRuleProtocols({ provider, targetFormat }); const payloadRuleResult = await applyConfiguredPayloadRules( bodyToSend, payloadRuleModel, payloadRuleProtocols ); bodyToSend = payloadRuleResult.payload; if (payloadRuleResult.applied.length > 0) { log?.debug?.( "PAYLOAD_RULES", `Applied ${payloadRuleResult.applied.length} rule(s) for ${payloadRuleModel} (${payloadRuleProtocols.join(", ")}): ${buildAppliedRulesSummary(payloadRuleResult.applied)}` ); } bodyToSend = truncateToolList(bodyToSend, provider, bypassDefaultToolLimit ?? false, log); bodyToSend = backfillQwenOAuthUser(bodyToSend, provider, credentials, log); bodyToSend = await injectPromptCacheKey(bodyToSend, provider, targetFormat); return bodyToSend; }