# Architecture Pipeline Kazeia *Version 2.0 — 28 mars 2026* ## Principe Le pipeline Kazeia est **modulaire** : STT et TTS sont indépendants et échangent uniquement du texte avec une chaîne de processeurs pluggables. ``` ┌─────────┐ ┌──────────────────────────┐ ┌─────────┐ │ STT │────→│ PROCESSOR CHAIN │────→│ TTS │ │(Whisper)│ │ │ │(Android/│ │ │ │ ┌──────────────────┐ │ │Chatterbox│ │ Audio │ │ │ Voice Commands │ │ │ │ │ → Text │ │ └────────┬─────────┘ │ │ Text │ │ │ │ ┌────────▼─────────┐ │ │ → Audio│ │ │ │ │ LLM (Qwen3 NPU) │ │ │ │ │ │ │ └────────┬─────────┘ │ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ │ │ │ │ (Future: RAG) │ │ │ │ │ │ │ └────────┬─────────┘ │ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ │ │ │ │ (Future: Emotion)│ │ │ │ │ │ │ └──────────────────┘ │ │ │ └─────────┘ └──────────────────────────┘ └─────────┘ ``` ## Interfaces ### SttEngine (Speech-to-Text) ```kotlin interface SttEngine { suspend fun load(modelPath: String?) fun isLoaded(): Boolean suspend fun transcribe(audioData: ShortArray, language: String): TranscriptionResult fun release() } ``` Implémentations : | Classe | Backend | Latence | NPU | |--------|---------|---------|-----| | `WhisperSttEngine` | whisper.cpp CPU | ~1500ms | Non | | `WhisperNpuSttEngine` | ExecuTorch QNN | ~50ms* | Oui | | `AndroidSttEngine` | Google SpeechRecognizer | ~500ms | Non (cloud) | ### TtsEngine (Text-to-Speech) ```kotlin interface TtsEngine { suspend fun load(modelPath: String?, voiceId: String?) fun isLoaded(): Boolean suspend fun synthesizeAndPlay(text: String, language: String, onStart: (() -> Unit)?, onComplete: (() -> Unit)?) fun stop() fun release() } ``` Implémentations : | Classe | Backend | Latence | Clonage voix | |--------|---------|---------|-------------| | `AndroidTtsEngine` | Google TTS | ~200ms | Non | | `ChatterboxTtsEngine` | ONNX CPU/NPU | ~3-10s | Oui | ### MessageProcessor (Middleware) ```kotlin interface MessageProcessor { val name: String suspend fun initialize() fun isReady(): Boolean suspend fun process(input: String, context: ConversationContext): ProcessorResult fun release() } ``` Implémentations : | Classe | Rôle | Priorité | |--------|------|----------| | `VoiceCommandProcessor2` | Intercepte les commandes vocales | 1 (premier) | | `LlmProcessor` | Génère des réponses via LLM | 2 | | `EchoProcessor` | Répète l'input (fallback/test) | 3 | | *(Future)* `EmotionProcessor` | Détecte l'émotion de la voix | 1.5 | | *(Future)* `RagProcessor` | Enrichit avec des documents | 1.5 | | *(Future)* `DiarizationProcessor` | Identifie le locuteur | 1 | ### Chaîne de traitement Les processeurs sont exécutés **dans l'ordre**. Le premier qui retourne `shouldContinueChain = false` termine la chaîne. ``` Input: "Bonjour, comment vas-tu ?" → VoiceCommandProcessor2: pas de commande → continue → LlmProcessor: "Je vais bien, comment puis-je t'aider ?" → done Output: "Je vais bien, comment puis-je t'aider ?" Input: "stop" → VoiceCommandProcessor2: commande STOP_LISTENING → done (shouldSpeak=false) Output: (arrête l'écoute) ``` ## Ajouter un nouveau processeur ```kotlin class MonNouveauProcessor : MessageProcessor { override val name = "MonProcesseur" override suspend fun process(input: String, context: ConversationContext): ProcessorResult { // Traiter l'input val enrichedInput = "[$emotion] $input" return ProcessorResult( responseText = "", shouldContinueChain = true, // passe au processeur suivant metadata = mapOf("emotion" to "triste") ) } } // Ajout au pipeline pipeline.addProcessor(MonNouveauProcessor()) ``` ## ConversationContext Le contexte est partagé entre tous les processeurs : ```kotlin data class ConversationContext( val history: List, // historique conversation val metadata: MutableMap, // données partagées val language: String, // "fr" val speakerId: String?, // identification locuteur val emotion: String?, // émotion détectée val sessionId: String // identifiant session ) ``` Les processeurs peuvent lire et écrire dans `metadata` pour communiquer entre eux. ## Performances actuelles | Composant | Backend | Latence | |-----------|---------|---------| | STT Whisper | CPU (whisper.cpp) | 1500ms | | STT Whisper | NPU (ExecuTorch) | ~50ms* | | LLM Qwen3-0.6B | NPU (ExecuTorch) | 93 tok/s, TTFT 31ms | | LLM Qwen3-1.7B | NPU (ExecuTorch) | 46 tok/s, TTFT 27ms | | TTS Android | Google | 200ms | | Pipeline total (CPU STT) | STT→LLM→TTS | ~3-7s | | Pipeline total (NPU STT)* | STT→LLM→TTS | ~1-3s | *STT NPU en cours d'intégration ## Fichiers ``` kazeia-android/app/src/main/java/com/kazeia/ ├── core/ │ ├── LlmEngine.kt # Interface LLM │ ├── SttEngine.kt # Interface STT │ ├── TtsEngine.kt # Interface TTS │ ├── VadEngine.kt # Interface VAD │ ├── ConversationState.kt # États pipeline │ └── Pipeline.kt # Interfaces MessageProcessor, PipelineOrchestrator ├── llm/ │ ├── ExecuTorchLlmEngine.kt # LLM sur NPU via ExecuTorch │ └── GenieLlmEngine.kt # LLM via Genie SDK (abandonné) ├── stt/ │ ├── WhisperSttEngine.kt # STT CPU via whisper.cpp │ ├── WhisperNpuSttEngine.kt # STT NPU via ExecuTorch │ └── AndroidSttEngine.kt # STT cloud via Google ├── tts/ │ ├── AndroidTtsEngine.kt # TTS Google natif │ └── ChatterboxTtsEngine.kt # TTS avec clonage voix ├── conversation/ │ ├── LlmProcessor.kt # Processor LLM │ ├── EchoProcessor.kt # Processor écho │ ├── VoiceCommandProcessor.kt # Commandes vocales (config JSON) │ ├── VoiceCommandProcessor2.kt # Adapter MessageProcessor │ ├── PromptBuilder.kt # Construction prompts │ └── StoppingCriteria.kt # Critères d'arrêt ├── service/ │ ├── KazeiaService.kt # Service Android foreground │ └── KazeiaPipeline.kt # Orchestrateur pipeline modulaire └── ui/ ├── ChatActivity.kt # Interface utilisateur ├── ChatAdapter.kt # Adapter RecyclerView ├── MiniGraphView.kt # Graphe temps réel └── ResourceMonitor.kt # Monitoring CPU/GPU/RAM ``` --- *Projet Kazeia — Damien Micottis & Richard Loyer*