use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateSource {
pub repo_url: String,
pub author: String,
pub path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntryPoint {
pub file: String,
pub module: String,
pub tag: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentArchitecture {
pub entrypoints: Vec<EntryPoint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunAgentConfig {
pub agent_name: String,
pub description: String,
pub framework: String,
pub template: String,
pub version: String,
pub created_at: DateTime<Utc>,
pub template_source: Option<TemplateSource>,
pub agent_architecture: AgentArchitecture,
#[serde(default)]
pub env_vars: HashMap<String, String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AgentInputArgs {
#[serde(default)]
pub input_args: Vec<serde_json::Value>,
#[serde(default)]
pub input_kwargs: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WebSocketActionType {
StartStream,
StopStream,
Ping,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebSocketAgentRequest {
pub action: WebSocketActionType,
pub agent_id: String,
pub entrypoint_tag: String,
pub input_data: AgentInputArgs,
#[serde(default)]
pub stream_config: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentRunRequest {
#[serde(default)]
pub input_data: AgentInputArgs,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentRunResponse {
pub success: bool,
pub output_data: Option<serde_json::Value>,
pub error: Option<String>,
pub execution_time: Option<f64>,
pub agent_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapacityInfo {
pub current_count: usize,
pub max_capacity: usize,
pub remaining_slots: usize,
pub is_full: bool,
pub agents: Vec<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentInfo {
pub message: String,
pub version: String,
pub host: String,
pub port: u16,
pub config: HashMap<String, serde_json::Value>,
pub endpoints: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MessageType {
ToolCall,
ToolResult,
AgentThought,
FinalResponse,
Error,
ExecutionError,
Status,
RawData,
Data,
StructuredData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafeMessage {
pub id: String,
#[serde(rename = "type")]
pub message_type: MessageType,
pub timestamp: String,
pub data: serde_json::Value,
pub metadata: Option<HashMap<String, serde_json::Value>>,
pub error: Option<String>,
}
impl SafeMessage {
pub fn new(id: String, message_type: MessageType, data: serde_json::Value) -> Self {
Self {
id,
message_type,
timestamp: Utc::now().to_rfc3339(),
data,
metadata: None,
error: None,
}
}
pub fn with_error(id: String, error: String) -> Self {
Self {
id,
message_type: MessageType::Error,
timestamp: Utc::now().to_rfc3339(),
data: serde_json::json!({"error": error}),
metadata: None,
error: Some(error),
}
}
pub fn to_dict(&self) -> HashMap<String, serde_json::Value> {
let mut dict = HashMap::new();
dict.insert("id".to_string(), serde_json::json!(self.id));
dict.insert("type".to_string(), serde_json::json!(self.message_type));
dict.insert("timestamp".to_string(), serde_json::json!(self.timestamp));
dict.insert("data".to_string(), self.data.clone());
if let Some(metadata) = &self.metadata {
dict.insert("metadata".to_string(), serde_json::json!(metadata));
}
if let Some(error) = &self.error {
dict.insert("error".to_string(), serde_json::json!(error));
}
dict
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentDeployment {
pub agent_id: String,
pub agent_path: String,
pub host: String,
pub port: u16,
pub framework: String,
pub status: String,
pub deployed_at: DateTime<Utc>,
pub last_run: Option<DateTime<Utc>>,
pub run_count: i64,
pub success_count: i64,
pub error_count: i64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentRun {
pub id: i64,
pub agent_id: String,
pub input_data: serde_json::Value,
pub output_data: Option<serde_json::Value>,
pub success: bool,
pub error_message: Option<String>,
pub execution_time: Option<f64>,
pub started_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
pub debug: bool,
pub cors_enabled: bool,
pub max_request_size: usize,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "127.0.0.1".to_string(),
port: 8450,
debug: false,
cors_enabled: true,
max_request_size: 16 * 1024 * 1024, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
pub connect_timeout: u64,
pub idle_timeout: u64,
}
impl Default for DatabaseConfig {
fn default() -> Self {
let db_path = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".runagent")
.join("runagent_local.db");
Self {
url: format!("sqlite://{}", db_path.to_string_lossy()),
max_connections: 10,
min_connections: 1,
connect_timeout: 30,
idle_timeout: 600,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientConfig {
pub api_key: Option<String>,
pub base_url: String,
pub timeout: u64,
pub max_retries: usize,
pub retry_delay: u64,
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
api_key: None,
base_url: crate::constants::DEFAULT_BASE_URL.to_string(),
timeout: 30,
max_retries: 3,
retry_delay: 1000,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_input_args_default() {
let args = AgentInputArgs::default();
assert!(args.input_args.is_empty());
assert!(args.input_kwargs.is_empty());
}
#[test]
fn test_safe_message_creation() {
let msg = SafeMessage::new(
"test-id".to_string(),
MessageType::Status,
serde_json::json!({"status": "ok"}),
);
assert_eq!(msg.id, "test-id");
assert!(matches!(msg.message_type, MessageType::Status));
}
#[test]
fn test_safe_message_with_error() {
let msg =
SafeMessage::with_error("error-id".to_string(), "Something went wrong".to_string());
assert_eq!(msg.id, "error-id");
assert!(matches!(msg.message_type, MessageType::Error));
assert!(msg.error.is_some());
}
#[test]
fn test_server_config_default() {
let config = ServerConfig::default();
assert_eq!(config.host, "127.0.0.1");
assert_eq!(config.port, 8450);
assert!(!config.debug);
assert!(config.cors_enabled);
}
#[test]
fn test_serialization() {
let entry_point = EntryPoint {
file: "main.py".to_string(),
module: "run".to_string(),
tag: "generic".to_string(),
};
let json = serde_json::to_string(&entry_point).unwrap();
let deserialized: EntryPoint = serde_json::from_str(&json).unwrap();
assert_eq!(entry_point.file, deserialized.file);
assert_eq!(entry_point.module, deserialized.module);
assert_eq!(entry_point.tag, deserialized.tag);
}
}