2:I[7012,["4765","static/chunks/4765-f5afdf8061f456f3.js","9856","static/chunks/9856-3b185291364d9bef.js","6687","static/chunks/app/docs/%5B...slug%5D/page-e07536548216bee4.js"],"MarkdownRenderer"] 4:I[9856,["4765","static/chunks/4765-f5afdf8061f456f3.js","9856","static/chunks/9856-3b185291364d9bef.js","6687","static/chunks/app/docs/%5B...slug%5D/page-e07536548216bee4.js"],""] 5:I[4126,[],""] 7:I[9630,[],""] 8:I[4278,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"HeadingProvider"] 9:I[1476,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"Header"] a:I[3167,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"Sidebar"] b:I[7409,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"PageFrame"] 3:T3e55, # VoiceAssist V2 - Architecture Diagrams **Last Updated**: 2025-11-20 (Phase 7) **Purpose**: Visual architecture diagrams for system understanding --- ## Table of Contents 1. [System Context Diagram](#system-context-diagram) 2. [Container Diagram](#container-diagram) 3. [Component Diagram](#component-diagram) 4. [Data Flow Diagrams](#data-flow-diagrams) 5. [Deployment Diagram](#deployment-diagram) 6. [Sequence Diagrams](#sequence-diagrams) --- ## System Context Diagram High-level view of VoiceAssist and its external dependencies. ```mermaid graph TB User[("👤 Clinician/User
(Browser/Mobile)")] Admin[("👨‍💼 Admin
(Browser)")] VA["🏥 VoiceAssist V2
(Medical AI Assistant)
Port: 8000"] NC["☁️ Nextcloud
(Separate Stack)
Port: 8080"] OpenAI["🤖 OpenAI API
(Embeddings + LLM)"] User -->|"HTTP/WebSocket
Query, Chat"| VA Admin -->|"HTTP
Manage KB, View Dashboard"| VA VA -->|"CalDAV/WebDAV
Calendar, Files"| NC VA -->|"OIDC (Future)
Authentication"| NC VA -->|"Embeddings
LLM Generation"| OpenAI NC -.->|"Stores Files"| User NC -.->|"Manages Calendar"| User style VA fill:#e1f5ff style NC fill:#fff4e1 style OpenAI fill:#f0e1ff ``` --- ## Container Diagram Internal containers within VoiceAssist system. ```mermaid graph TB subgraph "VoiceAssist System" subgraph "Application Layer" Gateway["🌐 API Gateway
(FastAPI)
Port: 8000"] Worker["⚙️ Background Worker
(ARQ)
Document Indexing"] end subgraph "Data Layer" PG[("🗄️ PostgreSQL
pgvector
Port: 5432")] Redis[("🔴 Redis
6 Databases
Port: 6379")] Qdrant[("🔍 Qdrant
Vector DB
Port: 6333")] end subgraph "Observability" Prom["📊 Prometheus
Port: 9090"] Graf["📈 Grafana
Port: 3000"] end end User[("👤 User")] -->|"HTTP/WS"| Gateway Gateway -->|"SQL"| PG Gateway -->|"Cache/Queue"| Redis Gateway -->|"Vector Search"| Qdrant Worker -->|"Read Jobs"| Redis Worker -->|"Index Docs"| Qdrant Gateway -->|"Metrics"| Prom Worker -->|"Metrics"| Prom Prom -->|"Data Source"| Graf style Gateway fill:#e1f5ff style Worker fill:#e1ffe1 style PG fill:#ffe1e1 style Redis fill:#fff4e1 style Qdrant fill:#f0e1ff ``` --- ## Component Diagram Internal components of the API Gateway application. ```mermaid graph TB subgraph "API Gateway (FastAPI Application)" subgraph "API Layer (app/api/)" AuthAPI["🔐 Auth API
(/api/auth/*)"] RealtimeAPI["💬 Realtime API
(/api/realtime/ws)"] AdminKB["📚 Admin KB API
(/api/admin/kb/*)"] AdminPanel["🎛️ Admin Panel API
(/api/admin/panel/*)"] IntegAPI["🔗 Integration API
(/api/integrations/*)"] MetricsAPI["📊 Metrics API
(/metrics)"] end subgraph "Service Layer (app/services/)" RAG["🧠 RAG Service
(QueryOrchestrator)"] LLM["🤖 LLM Client
(OpenAI Interface)"] KB["📖 KB Indexer
(Document Ingestion)"] Search["🔍 Search Aggregator
(Semantic Search)"] Cache["💾 Cache Service
(L1 + L2)"] Audit["📝 Audit Service
(Compliance Logging)"] CalDAV["📅 CalDAV Service
(Calendar Ops)"] FileIdx["📁 File Indexer
(Nextcloud Files)"] TokenRev["🚫 Token Revocation
(Redis Blacklist)"] end subgraph "Core Layer (app/core/)" Security["🔒 Security
(JWT, bcrypt)"] Database["🗄️ Database
(SQLAlchemy Sessions)"] Config["⚙️ Config
(Pydantic Settings)"] Envelope["📦 API Envelope
(Response Format)"] Metrics["📈 Metrics
(Prometheus Defs)"] end subgraph "Models Layer (app/models/)" UserModel["User Model"] AuditModel["Audit Log Model"] SessionModel["Session Model"] end end %% API to Service connections AuthAPI --> Security AuthAPI --> TokenRev AuthAPI --> Audit RealtimeAPI --> RAG AdminKB --> KB AdminKB --> Audit IntegAPI --> CalDAV IntegAPI --> FileIdx MetricsAPI --> Metrics %% Service to Service connections RAG --> Search RAG --> LLM RAG --> Cache Search --> Cache KB --> LLM FileIdx --> KB %% Service to Core connections RAG --> Database KB --> Database Audit --> Database TokenRev --> Cache %% Core to Models connections Database --> UserModel Database --> AuditModel Database --> SessionModel style AuthAPI fill:#ffe1e1 style RealtimeAPI fill:#e1f5ff style AdminKB fill:#fff4e1 style RAG fill:#e1ffe1 style Cache fill:#f0e1ff ``` --- ## Data Flow Diagrams ### RAG Query Flow ```mermaid sequenceDiagram participant User participant WebSocket as Realtime API
(WebSocket) participant Orch as Query
Orchestrator participant Cache as Cache
Service participant Search as Search
Aggregator participant Qdrant as Qdrant
(Vectors) participant LLM as LLM Client
(OpenAI) User->>WebSocket: Send Query
"What is diabetes?" WebSocket->>Orch: handle_query() Orch->>Search: generate_query_embedding() Search->>Cache: Check L1 (LRU) Cache-->>Search: Cache Miss Search->>Cache: Check L2 (Redis) Cache-->>Search: Cache Miss Search->>LLM: Create Embedding (OpenAI) LLM-->>Search: Embedding [1536 dims] Search->>Cache: Store in L2 + L1 Search->>Qdrant: Vector Search
(top_k=5, threshold=0.7) Qdrant-->>Search: Search Results
(5 chunks + metadata) Search->>Search: Format Context Search-->>Orch: Context + Citations Orch->>LLM: Generate Response
(with context) LLM-->>Orch: Response Text Orch->>Search: Extract Citations Search-->>Orch: Citation List Orch-->>WebSocket: QueryResponse
(answer + citations) loop Streaming WebSocket->>User: message_chunk end WebSocket->>User: message_complete
(full answer + citations) ``` ### Document Indexing Flow ```mermaid sequenceDiagram participant Admin participant API as Admin KB API participant Indexer as KB Indexer participant Extract as Text
Extractor participant LLM as LLM Client
(OpenAI) participant Qdrant as Qdrant
(Vectors) participant Audit as Audit
Service Admin->>API: POST /api/admin/kb/documents
(Upload PDF) API->>API: Check RBAC
(get_current_admin_user) API->>Indexer: index_pdf_document() Indexer->>Extract: Extract Text
(PyPDF2/pdfplumber) Extract-->>Indexer: Raw Text Indexer->>Indexer: Chunk Text
(500 chars, 50 overlap) loop For Each Chunk Indexer->>LLM: Create Embedding
(OpenAI API) LLM-->>Indexer: Embedding [1536 dims] Indexer->>Qdrant: Store Vector
(embedding + metadata) Qdrant-->>Indexer: Success end Indexer-->>API: IndexingResult
(document_id, chunks_indexed) API->>Audit: Log Upload Event Audit-->>API: Logged API-->>Admin: Success Response
(document_id, chunks: 42) ``` ### Authentication Flow ```mermaid sequenceDiagram participant User participant API as Auth API participant Validator as Password
Validator participant Security as Security
Service participant DB as PostgreSQL participant Audit as Audit
Service participant Redis as Redis
(Token Blacklist) rect rgb(240, 240, 255) Note over User,Redis: Registration User->>API: POST /api/auth/register
{email, password} API->>Validator: Validate Password
(strength, common passwords) Validator-->>API: Valid (score: 85) API->>Security: Hash Password (bcrypt) Security-->>API: Hashed Password API->>DB: INSERT INTO users DB-->>API: User Created API->>Audit: Log Registration Event Audit-->>API: Logged API-->>User: {user_id, email} end rect rgb(255, 240, 240) Note over User,Redis: Login User->>API: POST /api/auth/login
{email, password} API->>DB: SELECT user
WHERE email = ? DB-->>API: User Record API->>Security: Verify Password
(bcrypt compare) Security-->>API: Valid API->>Security: Generate JWT Tokens
(access 15min, refresh 7d) Security-->>API: {access_token, refresh_token} API->>DB: INSERT INTO sessions DB-->>API: Session Created API->>Audit: Log Login Event Audit-->>API: Logged API-->>User: {access_token, refresh_token} end rect rgb(240, 255, 240) Note over User,Redis: Authenticated Request User->>API: GET /api/auth/me
Authorization: Bearer API->>Security: Decode JWT Security-->>API: {user_id, email, role} API->>Redis: Check Token Revoked?
(token:revoked:{jti}) Redis-->>API: Not Revoked API->>DB: SELECT user
WHERE id = ? DB-->>API: User Data API-->>User: {user} end rect rgb(255, 255, 240) Note over User,Redis: Logout User->>API: POST /api/auth/logout API->>Redis: Add Token to Blacklist
(TTL = token expiry) Redis-->>API: Added API->>Audit: Log Logout Event Audit-->>API: Logged API-->>User: Success end ``` --- ## Deployment Diagram ### Current Deployment (Docker Compose - Phases 0-7) ```mermaid graph TB subgraph "Host Machine (MacBook Pro / Ubuntu Server)" subgraph "Docker Network: voiceassist_network" Server["📦 voiceassist-server
(FastAPI)
Port: 8000
2 CPU, 4GB RAM"] Worker["📦 voiceassist-worker
(ARQ)
1 CPU, 2GB RAM"] PG["📦 postgres
(pgvector/pgvector:pg16)
Port: 5432
2 CPU, 4GB RAM"] Redis["📦 redis
(redis:7-alpine)
Port: 6379
1 CPU, 1GB RAM"] Qdrant["📦 qdrant
(qdrant/qdrant:latest)
Port: 6333
2 CPU, 4GB RAM"] Prom["📦 prometheus
(prom/prometheus)
Port: 9090"] Graf["📦 grafana
(grafana/grafana)
Port: 3000"] end subgraph "Volumes" PGData["💾 postgres_data"] RedisData["💾 redis_data"] QdrantData["💾 qdrant_data"] end end subgraph "External" NC["☁️ Nextcloud
(Separate Stack)
localhost:8080 or
cloud.asimo.io"] OpenAI["🤖 OpenAI API
(api.openai.com)"] end Browser["🌐 Browser"] -->|"HTTP :8000"| Server Browser -->|"WS :8000"| Server Browser -->|"HTTP :3000"| Graf Server --> PG Server --> Redis Server --> Qdrant Server --> NC Server --> OpenAI Worker --> Redis Worker --> Qdrant Worker --> OpenAI Server --> Prom Worker --> Prom Prom --> Graf PG -.->|"Persists"| PGData Redis -.->|"Persists"| RedisData Qdrant -.->|"Persists"| QdrantData style Server fill:#e1f5ff style Worker fill:#e1ffe1 style PG fill:#ffe1e1 style Redis fill:#fff4e1 style Qdrant fill:#f0e1ff ``` ### Future Deployment (Kubernetes - Phases 11-14) ```mermaid graph TB subgraph "Kubernetes Cluster" subgraph "Ingress Layer" Ingress["⚡ Ingress Controller
(Nginx/Kong)
SSL Termination"] end subgraph "Service Mesh (Linkerd)" Gateway["🌐 API Gateway
(2-5 replicas)"] Auth["🔐 Auth Service
(2-3 replicas)"] Realtime["💬 Realtime Service
(3-10 replicas)"] RAGSvc["🧠 RAG Service
(3-10 replicas)"] Admin["🎛️ Admin Service
(2 replicas)"] end subgraph "Data Layer (StatefulSets)" PGPrimary["🗄️ PostgreSQL
Primary"] PGReplica1["🗄️ PostgreSQL
Replica 1"] PGReplica2["🗄️ PostgreSQL
Replica 2"] RedisMaster["🔴 Redis
Master"] RedisSlave["🔴 Redis
Slave"] QdrantCluster["🔍 Qdrant
Cluster (3 nodes)"] end subgraph "Observability" PromHA["📊 Prometheus
(HA Pair)"] GrafCluster["📈 Grafana"] Jaeger["🔍 Jaeger"] Loki["📝 Loki"] end end Internet["🌐 Internet"] -->|"HTTPS"| Ingress Ingress --> Gateway Gateway --> Auth Gateway --> Realtime Gateway --> RAGSvc Gateway --> Admin Auth --> PGPrimary Realtime --> RAGSvc RAGSvc --> QdrantCluster RAGSvc --> RedisMaster PGPrimary -.->|"Replication"| PGReplica1 PGPrimary -.->|"Replication"| PGReplica2 RedisMaster -.->|"Replication"| RedisSlave Gateway --> PromHA Auth --> PromHA Realtime --> PromHA RAGSvc --> PromHA PromHA --> GrafCluster PromHA --> Loki Gateway --> Jaeger Auth --> Jaeger Realtime --> Jaeger RAGSvc --> Jaeger style Ingress fill:#ffe1e1 style Gateway fill:#e1f5ff style RAGSvc fill:#e1ffe1 style PGPrimary fill:#ffe1e1 style QdrantCluster fill:#f0e1ff ``` --- ## Sequence Diagrams ### Calendar Event Creation ```mermaid sequenceDiagram participant User participant API as Integration API participant CalDAV as CalDAV
Service participant NC as Nextcloud
CalDAV Server participant Audit as Audit
Service User->>API: POST /api/integrations/calendar/events
{summary, start, end} API->>API: Authenticate User
(get_current_user) API->>CalDAV: create_event() CalDAV->>CalDAV: Build iCalendar
(vobject VEVENT) CalDAV->>NC: CalDAV PUT
/remote.php/dav/calendars/{user}/default/{uid}.ics NC-->>CalDAV: 201 Created CalDAV-->>API: Event UID API->>Audit: Log Event Creation Audit-->>API: Logged API-->>User: Success
{event_uid} ``` ### File Auto-Indexing from Nextcloud ```mermaid sequenceDiagram participant Admin participant API as Integration API participant FileIdx as File
Indexer participant NC as Nextcloud
WebDAV participant KBIdx as KB
Indexer participant Qdrant as Qdrant Admin->>API: POST /api/integrations/files/scan-and-index
{source_type: "guideline"} API->>API: Check RBAC
(get_current_admin_user) API->>FileIdx: scan_and_index_directory() FileIdx->>NC: WebDAV PROPFIND
/remote.php/dav/files/{user}/Documents NC-->>FileIdx: File List
[(file1.pdf, file2.txt, ...)] loop For Each File FileIdx->>FileIdx: Check Already Indexed?
(indexed_files set) alt Not Indexed FileIdx->>NC: WebDAV GET
Download file NC-->>FileIdx: File Bytes FileIdx->>KBIdx: index_document() KBIdx->>Qdrant: Store Vectors Qdrant-->>KBIdx: Success KBIdx-->>FileIdx: IndexingResult FileIdx->>FileIdx: Add to indexed_files else Already Indexed FileIdx->>FileIdx: Skip end end FileIdx-->>API: ScanResult
{discovered: 10, indexed: 7, skipped: 3} API-->>Admin: Success Response ``` --- ## Related Documentation - [UNIFIED_ARCHITECTURE.md](../UNIFIED_ARCHITECTURE.md) - Comprehensive architecture doc - [SERVICE_CATALOG.md](../SERVICE_CATALOG.md) - Service descriptions - [DATA_MODEL.md](../DATA_MODEL.md) - Data entities --- **Last Updated**: 2025-11-20 **Diagram Format**: Mermaid (render with Mermaid.js or GitHub/GitLab) **Review Cycle**: Updated with major architecture changes 6:["slug","architecture/ARCHITECTURE_DIAGRAMS","c"] 0:["X7oMT3VrOffzp0qvbeOas",[[["",{"children":["docs",{"children":[["slug","architecture/ARCHITECTURE_DIAGRAMS","c"],{"children":["__PAGE__?{\"slug\":[\"architecture\",\"ARCHITECTURE_DIAGRAMS\"]}",{}]}]}]},"$undefined","$undefined",true],["",{"children":["docs",{"children":[["slug","architecture/ARCHITECTURE_DIAGRAMS","c"],{"children":["__PAGE__",{},[["$L1",["$","div",null,{"children":[["$","div",null,{"className":"mb-6 flex items-center justify-between gap-4","children":[["$","div",null,{"children":[["$","p",null,{"className":"text-sm text-gray-500 dark:text-gray-400","children":"Docs / Raw"}],["$","h1",null,{"className":"text-3xl font-bold text-gray-900 dark:text-white","children":"Architecture Diagrams"}],["$","p",null,{"className":"text-sm text-gray-600 dark:text-gray-400","children":["Sourced from"," ",["$","code",null,{"className":"font-mono text-xs","children":["docs/","architecture/ARCHITECTURE_DIAGRAMS.md"]}]]}]]}],["$","a",null,{"href":"https://github.com/mohammednazmy/VoiceAssist/edit/main/docs/architecture/ARCHITECTURE_DIAGRAMS.md","target":"_blank","rel":"noreferrer","className":"inline-flex items-center gap-2 rounded-md border border-gray-200 dark:border-gray-700 px-3 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:border-primary-500 dark:hover:border-primary-400 hover:text-primary-700 dark:hover:text-primary-300","children":"Edit on GitHub"}]]}],["$","div",null,{"className":"rounded-lg border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6","children":["$","$L2",null,{"content":"$3"}]}],["$","div",null,{"className":"mt-6 flex flex-wrap gap-2 text-sm","children":[["$","$L4",null,{"href":"/reference/all-docs","className":"inline-flex items-center gap-1 rounded-md bg-gray-100 px-3 py-1 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700","children":"← All documentation"}],["$","$L4",null,{"href":"/","className":"inline-flex items-center gap-1 rounded-md bg-gray-100 px-3 py-1 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700","children":"Home"}]]}]]}],null],null],null]},[null,["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children","docs","children","$6","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[null,["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children","docs","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/7f586cdbbaa33ff7.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"__className_f367f3 h-full bg-white dark:bg-gray-900","children":[["$","a",null,{"href":"#main-content","className":"skip-to-content","children":"Skip to main content"}],["$","$L8",null,{"children":[["$","$L9",null,{}],["$","$La",null,{}],["$","main",null,{"id":"main-content","className":"lg:pl-64","role":"main","aria-label":"Documentation content","children":["$","$Lb",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]]}]]}]}]],null],null],["$Lc",null]]]] c:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Architecture Diagrams | Docs | VoiceAssist Docs"}],["$","meta","3",{"name":"description","content":"**Last Updated**: 2025-11-20 (Phase 7)"}],["$","meta","4",{"name":"keywords","content":"VoiceAssist,documentation,medical AI,voice assistant,healthcare,HIPAA,API"}],["$","meta","5",{"name":"robots","content":"index, follow"}],["$","meta","6",{"name":"googlebot","content":"index, follow"}],["$","link","7",{"rel":"canonical","href":"https://assistdocs.asimo.io"}],["$","meta","8",{"property":"og:title","content":"VoiceAssist Documentation"}],["$","meta","9",{"property":"og:description","content":"Comprehensive documentation for VoiceAssist - Enterprise Medical AI Assistant"}],["$","meta","10",{"property":"og:url","content":"https://assistdocs.asimo.io"}],["$","meta","11",{"property":"og:site_name","content":"VoiceAssist Docs"}],["$","meta","12",{"property":"og:type","content":"website"}],["$","meta","13",{"name":"twitter:card","content":"summary"}],["$","meta","14",{"name":"twitter:title","content":"VoiceAssist Documentation"}],["$","meta","15",{"name":"twitter:description","content":"Comprehensive documentation for VoiceAssist - Enterprise Medical AI Assistant"}],["$","meta","16",{"name":"next-size-adjust"}]] 1:null