chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { InMemoryCredentialStore } from "../src/auth/credential-store.ts";
|
||||
import type { ApiKeyAuth, CredentialStore, OAuthAuth, OAuthCredential, ProviderAuth } from "../src/auth/types.ts";
|
||||
import type { ApiKeyAuth, CredentialStore, OAuthAuth, ProviderAuth } from "../src/auth/types.ts";
|
||||
import { calculateCost, createModels, createProvider, hasApi, type Provider } from "../src/models.ts";
|
||||
import { InMemoryModelsStore, type ModelsStore, type ModelsStoreEntry } from "../src/models-store.ts";
|
||||
import type { Api, AssistantMessage, Context, Model, SimpleStreamOptions, StreamOptions, Usage } from "../src/types.ts";
|
||||
@@ -198,6 +198,20 @@ describe("Models runtime", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps chat reads independent from the all-model catalog", async () => {
|
||||
const provider = testProvider({ id: "chat-only" });
|
||||
provider.getAllModels = () => {
|
||||
throw new Error("all models unavailable");
|
||||
};
|
||||
const models = createModels();
|
||||
models.setProvider(provider);
|
||||
|
||||
expect(models.getModels("chat-only").map((model) => model.id)).toEqual(["model-a"]);
|
||||
expect(models.getModel("chat-only", "model-a")?.id).toBe("model-a");
|
||||
expect((await models.getAvailable("chat-only")).map((model) => model.id)).toEqual(["model-a"]);
|
||||
expect(models.getAllModels("chat-only")).toEqual([]);
|
||||
});
|
||||
|
||||
it("swallows provider source failures for both all-provider and single-provider listing", () => {
|
||||
const models = createModels();
|
||||
models.setProvider(
|
||||
@@ -425,6 +439,26 @@ describe("Models runtime", () => {
|
||||
expect(unconfiguredRefreshes).toBe(0);
|
||||
});
|
||||
|
||||
it("passes the registered provider's base URL, including wrapper overrides, to refreshModels", async () => {
|
||||
// A models.json baseUrl (e.g. a regional OpenRouter host) is applied by a wrapper around the
|
||||
// built-in provider. The built-in refresh must still request that URL.
|
||||
const baseUrls: (string | undefined)[] = [];
|
||||
const inner: Provider = {
|
||||
...testProvider({
|
||||
id: "dynamic",
|
||||
refreshModels: async (context) => {
|
||||
baseUrls.push(context.baseUrl);
|
||||
},
|
||||
}),
|
||||
baseUrl: "https://default.example.test/v1",
|
||||
};
|
||||
const models = createModels();
|
||||
models.setProvider({ ...inner, baseUrl: "https://regional.example.test/v1" });
|
||||
|
||||
await models.refresh();
|
||||
expect(baseUrls).toEqual(["https://regional.example.test/v1", "https://regional.example.test/v1"]);
|
||||
});
|
||||
|
||||
it("refreshes expired OAuth before refreshing models", async () => {
|
||||
const credentials = new InMemoryCredentialStore();
|
||||
let modelRefreshCredential: unknown;
|
||||
@@ -459,6 +493,37 @@ describe("Models runtime", () => {
|
||||
expect(await credentials.read("oauth-dynamic")).toMatchObject({ access: "fresh", refresh: "rotated" });
|
||||
});
|
||||
|
||||
it("persists an OAuth refresh that started before the model refresh was cancelled", async () => {
|
||||
const credentials = new InMemoryCredentialStore();
|
||||
await credentials.modify("p1", async () => ({
|
||||
type: "oauth",
|
||||
access: "old",
|
||||
refresh: "old-refresh",
|
||||
expires: 0,
|
||||
}));
|
||||
const controller = new AbortController();
|
||||
const models = createModels({ credentials });
|
||||
models.setProvider(
|
||||
testProvider({
|
||||
id: "p1",
|
||||
auth: {
|
||||
oauth: testOAuth({
|
||||
refresh: async (credential) => {
|
||||
controller.abort();
|
||||
return { ...credential, access: "new", refresh: "new-refresh", expires: Date.now() + 60_000 };
|
||||
},
|
||||
}),
|
||||
},
|
||||
refreshModels: async () => {},
|
||||
}),
|
||||
);
|
||||
|
||||
expect((await models.refresh({ signal: controller.signal })).aborted).toBe(true);
|
||||
await vi.waitFor(async () => {
|
||||
expect(await credentials.read("p1")).toMatchObject({ refresh: "new-refresh" });
|
||||
});
|
||||
});
|
||||
|
||||
it("always gives providers a concrete signal", async () => {
|
||||
let receivedSignal: AbortSignal | undefined;
|
||||
const models = createModels();
|
||||
@@ -732,46 +797,36 @@ describe("Models runtime", () => {
|
||||
expect(await credentials.read("p1")).toEqual({ type: "api_key", key: "first" });
|
||||
});
|
||||
|
||||
it("passes cancellation to OAuth refresh and preserves the previous credential", async () => {
|
||||
// Radius bug report 01a10855-43d8-7447-a710-2acf5ee0b2ee: refresh_token_invalidated after a cancelled refresh.
|
||||
it("persists an OAuth refresh that started before the request was cancelled", async () => {
|
||||
const credentials = new InMemoryCredentialStore();
|
||||
const previous: OAuthCredential = { type: "oauth", access: "old", refresh: "old-refresh", expires: 0 };
|
||||
await credentials.modify("p1", async () => previous);
|
||||
let startRefresh: (() => void) | undefined;
|
||||
let finishRefresh: ((credential: typeof previous) => void) | undefined;
|
||||
const refreshStarted = new Promise<void>((resolve) => {
|
||||
startRefresh = resolve;
|
||||
});
|
||||
const blockedRefresh = new Promise<typeof previous>((resolve) => {
|
||||
finishRefresh = resolve;
|
||||
});
|
||||
let receivedSignal: AbortSignal | undefined;
|
||||
await credentials.modify("p1", async () => ({
|
||||
type: "oauth",
|
||||
access: "old",
|
||||
refresh: "old-refresh",
|
||||
expires: 0,
|
||||
}));
|
||||
const controller = new AbortController();
|
||||
const models = createModels({ credentials });
|
||||
models.setProvider(
|
||||
testProvider({
|
||||
id: "p1",
|
||||
auth: {
|
||||
oauth: testOAuth({
|
||||
refresh: async (_credential, signal) => {
|
||||
receivedSignal = signal;
|
||||
startRefresh?.();
|
||||
return blockedRefresh;
|
||||
refresh: async (credential) => {
|
||||
// The provider has rotated old-refresh by the time the request is cancelled.
|
||||
controller.abort();
|
||||
return { ...credential, access: "new", refresh: "new-refresh", expires: Date.now() + 60_000 };
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
);
|
||||
const controller = new AbortController();
|
||||
const auth = models.getAuth("p1", { signal: controller.signal });
|
||||
await refreshStarted;
|
||||
controller.abort();
|
||||
|
||||
await expect(auth).rejects.toMatchObject({ name: "AbortError" });
|
||||
expect(receivedSignal).toBeInstanceOf(AbortSignal);
|
||||
expect(receivedSignal?.aborted).toBe(true);
|
||||
expect(receivedSignal?.reason).toBe(controller.signal.reason);
|
||||
finishRefresh?.({ ...previous, access: "new", expires: Date.now() + 60_000 });
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(await credentials.read("p1")).toEqual(previous);
|
||||
await expect(models.getAuth("p1", { signal: controller.signal })).rejects.toMatchObject({ name: "AbortError" });
|
||||
await vi.waitFor(async () => {
|
||||
expect(await credentials.read("p1")).toMatchObject({ refresh: "new-refresh" });
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves auth: stored credential owns the provider, ambient only when nothing stored", async () => {
|
||||
|
||||
Reference in New Issue
Block a user