feat(swift): voeg chat orchestration toe (E5)

Epic 5 compleet: Chat orchestration voor Swift Agenda Planning.
Alle agenda intents worden nu correct gerouteerd naar AgendaBlock
met user-friendly error handling en fallback opties.

E5.S1 - Action Routing (2 SP)
- routeIntentToArtifact() functie in action-parser
- Maps agenda intents naar juiste AgendaBlock mode
- Confidence threshold enforcement (< 0.7 → fallback)
- Required entity validation (patient voor create, identifier voor reschedule)
- Command-input gebruikt nieuwe routing ipv legacy openBlock
- Migratie naar modern artifact systeem (openArtifact)

E5.S2 - Chat Prompt Update (2 SP)
- 4 agenda intents toegevoegd aan Swift chat system prompt
- agenda_query: afspraken opvragen op datumrange
- create_appointment: nieuwe afspraak maken (required: patient, datetime)
- cancel_appointment: afspraak annuleren (disambiguation support)
- reschedule_appointment: afspraak verzetten (required: identifier)
- Entity extraction rules gedocumenteerd (dateRange, datetime, identifier)
- 4 complete voorbeelden met JSON action format
- Clarification questions voor incomplete data
- Prompt size: ~325 → ~525 regels (+60%)

E5.S3 - Error States (2 SP)
- AgendaErrorState component voor full-page errors
- AgendaErrorAlert component voor inline form errors
- getUserFriendlyMessage() vertaalt technical → user-friendly Dutch
- Auto-redirect bij auth errors (401 → /login)
- Fallback link naar /epd/agenda in alle error states
- Context-aware messaging (query/create/cancel/reschedule)
- Retry functionaliteit voor recoverable errors
- Dev-only technical details collapsible

Error message mapping:
- 401 → "Je sessie is verlopen. Log opnieuw in." + auto-redirect
- 403 → "Je hebt geen toegang tot deze afspraak."
- 404 → "De gevraagde afspraak kon niet worden gevonden."
- 500 → "Er ging iets mis op de server. Probeer het opnieuw."
- Network → "Geen internetverbinding. Controleer je netwerkverbinding."
- Timeout → "De aanvraag duurde te lang. Probeer het opnieuw."

Components updated:
- command-input: gebruikt routeIntentToArtifact + openArtifact
- agenda-create-form: gebruikt AgendaErrorAlert met fallback link
- chat/route: uitgebreide system prompt met agenda sectie

Nieuwe files:
- lib/swift/action-parser.ts: routeIntentToArtifact() functie
- components/swift/artifacts/blocks/agenda-error-state.tsx (225 regels)
- docs/swift/implementation-e5-s1-action-routing.md
- docs/swift/implementation-e5-s2-chat-prompt.md
- docs/swift/implementation-e5-s3-error-states.md

Documentatie:
- Bouwplan bijgewerkt: Epic 5 → Done
- 3 implementation docs met API specs en testing scenarios
- Error handling best practices gedocumenteerd

Progress: 48 SP / 51 SP (94%) - Epic 6 (QA) remaining

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
colinislit
2025-12-27 22:44:18 +01:00
parent a6b63665e1
commit 14a9d34337
9 changed files with 1445 additions and 19 deletions

View File

@@ -21,6 +21,7 @@ import type { BlockType } from '@/lib/swift/types';
import { Mic, MicOff, Send, Loader2 } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { safeFetch, getErrorInfo } from '@/lib/swift/error-handler';
import { routeIntentToArtifact } from '@/lib/swift/action-parser';
export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_, ref) {
const {
@@ -31,6 +32,7 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
activeBlock,
isVoiceActive,
openBlock,
openArtifact,
addRecentAction,
} = useSwiftStore();
const { toast } = useToast();
@@ -139,22 +141,27 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
const result = await response.json();
const { intent, confidence, entities } = result;
// Check if we have a valid intent with sufficient confidence
if (intent !== 'unknown' && confidence >= 0.5) {
// Open the appropriate block with prefill data
// Type assertion: intent is BlockType after 'unknown' check
openBlock(intent as BlockType, entities);
// Route intent to artifact using Epic 5.S1 routing logic
const artifactConfig = routeIntentToArtifact(intent, entities, confidence);
if (artifactConfig) {
// Open artifact with routing configuration
openArtifact({
type: artifactConfig.type,
title: artifactConfig.title,
prefill: artifactConfig.prefill,
});
// Add to recent actions
addRecentAction({
intent,
label: inputText.slice(0, 50), // Truncate for display
patientName: entities.patientName,
});
clearInput();
} else {
// Low confidence or unknown intent - show FallbackPicker
// Low confidence or missing required entities - show FallbackPicker
openBlock('fallback', { content: inputText });
clearInput();
}