mod conversational;
pub(super) use conversational::AcknowledgementShortcut;
use roboticus_core::InputAuthority;
use super::AppState;
use super::core::InferenceOutput;
use super::decomposition::DelegationProvenance;
use super::intent_registry::Intent;
pub(super) struct ShortcutContext<'a> {
#[allow(dead_code)]
pub state: &'a AppState,
pub user_content: &'a str,
pub turn_id: &'a str,
pub intents: &'a [Intent],
#[allow(dead_code)]
pub agent_name: &'a str,
#[allow(dead_code)]
pub channel_label: &'a str,
pub prepared_model: &'a str,
#[allow(dead_code)]
pub authority: InputAuthority,
#[allow(dead_code)]
pub delegation_provenance: &'a mut DelegationProvenance,
pub is_correction_turn: bool,
pub has_conversation_context: bool,
}
fn shortcut_output(
model: &str,
content: String,
quality: f64,
tool_results: Vec<(String, String)>,
turn_id: &str,
) -> InferenceOutput {
InferenceOutput {
content,
model: model.to_string(),
tokens_in: 0,
tokens_out: 0,
cost: 0.0,
react_turns: 1,
latency_ms: 0,
quality_score: quality,
escalated: false,
tool_results,
react_trace: Box::new(super::flight_recorder::ReactTrace::new(turn_id)),
}
}
#[async_trait::async_trait]
pub(super) trait ShortcutHandler: Send + Sync {
fn handles(&self, intents: &[Intent]) -> bool;
fn requires_cache_bypass(&self) -> bool {
true
}
fn execution_confidence(&self, _ctx: &ShortcutContext) -> (f64, &'static str) {
(0.0, "no evidence")
}
async fn execute(
&self,
ctx: &mut ShortcutContext<'_>,
) -> Result<Option<InferenceOutput>, String>;
}
pub(super) struct ShortcutDispatcher {
handlers: Vec<Box<dyn ShortcutHandler>>,
}
impl ShortcutDispatcher {
pub fn default_dispatcher() -> Self {
Self {
handlers: vec![
Box::new(AcknowledgementShortcut),
],
}
}
pub async fn try_dispatch(
&self,
ctx: &mut ShortcutContext<'_>,
bypass_cache: bool,
) -> Result<Option<InferenceOutput>, String> {
if ctx.is_correction_turn {
return Ok(None);
}
for handler in &self.handlers {
if !handler.handles(ctx.intents) {
continue;
}
if handler.requires_cache_bypass() && !bypass_cache {
continue;
}
let threshold = if ctx.has_conversation_context {
0.8
} else {
0.4
};
{
let (confidence, evidence) = handler.execution_confidence(ctx);
if confidence < threshold {
tracing::debug!(
handler = std::any::type_name_of_val(handler),
confidence,
threshold,
evidence,
"shortcut suppressed: insufficient execution confidence"
);
continue;
}
tracing::info!(
handler = std::any::type_name_of_val(handler),
confidence,
evidence,
threshold,
"shortcut firing (passed confidence gate)"
);
}
if let result @ Some(_) = handler.execute(ctx).await? {
return Ok(result);
}
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn acknowledgement_handles_intent() {
let s = AcknowledgementShortcut;
assert!(s.handles(&[Intent::Acknowledgement]));
assert!(!s.handles(&[Intent::Execution]));
assert!(!s.requires_cache_bypass());
}
#[test]
fn dispatcher_default_has_all_handlers() {
let d = ShortcutDispatcher::default_dispatcher();
assert_eq!(d.handlers.len(), 1);
}
#[test]
fn dispatcher_handler_order_acknowledgement_first() {
let d = ShortcutDispatcher::default_dispatcher();
assert!(d.handlers[0].handles(&[Intent::Acknowledgement]));
assert!(!d.handlers[0].requires_cache_bypass());
}
}