fix: error when users try to start a session on a graph agent
This commit is contained in:
@@ -37,6 +37,7 @@ use std::fs::{File, OpenOptions, read_dir, read_to_string, remove_dir_all, remov
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use crate::graph;
|
||||||
|
|
||||||
pub struct AutoContinueConfig {
|
pub struct AutoContinueConfig {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
@@ -1007,6 +1008,7 @@ impl RequestContext {
|
|||||||
})
|
})
|
||||||
.map(|v| v.name.to_string())
|
.map(|v| v.name.to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(agent) = &self.agent {
|
if let Some(agent) = &self.agent {
|
||||||
declaration_names.extend(
|
declaration_names.extend(
|
||||||
agent
|
agent
|
||||||
@@ -1031,6 +1033,7 @@ impl RequestContext {
|
|||||||
if item.is_empty() {
|
if item.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(values) = app.mapping_tools.get(item) {
|
if let Some(values) = app.mapping_tools.get(item) {
|
||||||
tool_names.extend(
|
tool_names.extend(
|
||||||
values
|
values
|
||||||
@@ -1089,9 +1092,11 @@ impl RequestContext {
|
|||||||
&& !v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
&& !v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(ref tool_names) = role_filter {
|
if let Some(ref tool_names) = role_filter {
|
||||||
agent_functions.retain(|v| tool_names.contains(&v.name));
|
agent_functions.retain(|v| tool_names.contains(&v.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
let tool_names: HashSet<String> = agent_functions
|
let tool_names: HashSet<String> = agent_functions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|v| {
|
.filter_map(|v| {
|
||||||
@@ -1157,6 +1162,7 @@ impl RequestContext {
|
|||||||
if item.is_empty() {
|
if item.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let item_invoke_name =
|
let item_invoke_name =
|
||||||
format!("{}_{item}", MCP_INVOKE_META_FUNCTION_NAME_PREFIX);
|
format!("{}_{item}", MCP_INVOKE_META_FUNCTION_NAME_PREFIX);
|
||||||
let item_search_name =
|
let item_search_name =
|
||||||
@@ -1226,9 +1232,11 @@ impl RequestContext {
|
|||||||
|| v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
|| v.name.starts_with(MCP_DESCRIBE_META_FUNCTION_NAME_PREFIX)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(ref server_names) = role_filter {
|
if let Some(ref server_names) = role_filter {
|
||||||
agent_functions.retain(|v| server_names.contains(&v.name));
|
agent_functions.retain(|v| server_names.contains(&v.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
let tool_names: HashSet<String> = agent_functions
|
let tool_names: HashSet<String> = agent_functions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|v| {
|
.filter_map(|v| {
|
||||||
@@ -2168,6 +2176,14 @@ impl RequestContext {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let is_graph_agent = graph::agent_has_graph(agent_name);
|
||||||
|
if is_graph_agent && session_name.is_some() {
|
||||||
|
bail!(
|
||||||
|
"Graph-based agent '{agent_name}' does not support sessions. \
|
||||||
|
The graph manages its own state; re-run without a session."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mcp_servers = if app.mcp_server_support {
|
let mcp_servers = if app.mcp_server_support {
|
||||||
(!agent.mcp_server_names().is_empty()).then(|| agent.mcp_server_names().join(","))
|
(!agent.mcp_server_names().is_empty()).then(|| agent.mcp_server_names().join(","))
|
||||||
} else {
|
} else {
|
||||||
@@ -2189,8 +2205,10 @@ impl RequestContext {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Graph agents manage their own state; never engage a session,
|
||||||
|
// not even an inherited app-level `agent_session` default.
|
||||||
let session_name = session_name.map(|v| v.to_string()).or_else(|| {
|
let session_name = session_name.map(|v| v.to_string()).or_else(|| {
|
||||||
if self.macro_flag {
|
if self.macro_flag || is_graph_agent {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
agent.agent_session().map(|v| v.to_string())
|
agent.agent_session().map(|v| v.to_string())
|
||||||
@@ -3583,4 +3601,79 @@ mod tests {
|
|||||||
"Agent should not be set when session check fails"
|
"Agent should not be set when session check fails"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn use_agent_errors_when_graph_agent_given_explicit_session() {
|
||||||
|
let _guard = TestConfigDirGuard::new();
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
|
||||||
|
let app = ctx.app.config.clone();
|
||||||
|
let agent_name = format!(
|
||||||
|
"test_graph_agent_{}",
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_nanos()
|
||||||
|
);
|
||||||
|
let agent_dir = paths::agent_data_dir(&agent_name);
|
||||||
|
create_dir_all(&agent_dir).unwrap();
|
||||||
|
write(
|
||||||
|
agent_dir.join("graph.yaml"),
|
||||||
|
format!(
|
||||||
|
"name: {agent_name}\nversion: \"1.0\"\nstart: done\nnodes:\n done:\n type: end\n output: ok\n"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let abort = utils::create_abort_signal();
|
||||||
|
let result = run_async(ctx.use_agent(&app, &agent_name, Some("test_session"), abort));
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(
|
||||||
|
result
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.contains("does not support sessions")
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
ctx.agent.is_none(),
|
||||||
|
"Agent should not be set when the graph-agent session guard fails"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn use_agent_skips_inherited_session_for_graph_agent() {
|
||||||
|
let _guard = TestConfigDirGuard::new();
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
ctx.update_app_config(|app| app.agent_session = Some("inherited".to_string()));
|
||||||
|
|
||||||
|
let app = ctx.app.config.clone();
|
||||||
|
let agent_name = format!(
|
||||||
|
"test_graph_agent_{}",
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_nanos()
|
||||||
|
);
|
||||||
|
let agent_dir = paths::agent_data_dir(&agent_name);
|
||||||
|
create_dir_all(&agent_dir).unwrap();
|
||||||
|
write(
|
||||||
|
agent_dir.join("graph.yaml"),
|
||||||
|
format!(
|
||||||
|
"name: {agent_name}\nversion: \"1.0\"\nstart: done\nnodes:\n done:\n type: end\n output: ok\n"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let abort = utils::create_abort_signal();
|
||||||
|
run_async(ctx.use_agent(&app, &agent_name, None, abort)).unwrap();
|
||||||
|
|
||||||
|
assert!(ctx.agent.is_some(), "Graph agent should load successfully");
|
||||||
|
assert!(
|
||||||
|
ctx.session.is_none(),
|
||||||
|
"Graph agent must not engage a session, not even an inherited default"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -17,7 +17,7 @@ use crate::utils::{
|
|||||||
AbortSignal, abortable_run_with_spinner, create_abort_signal, dimmed_text, set_text, temp_file,
|
AbortSignal, abortable_run_with_spinner, create_abort_signal, dimmed_text, set_text, temp_file,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::resolve_oauth_client;
|
use crate::{graph, resolve_oauth_client};
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use crossterm::cursor::SetCursorStyle;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
@@ -32,6 +32,7 @@ use reedline::{
|
|||||||
use reedline::{MenuBuilder, Signal};
|
use reedline::{MenuBuilder, Signal};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::{env, process, sync::Arc};
|
use std::{env, process, sync::Arc};
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
const MENU_NAME: &str = "completion_menu";
|
const MENU_NAME: &str = "completion_menu";
|
||||||
|
|
||||||
@@ -497,6 +498,12 @@ pub async fn run_repl_command(
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
".session" => {
|
".session" => {
|
||||||
|
if let Some(name) = graph::active_agent_graph_name(ctx) {
|
||||||
|
bail!(
|
||||||
|
"Graph-based agent '{name}' does not support sessions. \
|
||||||
|
The graph manages its own state."
|
||||||
|
);
|
||||||
|
}
|
||||||
let app = Arc::clone(&ctx.app.config);
|
let app = Arc::clone(&ctx.app.config);
|
||||||
ctx.use_session(app.as_ref(), args, abort_signal.clone())
|
ctx.use_session(app.as_ref(), args, abort_signal.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -508,7 +515,7 @@ pub async fn run_repl_command(
|
|||||||
};
|
};
|
||||||
eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),);
|
eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),);
|
||||||
if let Err(err) = ctx.autoname_session(app.as_ref()).await {
|
if let Err(err) = ctx.autoname_session(app.as_ref()).await {
|
||||||
log::warn!("Failed to autonaming the session: {err}");
|
warn!("Failed to autonaming the session: {err}");
|
||||||
}
|
}
|
||||||
if let Some(session) = ctx.session.as_mut() {
|
if let Some(session) = ctx.session.as_mut() {
|
||||||
session.set_autonaming(false);
|
session.set_autonaming(false);
|
||||||
@@ -860,10 +867,10 @@ async fn ask(
|
|||||||
|
|
||||||
let app = Arc::clone(&ctx.app.config);
|
let app = Arc::clone(&ctx.app.config);
|
||||||
|
|
||||||
if crate::graph::active_agent_graph_name(ctx).is_some() {
|
if graph::active_agent_graph_name(ctx).is_some() {
|
||||||
ctx.before_chat_completion(&input)?;
|
ctx.before_chat_completion(&input)?;
|
||||||
let output =
|
let output =
|
||||||
crate::graph::run_active_agent_graph(ctx, &input.text(), abort_signal.clone()).await?;
|
graph::run_active_agent_graph(ctx, &input.text(), abort_signal.clone()).await?;
|
||||||
app.print_markdown(&output)?;
|
app.print_markdown(&output)?;
|
||||||
ctx.after_chat_completion(app.as_ref(), &input, &output, &[])?;
|
ctx.after_chat_completion(app.as_ref(), &input, &output, &[])?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -937,7 +944,7 @@ async fn ask(
|
|||||||
};
|
};
|
||||||
eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),);
|
eprintln!("\n📢 {}", color.italic().paint("Autonaming the session."),);
|
||||||
if let Err(err) = ctx.autoname_session(app.as_ref()).await {
|
if let Err(err) = ctx.autoname_session(app.as_ref()).await {
|
||||||
log::warn!("Failed to autonaming the session: {err}");
|
warn!("Failed to autonaming the session: {err}");
|
||||||
}
|
}
|
||||||
if let Some(session) = ctx.session.as_mut() {
|
if let Some(session) = ctx.session.as_mut() {
|
||||||
session.set_autonaming(false);
|
session.set_autonaming(false);
|
||||||
@@ -964,7 +971,7 @@ async fn ask(
|
|||||||
eprintln!("\n📢 {}", color.italic().paint("Compressing the session."),);
|
eprintln!("\n📢 {}", color.italic().paint("Compressing the session."),);
|
||||||
|
|
||||||
if let Err(err) = ctx.compress_session().await {
|
if let Err(err) = ctx.compress_session().await {
|
||||||
log::warn!("Failed to compress the session: {err}");
|
warn!("Failed to compress the session: {err}");
|
||||||
}
|
}
|
||||||
if let Some(session) = ctx.session.as_mut() {
|
if let Some(session) = ctx.session.as_mut() {
|
||||||
session.set_compressing(false);
|
session.set_compressing(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user