Skip to main content

aster/blueprint/
blueprint_context.rs

1//! 蓝图上下文管理器
2//!
3//! 单例模式,用于在工具执行时提供当前蓝图任务的上下文信息。
4//! 这是连接蓝图系统和工具系统的桥梁。
5//!
6//! 使用场景:
7//! 1. Queen 分配任务时,设置活跃任务上下文
8//! 2. Edit/Write 工具执行时,检查是否有活跃上下文,如有则进行边界检查
9//! 3. Worker 完成任务后,清除上下文
10
11use chrono::{DateTime, Utc};
12use once_cell::sync::Lazy;
13use std::collections::HashMap;
14use tokio::sync::RwLock;
15
16use super::boundary_checker::{create_boundary_checker, BoundaryCheckResult, BoundaryChecker};
17use super::types::Blueprint;
18
19// ============================================================================
20// 任务上下文类型
21// ============================================================================
22
23/// 活跃任务上下文
24#[derive(Debug, Clone)]
25pub struct ActiveTaskContext {
26    /// 蓝图 ID
27    pub blueprint_id: String,
28    /// 任务 ID
29    pub task_id: String,
30    /// 任务所属模块 ID
31    pub module_id: Option<String>,
32    /// Worker Agent ID
33    pub worker_id: String,
34    /// 开始时间
35    pub started_at: DateTime<Utc>,
36}
37
38/// 文件操作类型
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40pub enum FileOperation {
41    Read,
42    #[default]
43    Write,
44    Delete,
45}
46
47// ============================================================================
48// 蓝图上下文管理器
49// ============================================================================
50
51/// 蓝图上下文管理器(内部状态)
52struct BlueprintContextInner {
53    /// 当前蓝图(可能没有)
54    current_blueprint: Option<Blueprint>,
55    /// 边界检查器(基于当前蓝图)
56    boundary_checker: Option<BoundaryChecker>,
57    /// 活跃任务上下文(Worker ID -> 上下文)
58    active_tasks: HashMap<String, ActiveTaskContext>,
59    /// 是否启用边界检查
60    boundary_check_enabled: bool,
61}
62
63impl Default for BlueprintContextInner {
64    fn default() -> Self {
65        Self {
66            current_blueprint: None,
67            boundary_checker: None,
68            active_tasks: HashMap::new(),
69            boundary_check_enabled: true,
70        }
71    }
72}
73
74/// 蓝图上下文管理器
75pub struct BlueprintContextManager {
76    inner: RwLock<BlueprintContextInner>,
77}
78
79impl BlueprintContextManager {
80    /// 创建新实例
81    fn new() -> Self {
82        Self {
83            inner: RwLock::new(BlueprintContextInner::default()),
84        }
85    }
86
87    // --------------------------------------------------------------------------
88    // 蓝图管理
89    // --------------------------------------------------------------------------
90
91    /// 设置当前蓝图(启动蜂群时调用)
92    pub async fn set_blueprint(&self, blueprint: Blueprint) {
93        let mut inner = self.inner.write().await;
94        let checker = create_boundary_checker(blueprint.clone(), None);
95        inner.current_blueprint = Some(blueprint);
96        inner.boundary_checker = Some(checker);
97    }
98
99    /// 清除当前蓝图(蜂群完成时调用)
100    pub async fn clear_blueprint(&self) {
101        let mut inner = self.inner.write().await;
102        inner.current_blueprint = None;
103        inner.boundary_checker = None;
104        inner.active_tasks.clear();
105    }
106
107    /// 获取当前蓝图
108    pub async fn get_blueprint(&self) -> Option<Blueprint> {
109        let inner = self.inner.read().await;
110        inner.current_blueprint.clone()
111    }
112
113    // --------------------------------------------------------------------------
114    // 任务上下文管理
115    // --------------------------------------------------------------------------
116
117    /// 设置活跃任务(Worker 开始任务时调用)
118    pub async fn set_active_task(&self, context: ActiveTaskContext) {
119        let mut inner = self.inner.write().await;
120        inner
121            .active_tasks
122            .insert(context.worker_id.clone(), context);
123    }
124
125    /// 获取活跃任务上下文
126    pub async fn get_active_task(&self, worker_id: &str) -> Option<ActiveTaskContext> {
127        let inner = self.inner.read().await;
128        inner.active_tasks.get(worker_id).cloned()
129    }
130
131    /// 清除活跃任务(Worker 完成任务时调用)
132    pub async fn clear_active_task(&self, worker_id: &str) {
133        let mut inner = self.inner.write().await;
134        inner.active_tasks.remove(worker_id);
135    }
136
137    /// 获取所有活跃任务
138    pub async fn get_all_active_tasks(&self) -> Vec<ActiveTaskContext> {
139        let inner = self.inner.read().await;
140        inner.active_tasks.values().cloned().collect()
141    }
142
143    /// 获取当前线程的任务上下文
144    /// 注意:在单线程环境中,如果只有一个活跃任务,返回它
145    pub async fn get_current_task_context(&self) -> Option<ActiveTaskContext> {
146        let tasks = self.get_all_active_tasks().await;
147        // 如果只有一个活跃任务,返回它
148        if tasks.len() == 1 {
149            return tasks.into_iter().next();
150        }
151        // 多个任务时,返回 None(需要明确指定 workerId)
152        None
153    }
154
155    // --------------------------------------------------------------------------
156    // 边界检查
157    // --------------------------------------------------------------------------
158
159    /// 启用/禁用边界检查
160    pub async fn set_boundary_check_enabled(&self, enabled: bool) {
161        let mut inner = self.inner.write().await;
162        inner.boundary_check_enabled = enabled;
163    }
164
165    /// 检查文件操作是否允许
166    pub async fn check_file_operation(
167        &self,
168        file_path: &str,
169        _operation: FileOperation,
170        worker_id: Option<&str>,
171    ) -> BoundaryCheckResult {
172        let inner = self.inner.read().await;
173
174        // 如果未启用边界检查,直接通过
175        if !inner.boundary_check_enabled {
176            return BoundaryCheckResult::allow();
177        }
178
179        // 如果没有蓝图或边界检查器,直接通过
180        let checker = match &inner.boundary_checker {
181            Some(c) => c,
182            None => return BoundaryCheckResult::allow(),
183        };
184
185        // 如果没有活跃任务,直接通过(不在蓝图执行上下文中)
186        if inner.active_tasks.is_empty() {
187            return BoundaryCheckResult::allow();
188        }
189
190        // 确定任务上下文
191        let context = if let Some(wid) = worker_id {
192            inner.active_tasks.get(wid).cloned()
193        } else if inner.active_tasks.len() == 1 {
194            inner.active_tasks.values().next().cloned()
195        } else {
196            None
197        };
198
199        // 如果有任务上下文,使用任务边界检查
200        if let Some(ctx) = context {
201            if let Some(ref module_id) = ctx.module_id {
202                return checker.check_task_boundary(Some(module_id.as_str()), file_path);
203            }
204        }
205
206        // 否则使用通用边界检查(无任务上下文时,不限制模块)
207        checker.check_task_boundary(None, file_path)
208    }
209
210    /// 检查并抛出异常(如果不允许)
211    /// 用于工具层面的硬约束
212    pub async fn enforce_file_operation(
213        &self,
214        file_path: &str,
215        operation: FileOperation,
216        worker_id: Option<&str>,
217    ) -> Result<(), String> {
218        let result = self
219            .check_file_operation(file_path, operation, worker_id)
220            .await;
221        if !result.allowed {
222            Err(format!(
223                "[蓝图边界检查] {}",
224                result.reason.unwrap_or_default()
225            ))
226        } else {
227            Ok(())
228        }
229    }
230
231    // --------------------------------------------------------------------------
232    // 调试和状态
233    // --------------------------------------------------------------------------
234
235    /// 获取当前状态(调试用)
236    pub async fn get_status(&self) -> BlueprintContextStatus {
237        let inner = self.inner.read().await;
238        BlueprintContextStatus {
239            has_blueprint: inner.current_blueprint.is_some(),
240            blueprint_id: inner.current_blueprint.as_ref().map(|b| b.id.clone()),
241            boundary_check_enabled: inner.boundary_check_enabled,
242            active_task_count: inner.active_tasks.len(),
243            active_tasks: inner.active_tasks.values().cloned().collect(),
244        }
245    }
246}
247
248/// 蓝图上下文状态
249#[derive(Debug, Clone)]
250pub struct BlueprintContextStatus {
251    pub has_blueprint: bool,
252    pub blueprint_id: Option<String>,
253    pub boundary_check_enabled: bool,
254    pub active_task_count: usize,
255    pub active_tasks: Vec<ActiveTaskContext>,
256}
257
258// ============================================================================
259// 全局单例
260// ============================================================================
261
262/// 全局蓝图上下文单例
263static BLUEPRINT_CONTEXT: Lazy<BlueprintContextManager> = Lazy::new(BlueprintContextManager::new);
264
265/// 获取全局蓝图上下文
266pub fn get_blueprint_context() -> &'static BlueprintContextManager {
267    &BLUEPRINT_CONTEXT
268}
269
270// ============================================================================
271// 便捷函数导出
272// ============================================================================
273
274/// 设置当前蓝图
275pub async fn set_blueprint(blueprint: Blueprint) {
276    get_blueprint_context().set_blueprint(blueprint).await;
277}
278
279/// 清除当前蓝图
280pub async fn clear_blueprint() {
281    get_blueprint_context().clear_blueprint().await;
282}
283
284/// 设置活跃任务
285pub async fn set_active_task(context: ActiveTaskContext) {
286    get_blueprint_context().set_active_task(context).await;
287}
288
289/// 清除活跃任务
290pub async fn clear_active_task(worker_id: &str) {
291    get_blueprint_context().clear_active_task(worker_id).await;
292}
293
294/// 检查文件操作
295pub async fn check_file_operation(
296    file_path: &str,
297    operation: FileOperation,
298    worker_id: Option<&str>,
299) -> BoundaryCheckResult {
300    get_blueprint_context()
301        .check_file_operation(file_path, operation, worker_id)
302        .await
303}
304
305/// 强制检查文件操作(失败时返回错误)
306pub async fn enforce_file_operation(
307    file_path: &str,
308    operation: FileOperation,
309    worker_id: Option<&str>,
310) -> Result<(), String> {
311    get_blueprint_context()
312        .enforce_file_operation(file_path, operation, worker_id)
313        .await
314}