mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-08 01:14:52 +00:00
fix(app): resolve remembered models when host catalogs arrive
Keep opening model resolution pending until its selected catalog is ready, so a loading snapshot cannot freeze an unavailable preference. Preserve user edits and error presentation while awaiting recovery. Co-authored-by: Matt Cowger <matt@cowger.us>
This commit is contained in:
co-authored by
Matt Cowger
parent
20120d111b
commit
4f7201665e
@@ -113,7 +113,7 @@ function buildProviderModelsByProvider(
|
||||
for (const entry of snapshotEntries ?? []) {
|
||||
map.set(
|
||||
entry.provider,
|
||||
entry.status === "ready" ? filterSelectableModels(entry.models ?? null) : null,
|
||||
entry.status === "ready" ? filterSelectableModels(entry.models ?? []) : null,
|
||||
);
|
||||
}
|
||||
return map;
|
||||
@@ -249,8 +249,11 @@ export function useAgentFormState(options: UseAgentFormStateOptions): UseAgentFo
|
||||
const modelSelectorProviders = snapshotModelSelectorProviders;
|
||||
const availableModels = snapshotSelectedProviderModels;
|
||||
const modeOptions = snapshotSelectedProviderModes;
|
||||
const selectedProviderHasError =
|
||||
snapshotSelectedEntry?.status === "error" || snapshotSelectedEntry?.status === "unavailable";
|
||||
const isModelSelectionLoading =
|
||||
isConnected &&
|
||||
!selectedProviderHasError &&
|
||||
(resolution.status === "pending" || snapshotIsLoading || selectedProviderIsLoading);
|
||||
const isAllModelsLoading = isModelSelectionLoading;
|
||||
|
||||
|
||||
@@ -1296,3 +1296,78 @@ it("owns input readiness, reopening and user edits in the reducer", () => {
|
||||
state = resolveAgentForm(state, { ...inputs, isPreferencesLoading: false, hasSnapshot: true });
|
||||
expect(state.form).toMatchObject({ provider: "codex", model: "astra" });
|
||||
});
|
||||
|
||||
it("resolves a remembered model against the new host after its catalog finishes loading", () => {
|
||||
const inputs = {
|
||||
type: "INPUTS_CHANGED" as const,
|
||||
serverId: "second-host",
|
||||
isVisible: true,
|
||||
isCreateFlow: true,
|
||||
isPreferencesLoading: false,
|
||||
hasSnapshot: true,
|
||||
initialValues: undefined,
|
||||
preferences: {
|
||||
provider: "codex",
|
||||
providerPreferences: { codex: { model: "remote/model" } },
|
||||
},
|
||||
allowedProviderMap: codexProviderMap,
|
||||
providerModelsByProvider: makeProviderModelsByProvider([["codex", null]]),
|
||||
};
|
||||
const firstHost = resolveAgentForm(makeState(), {
|
||||
...inputs,
|
||||
serverId: "first-host",
|
||||
providerModelsByProvider: makeProviderModelsByProvider([
|
||||
["codex", [{ provider: "codex", id: "remote/model", label: "Remote" }]],
|
||||
]),
|
||||
});
|
||||
const opening = resolveAgentForm(firstHost, inputs);
|
||||
expect(opening.form.model).toBe("remote/model");
|
||||
expect(opening.resolution.status).toBe("pending");
|
||||
const ready = resolveAgentForm(opening, {
|
||||
...inputs,
|
||||
providerModelsByProvider: makeProviderModelsByProvider([["codex", CODEX_MODELS]]),
|
||||
});
|
||||
expect(ready.form.model).toBe("gpt-5.3-codex");
|
||||
expect(ready.form.thinkingOptionId).toBe("xhigh");
|
||||
expect(ready.resolution.status).toBe("completed");
|
||||
});
|
||||
|
||||
it("retains user input while the opening catalog is pending", () => {
|
||||
const inputs = {
|
||||
type: "INPUTS_CHANGED" as const,
|
||||
serverId: "host",
|
||||
isVisible: true,
|
||||
isCreateFlow: true,
|
||||
isPreferencesLoading: false,
|
||||
hasSnapshot: true,
|
||||
initialValues: undefined,
|
||||
preferences: { provider: "codex", providerPreferences: { codex: { model: "remote/model" } } },
|
||||
allowedProviderMap: codexProviderMap,
|
||||
providerModelsByProvider: makeProviderModelsByProvider([["codex", null]]),
|
||||
};
|
||||
const opening = resolveAgentForm(makeState(), inputs);
|
||||
const edited = resolveAgentForm(opening, {
|
||||
type: "SET_MODEL_FROM_USER",
|
||||
modelId: "manual/model",
|
||||
availableModels: null,
|
||||
providerPrefs: undefined,
|
||||
});
|
||||
const ready = resolveAgentForm(edited, {
|
||||
...inputs,
|
||||
providerModelsByProvider: makeProviderModelsByProvider([["codex", CODEX_MODELS]]),
|
||||
});
|
||||
expect(ready.form.model).toBe("manual/model");
|
||||
expect(ready.resolution.status).toBe("completed");
|
||||
});
|
||||
|
||||
it("resolves modeless providers once their empty catalog is ready", () => {
|
||||
const ready = resolveAgentForm(makeState(), {
|
||||
type: "COMPLETE_RESOLUTION",
|
||||
initialValues: undefined,
|
||||
preferences: { provider: "pi" },
|
||||
providerModelsByProvider: makeProviderModelsByProvider([["pi", []]]),
|
||||
allowedProviderMap: buildProviderDefinitionMap([TEST_PI_DEFINITION]),
|
||||
});
|
||||
expect(ready.form).toMatchObject({ provider: "pi", model: "" });
|
||||
expect(ready.resolution.status).toBe("completed");
|
||||
});
|
||||
|
||||
@@ -533,7 +533,14 @@ function completeResolution(
|
||||
state.form,
|
||||
action.allowedProviderMap,
|
||||
);
|
||||
const nextState = { ...state, resolution: { status: "completed" } as const };
|
||||
const waitingForModels =
|
||||
resolved.provider !== null && action.providerModelsByProvider.get(resolved.provider) === null;
|
||||
const nextState = {
|
||||
...state,
|
||||
resolution: waitingForModels
|
||||
? PENDING_AGENT_FORM_RESOLUTION
|
||||
: ({ status: "completed" } as const),
|
||||
};
|
||||
if (!hasFormStateChanged(state.form, resolved)) return nextState;
|
||||
return { ...nextState, form: resolved };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user