AI:n drivs av plattformen — flytta Claude-anropet till backend

- Ny edge-funktion supabase/functions/felsokning-ai: äger Claude
  API-nyckeln (serverhemligheten ANTHROPIC_API_KEY), systemprompten,
  modellvalet (claude-opus-5) och svarsschemat. Kräver inloggad
  användare (verify_jwt = true), validerar promptlängd och hanterar
  refusal/fallback på serversidan.
- Klienten anropar plattformens endpoint via functions.invoke —
  @anthropic-ai/sdk borttagen ur klientberoendena, ingen nyckel
  hanteras eller lagras någonsin i webbläsaren.
- Nyckelpanelen på startsidan ersatt med statuspanel: AI-handledning
  ingår i tjänsten; i lokalt läge (ej inloggad) guidar den
  deterministiska metodiken ensam, utan felbrus.
- Master Prompt: onboarding utan API-nyckelsteg; nytt stycke om att
  AI:n drivs av plattformen och att AI-reglerna inte kan kringgås
  från klientsidan.
- Test låser att endpointen äger systemprompt, modell och schema.

Verifierat: 25 vitest-tester gröna, produktionsbygge ok, båda
Playwright-sviterna gröna.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-03 01:08:19 +00:00
parent 82b469b7e6
commit ed483774fb
11 changed files with 170 additions and 199 deletions
-6
View File
@@ -12,13 +12,9 @@ import { GENERISK_METODIK, valjMetodik, type Metodik } from "./metodik";
interface FelsokningState {
anvandare: string;
// Tenantens Claude API-nyckel (MVP: lagras endast på enheten; i
// produktionsversionen hanteras nycklar av backend, aldrig i klienten).
aiNyckel: string;
arenden: Record<string, Arende>;
nastaNummer: number;
sattAnvandare: (namn: string) => void;
sattAiNyckel: (nyckel: string) => void;
skapaArende: (objekt: Objekt, felbeskrivningText: string) => string;
laggTill: (arendeId: string, handelse: Handelse) => void;
// Används endast av synken: ersätter listan med den ihopflätade versionen.
@@ -38,12 +34,10 @@ export const useFelsokning = create<FelsokningState>()(
persist(
(set, get) => ({
anvandare: "",
aiNyckel: "",
arenden: {},
nastaNummer: 1,
sattAnvandare: (namn) => set({ anvandare: namn.trim() }),
sattAiNyckel: (nyckel) => set({ aiNyckel: nyckel.trim() }),
skapaArende: (objekt, felbeskrivningText) => {
const { anvandare, nastaNummer } = get();