//! 应用全局状态 — 数据库、Repo、事件总线、节点注册表、AI 会话 use std::path::Path; use std::sync::Arc; use anyhow::Result; use tokio::sync::Mutex; use df_ai::ai_tools::{AiToolRegistry, RiskLevel}; use df_storage::crud::{ AiConversationRepo, AiProviderRepo, AiToolExecutionRepo, IdeaRepo, NodeExecutionRepo, ProjectRepo, ReleaseRepo, TaskRepo, WorkflowRepo, }; use df_storage::db::Database; use df_workflow::eventbus::EventBus; use df_workflow::registry::NodeRegistry; use crate::commands::ai::AiSession; /// 应用全局状态 — 通过 `app.manage()` 注入,command 中以 `State<'_, AppState>` 取用 pub struct AppState { /// 数据库句柄(Arc 包装,便于在异步任务中重建 Repo) pub db: Arc, /// 想法表 Repo pub ideas: IdeaRepo, /// 项目表 Repo pub projects: ProjectRepo, /// 任务表 Repo pub tasks: TaskRepo, /// 发布表 Repo pub releases: ReleaseRepo, /// 工作流执行表 Repo pub workflows: WorkflowRepo, /// 节点执行表 Repo pub node_executions: NodeExecutionRepo, /// 工作流事件总线(tokio broadcast) pub event_bus: EventBus, /// 节点注册表(已注册内置节点) pub registry: Arc, // ── AI ── /// AI 提供商配置 Repo pub ai_providers: AiProviderRepo, /// AI 对话历史 Repo pub ai_conversations: AiConversationRepo, /// AI 工具执行审计 Repo pub ai_tool_executions: AiToolExecutionRepo, /// AI 工具注册表 pub ai_tools: Arc, /// AI 会话状态 pub ai_session: Arc>, } impl AppState { /// 初始化应用状态:打开(或创建)数据库并执行迁移,构建各 Repo 与节点注册表 pub async fn init(db_path: &Path) -> Result { let db = Arc::new(Database::open(db_path).await?); let ai_tools = Arc::new(build_ai_tool_registry()); Ok(Self { ideas: IdeaRepo::new(&db), projects: ProjectRepo::new(&db), tasks: TaskRepo::new(&db), releases: ReleaseRepo::new(&db), workflows: WorkflowRepo::new(&db), node_executions: NodeExecutionRepo::new(&db), ai_providers: AiProviderRepo::new(&db), ai_conversations: AiConversationRepo::new(&db), ai_tool_executions: AiToolExecutionRepo::new(&db), ai_session: Arc::new(Mutex::new(AiSession::new())), db, event_bus: EventBus::new(), registry: Arc::new(build_registry()), ai_tools, }) } } /// 构建节点注册表 — 注册内置节点 /// /// 注意:不使用 `NodeRegistry::default()`,其 script 工厂为占位实现(会 panic), /// 这里注册 df-nodes 提供的真实 ScriptNode 和 HumanNode。 fn build_registry() -> NodeRegistry { let mut registry = NodeRegistry::new(); registry.register("script", |_config| { Box::new(df_nodes::script_node::ScriptNode) }); registry.register("human", |_config| { Box::new(df_nodes::human_node::HumanNode) }); registry.register("ai", |_config| { Box::new(df_nodes::ai_node::AiNode) }); registry } // ============================================================ // AI 工具注册 // ============================================================ use serde_json::json; fn object_schema(fields: Vec<(&str, &str, bool)>) -> serde_json::Value { let mut props = serde_json::Map::new(); let mut required = Vec::new(); for (name, typ, req) in &fields { props.insert(name.to_string(), json!({ "type": typ })); if *req { required.push(name.to_string()); } } json!({ "type": "object", "properties": props, "required": required, }) } /// 构建 AI 工具注册表 — 注册所有 CRUD 操作为可调用工具 fn build_ai_tool_registry() -> AiToolRegistry { let mut registry = AiToolRegistry::new(); // ── 只读工具 (Low risk) ── registry.register( "list_projects", "列出所有项目,返回项目列表(ID、名称、状态、描述)", object_schema(vec![]), RiskLevel::Low, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "list_tasks", "列出任务,可按 project_id 筛选", object_schema(vec![("project_id", "string", false)]), RiskLevel::Low, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "list_ideas", "列出所有想法", object_schema(vec![]), RiskLevel::Low, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); // ── 创建工具 (Medium risk) ── registry.register( "update_project", "更新项目的指定字段(name/status/description),需要提供项目 ID、字段名和新值", object_schema(vec![ ("id", "string", true), ("field", "string", true), ("value", "string", true), ]), RiskLevel::Medium, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "create_project", "创建新项目", object_schema(vec![ ("name", "string", true), ("description", "string", false), ]), RiskLevel::Medium, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "create_task", "在指定项目下创建新任务", object_schema(vec![ ("project_id", "string", true), ("title", "string", true), ("description", "string", false), ("priority", "integer", false), ]), RiskLevel::Medium, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "create_idea", "捕获一个新想法", object_schema(vec![ ("title", "string", true), ("description", "string", false), ("tags", "string", false), ("source", "string", false), ]), RiskLevel::Medium, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); // ── 高风险工具 (High risk) ── registry.register( "delete_project", "删除项目及其所有关联数据", object_schema(vec![("id", "string", true)]), RiskLevel::High, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "run_workflow", "运行指定的工作流 DAG", object_schema(vec![ ("name", "string", true), ("dag", "object", true), ]), RiskLevel::High, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); // ── 文件系统工具 ── registry.register( "read_file", "读取文件内容,返回文本内容。支持 offset 和 limit 参数分页读取大文件", object_schema(vec![ ("path", "string", true), ("offset", "integer", false), ("limit", "integer", false), ]), RiskLevel::Low, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "list_directory", "列出目录内容,返回文件和子目录列表(名称、类型、大小)", object_schema(vec![ ("path", "string", true), ("recursive", "boolean", false), ]), RiskLevel::Low, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry.register( "write_file", "写入或创建文件,自动创建不存在的父目录", object_schema(vec![ ("path", "string", true), ("content", "string", true), ]), RiskLevel::Medium, Box::new(|_args| { Box::pin(async { Ok(json!({"note": "工具执行在 IPC 层处理"})) }) }), ); registry }