feat: unify installation model catalog
This commit is contained in:
@@ -3,10 +3,8 @@ import { server } from "../test/msw";
|
||||
import {
|
||||
asPiManagementApiError,
|
||||
getPiManagementLogs,
|
||||
getPiManagementOptions,
|
||||
getPiManagementStatus,
|
||||
runPiManagementTest,
|
||||
savePiManagementConfig,
|
||||
} from "./pi-management";
|
||||
|
||||
test("Pi management client calls only the sanctioned sanitized endpoints", async () => {
|
||||
@@ -16,14 +14,6 @@ test("Pi management client calls only the sanctioned sanitized endpoints", async
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname });
|
||||
return HttpResponse.json({ ready: true, version: "0.80.3", credentials: "present", config: {}, checkedAt: "2026-08-05T10:00:00.000Z" });
|
||||
}),
|
||||
http.get("/api/pi-management/options", ({ request }) => {
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname });
|
||||
return HttpResponse.json({ providers: ["zai"], models: [{ provider: "zai", id: "glm-5.2" }], reasoning: ["low", "medium", "high"], checkedAt: "2026-08-05T10:00:00.000Z" });
|
||||
}),
|
||||
http.put("/api/pi-management/config", async ({ request }) => {
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname, body: await request.json() });
|
||||
return HttpResponse.json({ provider: "zai", model: "glm-5.2", reasoning: "high", updatedAt: "2026-08-05T10:00:00.000Z" });
|
||||
}),
|
||||
http.post("/api/pi-management/test", ({ request }) => {
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname });
|
||||
return HttpResponse.json({ ready: true, checkedAt: "2026-08-05T10:00:00.000Z" });
|
||||
@@ -35,15 +25,11 @@ test("Pi management client calls only the sanctioned sanitized endpoints", async
|
||||
);
|
||||
|
||||
await expect(getPiManagementStatus()).resolves.toMatchObject({ version: "0.80.3", ready: true, credentials: "present" });
|
||||
await expect(getPiManagementOptions()).resolves.toMatchObject({ providers: ["zai"] });
|
||||
await expect(savePiManagementConfig({ provider: "zai", model: "glm-5.2", reasoning: "high" })).resolves.toMatchObject({ reasoning: "high" });
|
||||
await expect(runPiManagementTest()).resolves.toMatchObject({ ready: true });
|
||||
await expect(getPiManagementLogs()).resolves.toMatchObject({ lines: ["Pi smoke check succeeded"] });
|
||||
|
||||
expect(calls).toEqual([
|
||||
{ method: "GET", path: "/api/pi-management/status" },
|
||||
{ method: "GET", path: "/api/pi-management/options" },
|
||||
{ method: "PUT", path: "/api/pi-management/config", body: { provider: "zai", model: "glm-5.2", reasoning: "high" } },
|
||||
{ method: "POST", path: "/api/pi-management/test" },
|
||||
{ method: "GET", path: "/api/pi-management/logs" },
|
||||
]);
|
||||
|
||||
@@ -17,13 +17,6 @@ export interface PiManagementStatus {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface PiManagementOptions {
|
||||
providers: string[];
|
||||
models: Array<{ provider: string; id: string }>;
|
||||
reasoning: PiReasoning[];
|
||||
checkedAt: string;
|
||||
}
|
||||
|
||||
export interface PiManagementTestResult {
|
||||
ready: boolean;
|
||||
checkedAt: string;
|
||||
@@ -37,9 +30,7 @@ export interface PiManagementLogs {
|
||||
|
||||
export type PiManagementApiErrorCode =
|
||||
| "pi_management_forbidden"
|
||||
| "pi_management_unavailable"
|
||||
| "pi_management_invalid_config"
|
||||
| "pi_management_write_failed";
|
||||
| "pi_management_unavailable";
|
||||
|
||||
export interface PiManagementApiError {
|
||||
status: number;
|
||||
@@ -50,8 +41,6 @@ export interface PiManagementApiError {
|
||||
const errorCodes = new Set<PiManagementApiErrorCode>([
|
||||
"pi_management_forbidden",
|
||||
"pi_management_unavailable",
|
||||
"pi_management_invalid_config",
|
||||
"pi_management_write_failed",
|
||||
]);
|
||||
|
||||
/** Narrows the sanctioned error code and derives its message locally. */
|
||||
@@ -65,11 +54,6 @@ export function asPiManagementApiError(error: unknown): PiManagementApiError | u
|
||||
}
|
||||
|
||||
export const getPiManagementStatus = () => apiFetch<PiManagementStatus>("/pi-management/status");
|
||||
export const getPiManagementOptions = () => apiFetch<PiManagementOptions>("/pi-management/options");
|
||||
export const savePiManagementConfig = (config: PiInstallationConfig) =>
|
||||
apiFetch<PiInstallationConfig & { updatedAt: string }>("/pi-management/config", {
|
||||
method: "PUT", body: JSON.stringify(config),
|
||||
});
|
||||
export const runPiManagementTest = () =>
|
||||
apiFetch<PiManagementTestResult>("/pi-management/test", { method: "POST" });
|
||||
export const getPiManagementLogs = () => apiFetch<PiManagementLogs>("/pi-management/logs");
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
type AuthOperationPrecondition,
|
||||
} from "../auth/authOperation";
|
||||
import { getSettings } from "./settings";
|
||||
import { getWorkspace, listWorkspaces } from "./workspaces";
|
||||
import { listWorkspaces } from "./workspaces";
|
||||
import {
|
||||
WORKSPACE_POLICY_ERROR, WORKSPACE_SUMMARY_ERROR, WorkspaceSelectionError, workspacePolicyGate,
|
||||
workspacePreferences, type WorkspacePreference,
|
||||
@@ -34,24 +34,6 @@ async function selectedPreferences(precondition?: AuthOperationPrecondition): Pr
|
||||
});
|
||||
}
|
||||
|
||||
function reconcileWorkspacePolicy(preferences: WorkspacePreference, allowed: readonly string[], defaultModel?: string) {
|
||||
if (allowed.length === 0) throw new WorkspaceSelectionError(WORKSPACE_POLICY_ERROR);
|
||||
const selected = preferences.provider && preferences.model
|
||||
? `${preferences.provider}/${preferences.model}`
|
||||
: undefined;
|
||||
if (selected && allowed.includes(selected)) return preferences;
|
||||
const replacement = defaultModel && allowed.includes(defaultModel) ? defaultModel : allowed[0];
|
||||
const separator = replacement.indexOf("/");
|
||||
if (separator <= 0 || separator === replacement.length - 1) {
|
||||
throw new WorkspaceSelectionError(WORKSPACE_POLICY_ERROR);
|
||||
}
|
||||
return workspacePreferences.save({
|
||||
...preferences,
|
||||
provider: replacement.slice(0, separator),
|
||||
model: replacement.slice(separator + 1),
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureWorkspaceSelectionPolicy(precondition?: AuthOperationPrecondition): Promise<WorkspacePreference> {
|
||||
while (true) {
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
@@ -104,45 +86,8 @@ async function ensureWorkspaceSelectionPolicy(precondition?: AuthOperationPrecon
|
||||
throw new WorkspaceSelectionError(WORKSPACE_POLICY_ERROR);
|
||||
}
|
||||
workspacePolicyGate.select(workspaceId);
|
||||
const outcome = await Promise.race([
|
||||
getWorkspace(workspaceId).then(
|
||||
(record) => ({ kind: "record" as const, record }),
|
||||
() => ({ kind: "error" as const }),
|
||||
),
|
||||
workspacePolicyGate.waitForCurrent(() => workspacePreferences.load()).then(
|
||||
(selection) => ({ kind: "selection" as const, selection }),
|
||||
),
|
||||
]);
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
if (outcome.kind === "selection") {
|
||||
if (outcome.selection.workspaceId !== workspaceId) continue;
|
||||
return outcome.selection;
|
||||
}
|
||||
if (outcome.kind === "error") {
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
if (workspacePreferences.load().workspaceId !== workspaceId) continue;
|
||||
workspacePolicyGate.reject(workspaceId);
|
||||
throw new WorkspaceSelectionError(WORKSPACE_POLICY_ERROR);
|
||||
}
|
||||
const { record } = outcome;
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
if (workspacePreferences.load().workspaceId !== workspaceId) continue;
|
||||
let selection: WorkspacePreference;
|
||||
try {
|
||||
selection = reconcileWorkspacePolicy(
|
||||
preferences,
|
||||
record.workspace.llm_policy.allowed,
|
||||
record.workspace.llm_policy.default,
|
||||
);
|
||||
} catch (error) {
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
workspacePolicyGate.reject(workspaceId);
|
||||
throw error;
|
||||
}
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
workspacePolicyGate.resolve(workspaceId);
|
||||
requireAuthOperationPrecondition(precondition);
|
||||
if (workspacePreferences.load().workspaceId === workspaceId) return selection;
|
||||
return workspacePreferences.load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export interface WorkspaceEvidence {
|
||||
|
||||
export interface CanonicalWorkspace {
|
||||
workspace: {
|
||||
schema_version: 3;
|
||||
schema_version: 4;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
@@ -76,20 +76,6 @@ export interface CanonicalWorkspace {
|
||||
timeout_ms?: number;
|
||||
supported_transports: ("postgres_direct" | "rest_api" | "ssh_tunnel")[];
|
||||
};
|
||||
semantic_index: {
|
||||
vector_store: {
|
||||
engine: "qdrant";
|
||||
collection: string;
|
||||
dimensions: 1024;
|
||||
distance: "cosine";
|
||||
}
|
||||
embedding: {
|
||||
provider: "ollama_internal";
|
||||
model: "qwen3-embedding:0.6b";
|
||||
dimensions: 1024;
|
||||
};
|
||||
};
|
||||
llm_policy: { default?: `${string}/${string}`; allowed: `${string}/${string}`[] };
|
||||
diagnostics?: CanonicalDiagnostics;
|
||||
evidence?: WorkspaceEvidence;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user