refactor: rename swift → cortex in code and documentation
Complete rename of "swift" terminology to "cortex" across the codebase: Code Changes: - Rename directories: lib/swift → lib/cortex, components/swift → components/cortex - Rename API routes: /api/swift/* → /api/cortex/* - Rename page routes: /epd/swift → /epd/cortex - Rename store: swift-store.ts → cortex-store.ts Type Renames: - SwiftIntent → CortexIntent - SwiftStore → CortexStore - useSwiftStore → useCortexStore - SwiftContext → CortexContext - useSwiftVoice → useCortexVoice Documentation Updates (docs/intent/): - Safety Net → Nudge (Layer 3 rename) - SafetySuggestion → NudgeSuggestion - evaluateSafetyNet → evaluateNudge - SWIFT_* feature flags → CORTEX_* - All path references updated to match new structure Files affected: 47 files, ~700 lines changed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Layout
|
* Cortex Layout
|
||||||
*
|
*
|
||||||
* Full-screen layout for Swift Command Center.
|
* Full-screen layout for Cortex Command Center.
|
||||||
* No sidebar - the entire screen is the Command Center.
|
* No sidebar - the entire screen is the Command Center.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -9,11 +9,11 @@ import type { ReactNode } from 'react';
|
|||||||
import { getUser } from '@/lib/auth/server';
|
import { getUser } from '@/lib/auth/server';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
interface SwiftLayoutProps {
|
interface CortexLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function SwiftLayout({ children }: SwiftLayoutProps) {
|
export default async function CortexLayout({ children }: CortexLayoutProps) {
|
||||||
const user = await getUser();
|
const user = await getUser();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
13
app/(cortex)/epd/cortex/page.tsx
Normal file
13
app/(cortex)/epd/cortex/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cortex Command Center Page
|
||||||
|
*
|
||||||
|
* Main entry point for Cortex - the Contextual UI EPD.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CommandCenter } from '@/components/cortex';
|
||||||
|
|
||||||
|
export default function CortexPage() {
|
||||||
|
return <CommandCenter />;
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Swift Command Center Page
|
|
||||||
*
|
|
||||||
* Main entry point for Swift - the Contextual UI EPD.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { CommandCenter } from '@/components/swift';
|
|
||||||
|
|
||||||
export default function SwiftPage() {
|
|
||||||
return <CommandCenter />;
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Chat API Route (v3.0)
|
* Cortex Chat API Route (v3.0)
|
||||||
*
|
*
|
||||||
* Streaming chat endpoint voor Swift Assistent met Server-Sent Events (SSE).
|
* Streaming chat endpoint voor Cortex Assistent met Server-Sent Events (SSE).
|
||||||
*
|
*
|
||||||
* Epic: E3 (Chat API & Swift Assistent)
|
* Epic: E3 (Chat API & Cortex Assistent)
|
||||||
* Story: E3.S1 (Chat API endpoint skeleton)
|
* Story: E3.S1 (Chat API endpoint skeleton)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { getSession } from '@/lib/auth/server';
|
import { getSession } from '@/lib/auth/server';
|
||||||
import type { ChatMessage as ChatMessageType, ChatAction } from '@/stores/swift-store';
|
import type { ChatMessage as ChatMessageType, ChatAction } from '@/stores/cortex-store';
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
const SWIFT_MODEL = process.env.SWIFT_MODEL ?? 'claude-sonnet-4-20250514';
|
const CORTEX_MODEL = process.env.CORTEX_MODEL ?? 'claude-sonnet-4-20250514';
|
||||||
const MAX_HISTORY_MESSAGES = 20;
|
const MAX_HISTORY_MESSAGES = 20;
|
||||||
const MAX_USER_MESSAGE_LENGTH = 2000;
|
const MAX_USER_MESSAGE_LENGTH = 2000;
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ const RequestSchema = z.object({
|
|||||||
type RequestData = z.infer<typeof RequestSchema>;
|
type RequestData = z.infer<typeof RequestSchema>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Swift Assistent system prompt (E3.S3)
|
* Build Cortex Assistent system prompt (E3.S3)
|
||||||
* Full prompt with intent detection, entity extraction, and action generation
|
* Full prompt with intent detection, entity extraction, and action generation
|
||||||
*/
|
*/
|
||||||
function buildSystemPrompt(context?: RequestData['context']): string {
|
function buildSystemPrompt(context?: RequestData['context']): string {
|
||||||
@@ -92,7 +92,7 @@ function buildSystemPrompt(context?: RequestData['context']): string {
|
|||||||
|
|
||||||
const shiftContext = context?.shift ?? 'ochtend';
|
const shiftContext = context?.shift ?? 'ochtend';
|
||||||
|
|
||||||
return `Je bent Swift Assistent, een medische assistent voor Swift EPD, een Nederlands EPD-systeem voor GGZ-instellingen.
|
return `Je bent Cortex Assistent, een medische assistent voor Cortex EPD, een Nederlands EPD-systeem voor GGZ-instellingen.
|
||||||
|
|
||||||
## Je rol
|
## Je rol
|
||||||
|
|
||||||
@@ -524,7 +524,7 @@ export async function POST(request: NextRequest) {
|
|||||||
// 4. Prepare conversation history (limit to last N messages)
|
// 4. Prepare conversation history (limit to last N messages)
|
||||||
const history = messages.slice(-MAX_HISTORY_MESSAGES);
|
const history = messages.slice(-MAX_HISTORY_MESSAGES);
|
||||||
|
|
||||||
// 5. Build Swift Assistent system prompt (E3.S3)
|
// 5. Build Cortex Assistent system prompt (E3.S3)
|
||||||
const systemPrompt = buildSystemPrompt(context);
|
const systemPrompt = buildSystemPrompt(context);
|
||||||
|
|
||||||
// 6. Check for Claude API key
|
// 6. Check for Claude API key
|
||||||
@@ -543,7 +543,7 @@ export async function POST(request: NextRequest) {
|
|||||||
Accept: 'text/event-stream',
|
Accept: 'text/event-stream',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: SWIFT_MODEL,
|
model: CORTEX_MODEL,
|
||||||
max_tokens: 2048,
|
max_tokens: 2048,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
stream: true,
|
stream: true,
|
||||||
@@ -667,7 +667,7 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: 'Ongeldige JSON payload' }, { status: 400 });
|
return NextResponse.json({ error: 'Ongeldige JSON payload' }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('Swift chat endpoint error:', error);
|
console.error('Cortex chat endpoint error:', error);
|
||||||
return NextResponse.json({ error: 'Onverwachte serverfout' }, { status: 500 });
|
return NextResponse.json({ error: 'Onverwachte serverfout' }, { status: 500 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
import { createClient } from '@/lib/auth/server';
|
import { createClient } from '@/lib/auth/server';
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { classifyIntent, isHighConfidence } from '@/lib/swift/intent-classifier';
|
import { classifyIntent, isHighConfidence } from '@/lib/cortex/intent-classifier';
|
||||||
import { classifyIntentWithAI } from '@/lib/swift/intent-classifier-ai';
|
import { classifyIntentWithAI } from '@/lib/cortex/intent-classifier-ai';
|
||||||
import { extractEntities } from '@/lib/swift/entity-extractor';
|
import { extractEntities } from '@/lib/cortex/entity-extractor';
|
||||||
import type { IntentClassificationResult } from '@/lib/swift/types';
|
import type { IntentClassificationResult } from '@/lib/cortex/types';
|
||||||
|
|
||||||
// Request schema
|
// Request schema
|
||||||
const ClassifyRequestSchema = z.object({
|
const ClassifyRequestSchema = z.object({
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ interface EPDSidebarProps {
|
|||||||
|
|
||||||
// LEVEL 1: Behandelaar Context Navigation
|
// LEVEL 1: Behandelaar Context Navigation
|
||||||
const level1NavigationItems: NavigationItem[] = [
|
const level1NavigationItems: NavigationItem[] = [
|
||||||
{ id: "swift", name: "Swift", icon: Zap, href: "/epd/swift" },
|
{ id: "cortex", name: "Cortex", icon: Zap, href: "/epd/cortex" },
|
||||||
{ id: "dashboard", name: "Dashboard", icon: LayoutDashboard, href: "/epd/dashboard" },
|
{ id: "dashboard", name: "Dashboard", icon: LayoutDashboard, href: "/epd/dashboard" },
|
||||||
{ id: "clients", name: "Cliënten", icon: Users, href: "/epd/patients" },
|
{ id: "clients", name: "Cliënten", icon: Users, href: "/epd/patients" },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
* Stories: E1.S3, E3.S6, E4.S1, E4.S2
|
* Stories: E1.S3, E3.S6, E4.S1, E4.S2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { ArtifactContainer } from './artifact-container';
|
import { ArtifactContainer } from './artifact-container';
|
||||||
|
|
||||||
export function ArtifactArea() {
|
export function ArtifactArea() {
|
||||||
const openArtifacts = useSwiftStore((s) => s.openArtifacts);
|
const openArtifacts = useCortexStore((s) => s.openArtifacts);
|
||||||
const activeArtifactId = useSwiftStore((s) => s.activeArtifactId);
|
const activeArtifactId = useCortexStore((s) => s.activeArtifactId);
|
||||||
const switchArtifact = useSwiftStore((s) => s.switchArtifact);
|
const switchArtifact = useCortexStore((s) => s.switchArtifact);
|
||||||
const closeArtifact = useSwiftStore((s) => s.closeArtifact);
|
const closeArtifact = useCortexStore((s) => s.closeArtifact);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArtifactContainer
|
<ArtifactContainer
|
||||||
@@ -18,7 +18,7 @@ import { OverdrachtBlock } from '../blocks/overdracht-block';
|
|||||||
import { PatientDashboardBlock } from '../blocks/patient-dashboard-block';
|
import { PatientDashboardBlock } from '../blocks/patient-dashboard-block';
|
||||||
import { FallbackPicker } from '../blocks/fallback-picker';
|
import { FallbackPicker } from '../blocks/fallback-picker';
|
||||||
import { APPOINTMENT_TYPES, type AppointmentTypeCode, type LocationClassCode } from '@/app/epd/agenda/types';
|
import { APPOINTMENT_TYPES, type AppointmentTypeCode, type LocationClassCode } from '@/app/epd/agenda/types';
|
||||||
import type { Artifact, BlockType } from '@/stores/swift-store';
|
import type { Artifact, BlockType } from '@/stores/cortex-store';
|
||||||
|
|
||||||
interface ArtifactContainerProps {
|
interface ArtifactContainerProps {
|
||||||
artifacts: Artifact[];
|
artifacts: Artifact[];
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { Artifact } from '@/stores/swift-store';
|
import type { Artifact } from '@/stores/cortex-store';
|
||||||
|
|
||||||
interface ArtifactTabProps {
|
interface ArtifactTabProps {
|
||||||
artifact: Artifact;
|
artifact: Artifact;
|
||||||
@@ -93,7 +93,7 @@ export function AgendaCreateForm({ prefillData, onClose }: AgendaCreateFormProps
|
|||||||
|
|
||||||
setIsSearching(true);
|
setIsSearching(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/swift/patients/search?q=${encodeURIComponent(searchQuery)}`);
|
const res = await fetch(`/api/cortex/patients/search?q=${encodeURIComponent(searchQuery)}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setSearchResults(data.patients || []);
|
setSearchResults(data.patients || []);
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { motion, type Variants } from 'framer-motion';
|
import { motion, type Variants } from 'framer-motion';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import type { BlockSize } from '@/lib/swift/types';
|
import type { BlockSize } from '@/lib/cortex/types';
|
||||||
|
|
||||||
interface BlockContainerProps {
|
interface BlockContainerProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -66,7 +66,7 @@ const closeButtonVariants = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function BlockContainer({ title, size = 'md', children }: BlockContainerProps) {
|
export function BlockContainer({ title, size = 'md', children }: BlockContainerProps) {
|
||||||
const { closeBlock } = useSwiftStore();
|
const { closeBlock } = useCortexStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { BlockContainer } from './block-container';
|
import { BlockContainer } from './block-container';
|
||||||
import type { BlockPrefillData } from '@/stores/swift-store';
|
import type { BlockPrefillData } from '@/stores/cortex-store';
|
||||||
import { BLOCK_CONFIGS } from '@/lib/swift/types';
|
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||||
import {
|
import {
|
||||||
VERPLEEGKUNDIG_CATEGORIES,
|
VERPLEEGKUNDIG_CATEGORIES,
|
||||||
CATEGORY_CONFIG,
|
CATEGORY_CONFIG,
|
||||||
@@ -24,7 +24,7 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Loader2, Search, User, RefreshCw } from 'lucide-react';
|
import { Loader2, Search, User, RefreshCw } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { safeFetch, getErrorInfo, retryFetch } from '@/lib/swift/error-handler';
|
import { safeFetch, getErrorInfo, retryFetch } from '@/lib/cortex/error-handler';
|
||||||
|
|
||||||
interface DagnotitieBlockProps {
|
interface DagnotitieBlockProps {
|
||||||
prefill?: BlockPrefillData;
|
prefill?: BlockPrefillData;
|
||||||
@@ -39,7 +39,7 @@ interface Patient {
|
|||||||
|
|
||||||
export function DagnotatieBlock({ prefill }: DagnotitieBlockProps) {
|
export function DagnotatieBlock({ prefill }: DagnotitieBlockProps) {
|
||||||
const config = BLOCK_CONFIGS.dagnotitie;
|
const config = BLOCK_CONFIGS.dagnotitie;
|
||||||
const { closeBlock } = useSwiftStore();
|
const { closeBlock } = useCortexStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
// Form state
|
// Form state
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
import { useEffect, useCallback } from 'react';
|
import { useEffect, useCallback } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { FileText, Search, ArrowRightLeft, X } from 'lucide-react';
|
import { FileText, Search, ArrowRightLeft, X } from 'lucide-react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import type { BlockType } from '@/lib/swift/types';
|
import type { BlockType } from '@/lib/cortex/types';
|
||||||
|
|
||||||
interface FallbackPickerProps {
|
interface FallbackPickerProps {
|
||||||
originalInput?: string;
|
originalInput?: string;
|
||||||
@@ -54,7 +54,7 @@ const BLOCK_OPTIONS: BlockOption[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function FallbackPicker({ originalInput }: FallbackPickerProps) {
|
export function FallbackPicker({ originalInput }: FallbackPickerProps) {
|
||||||
const { openBlock, closeBlock, addRecentAction } = useSwiftStore();
|
const { openBlock, closeBlock, addRecentAction } = useCortexStore();
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(option: BlockOption) => {
|
(option: BlockOption) => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Blocks Barrel Export
|
* Cortex Blocks Barrel Export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { BlockContainer } from './block-container';
|
export { BlockContainer } from './block-container';
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { BlockContainer } from './block-container';
|
import { BlockContainer } from './block-container';
|
||||||
import type { BlockPrefillData } from '@/stores/swift-store';
|
import type { BlockPrefillData } from '@/stores/cortex-store';
|
||||||
import { BLOCK_CONFIGS } from '@/lib/swift/types';
|
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||||
import type { PatientOverzicht, AISamenvatting } from '@/lib/types/overdracht';
|
import type { PatientOverzicht, AISamenvatting } from '@/lib/types/overdracht';
|
||||||
import {
|
import {
|
||||||
Sparkles,
|
Sparkles,
|
||||||
@@ -28,8 +28,8 @@ import { format } from 'date-fns';
|
|||||||
import { nl } from 'date-fns/locale/nl';
|
import { nl } from 'date-fns/locale/nl';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { safeFetch, getErrorInfo, retryFetch } from '@/lib/swift/error-handler';
|
import { safeFetch, getErrorInfo, retryFetch } from '@/lib/cortex/error-handler';
|
||||||
import { LinkedEvidence } from '@/components/swift/shared/linked-evidence';
|
import { LinkedEvidence } from '@/components/cortex/shared/linked-evidence';
|
||||||
|
|
||||||
interface OverdrachtBlockProps {
|
interface OverdrachtBlockProps {
|
||||||
prefill?: BlockPrefillData;
|
prefill?: BlockPrefillData;
|
||||||
@@ -53,7 +53,7 @@ interface PatientSummary {
|
|||||||
|
|
||||||
export function OverdrachtBlock({ prefill }: OverdrachtBlockProps) {
|
export function OverdrachtBlock({ prefill }: OverdrachtBlockProps) {
|
||||||
const config = BLOCK_CONFIGS.overdracht;
|
const config = BLOCK_CONFIGS.overdracht;
|
||||||
const { activePatient } = useSwiftStore();
|
const { activePatient } = useCortexStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const [period, setPeriod] = useState<PeriodValue>('1d');
|
const [period, setPeriod] = useState<PeriodValue>('1d');
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { BlockContainer } from './block-container';
|
import { BlockContainer } from './block-container';
|
||||||
import { BLOCK_CONFIGS } from '@/lib/swift/types';
|
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||||
import type { PatientDetail, Report, VitalSign, Condition, RiskAssessment } from '@/lib/types/overdracht';
|
import type { PatientDetail, Report, VitalSign, Condition, RiskAssessment } from '@/lib/types/overdracht';
|
||||||
import { Loader2, FileText, Activity, Stethoscope, AlertTriangle, Calendar } from 'lucide-react';
|
import { Loader2, FileText, Activity, Stethoscope, AlertTriangle, Calendar } from 'lucide-react';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
@@ -18,7 +18,7 @@ import { nl } from 'date-fns/locale/nl';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export function PatientContextCard() {
|
export function PatientContextCard() {
|
||||||
const { activePatient, closeBlock } = useSwiftStore();
|
const { activePatient, closeBlock } = useCortexStore();
|
||||||
const [data, setData] = useState<PatientDetail | null>(null);
|
const [data, setData] = useState<PatientDetail | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -19,10 +19,10 @@ import {
|
|||||||
User,
|
User,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { BlockContainer } from './block-container';
|
import { BlockContainer } from './block-container';
|
||||||
import { safeFetch, getErrorInfo } from '@/lib/swift/error-handler';
|
import { safeFetch, getErrorInfo } from '@/lib/cortex/error-handler';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { BLOCK_CONFIGS } from '@/lib/swift/types';
|
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||||
import type { BlockPrefillData } from '@/stores/swift-store';
|
import type { BlockPrefillData } from '@/stores/cortex-store';
|
||||||
import type { FHIRPatient } from '@/lib/fhir';
|
import type { FHIRPatient } from '@/lib/fhir';
|
||||||
import type { Intake } from '@/lib/types/intake';
|
import type { Intake } from '@/lib/types/intake';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { BlockContainer } from './block-container';
|
import { BlockContainer } from './block-container';
|
||||||
import type { BlockPrefillData } from '@/stores/swift-store';
|
import type { BlockPrefillData } from '@/stores/cortex-store';
|
||||||
import { BLOCK_CONFIGS } from '@/lib/swift/types';
|
import { BLOCK_CONFIGS } from '@/lib/cortex/types';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Loader2, Search, User, Check } from 'lucide-react';
|
import { Loader2, Search, User, Check } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { safeFetch, getErrorInfo } from '@/lib/swift/error-handler';
|
import { safeFetch, getErrorInfo } from '@/lib/cortex/error-handler';
|
||||||
|
|
||||||
interface ZoekenBlockProps {
|
interface ZoekenBlockProps {
|
||||||
prefill?: BlockPrefillData;
|
prefill?: BlockPrefillData;
|
||||||
@@ -34,7 +34,7 @@ interface PatientSearchResult {
|
|||||||
|
|
||||||
export function ZoekenBlock({ prefill }: ZoekenBlockProps) {
|
export function ZoekenBlock({ prefill }: ZoekenBlockProps) {
|
||||||
const config = BLOCK_CONFIGS.zoeken;
|
const config = BLOCK_CONFIGS.zoeken;
|
||||||
const { closeBlock, setActivePatient, addRecentAction, openArtifact } = useSwiftStore();
|
const { closeBlock, setActivePatient, addRecentAction, openArtifact } = useCortexStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const prefillQuery = prefill?.patientName || prefill?.query || '';
|
const prefillQuery = prefill?.patientName || prefill?.query || '';
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
import { useState, useRef, KeyboardEvent, ChangeEvent, forwardRef, useImperativeHandle } from 'react';
|
import { useState, useRef, KeyboardEvent, ChangeEvent, forwardRef, useImperativeHandle } from 'react';
|
||||||
import { Send, Mic } from 'lucide-react';
|
import { Send, Mic } from 'lucide-react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface ChatInputProps {
|
interface ChatInputProps {
|
||||||
@@ -32,7 +32,7 @@ export const ChatInput = forwardRef<ChatInputHandle, ChatInputProps>(function Ch
|
|||||||
}, ref) {
|
}, ref) {
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const addChatMessage = useSwiftStore((s) => s.addChatMessage);
|
const addChatMessage = useCortexStore((s) => s.addChatMessage);
|
||||||
|
|
||||||
// Expose focus and clear methods to parent
|
// Expose focus and clear methods to parent
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
@@ -14,8 +14,8 @@ import { format } from 'date-fns';
|
|||||||
import { nl } from 'date-fns/locale';
|
import { nl } from 'date-fns/locale';
|
||||||
import { CheckCircle2, Sparkles } from 'lucide-react';
|
import { CheckCircle2, Sparkles } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { ChatMessage as ChatMessageType } from '@/stores/swift-store';
|
import type { ChatMessage as ChatMessageType } from '@/stores/cortex-store';
|
||||||
import { getConfidenceLabel } from '@/lib/swift/action-parser';
|
import { getConfidenceLabel } from '@/lib/cortex/action-parser';
|
||||||
|
|
||||||
// Message styling configuration per type
|
// Message styling configuration per type
|
||||||
const MESSAGE_STYLES = {
|
const MESSAGE_STYLES = {
|
||||||
@@ -13,20 +13,20 @@ import { useRef, useEffect, useState, useCallback } from 'react';
|
|||||||
import { ArrowDown } from 'lucide-react';
|
import { ArrowDown } from 'lucide-react';
|
||||||
import { ChatMessage } from './chat-message';
|
import { ChatMessage } from './chat-message';
|
||||||
import { ChatInput, ChatInputHandle } from './chat-input';
|
import { ChatInput, ChatInputHandle } from './chat-input';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { sendChatMessage } from '@/lib/swift/chat-api';
|
import { sendChatMessage } from '@/lib/cortex/chat-api';
|
||||||
import { parseActionFromResponse, shouldOpenArtifact } from '@/lib/swift/action-parser';
|
import { parseActionFromResponse, shouldOpenArtifact } from '@/lib/cortex/action-parser';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
export function ChatPanel() {
|
export function ChatPanel() {
|
||||||
const chatMessages = useSwiftStore((s) => s.chatMessages);
|
const chatMessages = useCortexStore((s) => s.chatMessages);
|
||||||
const addChatMessage = useSwiftStore((s) => s.addChatMessage);
|
const addChatMessage = useCortexStore((s) => s.addChatMessage);
|
||||||
const updateLastMessage = useSwiftStore((s) => s.updateLastMessage);
|
const updateLastMessage = useCortexStore((s) => s.updateLastMessage);
|
||||||
const setStreaming = useSwiftStore((s) => s.setStreaming);
|
const setStreaming = useCortexStore((s) => s.setStreaming);
|
||||||
const isStreaming = useSwiftStore((s) => s.isStreaming);
|
const isStreaming = useCortexStore((s) => s.isStreaming);
|
||||||
const setPendingAction = useSwiftStore((s) => s.setPendingAction);
|
const setPendingAction = useCortexStore((s) => s.setPendingAction);
|
||||||
const activePatient = useSwiftStore((s) => s.activePatient);
|
const activePatient = useCortexStore((s) => s.activePatient);
|
||||||
const shift = useSwiftStore((s) => s.shift);
|
const shift = useCortexStore((s) => s.shift);
|
||||||
|
|
||||||
// Refs for scrolling
|
// Refs for scrolling
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -108,7 +108,7 @@ export function ChatPanel() {
|
|||||||
<div className="max-w-md text-center text-slate-500">
|
<div className="max-w-md text-center text-slate-500">
|
||||||
<div className="text-4xl mb-4">💬</div>
|
<div className="text-4xl mb-4">💬</div>
|
||||||
<h3 className="text-lg font-medium text-slate-700 mb-2">
|
<h3 className="text-lg font-medium text-slate-700 mb-2">
|
||||||
Welkom bij Swift Assistent
|
Welkom bij Cortex Assistent
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm mb-4">
|
<p className="text-sm mb-4">
|
||||||
Typ of spreek wat je wilt doen...
|
Typ of spreek wat je wilt doen...
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import type { BlockType, BlockPrefillData } from '@/stores/swift-store';
|
import type { BlockType, BlockPrefillData } from '@/stores/cortex-store';
|
||||||
import { DagnotatieBlock } from '../blocks/dagnotitie-block';
|
import { DagnotatieBlock } from '../blocks/dagnotitie-block';
|
||||||
import { ZoekenBlock } from '../blocks/zoeken-block';
|
import { ZoekenBlock } from '../blocks/zoeken-block';
|
||||||
import { OverdrachtBlock } from '../blocks/overdracht-block';
|
import { OverdrachtBlock } from '../blocks/overdracht-block';
|
||||||
@@ -16,7 +16,7 @@ import { PatientContextCard } from '../blocks/patient-context-card';
|
|||||||
import { FallbackPicker } from '../blocks/fallback-picker';
|
import { FallbackPicker } from '../blocks/fallback-picker';
|
||||||
|
|
||||||
export function CanvasArea() {
|
export function CanvasArea() {
|
||||||
const { activeBlock, prefillData, activePatient } = useSwiftStore();
|
const { activeBlock, prefillData, activePatient } = useCortexStore();
|
||||||
|
|
||||||
function renderBlock(blockType: BlockType, prefill: BlockPrefillData) {
|
function renderBlock(blockType: BlockType, prefill: BlockPrefillData) {
|
||||||
switch (blockType) {
|
switch (blockType) {
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Command Center (v3.0)
|
* Command Center (v3.0)
|
||||||
*
|
*
|
||||||
* Main container for the Swift interface.
|
* Main container for the Cortex interface.
|
||||||
* Split-screen layout: Chat Panel (40%) | Artifact Area (60%)
|
* Split-screen layout: Chat Panel (40%) | Artifact Area (60%)
|
||||||
*
|
*
|
||||||
* Layout specs:
|
* Layout specs:
|
||||||
@@ -17,16 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useCallback, useRef } from 'react';
|
import { useEffect, useCallback, useRef } from 'react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { ContextBar } from './context-bar';
|
import { ContextBar } from './context-bar';
|
||||||
import { OfflineBanner } from './offline-banner';
|
import { OfflineBanner } from './offline-banner';
|
||||||
import { ChatPanel } from '../chat/chat-panel';
|
import { ChatPanel } from '../chat/chat-panel';
|
||||||
import { ArtifactArea } from '../artifacts/artifact-area';
|
import { ArtifactArea } from '../artifacts/artifact-area';
|
||||||
import { getArtifactTitle } from '../artifacts/artifact-container';
|
import { getArtifactTitle } from '../artifacts/artifact-container';
|
||||||
import { routeIntentToArtifact } from '@/lib/swift/action-parser';
|
import { routeIntentToArtifact } from '@/lib/cortex/action-parser';
|
||||||
|
|
||||||
export function CommandCenter() {
|
export function CommandCenter() {
|
||||||
const { closeAllArtifacts, openArtifacts, openArtifact, pendingAction, setPendingAction } = useSwiftStore();
|
const { closeAllArtifacts, openArtifacts, openArtifact, pendingAction, setPendingAction } = useCortexStore();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// Global keyboard shortcuts
|
// Global keyboard shortcuts
|
||||||
@@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { forwardRef, useState, useEffect, useRef } from 'react';
|
import { forwardRef, useState, useEffect, useRef } from 'react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import { useSwiftVoice } from '@/lib/swift/use-swift-voice';
|
import { useCortexVoice } from '@/lib/cortex/use-cortex-voice';
|
||||||
import type { BlockType } from '@/lib/swift/types';
|
import type { BlockType } from '@/lib/cortex/types';
|
||||||
import { Mic, MicOff, Send, Loader2 } from 'lucide-react';
|
import { Mic, MicOff, Send, Loader2 } from 'lucide-react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { safeFetch, getErrorInfo } from '@/lib/swift/error-handler';
|
import { safeFetch, getErrorInfo } from '@/lib/cortex/error-handler';
|
||||||
import { routeIntentToArtifact } from '@/lib/swift/action-parser';
|
import { routeIntentToArtifact } from '@/lib/cortex/action-parser';
|
||||||
|
|
||||||
export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_, ref) {
|
export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_, ref) {
|
||||||
const {
|
const {
|
||||||
@@ -34,7 +34,7 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
|
|||||||
openBlock,
|
openBlock,
|
||||||
openArtifact,
|
openArtifact,
|
||||||
addRecentAction,
|
addRecentAction,
|
||||||
} = useSwiftStore();
|
} = useCortexStore();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -46,7 +46,7 @@ export const CommandInput = forwardRef<HTMLInputElement>(function CommandInput(_
|
|||||||
stopRecording,
|
stopRecording,
|
||||||
analyserNode,
|
analyserNode,
|
||||||
isBrowserSupported,
|
isBrowserSupported,
|
||||||
} = useSwiftVoice();
|
} = useCortexVoice();
|
||||||
|
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
const waveformRef = useRef<HTMLCanvasElement>(null);
|
const waveformRef = useRef<HTMLCanvasElement>(null);
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Height: 48px (h-12)
|
* Height: 48px (h-12)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useSwiftStore, type ShiftType } from '@/stores/swift-store';
|
import { useCortexStore, type ShiftType } from '@/stores/cortex-store';
|
||||||
import { Sun, Moon, Sunrise, Sunset, X, User, ArrowLeft } from 'lucide-react';
|
import { Sun, Moon, Sunrise, Sunset, X, User, ArrowLeft } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useOffline } from './offline-banner';
|
import { useOffline } from './offline-banner';
|
||||||
@@ -20,7 +20,7 @@ const SHIFT_CONFIG: Record<ShiftType, { icon: typeof Sun; label: string; color:
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function ContextBar() {
|
export function ContextBar() {
|
||||||
const { shift, activePatient, setActivePatient } = useSwiftStore();
|
const { shift, activePatient, setActivePatient } = useCortexStore();
|
||||||
const isOffline = useOffline();
|
const isOffline = useOffline();
|
||||||
const shiftConfig = SHIFT_CONFIG[shift];
|
const shiftConfig = SHIFT_CONFIG[shift];
|
||||||
const ShiftIcon = shiftConfig.icon;
|
const ShiftIcon = shiftConfig.icon;
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
* Height: 48px (h-12)
|
* Height: 48px (h-12)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useSwiftStore, type SwiftIntent } from '@/stores/swift-store';
|
import { useCortexStore, type CortexIntent } from '@/stores/cortex-store';
|
||||||
import { FileText, Search, ArrowRightLeft, HelpCircle, Clock, Calendar, Plus, X } from 'lucide-react';
|
import { FileText, Search, ArrowRightLeft, HelpCircle, Clock, Calendar, Plus, X } from 'lucide-react';
|
||||||
|
|
||||||
const INTENT_CONFIG: Record<SwiftIntent, { icon: typeof FileText; color: string; label: string }> = {
|
const INTENT_CONFIG: Record<CortexIntent, { icon: typeof FileText; color: string; label: string }> = {
|
||||||
dagnotitie: { icon: FileText, color: 'text-blue-600 bg-blue-50 border border-blue-200', label: 'Notitie' },
|
dagnotitie: { icon: FileText, color: 'text-blue-600 bg-blue-50 border border-blue-200', label: 'Notitie' },
|
||||||
zoeken: { icon: Search, color: 'text-emerald-600 bg-emerald-50 border border-emerald-200', label: 'Zoeken' },
|
zoeken: { icon: Search, color: 'text-emerald-600 bg-emerald-50 border border-emerald-200', label: 'Zoeken' },
|
||||||
overdracht: { icon: ArrowRightLeft, color: 'text-purple-600 bg-purple-50 border border-purple-200', label: 'Overdracht' },
|
overdracht: { icon: ArrowRightLeft, color: 'text-purple-600 bg-purple-50 border border-purple-200', label: 'Overdracht' },
|
||||||
@@ -34,7 +34,7 @@ function formatRelativeTime(date: Date): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function RecentStrip() {
|
export function RecentStrip() {
|
||||||
const { recentActions, setInputValue, openBlock } = useSwiftStore();
|
const { recentActions, setInputValue, openBlock } = useCortexStore();
|
||||||
|
|
||||||
const handleActionClick = (action: typeof recentActions[0]) => {
|
const handleActionClick = (action: typeof recentActions[0]) => {
|
||||||
// Set the input to repeat the action
|
// Set the input to repeat the action
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Components Barrel Export
|
* Cortex Components Barrel Export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './command-center';
|
export * from './command-center';
|
||||||
@@ -74,7 +74,7 @@ enum IntentCategory {
|
|||||||
SEARCH = 'search', // Zoeken, info opvragen
|
SEARCH = 'search', // Zoeken, info opvragen
|
||||||
}
|
}
|
||||||
|
|
||||||
type SwiftIntent =
|
type CortexIntent =
|
||||||
// SAFETY (Hoogste prioriteit - altijd eerst checken)
|
// SAFETY (Hoogste prioriteit - altijd eerst checken)
|
||||||
| 'risicotaxatie' // "Risico inschatten bij Jan" / "Suïcidaliteit checken"
|
| 'risicotaxatie' // "Risico inschatten bij Jan" / "Suïcidaliteit checken"
|
||||||
| 'signaleringsplan' // "Signaleringsplan maken" / "Waarschuwingssignalen vastleggen"
|
| 'signaleringsplan' // "Signaleringsplan maken" / "Waarschuwingssignalen vastleggen"
|
||||||
@@ -159,7 +159,7 @@ function hasSafetySignal(input: string): boolean {
|
|||||||
### Implementation
|
### Implementation
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/intent-classifier-hierarchical.ts
|
// lib/cortex/intent-classifier-hierarchical.ts
|
||||||
|
|
||||||
interface CategoryPattern {
|
interface CategoryPattern {
|
||||||
pattern: RegExp;
|
pattern: RegExp;
|
||||||
@@ -435,14 +435,14 @@ export function classifyIntentHierarchical(input: string): ClassificationResult
|
|||||||
|
|
||||||
// PHASE 2: Detect intent within category (smaller search space)
|
// PHASE 2: Detect intent within category (smaller search space)
|
||||||
const categoryIntents = INTENT_PATTERNS_BY_CATEGORY[bestCategory];
|
const categoryIntents = INTENT_PATTERNS_BY_CATEGORY[bestCategory];
|
||||||
let bestIntent: SwiftIntent = 'unknown';
|
let bestIntent: CortexIntent = 'unknown';
|
||||||
let intentConfidence = 0;
|
let intentConfidence = 0;
|
||||||
|
|
||||||
for (const [intent, patterns] of Object.entries(categoryIntents)) {
|
for (const [intent, patterns] of Object.entries(categoryIntents)) {
|
||||||
for (const { pattern, weight } of patterns) {
|
for (const { pattern, weight } of patterns) {
|
||||||
if (pattern.test(input)) {
|
if (pattern.test(input)) {
|
||||||
if (weight > intentConfidence) {
|
if (weight > intentConfidence) {
|
||||||
bestIntent = intent as SwiftIntent;
|
bestIntent = intent as CortexIntent;
|
||||||
intentConfidence = weight;
|
intentConfidence = weight;
|
||||||
}
|
}
|
||||||
if (weight === 1.0) break; // Perfect match
|
if (weight === 1.0) break; // Perfect match
|
||||||
@@ -542,16 +542,16 @@ Order intents op basis van gebruiksfrequentie.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface IntentMetrics {
|
interface IntentMetrics {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
frequency: number; // Times used
|
frequency: number; // Times used
|
||||||
avgConfidence: number; // Average confidence
|
avgConfidence: number; // Average confidence
|
||||||
avgProcessingTime: number;
|
avgProcessingTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track usage
|
// Track usage
|
||||||
const INTENT_STATS = new Map<SwiftIntent, IntentMetrics>();
|
const INTENT_STATS = new Map<CortexIntent, IntentMetrics>();
|
||||||
|
|
||||||
function trackIntentUsage(intent: SwiftIntent, confidence: number, time: number) {
|
function trackIntentUsage(intent: CortexIntent, confidence: number, time: number) {
|
||||||
const stats = INTENT_STATS.get(intent) || {
|
const stats = INTENT_STATS.get(intent) || {
|
||||||
intent,
|
intent,
|
||||||
frequency: 0,
|
frequency: 0,
|
||||||
@@ -704,9 +704,9 @@ function classifyCompositional(input: string): ComposedIntent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map to legacy intent
|
// Map to legacy intent
|
||||||
function toLegacyIntent(composed: ComposedIntent): SwiftIntent {
|
function toLegacyIntent(composed: ComposedIntent): CortexIntent {
|
||||||
const key = `${composed.subject}_${composed.action}`;
|
const key = `${composed.subject}_${composed.action}`;
|
||||||
const mapping: Record<string, SwiftIntent> = {
|
const mapping: Record<string, CortexIntent> = {
|
||||||
// Risico
|
// Risico
|
||||||
'risico_inschatten': 'risicotaxatie',
|
'risico_inschatten': 'risicotaxatie',
|
||||||
'suicidaliteit_inschatten': 'risicotaxatie',
|
'suicidaliteit_inschatten': 'risicotaxatie',
|
||||||
@@ -951,7 +951,7 @@ const patterns = [
|
|||||||
### Code Structuur (GGZ)
|
### Code Structuur (GGZ)
|
||||||
|
|
||||||
```
|
```
|
||||||
lib/swift/
|
lib/cortex/
|
||||||
├── intent-classifier.ts # Current (keep for now)
|
├── intent-classifier.ts # Current (keep for now)
|
||||||
├── intent-classifier-hierarchical.ts # New (implement in fase 2)
|
├── intent-classifier-hierarchical.ts # New (implement in fase 2)
|
||||||
├── intent-classifier-ai.ts # Current AI fallback
|
├── intent-classifier-ai.ts # Current AI fallback
|
||||||
@@ -1023,8 +1023,8 @@ describe('Intent Classification Performance', () => {
|
|||||||
**Step 1: Define Categories (GGZ)**
|
**Step 1: Define Categories (GGZ)**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/intent-categories.ts
|
// lib/cortex/intent-categories.ts
|
||||||
export const INTENT_CATEGORY_MAP: Record<SwiftIntent, IntentCategory> = {
|
export const INTENT_CATEGORY_MAP: Record<CortexIntent, IntentCategory> = {
|
||||||
// 🚨 Safety (Hoogste prioriteit)
|
// 🚨 Safety (Hoogste prioriteit)
|
||||||
'risicotaxatie': IntentCategory.SAFETY,
|
'risicotaxatie': IntentCategory.SAFETY,
|
||||||
'signaleringsplan': IntentCategory.SAFETY,
|
'signaleringsplan': IntentCategory.SAFETY,
|
||||||
@@ -1085,19 +1085,19 @@ export const INTENT_CATEGORY_MAP: Record<SwiftIntent, IntentCategory> = {
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create pattern files per category
|
# Create pattern files per category
|
||||||
mkdir lib/swift/intent-patterns
|
mkdir lib/cortex/intent-patterns
|
||||||
touch lib/swift/intent-patterns/safety.ts # 🚨 Prioriteit
|
touch lib/cortex/intent-patterns/safety.ts # 🚨 Prioriteit
|
||||||
touch lib/swift/intent-patterns/treatment.ts
|
touch lib/cortex/intent-patterns/treatment.ts
|
||||||
touch lib/swift/intent-patterns/observation.ts
|
touch lib/cortex/intent-patterns/observation.ts
|
||||||
touch lib/swift/intent-patterns/medication.ts
|
touch lib/cortex/intent-patterns/medication.ts
|
||||||
touch lib/swift/intent-patterns/assessment.ts
|
touch lib/cortex/intent-patterns/assessment.ts
|
||||||
touch lib/swift/intent-patterns/scheduling.ts
|
touch lib/cortex/intent-patterns/scheduling.ts
|
||||||
touch lib/swift/intent-patterns/communication.ts
|
touch lib/cortex/intent-patterns/communication.ts
|
||||||
touch lib/swift/intent-patterns/search.ts
|
touch lib/cortex/intent-patterns/search.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/intent-patterns/safety.ts
|
// lib/cortex/intent-patterns/safety.ts
|
||||||
export const SAFETY_PATTERNS = {
|
export const SAFETY_PATTERNS = {
|
||||||
risicotaxatie: [
|
risicotaxatie: [
|
||||||
{ pattern: /^risico\s*(taxatie|inschatting)/i, weight: 1.0 },
|
{ pattern: /^risico\s*(taxatie|inschatting)/i, weight: 1.0 },
|
||||||
@@ -1111,7 +1111,7 @@ export const SAFETY_PATTERNS = {
|
|||||||
// ...
|
// ...
|
||||||
};
|
};
|
||||||
|
|
||||||
// lib/swift/intent-patterns/treatment.ts
|
// lib/cortex/intent-patterns/treatment.ts
|
||||||
export const TREATMENT_PATTERNS = {
|
export const TREATMENT_PATTERNS = {
|
||||||
behandelplan_maken: [
|
behandelplan_maken: [
|
||||||
{ pattern: /^(maak|start)\s*behandelplan/i, weight: 1.0 },
|
{ pattern: /^(maak|start)\s*behandelplan/i, weight: 1.0 },
|
||||||
@@ -1126,7 +1126,7 @@ export const TREATMENT_PATTERNS = {
|
|||||||
**Step 3: Build Hierarchical Classifier**
|
**Step 3: Build Hierarchical Classifier**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/intent-classifier-hierarchical.ts
|
// lib/cortex/intent-classifier-hierarchical.ts
|
||||||
import { DOCUMENTATION_PATTERNS } from './intent-patterns/documentation';
|
import { DOCUMENTATION_PATTERNS } from './intent-patterns/documentation';
|
||||||
import { PATIENT_CARE_PATTERNS } from './intent-patterns/patient-care';
|
import { PATIENT_CARE_PATTERNS } from './intent-patterns/patient-care';
|
||||||
// ... import all
|
// ... import all
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
3. [Data Models & Types](#3-data-models--types)
|
3. [Data Models & Types](#3-data-models--types)
|
||||||
4. [Layer 1: Reflex Arc](#4-layer-1-reflex-arc)
|
4. [Layer 1: Reflex Arc](#4-layer-1-reflex-arc)
|
||||||
5. [Layer 2: Intent Orchestrator](#5-layer-2-intent-orchestrator)
|
5. [Layer 2: Intent Orchestrator](#5-layer-2-intent-orchestrator)
|
||||||
6. [Layer 3: Safety Net](#6-layer-3-safety-net)
|
6. [Layer 3: Nudge](#6-layer-3-nudge)
|
||||||
7. [API Design](#7-api-design)
|
7. [API Design](#7-api-design)
|
||||||
8. [Frontend Components](#8-frontend-components)
|
8. [Frontend Components](#8-frontend-components)
|
||||||
9. [State Management](#9-state-management)
|
9. [State Management](#9-state-management)
|
||||||
@@ -92,7 +92,7 @@ Transformatie naar een **agentic systeem** dat:
|
|||||||
[Action Completed]
|
[Action Completed]
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ LAYER 3: SAFETY NET (POST-ACTION INTELLIGENCE) [async] │
|
│ LAYER 3: NUDGE (POST-ACTION INTELLIGENCE) [async] │
|
||||||
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||||
│ │ • Protocol Rules Engine: medical domain knowledge │ │
|
│ │ • Protocol Rules Engine: medical domain knowledge │ │
|
||||||
│ │ • Trigger evaluation: "Does this action warrant a follow-up?" │ │
|
│ │ • Trigger evaluation: "Does this action warrant a follow-up?" │ │
|
||||||
@@ -108,7 +108,7 @@ Transformatie naar een **agentic systeem** dat:
|
|||||||
```
|
```
|
||||||
┌──────────┐ ┌─────────┐ ┌─────────────┐ ┌──────────┐ ┌───────────┐
|
┌──────────┐ ┌─────────┐ ┌─────────────┐ ┌──────────┐ ┌───────────┐
|
||||||
│ Speech │───►│ Input │───►│ Classify │───►│ Execute │───►│ Safety │
|
│ Speech │───►│ Input │───►│ Classify │───►│ Execute │───►│ Safety │
|
||||||
│ /Text │ │ Buffer │ │ (L1/L2) │ │ Chain │ │ Net │
|
│ /Text │ │ Buffer │ │ (L1/L2) │ │ Chain │ │ Nudge │
|
||||||
└──────────┘ └─────────┘ └─────────────┘ └──────────┘ └───────────┘
|
└──────────┘ └─────────┘ └─────────────┘ └──────────┘ └───────────┘
|
||||||
│ │ │
|
│ │ │
|
||||||
▼ ▼ ▼
|
▼ ▼ ▼
|
||||||
@@ -122,14 +122,14 @@ Transformatie naar een **agentic systeem** dat:
|
|||||||
|
|
||||||
## 3. Data Models & Types
|
## 3. Data Models & Types
|
||||||
|
|
||||||
### 3.1 Core Types (lib/swift/types.ts)
|
### 3.1 Core Types (lib/cortex/types.ts)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// INTENT TYPES
|
// INTENT TYPES
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
export type SwiftIntent =
|
export type CortexIntent =
|
||||||
| 'dagnotitie'
|
| 'dagnotitie'
|
||||||
| 'zoeken'
|
| 'zoeken'
|
||||||
| 'overdracht'
|
| 'overdracht'
|
||||||
@@ -146,7 +146,7 @@ export type SwiftIntent =
|
|||||||
/**
|
/**
|
||||||
* Full context passed to AI for intelligent classification
|
* Full context passed to AI for intelligent classification
|
||||||
*/
|
*/
|
||||||
export interface SwiftContext {
|
export interface CortexContext {
|
||||||
// Active patient (if any)
|
// Active patient (if any)
|
||||||
activePatient: {
|
activePatient: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -175,7 +175,7 @@ export interface SwiftContext {
|
|||||||
|
|
||||||
// Recent intents (for continuity)
|
// Recent intents (for continuity)
|
||||||
recentIntents: {
|
recentIntents: {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
patientName?: string;
|
patientName?: string;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
}[];
|
}[];
|
||||||
@@ -183,7 +183,7 @@ export interface SwiftContext {
|
|||||||
// User preferences (adaptive confidence)
|
// User preferences (adaptive confidence)
|
||||||
userPreferences?: {
|
userPreferences?: {
|
||||||
confirmationLevel: 'always' | 'destructive' | 'never';
|
confirmationLevel: 'always' | 'destructive' | 'never';
|
||||||
frequentIntents: SwiftIntent[];
|
frequentIntents: CortexIntent[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ export interface IntentAction {
|
|||||||
sequence: number; // Order in chain (1, 2, 3...)
|
sequence: number; // Order in chain (1, 2, 3...)
|
||||||
|
|
||||||
// Classification
|
// Classification
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
|
|
||||||
// Extracted data
|
// Extracted data
|
||||||
@@ -311,25 +311,25 @@ export type AppointmentType =
|
|||||||
| 'overig';
|
| 'overig';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// SAFETY NET TYPES (NEW in V2)
|
// NUDGE TYPES (NEW in V2)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A suggestion generated by the Safety Net
|
* A suggestion generated by the Nudge layer
|
||||||
*/
|
*/
|
||||||
export interface SafetySuggestion {
|
export interface NudgeSuggestion {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
// What triggered this suggestion
|
// What triggered this suggestion
|
||||||
trigger: {
|
trigger: {
|
||||||
actionId: string;
|
actionId: string;
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The suggestion itself
|
// The suggestion itself
|
||||||
suggestion: {
|
suggestion: {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
entities: Partial<ExtractedEntities>;
|
entities: Partial<ExtractedEntities>;
|
||||||
message: string; // "Wondcontrole inplannen over 3 dagen?"
|
message: string; // "Wondcontrole inplannen over 3 dagen?"
|
||||||
rationale: string; // "Bij wondzorg hoort standaard een controle"
|
rationale: string; // "Bij wondzorg hoort standaard een controle"
|
||||||
@@ -355,13 +355,13 @@ export interface ProtocolRule {
|
|||||||
|
|
||||||
// When to trigger
|
// When to trigger
|
||||||
trigger: {
|
trigger: {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
conditions?: ProtocolCondition[];
|
conditions?: ProtocolCondition[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// What to suggest
|
// What to suggest
|
||||||
suggestion: {
|
suggestion: {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
message: string;
|
message: string;
|
||||||
prefillFrom: (source: ExtractedEntities) => Partial<ExtractedEntities>;
|
prefillFrom: (source: ExtractedEntities) => Partial<ExtractedEntities>;
|
||||||
};
|
};
|
||||||
@@ -386,7 +386,7 @@ export interface ProtocolCondition {
|
|||||||
* Result from Layer 1 (Local Reflex)
|
* Result from Layer 1 (Local Reflex)
|
||||||
*/
|
*/
|
||||||
export interface LocalClassificationResult {
|
export interface LocalClassificationResult {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
matchedPattern?: string;
|
matchedPattern?: string;
|
||||||
processingTimeMs: number;
|
processingTimeMs: number;
|
||||||
@@ -434,33 +434,33 @@ export interface ClassificationResult {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 Store Types (stores/swift-store.ts additions)
|
### 3.2 Store Types (stores/cortex-store.ts additions)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Add to existing SwiftStore interface
|
// Add to existing CortexStore interface
|
||||||
|
|
||||||
interface SwiftStoreV2 extends SwiftStore {
|
interface CortexStoreV2 extends CortexStore {
|
||||||
// Context (enhanced)
|
// Context (enhanced)
|
||||||
context: SwiftContext;
|
context: CortexContext;
|
||||||
|
|
||||||
// Intent Chain state
|
// Intent Chain state
|
||||||
activeChain: IntentChain | null;
|
activeChain: IntentChain | null;
|
||||||
chainHistory: IntentChain[];
|
chainHistory: IntentChain[];
|
||||||
|
|
||||||
// Safety Net state
|
// Nudge state
|
||||||
pendingSuggestions: SafetySuggestion[];
|
pendingSuggestions: NudgeSuggestion[];
|
||||||
suggestionHistory: SafetySuggestion[];
|
suggestionHistory: NudgeSuggestion[];
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
setContext: (context: Partial<SwiftContext>) => void;
|
setContext: (context: Partial<CortexContext>) => void;
|
||||||
|
|
||||||
// Chain actions
|
// Chain actions
|
||||||
startChain: (chain: IntentChain) => void;
|
startChain: (chain: IntentChain) => void;
|
||||||
updateActionStatus: (chainId: string, actionId: string, status: IntentAction['status']) => void;
|
updateActionStatus: (chainId: string, actionId: string, status: IntentAction['status']) => void;
|
||||||
completeChain: (chainId: string) => void;
|
completeChain: (chainId: string) => void;
|
||||||
|
|
||||||
// Safety Net actions
|
// Nudge actions
|
||||||
addSuggestion: (suggestion: SafetySuggestion) => void;
|
addSuggestion: (suggestion: NudgeSuggestion) => void;
|
||||||
acceptSuggestion: (suggestionId: string) => void;
|
acceptSuggestion: (suggestionId: string) => void;
|
||||||
dismissSuggestion: (suggestionId: string) => void;
|
dismissSuggestion: (suggestionId: string) => void;
|
||||||
}
|
}
|
||||||
@@ -479,7 +479,7 @@ Razendsnelle (<20ms) afhandeling van **simpele, eenduidige commando's** met hoge
|
|||||||
- Geen context-afhankelijke woorden ("hij", "haar", "die afspraak")
|
- Geen context-afhankelijke woorden ("hij", "haar", "die afspraak")
|
||||||
- Geen tijdsrelaties die interpretatie nodig hebben
|
- Geen tijdsrelaties die interpretatie nodig hebben
|
||||||
|
|
||||||
### 4.3 Implementatie (lib/swift/reflex-classifier.ts)
|
### 4.3 Implementatie (lib/cortex/reflex-classifier.ts)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/**
|
/**
|
||||||
@@ -489,7 +489,7 @@ Razendsnelle (<20ms) afhandeling van **simpele, eenduidige commando's** met hoge
|
|||||||
* Escalates to Layer 2 when complexity is detected.
|
* Escalates to Layer 2 when complexity is detected.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { LocalClassificationResult, SwiftIntent } from './types';
|
import type { LocalClassificationResult, CortexIntent } from './types';
|
||||||
|
|
||||||
// Multi-intent signal words
|
// Multi-intent signal words
|
||||||
const MULTI_INTENT_SIGNALS = [
|
const MULTI_INTENT_SIGNALS = [
|
||||||
@@ -513,7 +513,7 @@ const CONTEXT_SIGNALS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Intent patterns with weights
|
// Intent patterns with weights
|
||||||
const REFLEX_PATTERNS: Record<SwiftIntent, Array<{ pattern: RegExp; weight: number }>> = {
|
const REFLEX_PATTERNS: Record<CortexIntent, Array<{ pattern: RegExp; weight: number }>> = {
|
||||||
dagnotitie: [
|
dagnotitie: [
|
||||||
{ pattern: /^dagnotitie\b/i, weight: 1.0 },
|
{ pattern: /^dagnotitie\b/i, weight: 1.0 },
|
||||||
{ pattern: /^notitie\s+\w+\s+(medicatie|adl|gedrag|incident|observatie)\b/i, weight: 0.95 },
|
{ pattern: /^notitie\s+\w+\s+(medicatie|adl|gedrag|incident|observatie)\b/i, weight: 0.95 },
|
||||||
@@ -617,14 +617,14 @@ export function classifyWithReflex(input: string): LocalClassificationResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Pattern matching
|
// Step 2: Pattern matching
|
||||||
let bestMatch: { intent: SwiftIntent; confidence: number; pattern: string } | null = null;
|
let bestMatch: { intent: CortexIntent; confidence: number; pattern: string } | null = null;
|
||||||
|
|
||||||
for (const [intent, patterns] of Object.entries(REFLEX_PATTERNS)) {
|
for (const [intent, patterns] of Object.entries(REFLEX_PATTERNS)) {
|
||||||
for (const { pattern, weight } of patterns) {
|
for (const { pattern, weight } of patterns) {
|
||||||
if (pattern.test(trimmedInput)) {
|
if (pattern.test(trimmedInput)) {
|
||||||
if (!bestMatch || weight > bestMatch.confidence) {
|
if (!bestMatch || weight > bestMatch.confidence) {
|
||||||
bestMatch = {
|
bestMatch = {
|
||||||
intent: intent as SwiftIntent,
|
intent: intent as CortexIntent,
|
||||||
confidence: weight,
|
confidence: weight,
|
||||||
pattern: pattern.source,
|
pattern: pattern.source,
|
||||||
};
|
};
|
||||||
@@ -677,7 +677,7 @@ De AI krijgt altijd volledige context mee:
|
|||||||
/**
|
/**
|
||||||
* Build context for AI classification
|
* Build context for AI classification
|
||||||
*/
|
*/
|
||||||
export function buildSwiftContext(store: SwiftStore): SwiftContext {
|
export function buildCortexContext(store: CortexStore): CortexContext {
|
||||||
return {
|
return {
|
||||||
activePatient: store.activePatient ? {
|
activePatient: store.activePatient ? {
|
||||||
id: store.activePatient.id,
|
id: store.activePatient.id,
|
||||||
@@ -699,7 +699,7 @@ export function buildSwiftContext(store: SwiftStore): SwiftContext {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5.3 Implementatie (lib/swift/orchestrator.ts)
|
### 5.3 Implementatie (lib/cortex/orchestrator.ts)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/**
|
/**
|
||||||
@@ -710,7 +710,7 @@ export function buildSwiftContext(store: SwiftStore): SwiftContext {
|
|||||||
|
|
||||||
import Anthropic from '@anthropic-ai/sdk';
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
import type {
|
import type {
|
||||||
SwiftContext,
|
CortexContext,
|
||||||
IntentChain,
|
IntentChain,
|
||||||
IntentAction,
|
IntentAction,
|
||||||
AIClassificationResult
|
AIClassificationResult
|
||||||
@@ -789,7 +789,7 @@ Als je twijfelt, stel een verduidelijkingsvraag:
|
|||||||
/**
|
/**
|
||||||
* Format context for AI prompt
|
* Format context for AI prompt
|
||||||
*/
|
*/
|
||||||
function formatContextForPrompt(context: SwiftContext): string {
|
function formatContextForPrompt(context: CortexContext): string {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
|
||||||
// Active patient
|
// Active patient
|
||||||
@@ -829,7 +829,7 @@ function formatContextForPrompt(context: SwiftContext): string {
|
|||||||
*/
|
*/
|
||||||
export async function classifyWithOrchestrator(
|
export async function classifyWithOrchestrator(
|
||||||
input: string,
|
input: string,
|
||||||
context: SwiftContext
|
context: CortexContext
|
||||||
): Promise<AIClassificationResult> {
|
): Promise<AIClassificationResult> {
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
|
|
||||||
@@ -903,7 +903,7 @@ Analyseer en extraheer alle intenties.`,
|
|||||||
*/
|
*/
|
||||||
function parseAIResponse(rawText: string): {
|
function parseAIResponse(rawText: string): {
|
||||||
actions: Array<{
|
actions: Array<{
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
requiresConfirmation?: boolean;
|
requiresConfirmation?: boolean;
|
||||||
@@ -944,7 +944,7 @@ function parseAIResponse(rawText: string): {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Layer 3: Safety Net
|
## 6. Layer 3: Nudge
|
||||||
|
|
||||||
### 6.1 Doel
|
### 6.1 Doel
|
||||||
Proactieve suggesties na succesvolle acties op basis van medische protocollen en domeinkennis.
|
Proactieve suggesties na succesvolle acties op basis van medische protocollen en domeinkennis.
|
||||||
@@ -953,7 +953,7 @@ Proactieve suggesties na succesvolle acties op basis van medische protocollen en
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/**
|
/**
|
||||||
* Layer 3: Safety Net
|
* Layer 3: Nudge
|
||||||
*
|
*
|
||||||
* Post-action intelligence that suggests follow-up actions
|
* Post-action intelligence that suggests follow-up actions
|
||||||
* based on medical protocols and domain knowledge.
|
* based on medical protocols and domain knowledge.
|
||||||
@@ -961,7 +961,7 @@ Proactieve suggesties na succesvolle acties op basis van medische protocollen en
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
IntentAction,
|
IntentAction,
|
||||||
SafetySuggestion,
|
NudgeSuggestion,
|
||||||
ProtocolRule,
|
ProtocolRule,
|
||||||
ExtractedEntities
|
ExtractedEntities
|
||||||
} from './types';
|
} from './types';
|
||||||
@@ -1102,7 +1102,7 @@ export const PROTOCOL_RULES: ProtocolRule[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// SAFETY NET ENGINE
|
// NUDGE ENGINE
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1139,10 +1139,10 @@ function checkCondition(
|
|||||||
/**
|
/**
|
||||||
* Evaluate all protocol rules against a completed action
|
* Evaluate all protocol rules against a completed action
|
||||||
*/
|
*/
|
||||||
export function evaluateSafetyNet(
|
export function evaluateNudge(
|
||||||
completedAction: IntentAction
|
completedAction: IntentAction
|
||||||
): SafetySuggestion[] {
|
): NudgeSuggestion[] {
|
||||||
const suggestions: SafetySuggestion[] = [];
|
const suggestions: NudgeSuggestion[] = [];
|
||||||
|
|
||||||
for (const rule of PROTOCOL_RULES) {
|
for (const rule of PROTOCOL_RULES) {
|
||||||
if (!rule.enabled) continue;
|
if (!rule.enabled) continue;
|
||||||
@@ -1159,7 +1159,7 @@ export function evaluateSafetyNet(
|
|||||||
if (!conditionsMet) continue;
|
if (!conditionsMet) continue;
|
||||||
|
|
||||||
// Generate suggestion
|
// Generate suggestion
|
||||||
const suggestion: SafetySuggestion = {
|
const suggestion: NudgeSuggestion = {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
trigger: {
|
trigger: {
|
||||||
actionId: completedAction.id,
|
actionId: completedAction.id,
|
||||||
@@ -1201,16 +1201,16 @@ export function evaluateSafetyNet(
|
|||||||
// app/api/intent/classify/route.ts (V2)
|
// app/api/intent/classify/route.ts (V2)
|
||||||
|
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import { classifyWithReflex } from '@/lib/swift/reflex-classifier';
|
import { classifyWithReflex } from '@/lib/cortex/reflex-classifier';
|
||||||
import { classifyWithOrchestrator } from '@/lib/swift/orchestrator';
|
import { classifyWithOrchestrator } from '@/lib/cortex/orchestrator';
|
||||||
import { evaluateSafetyNet } from '@/lib/swift/safety-net';
|
import { evaluateNudge } from '@/lib/cortex/nudge';
|
||||||
import { extractEntities } from '@/lib/swift/entity-extractor';
|
import { extractEntities } from '@/lib/cortex/entity-extractor';
|
||||||
import type { ClassificationResult, SwiftContext } from '@/lib/swift/types';
|
import type { ClassificationResult, CortexContext } from '@/lib/cortex/types';
|
||||||
|
|
||||||
// Request schema
|
// Request schema
|
||||||
interface ClassifyRequest {
|
interface ClassifyRequest {
|
||||||
input: string;
|
input: string;
|
||||||
context: SwiftContext;
|
context: CortexContext;
|
||||||
options?: {
|
options?: {
|
||||||
forceAI?: boolean;
|
forceAI?: boolean;
|
||||||
skipSafetyNet?: boolean;
|
skipSafetyNet?: boolean;
|
||||||
@@ -1227,8 +1227,8 @@ interface ClassifyResponse {
|
|||||||
clarificationQuestion?: string;
|
clarificationQuestion?: string;
|
||||||
clarificationOptions?: string[];
|
clarificationOptions?: string[];
|
||||||
|
|
||||||
// Safety Net suggestions (if any)
|
// Nudge suggestions (if any)
|
||||||
suggestions?: SafetySuggestion[];
|
suggestions?: NudgeSuggestion[];
|
||||||
|
|
||||||
// Debug info (dev only)
|
// Debug info (dev only)
|
||||||
debug?: object;
|
debug?: object;
|
||||||
@@ -1326,14 +1326,14 @@ interface ExecuteRequest {
|
|||||||
interface ExecuteResponse {
|
interface ExecuteResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
action: IntentAction;
|
action: IntentAction;
|
||||||
suggestions?: SafetySuggestion[]; // From Safety Net
|
suggestions?: NudgeSuggestion[]; // From Nudge
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.3 Context API
|
### 7.3 Context API
|
||||||
|
|
||||||
**Endpoint:** `GET /api/swift/context`
|
**Endpoint:** `GET /api/cortex/context`
|
||||||
|
|
||||||
Returns current context for AI classification:
|
Returns current context for AI classification:
|
||||||
- Active patient
|
- Active patient
|
||||||
@@ -1363,19 +1363,19 @@ CommandCenter (v3.0)
|
|||||||
├── ArtifactArea
|
├── ArtifactArea
|
||||||
│ ├── ArtifactTabs
|
│ ├── ArtifactTabs
|
||||||
│ └── ArtifactContainer
|
│ └── ArtifactContainer
|
||||||
└── SuggestionToast (NEW - Layer 3)
|
└── NudgeToast (NEW - Layer 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.2 ActionChainCard Component
|
### 8.2 ActionChainCard Component
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// components/swift/chat/action-chain-card.tsx
|
// components/cortex/chat/action-chain-card.tsx
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Check, X, Loader2, AlertCircle } from 'lucide-react';
|
import { Check, X, Loader2, AlertCircle } from 'lucide-react';
|
||||||
import type { IntentChain, IntentAction } from '@/lib/swift/types';
|
import type { IntentChain, IntentAction } from '@/lib/cortex/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@@ -1536,21 +1536,21 @@ function ActionItem({
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.3 SuggestionToast Component (Layer 3 UI)
|
### 8.3 NudgeToast Component (Layer 3 UI)
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// components/swift/suggestion-toast.tsx
|
// components/cortex/command-center/nudge-toast.tsx
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { X, Lightbulb, ArrowRight } from 'lucide-react';
|
import { X, Lightbulb, ArrowRight } from 'lucide-react';
|
||||||
import type { SafetySuggestion } from '@/lib/swift/types';
|
import type { NudgeSuggestion } from '@/lib/cortex/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface SuggestionToastProps {
|
interface NudgeToastProps {
|
||||||
suggestion: SafetySuggestion;
|
suggestion: NudgeSuggestion;
|
||||||
onAccept: (suggestionId: string) => void;
|
onAccept: (suggestionId: string) => void;
|
||||||
onDismiss: (suggestionId: string) => void;
|
onDismiss: (suggestionId: string) => void;
|
||||||
}
|
}
|
||||||
@@ -1561,11 +1561,11 @@ const PRIORITY_STYLES = {
|
|||||||
low: 'border-blue-200 bg-blue-50',
|
low: 'border-blue-200 bg-blue-50',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SuggestionToast({
|
export function NudgeToast({
|
||||||
suggestion,
|
suggestion,
|
||||||
onAccept,
|
onAccept,
|
||||||
onDismiss
|
onDismiss
|
||||||
}: SuggestionToastProps) {
|
}: NudgeToastProps) {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [timeLeft, setTimeLeft] = useState(100);
|
const [timeLeft, setTimeLeft] = useState(100);
|
||||||
|
|
||||||
@@ -1660,7 +1660,7 @@ export function SuggestionToast({
|
|||||||
### 8.4 ClarificationCard Component
|
### 8.4 ClarificationCard Component
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// components/swift/chat/clarification-card.tsx
|
// components/cortex/chat/clarification-card.tsx
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
@@ -1710,24 +1710,24 @@ export function ClarificationCard({
|
|||||||
|
|
||||||
## 9. State Management
|
## 9. State Management
|
||||||
|
|
||||||
### 9.1 Enhanced Swift Store
|
### 9.1 Enhanced Cortex Store
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// stores/swift-store.ts (V2 additions)
|
// stores/cortex-store.ts (V2 additions)
|
||||||
|
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import type {
|
import type {
|
||||||
SwiftContext,
|
CortexContext,
|
||||||
IntentChain,
|
IntentChain,
|
||||||
IntentAction,
|
IntentAction,
|
||||||
SafetySuggestion,
|
NudgeSuggestion,
|
||||||
} from '@/lib/swift/types';
|
} from '@/lib/cortex/types';
|
||||||
|
|
||||||
interface SwiftStoreV2 {
|
interface CortexStoreV2 {
|
||||||
// ============ CONTEXT ============
|
// ============ CONTEXT ============
|
||||||
context: SwiftContext;
|
context: CortexContext;
|
||||||
setContext: (context: Partial<SwiftContext>) => void;
|
setContext: (context: Partial<CortexContext>) => void;
|
||||||
|
|
||||||
// ============ INTENT CHAINS ============
|
// ============ INTENT CHAINS ============
|
||||||
activeChain: IntentChain | null;
|
activeChain: IntentChain | null;
|
||||||
@@ -1745,10 +1745,10 @@ interface SwiftStoreV2 {
|
|||||||
skipAction: (chainId: string, actionId: string) => void;
|
skipAction: (chainId: string, actionId: string) => void;
|
||||||
completeChain: (chainId: string) => void;
|
completeChain: (chainId: string) => void;
|
||||||
|
|
||||||
// ============ SAFETY NET ============
|
// ============ NUDGE ============
|
||||||
suggestions: SafetySuggestion[];
|
suggestions: NudgeSuggestion[];
|
||||||
|
|
||||||
addSuggestion: (suggestion: SafetySuggestion) => void;
|
addSuggestion: (suggestion: NudgeSuggestion) => void;
|
||||||
acceptSuggestion: (suggestionId: string) => void;
|
acceptSuggestion: (suggestionId: string) => void;
|
||||||
dismissSuggestion: (suggestionId: string) => void;
|
dismissSuggestion: (suggestionId: string) => void;
|
||||||
clearExpiredSuggestions: () => void;
|
clearExpiredSuggestions: () => void;
|
||||||
@@ -1760,11 +1760,11 @@ interface SwiftStoreV2 {
|
|||||||
originalInput: string;
|
originalInput: string;
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
setClarification: (clarification: SwiftStoreV2['pendingClarification']) => void;
|
setClarification: (clarification: CortexStoreV2['pendingClarification']) => void;
|
||||||
answerClarification: (answer: string) => void;
|
answerClarification: (answer: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSwiftStoreV2 = create<SwiftStoreV2>()(
|
export const useCortexStoreV2 = create<CortexStoreV2>()(
|
||||||
devtools(
|
devtools(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
@@ -1899,13 +1899,13 @@ export const useSwiftStoreV2 = create<SwiftStoreV2>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'swift-store-v2',
|
name: 'cortex-store-v2',
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
chainHistory: state.chainHistory.slice(0, 10),
|
chainHistory: state.chainHistory.slice(0, 10),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
{ name: 'swift-v2' }
|
{ name: 'cortex-v2' }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
@@ -1918,8 +1918,8 @@ export const useSwiftStoreV2 = create<SwiftStoreV2>()(
|
|||||||
|
|
||||||
| # | Task | Beschrijving | Effort |
|
| # | Task | Beschrijving | Effort |
|
||||||
|---|------|--------------|--------|
|
|---|------|--------------|--------|
|
||||||
| 1.1 | Context Types | Nieuwe types voor SwiftContext | S |
|
| 1.1 | Context Types | Nieuwe types voor CortexContext | S |
|
||||||
| 1.2 | Context API | GET /api/swift/context endpoint | M |
|
| 1.2 | Context API | GET /api/cortex/context endpoint | M |
|
||||||
| 1.3 | Context Injection | Update AI classifier om context te ontvangen | M |
|
| 1.3 | Context Injection | Update AI classifier om context te ontvangen | M |
|
||||||
| 1.4 | Reflex Complexity Detection | Multi-intent en context signals detectie | S |
|
| 1.4 | Reflex Complexity Detection | Multi-intent en context signals detectie | S |
|
||||||
| 1.5 | Clarification UI | ClarificationCard component | S |
|
| 1.5 | Clarification UI | ClarificationCard component | S |
|
||||||
@@ -1938,13 +1938,13 @@ export const useSwiftStoreV2 = create<SwiftStoreV2>()(
|
|||||||
|
|
||||||
**Deliverable:** "Zeg Jan af en maak notitie" werkt
|
**Deliverable:** "Zeg Jan af en maak notitie" werkt
|
||||||
|
|
||||||
### Fase 3: Safety Net (Week 5-6)
|
### Fase 3: Nudge (Week 5-6)
|
||||||
|
|
||||||
| # | Task | Beschrijving | Effort |
|
| # | Task | Beschrijving | Effort |
|
||||||
|---|------|--------------|--------|
|
|---|------|--------------|--------|
|
||||||
| 3.1 | Protocol Rules | Rule definitions voor wondzorg, medicatie | M |
|
| 3.1 | Protocol Rules | Rule definitions voor wondzorg, medicatie | M |
|
||||||
| 3.2 | Safety Net Engine | evaluateSafetyNet functie | M |
|
| 3.2 | Nudge Engine | evaluateNudge functie | M |
|
||||||
| 3.3 | SuggestionToast | UI component met timer | M |
|
| 3.3 | NudgeToast | UI component met timer | M |
|
||||||
| 3.4 | Suggestion Flow | Accept/dismiss handling | S |
|
| 3.4 | Suggestion Flow | Accept/dismiss handling | S |
|
||||||
| 3.5 | Protocol Admin | Admin UI voor regels (optional) | L |
|
| 3.5 | Protocol Admin | Admin UI voor regels (optional) | L |
|
||||||
|
|
||||||
@@ -1967,7 +1967,7 @@ export const useSwiftStoreV2 = create<SwiftStoreV2>()(
|
|||||||
### 11.1 Unit Tests
|
### 11.1 Unit Tests
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/__tests__/reflex-classifier.test.ts
|
// lib/cortex/__tests__/reflex-classifier.test.ts
|
||||||
|
|
||||||
import { classifyWithReflex } from '../reflex-classifier';
|
import { classifyWithReflex } from '../reflex-classifier';
|
||||||
|
|
||||||
@@ -2058,7 +2058,7 @@ describe('POST /api/intent/classify', () => {
|
|||||||
### 11.3 Test Zinnen Dataset
|
### 11.3 Test Zinnen Dataset
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// lib/swift/__tests__/test-sentences.json
|
// lib/cortex/__tests__/test-sentences.json
|
||||||
{
|
{
|
||||||
"single_intent": [
|
"single_intent": [
|
||||||
{ "input": "notitie jan medicatie", "expected": ["dagnotitie"] },
|
{ "input": "notitie jan medicatie", "expected": ["dagnotitie"] },
|
||||||
@@ -2110,17 +2110,17 @@ describe('POST /api/intent/classify', () => {
|
|||||||
Het V2 systeem moet naast V1 kunnen draaien tijdens de migratie:
|
Het V2 systeem moet naast V1 kunnen draaien tijdens de migratie:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/intent-classifier-adapter.ts
|
// lib/cortex/intent-classifier-adapter.ts
|
||||||
|
|
||||||
import { classifyIntent as classifyV1 } from './intent-classifier';
|
import { classifyIntent as classifyV1 } from './intent-classifier';
|
||||||
import { classifyWithReflex } from './reflex-classifier';
|
import { classifyWithReflex } from './reflex-classifier';
|
||||||
import { classifyWithOrchestrator } from './orchestrator';
|
import { classifyWithOrchestrator } from './orchestrator';
|
||||||
|
|
||||||
const USE_V2 = process.env.NEXT_PUBLIC_SWIFT_V2 === 'true';
|
const USE_V2 = process.env.NEXT_PUBLIC_CORTEX_V2 === 'true';
|
||||||
|
|
||||||
export async function classifyIntent(
|
export async function classifyIntent(
|
||||||
input: string,
|
input: string,
|
||||||
context?: SwiftContext
|
context?: CortexContext
|
||||||
): Promise<ClassificationResult> {
|
): Promise<ClassificationResult> {
|
||||||
if (!USE_V2) {
|
if (!USE_V2) {
|
||||||
// V1 path - single intent
|
// V1 path - single intent
|
||||||
@@ -2167,22 +2167,22 @@ export async function classifyIntent(
|
|||||||
|
|
||||||
export const FEATURE_FLAGS = {
|
export const FEATURE_FLAGS = {
|
||||||
// V2 Features
|
// V2 Features
|
||||||
SWIFT_V2_ENABLED: process.env.NEXT_PUBLIC_SWIFT_V2 === 'true',
|
CORTEX_V2_ENABLED: process.env.NEXT_PUBLIC_CORTEX_V2 === 'true',
|
||||||
SWIFT_MULTI_INTENT: process.env.NEXT_PUBLIC_SWIFT_MULTI_INTENT === 'true',
|
CORTEX_MULTI_INTENT: process.env.NEXT_PUBLIC_CORTEX_MULTI_INTENT === 'true',
|
||||||
SWIFT_SAFETY_NET: process.env.NEXT_PUBLIC_SWIFT_SAFETY_NET === 'true',
|
CORTEX_NUDGE: process.env.NEXT_PUBLIC_CORTEX_NUDGE === 'true',
|
||||||
SWIFT_CONTEXT_INJECTION: process.env.NEXT_PUBLIC_SWIFT_CONTEXT === 'true',
|
CORTEX_CONTEXT_INJECTION: process.env.NEXT_PUBLIC_CORTEX_CONTEXT === 'true',
|
||||||
|
|
||||||
// Rollout percentage (A/B testing)
|
// Rollout percentage (A/B testing)
|
||||||
SWIFT_V2_ROLLOUT: parseInt(process.env.NEXT_PUBLIC_SWIFT_V2_ROLLOUT || '0', 10),
|
CORTEX_V2_ROLLOUT: parseInt(process.env.NEXT_PUBLIC_CORTEX_V2_ROLLOUT || '0', 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isSwiftV2Enabled(userId?: string): boolean {
|
export function isCortexV2Enabled(userId?: string): boolean {
|
||||||
if (!FEATURE_FLAGS.SWIFT_V2_ENABLED) return false;
|
if (!FEATURE_FLAGS.CORTEX_V2_ENABLED) return false;
|
||||||
|
|
||||||
// A/B test based on user ID hash
|
// A/B test based on user ID hash
|
||||||
if (userId && FEATURE_FLAGS.SWIFT_V2_ROLLOUT < 100) {
|
if (userId && FEATURE_FLAGS.CORTEX_V2_ROLLOUT < 100) {
|
||||||
const hash = simpleHash(userId);
|
const hash = simpleHash(userId);
|
||||||
return hash % 100 < FEATURE_FLAGS.SWIFT_V2_ROLLOUT;
|
return hash % 100 < FEATURE_FLAGS.CORTEX_V2_ROLLOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -2212,13 +2212,13 @@ export function isSwiftV2Enabled(userId?: string): boolean {
|
|||||||
| **IntentChain** | Lijst van intents geëxtraheerd uit één uiting |
|
| **IntentChain** | Lijst van intents geëxtraheerd uit één uiting |
|
||||||
| **Reflex Arc** | Layer 1 - snelle lokale pattern matching |
|
| **Reflex Arc** | Layer 1 - snelle lokale pattern matching |
|
||||||
| **Orchestrator** | Layer 2 - AI-gedreven classificatie |
|
| **Orchestrator** | Layer 2 - AI-gedreven classificatie |
|
||||||
| **Safety Net** | Layer 3 - proactieve suggesties |
|
| **Nudge** | Layer 3 - proactieve suggesties |
|
||||||
| **Entity** | Geëxtraheerde data (patiëntnaam, datum, etc.) |
|
| **Entity** | Geëxtraheerde data (patiëntnaam, datum, etc.) |
|
||||||
| **Artifact** | UI component voor een specifieke taak |
|
| **Artifact** | UI component voor een specifieke taak |
|
||||||
|
|
||||||
### B. Referenties
|
### B. Referenties
|
||||||
|
|
||||||
- [FO Swift Intent System V2](./fo-swift-intent-system-v2.md)
|
- [FO Cortex Intent System V2](./fo-swift-intent-system-v2.md)
|
||||||
- [UX Simulatie Next Level](./ux-simulation-intent-next-level.md)
|
- [UX Simulatie Next Level](./ux-simulation-intent-next-level.md)
|
||||||
- [UX Evaluatie Schaalbaarheid](./ux-evaluation-intent-scalability.md)
|
- [UX Evaluatie Schaalbaarheid](./ux-evaluation-intent-scalability.md)
|
||||||
- [Architecture Proposal V2](./intent-architecture-v2-proposal.md)
|
- [Architecture Proposal V2](./intent-architecture-v2-proposal.md)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Het systeem bestaat uit drie samenwerkende lagen die elk een andere rol spelen i
|
|||||||
* *Rol:* AI-gedreven analyse voor complexe zinnen, context-disambiguatie en **Multi-Intents**.
|
* *Rol:* AI-gedreven analyse voor complexe zinnen, context-disambiguatie en **Multi-Intents**.
|
||||||
* *Voorbeeld:* "Zeg Jan af **en** maak een notitie."
|
* *Voorbeeld:* "Zeg Jan af **en** maak een notitie."
|
||||||
|
|
||||||
3. **Layer 3: The Safety Net & Suggestion Engine (De Partner)**
|
3. **Layer 3: The Nudge & Suggestion Engine (De Partner)**
|
||||||
* *Rol:* Proactieve business logic die *na* een actie meedenkt.
|
* *Rol:* Proactieve business logic die *na* een actie meedenkt.
|
||||||
* *Voorbeeld:* Na "Wondzorg registratie" → Suggestie: "Wondcontrole inplannen?"
|
* *Voorbeeld:* Na "Wondzorg registratie" → Suggestie: "Wondcontrole inplannen?"
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ Het systeem bestaat uit drie samenwerkende lagen die elk een andere rol spelen i
|
|||||||
3. Extraheert entities (Wie, Wanneer, Wat).
|
3. Extraheert entities (Wie, Wanneer, Wat).
|
||||||
* **Output:** Een lijst van uit te voeren acties: `[ActionA, ActionB]`.
|
* **Output:** Een lijst van uit te voeren acties: `[ActionA, ActionB]`.
|
||||||
|
|
||||||
### 4.3 Layer 3: The Safety Net (Post-Action Logic)
|
### 4.3 Layer 3: The Nudge (Post-Action Logic)
|
||||||
* **Trigger:** Succesvolle afronding van een intent (bijv. `CreateAppointment` klaar).
|
* **Trigger:** Succesvolle afronding van een intent (bijv. `CreateAppointment` klaar).
|
||||||
* **Werking:** Draait `Domain Rules` op de uitgevoerde actie.
|
* **Werking:** Draait `Domain Rules` op de uitgevoerde actie.
|
||||||
* **UI:** Toont een **Suggestion Toast** of **Card** ("Wil je ook...?").
|
* **UI:** Toont een **Suggestion Toast** of **Card** ("Wil je ook...?").
|
||||||
@@ -104,7 +104,7 @@ De UI past zich aan op basis van de complexiteit van de intentie.
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ 4. Safety Net (Proactive Toast) │
|
│ 4. Nudge (Proactive Toast) │
|
||||||
│ 💡 "Wil je de griep-poli waarschuwen?" [Ja, doe maar] [X] │
|
│ 💡 "Wil je de griep-poli waarschuwen?" [Ja, doe maar] [X] │
|
||||||
└─────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
@@ -124,7 +124,7 @@ Het systeem moet worden omgebouwd van `Single Intent` naar `Intent Chain`:
|
|||||||
|
|
||||||
**Oud:**
|
**Oud:**
|
||||||
```typescript
|
```typescript
|
||||||
interface Result { intent: SwiftIntent }
|
interface Result { intent: CortexIntent }
|
||||||
```
|
```
|
||||||
|
|
||||||
**Nieuw:**
|
**Nieuw:**
|
||||||
@@ -135,7 +135,7 @@ interface IntentChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IntentAction {
|
interface IntentAction {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
status: 'pending' | 'success' | 'failed';
|
status: 'pending' | 'success' | 'failed';
|
||||||
requiresConfirmation: boolean;
|
requiresConfirmation: boolean;
|
||||||
@@ -156,12 +156,12 @@ interface IntentAction {
|
|||||||
* Prompt engineering voor multi-intent herkenning ("En", "Daarna").
|
* Prompt engineering voor multi-intent herkenning ("En", "Daarna").
|
||||||
|
|
||||||
### Fase 3: Proactivity (Maand 2)
|
### Fase 3: Proactivity (Maand 2)
|
||||||
* Bouwen van de `Safety Net` listeners.
|
* Bouwen van de `Nudge` listeners.
|
||||||
* Protocollen toevoegen voor Medicatie en Wondzorg.
|
* Protocollen toevoegen voor Medicatie en Wondzorg.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Bijlagen & Referenties
|
## 8. Bijlagen & Referenties
|
||||||
* **PRD/Vision:** `docs/swift/ux-simulation-intent-next-level.md`
|
* **PRD/Vision:** `docs/swift/ux-simulation-intent-next-level.md`
|
||||||
* **Technical Base:** `lib/swift/intent-classifier-ai.ts`
|
* **Technical Base:** `lib/cortex/intent-classifier-ai.ts`
|
||||||
* **Legacy Docs:** `docs/swift/intent-architecture-v2-proposal.md`
|
* **Legacy Docs:** `docs/swift/intent-architecture-v2-proposal.md`
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ De Swift Cortex V2 architectuur is **goed haalbaar** binnen de bestaande codebas
|
|||||||
| Aspect | Score | Toelichting |
|
| Aspect | Score | Toelichting |
|
||||||
|--------|-------|-------------|
|
|--------|-------|-------------|
|
||||||
| **Technische haalbaarheid** | 🟢 Hoog | Bestaande architectuur is compatibel |
|
| **Technische haalbaarheid** | 🟢 Hoog | Bestaande architectuur is compatibel |
|
||||||
| **Complexiteit** | 🟡 Middel | Multi-intent en Safety Net zijn nieuwe concepten |
|
| **Complexiteit** | 🟡 Middel | Multi-intent en Nudge zijn nieuwe concepten |
|
||||||
| **Risico** | 🟢 Laag | Incrementeel te bouwen, backward compatible |
|
| **Risico** | 🟢 Laag | Incrementeel te bouwen, backward compatible |
|
||||||
| **MVP Scope** | 🟢 Realistisch | 6 user stories, goed afgebakend |
|
| **MVP Scope** | 🟢 Realistisch | 6 user stories, goed afgebakend |
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ De Swift Cortex V2 architectuur is **goed haalbaar** binnen de bestaande codebas
|
|||||||
|------------|----------------------|-----|
|
|------------|----------------------|-----|
|
||||||
| **Layer 1: Reflex Arc** | ✅ `intent-classifier.ts` (60 patterns) | Minimaal - voeg complexity detection toe |
|
| **Layer 1: Reflex Arc** | ✅ `intent-classifier.ts` (60 patterns) | Minimaal - voeg complexity detection toe |
|
||||||
| **Layer 2: Orchestrator** | ✅ `intent-classifier-ai.ts` (Haiku) | Middel - upgrade prompt voor multi-intent |
|
| **Layer 2: Orchestrator** | ✅ `intent-classifier-ai.ts` (Haiku) | Middel - upgrade prompt voor multi-intent |
|
||||||
| **Layer 3: Safety Net** | ❌ Niet aanwezig | Nieuw te bouwen |
|
| **Layer 3: Nudge** | ❌ Niet aanwezig | Nieuw te bouwen |
|
||||||
| **Context Injection** | 🟡 Basis aanwezig (`activePatient`, `shift`) | Uitbreiden met `agendaToday`, `recentIntents` |
|
| **Context Injection** | 🟡 Basis aanwezig (`activePatient`, `shift`) | Uitbreiden met `agendaToday`, `recentIntents` |
|
||||||
| **Multi-Intent Chains** | ❌ Single intent model | Data model refactor nodig |
|
| **Multi-Intent Chains** | ❌ Single intent model | Data model refactor nodig |
|
||||||
| **Entity Extraction** | ✅ `entity-extractor.ts` | Minimaal - voeg `patientResolution` toe |
|
| **Entity Extraction** | ✅ `entity-extractor.ts` | Minimaal - voeg `patientResolution` toe |
|
||||||
@@ -38,7 +38,7 @@ De Swift Cortex V2 architectuur is **goed haalbaar** binnen de bestaande codebas
|
|||||||
### 1.2 Bestaande Bestanden (Assets)
|
### 1.2 Bestaande Bestanden (Assets)
|
||||||
|
|
||||||
```
|
```
|
||||||
lib/swift/
|
lib/cortex/
|
||||||
├── types.ts ✅ Basis types, uitbreiden met IntentChain
|
├── types.ts ✅ Basis types, uitbreiden met IntentChain
|
||||||
├── intent-classifier.ts ✅ Layer 1 basis, voeg signals detection toe
|
├── intent-classifier.ts ✅ Layer 1 basis, voeg signals detection toe
|
||||||
├── intent-classifier-ai.ts ✅ Layer 2 basis, upgrade prompt
|
├── intent-classifier-ai.ts ✅ Layer 2 basis, upgrade prompt
|
||||||
@@ -49,16 +49,16 @@ lib/swift/
|
|||||||
├── error-handler.ts ✅ Recent toegevoegd
|
├── error-handler.ts ✅ Recent toegevoegd
|
||||||
└── [NIEUW] reflex-classifier.ts → Upgrade van intent-classifier
|
└── [NIEUW] reflex-classifier.ts → Upgrade van intent-classifier
|
||||||
└── [NIEUW] orchestrator.ts → Upgrade van intent-classifier-ai
|
└── [NIEUW] orchestrator.ts → Upgrade van intent-classifier-ai
|
||||||
└── [NIEUW] safety-net.ts → Nieuw te bouwen
|
└── [NIEUW] nudge.ts → Nieuw te bouwen
|
||||||
|
|
||||||
stores/
|
stores/
|
||||||
└── swift-store.ts ✅ Uitbreiden met chain state + suggestions
|
└── cortex-store.ts ✅ Uitbreiden met chain state + suggestions
|
||||||
|
|
||||||
components/swift/
|
components/cortex/
|
||||||
├── chat/ ✅ Bestaand, voeg ActionChainCard toe
|
├── chat/ ✅ Bestaand, voeg ActionChainCard toe
|
||||||
├── artifacts/ ✅ Bestaand, geen wijzigingen
|
├── artifacts/ ✅ Bestaand, geen wijzigingen
|
||||||
├── command-center/ ✅ Bestaand, voeg SuggestionToast toe
|
├── command-center/ ✅ Bestaand, voeg NudgeToast toe
|
||||||
└── [NIEUW] suggestion-toast.tsx
|
└── [NIEUW] nudge-toast.tsx
|
||||||
└── [NIEUW] chat/action-chain-card.tsx
|
└── [NIEUW] chat/action-chain-card.tsx
|
||||||
└── [NIEUW] chat/clarification-card.tsx
|
└── [NIEUW] chat/clarification-card.tsx
|
||||||
```
|
```
|
||||||
@@ -73,7 +73,7 @@ components/swift/
|
|||||||
```typescript
|
```typescript
|
||||||
// Huidige single-intent response
|
// Huidige single-intent response
|
||||||
interface ClassificationResult {
|
interface ClassificationResult {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -90,7 +90,7 @@ interface IntentChain {
|
|||||||
**Impact:**
|
**Impact:**
|
||||||
- `types.ts`: Nieuwe interfaces toevoegen (~50 regels)
|
- `types.ts`: Nieuwe interfaces toevoegen (~50 regels)
|
||||||
- `orchestrator.ts`: Nieuwe AI prompt met multi-intent instructies (~150 regels)
|
- `orchestrator.ts`: Nieuwe AI prompt met multi-intent instructies (~150 regels)
|
||||||
- `swift-store.ts`: Chain state toevoegen (~30 regels)
|
- `cortex-store.ts`: Chain state toevoegen (~30 regels)
|
||||||
- `action-chain-card.tsx`: Nieuwe UI component (~150 regels)
|
- `action-chain-card.tsx`: Nieuwe UI component (~150 regels)
|
||||||
|
|
||||||
**Effort: M (Medium)**
|
**Effort: M (Medium)**
|
||||||
@@ -110,7 +110,7 @@ context: {
|
|||||||
|
|
||||||
**V2 vereist:**
|
**V2 vereist:**
|
||||||
```typescript
|
```typescript
|
||||||
interface SwiftContext {
|
interface CortexContext {
|
||||||
activePatient: { id, name, recentNotes?, upcomingAppointments? };
|
activePatient: { id, name, recentNotes?, upcomingAppointments? };
|
||||||
currentView: string;
|
currentView: string;
|
||||||
shift: ShiftType;
|
shift: ShiftType;
|
||||||
@@ -121,7 +121,7 @@ interface SwiftContext {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Impact:**
|
**Impact:**
|
||||||
- Nieuwe `GET /api/swift/context` endpoint (~80 regels)
|
- Nieuwe `GET /api/cortex/context` endpoint (~80 regels)
|
||||||
- Context builder utility (~50 regels)
|
- Context builder utility (~50 regels)
|
||||||
- Store uitbreiding voor context sync (~20 regels)
|
- Store uitbreiding voor context sync (~20 regels)
|
||||||
|
|
||||||
@@ -147,20 +147,20 @@ interface SwiftContext {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2.4 Safety Net / Proactive Suggestions (US-MVP-06)
|
### 2.4 Nudge / Proactive Suggestions (US-MVP-06)
|
||||||
|
|
||||||
**Huidige situatie:**
|
**Huidige situatie:**
|
||||||
- Niet aanwezig
|
- Niet aanwezig
|
||||||
|
|
||||||
**V2 vereist:**
|
**V2 vereist:**
|
||||||
- Protocol Rules database
|
- Protocol Rules database
|
||||||
- `evaluateSafetyNet()` functie
|
- `evaluateNudge()` functie
|
||||||
- `SuggestionToast` component
|
- `NudgeToast` component
|
||||||
- Store state voor suggestions
|
- Store state voor suggestions
|
||||||
|
|
||||||
**Impact:**
|
**Impact:**
|
||||||
- `safety-net.ts`: Nieuwe module (~200 regels)
|
- `nudge.ts`: Nieuwe module (~200 regels)
|
||||||
- `suggestion-toast.tsx`: Nieuw component (~100 regels)
|
- `nudge-toast.tsx`: Nieuw component (~100 regels)
|
||||||
- Store uitbreiding (~40 regels)
|
- Store uitbreiding (~40 regels)
|
||||||
- Integratie in action execution flow
|
- Integratie in action execution flow
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ interface SwiftContext {
|
|||||||
| Risico | Impact | Mitigatie |
|
| Risico | Impact | Mitigatie |
|
||||||
|--------|--------|-----------|
|
|--------|--------|-----------|
|
||||||
| Feature creep | MVP te groot | Strikte scope (6 stories) |
|
| Feature creep | MVP te groot | Strikte scope (6 stories) |
|
||||||
| Protocol complexity | Safety Net te ambitieus | Begin met 1 hardcoded regel |
|
| Protocol complexity | Nudge te ambitieus | Begin met 1 hardcoded regel |
|
||||||
| Over-engineering | Te veel abstractie | "Working software" first |
|
| Over-engineering | Te veel abstractie | "Working software" first |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -195,10 +195,10 @@ interface SwiftContext {
|
|||||||
|
|
||||||
```
|
```
|
||||||
Fase 1: Foundation (Week 1)
|
Fase 1: Foundation (Week 1)
|
||||||
├── SwiftContext type definitie
|
├── CortexContext type definitie
|
||||||
├── GET /api/swift/context endpoint
|
├── GET /api/cortex/context endpoint
|
||||||
├── Reflex complexity detection upgrade
|
├── Reflex complexity detection upgrade
|
||||||
├── Feature flag: SWIFT_V2_ENABLED
|
├── Feature flag: CORTEX_V2_ENABLED
|
||||||
└── Deliverable: Context beschikbaar, backward compatible
|
└── Deliverable: Context beschikbaar, backward compatible
|
||||||
|
|
||||||
Fase 2: Multi-Intent (Week 2)
|
Fase 2: Multi-Intent (Week 2)
|
||||||
@@ -208,9 +208,9 @@ Fase 2: Multi-Intent (Week 2)
|
|||||||
├── Store chain state
|
├── Store chain state
|
||||||
└── Deliverable: "Zeg af en maak notitie" werkt
|
└── Deliverable: "Zeg af en maak notitie" werkt
|
||||||
|
|
||||||
Fase 3: Safety Net MVP (Week 3)
|
Fase 3: Nudge MVP (Week 3)
|
||||||
├── 1 hardcoded protocol regel (wondzorg)
|
├── 1 hardcoded protocol regel (wondzorg)
|
||||||
├── SuggestionToast component
|
├── NudgeToast component
|
||||||
├── Trigger na dagnotitie
|
├── Trigger na dagnotitie
|
||||||
└── Deliverable: Proactieve suggestie demo
|
└── Deliverable: Proactieve suggestie demo
|
||||||
|
|
||||||
@@ -226,9 +226,9 @@ Fase 4: Polish (Week 4)
|
|||||||
De V2 architectuur kan naast V1 draaien:
|
De V2 architectuur kan naast V1 draaien:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// lib/swift/classifier-adapter.ts
|
// lib/cortex/classifier-adapter.ts
|
||||||
export async function classifyIntent(input: string, context?: SwiftContext) {
|
export async function classifyIntent(input: string, context?: CortexContext) {
|
||||||
if (!FEATURE_FLAGS.SWIFT_V2_ENABLED) {
|
if (!FEATURE_FLAGS.CORTEX_V2_ENABLED) {
|
||||||
return classifyV1(input); // Bestaande flow
|
return classifyV1(input); // Bestaande flow
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,12 +251,12 @@ export async function classifyIntent(input: string, context?: SwiftContext) {
|
|||||||
| `types.ts` | 150 regels | - | S |
|
| `types.ts` | 150 regels | - | S |
|
||||||
| `reflex-classifier.ts` | 200 regels | upgrade | M |
|
| `reflex-classifier.ts` | 200 regels | upgrade | M |
|
||||||
| `orchestrator.ts` | 250 regels | upgrade | M |
|
| `orchestrator.ts` | 250 regels | upgrade | M |
|
||||||
| `safety-net.ts` | 200 regels | nieuw | M |
|
| `nudge.ts` | 200 regels | nieuw | M |
|
||||||
| `swift-store.ts` | - | +100 regels | S |
|
| `cortex-store.ts` | - | +100 regels | S |
|
||||||
| `action-chain-card.tsx` | 180 regels | nieuw | M |
|
| `action-chain-card.tsx` | 180 regels | nieuw | M |
|
||||||
| `suggestion-toast.tsx` | 100 regels | nieuw | S |
|
| `nudge-toast.tsx` | 100 regels | nieuw | S |
|
||||||
| `clarification-card.tsx` | 60 regels | nieuw | S |
|
| `clarification-card.tsx` | 60 regels | nieuw | S |
|
||||||
| `/api/swift/context` | 80 regels | nieuw | S |
|
| `/api/cortex/context` | 80 regels | nieuw | S |
|
||||||
| `/api/intent/classify` | 150 regels | nieuw | M |
|
| `/api/intent/classify` | 150 regels | nieuw | M |
|
||||||
| Tests | 300 regels | nieuw | M |
|
| Tests | 300 regels | nieuw | M |
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ export async function classifyIntent(input: string, context?: SwiftContext) {
|
|||||||
|------|--------|--------------|
|
|------|--------|--------------|
|
||||||
| Fase 1: Foundation | 2-3 dagen | Laag |
|
| Fase 1: Foundation | 2-3 dagen | Laag |
|
||||||
| Fase 2: Multi-Intent | 3-4 dagen | Middel |
|
| Fase 2: Multi-Intent | 3-4 dagen | Middel |
|
||||||
| Fase 3: Safety Net | 2-3 dagen | Middel |
|
| Fase 3: Nudge | 2-3 dagen | Middel |
|
||||||
| Fase 4: Polish | 2-3 dagen | Laag |
|
| Fase 4: Polish | 2-3 dagen | Laag |
|
||||||
|
|
||||||
**Totaal: 9-13 werkdagen voor MVP**
|
**Totaal: 9-13 werkdagen voor MVP**
|
||||||
@@ -286,7 +286,7 @@ export async function classifyIntent(input: string, context?: SwiftContext) {
|
|||||||
|
|
||||||
### 6.2 DON'T's
|
### 6.2 DON'T's
|
||||||
|
|
||||||
1. **Niet alle protocollen tegelijk** - Begin met 1 Safety Net regel
|
1. **Niet alle protocollen tegelijk** - Begin met 1 Nudge regel
|
||||||
2. **Geen over-engineering** - De `ProtocolRule` interface is voor later
|
2. **Geen over-engineering** - De `ProtocolRule` interface is voor later
|
||||||
3. **Niet de store herschrijven** - Extend, niet replace
|
3. **Niet de store herschrijven** - Extend, niet replace
|
||||||
4. **Geen rollout strategie nodig** - Dit is een prototype
|
4. **Geen rollout strategie nodig** - Dit is een prototype
|
||||||
@@ -320,7 +320,7 @@ De Swift Cortex V2 architectuur is **volledig haalbaar** binnen de bestaande cod
|
|||||||
|
|
||||||
### Volgende Stap
|
### Volgende Stap
|
||||||
|
|
||||||
Start met **Fase 1: Foundation** - de SwiftContext API endpoint. Dit is:
|
Start met **Fase 1: Foundation** - de CortexContext API endpoint. Dit is:
|
||||||
- Low risk
|
- Low risk
|
||||||
- Onafhankelijk van andere features
|
- Onafhankelijk van andere features
|
||||||
- Direct waarde toevoegend aan bestaande AI classificatie
|
- Direct waarde toevoegend aan bestaande AI classificatie
|
||||||
@@ -334,13 +334,13 @@ Start met **Fase 1: Foundation** - de SwiftContext API endpoint. Dit is:
|
|||||||
|
|
||||||
| Bestand | Regels | Functie |
|
| Bestand | Regels | Functie |
|
||||||
|---------|--------|---------|
|
|---------|--------|---------|
|
||||||
| `lib/swift/types.ts` | ~180 | Type definities |
|
| `lib/cortex/types.ts` | ~180 | Type definities |
|
||||||
| `lib/swift/intent-classifier.ts` | ~200 | Layer 1 classifier |
|
| `lib/cortex/intent-classifier.ts` | ~200 | Layer 1 classifier |
|
||||||
| `lib/swift/intent-classifier-ai.ts` | ~100 | Layer 2 AI fallback |
|
| `lib/cortex/intent-classifier-ai.ts` | ~100 | Layer 2 AI fallback |
|
||||||
| `lib/swift/entity-extractor.ts` | ~250 | Entity extraction |
|
| `lib/cortex/entity-extractor.ts` | ~250 | Entity extraction |
|
||||||
| `lib/swift/date-time-parser.ts` | ~200 | Datum/tijd parsing |
|
| `lib/cortex/date-time-parser.ts` | ~200 | Datum/tijd parsing |
|
||||||
| `lib/swift/action-parser.ts` | ~150 | Action routing |
|
| `lib/cortex/action-parser.ts` | ~150 | Action routing |
|
||||||
| `stores/swift-store.ts` | ~200 | Zustand state |
|
| `stores/cortex-store.ts` | ~200 | Zustand state |
|
||||||
|
|
||||||
### B. V2 Documentatie Verwijzingen
|
### B. V2 Documentatie Verwijzingen
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ We vervangen de simpele `Classifier` door een slimmere `Cortex`.
|
|||||||
* **Nieuwe capability: Entity Disambiguation.**
|
* **Nieuwe capability: Entity Disambiguation.**
|
||||||
* Snap dat "Jan" verwijst naar de patiënt die ik *vandaaag* in mijn agenda heb.
|
* Snap dat "Jan" verwijst naar de patiënt die ik *vandaaag* in mijn agenda heb.
|
||||||
|
|
||||||
### Layer 3: The Safety Net (Contextual Logic)
|
### Layer 3: The Nudge (Contextual Logic)
|
||||||
* **Wat:** Een business logic laag die *na* de intent draait.
|
* **Wat:** Een business logic laag die *na* de intent draait.
|
||||||
* **Functie:** Proactive Suggestions.
|
* **Functie:** Proactive Suggestions.
|
||||||
* *Trigger:* Intent `CreateWoundCareNote` 'completed'.
|
* *Trigger:* Intent `CreateWoundCareNote` 'completed'.
|
||||||
@@ -81,6 +81,6 @@ graph TD
|
|||||||
Plan --> Action1["Action A"]
|
Plan --> Action1["Action A"]
|
||||||
Plan --> Action2["Action B"]
|
Plan --> Action2["Action B"]
|
||||||
|
|
||||||
Action1 --> Safety["Layer 3: Safety Net"]
|
Action1 --> Safety["Layer 3: Nudge"]
|
||||||
Safety -->|Trigger Found| Suggestion["Suggest Follow-up"]
|
Safety -->|Trigger Found| Suggestion["Suggest Follow-up"]
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ We splitsen de ontwikkeling in **MVP** (Wat we nu bouwen voor de publieke demo)
|
|||||||
2. **Multi-Intent:** Het kunnen verwerken van samengestelde zinnen ("Zeg af en maak notitie").
|
2. **Multi-Intent:** Het kunnen verwerken van samengestelde zinnen ("Zeg af en maak notitie").
|
||||||
3. **Context Awareness:** Het correct interpreteren van "hij", "deze", "morgen" o.b.v. de huidige schermstatus.
|
3. **Context Awareness:** Het correct interpreteren van "hij", "deze", "morgen" o.b.v. de huidige schermstatus.
|
||||||
4. **UI Feedback:** Visualisatie van het "denkproces" en gestapelde resultaten (Stacked Cards).
|
4. **UI Feedback:** Visualisatie van het "denkproces" en gestapelde resultaten (Stacked Cards).
|
||||||
5. **Basic Safety Net:** Eén hardcoded voorbeeld van proactiviteit (bijv. "Wondcontrole suggestie") om het concept te tonen.
|
5. **Basic Nudge:** Eén hardcoded voorbeeld van proactiviteit (bijv. "Wondcontrole suggestie") om het concept te tonen.
|
||||||
|
|
||||||
### ❌ Out of Scope (Post-MVP - "The Product")
|
### ❌ Out of Scope (Post-MVP - "The Product")
|
||||||
*Focus: Veiligheid, robuustheid, edge-cases.*
|
*Focus: Veiligheid, robuustheid, edge-cases.*
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 1. Algemene Conclusie
|
## 1. Algemene Conclusie
|
||||||
Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen architectuur die de grootste pijnpunten van V1 (traagheid bij simpele taken, domheid bij complexe taken) effectief oplost. De opsplitsing in drie lagen (Reflex, Orchestrator, Safety Net) is logisch en schaalbaar.
|
Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen architectuur die de grootste pijnpunten van V1 (traagheid bij simpele taken, domheid bij complexe taken) effectief oplost. De opsplitsing in drie lagen (Reflex, Orchestrator, Nudge) is logisch en schaalbaar.
|
||||||
|
|
||||||
**Oordeel:** ✅ **Go for launch**, mits onderstaande punten in acht worden genomen.
|
**Oordeel:** ✅ **Go for launch**, mits onderstaande punten in acht worden genomen.
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen
|
|||||||
* **Pros:**
|
* **Pros:**
|
||||||
* **Separation of Concerns:** De scheiding tussen deterministische regex (L1) en probabilistische AI (L2) beschermt de performance van basisfuncties.
|
* **Separation of Concerns:** De scheiding tussen deterministische regex (L1) en probabilistische AI (L2) beschermt de performance van basisfuncties.
|
||||||
* **Schaalbaarheid:** L2 is losgekoppeld; we kunnen het model (Claude Haiku) later vervangen door GPT-4o of een local model zonder L1 te breken.
|
* **Schaalbaarheid:** L2 is losgekoppeld; we kunnen het model (Claude Haiku) later vervangen door GPT-4o of een local model zonder L1 te breken.
|
||||||
* **Type Safety:** De definities voor `IntentChain` en `SwiftContext` zijn robuust.
|
* **Type Safety:** De definities voor `IntentChain` en `CortexContext` zijn robuust.
|
||||||
* **Cons / Risico's:**
|
* **Cons / Risico's:**
|
||||||
* **State Complexity:** Het beheren van een `IntentChain` (met statussen als `pending`, `executing`, `failed`) introduceert complexe state management logica. Wat als stap 1 slaagt maar stap 2 faalt? Rollback support (genoemd in L2 architecture overview) is complex om generiek te bouwen.
|
* **State Complexity:** Het beheren van een `IntentChain` (met statussen als `pending`, `executing`, `failed`) introduceert complexe state management logica. Wat als stap 1 slaagt maar stap 2 faalt? Rollback support (genoemd in L2 architecture overview) is complex om generiek te bouwen.
|
||||||
* **Drift:** Risico dat L1 (Regex) en L2 (AI) uit elkaar groeien. Als de AI "agenda" anders interpreteert dan de Regex.
|
* **Drift:** Risico dat L1 (Regex) en L2 (AI) uit elkaar groeien. Als de AI "agenda" anders interpreteert dan de Regex.
|
||||||
@@ -42,7 +42,7 @@ Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen
|
|||||||
**Perspectief:** UI implementatie, feedback loops, React state.
|
**Perspectief:** UI implementatie, feedback loops, React state.
|
||||||
|
|
||||||
* **Pros:**
|
* **Pros:**
|
||||||
* **Store Integration:** Uitbreiding van `SwiftStore` is logisch.
|
* **Store Integration:** Uitbreiding van `CortexStore` is logisch.
|
||||||
* **Reflex Snelheid:** Client-side regex betekent instant feedback (<20ms), wat de UX enorm verbetert.
|
* **Reflex Snelheid:** Client-side regex betekent instant feedback (<20ms), wat de UX enorm verbetert.
|
||||||
* **Cons:**
|
* **Cons:**
|
||||||
* **UI Complexiteit:** Het visualiseren van "Stacked Cards" voor multi-intents is nieuw. Hoe tonen we de voortgang van "Actie 1 klaar, Actie 2 bezig"?
|
* **UI Complexiteit:** Het visualiseren van "Stacked Cards" voor multi-intents is nieuw. Hoe tonen we de voortgang van "Actie 1 klaar, Actie 2 bezig"?
|
||||||
@@ -54,12 +54,12 @@ Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen
|
|||||||
|
|
||||||
* **Pros:**
|
* **Pros:**
|
||||||
* **Killer Feature:** Multi-intent ("Zeg af en email") is een enorme meerwaarde die de gebruiker tijd bespaart.
|
* **Killer Feature:** Multi-intent ("Zeg af en email") is een enorme meerwaarde die de gebruiker tijd bespaart.
|
||||||
* **Proactivity:** De Safety Net suggesties ("Wondcontrole inplannen?") transformeren het systeem van typemachine naar partner.
|
* **Proactivity:** De Nudge suggesties ("Wondcontrole inplannen?") transformeren het systeem van typemachine naar partner.
|
||||||
* **No Dead Ends:** Het doel "Nooit 'ik snap het niet' zeggen" is perfect.
|
* **No Dead Ends:** Het doel "Nooit 'ik snap het niet' zeggen" is perfect.
|
||||||
* **Cons / Risico's:**
|
* **Cons / Risico's:**
|
||||||
* **Uncanny Valley:** Als L1 "dom" voelt en L2 "slim", snapt de gebruiker dan wanneer hij tegen wie praat?
|
* **Uncanny Valley:** Als L1 "dom" voelt en L2 "slim", snapt de gebruiker dan wanneer hij tegen wie praat?
|
||||||
* **Over-proactive:** Te veel Safety Net suggesties worden irritant (Clippy effect). "Wil je dit opslaan?" "Wil je dat doen?".
|
* **Over-proactive:** Te veel Nudge suggesties worden irritant (Clippy effect). "Wil je dit opslaan?" "Wil je dat doen?".
|
||||||
* **Advies:** Start Safety Net met zeer conservatieve regels. Alleen medisch kritieke suggesties, geen administratieve "nagging".
|
* **Advies:** Start Nudge met zeer conservatieve regels. Alleen medisch kritieke suggesties, geen administratieve "nagging".
|
||||||
|
|
||||||
### 🧪 QA Engineer / Tester
|
### 🧪 QA Engineer / Tester
|
||||||
**Perspectief:** Testbaarheid, betrouwbaarheid.
|
**Perspectief:** Testbaarheid, betrouwbaarheid.
|
||||||
@@ -96,10 +96,10 @@ Het voorgestelde **Hyper-Hybrid model (Swift Cortex)** is een sterke, volwassen
|
|||||||
|-----------|--------------|--------------|
|
|-----------|--------------|--------------|
|
||||||
| Layer 1 (Reflex) | ⭐⭐⭐⭐⭐ (Hoog) | Laag |
|
| Layer 1 (Reflex) | ⭐⭐⭐⭐⭐ (Hoog) | Laag |
|
||||||
| Layer 2 (Orchestrator) | ⭐⭐⭐⭐ (Goed) | Middel |
|
| Layer 2 (Orchestrator) | ⭐⭐⭐⭐ (Goed) | Middel |
|
||||||
| Layer 3 (Safety Net) | ⭐⭐⭐⭐ (Goed) | Laag (als we simpel beginnen) |
|
| Layer 3 (Nudge) | ⭐⭐⭐⭐ (Goed) | Laag (als we simpel beginnen) |
|
||||||
| Multi-Intent Frontend | ⭐⭐⭐ (Uitdagend) | Hoog (UI flows) |
|
| Multi-Intent Frontend | ⭐⭐⭐ (Uitdagend) | Hoog (UI flows) |
|
||||||
|
|
||||||
**Advies:** Start direct met **Fase 1 (Reflex + Basic Orchestrator)**. Schuif Layer 3 (Safety Net) naar de volgende sprint om focus te houden op de core flow.
|
**Advies:** Start direct met **Fase 1 (Reflex + Basic Orchestrator)**. Schuif Layer 3 (Nudge) naar de volgende sprint om focus te houden op de core flow.
|
||||||
|
|
||||||
## 6. Concretie Actiepunten
|
## 6. Concretie Actiepunten
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Action Parser for Swift Assistent
|
* Action Parser for Cortex Assistent
|
||||||
*
|
*
|
||||||
* Parses JSON action objects from AI responses and validates them.
|
* Parses JSON action objects from AI responses and validates them.
|
||||||
*
|
*
|
||||||
* Epic: E3 (Chat API & Swift Assistent)
|
* Epic: E3 (Chat API & Cortex Assistent)
|
||||||
* Story: E3.S4 (Intent detection in response)
|
* Story: E3.S4 (Intent detection in response)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import type { ChatAction, SwiftIntent, BlockType } from '@/stores/swift-store';
|
import type { ChatAction, CortexIntent, BlockType } from '@/stores/cortex-store';
|
||||||
|
|
||||||
// Validation schema for action objects
|
// Validation schema for action objects
|
||||||
const ActionSchema = z.object({
|
const ActionSchema = z.object({
|
||||||
@@ -156,10 +156,19 @@ export function parseActionFromResponse(responseText: string): ParsedActionResul
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize entities - convert complex identifier to string
|
||||||
|
const normalizedEntities = {
|
||||||
|
...validation.data.entities,
|
||||||
|
// Convert object identifier to string if needed
|
||||||
|
identifier: typeof validation.data.entities.identifier === 'object'
|
||||||
|
? validation.data.entities.identifier.encounterId || JSON.stringify(validation.data.entities.identifier)
|
||||||
|
: validation.data.entities.identifier,
|
||||||
|
};
|
||||||
|
|
||||||
// Valid action found
|
// Valid action found
|
||||||
const action: ChatAction = {
|
const action: ChatAction = {
|
||||||
intent: validation.data.intent,
|
intent: validation.data.intent,
|
||||||
entities: validation.data.entities,
|
entities: normalizedEntities,
|
||||||
confidence: validation.data.confidence,
|
confidence: validation.data.confidence,
|
||||||
artifact: validation.data.artifact,
|
artifact: validation.data.artifact,
|
||||||
};
|
};
|
||||||
@@ -191,7 +200,7 @@ export function getConfidenceLabel(confidence: number): string {
|
|||||||
/**
|
/**
|
||||||
* Validate that artifact type matches intent
|
* Validate that artifact type matches intent
|
||||||
*/
|
*/
|
||||||
export function validateArtifactType(intent: SwiftIntent, artifactType?: BlockType): boolean {
|
export function validateArtifactType(intent: CortexIntent, artifactType?: BlockType): boolean {
|
||||||
if (!artifactType) return true; // No artifact is valid
|
if (!artifactType) return true; // No artifact is valid
|
||||||
if (artifactType === 'patient-dashboard') return true;
|
if (artifactType === 'patient-dashboard') return true;
|
||||||
|
|
||||||
@@ -199,14 +208,14 @@ export function validateArtifactType(intent: SwiftIntent, artifactType?: BlockTy
|
|||||||
if (intent === 'unknown') return artifactType === 'fallback';
|
if (intent === 'unknown') return artifactType === 'fallback';
|
||||||
|
|
||||||
// For agenda intents, all map to agenda block types
|
// For agenda intents, all map to agenda block types
|
||||||
const agendaIntents: SwiftIntent[] = [
|
const agendaIntents: CortexIntent[] = [
|
||||||
'agenda_query',
|
'agenda_query',
|
||||||
'create_appointment',
|
'create_appointment',
|
||||||
'cancel_appointment',
|
'cancel_appointment',
|
||||||
'reschedule_appointment',
|
'reschedule_appointment',
|
||||||
];
|
];
|
||||||
if (agendaIntents.includes(intent)) {
|
if (agendaIntents.includes(intent)) {
|
||||||
return agendaIntents.includes(artifactType as SwiftIntent);
|
return agendaIntents.includes(artifactType as CortexIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return intent === artifactType;
|
return intent === artifactType;
|
||||||
@@ -224,7 +233,7 @@ export function validateArtifactType(intent: SwiftIntent, artifactType?: BlockTy
|
|||||||
* @returns Artifact configuration or null if confidence too low or required data missing
|
* @returns Artifact configuration or null if confidence too low or required data missing
|
||||||
*/
|
*/
|
||||||
export function routeIntentToArtifact(
|
export function routeIntentToArtifact(
|
||||||
intent: SwiftIntent,
|
intent: CortexIntent,
|
||||||
entities: Record<string, any>,
|
entities: Record<string, any>,
|
||||||
confidence: number
|
confidence: number
|
||||||
): { type: BlockType; prefill: Record<string, any>; title: string } | null {
|
): { type: BlockType; prefill: Record<string, any>; title: string } | null {
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Chat API Client
|
* Cortex Chat API Client
|
||||||
*
|
*
|
||||||
* Client-side helper voor het aanroepen van de Swift chat API met streaming support.
|
* Client-side helper voor het aanroepen van de Cortex chat API met streaming support.
|
||||||
*
|
*
|
||||||
* Epic: E3 (Chat API & Swift Assistent)
|
* Epic: E3 (Chat API & Cortex Assistent)
|
||||||
* Story: E3.S1 (Chat API endpoint skeleton)
|
* Story: E3.S1 (Chat API endpoint skeleton)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ChatMessage } from '@/stores/swift-store';
|
import type { ChatMessage } from '@/stores/cortex-store';
|
||||||
|
|
||||||
export interface ChatContext {
|
export interface ChatContext {
|
||||||
activePatient?: {
|
activePatient?: {
|
||||||
@@ -36,7 +36,7 @@ export async function sendChatMessage(
|
|||||||
onError?: (error: string) => void
|
onError?: (error: string) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/swift/chat', {
|
const response = await fetch('/api/cortex/chat', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* Date/Time Parser Utilities
|
* Date/Time Parser Utilities
|
||||||
*
|
*
|
||||||
* Parses natural language date and time expressions (Dutch)
|
* Parses natural language date and time expressions (Dutch)
|
||||||
* for Swift agenda functionality.
|
* for Cortex agenda functionality.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
||||||
import type { ExtractedEntities, SwiftIntent } from './types';
|
import type { ExtractedEntities, CortexIntent } from './types';
|
||||||
import {
|
import {
|
||||||
parseRelativeDate,
|
parseRelativeDate,
|
||||||
parseTime,
|
parseTime,
|
||||||
@@ -90,7 +90,7 @@ const COMMON_NAMES = new Set([
|
|||||||
/**
|
/**
|
||||||
* Extract entities from user input based on the detected intent.
|
* Extract entities from user input based on the detected intent.
|
||||||
*/
|
*/
|
||||||
export function extractEntities(input: string, intent: SwiftIntent): ExtractedEntities {
|
export function extractEntities(input: string, intent: CortexIntent): ExtractedEntities {
|
||||||
const trimmedInput = input.trim().toLowerCase();
|
const trimmedInput = input.trim().toLowerCase();
|
||||||
const entities: ExtractedEntities = {};
|
const entities: ExtractedEntities = {};
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Error Handler Utility voor Swift
|
* Error Handler Utility voor Cortex
|
||||||
*
|
*
|
||||||
* E5.S2: Gecentraliseerde error handling met network detection,
|
* E5.S2: Gecentraliseerde error handling met network detection,
|
||||||
* gebruiksvriendelijke berichten en retry logic.
|
* gebruiksvriendelijke berichten en retry logic.
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Library Barrel Export
|
* Cortex Library Barrel Export
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './use-swift-voice';
|
export * from './use-cortex-voice';
|
||||||
export * from './intent-classifier';
|
export * from './intent-classifier';
|
||||||
export * from './intent-classifier-ai';
|
export * from './intent-classifier-ai';
|
||||||
export * from './entity-extractor';
|
export * from './entity-extractor';
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import type { SwiftIntent, ExtractedEntities } from './types';
|
import type { CortexIntent, ExtractedEntities } from './types';
|
||||||
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
||||||
|
|
||||||
// Zod schema for AI response validation
|
// Zod schema for AI response validation
|
||||||
@@ -37,7 +37,7 @@ const AIIntentResponseSchema = z.object({
|
|||||||
type AIIntentResponse = z.infer<typeof AIIntentResponseSchema>;
|
type AIIntentResponse = z.infer<typeof AIIntentResponseSchema>;
|
||||||
|
|
||||||
export interface AIClassificationResult {
|
export interface AIClassificationResult {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
source: 'ai';
|
source: 'ai';
|
||||||
@@ -45,7 +45,7 @@ export interface AIClassificationResult {
|
|||||||
reasoning?: string;
|
reasoning?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const INTENT_CLASSIFIER_SYSTEM_PROMPT = `Je bent een intent classifier voor een Nederlands EPD (Elektronisch Patiënten Dossier) systeem genaamd Swift.
|
const INTENT_CLASSIFIER_SYSTEM_PROMPT = `Je bent een intent classifier voor een Nederlands EPD (Elektronisch Patiënten Dossier) systeem genaamd Cortex.
|
||||||
|
|
||||||
Je taak is om de intentie van een zorgmedewerker te classificeren in één van deze categorieën:
|
Je taak is om de intentie van een zorgmedewerker te classificeren in één van deze categorieën:
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ export async function classifyIntentWithAI(input: string): Promise<AIClassificat
|
|||||||
// and the local extractor will structure them properly
|
// and the local extractor will structure them properly
|
||||||
|
|
||||||
return {
|
return {
|
||||||
intent: validated.intent as SwiftIntent,
|
intent: validated.intent as CortexIntent,
|
||||||
confidence: validated.confidence,
|
confidence: validated.confidence,
|
||||||
entities,
|
entities,
|
||||||
source: 'ai',
|
source: 'ai',
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Local Intent Classifier
|
* Local Intent Classifier
|
||||||
*
|
*
|
||||||
* Fast regex-based intent classification for Swift.
|
* Fast regex-based intent classification for Cortex.
|
||||||
* Target: <50ms response time.
|
* Target: <50ms response time.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SwiftIntent } from './types';
|
import type { CortexIntent } from './types';
|
||||||
|
|
||||||
export interface ClassificationResult {
|
export interface ClassificationResult {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
matchedPattern?: string;
|
matchedPattern?: string;
|
||||||
processingTimeMs: number;
|
processingTimeMs: number;
|
||||||
@@ -21,7 +21,7 @@ interface PatternConfig {
|
|||||||
|
|
||||||
// Intent patterns with weights
|
// Intent patterns with weights
|
||||||
// Weight 1.0 = exact match, 0.8 = strong match, 0.6 = partial match
|
// Weight 1.0 = exact match, 0.8 = strong match, 0.6 = partial match
|
||||||
const INTENT_PATTERNS: Record<Exclude<SwiftIntent, 'unknown'>, PatternConfig[]> = {
|
const INTENT_PATTERNS: Record<Exclude<CortexIntent, 'unknown'>, PatternConfig[]> = {
|
||||||
dagnotitie: [
|
dagnotitie: [
|
||||||
// Exact commands
|
// Exact commands
|
||||||
{ pattern: /^dagnotitie\b/i, weight: 1.0 },
|
{ pattern: /^dagnotitie\b/i, weight: 1.0 },
|
||||||
@@ -159,7 +159,7 @@ const HELP_PATTERNS: PatternConfig[] = [
|
|||||||
{ pattern: /^help\b/i, weight: 1.0 },
|
{ pattern: /^help\b/i, weight: 1.0 },
|
||||||
{ pattern: /^hulp\b/i, weight: 1.0 },
|
{ pattern: /^hulp\b/i, weight: 1.0 },
|
||||||
{ pattern: /^\?\s*$/i, weight: 1.0 },
|
{ pattern: /^\?\s*$/i, weight: 1.0 },
|
||||||
{ pattern: /^wat\s+kan\s+(ik|je|swift)\b/i, weight: 1.0 },
|
{ pattern: /^wat\s+kan\s+(ik|je|cortex)\b/i, weight: 1.0 },
|
||||||
{ pattern: /^hoe\s+werkt\b/i, weight: 0.9 },
|
{ pattern: /^hoe\s+werkt\b/i, weight: 0.9 },
|
||||||
{ pattern: /^voorbeelden?\b/i, weight: 0.9 },
|
{ pattern: /^voorbeelden?\b/i, weight: 0.9 },
|
||||||
];
|
];
|
||||||
@@ -194,14 +194,14 @@ export function classifyIntent(input: string): ClassificationResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find best matching intent
|
// Find best matching intent
|
||||||
let bestMatch: { intent: SwiftIntent; confidence: number; pattern: string } | null = null;
|
let bestMatch: { intent: CortexIntent; confidence: number; pattern: string } | null = null;
|
||||||
|
|
||||||
for (const [intent, patterns] of Object.entries(INTENT_PATTERNS)) {
|
for (const [intent, patterns] of Object.entries(INTENT_PATTERNS)) {
|
||||||
for (const { pattern, weight } of patterns) {
|
for (const { pattern, weight } of patterns) {
|
||||||
if (pattern.test(trimmedInput)) {
|
if (pattern.test(trimmedInput)) {
|
||||||
if (!bestMatch || weight > bestMatch.confidence) {
|
if (!bestMatch || weight > bestMatch.confidence) {
|
||||||
bestMatch = {
|
bestMatch = {
|
||||||
intent: intent as SwiftIntent,
|
intent: intent as CortexIntent,
|
||||||
confidence: weight,
|
confidence: weight,
|
||||||
pattern: pattern.toString(),
|
pattern: pattern.toString(),
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Swift Type Definitions
|
* Cortex Type Definitions
|
||||||
*
|
*
|
||||||
* Core types for the Swift Contextual UI system.
|
* Core types for the Cortex Contextual UI system.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
import type { VerpleegkundigCategory } from '@/lib/types/report';
|
||||||
|
|
||||||
// Intent types
|
// Intent types
|
||||||
export type SwiftIntent =
|
export type CortexIntent =
|
||||||
| 'dagnotitie'
|
| 'dagnotitie'
|
||||||
| 'zoeken'
|
| 'zoeken'
|
||||||
| 'overdracht'
|
| 'overdracht'
|
||||||
@@ -17,14 +17,14 @@ export type SwiftIntent =
|
|||||||
| 'reschedule_appointment'
|
| 'reschedule_appointment'
|
||||||
| 'unknown';
|
| 'unknown';
|
||||||
|
|
||||||
export type BlockType = Exclude<SwiftIntent, 'unknown'> | 'patient-dashboard';
|
export type BlockType = Exclude<CortexIntent, 'unknown'> | 'patient-dashboard';
|
||||||
|
|
||||||
// Shift types
|
// Shift types
|
||||||
export type ShiftType = 'nacht' | 'ochtend' | 'middag' | 'avond';
|
export type ShiftType = 'nacht' | 'ochtend' | 'middag' | 'avond';
|
||||||
|
|
||||||
// Intent classification result
|
// Intent classification result
|
||||||
export interface IntentClassificationResult {
|
export interface IntentClassificationResult {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
source: 'local' | 'ai';
|
source: 'local' | 'ai';
|
||||||
@@ -140,7 +140,7 @@ export const BLOCK_CONFIGS: Record<BlockType, BlockConfig> = {
|
|||||||
// Recent action type
|
// Recent action type
|
||||||
export interface RecentAction {
|
export interface RecentAction {
|
||||||
id: string;
|
id: string;
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
label: string;
|
label: string;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
patientName?: string;
|
patientName?: string;
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swift Voice Hook
|
* Cortex Voice Hook
|
||||||
*
|
*
|
||||||
* Wraps useDeepgramStreaming for Swift-specific voice input behavior.
|
* Wraps useDeepgramStreaming for Cortex-specific voice input behavior.
|
||||||
* Streams transcript directly to the command input.
|
* Streams transcript directly to the command input.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { useSwiftStore } from '@/stores/swift-store';
|
import { useCortexStore } from '@/stores/cortex-store';
|
||||||
import {
|
import {
|
||||||
useDeepgramStreaming,
|
useDeepgramStreaming,
|
||||||
type TranscriptResult,
|
type TranscriptResult,
|
||||||
} from '@/hooks/use-deepgram-streaming';
|
} from '@/hooks/use-deepgram-streaming';
|
||||||
|
|
||||||
export interface UseSwiftVoiceReturn {
|
export interface UseCortexVoiceReturn {
|
||||||
isRecording: boolean;
|
isRecording: boolean;
|
||||||
isConnecting: boolean;
|
isConnecting: boolean;
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
@@ -25,8 +25,8 @@ export interface UseSwiftVoiceReturn {
|
|||||||
isBrowserSupported: boolean;
|
isBrowserSupported: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSwiftVoice(): UseSwiftVoiceReturn {
|
export function useCortexVoice(): UseCortexVoiceReturn {
|
||||||
const { setInputValue, setVoiceActive, inputValue } = useSwiftStore();
|
const { setInputValue, setVoiceActive, inputValue } = useCortexStore();
|
||||||
|
|
||||||
// Track the base text (what was in input before recording started)
|
// Track the base text (what was in input before recording started)
|
||||||
const baseTextRef = useRef('');
|
const baseTextRef = useRef('');
|
||||||
@@ -59,7 +59,7 @@ export function useSwiftVoice(): UseSwiftVoiceReturn {
|
|||||||
|
|
||||||
const handleError = useCallback(
|
const handleError = useCallback(
|
||||||
(error: Error) => {
|
(error: Error) => {
|
||||||
console.error('[SwiftVoice] Error:', error.message);
|
console.error('[CortexVoice] Error:', error.message);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Manual verification script for entity extraction with date/time parser
|
* Manual verification script for entity extraction with date/time parser
|
||||||
* Run with: pnpm tsx lib/swift/verify-entity-extraction.ts
|
* Run with: pnpm tsx lib/cortex/verify-entity-extraction.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { extractEntities } from './entity-extractor';
|
import { extractEntities } from './entity-extractor';
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Manual verification script for date-time parser
|
* Manual verification script for date-time parser
|
||||||
* Run with: pnpm tsx lib/swift/verify-parser.ts
|
* Run with: pnpm tsx lib/cortex/verify-parser.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -88,7 +88,7 @@ export async function middleware(request: NextRequest) {
|
|||||||
// Helper: get preferred interface redirect path
|
// Helper: get preferred interface redirect path
|
||||||
const getPreferredPath = () => {
|
const getPreferredPath = () => {
|
||||||
const preference = user?.user_metadata?.preferred_interface
|
const preference = user?.user_metadata?.preferred_interface
|
||||||
return preference === 'swift' ? '/epd/swift' : '/epd/clients'
|
return preference === 'cortex' ? '/epd/cortex' : '/epd/clients'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to preferred interface if authenticated and trying to access login
|
// Redirect to preferred interface if authenticated and trying to access login
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import type { VerpleegkundigCategory } from '@/lib/types/report';
|
|||||||
// Database types
|
// Database types
|
||||||
export type Patient = Database['public']['Tables']['patients']['Row'];
|
export type Patient = Database['public']['Tables']['patients']['Row'];
|
||||||
|
|
||||||
// Swift-specific types
|
// Cortex-specific types
|
||||||
export type ShiftType = 'nacht' | 'ochtend' | 'middag' | 'avond';
|
export type ShiftType = 'nacht' | 'ochtend' | 'middag' | 'avond';
|
||||||
|
|
||||||
export type SwiftIntent =
|
export type CortexIntent =
|
||||||
| 'dagnotitie'
|
| 'dagnotitie'
|
||||||
| 'zoeken'
|
| 'zoeken'
|
||||||
| 'overdracht'
|
| 'overdracht'
|
||||||
@@ -19,7 +19,7 @@ export type SwiftIntent =
|
|||||||
| 'reschedule_appointment'
|
| 'reschedule_appointment'
|
||||||
| 'unknown';
|
| 'unknown';
|
||||||
|
|
||||||
export type BlockType = Exclude<SwiftIntent, 'unknown'> | 'fallback' | 'patient-dashboard';
|
export type BlockType = Exclude<CortexIntent, 'unknown'> | 'fallback' | 'patient-dashboard';
|
||||||
|
|
||||||
// Chat types (v3.0)
|
// Chat types (v3.0)
|
||||||
export type ChatMessageType = 'user' | 'assistant' | 'system' | 'error';
|
export type ChatMessageType = 'user' | 'assistant' | 'system' | 'error';
|
||||||
@@ -33,7 +33,7 @@ export interface ChatMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatAction {
|
export interface ChatAction {
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
entities: ExtractedEntities;
|
entities: ExtractedEntities;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
artifact?: {
|
artifact?: {
|
||||||
@@ -71,14 +71,14 @@ export interface Artifact {
|
|||||||
// Recent action for the Recent Strip
|
// Recent action for the Recent Strip
|
||||||
export interface RecentAction {
|
export interface RecentAction {
|
||||||
id: string;
|
id: string;
|
||||||
intent: SwiftIntent;
|
intent: CortexIntent;
|
||||||
label: string;
|
label: string;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
patientName?: string;
|
patientName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store interface
|
// Store interface
|
||||||
interface SwiftStore {
|
interface CortexStore {
|
||||||
// Context
|
// Context
|
||||||
activePatient: Patient | null;
|
activePatient: Patient | null;
|
||||||
shift: ShiftType;
|
shift: ShiftType;
|
||||||
@@ -167,7 +167,7 @@ const initialState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create the store
|
// Create the store
|
||||||
export const useSwiftStore = create<SwiftStore>()(
|
export const useCortexStore = create<CortexStore>()(
|
||||||
devtools(
|
devtools(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
...initialState,
|
...initialState,
|
||||||
@@ -372,7 +372,7 @@ export const useSwiftStore = create<SwiftStore>()(
|
|||||||
reset: () => set(initialState, false, 'reset'),
|
reset: () => set(initialState, false, 'reset'),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'swift-store',
|
name: 'cortex-store',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
Reference in New Issue
Block a user