Watch
0
0
Fork
You've already forked hyperhive
0

swarm-stt: the request subject, the service's NATS permissions and the --stt-client flag

This commit is contained in:
damocles 2026-11-11 21:53:10 +01:00 • committed by mara
commit 47bec6bef3
2 changed files with 171 additions and 1 deletions

View file

@ -28,6 +28,7 @@ pub mod review_classifier;
pub mod router_api;
pub mod router_sink;
pub mod router_todo;
pub mod stt;
pub mod subagent_term;
pub mod wanted;

84
swarm-wire/src/stt.rs Normal file
View file

@ -0,0 +1,84 @@
//! The speech-to-text service's request: the subject an agent sends a voice
//! memo on, and what comes back.
//!
//! Three crates name these: an agent's harness sends the audio, `swarm-stt`
//! answers, and the auth-callout responder grants each its side. The request
//! body is the raw bytes of the audio file, nothing wrapped around them, so
//! the service hashes exactly what the sender held. The answer is a
//! [`Response`] as JSON on the request's reply subject.
use std::time::Duration;
use serde::{Deserialize, Serialize};
/// The subject an agent sends its audio on, `{prefix}.<agent>`. The agent's own
/// name ends it, so an agent's token can publish only to its own subject, and
/// the service learns from the subject who asked.
pub const SUBJECT_PREFIX: &str = "$SWARM.stt.transcribe";
/// The subject agent `agent` sends audio on.
#[must_use]
pub fn request_subject(agent: &str) -> String {
format!("{SUBJECT_PREFIX}.{agent}")
}
/// What the service subscribes to: every agent's request subject.
#[must_use]
pub fn subject_wildcard() -> String {
format!("{SUBJECT_PREFIX}.*")
}
/// How long a sender waits for the answer, and the queue lets the service
/// reply: the longest run, plus the time a request may wait for the engine's
/// one slot.
pub const REPLY_DEADLINE: Duration = Duration::from_mins(10);
/// The service's answer to one request.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum Response {
/// The audio was transcribed, now or earlier.
Ok {
/// The text spoken in the audio (empty for silence).
text: String,
/// `true` when no engine run was started for this request.
cached: bool,
},
/// The service could not transcribe it; the message says why and is safe
/// to show to the sender.
Failed {
/// Why.
message: String,
},
}
#[cfg(test)]
mod tests {
use super::{Response, request_subject, subject_wildcard};
#[test]
fn an_agent_sends_on_its_own_subject_and_the_service_hears_them_all() {
assert_eq!(request_subject("iris"), "$SWARM.stt.transcribe.iris");
assert_eq!(subject_wildcard(), "$SWARM.stt.transcribe.*");
}
#[test]
fn a_response_travels_as_tagged_json() {
let ok = Response::Ok {
text: "hello".into(),
cached: true,
};
let json = serde_json::to_string(&ok).unwrap();
assert_eq!(json, r#"{"status":"ok","text":"hello","cached":true}"#);
assert_eq!(serde_json::from_str::<Response>(&json).unwrap(), ok);
let failed: Response =
serde_json::from_str(r#"{"status":"failed","message":"too large"}"#).unwrap();
assert_eq!(
failed,
Response::Failed {
message: "too large".into()
}
);
}
}