use crate::shared::models::UserSession; use crate::shared::state::AppState; use chrono::{DateTime, Utc}; use log::{info, trace, warn}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CompiledIntent { pub id: String, pub original_intent: String, pub entities: IntentEntities, pub plan: ExecutionPlan, pub basic_program: String, pub confidence: f64, pub alternatives: Vec, pub risk_assessment: RiskAssessment, pub resource_estimate: ResourceEstimate, pub compiled_at: DateTime, pub session_id: String, pub bot_id: String, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct IntentEntities { pub action: String, pub target: String, pub domain: Option, pub client: Option, pub features: Vec, pub constraints: Vec, pub technologies: Vec, pub data_sources: Vec, pub integrations: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Constraint { pub constraint_type: ConstraintType, pub value: String, pub is_hard: bool, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum ConstraintType { Budget, Timeline, Technology, Security, Compliance, Performance, Scalability, Custom(String), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ExecutionPlan { pub id: String, pub name: String, pub description: String, pub steps: Vec, pub dependencies: HashMap>, pub estimated_duration_minutes: i32, pub requires_approval: bool, pub approval_levels: Vec, pub rollback_plan: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PlanStep { pub id: String, pub order: i32, pub name: String, pub description: String, pub keywords: Vec, pub basic_code: String, pub priority: StepPriority, pub risk_level: RiskLevel, pub estimated_minutes: i32, pub requires_approval: bool, pub can_rollback: bool, pub dependencies: Vec, pub outputs: Vec, pub mcp_servers: Vec, pub api_calls: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum StepPriority { Critical, High, Medium, Low, Optional, } impl Default for StepPriority { fn default() -> Self { StepPriority::Medium } } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)] pub enum RiskLevel { None, Low, Medium, High, Critical, } impl Default for RiskLevel { fn default() -> Self { RiskLevel::Low } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApiCallSpec { pub name: String, pub method: String, pub url_template: String, pub headers: HashMap, pub body_template: Option, pub auth_type: AuthType, pub retry_config: RetryConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum AuthType { None, ApiKey { header: String, key_ref: String, }, Bearer { token_ref: String, }, Basic { user_ref: String, pass_ref: String, }, OAuth2 { client_id_ref: String, client_secret_ref: String, }, } impl Default for AuthType { fn default() -> Self { AuthType::None } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RetryConfig { pub max_retries: i32, pub backoff_ms: i32, pub retry_on_status: Vec, } impl Default for RetryConfig { fn default() -> Self { RetryConfig { max_retries: 3, backoff_ms: 1000, retry_on_status: vec![429, 500, 502, 503, 504], } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ApprovalLevel { pub level: i32, pub approver: String, pub reason: String, pub timeout_minutes: i32, pub default_action: DefaultApprovalAction, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DefaultApprovalAction { Approve, Reject, Escalate, Pause, } impl Default for DefaultApprovalAction { fn default() -> Self { DefaultApprovalAction::Pause } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AlternativeInterpretation { pub id: String, pub description: String, pub confidence: f64, pub plan_summary: String, pub pros: Vec, pub cons: Vec, pub estimated_cost: Option, pub estimated_time_hours: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RiskAssessment { pub overall_risk: RiskLevel, pub risks: Vec, pub mitigations: Vec, pub requires_human_review: bool, pub review_reason: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct IdentifiedRisk { pub id: String, pub category: RiskCategory, pub description: String, pub probability: f64, pub impact: RiskLevel, pub affected_steps: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum RiskCategory { DataLoss, SecurityBreach, CostOverrun, TimelineSlip, IntegrationFailure, ComplianceViolation, PerformanceIssue, DependencyFailure, Custom(String), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RiskMitigation { pub risk_id: String, pub strategy: String, pub basic_code: Option, pub fallback_plan: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResourceEstimate { pub compute_hours: f64, pub storage_gb: f64, pub api_calls: i32, pub llm_tokens: i32, pub estimated_cost_usd: f64, pub human_hours: f64, pub mcp_servers_needed: Vec, pub external_services: Vec, } impl Default for ResourceEstimate { fn default() -> Self { ResourceEstimate { compute_hours: 0.0, storage_gb: 0.0, api_calls: 0, llm_tokens: 0, estimated_cost_usd: 0.0, human_hours: 0.0, mcp_servers_needed: Vec::new(), external_services: Vec::new(), } } } pub struct IntentCompiler { _state: Arc, config: IntentCompilerConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct IntentCompilerConfig { pub enabled: bool, pub model: String, pub temperature: f64, pub max_tokens: i32, pub auto_execute_low_risk: bool, pub require_approval_above: RiskLevel, pub simulate_before_execute: bool, pub max_plan_steps: i32, pub available_keywords: Vec, pub available_mcp_servers: Vec, } impl Default for IntentCompilerConfig { fn default() -> Self { IntentCompilerConfig { enabled: true, model: "gpt-4".to_string(), temperature: 0.3, max_tokens: 4000, auto_execute_low_risk: false, require_approval_above: RiskLevel::Medium, simulate_before_execute: true, max_plan_steps: 50, available_keywords: get_all_keywords(), available_mcp_servers: Vec::new(), } } } impl std::fmt::Debug for IntentCompiler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("IntentCompiler") .field("config", &self.config) .finish() } } impl IntentCompiler { pub fn new(state: Arc) -> Self { IntentCompiler { _state: state, config: IntentCompilerConfig::default(), } } pub fn with_config(state: Arc, config: IntentCompilerConfig) -> Self { IntentCompiler { _state: state, config, } } pub async fn compile( &self, intent: &str, session: &UserSession, ) -> Result> { info!( "Compiling intent for session {}: {}", session.id, &intent[..intent.len().min(100)] ); let entities = self.extract_entities(intent).await?; trace!("Extracted entities: {:?}", entities); let plan = self.generate_plan(intent, &entities).await?; trace!("Generated plan with {} steps", plan.steps.len()); let basic_program = self.generate_basic_program(&plan, &entities).await?; trace!( "Generated BASIC program: {} lines", basic_program.lines().count() ); let risk_assessment = self.assess_risks(&plan).await?; let resource_estimate = self.estimate_resources(&plan).await?; let (confidence, alternatives) = self.check_ambiguity(intent, &entities, &plan).await?; let compiled = CompiledIntent { id: Uuid::new_v4().to_string(), original_intent: intent.to_string(), entities, plan, basic_program, confidence, alternatives, risk_assessment, resource_estimate, compiled_at: Utc::now(), session_id: session.id.to_string(), bot_id: session.bot_id.to_string(), }; self.store_compiled_intent(&compiled).await?; Ok(compiled) } async fn extract_entities( &self, intent: &str, ) -> Result> { let prompt = format!( r#"Analyze this user request and extract structured information. User Request: "{}" Extract the following as JSON: {{ "action": "primary action (create/update/delete/analyze/report/integrate/automate)", "target": "what to create/modify (CRM, website, report, API, etc.)", "domain": "industry/domain if mentioned (financial, healthcare, retail, etc.) or null", "client": "client/company name if mentioned or null", "features": ["list of specific features requested"], "constraints": [ {{"type": "budget|timeline|technology|security|compliance|performance", "value": "constraint value", "is_hard": true/false}} ], "technologies": ["specific technologies/tools mentioned"], "data_sources": ["data sources mentioned"], "integrations": ["external systems to integrate with"] }} Respond ONLY with valid JSON, no explanation."#, intent ); let response = self.call_llm(&prompt).await?; let entities: IntentEntities = serde_json::from_str(&response).unwrap_or_else(|e| { warn!("Failed to parse entity extraction response: {}", e); IntentEntities { action: "create".to_string(), target: intent.to_string(), ..Default::default() } }); Ok(entities) } async fn generate_plan( &self, intent: &str, entities: &IntentEntities, ) -> Result> { let keywords_list = self.config.available_keywords.join(", "); let mcp_servers_list = self.config.available_mcp_servers.join(", "); let prompt = format!( r#"Generate an execution plan for this task. Original Request: "{}" Extracted Information: - Action: {} - Target: {} - Domain: {} - Client: {} - Features: {:?} - Technologies: {:?} - Integrations: {:?} Available BASIC Keywords: {} Available MCP Servers: {} Generate a detailed execution plan as JSON: {{ "name": "short plan name", "description": "brief description", "steps": [ {{ "id": "step-1", "order": 1, "name": "Step name", "description": "What this step does", "keywords": ["BASIC keywords this step uses"], "priority": "CRITICAL|HIGH|MEDIUM|LOW|OPTIONAL", "risk_level": "NONE|LOW|MEDIUM|HIGH|CRITICAL", "estimated_minutes": 5, "requires_approval": false, "can_rollback": true, "dependencies": [], "outputs": ["variables/resources produced"], "mcp_servers": ["MCP servers needed"], "api_calls": [] }} ], "requires_approval": true/false, "estimated_duration_minutes": 60, "rollback_plan": "how to undo if needed" }} Maximum {} steps. Focus on practical, executable steps. Respond ONLY with valid JSON."#, intent, entities.action, entities.target, entities.domain.as_deref().unwrap_or("general"), entities.client.as_deref().unwrap_or("none"), entities.features, entities.technologies, entities.integrations, keywords_list, mcp_servers_list, self.config.max_plan_steps ); let response = self.call_llm(&prompt).await?; #[derive(Deserialize)] struct PlanResponse { name: String, description: String, steps: Vec, requires_approval: Option, estimated_duration_minutes: Option, rollback_plan: Option, } #[derive(Deserialize)] struct PlanStepResponse { id: String, order: i32, name: String, description: String, keywords: Vec, priority: Option, risk_level: Option, estimated_minutes: Option, requires_approval: Option, can_rollback: Option, dependencies: Option>, outputs: Option>, mcp_servers: Option>, api_calls: Option>, } let plan_response: PlanResponse = serde_json::from_str(&response)?; let steps: Vec = plan_response .steps .into_iter() .map(|s| PlanStep { id: s.id, order: s.order, name: s.name, description: s.description, keywords: s.keywords, basic_code: String::new(), priority: match s.priority.as_deref() { Some("CRITICAL") => StepPriority::Critical, Some("HIGH") => StepPriority::High, Some("MEDIUM") => StepPriority::Medium, Some("LOW") => StepPriority::Low, Some("OPTIONAL") => StepPriority::Optional, _ => StepPriority::Medium, }, risk_level: match s.risk_level.as_deref() { Some("NONE") => RiskLevel::None, Some("LOW") => RiskLevel::Low, Some("MEDIUM") => RiskLevel::Medium, Some("HIGH") => RiskLevel::High, Some("CRITICAL") => RiskLevel::Critical, _ => RiskLevel::Low, }, estimated_minutes: s.estimated_minutes.unwrap_or(5), requires_approval: s.requires_approval.unwrap_or(false), can_rollback: s.can_rollback.unwrap_or(true), dependencies: s.dependencies.unwrap_or_default(), outputs: s.outputs.unwrap_or_default(), mcp_servers: s.mcp_servers.unwrap_or_default(), api_calls: s.api_calls.unwrap_or_default(), }) .collect(); let mut dependencies: HashMap> = HashMap::new(); for step in &steps { dependencies.insert(step.id.clone(), step.dependencies.clone()); } let approval_levels = self.determine_approval_levels(&steps); Ok(ExecutionPlan { id: Uuid::new_v4().to_string(), name: plan_response.name, description: plan_response.description, steps, dependencies, estimated_duration_minutes: plan_response.estimated_duration_minutes.unwrap_or(60), requires_approval: plan_response.requires_approval.unwrap_or(false), approval_levels, rollback_plan: plan_response.rollback_plan, }) } async fn generate_basic_program( &self, plan: &ExecutionPlan, entities: &IntentEntities, ) -> Result> { let mut program = String::new(); program.push_str(&format!( "' =============================================================================\n" )); program.push_str(&format!("' AUTO-GENERATED BASIC PROGRAM\n")); program.push_str(&format!("' Plan: {}\n", plan.name)); program.push_str(&format!("' Description: {}\n", plan.description)); program.push_str(&format!( "' Generated: {}\n", Utc::now().format("%Y-%m-%d %H:%M:%S") )); program.push_str(&format!( "' =============================================================================\n\n" )); program.push_str(&format!( "PLAN_START \"{}\", \"{}\"\n", plan.name, plan.description )); for step in &plan.steps { let priority_str = match step.priority { StepPriority::Critical => "CRITICAL", StepPriority::High => "HIGH", StepPriority::Medium => "MEDIUM", StepPriority::Low => "LOW", StepPriority::Optional => "OPTIONAL", }; program.push_str(&format!( " STEP {}, \"{}\", {}\n", step.order, step.name, priority_str )); } program.push_str("PLAN_END\n\n"); program.push_str("' Initialize context variables\n"); program.push_str(&format!("SET action = \"{}\"\n", entities.action)); program.push_str(&format!("SET target = \"{}\"\n", entities.target)); if let Some(ref client) = entities.client { program.push_str(&format!("SET client = \"{}\"\n", client)); } if let Some(ref domain) = entities.domain { program.push_str(&format!("SET domain = \"{}\"\n", domain)); } program.push_str("\n"); program.push_str("' Set LLM context\n"); program.push_str(&format!( "SET CONTEXT \"Task: {} {} for {}\"\n\n", entities.action, entities.target, entities.client.as_deref().unwrap_or("general use") )); for step in &plan.steps { program.push_str(&format!( "' -----------------------------------------------------------------------------\n" )); program.push_str(&format!("' STEP {}: {}\n", step.order, step.name)); program.push_str(&format!("' {}\n", step.description)); program.push_str(&format!( "' Risk: {:?}, Approval Required: {}\n", step.risk_level, step.requires_approval )); program.push_str(&format!( "' -----------------------------------------------------------------------------\n" )); let step_code = self.generate_step_code(step, entities).await?; program.push_str(&step_code); program.push_str("\n"); } program.push_str("' Task completed\n"); program.push_str("TALK \"Task completed successfully!\"\n"); program.push_str(&format!( "AUDIT_LOG \"plan-complete\", \"{}\", \"success\"\n", plan.id )); Ok(program) } async fn generate_step_code( &self, step: &PlanStep, _entities: &IntentEntities, ) -> Result> { let mut code = String::new(); if step.requires_approval { code.push_str(&format!( "REQUIRE_APPROVAL \"step-{}\", \"{}\"\n", step.order, step.description )); code.push_str("IF NOT approved THEN\n"); code.push_str(&format!( " TALK \"Step {} was not approved, skipping...\"\n", step.order )); code.push_str(&format!(" GOTO step_{}_end\n", step.order)); code.push_str("END IF\n\n"); } if matches!(step.risk_level, RiskLevel::High | RiskLevel::Critical) { code.push_str(&format!( "simulation_result = SIMULATE_IMPACT \"step-{}\"\n", step.order )); code.push_str("IF simulation_result.risk_score > 0.7 THEN\n"); code.push_str(&format!( " TALK \"High risk detected in step {}, requesting manual review...\"\n", step.order )); code.push_str(" REQUIRE_APPROVAL \"high-risk-override\", simulation_result.summary\n"); code.push_str("END IF\n\n"); } code.push_str(&format!( "AUDIT_LOG \"step-start\", \"step-{}\", \"{}\"\n", step.order, step.name )); for keyword in &step.keywords { match keyword.to_uppercase().as_str() { "CREATE_TASK" => { code.push_str(&format!( "task_{} = CREATE_TASK \"{}\", \"auto\", \"+1 day\", null\n", step.order, step.name )); } "LLM" => { code.push_str(&format!( "llm_result_{} = LLM \"{}\"\n", step.order, step.description )); } "RUN_PYTHON" => { code.push_str(&format!( "python_result_{} = RUN_PYTHON \"# {}\nprint('Step {} executed')\"\n", step.order, step.description, step.order )); } "RUN_JAVASCRIPT" => { code.push_str(&format!( "js_result_{} = RUN_JAVASCRIPT \"console.log('Step {} executed');\"\n", step.order, step.order )); } "GET" => { code.push_str(&format!("data_{} = GET \"{}_data\"\n", step.order, step.id)); } "SET" => { code.push_str(&format!("SET step_{}_complete = true\n", step.order)); } "SAVE" => { code.push_str(&format!("SAVE step_{}_result TO \"results\"\n", step.order)); } "POST" | "PUT" | "PATCH" | "DELETE HTTP" => { for api_call in &step.api_calls { code.push_str(&format!( "{} \"{}\" INTO api_result_{}\n", keyword, api_call.url_template, step.order )); } } "USE_MCP" => { for mcp_server in &step.mcp_servers { code.push_str(&format!( "mcp_result_{} = USE_MCP \"{}\", \"{}\"\n", step.order, mcp_server, step.description )); } } "SEND_MAIL" => { code.push_str(&format!( "SEND_MAIL \"status@bot.local\", \"Step {} Complete\", \"{}\"\n", step.order, step.description )); } _ => { code.push_str(&format!("' Using keyword: {}\n", keyword)); } } } for output in &step.outputs { code.push_str(&format!("SET output_{} = result_{}\n", output, step.order)); } code.push_str(&format!( "AUDIT_LOG \"step-end\", \"step-{}\", \"complete\"\n", step.order )); code.push_str(&format!("step_{}_end:\n\n", step.order)); Ok(code) } async fn call_llm( &self, prompt: &str, ) -> Result> { trace!("Calling LLM with prompt length: {}", prompt.len()); let response = serde_json::json!({ "action": "create", "target": "system", "domain": null, "client": null, "features": [], "constraints": [], "technologies": [], "data_sources": [], "integrations": [] }); Ok(response.to_string()) } async fn assess_risks( &self, plan: &ExecutionPlan, ) -> Result> { let mut risks = Vec::new(); let mut overall_risk = RiskLevel::Low; for step in &plan.steps { if step.risk_level >= RiskLevel::High { overall_risk = step.risk_level.clone(); risks.push(IdentifiedRisk { id: format!("risk-{}", step.id), category: RiskCategory::DependencyFailure, description: format!("Step '{}' has high risk level", step.name), probability: 0.3, impact: step.risk_level.clone(), affected_steps: vec![step.id.clone()], }); } } Ok(RiskAssessment { overall_risk, risks, mitigations: Vec::new(), requires_human_review: overall_risk >= RiskLevel::High, review_reason: if overall_risk >= RiskLevel::High { Some("High risk steps detected".to_string()) } else { None }, }) } async fn estimate_resources( &self, plan: &ExecutionPlan, ) -> Result> { let mut estimate = ResourceEstimate::default(); for step in &plan.steps { estimate.compute_hours += (step.estimated_minutes as f64) / 60.0; estimate.api_calls += step.api_calls.len() as i32; for keyword in &step.keywords { if keyword == "LLM" { estimate.llm_tokens += 1000; } } for mcp in &step.mcp_servers { if !estimate.mcp_servers_needed.contains(mcp) { estimate.mcp_servers_needed.push(mcp.clone()); } } } let llm_cost = (estimate.llm_tokens as f64) * 0.00002; estimate.estimated_cost_usd = estimate.compute_hours * 0.10 + (estimate.api_calls as f64) * 0.001 + llm_cost; Ok(estimate) } async fn check_ambiguity( &self, _intent: &str, _entities: &IntentEntities, _plan: &ExecutionPlan, ) -> Result<(f64, Vec), Box> { Ok((0.85, Vec::new())) } async fn store_compiled_intent( &self, _compiled: &CompiledIntent, ) -> Result<(), Box> { info!("Storing compiled intent (stub)"); Ok(()) } fn determine_approval_levels(&self, steps: &[PlanStep]) -> Vec { let mut levels = Vec::new(); let has_high_risk = steps.iter().any(|s| s.risk_level >= RiskLevel::High); if has_high_risk { levels.push(ApprovalLevel { level: 1, approver: "admin".to_string(), reason: "High risk steps require approval".to_string(), timeout_minutes: 60, default_action: DefaultApprovalAction::Pause, }); } levels } } fn get_all_keywords() -> Vec { vec![ "ADD BOT".to_string(), "ADD MEMBER".to_string(), "ADD SUGGESTION".to_string(), "ADD TOOL".to_string(), "AUDIT_LOG".to_string(), "BOOK".to_string(), "CLEAR KB".to_string(), "CLEAR TOOLS".to_string(), "CREATE DRAFT".to_string(), "CREATE SITE".to_string(), "CREATE_TASK".to_string(), "DELETE".to_string(), "DELETE HTTP".to_string(), "DOWNLOAD".to_string(), "FILL".to_string(), "FILTER".to_string(), "FIND".to_string(), "FIRST".to_string(), "GET".to_string(), "GRAPHQL".to_string(), "HEAR".to_string(), "INSERT".to_string(), "JOIN".to_string(), "LAST".to_string(), "LIST".to_string(), "LLM".to_string(), "MAP".to_string(), "MERGE".to_string(), "PATCH".to_string(), "PIVOT".to_string(), "POST".to_string(), "PRINT".to_string(), "PUT".to_string(), "REMEMBER".to_string(), "REQUIRE_APPROVAL".to_string(), "RUN_BASH".to_string(), "RUN_JAVASCRIPT".to_string(), "RUN_PYTHON".to_string(), "SAVE".to_string(), "SEND_MAIL".to_string(), "SEND_TEMPLATE".to_string(), "SET".to_string(), "SET CONTEXT".to_string(), "SET SCHEDULE".to_string(), "SET USER".to_string(), "SIMULATE_IMPACT".to_string(), "SMS".to_string(), "SOAP".to_string(), "TALK".to_string(), "UPDATE".to_string(), "UPLOAD".to_string(), "USE KB".to_string(), "USE MODEL".to_string(), "USE TOOL".to_string(), "USE WEBSITE".to_string(), "USE_MCP".to_string(), "WAIT".to_string(), "WEATHER".to_string(), "WEBHOOK".to_string(), ] }