use crate::agents::config::AgentConfig;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct AgentInput<'a> {
pub prompt: &'a str,
pub agent_config: &'a AgentConfig,
pub logfile: Option<&'a Path>,
}
#[derive(Debug, Clone)]
pub struct AgentOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
}
#[derive(Debug, Clone)]
pub enum AgentInvokeError {
ExecutionFailed(String),
ProcessKilled(String),
InvalidInput(String),
AgentError(crate::agents::error::AgentErrorKind),
NoOutput,
TruncatedOutput,
}
impl std::fmt::Display for AgentInvokeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ExecutionFailed(msg) => write!(f, "Agent execution failed: {msg}"),
Self::ProcessKilled(msg) => write!(f, "Agent process killed: {msg}"),
Self::InvalidInput(msg) => write!(f, "Invalid invocation input: {msg}"),
Self::AgentError(kind) => write!(f, "Agent error: {}", kind.description()),
Self::NoOutput => write!(f, "Agent produced no output"),
Self::TruncatedOutput => write!(f, "Agent output was truncated"),
}
}
}
impl std::error::Error for AgentInvokeError {}
pub trait AgentInvoker: Send + Sync {
fn invoke(&self, input: AgentInput<'_>) -> Result<AgentOutput, AgentInvokeError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_invoker_is_object_safe() {
fn assert_object_safe(_: &dyn AgentInvoker) {}
assert_object_safe(&MockAgentInvoker);
}
#[test]
fn test_agent_input_clone() {
let config = AgentConfig::default();
let input = AgentInput {
prompt: "test prompt",
agent_config: &config,
logfile: None,
};
let cloned = input.clone();
assert_eq!(cloned.prompt, "test prompt");
}
#[test]
fn test_agent_output_clone() {
let output = AgentOutput {
stdout: "test output".to_string(),
stderr: "test error".to_string(),
exit_code: 0,
};
let cloned = output.clone();
assert_eq!(cloned.stdout, "test output");
assert_eq!(cloned.stderr, "test error");
assert_eq!(cloned.exit_code, 0);
}
#[test]
fn test_agent_invoke_error_display() {
let err = AgentInvokeError::ExecutionFailed("command not found".to_string());
assert!(err.to_string().contains("Agent execution failed"));
assert!(err.to_string().contains("command not found"));
let err = AgentInvokeError::NoOutput;
assert!(err.to_string().contains("no output"));
}
#[derive(Debug)]
struct MockAgentInvoker;
impl AgentInvoker for MockAgentInvoker {
fn invoke(&self, input: AgentInput<'_>) -> Result<AgentOutput, AgentInvokeError> {
Ok(AgentOutput {
stdout: format!("mock response to: {}", input.prompt),
stderr: String::new(),
exit_code: 0,
})
}
}
#[test]
fn test_mock_agent_invoker() {
let invoker = MockAgentInvoker;
let config = AgentConfig::default();
let input = AgentInput {
prompt: "hello",
agent_config: &config,
logfile: None,
};
let result = invoker.invoke(input).expect("should succeed");
assert!(result.stdout.contains("hello"));
assert_eq!(result.exit_code, 0);
}
}