aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-core/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/asist-core/src/error.rs')
-rw-r--r--crates/asist-core/src/error.rs82
1 files changed, 82 insertions, 0 deletions
diff --git a/crates/asist-core/src/error.rs b/crates/asist-core/src/error.rs
new file mode 100644
index 0000000..b559bee
--- /dev/null
+++ b/crates/asist-core/src/error.rs
@@ -0,0 +1,82 @@
+use std::fmt;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Un fallo de una etapa del pipeline. El orquestador decide si es fatal o si
+/// basta con abortar el turno en curso, así que el error lleva esa distinción
+/// en lugar de dejarla al criterio de quien lo recibe.
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("configuración inválida: {0}")]
+ Config(String),
+
+ #[error("audio: {0}")]
+ Audio(String),
+
+ #[error("ASR: {0}")]
+ Asr(String),
+
+ #[error("LLM: {0}")]
+ Llm(String),
+
+ #[error("TTS: {0}")]
+ Tts(String),
+
+ #[error("HTTP {status} en {url}: {body}")]
+ Http {
+ status: u16,
+ url: String,
+ body: String,
+ },
+
+ #[error("transporte hacia {url}: {source}")]
+ Transport {
+ url: String,
+ #[source]
+ source: std::io::Error,
+ },
+
+ #[error("herramienta «{tool}»: {message}")]
+ Tool { tool: String, message: String },
+
+ #[error("operación cancelada")]
+ Cancelled,
+
+ #[error(transparent)]
+ Json(#[from] serde_json::Error),
+
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+}
+
+impl Error {
+ /// Un error fatal tumba el asistente; el resto sólo aborta el turno.
+ ///
+ /// Los servidores locales se caen y se reinician, y el usuario prefiere
+ /// oír «no te he entendido» a que el proceso muera, así que sólo la
+ /// configuración y el audio (que no se pueden reintentar) son fatales.
+ pub fn is_fatal(&self) -> bool {
+ matches!(self, Error::Config(_) | Error::Audio(_))
+ }
+
+ /// `true` cuando reintentar tiene sentido: el servidor aún está cargando
+ /// el modelo, o se ha caído la conexión a mitad de una petición.
+ pub fn is_retryable(&self) -> bool {
+ match self {
+ Error::Transport { .. } => true,
+ Error::Http { status, .. } => *status == 503 || *status == 429 || *status >= 500,
+ _ => false,
+ }
+ }
+}
+
+/// Contexto legible para los `Result` que cruzan una frontera de crate.
+pub trait Context<T> {
+ fn ctx(self, f: impl FnOnce() -> String) -> Result<T>;
+}
+
+impl<T, E: fmt::Display> Context<T> for std::result::Result<T, E> {
+ fn ctx(self, f: impl FnOnce() -> String) -> Result<T> {
+ self.map_err(|e| Error::Config(format!("{}: {}", f(), e)))
+ }
+}