fix(coding-agent): refresh model catalogs in picker
This commit is contained in:
@@ -11,11 +11,11 @@ function wrap(runtime: ModelRuntime): ModelRegistry {
|
||||
}
|
||||
|
||||
export async function createModelRegistry(credentials: CredentialStore, modelsPath?: string): Promise<ModelRegistry> {
|
||||
return wrap(await ModelRuntime.create({ credentials, modelsPath }));
|
||||
return wrap(await ModelRuntime.create({ credentials, modelsPath, allowModelNetwork: false }));
|
||||
}
|
||||
|
||||
export async function createInMemoryModelRegistry(credentials: CredentialStore): Promise<ModelRegistry> {
|
||||
return wrap(await ModelRuntime.create({ credentials, modelsPath: null }));
|
||||
return wrap(await ModelRuntime.create({ credentials, modelsPath: null, allowModelNetwork: false }));
|
||||
}
|
||||
|
||||
export function getModelRuntime(modelRegistry: ModelRegistry): ModelRuntime {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { createProvider, InMemoryModelsStore, type Model } from "@earendil-works/pi-ai";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { withRemoteCatalog } from "../src/core/remote-catalog-provider.ts";
|
||||
|
||||
function model(id: string): Model<"openai-completions"> {
|
||||
return {
|
||||
id,
|
||||
name: id,
|
||||
api: "openai-completions",
|
||||
provider: "test-provider",
|
||||
baseUrl: "https://example.test/v1",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1000,
|
||||
maxTokens: 100,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
describe("remote catalog provider", () => {
|
||||
it("parses catalogs keyed by model ID", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
new Response(JSON.stringify({ dynamic: model("dynamic") }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
);
|
||||
const provider = withRemoteCatalog(
|
||||
createProvider({
|
||||
id: "test-provider",
|
||||
auth: { apiKey: { name: "Test", resolve: async () => ({ auth: {} }) } },
|
||||
models: [model("static")],
|
||||
api: {
|
||||
stream: () => {
|
||||
throw new Error("not used");
|
||||
},
|
||||
streamSimple: () => {
|
||||
throw new Error("not used");
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const store = new InMemoryModelsStore();
|
||||
await provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (models) => store.write(provider.id, models),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
allowNetwork: true,
|
||||
});
|
||||
|
||||
expect(provider.getModels().map((entry) => entry.id)).toEqual(["static", "dynamic"]);
|
||||
expect((await store.read(provider.id))?.map((entry) => entry.id)).toEqual(["dynamic"]);
|
||||
});
|
||||
|
||||
it("treats unimplemented pi.dev catalog routes as an unavailable overlay", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("not implemented", { status: 501 }));
|
||||
const provider = withRemoteCatalog(
|
||||
createProvider({
|
||||
id: "test-provider",
|
||||
auth: { apiKey: { name: "Test", resolve: async () => ({ auth: {} }) } },
|
||||
models: [model("static")],
|
||||
api: {
|
||||
stream: () => {
|
||||
throw new Error("not used");
|
||||
},
|
||||
streamSimple: () => {
|
||||
throw new Error("not used");
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const store = new InMemoryModelsStore();
|
||||
|
||||
await expect(
|
||||
provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (models) => store.write(provider.id, models),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
allowNetwork: true,
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
expect(provider.getModels().map((entry) => entry.id)).toEqual(["static"]);
|
||||
expect(await store.read(provider.id)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -86,7 +86,9 @@ describe("issue #3217 scoped model ordering", () => {
|
||||
);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(stripAnsi(selector.render(120).join("\n"))).toContain(`[${modelOne.provider}]`);
|
||||
const rendered = stripAnsi(selector.render(120).join("\n"));
|
||||
expect(rendered).toContain(`[${modelOne.provider}]`);
|
||||
expect(rendered).toContain("Model catalogs refreshed.");
|
||||
});
|
||||
|
||||
const renderedLines = stripAnsi(selector.render(120).join("\n"))
|
||||
|
||||
Reference in New Issue
Block a user