feat: provider model freshness TTL and diagnostic UI (#426)
* feat: add server-side TTL for provider snapshots and model list UI Provider snapshots cached forever, causing newly available models (e.g. OpenCode Go minimax, glm) to never appear in the picker. Server: ProviderSnapshotManager now returns stale data immediately and kicks off a background re-warm when the snapshot is older than 5 minutes. Injectable TTL/clock for testing. App: Provider settings rows show model count and are tappable to open a diagnostic sheet with a read-only model list (label + ID). * feat: add per-provider refresh for models and diagnostic * refactor: await refresh completion and clean up diagnostic sheet state - ProviderSnapshotManager.refresh is now async; session.ts awaits it so the RPC ACK reflects real completion instead of just queuing work. - Preserve models/modes/fetchedAt on entries during targeted refresh so the list no longer flashes empty mid-refresh. - Show "Updated Xs ago" next to the Models count, plus loading and error states for the list body. - Split resetSnapshotToLoading into full vs targeted branches. - Flatten nested ternary in model list rendering into renderModelsBody. - Drop redundant local refreshing state and cargo useMemo wrappers. * fix(app): widen isImeComposingKeyboardEvent type to accept optional fields TextInputKeyPressEventData has isComposing and keyCode as optional, so Pick<KeyboardEvent, ...> was too narrow and broke typecheck on main.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { ProviderSnapshotEntry } from "@server/server/agent/agent-sdk-types";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AgentProvider, ProviderSnapshotEntry } from "@server/server/agent/agent-sdk-types";
|
||||
import type { DaemonClient } from "@server/client/daemon-client";
|
||||
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
||||
import { useSessionForServer } from "./use-session-directory";
|
||||
@@ -14,9 +14,10 @@ interface UseProvidersSnapshotResult {
|
||||
entries: ProviderSnapshotEntry[] | undefined;
|
||||
isLoading: boolean;
|
||||
isFetching: boolean;
|
||||
isRefreshing: boolean;
|
||||
error: string | null;
|
||||
supportsSnapshot: boolean;
|
||||
refresh: () => void;
|
||||
refresh: (providers?: AgentProvider[]) => Promise<void>;
|
||||
invalidate: () => void;
|
||||
}
|
||||
|
||||
@@ -43,6 +44,16 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
|
||||
},
|
||||
});
|
||||
|
||||
const refreshMutation = useMutation({
|
||||
mutationFn: async (providers?: AgentProvider[]) => {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
await client.refreshProvidersSnapshot({ providers });
|
||||
},
|
||||
});
|
||||
const { mutateAsync: refreshSnapshot, isPending: isRefreshing } = refreshMutation;
|
||||
|
||||
useEffect(() => {
|
||||
if (!supportsSnapshot || !client || !isConnected || !serverId) {
|
||||
return;
|
||||
@@ -60,12 +71,12 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
|
||||
});
|
||||
}, [client, isConnected, serverId, queryClient, queryKey, supportsSnapshot]);
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
void client.refreshProvidersSnapshot();
|
||||
}, [client]);
|
||||
const refresh = useCallback(
|
||||
async (providers?: AgentProvider[]) => {
|
||||
await refreshSnapshot(providers);
|
||||
},
|
||||
[refreshSnapshot],
|
||||
);
|
||||
|
||||
const invalidate = useCallback(() => {
|
||||
void queryClient.invalidateQueries({ queryKey });
|
||||
@@ -75,6 +86,7 @@ export function useProvidersSnapshot(serverId: string | null): UseProvidersSnaps
|
||||
entries: snapshotQuery.data?.entries ?? undefined,
|
||||
isLoading: snapshotQuery.isLoading,
|
||||
isFetching: snapshotQuery.isFetching,
|
||||
isRefreshing,
|
||||
error: snapshotQuery.error instanceof Error ? snapshotQuery.error.message : null,
|
||||
supportsSnapshot,
|
||||
refresh,
|
||||
|
||||
Reference in New Issue
Block a user