Cortex handelt een no-show af vanuit één chatcommando: afspraak annuleren (declarabiliteits-nudge), en de openstaande concept- huisartsbrief wordt via LLM herschreven en ter review aangeboden in een document artifact (human-in-the-loop, PATCH dispatch zet status op verzendklaar). - API-routes: context, rescript, cancel, dispatch - NoShowDocumentBlock: review/edit UI met origineel-vergelijk - Mock-data voor concept huisartsbrief - PRD, FO, bouwplan en epics in docs/intent/noshow-case/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { z } from 'zod';
|
|
import { createClient } from '@/lib/auth/server';
|
|
|
|
const RescriptSchema = z.object({
|
|
documentId: z.string().min(1),
|
|
originalContent: z.string().min(1, 'Originele inhoud is verplicht'),
|
|
patientId: z.string().min(1),
|
|
});
|
|
|
|
const RESCRIPT_SYSTEM_PROMPT = `Je bent een klinisch schrijver in een GGZ instelling.
|
|
Je taak is een bestaande conceptbrief aanpassen zodat de no-show van de patiënt
|
|
professioneel en contextbewust is verwerkt.
|
|
|
|
Regels:
|
|
- Integreer de no-show in de lopende tekst — voeg het NIET als losse zin achteraan toe
|
|
- Gebruik formele GGZ-briefstijl: "de patiënt is helaas niet verschenen op het geplande consult"
|
|
- Bewaar alle bestaande informatie in de brief volledig
|
|
- Voeg een zin toe over het verzetten van het vervolgcontact
|
|
- De brief moet leesbaar en coherent blijven
|
|
- Reageer UITSLUITEND met de herschreven brieftekst — geen uitleg, geen inleiding`;
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const supabase = await createClient();
|
|
const { data: { user }, error: authError } = await supabase.auth.getUser();
|
|
if (authError || !user) {
|
|
return NextResponse.json({ error: 'Niet geautoriseerd' }, { status: 401 });
|
|
}
|
|
|
|
let body: z.infer<typeof RescriptSchema>;
|
|
try {
|
|
body = RescriptSchema.parse(await request.json());
|
|
} catch {
|
|
return NextResponse.json({ error: 'Ongeldige invoer' }, { status: 400 });
|
|
}
|
|
|
|
const { documentId, originalContent } = body;
|
|
|
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
if (!apiKey) {
|
|
return NextResponse.json({ error: 'API key ontbreekt' }, { status: 500 });
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'x-api-key': apiKey,
|
|
'anthropic-version': '2023-06-01',
|
|
},
|
|
body: JSON.stringify({
|
|
model: 'claude-haiku-4-5-20251001',
|
|
max_tokens: 1500,
|
|
system: RESCRIPT_SYSTEM_PROMPT,
|
|
messages: [
|
|
{
|
|
role: 'user',
|
|
content: `Herschrijf de volgende conceptbrief zodat de no-show van vandaag professioneel is verwerkt:\n\n${originalContent}`,
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Anthropic API fout: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const rescriptedContent = data.content?.[0]?.text ?? originalContent;
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
documentId,
|
|
rescriptedContent,
|
|
originalContent,
|
|
});
|
|
} catch (error) {
|
|
console.error('[noshow/rescript] LLM error:', error);
|
|
|
|
// Graceful degradation: originele inhoud teruggeven
|
|
return NextResponse.json({
|
|
success: false,
|
|
documentId,
|
|
rescriptedContent: originalContent,
|
|
originalContent,
|
|
warning: 'AI herschrijving mislukt — originele tekst wordt getoond',
|
|
});
|
|
}
|
|
}
|