fix: bound model catalog state cache

This commit is contained in:
Shakker
2026-06-09 16:52:48 +01:00
committed by Shakker
parent da7f9c51df
commit 61e93a800d
4 changed files with 39 additions and 17 deletions

View File

@@ -89,6 +89,38 @@ describe("model catalog state cache", () => {
).toBeUndefined();
});
it("prunes expired agent catalog rows on write", () => {
const expiredEntries = [{ provider: "openai", id: "gpt-5.5", name: "GPT-5.5" }];
writeCachedAgentModelCatalog({
agentDir: "/agent/main",
catalogKey: "expired-key",
entries: expiredEntries,
nowMs: 1_000,
});
writeCachedAgentModelCatalog({
agentDir: "/agent/main",
catalogKey: "fresh-key",
entries: [{ provider: "openai", id: "gpt-5.6", name: "GPT-5.6" }],
nowMs: 31 * 60 * 1_000,
});
expect(
readCachedAgentModelCatalog({
agentDir: "/agent/main",
catalogKey: "expired-key",
nowMs: 1_000,
}),
).toBeUndefined();
expect(
readCachedAgentModelCatalog({
agentDir: "/agent/main",
catalogKey: "fresh-key",
nowMs: 31 * 60 * 1_000,
}),
).toEqual([{ provider: "openai", id: "gpt-5.6", name: "GPT-5.6" }]);
});
it("builds stable keys that change with relevant catalog inputs", () => {
const base = buildAgentModelCatalogCacheKey({
agentDir: "/agent/main",

View File

@@ -136,6 +136,12 @@ export function writeCachedAgentModelCatalog(params: WriteCachedAgentModelCatalo
} satisfies CachedAgentModelCatalogPayload);
runOpenClawStateWriteTransaction((database) => {
const db = getNodeSqliteKysely<AgentModelCatalogDatabase>(database.db);
executeSqliteQuerySync(
database.db,
db
.deleteFrom("agent_model_catalogs")
.where("updated_at", "<", updatedAt - AGENT_MODEL_CATALOG_CACHE_TTL_MS),
);
executeSqliteQuerySync(
database.db,
db

View File

@@ -733,6 +733,7 @@ describe("loadModelCatalog", () => {
const entry = requireCatalogEntry(result, "openai", "gpt-test");
expect(entry.name).toBe("GPT Test");
expect(readCachedAgentModelCatalogMock).not.toHaveBeenCalled();
expect(prepareOpenClawModelsJsonSourceMock).not.toHaveBeenCalled();
expect(importAgentDiscoveryModule).not.toHaveBeenCalled();
expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled();

View File

@@ -389,23 +389,6 @@ async function loadReadOnlyPersistedModelCatalog(params?: {
manifestPlugins ??= getMetadataSnapshot().plugins;
return manifestPlugins;
};
const sourceFingerprint = await buildModelsJsonSourceFingerprint(cfg, agentDir, {
pluginMetadataSnapshot: params?.metadataSnapshot,
workspaceDir,
});
const cached = readCachedAgentModelCatalog({
agentDir,
catalogKey: buildLoadModelCatalogStateCacheKey({
agentDir,
config: cfg,
metadataSnapshot: params?.metadataSnapshot,
sourceFingerprint: sourceFingerprint.fingerprint,
workspaceDir,
}),
}) as ModelCatalogEntry[] | undefined;
if (cached?.length) {
return cached;
}
const providers = await loadReadOnlyPersistedProviderRows(agentDir, getMetadataSnapshot);
for (const [providerRaw, providerConfig] of Object.entries(providers)) {
if (!Array.isArray(providerConfig?.models)) {