feat(forms): show model parameters in the picker and fill picks quietly

Each model in the picker dialog now shows its context window, reasoning
and image input, resolved with the same lookup as the parameter fill
(same-endpoint preset, then models.dev by endpoint, then the vendor's own
entry), so what the dialog shows is what Apply fills in. Prices are left
out: models.dev only has them when the endpoint is recognized. The vendor
column has a fixed width so the parameters line up. The dialog body mounts
only while open, which is also when it subscribes to the models.dev query.

Since the dialog already showed the values, models added from it are
filled without the "parameters filled, please check" toast. The fill
function of useModelMetadataFill now takes { baseUrl, silent } instead of a
bare base URL (the Pi form passes its per-model address that way). Picking
a model from a row's dropdown still shows the toast.

ClaudeStackModelsField takes the provider's base URL so its dialog can
show parameters too; its rows have no such fields to fill.
This commit is contained in:
Jason committed 2026-10-11 22:51:29 +08:00
1 parent 8347e1ab03
commit 644b7d5c04
13 files changed
+208 -37

No files matched your search

+14 -1
View File
@@ -77,7 +77,9 @@ describe("useModelMetadataFill", () => {
const { result } = renderFill();
const apply = vi.fn(() => true);
result.current("model-x", apply, "https://other.example.com/v1");
result.current("model-x", apply, {
baseUrl: "https://other.example.com/v1",
});
expect(apply).not.toHaveBeenCalled();
});
@@ -110,4 +112,15 @@ describe("useModelMetadataFill", () => {
await new Promise((resolve) => setTimeout(resolve, 80));
expect(toastInfo).not.toHaveBeenCalled();
});
it("fills without a reminder when asked to stay silent", async () => {
server.use(http.get(MODELS_DEV_API_URL, () => HttpResponse.error()));
toastInfo.mockClear();
const { result } = renderFill();
const apply = vi.fn(() => true);
result.current("model-x", apply, { silent: true });
expect(apply).toHaveBeenCalledTimes(1);
await new Promise((resolve) => setTimeout(resolve, 80));
expect(toastInfo).not.toHaveBeenCalled();
});
});