fix(catalog): invalidated cached copilot grok route

Drop cached grok-4.5 Chat Completions rows when the bundled Copilot catalog fingerprint changes, matching the existing MAI endpoint migration.

Cover both cached endpoint migrations through the model manager's default online-if-uncached path.

Fixes #7096
This commit is contained in:
roboomp
2026-07-30 15:32:16 +00:00
parent 4819341053
commit 1e3ac8f69d
2 changed files with 48 additions and 43 deletions
@@ -333,50 +333,55 @@ describe("github copilot model limits mapping", () => {
expect(model).toBeDefined();
expect(model?.api).toBe("openai-responses");
});
it("invalidates a cached MAI-Code completion route after the endpoint migration", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-ai-copilot-mai-cache-"));
const cacheDbPath = path.join(tempDir, "models.db");
const cacheProviderId = "github-copilot-mai-cache-test";
try {
const oldManager = createModelManager({
providerId: "github-copilot",
cacheProviderId,
cacheDbPath,
staticModels: [],
fetchDynamicModels: async () => [
{
id: "mai-code-1-flash-picker",
name: "MAI-Code-1-Flash",
api: "openai-completions" as const,
provider: "github-copilot",
baseUrl: "https://api.githubcopilot.com",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 256_000,
maxTokens: 128_000,
},
],
});
await oldManager.refresh("online");
for (const migration of [
{ id: "mai-code-1-flash-picker", name: "MAI-Code-1-Flash", expectedApi: "openai-responses" },
{ id: "grok-4.5", name: "Grok 4.5", expectedApi: undefined },
]) {
it(`invalidates a cached ${migration.name} completion route after the endpoint migration`, async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), `pi-ai-copilot-${migration.id}-cache-`));
const cacheDbPath = path.join(tempDir, "models.db");
const cacheProviderId = `github-copilot-${migration.id}-cache-test`;
try {
const oldManager = createModelManager({
providerId: "github-copilot",
cacheProviderId,
cacheDbPath,
staticModels: [],
fetchDynamicModels: async () => [
{
id: migration.id,
name: migration.name,
api: "openai-completions" as const,
provider: "github-copilot",
baseUrl: "https://api.githubcopilot.com",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 256_000,
maxTokens: 128_000,
},
],
});
await oldManager.refresh("online");
const fetchMock = vi.fn(async () => {
throw new Error("a fresh cache must avoid discovery");
});
const manager = createModelManager({
...githubCopilotModelManagerOptions({ apiKey: "copilot-test-key", fetch: fetchMock }),
cacheProviderId,
cacheDbPath,
});
const { models } = await manager.refresh("online-if-uncached");
const model = models.find(candidate => candidate.id === "mai-code-1-flash-picker");
const fetchMock = vi.fn(async () => {
throw new Error("a fresh cache must avoid discovery");
});
const manager = createModelManager({
...githubCopilotModelManagerOptions({ apiKey: "copilot-test-key", fetch: fetchMock }),
cacheProviderId,
cacheDbPath,
});
const { models } = await manager.refresh("online-if-uncached");
const model = models.find(candidate => candidate.id === migration.id);
expect(fetchMock).not.toHaveBeenCalled();
expect(model?.api).toBe("openai-responses");
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
expect(fetchMock).not.toHaveBeenCalled();
expect(model?.api).toBe(migration.expectedApi);
} finally {
await fs.rm(tempDir, { recursive: true, force: true });
}
});
}
});
/**