Fixes 4 bugs preventing intake blocks from working: 1. Chat API validation: Add 'nudge' to message type enum, allow empty content for streaming messages 2. AI chat recognition: Add P3 intake intents (intake_status, risico_query, diagnose_query, intake_navigeer) to system prompt with triggers, entities, and JSON examples 3. Artifact rendering: Add intake block imports and switch cases to artifact-container.tsx 4. API 400 error: Convert null to undefined for optional Zod params (searchParams.get returns null, Zod .optional() expects undefined) Also adds: - Testplan for E2E testing (60+ test cases) - Session log with lessons learned and new intent checklist Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
347 lines
8.7 KiB
Markdown
347 lines
8.7 KiB
Markdown
# Session Log — 03-02-2026
|
|
|
|
**Project:** Cortex Intake Blocks MVP
|
|
**Sessie:** Testing, Bugfixes & Integration
|
|
**Tijd:** Avond
|
|
|
|
---
|
|
|
|
## Samenvatting
|
|
|
|
Deze sessie richtte zich op het volledig werkend krijgen van de Intake Blocks MVP:
|
|
1. Testplan maken
|
|
2. Meerdere kritieke bugs oplossen
|
|
3. Intake intents integreren in de volledige flow
|
|
|
|
**Eindresultaat:** ✅ Alle 4 intake intents werken nu volledig
|
|
|
|
---
|
|
|
|
## Bugs Opgelost
|
|
|
|
### Bug 1: Chat API Validatiefout
|
|
|
|
**Symptoom:** `Chat API error: Error: Validatiefout`
|
|
|
|
**Oorzaak:** Twee issues in Zod schema:
|
|
1. `nudge` type ontbrak in `ChatMessageSchema.type` enum
|
|
2. `content: z.string().min(1)` blokkeerde lege streaming berichten
|
|
|
|
**Fix:** `app/api/cortex/chat/route.ts`
|
|
```typescript
|
|
// VOOR:
|
|
type: z.enum(['user', 'assistant', 'system', 'error']),
|
|
content: z.string().min(1),
|
|
|
|
// NA:
|
|
type: z.enum(['user', 'assistant', 'system', 'error', 'nudge']),
|
|
content: z.string(), // Allow empty
|
|
```
|
|
|
|
---
|
|
|
|
### Bug 2: AI Chat herkent intake intents niet
|
|
|
|
**Symptoom:** Chat AI vraagt "Wil je het dossier opzoeken?" ipv navigeren
|
|
|
|
**Oorzaak:** Intake intents (`intake_status`, `risico_query`, `diagnose_query`, `intake_navigeer`) ontbraken in de AI system prompt.
|
|
|
|
**Fix:** `app/api/cortex/chat/route.ts` - System prompt uitgebreid met:
|
|
- P3 Intents sectie met 4 intake intents
|
|
- Triggers en entities per intent
|
|
- 4 voorbeelden (voorbeeld 9-12) met correcte JSON format
|
|
|
|
---
|
|
|
|
### Bug 3: Intake blocks worden niet gerenderd
|
|
|
|
**Symptoom:** Console log toont "Opening artifact: intake_status" maar niets verschijnt
|
|
|
|
**Oorzaak:** `artifact-container.tsx` miste:
|
|
1. Imports voor intake block components
|
|
2. Switch cases in `renderArtifactBlock()`
|
|
3. Titles in `getArtifactTitle()`
|
|
|
|
**Fix:** `components/cortex/artifacts/artifact-container.tsx`
|
|
```typescript
|
|
// Imports toegevoegd:
|
|
import { IntakeStatusBlock } from '../blocks/intake-status-block';
|
|
import { RisicoBlock } from '../blocks/risico-block';
|
|
import { DiagnoseBlock } from '../blocks/diagnose-block';
|
|
|
|
// Switch cases toegevoegd:
|
|
case 'intake_status':
|
|
return <IntakeStatusBlock key={artifact.id} prefill={artifact.prefill} />;
|
|
case 'risico_query':
|
|
return <RisicoBlock key={artifact.id} prefill={artifact.prefill} />;
|
|
case 'diagnose_query':
|
|
return <DiagnoseBlock key={artifact.id} prefill={artifact.prefill} />;
|
|
```
|
|
|
|
---
|
|
|
|
### Bug 4: API retourneert 400 Bad Request
|
|
|
|
**Symptoom:** `/api/cortex/intake/status?patientId=xxx` → 400 error
|
|
|
|
**Oorzaak:** `searchParams.get('intakeId')` retourneert `null`, maar Zod `.optional()` verwacht `undefined`.
|
|
|
|
**Fix:** Alle 3 intake API routes:
|
|
```typescript
|
|
// VOOR:
|
|
const intakeId = searchParams.get('intakeId');
|
|
|
|
// NA:
|
|
const intakeId = searchParams.get('intakeId') || undefined;
|
|
```
|
|
|
|
**Bestanden:**
|
|
- `app/api/cortex/intake/status/route.ts`
|
|
- `app/api/cortex/intake/risico/route.ts`
|
|
- `app/api/cortex/intake/diagnose/route.ts`
|
|
|
|
---
|
|
|
|
## Lessons Learned
|
|
|
|
### 1. Zod + URLSearchParams
|
|
`searchParams.get()` retourneert `null`, niet `undefined`. Gebruik altijd `|| undefined` voor optionele Zod parameters.
|
|
|
|
### 2. AI Chat System Prompt
|
|
Nieuwe intents moeten expliciet in de system prompt staan met:
|
|
- Intent naam
|
|
- Trigger voorbeelden
|
|
- Entities
|
|
- Voorbeeld JSON output
|
|
|
|
### 3. Artifact Rendering
|
|
Nieuwe blocks vereisen wijzigingen op 3 plekken in `artifact-container.tsx`:
|
|
1. Import statement
|
|
2. Switch case in `renderArtifactBlock()`
|
|
3. Title in `getArtifactTitle()`
|
|
|
|
### 4. Intent Flow
|
|
Een intent doorloopt meerdere lagen - elke laag moet de intent kennen:
|
|
1. Reflex classifier (patterns)
|
|
2. AI chat (system prompt)
|
|
3. Action parser (routing)
|
|
4. Artifact container (rendering)
|
|
|
|
---
|
|
|
|
## Checklist: Nieuwe Intent Toevoegen
|
|
|
|
### Stap 1: Types & Patterns
|
|
|
|
**`lib/cortex/types.ts`**
|
|
```typescript
|
|
// 1. Voeg toe aan CortexIntent type
|
|
type CortexIntent =
|
|
| 'bestaande_intent'
|
|
| 'nieuwe_intent' // ← nieuw
|
|
| 'unknown';
|
|
|
|
// 2. Voeg toe aan BLOCK_CONFIGS (als het een block is)
|
|
nieuwe_intent: {
|
|
type: 'nieuwe_intent',
|
|
title: 'Nieuwe Intent',
|
|
size: 'md',
|
|
icon: 'IconName',
|
|
},
|
|
```
|
|
|
|
**`lib/cortex/reflex-classifier.ts`**
|
|
```typescript
|
|
// Voeg patterns toe aan REFLEX_PATTERNS
|
|
nieuwe_intent: [
|
|
{ pattern: /^trigger woorden/i, weight: 1.0 },
|
|
{ pattern: /^alternatieve trigger/i, weight: 0.9 },
|
|
],
|
|
```
|
|
|
|
### Stap 2: AI Chat Integration
|
|
|
|
**`app/api/cortex/chat/route.ts`**
|
|
```typescript
|
|
// In buildSystemPrompt(), voeg toe aan intents lijst:
|
|
- **nieuwe_intent** — Beschrijving
|
|
- Triggers: "woord1", "woord2", "woord3"
|
|
- Entities: entity1 (type), entity2 (type)
|
|
- Actie: Wat er gebeurt
|
|
|
|
// Voeg een voorbeeld toe:
|
|
### Voorbeeld N: Nieuwe Intent
|
|
|
|
**User:**
|
|
"trigger zin"
|
|
|
|
**AI Response:**
|
|
"Korte bevestiging.
|
|
|
|
\`\`\`json
|
|
{
|
|
"type": "action",
|
|
"intent": "nieuwe_intent",
|
|
"entities": { ... },
|
|
"confidence": 0.98,
|
|
"artifact": {
|
|
"type": "nieuwe_intent",
|
|
"prefill": { ... }
|
|
}
|
|
}
|
|
\`\`\`"
|
|
```
|
|
|
|
### Stap 3: Action Parser
|
|
|
|
**`lib/cortex/action-parser.ts`**
|
|
```typescript
|
|
// 1. Voeg toe aan VALID_INTENTS array
|
|
const VALID_INTENTS = [
|
|
'bestaande_intent',
|
|
'nieuwe_intent', // ← nieuw
|
|
];
|
|
|
|
// 2. Voeg toe aan routeIntentToArtifact()
|
|
case 'nieuwe_intent':
|
|
return {
|
|
type: 'nieuwe_intent',
|
|
prefill: { ...entities },
|
|
};
|
|
```
|
|
|
|
### Stap 4: API Route (als data nodig is)
|
|
|
|
**`app/api/cortex/[domain]/route.ts`**
|
|
```typescript
|
|
// Query params validatie - let op null → undefined!
|
|
const optionalParam = searchParams.get('param') || undefined;
|
|
|
|
// Zod schema
|
|
const QuerySchema = z.object({
|
|
requiredParam: z.string().uuid(),
|
|
optionalParam: z.string().optional(), // NOT .nullable()!
|
|
});
|
|
```
|
|
|
|
### Stap 5: Block Component
|
|
|
|
**`components/cortex/blocks/nieuwe-intent-block.tsx`**
|
|
```typescript
|
|
export function NieuweIntentBlock({ prefill }: Props) {
|
|
const { patientId, hasPatientContext } = useIntakeContext(prefill);
|
|
|
|
const { data, isLoading, error } = useBlockData<ResponseType>({
|
|
endpoint: '/api/cortex/...',
|
|
params: { patientId },
|
|
enabled: hasPatientContext,
|
|
});
|
|
|
|
if (!hasPatientContext) return <BlockEmpty ... />;
|
|
if (isLoading) return <BlockLoading ... />;
|
|
if (error) return <BlockError ... />;
|
|
|
|
return <BlockContainer>...</BlockContainer>;
|
|
}
|
|
```
|
|
|
|
### Stap 6: Artifact Container
|
|
|
|
**`components/cortex/artifacts/artifact-container.tsx`**
|
|
```typescript
|
|
// 1. Import
|
|
import { NieuweIntentBlock } from '../blocks/nieuwe-intent-block';
|
|
|
|
// 2. renderArtifactBlock() switch case
|
|
case 'nieuwe_intent':
|
|
return <NieuweIntentBlock key={artifact.id} prefill={artifact.prefill} />;
|
|
|
|
// 3. getArtifactTitle() switch case
|
|
case 'nieuwe_intent':
|
|
return 'Nieuwe Intent Titel';
|
|
```
|
|
|
|
### Stap 7: Navigatie Intent (optioneel)
|
|
|
|
Als de intent navigeert ipv een block toont:
|
|
|
|
**`components/cortex/command-center/command-center.tsx`**
|
|
```typescript
|
|
// In useEffect voor pendingAction:
|
|
if (pendingAction.intent === 'nieuwe_navigeer') {
|
|
// Extract target
|
|
const target = pendingAction.entities.navigationTarget;
|
|
|
|
// Navigate
|
|
router.push(`/epd/path/${target}`);
|
|
|
|
// Toast feedback
|
|
toast({ title: 'Navigeren...', description: `Naar ${target}` });
|
|
|
|
// Clear action
|
|
setPendingAction(null);
|
|
return;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Checklist: Nieuwe UI Block
|
|
|
|
1. **Block component maken**
|
|
- `components/cortex/blocks/xxx-block.tsx`
|
|
- Gebruik shared components: `BlockContainer`, `BlockLoading`, `BlockError`, `BlockEmpty`
|
|
- Gebruik hooks: `useBlockData`, `useIntakeContext`
|
|
|
|
2. **API route maken** (indien nodig)
|
|
- `app/api/cortex/[domain]/route.ts`
|
|
- Let op: `|| undefined` voor optionele params!
|
|
|
|
3. **Artifact container updaten**
|
|
- Import toevoegen
|
|
- Switch case toevoegen
|
|
- Title toevoegen
|
|
|
|
4. **Types updaten**
|
|
- `BLOCK_CONFIGS` in `types.ts`
|
|
|
|
---
|
|
|
|
## Gewijzigde Bestanden (Totaal)
|
|
|
|
| Bestand | Wijziging |
|
|
|---------|-----------|
|
|
| `app/api/cortex/chat/route.ts` | Zod fix + intake intents in prompt |
|
|
| `app/api/cortex/intake/status/route.ts` | null → undefined fix + logging |
|
|
| `app/api/cortex/intake/risico/route.ts` | null → undefined fix |
|
|
| `app/api/cortex/intake/diagnose/route.ts` | null → undefined fix |
|
|
| `lib/cortex/chat-api.ts` | Betere error logging |
|
|
| `lib/cortex/hooks/use-block-data.ts` | Error details logging |
|
|
| `components/cortex/artifacts/artifact-container.tsx` | Intake blocks import + render |
|
|
| `docs/.../testplan-intake-blocks-mvp.md` | Nieuw testplan |
|
|
| `docs/.../session-log-2026-02-03.md` | Deze log |
|
|
|
|
---
|
|
|
|
## Eindstatus
|
|
|
|
| Component | Status |
|
|
|-----------|--------|
|
|
| `intake_status` intent | ✅ Werkt |
|
|
| `risico_query` intent | ✅ Werkt |
|
|
| `diagnose_query` intent | ✅ Werkt |
|
|
| `intake_navigeer` intent | ✅ Werkt |
|
|
| IntakeStatusBlock | ✅ Toont data |
|
|
| RisicoBlock | ✅ Toont data |
|
|
| DiagnoseBlock | ✅ Toont data |
|
|
| Navigatie | ✅ Werkt met toast |
|
|
|
|
---
|
|
|
|
## Volgende Stappen
|
|
|
|
1. [ ] E2E tests uitvoeren volgens testplan
|
|
2. [ ] Performance monitoring (API response times)
|
|
3. [ ] Voice input testen
|
|
4. [ ] Fase 3b intents toevoegen (kindcheck, anamnese, behandeladvies)
|