mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
46bdbe585f
Co-authored-by: Amp <amp@ampcode.com>
150 lines
5.3 KiB
TypeScript
150 lines
5.3 KiB
TypeScript
import type { EmbeddedRunAttemptParamsV2 as EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
import {
|
|
resolveCodexAppServerReasoningEffort,
|
|
type CodexReasoningEffort,
|
|
} from "./reasoning-effort.js";
|
|
import {
|
|
isCodexAppServerNativeAuthProfile,
|
|
type CodexAppServerAuthProfileLookup,
|
|
type CodexAppServerThreadBinding,
|
|
} from "./session-binding.js";
|
|
|
|
export const CODEX_NATIVE_PERSONALITY_NONE = "none";
|
|
|
|
export function resolveCodexBindingModelProviderFallback(params: {
|
|
provider?: string;
|
|
currentModel: string | undefined;
|
|
bindingModel: string | undefined;
|
|
bindingModelProvider: string | undefined;
|
|
}): string | undefined {
|
|
const provider = params.provider?.trim().toLowerCase();
|
|
if (provider && provider !== "codex") {
|
|
return undefined;
|
|
}
|
|
const currentModel = params.currentModel?.trim();
|
|
const bindingModel = params.bindingModel?.trim();
|
|
if (
|
|
currentModel &&
|
|
bindingModel &&
|
|
currentModel === bindingModel &&
|
|
params.bindingModelProvider
|
|
) {
|
|
return params.bindingModelProvider;
|
|
}
|
|
return hasProviderQualifiedModelRef(currentModel) ? undefined : params.bindingModelProvider;
|
|
}
|
|
|
|
export function resolveCodexAppServerThreadModelSelection(params: {
|
|
provider: string;
|
|
model: string;
|
|
binding?: Pick<
|
|
CodexAppServerThreadBinding,
|
|
"threadId" | "authProfileId" | "model" | "modelProvider"
|
|
>;
|
|
authProfileId?: string;
|
|
authProfileStore?: CodexAppServerAuthProfileLookup["authProfileStore"];
|
|
agentDir?: string;
|
|
config?: CodexAppServerAuthProfileLookup["config"];
|
|
}): { model: string; modelProvider?: string } {
|
|
const authProfileId = params.authProfileId ?? params.binding?.authProfileId;
|
|
const explicitModelProvider = resolveCodexAppServerModelProvider({
|
|
provider: params.provider,
|
|
authProfileId,
|
|
authProfileStore: params.authProfileStore,
|
|
agentDir: params.agentDir,
|
|
config: params.config,
|
|
});
|
|
const bindingModelProvider = params.binding?.threadId
|
|
? resolveCodexBindingModelProviderFallback({
|
|
provider: params.provider,
|
|
currentModel: params.model,
|
|
bindingModel: params.binding.model,
|
|
bindingModelProvider: params.binding.modelProvider,
|
|
})
|
|
: undefined;
|
|
return resolveCodexAppServerRequestModelSelection({
|
|
model: params.model,
|
|
modelProvider: explicitModelProvider ?? bindingModelProvider,
|
|
authProfileId,
|
|
authProfileStore: params.authProfileStore,
|
|
agentDir: params.agentDir,
|
|
config: params.config,
|
|
});
|
|
}
|
|
|
|
export function resolveCodexAppServerRequestModelSelection(params: {
|
|
model: string;
|
|
modelProvider?: string | null;
|
|
authProfileId?: string;
|
|
authProfileStore?: CodexAppServerAuthProfileLookup["authProfileStore"];
|
|
agentDir?: string;
|
|
config?: CodexAppServerAuthProfileLookup["config"];
|
|
}): { model: string; modelProvider?: string } {
|
|
const model = params.model.trim();
|
|
const modelProvider = params.modelProvider?.trim();
|
|
if (modelProvider) {
|
|
return { model, modelProvider };
|
|
}
|
|
// Codex app-server expects provider-qualified refs as separate fields. Keep
|
|
// explicit providers intact so provider-owned slashy model ids are not split.
|
|
const slashIndex = model.indexOf("/");
|
|
if (slashIndex <= 0 || slashIndex >= model.length - 1) {
|
|
return { model };
|
|
}
|
|
const inferredProvider = model.slice(0, slashIndex);
|
|
const inferredModelProvider = resolveCodexAppServerModelProvider({
|
|
provider: inferredProvider,
|
|
authProfileId: params.authProfileId,
|
|
authProfileStore: params.authProfileStore,
|
|
agentDir: params.agentDir,
|
|
config: params.config,
|
|
});
|
|
return {
|
|
model: model.slice(slashIndex + 1).trim(),
|
|
...(inferredModelProvider ? { modelProvider: inferredModelProvider } : {}),
|
|
};
|
|
}
|
|
|
|
function hasProviderQualifiedModelRef(model: string | undefined): boolean {
|
|
const trimmed = model?.trim();
|
|
const slashIndex = trimmed?.indexOf("/") ?? -1;
|
|
return slashIndex > 0 && slashIndex < (trimmed?.length ?? 0) - 1;
|
|
}
|
|
|
|
export function resolveCodexAppServerModelProvider(params: {
|
|
provider: string;
|
|
authProfileId?: string;
|
|
authProfileStore?: CodexAppServerAuthProfileLookup["authProfileStore"];
|
|
agentDir?: string;
|
|
config?: CodexAppServerAuthProfileLookup["config"];
|
|
}): string | undefined {
|
|
const normalized = params.provider.trim();
|
|
const normalizedLower = normalized.toLowerCase();
|
|
if (!normalized || normalizedLower === "codex") {
|
|
// `codex` is OpenClaw's virtual provider; let Codex app-server keep its
|
|
// native provider/auth selection instead of forcing the legacy OpenAI path.
|
|
return undefined;
|
|
}
|
|
if (isCodexAppServerNativeAuthProfile(params) && normalizedLower === "openai") {
|
|
// When OpenClaw is forwarding ChatGPT/Codex OAuth, `openai` is Codex's
|
|
// native provider id, not a public OpenAI API-key choice. Omit the override
|
|
// so app-server keeps its configured provider/auth pair for this session.
|
|
return undefined;
|
|
}
|
|
return normalizedLower === "openai" ? "openai" : normalized;
|
|
}
|
|
|
|
// Provider catalog metadata owns model-specific effort support. Keeping this
|
|
// bridge model-agnostic prevents the picker and native runtime from drifting.
|
|
export function resolveReasoningEffort(
|
|
thinkLevel: EmbeddedRunAttemptParams["thinkLevel"] | "ultra",
|
|
modelId: string,
|
|
supportedReasoningEfforts?: readonly string[],
|
|
): CodexReasoningEffort | null {
|
|
return resolveCodexAppServerReasoningEffort({
|
|
thinkLevel,
|
|
modelId,
|
|
supportedReasoningEfforts,
|
|
});
|
|
}
|