mirror of https://github.com/openclaw/openclaw.git
230 lines
6.8 KiB
TypeScript
230 lines
6.8 KiB
TypeScript
import { DEFAULT_CONTEXT_TOKENS, DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
|
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
|
|
import type { SessionEntry } from "../config/sessions/types.js";
|
|
import type { OpenClawConfig } from "../config/types.js";
|
|
|
|
function parseStatusModelRef(
|
|
raw: string,
|
|
defaultProvider: string,
|
|
): { provider: string; model: string } | null {
|
|
const trimmed = raw.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
const slash = trimmed.indexOf("/");
|
|
if (slash === -1) {
|
|
return { provider: defaultProvider, model: trimmed };
|
|
}
|
|
const provider = trimmed.slice(0, slash).trim();
|
|
const model = trimmed.slice(slash + 1).trim();
|
|
if (!provider || !model) {
|
|
return null;
|
|
}
|
|
return { provider, model };
|
|
}
|
|
|
|
function resolveStatusModelRefFromRaw(params: {
|
|
cfg: OpenClawConfig;
|
|
rawModel: string;
|
|
defaultProvider: string;
|
|
}): { provider: string; model: string } | null {
|
|
const trimmed = params.rawModel.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
const configuredModels = params.cfg.agents?.defaults?.models ?? {};
|
|
if (!trimmed.includes("/")) {
|
|
const aliasKey = trimmed.toLowerCase();
|
|
for (const [modelKey, entry] of Object.entries(configuredModels)) {
|
|
const aliasValue = (entry as { alias?: unknown } | undefined)?.alias;
|
|
const alias = typeof aliasValue === "string" ? aliasValue.trim() : "";
|
|
if (!alias || alias.toLowerCase() !== aliasKey) {
|
|
continue;
|
|
}
|
|
const parsed = parseStatusModelRef(modelKey, params.defaultProvider);
|
|
if (parsed) {
|
|
return parsed;
|
|
}
|
|
}
|
|
return { provider: "anthropic", model: trimmed };
|
|
}
|
|
return parseStatusModelRef(trimmed, params.defaultProvider);
|
|
}
|
|
|
|
function resolveConfiguredStatusModelRef(params: {
|
|
cfg: OpenClawConfig;
|
|
defaultProvider: string;
|
|
defaultModel: string;
|
|
agentId?: string;
|
|
}): { provider: string; model: string } {
|
|
const agentRawModel = params.agentId
|
|
? resolveAgentModelPrimaryValue(
|
|
params.cfg.agents?.list?.find((entry) => entry?.id === params.agentId)?.model,
|
|
)
|
|
: undefined;
|
|
if (agentRawModel) {
|
|
const parsed = resolveStatusModelRefFromRaw({
|
|
cfg: params.cfg,
|
|
rawModel: agentRawModel,
|
|
defaultProvider: params.defaultProvider,
|
|
});
|
|
if (parsed) {
|
|
return parsed;
|
|
}
|
|
}
|
|
|
|
const defaultsRawModel = resolveAgentModelPrimaryValue(params.cfg.agents?.defaults?.model);
|
|
if (defaultsRawModel) {
|
|
const parsed = resolveStatusModelRefFromRaw({
|
|
cfg: params.cfg,
|
|
rawModel: defaultsRawModel,
|
|
defaultProvider: params.defaultProvider,
|
|
});
|
|
if (parsed) {
|
|
return parsed;
|
|
}
|
|
}
|
|
|
|
const configuredProviders = params.cfg.models?.providers;
|
|
if (configuredProviders && typeof configuredProviders === "object") {
|
|
const hasDefaultProvider = Boolean(configuredProviders[params.defaultProvider]);
|
|
if (!hasDefaultProvider) {
|
|
const availableProvider = Object.entries(configuredProviders).find(
|
|
([, providerCfg]) =>
|
|
providerCfg &&
|
|
Array.isArray(providerCfg.models) &&
|
|
providerCfg.models.length > 0 &&
|
|
providerCfg.models[0]?.id,
|
|
);
|
|
if (availableProvider) {
|
|
const [providerName, providerCfg] = availableProvider;
|
|
return { provider: providerName, model: providerCfg.models[0].id };
|
|
}
|
|
}
|
|
}
|
|
|
|
return { provider: params.defaultProvider, model: params.defaultModel };
|
|
}
|
|
|
|
function resolveConfiguredProviderContextWindow(
|
|
cfg: OpenClawConfig | undefined,
|
|
provider: string,
|
|
model: string,
|
|
): number | undefined {
|
|
const providers = cfg?.models?.providers;
|
|
if (!providers || typeof providers !== "object") {
|
|
return undefined;
|
|
}
|
|
const providerKey = provider.trim().toLowerCase();
|
|
for (const [id, providerConfig] of Object.entries(providers)) {
|
|
if (id.trim().toLowerCase() !== providerKey || !Array.isArray(providerConfig?.models)) {
|
|
continue;
|
|
}
|
|
for (const entry of providerConfig.models) {
|
|
if (
|
|
typeof entry?.id === "string" &&
|
|
entry.id === model &&
|
|
typeof entry.contextWindow === "number" &&
|
|
entry.contextWindow > 0
|
|
) {
|
|
return entry.contextWindow;
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function classifySessionKey(key: string, entry?: SessionEntry) {
|
|
if (key === "global") {
|
|
return "global";
|
|
}
|
|
if (key === "unknown") {
|
|
return "unknown";
|
|
}
|
|
if (entry?.chatType === "group" || entry?.chatType === "channel") {
|
|
return "group";
|
|
}
|
|
if (key.includes(":group:") || key.includes(":channel:")) {
|
|
return "group";
|
|
}
|
|
return "direct";
|
|
}
|
|
|
|
function resolveSessionModelRef(
|
|
cfg: OpenClawConfig,
|
|
entry?:
|
|
| SessionEntry
|
|
| Pick<SessionEntry, "model" | "modelProvider" | "modelOverride" | "providerOverride">,
|
|
agentId?: string,
|
|
): { provider: string; model: string } {
|
|
const resolved = resolveConfiguredStatusModelRef({
|
|
cfg,
|
|
defaultProvider: DEFAULT_PROVIDER,
|
|
defaultModel: DEFAULT_MODEL,
|
|
agentId,
|
|
});
|
|
|
|
let provider = resolved.provider;
|
|
let model = resolved.model;
|
|
const runtimeModel = entry?.model?.trim();
|
|
const runtimeProvider = entry?.modelProvider?.trim();
|
|
if (runtimeModel) {
|
|
if (runtimeProvider) {
|
|
return { provider: runtimeProvider, model: runtimeModel };
|
|
}
|
|
const parsedRuntime = parseStatusModelRef(runtimeModel, provider || DEFAULT_PROVIDER);
|
|
if (parsedRuntime) {
|
|
provider = parsedRuntime.provider;
|
|
model = parsedRuntime.model;
|
|
} else {
|
|
model = runtimeModel;
|
|
}
|
|
return { provider, model };
|
|
}
|
|
|
|
const storedModelOverride = entry?.modelOverride?.trim();
|
|
if (storedModelOverride) {
|
|
const overrideProvider = entry?.providerOverride?.trim() || provider || DEFAULT_PROVIDER;
|
|
const parsedOverride = parseStatusModelRef(storedModelOverride, overrideProvider);
|
|
if (parsedOverride) {
|
|
provider = parsedOverride.provider;
|
|
model = parsedOverride.model;
|
|
} else {
|
|
provider = overrideProvider;
|
|
model = storedModelOverride;
|
|
}
|
|
}
|
|
return { provider, model };
|
|
}
|
|
|
|
function resolveContextTokensForModel(params: {
|
|
cfg?: OpenClawConfig;
|
|
provider?: string;
|
|
model?: string;
|
|
contextTokensOverride?: number;
|
|
fallbackContextTokens?: number;
|
|
allowAsyncLoad?: boolean;
|
|
}): number | undefined {
|
|
void params.allowAsyncLoad;
|
|
if (typeof params.contextTokensOverride === "number" && params.contextTokensOverride > 0) {
|
|
return params.contextTokensOverride;
|
|
}
|
|
if (params.provider && params.model) {
|
|
const configuredWindow = resolveConfiguredProviderContextWindow(
|
|
params.cfg,
|
|
params.provider,
|
|
params.model,
|
|
);
|
|
if (configuredWindow !== undefined) {
|
|
return configuredWindow;
|
|
}
|
|
}
|
|
return params.fallbackContextTokens ?? DEFAULT_CONTEXT_TOKENS;
|
|
}
|
|
|
|
export const statusSummaryRuntime = {
|
|
resolveContextTokensForModel,
|
|
classifySessionKey,
|
|
resolveSessionModelRef,
|
|
};
|