mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-13 18:21:27 +00:00
111 lines
3.2 KiB
TypeScript
111 lines
3.2 KiB
TypeScript
import type {
|
|
ProviderResolveTransportTurnStateContext,
|
|
ProviderResolveWebSocketSessionPolicyContext,
|
|
ProviderTransportTurnState,
|
|
ProviderWebSocketSessionPolicy,
|
|
} from "openclaw/plugin-sdk/plugin-entry";
|
|
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|
import { isOpenAIApiBaseUrl, isOpenAICodexBaseUrl } from "./shared.js";
|
|
|
|
const DEFAULT_OPENAI_WS_DEGRADE_COOLDOWN_MS = 60_000;
|
|
const AZURE_PROVIDER_IDS = new Set(["azure-openai", "azure-openai-responses"]);
|
|
const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
|
|
|
|
function isAzureOpenAIBaseUrl(baseUrl?: string): boolean {
|
|
const trimmed = baseUrl?.trim();
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
try {
|
|
return new URL(trimmed).hostname.toLowerCase().endsWith(".openai.azure.com");
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function normalizeIdentityValue(value: string, maxLength = 160): string {
|
|
const trimmed = value.trim().replace(/[\r\n]+/g, " ");
|
|
return trimmed.length > maxLength ? trimmed.slice(0, maxLength) : trimmed;
|
|
}
|
|
|
|
function usesKnownNativeOpenAIRoute(provider: string, baseUrl?: string): boolean {
|
|
const normalizedProvider = normalizeProviderId(provider);
|
|
if (!normalizedProvider) {
|
|
return false;
|
|
}
|
|
if (normalizedProvider === "openai") {
|
|
return !baseUrl || isOpenAIApiBaseUrl(baseUrl);
|
|
}
|
|
if (AZURE_PROVIDER_IDS.has(normalizedProvider)) {
|
|
return !baseUrl || isAzureOpenAIBaseUrl(baseUrl);
|
|
}
|
|
if (normalizedProvider === OPENAI_CODEX_PROVIDER_ID) {
|
|
return !baseUrl || isOpenAIApiBaseUrl(baseUrl) || isOpenAICodexBaseUrl(baseUrl);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function resolveSessionHeaders(params: {
|
|
provider: string;
|
|
baseUrl?: string;
|
|
sessionId?: string;
|
|
}): Record<string, string> | undefined {
|
|
if (!params.sessionId || !usesKnownNativeOpenAIRoute(params.provider, params.baseUrl)) {
|
|
return undefined;
|
|
}
|
|
const sessionId = normalizeIdentityValue(params.sessionId);
|
|
if (!sessionId) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
"x-client-request-id": sessionId,
|
|
"x-openclaw-session-id": sessionId,
|
|
};
|
|
}
|
|
|
|
export function resolveOpenAITransportTurnState(
|
|
ctx: ProviderResolveTransportTurnStateContext,
|
|
): ProviderTransportTurnState | undefined {
|
|
const sessionHeaders = resolveSessionHeaders({
|
|
provider: ctx.provider,
|
|
baseUrl: ctx.model?.baseUrl,
|
|
sessionId: ctx.sessionId,
|
|
});
|
|
if (!sessionHeaders) {
|
|
return undefined;
|
|
}
|
|
|
|
const turnId = normalizeIdentityValue(ctx.turnId);
|
|
const attempt = String(Math.max(1, ctx.attempt));
|
|
|
|
return {
|
|
headers: {
|
|
...sessionHeaders,
|
|
"x-openclaw-turn-id": turnId,
|
|
"x-openclaw-turn-attempt": attempt,
|
|
},
|
|
metadata: {
|
|
openclaw_session_id: sessionHeaders["x-openclaw-session-id"] ?? "",
|
|
openclaw_turn_id: turnId,
|
|
openclaw_turn_attempt: attempt,
|
|
openclaw_transport: ctx.transport,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function resolveOpenAIWebSocketSessionPolicy(
|
|
ctx: ProviderResolveWebSocketSessionPolicyContext,
|
|
): ProviderWebSocketSessionPolicy | undefined {
|
|
if (!usesKnownNativeOpenAIRoute(ctx.provider, ctx.model?.baseUrl)) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
headers: resolveSessionHeaders({
|
|
provider: ctx.provider,
|
|
baseUrl: ctx.model?.baseUrl,
|
|
sessionId: ctx.sessionId,
|
|
}),
|
|
degradeCooldownMs: DEFAULT_OPENAI_WS_DEGRADE_COOLDOWN_MS,
|
|
};
|
|
}
|