diff options
| author | elvis <elvis@claros.ar> | 2026-09-06 19:21:16 -0300 |
|---|---|---|
| committer | elvis <elvis@claros.ar> | 2026-09-06 19:23:37 -0300 |
| commit | f8f98e83481e7376a235fb305a095552433f79ab (patch) | |
| tree | 6d0ecddb26bef8430a086431de40f8ce9b1039e6 /Cargo.toml | |
| download | asist-p-f8f98e83481e7376a235fb305a095552433f79ab.tar.gz asist-p-f8f98e83481e7376a235fb305a095552433f79ab.zip | |
Local voice assistant on top of Canary, llama.cpp and qwentts
Pipeline en Rust de hilos y canales que une los tres motores: el micrófono
alimenta un segmentador con VAD, las intervenciones cerradas van al
reconocedor, la transcripción al modelo y cada frase que este cierra sale
hacia el sintetizador sin esperar al resto de la respuesta.
Dos reglas sostienen el diseño: ninguna etapa bloquea a la anterior —quien va
sobrado descarta trabajo en lugar de acumular retraso— y todo lo que viaja por
los canales lleva el turno al que pertenece, así que interrumpir es subir el
contador y levantar dos banderas de cancelación.
Seis crates: core (configuración, eventos, HTTP, telemetría, herramientas),
audio (cpal, VAD, anillo de reproducción), asr, llm, tts y app (supervisor de
procesos y orquestador). Los motores van como submódulos fijados a un commit,
con los cambios locales en vendor/patches.
Midiendo el pipeline aparecieron tres cuellos de botella de configuración que
valieron más que cualquier cambio de código, todos documentados en
docs/RENDIMIENTO.md:
- tts-server decodificaba el audio en bloques de 24 s, de modo que el modo
«streaming» llegaba de una pieza: 4948 ms -> 585 ms hasta el primer audio.
- La plantilla de chat del modelo abre <think> y no lo cierra nunca, sin
variable que lo apague: 8630 ms -> 413 ms hasta el primer token, con una
copia de la plantilla que deja el bloque cerrado de entrada.
- Cualquier indicación de estilo junto a la guía de herramientas hace que este
modelo de 2B deje de llamarlas y se invente el dato (8/8 aciertos con la
guía sola, 0/8 con la persona de asistente de voz). El turno alterna ahora
entre dos instrucciones de sistema.
La ejecución de órdenes del sistema queda implementada y apagada, tras cuatro
barreras: lista blanca sobre el ejecutable, rutas rechazadas, sin shell que
interprete metacaracteres y plazo máximo.
68 pruebas unitarias sin modelos, más seis de integración que se saltan solas
si no hay servidores y se turnan la GPU: en paralelo, los dos servidores no
caben en 4 GB y miden contención en vez de latencia.
Claude-Session: https://claude.ai/code/session_01FNxz5cSdQSscJH9H7b8uGU
Diffstat (limited to 'Cargo.toml')
| -rw-r--r-- | Cargo.toml | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1ef3f34 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,58 @@ +[workspace] +resolver = "2" +members = [ + "crates/asist-core", + "crates/asist-audio", + "crates/asist-asr", + "crates/asist-llm", + "crates/asist-tts", + "crates/asist-app", +] +# Los submódulos son dependencias por ruta, no miembros del workspace: sin esto +# cargo los adopta por estar dentro del directorio, y `cargo test` acabaría +# ejecutando las pruebas de integración de canary-rs, que esperan un modelo en +# su propia ruta por defecto y fallan aquí. +exclude = ["vendor"] + +[workspace.package] +version = "0.1.0" +edition = "2021" +rust-version = "1.85" +license = "MIT" +repository = "https://github.com/elvis/asist-p" + +[workspace.dependencies] +asist-core = { path = "crates/asist-core" } +asist-audio = { path = "crates/asist-audio" } +asist-asr = { path = "crates/asist-asr" } +asist-llm = { path = "crates/asist-llm" } +asist-tts = { path = "crates/asist-tts" } + +canary-rs = { path = "vendor/canary-rs", default-features = false, features = ["cpu", "ort-defaults"] } +# canary-rs pide "2.0.0-rc.12", que en semver de prelanzamientos también admite +# rc.13 — y ahí el módulo CoreML pasó a estar tras una feature, así que sus +# reexportaciones incondicionales dejan de compilar. Se fija la versión contra +# la que canary-rs se construye de verdad. +ort = "=2.0.0-rc.12" + +anyhow = "1" +base64 = "0.22" +cpal = "0.17" +crossbeam-channel = "0.5" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" +toml = "0.8" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } + +[profile.release] +opt-level = 3 +lto = "thin" +codegen-units = 1 +debug = 1 # keep frame pointers/line info: perf profiling of the audio path needs them + +# The pipeline is unusable in a debug build (Canary decodes ~10x slower), so the +# heavy dependencies are optimised even when the workspace itself is not. +[profile.dev.package."*"] +opt-level = 3 |