2025-12-22 18:05:44 +01:00
|
|
|
// Lazy-load pi-coding-agent model metadata so we can infer context windows when
|
|
|
|
|
// the agent reports a model id. This includes custom models.json entries.
|
2025-12-05 22:33:09 +01:00
|
|
|
|
2025-12-23 02:48:48 +01:00
|
|
|
import { loadConfig } from "../config/config.js";
|
2026-01-30 03:15:10 +01:00
|
|
|
import { resolveOpenClawAgentDir } from "./agent-paths.js";
|
|
|
|
|
import { ensureOpenClawModelsJson } from "./models-config.js";
|
2025-12-23 02:48:48 +01:00
|
|
|
|
2025-12-05 22:33:09 +01:00
|
|
|
type ModelEntry = { id: string; contextWindow?: number };
|
2026-02-15 13:11:44 -08:00
|
|
|
type ConfigModelEntry = { id?: string; contextWindow?: number };
|
|
|
|
|
type ProviderConfigEntry = { models?: ConfigModelEntry[] };
|
|
|
|
|
type ModelsConfig = { providers?: Record<string, ProviderConfigEntry | undefined> };
|
|
|
|
|
|
|
|
|
|
export function applyConfiguredContextWindows(params: {
|
|
|
|
|
cache: Map<string, number>;
|
|
|
|
|
modelsConfig: ModelsConfig | undefined;
|
|
|
|
|
}) {
|
|
|
|
|
const providers = params.modelsConfig?.providers;
|
|
|
|
|
if (!providers || typeof providers !== "object") {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (const provider of Object.values(providers)) {
|
|
|
|
|
if (!Array.isArray(provider?.models)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (const model of provider.models) {
|
|
|
|
|
const modelId = typeof model?.id === "string" ? model.id : undefined;
|
|
|
|
|
const contextWindow =
|
|
|
|
|
typeof model?.contextWindow === "number" ? model.contextWindow : undefined;
|
|
|
|
|
if (!modelId || !contextWindow || contextWindow <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
params.cache.set(modelId, contextWindow);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-05 22:33:09 +01:00
|
|
|
|
|
|
|
|
const MODEL_CACHE = new Map<string, number>();
|
|
|
|
|
const loadPromise = (async () => {
|
2026-02-15 13:11:44 -08:00
|
|
|
let cfg: ReturnType<typeof loadConfig> | undefined;
|
|
|
|
|
try {
|
|
|
|
|
cfg = loadConfig();
|
|
|
|
|
} catch {
|
|
|
|
|
// If config can't be loaded, leave cache empty.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-05 22:33:09 +01:00
|
|
|
try {
|
2026-01-30 03:15:10 +01:00
|
|
|
await ensureOpenClawModelsJson(cfg);
|
2026-02-15 13:11:44 -08:00
|
|
|
} catch {
|
|
|
|
|
// Continue with best-effort discovery/overrides.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const { discoverAuthStorage, discoverModels } = await import("./pi-model-discovery.js");
|
2026-01-30 03:15:10 +01:00
|
|
|
const agentDir = resolveOpenClawAgentDir();
|
2026-01-31 06:40:45 +01:00
|
|
|
const authStorage = discoverAuthStorage(agentDir);
|
|
|
|
|
const modelRegistry = discoverModels(authStorage, agentDir);
|
2025-12-26 11:49:13 +01:00
|
|
|
const models = modelRegistry.getAll() as ModelEntry[];
|
2025-12-22 18:05:44 +01:00
|
|
|
for (const m of models) {
|
2026-01-31 16:19:20 +09:00
|
|
|
if (!m?.id) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-12-22 18:05:44 +01:00
|
|
|
if (typeof m.contextWindow === "number" && m.contextWindow > 0) {
|
|
|
|
|
MODEL_CACHE.set(m.id, m.contextWindow);
|
2025-12-05 22:33:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
2026-02-15 13:11:44 -08:00
|
|
|
// If model discovery fails, continue with config overrides only.
|
2025-12-05 22:33:09 +01:00
|
|
|
}
|
2026-02-15 13:11:44 -08:00
|
|
|
|
|
|
|
|
applyConfiguredContextWindows({
|
|
|
|
|
cache: MODEL_CACHE,
|
|
|
|
|
modelsConfig: cfg.models as ModelsConfig | undefined,
|
|
|
|
|
});
|
|
|
|
|
})().catch(() => {
|
|
|
|
|
// Keep lookup best-effort.
|
|
|
|
|
});
|
2025-12-05 22:33:09 +01:00
|
|
|
|
|
|
|
|
export function lookupContextTokens(modelId?: string): number | undefined {
|
2026-01-31 16:19:20 +09:00
|
|
|
if (!modelId) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
2025-12-05 22:33:09 +01:00
|
|
|
// Best-effort: kick off loading, but don't block.
|
|
|
|
|
void loadPromise;
|
|
|
|
|
return MODEL_CACHE.get(modelId);
|
|
|
|
|
}
|