openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
139 lines (138 loc) • 5.94 kB
JavaScript
import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.js";
import { s as buildBaseOptions } from "./sanitize-unicode-CgzZKr22.js";
import { n as getEnvApiKey } from "./env-api-keys-8q9bEA0v.js";
import { i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-zVCJiu45.js";
import { t as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-Bx1f-UHU.js";
import { a as resolveResponsesReasoningEffort, n as convertResponsesMessages, o as runResponsesStreamLifecycle, r as createResponsesAssistantOutput, t as applyCommonResponsesParams } from "./openai-responses-shared-C7frbROV.js";
import OpenAI from "openai";
//#region src/llm/providers/openai-responses.ts
const OPENAI_TOOL_CALL_PROVIDERS = new Set(["openai", "opencode"]);
/**
* Resolve cache retention preference.
* Defaults to "short" and uses OPENCLAW_CACHE_RETENTION for backward compatibility.
*/
function resolveCacheRetention(cacheRetention) {
if (cacheRetention) return cacheRetention;
if (typeof process !== "undefined" && process.env.OPENCLAW_CACHE_RETENTION === "long") return "long";
return "short";
}
function getCompat(model) {
return {
sendSessionIdHeader: model.compat?.sendSessionIdHeader ?? true,
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true
};
}
function getPromptCacheRetention(compat, cacheRetention) {
return cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : void 0;
}
function formatOpenAIResponsesError(error) {
if (error instanceof Error) {
const status = error.status;
const statusCode = typeof status === "number" ? status : void 0;
if (statusCode !== void 0) return `OpenAI API error (${statusCode}): ${error.message}`;
return error.message;
}
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
/**
* Generate function for OpenAI Responses API
*/
const streamOpenAIResponses = (model, context, options) => {
const stream = new AssistantMessageEventStream();
runResponsesStreamLifecycle({
stream,
model,
output: createResponsesAssistantOutput(model),
options,
createClient: () => {
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
const cacheSessionId = resolveCacheRetention(options?.cacheRetention) === "none" ? void 0 : options?.sessionId;
return createClient(model, context, apiKey, options?.headers, cacheSessionId);
},
buildParams: () => buildParams(model, context, options),
processStreamOptions: {
serviceTier: options?.serviceTier,
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
},
formatError: formatOpenAIResponsesError
});
return stream;
};
const streamSimpleOpenAIResponses = (model, context, options) => {
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
return streamOpenAIResponses(model, context, {
...buildBaseOptions(model, options, apiKey),
reasoningEffort: resolveResponsesReasoningEffort(model, options?.reasoning),
replayResponsesItemIds: options?.replayResponsesItemIds
});
};
function createClient(model, context, apiKey, optionsHeaders, sessionId) {
if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
const compat = getCompat(model);
const headers = { ...model.headers };
if (model.provider === "github-copilot") {
const hasImages = hasCopilotVisionInput(context.messages);
const copilotHeaders = buildCopilotDynamicHeaders({
messages: context.messages,
hasImages
});
Object.assign(headers, copilotHeaders);
}
if (sessionId) {
if (compat.sendSessionIdHeader) headers.session_id = sessionId;
headers["x-client-request-id"] = sessionId;
}
if (optionsHeaders) Object.assign(headers, optionsHeaders);
const defaultHeaders = model.provider === "cloudflare-ai-gateway" ? {
...headers,
Authorization: headers.Authorization ?? null,
"cf-aig-authorization": `Bearer ${apiKey}`
} : headers;
return new OpenAI({
apiKey,
baseURL: isCloudflareProvider(model.provider) ? resolveCloudflareBaseUrl(model) : model.baseUrl,
dangerouslyAllowBrowser: true,
defaultHeaders
});
}
function buildParams(model, context, options) {
const messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS, { replayResponsesItemIds: options?.replayResponsesItemIds ?? false });
const cacheRetention = resolveCacheRetention(options?.cacheRetention);
const compat = getCompat(model);
const params = {
model: model.id,
input: messages,
stream: true,
prompt_cache_key: cacheRetention === "none" ? void 0 : clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId),
prompt_cache_retention: getPromptCacheRetention(compat, cacheRetention),
store: false
};
if (options?.maxTokens) params.max_output_tokens = options?.maxTokens;
if (options?.temperature !== void 0) params.temperature = options?.temperature;
if (options?.serviceTier !== void 0) params.service_tier = options.serviceTier;
applyCommonResponsesParams(params, model, context, options, { setDefaultReasoningOff: model.provider !== "github-copilot" });
return params;
}
function getServiceTierCostMultiplier(model, serviceTier) {
switch (serviceTier) {
case "flex": return .5;
case "priority": return model.id === "gpt-5.5" ? 2.5 : 2;
default: return 1;
}
}
function applyServiceTierPricing(usage, serviceTier, model) {
const multiplier = getServiceTierCostMultiplier(model, serviceTier);
if (multiplier === 1) return;
usage.cost.input *= multiplier;
usage.cost.output *= multiplier;
usage.cost.cacheRead *= multiplier;
usage.cost.cacheWrite *= multiplier;
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
}
//#endregion
export { streamOpenAIResponses, streamSimpleOpenAIResponses };