feat: Added agent variables support for graph agents and improved script executor to use the same environment variables as normal agent tool calling for further flexibility
This commit is contained in:
@@ -678,6 +678,7 @@ impl AgentConfig {
|
|||||||
global_tools: graph.global_tools.clone(),
|
global_tools: graph.global_tools.clone(),
|
||||||
mcp_servers: graph.mcp_servers.clone(),
|
mcp_servers: graph.mcp_servers.clone(),
|
||||||
conversation_starters: graph.conversation_starters.clone(),
|
conversation_starters: graph.conversation_starters.clone(),
|
||||||
|
variables: graph.variables.clone(),
|
||||||
can_spawn_agents: graph.has_agent_node(),
|
can_spawn_agents: graph.has_agent_node(),
|
||||||
max_concurrent_agents: default_max_concurrent_agents(),
|
max_concurrent_agents: default_max_concurrent_agents(),
|
||||||
max_agent_depth: default_max_agent_depth(),
|
max_agent_depth: default_max_agent_depth(),
|
||||||
|
|||||||
+3
-1
@@ -14,7 +14,9 @@ pub(crate) mod todo;
|
|||||||
mod tool_scope;
|
mod tool_scope;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
pub use self::agent::{Agent, AgentVariables, complete_agent_variables, list_agents};
|
pub use self::agent::{
|
||||||
|
Agent, AgentVariable, AgentVariables, complete_agent_variables, list_agents,
|
||||||
|
};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use self::app_config::AppConfig;
|
pub use self::app_config::AppConfig;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
|||||||
@@ -76,7 +76,12 @@ impl GraphExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut state = StateManager::new(graph.initial_state.clone());
|
let mut state = StateManager::new(graph.initial_state.clone());
|
||||||
let script_executor = ScriptExecutor::new(&base_dir);
|
let agent_envs = ctx
|
||||||
|
.agent
|
||||||
|
.as_ref()
|
||||||
|
.map(|a| a.variable_envs())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let script_executor = ScriptExecutor::new(&base_dir).with_envs(agent_envs);
|
||||||
let max_iterations = graph.settings.max_loop_iterations;
|
let max_iterations = graph.settings.max_loop_iterations;
|
||||||
let graph_timeout = graph.settings.timeout.map(Duration::from_secs);
|
let graph_timeout = graph.settings.timeout.map(Duration::from_secs);
|
||||||
let max_concurrency = graph.settings.max_concurrency;
|
let max_concurrency = graph.settings.max_concurrency;
|
||||||
|
|||||||
+49
-2
@@ -1,26 +1,43 @@
|
|||||||
use super::state::{StateManager, StateRepresentation};
|
use super::state::{StateManager, StateRepresentation};
|
||||||
use super::types::ScriptNode;
|
use super::types::ScriptNode;
|
||||||
|
use crate::config::paths;
|
||||||
use crate::function::Language;
|
use crate::function::Language;
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, anyhow, bail};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
const PATH_SEP: &str = ";";
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
const PATH_SEP: &str = ":";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ScriptExecutor {
|
pub struct ScriptExecutor {
|
||||||
base_dir: PathBuf,
|
base_dir: PathBuf,
|
||||||
|
extra_envs: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptExecutor {
|
impl ScriptExecutor {
|
||||||
pub fn new(base_dir: impl Into<PathBuf>) -> Self {
|
pub fn new(base_dir: impl Into<PathBuf>) -> Self {
|
||||||
|
let base_dir = base_dir.into();
|
||||||
|
let extra_envs = build_default_envs(&base_dir);
|
||||||
Self {
|
Self {
|
||||||
base_dir: base_dir.into(),
|
base_dir,
|
||||||
|
extra_envs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_envs(mut self, envs: HashMap<String, String>) -> Self {
|
||||||
|
self.extra_envs.extend(envs);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
&self,
|
&self,
|
||||||
node: &ScriptNode,
|
node: &ScriptNode,
|
||||||
@@ -35,9 +52,9 @@ impl ScriptExecutor {
|
|||||||
let state_repr = state_manager.serialize_state()?;
|
let state_repr = state_manager.serialize_state()?;
|
||||||
|
|
||||||
let mut cmd = build_command(language, &script_path)?;
|
let mut cmd = build_command(language, &script_path)?;
|
||||||
cmd.current_dir(&self.base_dir);
|
|
||||||
cmd.stdout(Stdio::piped());
|
cmd.stdout(Stdio::piped());
|
||||||
cmd.stderr(Stdio::piped());
|
cmd.stderr(Stdio::piped());
|
||||||
|
cmd.envs(&self.extra_envs);
|
||||||
match &state_repr {
|
match &state_repr {
|
||||||
StateRepresentation::Inline(json) => {
|
StateRepresentation::Inline(json) => {
|
||||||
cmd.env("GRAPH_STATE", json);
|
cmd.env("GRAPH_STATE", json);
|
||||||
@@ -111,6 +128,36 @@ fn apply_state_updates(node: &ScriptNode, state_manager: &mut StateManager) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_default_envs(agent_data_dir: &Path) -> HashMap<String, String> {
|
||||||
|
let mut envs = HashMap::new();
|
||||||
|
envs.insert(
|
||||||
|
"LLM_ROOT_DIR".to_string(),
|
||||||
|
paths::config_dir().to_string_lossy().into_owned(),
|
||||||
|
);
|
||||||
|
envs.insert(
|
||||||
|
"LLM_PROMPT_UTILS_FILE".to_string(),
|
||||||
|
paths::bash_prompt_utils_file()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
);
|
||||||
|
envs.insert(
|
||||||
|
"LLM_AGENT_DATA_DIR".to_string(),
|
||||||
|
agent_data_dir.to_string_lossy().into_owned(),
|
||||||
|
);
|
||||||
|
envs.insert("CLICOLOR_FORCE".to_string(), "1".to_string());
|
||||||
|
envs.insert("FORCE_COLOR".to_string(), "1".to_string());
|
||||||
|
|
||||||
|
if let Ok(current_path) = env::var("PATH") {
|
||||||
|
let bin_dir = paths::functions_bin_dir();
|
||||||
|
envs.insert(
|
||||||
|
"PATH".to_string(),
|
||||||
|
format!("{}{}{}", bin_dir.display(), PATH_SEP, current_path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
envs
|
||||||
|
}
|
||||||
|
|
||||||
fn detect_language(script_path: &Path) -> Result<Language> {
|
fn detect_language(script_path: &Path) -> Result<Language> {
|
||||||
let ext = script_path
|
let ext = script_path
|
||||||
.extension()
|
.extension()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::config::AgentVariable;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -33,6 +34,9 @@ pub struct Graph {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub conversation_starters: Vec<String>,
|
pub conversation_starters: Vec<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub variables: Vec<AgentVariable>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub settings: GraphSettings,
|
pub settings: GraphSettings,
|
||||||
|
|
||||||
|
|||||||
@@ -848,6 +848,7 @@ mod tests {
|
|||||||
global_tools: Vec::new(),
|
global_tools: Vec::new(),
|
||||||
mcp_servers: Vec::new(),
|
mcp_servers: Vec::new(),
|
||||||
conversation_starters: Vec::new(),
|
conversation_starters: Vec::new(),
|
||||||
|
variables: Vec::new(),
|
||||||
settings: GraphSettings::default(),
|
settings: GraphSettings::default(),
|
||||||
initial_state: HashMap::new(),
|
initial_state: HashMap::new(),
|
||||||
reducers: HashMap::new(),
|
reducers: HashMap::new(),
|
||||||
|
|||||||
Reference in New Issue
Block a user