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>
225 lines
6.5 KiB
Markdown
225 lines
6.5 KiB
Markdown
# Epic 5.S1 Implementation: Action Routing
|
|
|
|
**Story**: E5.S1 - Action routing
|
|
**Date**: 2025-12-27
|
|
**Status**: ✅ Complete
|
|
**Story Points**: 2 SP
|
|
|
|
---
|
|
|
|
## 🎯 Objective
|
|
|
|
Implement intent routing logic that maps agenda intents to the AgendaBlock artifact with the appropriate mode and prefill data.
|
|
|
|
---
|
|
|
|
## 📝 Implementation Summary
|
|
|
|
### 1. Updated Action Parser Schemas
|
|
|
|
**File**: `lib/swift/action-parser.ts`
|
|
|
|
Added agenda intents to validation schemas:
|
|
- `agenda_query`
|
|
- `create_appointment`
|
|
- `cancel_appointment`
|
|
- `reschedule_appointment`
|
|
|
|
### 2. Created Intent Routing Function
|
|
|
|
**Function**: `routeIntentToArtifact(intent, entities, confidence)`
|
|
|
|
**Purpose**: Maps intents to artifact configurations with:
|
|
- Artifact type
|
|
- Title
|
|
- Prefill data
|
|
|
|
**Logic**:
|
|
```typescript
|
|
if (confidence < 0.7) return null; // Trigger fallback
|
|
|
|
switch (intent) {
|
|
case 'agenda_query':
|
|
return { type: 'agenda_query', title: 'Agenda', prefill: { dateRange } };
|
|
|
|
case 'create_appointment':
|
|
if (!patientName && !patientId) return null; // Missing required entity
|
|
return { type: 'create_appointment', title: 'Nieuwe afspraak', prefill: {...} };
|
|
|
|
case 'cancel_appointment':
|
|
return { type: 'cancel_appointment', title: 'Afspraak annuleren', prefill: {...} };
|
|
|
|
case 'reschedule_appointment':
|
|
if (!identifier) return null; // Need to know which appointment
|
|
return { type: 'reschedule_appointment', title: 'Afspraak verzetten', prefill: {...} };
|
|
|
|
// ... other intents (dagnotitie, zoeken, overdracht)
|
|
}
|
|
```
|
|
|
|
**Key Features**:
|
|
- ✅ Confidence threshold (0.7) enforcement
|
|
- ✅ Required entity validation (patient for create, identifier for reschedule)
|
|
- ✅ Returns null to trigger clarification when needed
|
|
- ✅ Proper prefill data extraction for each intent
|
|
|
|
### 3. Updated Command Input
|
|
|
|
**File**: `components/swift/command-center/command-input.tsx`
|
|
|
|
**Changes**:
|
|
1. Import `routeIntentToArtifact` from action-parser
|
|
2. Add `openArtifact` to store hooks
|
|
3. Replace legacy `openBlock` logic with routing:
|
|
|
|
```typescript
|
|
// OLD (direct block opening):
|
|
if (intent !== 'unknown' && confidence >= 0.5) {
|
|
openBlock(intent as BlockType, entities);
|
|
}
|
|
|
|
// NEW (routing with artifact system):
|
|
const artifactConfig = routeIntentToArtifact(intent, entities, confidence);
|
|
if (artifactConfig) {
|
|
openArtifact({
|
|
type: artifactConfig.type,
|
|
title: artifactConfig.title,
|
|
prefill: artifactConfig.prefill,
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔑 Key Routing Rules
|
|
|
|
| Intent | Artifact Type | Required Entities | Mode |
|
|
|--------|---------------|-------------------|------|
|
|
| `agenda_query` | `agenda_query` | None | List view with date filter |
|
|
| `create_appointment` | `create_appointment` | `patientName` OR `patientId` | Create form |
|
|
| `cancel_appointment` | `cancel_appointment` | None | Cancel view (may need disambiguation) |
|
|
| `reschedule_appointment` | `reschedule_appointment` | `identifier` | Reschedule form |
|
|
|
|
---
|
|
|
|
## 📊 Confidence Thresholds
|
|
|
|
| Confidence | Action | Example |
|
|
|------------|--------|---------|
|
|
| **≥ 0.7** | Open artifact directly | "afspraken vandaag" → AgendaBlock opens |
|
|
| **< 0.7** | Show fallback picker | Low confidence → User chooses intent manually |
|
|
| **Missing required entity** | Show fallback picker | "maak afspraak" (no patient) → Clarification needed |
|
|
|
|
---
|
|
|
|
## 🔄 Data Flow
|
|
|
|
```
|
|
User Input
|
|
↓
|
|
Intent Classification API (/api/intent/classify)
|
|
↓
|
|
{ intent, entities, confidence }
|
|
↓
|
|
routeIntentToArtifact()
|
|
↓
|
|
Artifact Config { type, title, prefill } OR null
|
|
↓
|
|
openArtifact() OR openBlock('fallback')
|
|
↓
|
|
AgendaBlock renders with prefilled data
|
|
```
|
|
|
|
---
|
|
|
|
## 📁 Files Modified
|
|
|
|
```
|
|
lib/swift/action-parser.ts
|
|
├── Updated ActionSchema with agenda intents
|
|
├── Updated artifact type enum
|
|
├── Added routeIntentToArtifact() function
|
|
└── Updated validateArtifactType() for agenda intents
|
|
|
|
components/swift/command-center/command-input.tsx
|
|
├── Import routeIntentToArtifact
|
|
├── Use openArtifact from store
|
|
└── Replace direct block opening with routing logic
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Acceptance Criteria
|
|
|
|
| Criterion | Status | Notes |
|
|
|-----------|--------|-------|
|
|
| Agenda intents open AgendaBlock | ✅ | Via routing function |
|
|
| Correct mode selected | ✅ | Based on intent type |
|
|
| Prefill data passed correctly | ✅ | Mapped from entities |
|
|
| Confidence threshold enforced | ✅ | < 0.7 shows fallback |
|
|
| Required entities validated | ✅ | Returns null when missing |
|
|
| TypeScript type safety | ✅ | No compilation errors |
|
|
|
|
---
|
|
|
|
## 🧪 Testing Scenarios
|
|
|
|
### Scenario 1: High Confidence Agenda Query
|
|
**Input**: "afspraken vandaag"
|
|
**Expected**:
|
|
- Intent: `agenda_query`
|
|
- Confidence: ~0.95
|
|
- Result: AgendaBlock opens in list mode with today's date range
|
|
|
|
### Scenario 2: Create Appointment with Patient
|
|
**Input**: "maak afspraak jan morgen 14:00"
|
|
**Expected**:
|
|
- Intent: `create_appointment`
|
|
- Entities: `{ patientName: "jan", datetime: {...} }`
|
|
- Result: AgendaBlock opens in create mode with prefilled patient and time
|
|
|
|
### Scenario 3: Create Appointment without Patient (Missing Entity)
|
|
**Input**: "maak afspraak morgen 14:00"
|
|
**Expected**:
|
|
- Intent: `create_appointment`
|
|
- Entities: `{ datetime: {...} }` (no patient)
|
|
- Result: FallbackPicker opens (routing returns null due to missing required entity)
|
|
|
|
### Scenario 4: Low Confidence
|
|
**Input**: "agenda ding morgen"
|
|
**Expected**:
|
|
- Intent: `agenda_query` (maybe)
|
|
- Confidence: < 0.7
|
|
- Result: FallbackPicker opens (routing returns null due to low confidence)
|
|
|
|
### Scenario 5: Reschedule with Identifier
|
|
**Input**: "verzet 14:00 naar 15:00"
|
|
**Expected**:
|
|
- Intent: `reschedule_appointment`
|
|
- Entities: `{ identifier: {...}, newDatetime: {...} }`
|
|
- Result: AgendaBlock opens in reschedule mode
|
|
|
|
---
|
|
|
|
## 🚀 Next Steps
|
|
|
|
1. **E5.S2**: Update chat API prompt to include agenda intent examples
|
|
2. **E5.S3**: Add error state handling with fallback links
|
|
3. **E6**: Manual QA testing of complete agenda flow
|
|
|
|
---
|
|
|
|
## 💡 Lessons Learned
|
|
|
|
1. **Artifact System**: Modern approach using `openArtifact()` is cleaner than legacy `openBlock()`
|
|
2. **Centralized Routing**: Single function makes intent mapping maintainable and testable
|
|
3. **Validation Early**: Checking required entities in routing prevents incomplete forms
|
|
4. **Confidence Thresholds**: 0.7 threshold balances automation with user control
|
|
5. **Type Safety**: TypeScript schemas ensure consistency across intent types
|
|
|
|
---
|
|
|
|
**Implementation Status**: ✅ Complete
|
|
**Ready for**: E5.S2 (Chat prompt update)
|
|
**Estimated Testing Time**: 15-20 minutes (manual testing with various inputs)
|