Skip to main content

vv_agent/tools/base/
context.rs

1use std::collections::BTreeMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use serde_json::Value;
6
7use super::paths::resolve_workspace_path_checked;
8use super::SubTaskRunner;
9use crate::model::ModelProvider;
10use crate::workspace::{LocalWorkspaceBackend, WorkspaceBackend};
11
12#[derive(Clone)]
13pub struct ToolContext {
14    pub workspace: PathBuf,
15    pub shared_state: BTreeMap<String, Value>,
16    pub cycle_index: u32,
17    pub task_id: String,
18    pub metadata: BTreeMap<String, Value>,
19    pub workspace_backend: Arc<dyn WorkspaceBackend>,
20    pub model_provider: Option<Arc<dyn ModelProvider>>,
21    pub sub_task_runner: Option<SubTaskRunner>,
22    pub sub_task_manager: Option<crate::runtime::sub_task_manager::SubTaskManager>,
23    pub execution_backend: Option<crate::runtime::backends::RuntimeExecutionBackend>,
24}
25
26impl std::fmt::Debug for ToolContext {
27    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        formatter
29            .debug_struct("ToolContext")
30            .field("workspace", &self.workspace)
31            .field("shared_state", &self.shared_state)
32            .field("cycle_index", &self.cycle_index)
33            .field("task_id", &self.task_id)
34            .field("metadata", &self.metadata)
35            .field("has_model_provider", &self.model_provider.is_some())
36            .field("has_sub_task_runner", &self.sub_task_runner.is_some())
37            .field("has_sub_task_manager", &self.sub_task_manager.is_some())
38            .field("has_execution_backend", &self.execution_backend.is_some())
39            .finish_non_exhaustive()
40    }
41}
42
43impl ToolContext {
44    pub fn new(workspace: impl Into<PathBuf>) -> Self {
45        let workspace = workspace.into();
46        Self {
47            workspace: workspace.clone(),
48            shared_state: BTreeMap::new(),
49            cycle_index: 0,
50            task_id: String::new(),
51            metadata: BTreeMap::new(),
52            workspace_backend: Arc::new(crate::workspace::LocalWorkspaceBackend::new(workspace)),
53            model_provider: None,
54            sub_task_runner: None,
55            sub_task_manager: None,
56            execution_backend: None,
57        }
58    }
59
60    pub fn allow_outside_workspace_paths(&self) -> bool {
61        for key in [
62            "allow_outside_workspace_paths",
63            "allow_outside_workspace",
64            "workspace_allow_outside_main",
65            "workspace_allow_outside",
66        ] {
67            if let Some(parsed) = parse_bool(self.metadata.get(key)) {
68                return parsed;
69            }
70        }
71        false
72    }
73
74    pub fn resolve_workspace_path(&self, raw_path: &str) -> Result<PathBuf, String> {
75        resolve_workspace_path_checked(
76            &self.workspace,
77            raw_path,
78            self.allow_outside_workspace_paths(),
79        )
80    }
81
82    pub fn effective_workspace_backend(&self) -> Arc<dyn WorkspaceBackend> {
83        if self.allow_outside_workspace_paths() {
84            if let Some(local) = self
85                .workspace_backend
86                .as_any()
87                .downcast_ref::<LocalWorkspaceBackend>()
88            {
89                let mut local = local.clone();
90                local.allow_outside_root = true;
91                return Arc::new(local);
92            }
93        }
94        self.workspace_backend.clone()
95    }
96}
97
98fn parse_bool(value: Option<&Value>) -> Option<bool> {
99    match value {
100        Some(Value::Bool(value)) => Some(*value),
101        Some(Value::Number(number)) => number.as_i64().and_then(|value| match value {
102            0 => Some(false),
103            1 => Some(true),
104            _ => None,
105        }),
106        Some(Value::String(value)) => match value.trim().to_ascii_lowercase().as_str() {
107            "1" | "true" | "yes" | "on" => Some(true),
108            "0" | "false" | "no" | "off" => Some(false),
109            _ => None,
110        },
111        _ => None,
112    }
113}