package com.kazeia.llm // Remplace ExecuTorchLlmEngine. Option C: prefill HTP / decode CPU (géré côté natif). // Mono-tour: generate(sys, usr). Multi-tour: ChatSession (gère l'historique + le template ChatML). class EngineJni { external fun load(ggufPath: String, nCtx: Int): Long external fun generate(h: Long, sys: String, usr: String, maxTok: Int): String external fun generateRaw(h: Long, prompt: String, maxTok: Int): String // prompt complet déjà formaté external fun reset(h: Long) external fun free(h: Long) // -- TTS Talker (Qwen3-TTS) : I/O en embeddings, pas en tokens (vocab=3072 codes audio, pas de BPE). // Le caller (cf. TalkerEngine.kt) construit les embeds de prefill (text+x-vector) et de step // (sum 16 codecs + tts_pad + trailing_text_hidden), fait l'échantillonnage, et compose le // pipeline Talker→CP→Decoder. Retours conventionnels : 0 = OK, négatif = erreur. external fun nEmbd(h: Long): Int // taille d'un embed (1024 pour Talker-0.6B) external fun nVocab(h: Long): Int // 3072 pour le Talker external fun resetEmbeds(h: Long) // KV clear + pos=0, à appeler en début de génération TTS external fun prefillEmbeds(h: Long, embdFlat: FloatArray, t: Int, outHidden: FloatArray): Int external fun decodeEmbed(h: Long, embd: FloatArray, outLogits: FloatArray, outHidden: FloatArray): Int companion object { init { System.loadLibrary("kazeia_engine") } } } class EngineLlmEngine(model: String, ctx: Int = 4096) { private val jni = EngineJni() private val h = jni.load(model, ctx) init { require(h != 0L) { "Kazeia-Engine: échec du chargement du modèle ($model)" } } // Mono-tour : system + un message user. fun generate(sys: String, usr: String, max: Int = 96) = jni.generate(h, sys, usr, max) // Multi-tour : prompt complet pré-formaté (voir ChatSession). fun generateRaw(prompt: String, max: Int = 96) = jni.generateRaw(h, prompt, max) fun newChat(system: String) = ChatSession(this, system) fun reset() = jni.reset(h) fun release() = jni.free(h) } // Conversation multi-tour : accumule l'historique et construit le ChatML Qwen3.5 + thinking-off. // Structure identique à celle validée (jni/dual_ctx_mt.cpp) : mémoire conversationnelle correcte. class ChatSession(private val engine: EngineLlmEngine, private val system: String) { private val turns = StringBuilder() // "<|im_start|>role\ntext<|im_end|>\n" accumulés fun ask(user: String, max: Int = 96): String { val prompt = buildString { append("<|im_start|>system\n").append(system).append("<|im_end|>\n") append(turns) append("<|im_start|>user\n").append(user).append("<|im_end|>\n") append("<|im_start|>assistant\n\n\n\n\n") // thinking OFF déterministe } val resp = engine.generateRaw(prompt, max) turns.append("<|im_start|>user\n").append(user).append("<|im_end|>\n") .append("<|im_start|>assistant\n").append(resp).append("<|im_end|>\n") return resp } fun clear() { turns.setLength(0) } }