use crate::core::shared::state::AppState; use crate::designer::bas_analyzer::{BasFileAnalyzer, BasFileType, WorkflowMetadata}; use axum::{ extract::State, http::StatusCode, response::Html, Json, }; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkflowNode { pub id: String, pub node_type: NodeType, pub position: Position, pub config: NodeConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Position { pub x: f32, pub y: f32, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum NodeType { BotAgent { bot_name: String, action: String }, HumanApproval { approver: String, timeout: u32 }, Condition { expression: String }, Parallel { branches: Vec }, Event { event_name: String }, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NodeConfig { pub label: String, pub description: Option, pub parameters: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkflowConnection { pub from_node: String, pub to_node: String, pub condition: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkflowCanvas { pub id: Uuid, pub name: String, pub nodes: Vec, pub connections: Vec, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } impl WorkflowCanvas { pub fn new(name: String) -> Self { Self { id: Uuid::new_v4(), name, nodes: Vec::new(), connections: Vec::new(), created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), } } pub fn generate_basic_code(&self) -> String { let mut code = format!("' Generated workflow: {}\n", self.name); code.push_str(&format!("ORCHESTRATE WORKFLOW \"{}\"\n", self.name)); let mut step_counter = 1; for node in &self.nodes { match &node.node_type { NodeType::BotAgent { bot_name, action } => { code.push_str(&format!(" STEP {}: BOT \"{}\" \"{}\"\n", step_counter, bot_name, action)); step_counter += 1; } NodeType::HumanApproval { approver, timeout } => { code.push_str(&format!(" STEP {}: HUMAN APPROVAL FROM \"{}\"\n", step_counter, approver)); code.push_str(&format!(" TIMEOUT {}\n", timeout)); step_counter += 1; } NodeType::Condition { expression } => { code.push_str(&format!(" IF {} THEN\n", expression)); } NodeType::Parallel { branches: _ } => { code.push_str(&format!(" STEP {}: PARALLEL\n", step_counter)); code.push_str(" BRANCH A: BOT \"branch-a\" \"process\"\n"); code.push_str(" BRANCH B: BOT \"branch-b\" \"process\"\n"); code.push_str(" END PARALLEL\n"); step_counter += 1; } NodeType::Event { event_name } => { code.push_str(&format!(" PUBLISH EVENT \"{}\"\n", event_name)); } } } code.push_str("END WORKFLOW\n"); code } pub fn detect_file_type(content: &str) -> BasFileType { match BasFileAnalyzer::analyze_content(content) { Ok(file_type) => file_type, Err(_) => BasFileType::Regular, } } pub fn get_metadata(&self) -> WorkflowMetadata { let code = self.generate_basic_code(); BasFileAnalyzer::get_workflow_metadata(&code) } } pub async fn workflow_designer_page( State(_state): State>, ) -> Result, StatusCode> { let html = r##" Workflow Designer

Workflow Designer

Generated BASIC code will appear here...
"##; Ok(Html(html.to_string())) } #[derive(Deserialize)] pub struct GenerateCodeRequest { pub nodes: Vec, pub connections: Vec, } pub async fn generate_workflow_code( State(_state): State>, Json(request): Json, ) -> Result, StatusCode> { let canvas = WorkflowCanvas { id: Uuid::new_v4(), name: "Generated Workflow".to_string(), nodes: request.nodes, connections: request.connections, created_at: chrono::Utc::now(), updated_at: chrono::Utc::now(), }; let code = canvas.generate_basic_code(); Ok(Html(format!("
{}
", code))) } #[derive(Deserialize)] pub struct AnalyzeFileRequest { pub content: String, } #[derive(Serialize)] pub struct AnalyzeFileResponse { pub file_type: String, pub metadata: Option, pub suggestions: Vec, } pub async fn analyze_bas_file( State(_state): State>, Json(request): Json, ) -> Result, StatusCode> { let file_type = WorkflowCanvas::detect_file_type(&request.content); let (type_str, metadata, suggestions) = match file_type { BasFileType::Workflow => { let meta = BasFileAnalyzer::get_workflow_metadata(&request.content); let mut suggestions = Vec::new(); if meta.step_count > 10 { suggestions.push("Consider breaking this workflow into smaller sub-workflows".to_string()); } if meta.bot_count > 5 { suggestions.push("High bot count - ensure proper resource management".to_string()); } if !meta.has_human_approval && meta.step_count > 3 { suggestions.push("Consider adding human approval for complex workflows".to_string()); } ("workflow".to_string(), Some(meta), suggestions) } BasFileType::Tool => { let suggestions = vec![ "Tools should be simple and focused on single operations".to_string(), "Consider using USE TOOL instead of complex logic".to_string(), ]; ("tool".to_string(), None, suggestions) } BasFileType::Regular => { let suggestions = vec![ "Regular bot - consider upgrading to workflow for complex logic".to_string(), ]; ("regular".to_string(), None, suggestions) } }; Ok(Json(AnalyzeFileResponse { file_type: type_str, metadata, suggestions, })) }