feat(cortex): Epic 0 - Foundation & Context voor V2 architectuur
Implementeert de basis voor de Cortex V2 three-layer architecture. E0.S1 - CortexContext Types (lib/cortex/types.ts): - EscalationReason type voor Reflex → Orchestrator - LocalClassificationResult met ambiguity detection - CortexContext voor AI classificatie context - IntentChain en IntentAction voor multi-intent flows - NudgeSuggestion voor proactieve suggesties - CONFIDENCE_THRESHOLD (0.7) en AMBIGUITY_THRESHOLD (0.1) - getCurrentShift() utility (DRY - herbruikbaar) E0.S2 - Context API (app/api/cortex/context/route.ts): - GET endpoint voor huidige context - Haalt agenda vandaag op uit database - Retourneert CortexContext object E0.S3 - Feature Flags (lib/config/feature-flags.ts): - CORTEX_V2_ENABLED, CORTEX_MULTI_INTENT - CORTEX_NUDGE, CORTEX_LOGGING - isFeatureEnabled() helper - Dev mode: default true E0.S4 - CortexStore V2 (stores/cortex-store.ts): - Types geïmporteerd uit lib/cortex (DRY) - ChatEntities type voor AI responses (string-based) - V2 state: context, activeChain, chainHistory, suggestions - V2 actions: chain management, nudge management, clarification E0.S5 - Classification Logging (lib/cortex/logger.ts): - PII sanitization (BSN, tel, namen, email) - logClassification(), logEscalation(), logNudge() - logPerformance() met threshold checks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
111
app/api/cortex/context/route.ts
Normal file
111
app/api/cortex/context/route.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { createClient } from '@/lib/auth/server';
|
||||
import { getCurrentShift } from '@/lib/cortex/types';
|
||||
import type { CortexContext } from '@/lib/cortex/types';
|
||||
|
||||
/**
|
||||
* Cortex Context API
|
||||
*
|
||||
* GET /api/cortex/context
|
||||
*
|
||||
* Returns the current context for AI classification.
|
||||
* Includes: shift, agenda today, recent intents.
|
||||
* Note: activePatient is set client-side via store.
|
||||
*/
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Auth check
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
data: { user },
|
||||
error: authError,
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (authError || !user) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Niet geautoriseerd. Log opnieuw in.' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Get practitioner info
|
||||
const { data: practitioner } = await supabase
|
||||
.from('practitioners')
|
||||
.select('id')
|
||||
.eq('user_id', user.id)
|
||||
.single();
|
||||
|
||||
// Get today's date range
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
// Fetch today's appointments
|
||||
const { data: appointments } = await supabase
|
||||
.from('encounters')
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
period_start,
|
||||
type_display,
|
||||
patient:patients(id, name_given, name_prefix, name_family)
|
||||
`
|
||||
)
|
||||
.eq('practitioner_id', practitioner?.id ?? '')
|
||||
.gte('period_start', today.toISOString())
|
||||
.lt('period_start', tomorrow.toISOString())
|
||||
.order('period_start');
|
||||
|
||||
// Build context object
|
||||
const context: CortexContext = {
|
||||
activePatient: null, // Set by client via store
|
||||
currentView: 'dashboard', // Default, overridden by client
|
||||
shift: getCurrentShift(),
|
||||
currentTime: new Date(),
|
||||
agendaToday: (appointments || []).map((apt) => {
|
||||
// Handle the nested patient relation
|
||||
const patient = apt.patient as {
|
||||
id: string;
|
||||
name_given: string[];
|
||||
name_prefix: string | null;
|
||||
name_family: string;
|
||||
} | null;
|
||||
|
||||
// Construct full name: "Voornaam [tussenvoegsel] Achternaam"
|
||||
const fullName = patient
|
||||
? [
|
||||
patient.name_given?.[0],
|
||||
patient.name_prefix,
|
||||
patient.name_family,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
: 'Onbekend';
|
||||
|
||||
return {
|
||||
time: new Date(apt.period_start).toLocaleTimeString('nl-NL', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
}),
|
||||
patientName: fullName,
|
||||
patientId: patient?.id || '',
|
||||
type: apt.type_display || 'afspraak',
|
||||
};
|
||||
}),
|
||||
recentIntents: [], // Populated by client from store history
|
||||
};
|
||||
|
||||
return NextResponse.json({ context }, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error('[Cortex Context API] Error:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error:
|
||||
'Er ging iets mis bij het ophalen van context. Probeer het opnieuw.',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user