fix(providers): prevent stale models during refresh
Provider settings sheets can remain mounted while hidden, so the old display cache could survive into a different provider. Scope the cache to the active provider identity and remount the sheet when that identity changes.
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { AgentModelDefinition } from "@getpaseo/protocol/agent-types";
|
||||||
|
import {
|
||||||
|
resolveProviderDiscoveredModels,
|
||||||
|
type ProviderDiscoveredModelsCache,
|
||||||
|
} from "./provider-diagnostic-models";
|
||||||
|
|
||||||
|
const piModel: AgentModelDefinition = {
|
||||||
|
provider: "pi",
|
||||||
|
id: "pi/model",
|
||||||
|
label: "Pi Model",
|
||||||
|
};
|
||||||
|
|
||||||
|
const grokModel: AgentModelDefinition = {
|
||||||
|
provider: "grok",
|
||||||
|
id: "grok-build",
|
||||||
|
label: "Grok Build",
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolveModels(input: {
|
||||||
|
serverId?: string;
|
||||||
|
provider: string;
|
||||||
|
currentModels?: AgentModelDefinition[];
|
||||||
|
loading?: boolean;
|
||||||
|
cache?: ProviderDiscoveredModelsCache | null;
|
||||||
|
}) {
|
||||||
|
return resolveProviderDiscoveredModels({
|
||||||
|
serverId: input.serverId ?? "local",
|
||||||
|
provider: input.provider,
|
||||||
|
currentModels: input.currentModels,
|
||||||
|
providerSnapshotRefreshing: input.loading === true,
|
||||||
|
previousCache: input.cache ?? null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("resolveProviderDiscoveredModels", () => {
|
||||||
|
it("keeps a provider's cached discovered models visible while that provider refreshes", () => {
|
||||||
|
const ready = resolveModels({ provider: "grok", currentModels: [grokModel] });
|
||||||
|
|
||||||
|
const refreshing = resolveModels({ provider: "grok", loading: true, cache: ready.cache });
|
||||||
|
|
||||||
|
expect(refreshing.models).toEqual([grokModel]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show one provider's cached models while another provider loads", () => {
|
||||||
|
const ready = resolveModels({ provider: "pi", currentModels: [piModel] });
|
||||||
|
|
||||||
|
const refreshing = resolveModels({ provider: "grok", loading: true, cache: ready.cache });
|
||||||
|
|
||||||
|
expect(refreshing.models).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show another server's cached models while the same provider loads", () => {
|
||||||
|
const ready = resolveModels({
|
||||||
|
serverId: "server-a",
|
||||||
|
provider: "grok",
|
||||||
|
currentModels: [grokModel],
|
||||||
|
});
|
||||||
|
|
||||||
|
const refreshing = resolveModels({
|
||||||
|
serverId: "server-b",
|
||||||
|
provider: "grok",
|
||||||
|
loading: true,
|
||||||
|
cache: ready.cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(refreshing.models).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { AgentModelDefinition } from "@getpaseo/protocol/agent-types";
|
||||||
|
|
||||||
|
export interface ProviderDiscoveredModelsCache {
|
||||||
|
serverId: string;
|
||||||
|
provider: string;
|
||||||
|
models: AgentModelDefinition[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolveProviderDiscoveredModelsInput {
|
||||||
|
serverId: string;
|
||||||
|
provider: string;
|
||||||
|
currentModels: AgentModelDefinition[] | undefined;
|
||||||
|
providerSnapshotRefreshing: boolean;
|
||||||
|
previousCache: ProviderDiscoveredModelsCache | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResolveProviderDiscoveredModelsResult {
|
||||||
|
models: AgentModelDefinition[];
|
||||||
|
cache: ProviderDiscoveredModelsCache | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveProviderDiscoveredModels({
|
||||||
|
serverId,
|
||||||
|
provider,
|
||||||
|
currentModels,
|
||||||
|
providerSnapshotRefreshing,
|
||||||
|
previousCache,
|
||||||
|
}: ResolveProviderDiscoveredModelsInput): ResolveProviderDiscoveredModelsResult {
|
||||||
|
if (currentModels && currentModels.length > 0) {
|
||||||
|
const cache = { serverId, provider, models: currentModels };
|
||||||
|
return { models: currentModels, cache };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
providerSnapshotRefreshing &&
|
||||||
|
previousCache?.serverId === serverId &&
|
||||||
|
previousCache.provider === provider
|
||||||
|
) {
|
||||||
|
return { models: previousCache.models, cache: previousCache };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { models: [], cache: previousCache };
|
||||||
|
}
|
||||||
@@ -32,6 +32,10 @@ import { formatTimeAgo } from "@/utils/time";
|
|||||||
import { compareMatchScores, scoreTextFields } from "@/utils/score-match";
|
import { compareMatchScores, scoreTextFields } from "@/utils/score-match";
|
||||||
import type { AgentModelDefinition, AgentProvider } from "@getpaseo/protocol/agent-types";
|
import type { AgentModelDefinition, AgentProvider } from "@getpaseo/protocol/agent-types";
|
||||||
import type { ProviderProfileModel } from "@getpaseo/protocol/provider-config";
|
import type { ProviderProfileModel } from "@getpaseo/protocol/provider-config";
|
||||||
|
import {
|
||||||
|
resolveProviderDiscoveredModels,
|
||||||
|
type ProviderDiscoveredModelsCache,
|
||||||
|
} from "./provider-diagnostic-models";
|
||||||
|
|
||||||
interface ProviderDiagnosticSheetProps {
|
interface ProviderDiagnosticSheetProps {
|
||||||
provider: string;
|
provider: string;
|
||||||
@@ -599,21 +603,16 @@ export function ProviderDiagnosticSheet({
|
|||||||
: null;
|
: null;
|
||||||
const modelsRefreshing = isRefreshing || providerSnapshotRefreshing;
|
const modelsRefreshing = isRefreshing || providerSnapshotRefreshing;
|
||||||
|
|
||||||
const stableDiscoveredRef = useRef<AgentModelDefinition[]>([]);
|
const stableDiscoveredRef = useRef<ProviderDiscoveredModelsCache | null>(null);
|
||||||
const currentModels = providerEntry?.models;
|
const currentModels = providerEntry?.models;
|
||||||
if (currentModels && currentModels.length > 0) {
|
const { models: discoveredModels, cache: nextDiscoveredCache } = resolveProviderDiscoveredModels({
|
||||||
stableDiscoveredRef.current = currentModels;
|
serverId,
|
||||||
}
|
provider,
|
||||||
|
currentModels,
|
||||||
const discoveredModels = useMemo(() => {
|
providerSnapshotRefreshing,
|
||||||
if (currentModels && currentModels.length > 0) {
|
previousCache: stableDiscoveredRef.current,
|
||||||
return currentModels;
|
});
|
||||||
}
|
stableDiscoveredRef.current = nextDiscoveredCache;
|
||||||
if (providerSnapshotRefreshing) {
|
|
||||||
return stableDiscoveredRef.current;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [currentModels, providerSnapshotRefreshing]);
|
|
||||||
|
|
||||||
const [clockTick, setClockTick] = useState(0);
|
const [clockTick, setClockTick] = useState(0);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export function ProviderSettingsHost() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ProviderDiagnosticSheet
|
<ProviderDiagnosticSheet
|
||||||
|
key={`${serverId}:${provider}`}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
serverId={serverId}
|
serverId={serverId}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
|||||||
Reference in New Issue
Block a user