feat(webui): refine output timeline and model controls (#4108)
* feat(webui): refine output timeline and composer queue * feat(webui): add provider model picker * fix(webui): polish model settings and heartbeat checks * chore: keep heartbeat changes out of webui pr * refactor(webui): isolate settings routes * fix(providers): align minimax anthropic test * fix(providers): keep minimax anthropic base sdk-compatible * fix(providers): normalize anthropic base urls
This commit is contained in:
@@ -245,6 +245,104 @@ describe("SettingsView Apps catalog", () => {
|
||||
expect(screen.getByRole("button", { name: "256K" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("loads provider models and lets users choose one without typing the id manually", async () => {
|
||||
const payload: SettingsPayload = {
|
||||
...settingsPayload(),
|
||||
agent: {
|
||||
...settingsPayload().agent,
|
||||
model: "deepseek-chat",
|
||||
provider: "deepseek",
|
||||
resolved_provider: "deepseek",
|
||||
},
|
||||
model_presets: [
|
||||
{
|
||||
...settingsPayload().model_presets[0],
|
||||
model: "deepseek-chat",
|
||||
provider: "deepseek",
|
||||
},
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
name: "deepseek",
|
||||
label: "DeepSeek",
|
||||
configured: true,
|
||||
auth_type: "api_key",
|
||||
api_key_required: true,
|
||||
api_key_hint: "sk-...",
|
||||
api_base: "https://api.deepseek.com",
|
||||
default_api_base: "https://api.deepseek.com",
|
||||
},
|
||||
],
|
||||
};
|
||||
const updatedPayload: SettingsPayload = {
|
||||
...payload,
|
||||
agent: {
|
||||
...payload.agent,
|
||||
model: "deepseek-reasoner",
|
||||
},
|
||||
model_presets: [
|
||||
{
|
||||
...payload.model_presets[0],
|
||||
model: "deepseek-reasoner",
|
||||
},
|
||||
],
|
||||
};
|
||||
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
|
||||
const url = String(input);
|
||||
if (url === "/api/settings") return jsonResponse(payload);
|
||||
if (url === "/api/settings/cli-apps") {
|
||||
return jsonResponse({ apps: [], installed_count: 0 });
|
||||
}
|
||||
if (url === "/api/settings/mcp-presets") {
|
||||
return jsonResponse({ presets: [], installed_count: 0 });
|
||||
}
|
||||
if (url === "/api/settings/provider-models?provider=deepseek") {
|
||||
return jsonResponse({
|
||||
provider: "deepseek",
|
||||
label: "DeepSeek",
|
||||
status: "available",
|
||||
catalog_kind: "official",
|
||||
models: [
|
||||
{ id: "deepseek-chat", owned_by: "deepseek", context_window: 65536 },
|
||||
{ id: "deepseek-reasoner", owned_by: "deepseek", context_window: 65536 },
|
||||
],
|
||||
model_count: 2,
|
||||
fetched_at: 1,
|
||||
});
|
||||
}
|
||||
if (url === "/api/settings/update?model_preset=default&model=deepseek-reasoner") {
|
||||
return jsonResponse(updatedPayload);
|
||||
}
|
||||
return { ok: false, status: 404, json: async () => ({}) } as Response;
|
||||
});
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
renderSettingsView({ initialSection: "models" });
|
||||
|
||||
const modelButtons = await screen.findAllByRole("button", { name: /deepseek-chat/i });
|
||||
fireEvent.pointerDown(modelButtons[modelButtons.length - 1]);
|
||||
await screen.findByText("deepseek-reasoner");
|
||||
fireEvent.click(screen.getAllByText("deepseek-reasoner")[0]);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"/api/settings/provider-models?provider=deepseek",
|
||||
expect.objectContaining({
|
||||
headers: { Authorization: "Bearer tok" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
"/api/settings/update?model_preset=default&model=deepseek-reasoner",
|
||||
expect.objectContaining({
|
||||
headers: { Authorization: "Bearer tok" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("saves network safety without exposing technical SSRF copy", async () => {
|
||||
const payload = settingsPayload();
|
||||
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
|
||||
|
||||
Reference in New Issue
Block a user