Files
openclaw/src/auto-reply/model-runtime.ts
2026-04-08 00:09:42 +01:00

98 lines
2.9 KiB
TypeScript

import type { SessionEntry } from "../config/sessions.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "../shared/string-coerce.js";
export function formatProviderModelRef(providerRaw: string, modelRaw: string): string {
const provider = normalizeOptionalString(providerRaw) ?? "";
const model = normalizeOptionalString(modelRaw) ?? "";
if (!provider) {
return model;
}
if (!model) {
return provider;
}
const prefix = `${provider}/`;
if (normalizeLowercaseStringOrEmpty(model).startsWith(normalizeLowercaseStringOrEmpty(prefix))) {
const normalizedModel = model.slice(prefix.length).trim();
if (normalizedModel) {
return `${provider}/${normalizedModel}`;
}
}
return `${provider}/${model}`;
}
type ModelRef = {
provider: string;
model: string;
label: string;
};
function normalizeModelWithinProvider(provider: string, modelRaw: string): string {
const model = normalizeOptionalString(modelRaw) ?? "";
if (!provider || !model) {
return model;
}
const prefix = `${provider}/`;
if (normalizeLowercaseStringOrEmpty(model).startsWith(normalizeLowercaseStringOrEmpty(prefix))) {
const withoutPrefix = model.slice(prefix.length).trim();
if (withoutPrefix) {
return withoutPrefix;
}
}
return model;
}
function normalizeModelRef(
rawModel: string,
fallbackProvider: string,
parseEmbeddedProvider = false,
): ModelRef {
const trimmed = normalizeOptionalString(rawModel) ?? "";
const slashIndex = parseEmbeddedProvider ? trimmed.indexOf("/") : -1;
if (slashIndex > 0) {
const provider = normalizeOptionalString(trimmed.slice(0, slashIndex)) ?? "";
const model = normalizeOptionalString(trimmed.slice(slashIndex + 1)) ?? "";
if (provider && model) {
return {
provider,
model,
label: `${provider}/${model}`,
};
}
}
const provider = normalizeOptionalString(fallbackProvider) ?? "";
const dedupedModel = normalizeModelWithinProvider(provider, trimmed);
return {
provider,
model: dedupedModel || trimmed,
label: provider ? formatProviderModelRef(provider, dedupedModel || trimmed) : trimmed,
};
}
export function resolveSelectedAndActiveModel(params: {
selectedProvider: string;
selectedModel: string;
sessionEntry?: Pick<SessionEntry, "modelProvider" | "model">;
}): {
selected: ModelRef;
active: ModelRef;
activeDiffers: boolean;
} {
const selected = normalizeModelRef(params.selectedModel, params.selectedProvider);
const runtimeModel = normalizeOptionalString(params.sessionEntry?.model);
const runtimeProvider = normalizeOptionalString(params.sessionEntry?.modelProvider);
const active = runtimeModel
? normalizeModelRef(runtimeModel, runtimeProvider || selected.provider, !runtimeProvider)
: selected;
const activeDiffers = active.provider !== selected.provider || active.model !== selected.model;
return {
selected,
active,
activeDiffers,
};
}