use crate::ast::{Declaration, DeclarationTrivia, Program, VoiceDefinition};
use crate::lexer::Lexer;
use crate::parser::Parser;
const MULAW_OTS_PAIR: &str = r#"
ots InboundMulawToPcm16 {
teleology: "Decode mu-law 8kHz inbound audio for LLM-streaming pipelines"
homotopy_search: shallow
loss_function: "RMSE on reconstructed waveform"
}
ots OutboundPcm16ToMulaw {
teleology: "Encode PCM16 LLM output back to mu-law for the carrier"
homotopy_search: shallow
loss_function: "RMSE on reconstructed waveform"
}
"#;
pub fn expansion_source(v: &VoiceDefinition, include_ots: bool) -> String {
let mut out = String::new();
let name = &v.name;
let carrier = if v.carrier.is_empty() { "mulaw8k" } else { v.carrier.as_str() };
if include_ots && carrier == "mulaw8k" {
out.push_str(MULAW_OTS_PAIR);
}
if v.interruptible {
out.push_str(&format!(
"\nsession {name}CarrierTurn {{\n \
agent: [ receive AudioIn, interrupt {{ send AudioOut, loop }} on CallerSpeech as cause resumable {{ resume }} ]\n \
caller: [ send AudioIn, interrupt {{ receive AudioOut, loop }} on CallerSpeech as cause resumable {{ resume }} ]\n\
}}\n"
));
} else {
out.push_str(&format!(
"\nsession {name}CarrierTurn {{\n \
agent: [ receive AudioIn, send AudioOut, loop ]\n \
caller: [ send AudioIn, receive AudioOut, loop ]\n\
}}\n"
));
}
out.push_str(&format!("\nsocket {name}Call {{\n protocol: {name}CarrierTurn\n backpressure: credit(8)\n"));
if v.interruptible {
out.push_str(" reconnect: cognitive_state\n");
}
if let Some(basis) = &v.legal_basis {
out.push_str(&format!(" legal_basis: {basis}\n"));
}
out.push_str("}\n");
for (leg, suffix) in [(&v.stt, "SttLink"), (&v.tts, "TtsLink"), (&v.realtime, "RealtimeLink")] {
if let Some(r) = leg {
if r.contains('@') {
out.push_str(&format!("\nupstream {name}{suffix} from {r} {{ }}\n"));
}
}
}
out
}
pub fn expand(program: &mut Program) {
let mut ots_present = program.declarations.iter().any(
|d| matches!(d, Declaration::Ots(o) if o.name == "InboundMulawToPcm16"),
);
let mut i = 0;
while i < program.declarations.len() {
let src = match &program.declarations[i] {
Declaration::Voice(v) => {
let s = expansion_source(v, !ots_present);
let carrier_is_mulaw = v.carrier.is_empty() || v.carrier == "mulaw8k";
if carrier_is_mulaw {
ots_present = true;
}
s
}
_ => {
i += 1;
continue;
}
};
let tokens = Lexer::new(&src, "<voice-expansion>")
.tokenize()
.expect("voice expansion source must lex");
let expanded = Parser::new(tokens).parse().expect("voice expansion source must parse");
let mut at = i + 1;
for d in expanded.declarations {
program.declarations.insert(at, d);
program.declaration_trivia.insert(at, DeclarationTrivia::default());
at += 1;
}
i = at;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::type_checker::TypeChecker;
fn parse(src: &str) -> Program {
let tokens = Lexer::new(src, "<t>").tokenize().expect("lex");
Parser::new(tokens).parse().expect("parse")
}
#[test]
fn twenty_line_cascaded_voice_agent_compiles_clean() {
let src = r#"
voice Concierge {
stt: DeepgramSTT@v1
tts: ElevenLabsTTS@v1
interruptible: true
legal_basis: legitimate_interest
}
"#;
assert!(src.lines().filter(|l| !l.trim().is_empty()).count() < 20);
let prog = parse(src);
let errors = TypeChecker::new(&prog).check();
assert!(
errors.is_empty(),
"the 20-line agent must check clean, got: {:?}",
errors.iter().map(|e| &e.message).collect::<Vec<_>>()
);
let has = |f: &dyn Fn(&Declaration) -> bool| prog.declarations.iter().any(f);
assert!(has(&|d| matches!(d, Declaration::Ots(o) if o.name == "InboundMulawToPcm16")));
assert!(has(&|d| matches!(d, Declaration::Session(s) if s.name == "ConciergeCarrierTurn")));
assert!(has(&|d| matches!(d, Declaration::Socket(s) if s.name == "ConciergeCall"
&& s.reconnect && s.legal_basis.as_deref() == Some("legitimate_interest"))));
assert!(has(&|d| matches!(d, Declaration::Upstream(u) if u.name == "ConciergeSttLink"
&& u.preset.as_deref() == Some("DeepgramSTT@v1") && !u.map.is_empty())));
assert!(has(&|d| matches!(d, Declaration::Upstream(u) if u.name == "ConciergeTtsLink")));
assert!(has(&|d| matches!(d, Declaration::Voice(_))));
}
#[test]
fn fused_realtime_voice_expands_one_leg() {
let prog = parse(
r#"
voice Live {
realtime: OpenAIRealtime@v1
carrier: pcm16
}
"#,
);
let errors = TypeChecker::new(&prog).check();
assert!(errors.is_empty(), "got: {:?}", errors.iter().map(|e| &e.message).collect::<Vec<_>>());
assert!(prog.declarations.iter().any(|d| matches!(d, Declaration::Upstream(u) if u.name == "LiveRealtimeLink")));
assert!(!prog.declarations.iter().any(|d| matches!(d, Declaration::Ots(_))));
assert!(prog.declarations.iter().any(|d| matches!(d, Declaration::Socket(s) if s.name == "LiveCall" && !s.reconnect)));
}
#[test]
fn two_mulaw_voices_share_one_codec_pair() {
let prog = parse(
r#"
voice A { stt: DeepgramSTT@v1 tts: CartesiaTTS@v1 }
voice B { stt: AssemblyAISTT@v1 tts: ElevenLabsTTS@v1 }
"#,
);
let inbound = prog
.declarations
.iter()
.filter(|d| matches!(d, Declaration::Ots(o) if o.name == "InboundMulawToPcm16"))
.count();
assert_eq!(inbound, 1, "the codec pair injects once per program");
}
#[test]
fn leg_naming_a_declared_upstream_is_not_redeclared() {
let prog = parse(
r#"
upstream MySTT from DeepgramSTT@v1 { secret: upstream.mystt.api_key }
upstream MyTTS from ElevenLabsTTS@v1 { secret: upstream.mytts.api_key }
voice Concierge {
stt: MySTT
tts: MyTTS
}
"#,
);
let errors = TypeChecker::new(&prog).check();
assert!(errors.is_empty(), "got: {:?}", errors.iter().map(|e| &e.message).collect::<Vec<_>>());
assert!(
!prog.declarations.iter().any(|d| matches!(d, Declaration::Upstream(u) if u.name == "ConciergeSttLink")),
"a declared-upstream leg is referenced, never re-declared"
);
}
}