Merge remote-tracking branch 'origin/main' into fix/issue-6647-retry-summary-requests-2
This commit is contained in:
@@ -220,7 +220,7 @@ describe("llama.cpp extension", () => {
|
||||
send({
|
||||
model: "owner/repo:Q4_K_M",
|
||||
event: "download_progress",
|
||||
data: { "https://example/model.gguf": { done: 512, total: 1024 } },
|
||||
data: { progress: { "https://example/model.gguf": { done: 512, total: 1024 } } },
|
||||
});
|
||||
status = "unloaded";
|
||||
send({ model: "owner/repo:Q4_K_M", event: "download_finished", data: {} });
|
||||
|
||||
@@ -652,7 +652,7 @@ describe("ModelRegistry", () => {
|
||||
expect(anthropicModels.some((m) => m.id === "claude-custom")).toBe(false);
|
||||
expect(anthropicModels.some((m) => m.id === "claude-custom-2")).toBe(true);
|
||||
expect(anthropicModels.some((m) => m.id.includes("claude"))).toBe(true);
|
||||
});
|
||||
}, 60_000);
|
||||
|
||||
test("removing custom models from models.json keeps built-in provider models", async () => {
|
||||
writeModelsJson({
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { createProvider, InMemoryModelsStore, type Model } from "@earendil-works/pi-ai";
|
||||
import { statSync } from "node:fs";
|
||||
import {
|
||||
createProvider,
|
||||
InMemoryModelsStore,
|
||||
type Model,
|
||||
type ModelsStoreEntry,
|
||||
type ProviderModelsStore,
|
||||
} from "@earendil-works/pi-ai";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { VERSION } from "../src/config.ts";
|
||||
import { withRemoteCatalog } from "../src/core/remote-catalog-provider.ts";
|
||||
@@ -18,6 +25,34 @@ function model(id: string): Model<"openai-completions"> {
|
||||
};
|
||||
}
|
||||
|
||||
function testProvider(localCatalogUrl?: URL) {
|
||||
return 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");
|
||||
},
|
||||
},
|
||||
}),
|
||||
"https://pi.dev",
|
||||
localCatalogUrl,
|
||||
);
|
||||
}
|
||||
|
||||
function scopedStore(store: InMemoryModelsStore): ProviderModelsStore {
|
||||
return {
|
||||
read: () => store.read("test-provider"),
|
||||
write: (entry: ModelsStoreEntry) => store.write("test-provider", entry),
|
||||
delete: () => store.delete("test-provider"),
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
describe("remote catalog provider", () => {
|
||||
@@ -29,50 +64,12 @@ describe("remote catalog provider", () => {
|
||||
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 provider = testProvider();
|
||||
const store = new InMemoryModelsStore();
|
||||
await provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (entry) => store.write(provider.id, entry),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
allowNetwork: true,
|
||||
});
|
||||
await provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (entry) => store.write(provider.id, entry),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
allowNetwork: true,
|
||||
});
|
||||
await provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (entry) => store.write(provider.id, entry),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
allowNetwork: true,
|
||||
force: true,
|
||||
});
|
||||
const refresh = { credential: { type: "api_key" } as const, store: scopedStore(store), allowNetwork: true };
|
||||
await provider.refreshModels?.(refresh);
|
||||
await provider.refreshModels?.(refresh);
|
||||
await provider.refreshModels?.({ ...refresh, force: true });
|
||||
|
||||
expect(provider.getModels().map((entry) => entry.id)).toEqual(["static", "dynamic"]);
|
||||
expect((await store.read(provider.id))?.models.map((entry) => entry.id)).toEqual(["dynamic"]);
|
||||
@@ -82,33 +79,40 @@ describe("remote catalog provider", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers the newer of the generated and remote catalogs", async () => {
|
||||
const localCatalogUrl = new URL(import.meta.url);
|
||||
const localMtime = statSync(localCatalogUrl).mtimeMs;
|
||||
const newerHeader = new Date(localMtime + 60_000).toUTCString();
|
||||
const responses = [
|
||||
new Response(JSON.stringify({ old: model("old") }), {
|
||||
headers: { "last-modified": new Date(localMtime - 60_000).toUTCString() },
|
||||
}),
|
||||
new Response(JSON.stringify({ newer: model("newer") }), {
|
||||
headers: { "last-modified": newerHeader },
|
||||
}),
|
||||
];
|
||||
vi.spyOn(globalThis, "fetch").mockImplementation(async () => responses.shift() as Response);
|
||||
const provider = testProvider(localCatalogUrl);
|
||||
const store = new InMemoryModelsStore();
|
||||
const refresh = { credential: { type: "api_key" } as const, store: scopedStore(store), allowNetwork: true };
|
||||
|
||||
await provider.refreshModels?.(refresh);
|
||||
expect(provider.getModels().map((entry) => entry.id)).toEqual(["static"]);
|
||||
|
||||
await provider.refreshModels?.({ ...refresh, force: true });
|
||||
expect(provider.getModels().map((entry) => entry.id)).toEqual(["static", "newer"]);
|
||||
expect(await store.read(provider.id)).toMatchObject({ lastModified: Date.parse(newerHeader) });
|
||||
});
|
||||
|
||||
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 provider = testProvider();
|
||||
const store = new InMemoryModelsStore();
|
||||
|
||||
await expect(
|
||||
provider.refreshModels?.({
|
||||
credential: { type: "api_key" },
|
||||
store: {
|
||||
read: () => store.read(provider.id),
|
||||
write: (entry) => store.write(provider.id, entry),
|
||||
delete: () => store.delete(provider.id),
|
||||
},
|
||||
store: scopedStore(store),
|
||||
allowNetwork: true,
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
Reference in New Issue
Block a user