Files
temetro/frontend/lib/ai-settings.ts
T
Khalid Abdi b299501ab2 frontend+backend: patients/settings UI, AI Auto/Off modes, wallet doc push
- patients: move status filter to its own "Filter" row above the table
- patient sheet: name + ⋯ menu on one left row; Edit moved into the ⋯ menu
- settings: redesign sections with the COSS CardFrame surface (SettingsFrame)
- AI: add Automatic (auto-pick provider) and Off modes; default to Automatic
  so a fresh install shows the setup banner until a provider is configured
- AI: fix the setup banner never showing (defaulted Ollama URL counted as
  configured); add an "AI off" notice; add a fallback render for unknown chat
  data parts so cards never silently vanish
- backend: include patient attachment metadata in the wallet record-update
  bundle so pushed documents reach the wallet
- i18n: add mode/off/card keys across all five locales

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 20:22:36 +03:00

79 lines
2.5 KiB
TypeScript

import { apiFetch } from "@/lib/api-client";
import type { Effort } from "@/lib/ai-models";
// Mirrors backend/src/types/ai.ts. Per-user AI configuration fetched from and
// saved to /api/ai/config. Provider API keys are write-only: they are never
// returned, only `apiKeySet` reports which providers have a stored key.
// "auto" auto-picks a cloud provider when a key is set, else falls back to local
// Ollama; "off" disables the assistant entirely.
export type AiMode = "api" | "local" | "auto" | "off";
export type ApiProvider = "openai" | "anthropic" | "gemini";
export type VeilLevel = "off" | "names" | "full";
export type AiConfig = {
mode: AiMode;
provider: ApiProvider;
ollamaBaseUrl: string;
ollamaModel: string;
defaultModel: string;
defaultEffort: Effort;
veilLevel: VeilLevel;
apiKeySet: Record<ApiProvider, boolean>;
};
export type AiConfigPatch = Partial<
Omit<AiConfig, "apiKeySet">
> & {
// Plaintext key for the currently selected provider; "" clears it.
apiKey?: string;
};
export async function getAiConfig(): Promise<AiConfig> {
const res = await apiFetch<{ config: AiConfig }>("/api/ai/config");
return res.config;
}
export async function saveAiConfig(patch: AiConfigPatch): Promise<AiConfig> {
const res = await apiFetch<{ config: AiConfig }>("/api/ai/config", {
method: "PUT",
body: JSON.stringify(patch),
});
return res.config;
}
export async function testAiConnection(input: {
mode: AiMode;
provider?: ApiProvider;
ollamaBaseUrl?: string;
}): Promise<{ ok: boolean; message: string }> {
return apiFetch<{ ok: boolean; message: string }>("/api/ai/test", {
method: "POST",
body: JSON.stringify(input),
});
}
// Commit records the clinician approved in a chat import preview. The backend
// re-validates and writes via the audited patient service.
export async function commitImport(
records: unknown[],
): Promise<{ created: string[]; failed: { fileNumber?: string; error: string }[] }> {
return apiFetch("/api/ai/import", {
method: "POST",
body: JSON.stringify({ records }),
});
}
// Re-validate edited import records (dry run) so the review UI can refresh which
// rows are ready vs. need fixing. Writes nothing.
export async function validateImport(records: unknown[]): Promise<{
valid: unknown[];
invalid: { index: number; errors: string[]; record: unknown }[];
total: number;
}> {
return apiFetch("/api/ai/import/validate", {
method: "POST",
body: JSON.stringify({ records }),
});
}