aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-core/src/http.rs
diff options
context:
space:
mode:
authorelvis <elvis@claros.ar>2026-09-26 20:20:19 -0300
committerelvis <elvis@claros.ar>2026-09-26 20:20:19 -0300
commit8518a63f55153e7f45fd49ad6caff5555f4e374f (patch)
tree636684eea3fa6f35d78282ab95eb687ef49154af /crates/asist-core/src/http.rs
parent69de76dc9cbedc6092d1e5ce84094a8030de1470 (diff)
downloadasist-p-main.tar.gz
asist-p-main.zip
Translate code, comments, logs and terminal UI to English; add English README; rename scriptsHEADmain
Diffstat (limited to 'crates/asist-core/src/http.rs')
-rw-r--r--crates/asist-core/src/http.rs66
1 files changed, 34 insertions, 32 deletions
diff --git a/crates/asist-core/src/http.rs b/crates/asist-core/src/http.rs
index 32b6680..3273bb8 100644
--- a/crates/asist-core/src/http.rs
+++ b/crates/asist-core/src/http.rs
@@ -1,12 +1,12 @@
-//! Cliente HTTP/1.1 mínimo para hablar con los servidores locales.
+//! Minimal HTTP/1.1 client to talk to the local servers.
//!
-//! Los tres motores (llama-server, tts-server) escuchan en localhost sobre HTTP
-//! plano, así que no hace falta TLS ni un runtime asíncrono. A cambio de las
-//! ~300 líneas de aquí se gana lo único que ninguna librería genérica da
-//! cómodo y que este pipeline necesita de verdad: **abortar una respuesta a
-//! media descarga**. Cuando el usuario interrumpe al asistente hay que dejar de
-//! leer audio ya generado y cerrar el socket en ese mismo instante; con un
-//! cliente que sólo expone `read_to_end` eso no se puede hacer.
+//! The engines (llama-server, tts-server) listen on localhost over plain
+//! HTTP, so neither TLS nor an async runtime is needed. In exchange for the
+//! ~300 lines here we get the one thing no generic library makes easy and
+//! this pipeline really needs: **aborting a response mid-download**. When the
+//! user interrupts the assistant, reading already-generated audio must stop
+//! and the socket must close that very instant; a client that only exposes
+//! `read_to_end` cannot do that.
use std::io::{BufRead, BufReader, Read, Write};
use std::net::TcpStream;
@@ -16,11 +16,11 @@ use std::time::Duration;
use crate::error::{Error, Result};
-/// Bandera compartida que aborta una lectura en curso.
+/// Shared flag that aborts a read in progress.
///
-/// Se comprueba entre bloques, así que el corte ocurre como muy tarde un
-/// `read` después de activarla —del orden de milisegundos con los tamaños de
-/// bloque que usan los servidores.
+/// It is checked between blocks, so the cut happens at most one `read` after
+/// it is set, in the order of milliseconds with the block sizes the servers
+/// use.
#[derive(Clone, Default)]
pub struct Cancel(Arc<AtomicBool>);
@@ -44,11 +44,11 @@ impl Cancel {
#[derive(Clone, Debug)]
pub struct HttpClient {
- /// `host:puerto` del servidor local.
+ /// `host:port` of the local server.
pub authority: String,
pub connect_timeout: Duration,
- /// Tiempo máximo sin recibir un solo byte. No es el tiempo total: una
- /// síntesis larga puede tardar minutos siempre que siga fluyendo.
+ /// Maximum time without receiving a single byte. It is not the total time:
+ /// a long synthesis can take minutes as long as it keeps flowing.
pub read_timeout: Duration,
}
@@ -75,7 +75,7 @@ impl HttpClient {
}
})?;
stream.set_read_timeout(Some(self.read_timeout))?;
- // Los cuerpos son pequeños y la latencia manda: nada de Nagle.
+ // Bodies are small and latency rules: no Nagle.
let _ = stream.set_nodelay(true);
Ok(stream)
}
@@ -106,22 +106,22 @@ impl HttpClient {
Response::read_head(BufReader::new(stream), url)
}
- /// GET que devuelve el cuerpo completo ya decodificado.
+ /// GET returning the whole, already decoded body.
pub fn get(&self, path: &str) -> Result<Vec<u8>> {
self.send("GET", path, None)?.into_body()
}
- /// POST de JSON que devuelve el cuerpo completo.
+ /// JSON POST returning the whole body.
pub fn post_json(&self, path: &str, body: &serde_json::Value) -> Result<Vec<u8>> {
let payload = serde_json::to_vec(body)?;
self.send("POST", path, Some(&payload))?.into_body()
}
- /// POST de JSON cuya respuesta se consume a trozos según llega.
+ /// JSON POST whose response is consumed in chunks as it arrives.
///
- /// `on_chunk` recibe cada bloque en cuanto está disponible y devuelve
- /// `false` para cortar; junto a `cancel` son las dos vías por las que el
- /// barge-in detiene una síntesis a medias.
+ /// `on_chunk` gets each block as soon as it is available and returns
+ /// `false` to stop; together with `cancel` they are the two ways barge-in
+ /// stops a synthesis midway.
pub fn post_json_streaming(
&self,
path: &str,
@@ -135,7 +135,7 @@ impl HttpClient {
response.stream(cancel, &mut on_chunk)
}
- /// POST de JSON que entrega la respuesta línea a línea (SSE de llama-server).
+ /// JSON POST that delivers the response line by line (llama-server SSE).
pub fn post_json_lines(
&self,
path: &str,
@@ -157,7 +157,7 @@ impl HttpClient {
})
}
- /// Sondea `/health` y devuelve `true` si el servidor está listo.
+ /// Polls `/health` and returns `true` if the server is ready.
pub fn healthy(&self, path: &str) -> bool {
self.get(path).is_ok()
}
@@ -172,14 +172,14 @@ fn resolve(authority: &str, path: &str) -> Result<std::net::SocketAddr> {
source: e,
})?
.next()
- .ok_or_else(|| Error::Config(format!("no se pudo resolver «{authority}»")))
+ .ok_or_else(|| Error::Config(format!("could not resolve «{authority}»")))
}
-/// Codificación del cuerpo anunciada por el servidor.
+/// Body encoding announced by the server.
enum Body {
Chunked,
Length(usize),
- /// Sin longitud ni chunked: el cuerpo acaba cuando cierra el socket.
+ /// No length and not chunked: the body ends when the socket closes.
UntilClose,
}
@@ -203,7 +203,9 @@ impl Response {
.split_whitespace()
.nth(1)
.and_then(|s| s.parse::<u16>().ok())
- .ok_or_else(|| Error::Config(format!("respuesta HTTP ilegible de {url}: {line:?}")))?;
+ .ok_or_else(|| {
+ Error::Config(format!("unreadable HTTP response from {url}: {line:?}"))
+ })?;
let mut body = Body::UntilClose;
loop {
@@ -246,7 +248,7 @@ impl Response {
}
let status = self.status;
let url = self.url.clone();
- // Leer el cuerpo del error es lo que convierte un «500» en un mensaje útil.
+ // Reading the error body is what turns a «500» into a useful message.
let body = self.drain().unwrap_or_default();
Err(Error::Http {
status,
@@ -291,11 +293,11 @@ impl Response {
if size_line.is_empty() {
continue;
}
- // Las extensiones de chunk van tras un ';' y aquí no interesan.
+ // Chunk extensions come after a ';' and do not matter here.
let size =
usize::from_str_radix(size_line.split(';').next().unwrap_or("0").trim(), 16)
.map_err(|_| {
- Error::Config(format!("tamaño de chunk ilegible: {size_line:?}"))
+ Error::Config(format!("unreadable chunk size: {size_line:?}"))
})?;
if size == 0 {
return Ok(());
@@ -312,7 +314,7 @@ impl Response {
}
left -= want;
}
- // CRLF de cierre del chunk.
+ // Closing CRLF of the chunk.
let mut crlf = [0u8; 2];
self.reader.read_exact(&mut crlf).map_err(io)?;
},