chore: use a Conversation type (#3735)
This commit is contained in:
@@ -34,7 +34,7 @@ use crate::agents::types::SessionConfig;
|
||||
use crate::agents::types::{FrontendTool, ToolResultReceiver};
|
||||
use crate::config::{Config, ExtensionConfigManager, PermissionManager};
|
||||
use crate::context_mgmt::auto_compact;
|
||||
use crate::message::{push_message, Message, ToolRequest};
|
||||
use crate::conversation::{debug_conversation_fix, fix_conversation, Conversation};
|
||||
use crate::permission::permission_judge::{check_tool_permissions, PermissionCheckResult};
|
||||
use crate::permission::PermissionConfirmation;
|
||||
use crate::providers::base::Provider;
|
||||
@@ -56,13 +56,13 @@ use super::final_output_tool::FinalOutputTool;
|
||||
use super::platform_tools;
|
||||
use super::tool_execution::{ToolCallResult, CHAT_MODE_TOOL_SKIPPED_RESPONSE, DECLINED_RESPONSE};
|
||||
use crate::agents::subagent_task_config::TaskConfig;
|
||||
use crate::conversation_fixer::{debug_conversation_fix, ConversationFixer};
|
||||
use crate::conversation::message::{Message, ToolRequest};
|
||||
|
||||
const DEFAULT_MAX_TURNS: u32 = 1000;
|
||||
|
||||
/// Context needed for the reply function
|
||||
pub struct ReplyContext {
|
||||
pub messages: Vec<Message>,
|
||||
pub messages: Conversation,
|
||||
pub tools: Vec<Tool>,
|
||||
pub toolshim_tools: Vec<Tool>,
|
||||
pub system_prompt: String,
|
||||
@@ -199,7 +199,7 @@ impl Agent {
|
||||
/// Handle retry logic for the agent reply loop
|
||||
async fn handle_retry_logic(
|
||||
&self,
|
||||
messages: &mut Vec<Message>,
|
||||
messages: &mut Conversation,
|
||||
session: &Option<SessionConfig>,
|
||||
initial_messages: &[Message],
|
||||
) -> Result<bool> {
|
||||
@@ -218,24 +218,29 @@ impl Agent {
|
||||
|
||||
async fn prepare_reply_context(
|
||||
&self,
|
||||
unfixed_messages: &[Message],
|
||||
unfixed_conversation: Conversation,
|
||||
session: &Option<SessionConfig>,
|
||||
) -> Result<ReplyContext> {
|
||||
let (messages, issues) = ConversationFixer::fix_conversation(Vec::from(unfixed_messages));
|
||||
let unfixed_messages = unfixed_conversation.messages().clone();
|
||||
let (conversation, issues) = fix_conversation(unfixed_conversation.clone());
|
||||
if !issues.is_empty() {
|
||||
tracing::warn!(
|
||||
"Conversation issue fixed: {}",
|
||||
debug_conversation_fix(unfixed_messages, &messages, &issues)
|
||||
debug_conversation_fix(
|
||||
unfixed_messages.as_slice(),
|
||||
conversation.messages(),
|
||||
&issues
|
||||
)
|
||||
);
|
||||
}
|
||||
let initial_messages = messages.clone();
|
||||
let initial_messages = conversation.messages().clone();
|
||||
let config = Config::global();
|
||||
|
||||
let (tools, toolshim_tools, system_prompt) = self.prepare_tools_and_prompt().await?;
|
||||
let goose_mode = Self::determine_goose_mode(session.as_ref(), config);
|
||||
|
||||
Ok(ReplyContext {
|
||||
messages,
|
||||
messages: conversation,
|
||||
tools,
|
||||
toolshim_tools,
|
||||
system_prompt,
|
||||
@@ -760,7 +765,7 @@ impl Agent {
|
||||
&self,
|
||||
messages: &[Message],
|
||||
session: &Option<SessionConfig>,
|
||||
) -> Result<Option<(Vec<Message>, String)>> {
|
||||
) -> Result<Option<(Conversation, String)>> {
|
||||
// Try to get session metadata for more accurate token counts
|
||||
let session_metadata = if let Some(session_config) = session {
|
||||
match session::storage::get_path(session_config.id.clone()) {
|
||||
@@ -802,22 +807,22 @@ impl Agent {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[instrument(skip(self, unfixed_messages, session), fields(user_message))]
|
||||
#[instrument(skip(self, unfixed_conversation, session), fields(user_message))]
|
||||
pub async fn reply(
|
||||
&self,
|
||||
unfixed_messages: &[Message],
|
||||
unfixed_conversation: Conversation,
|
||||
session: Option<SessionConfig>,
|
||||
cancel_token: Option<CancellationToken>,
|
||||
) -> Result<BoxStream<'_, Result<AgentEvent>>> {
|
||||
// Handle auto-compaction before processing
|
||||
let (messages, compaction_msg) = match self
|
||||
.handle_auto_compaction(unfixed_messages, &session)
|
||||
.handle_auto_compaction(unfixed_conversation.messages(), &session)
|
||||
.await?
|
||||
{
|
||||
Some((compacted_messages, msg)) => (compacted_messages, Some(msg)),
|
||||
None => {
|
||||
let context = self
|
||||
.prepare_reply_context(unfixed_messages, &session)
|
||||
.prepare_reply_context(unfixed_conversation, &session)
|
||||
.await?;
|
||||
(context.messages, None)
|
||||
}
|
||||
@@ -827,10 +832,10 @@ impl Agent {
|
||||
if let Some(compaction_msg) = compaction_msg {
|
||||
return Ok(Box::pin(async_stream::try_stream! {
|
||||
yield AgentEvent::Message(Message::assistant().with_text(compaction_msg));
|
||||
yield AgentEvent::HistoryReplaced(messages.clone());
|
||||
yield AgentEvent::HistoryReplaced(messages.messages().clone());
|
||||
|
||||
// Continue with normal reply processing using compacted messages
|
||||
let mut reply_stream = self.reply_internal(&messages, session, cancel_token).await?;
|
||||
let mut reply_stream = self.reply_internal(messages, session, cancel_token).await?;
|
||||
while let Some(event) = reply_stream.next().await {
|
||||
yield event?;
|
||||
}
|
||||
@@ -838,13 +843,13 @@ impl Agent {
|
||||
}
|
||||
|
||||
// No compaction needed, proceed with normal processing
|
||||
self.reply_internal(&messages, session, cancel_token).await
|
||||
self.reply_internal(messages, session, cancel_token).await
|
||||
}
|
||||
|
||||
/// Main reply method that handles the actual agent processing
|
||||
async fn reply_internal(
|
||||
&self,
|
||||
messages: &[Message],
|
||||
messages: Conversation,
|
||||
session: Option<SessionConfig>,
|
||||
cancel_token: Option<CancellationToken>,
|
||||
) -> Result<BoxStream<'_, Result<AgentEvent>>> {
|
||||
@@ -905,7 +910,7 @@ impl Agent {
|
||||
let mut stream = Self::stream_response_from_provider(
|
||||
self.provider().await?,
|
||||
&system_prompt,
|
||||
&messages,
|
||||
messages.messages(),
|
||||
&tools,
|
||||
&toolshim_tools,
|
||||
).await?;
|
||||
@@ -1074,8 +1079,8 @@ impl Agent {
|
||||
yield AgentEvent::Message(final_message_tool_resp.clone());
|
||||
|
||||
added_message = true;
|
||||
push_message(&mut messages_to_add, response);
|
||||
push_message(&mut messages_to_add, final_message_tool_resp);
|
||||
messages_to_add.push(response);
|
||||
messages_to_add.push(final_message_tool_resp);
|
||||
}
|
||||
}
|
||||
Err(ProviderError::ContextLengthExceeded(_)) => {
|
||||
@@ -1244,7 +1249,7 @@ impl Agent {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_recipe(&self, mut messages: Vec<Message>) -> Result<Recipe> {
|
||||
pub async fn create_recipe(&self, mut messages: Conversation) -> Result<Recipe> {
|
||||
let extension_manager = self.extension_manager.read().await;
|
||||
let extensions_info = extension_manager.get_extensions_info().await;
|
||||
|
||||
@@ -1273,7 +1278,7 @@ impl Agent {
|
||||
.await
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.complete(&system_prompt, &messages, &tools)
|
||||
.complete(&system_prompt, messages.messages(), &tools)
|
||||
.await?;
|
||||
|
||||
let content = result.as_concat_text();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::Ok;
|
||||
|
||||
use crate::message::Message;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::conversation::Conversation;
|
||||
use crate::token_counter::create_async_token_counter;
|
||||
|
||||
use crate::context_mgmt::summarize::summarize_messages_async;
|
||||
@@ -14,7 +15,7 @@ impl Agent {
|
||||
pub async fn truncate_context(
|
||||
&self,
|
||||
messages: &[Message], // last message is a user msg that led to assistant message with_context_length_exceeded
|
||||
) -> Result<(Vec<Message>, Vec<usize>), anyhow::Error> {
|
||||
) -> Result<(Conversation, Vec<usize>), anyhow::Error> {
|
||||
let provider = self.provider().await?;
|
||||
let token_counter = create_async_token_counter()
|
||||
.await
|
||||
@@ -51,7 +52,7 @@ impl Agent {
|
||||
pub async fn summarize_context(
|
||||
&self,
|
||||
messages: &[Message], // last message is a user msg that led to assistant message with_context_length_exceeded
|
||||
) -> Result<(Vec<Message>, Vec<usize>), anyhow::Error> {
|
||||
) -> Result<(Conversation, Vec<usize>), anyhow::Error> {
|
||||
let provider = self.provider().await?;
|
||||
let token_counter = create_async_token_counter()
|
||||
.await
|
||||
|
||||
@@ -5,8 +5,10 @@ use std::sync::Arc;
|
||||
use async_stream::try_stream;
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use super::super::agents::Agent;
|
||||
use crate::agents::router_tool_selector::RouterToolSelectionStrategy;
|
||||
use crate::message::{Message, MessageContent, ToolRequest};
|
||||
use crate::conversation::message::{Message, MessageContent, ToolRequest};
|
||||
use crate::conversation::Conversation;
|
||||
use crate::providers::base::{stream_from_single_message, MessageStream, Provider, ProviderUsage};
|
||||
use crate::providers::errors::ProviderError;
|
||||
use crate::providers::toolshim::{
|
||||
@@ -16,8 +18,6 @@ use crate::providers::toolshim::{
|
||||
use crate::session;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
use super::super::agents::Agent;
|
||||
|
||||
async fn toolshim_postprocess(
|
||||
response: Message,
|
||||
toolshim_tools: &[Tool],
|
||||
@@ -127,12 +127,12 @@ impl Agent {
|
||||
let messages_for_provider = if config.toolshim {
|
||||
convert_tool_messages_to_text(messages)
|
||||
} else {
|
||||
messages.to_vec()
|
||||
Conversation::new_unvalidated(messages.to_vec())
|
||||
};
|
||||
|
||||
// Call the provider to get a response
|
||||
let (mut response, usage) = provider
|
||||
.complete(system_prompt, &messages_for_provider, tools)
|
||||
.complete(system_prompt, messages_for_provider.messages(), tools)
|
||||
.await?;
|
||||
|
||||
crate::providers::base::set_current_model(&usage.model);
|
||||
@@ -159,7 +159,7 @@ impl Agent {
|
||||
let messages_for_provider = if config.toolshim {
|
||||
convert_tool_messages_to_text(messages)
|
||||
} else {
|
||||
messages.to_vec()
|
||||
Conversation::new_unvalidated(messages.to_vec())
|
||||
};
|
||||
|
||||
// Clone owned data to move into the async stream
|
||||
@@ -170,11 +170,19 @@ impl Agent {
|
||||
|
||||
let mut stream = if provider.supports_streaming() {
|
||||
provider
|
||||
.stream(system_prompt.as_str(), &messages_for_provider, &tools)
|
||||
.stream(
|
||||
system_prompt.as_str(),
|
||||
messages_for_provider.messages(),
|
||||
&tools,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
let (message, usage) = provider
|
||||
.complete(system_prompt.as_str(), &messages_for_provider, &tools)
|
||||
.complete(
|
||||
system_prompt.as_str(),
|
||||
messages_for_provider.messages(),
|
||||
&tools,
|
||||
)
|
||||
.await?;
|
||||
stream_from_single_message(message, usage)
|
||||
};
|
||||
|
||||
@@ -11,7 +11,8 @@ use crate::agents::types::{
|
||||
RetryConfig, SuccessCheck, DEFAULT_ON_FAILURE_TIMEOUT_SECONDS, DEFAULT_RETRY_TIMEOUT_SECONDS,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::message::Message;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::conversation::Conversation;
|
||||
use crate::tool_monitor::ToolMonitor;
|
||||
|
||||
/// Result of a retry logic evaluation
|
||||
@@ -92,12 +93,11 @@ impl RetryManager {
|
||||
|
||||
/// Reset status for retry: clear message history and final output tool state
|
||||
async fn reset_status_for_retry(
|
||||
messages: &mut Vec<Message>,
|
||||
messages: &mut Conversation,
|
||||
initial_messages: &[Message],
|
||||
final_output_tool: &Arc<Mutex<Option<crate::agents::final_output_tool::FinalOutputTool>>>,
|
||||
) {
|
||||
messages.clear();
|
||||
messages.extend_from_slice(initial_messages);
|
||||
*messages = Conversation::new_unvalidated(initial_messages.to_vec());
|
||||
info!("Reset message history to initial state for retry");
|
||||
|
||||
if let Some(final_output_tool) = final_output_tool.lock().await.as_mut() {
|
||||
@@ -109,7 +109,7 @@ impl RetryManager {
|
||||
/// Handle retry logic for the agent reply loop
|
||||
pub async fn handle_retry_logic(
|
||||
&self,
|
||||
messages: &mut Vec<Message>,
|
||||
messages: &mut Conversation,
|
||||
session: &Option<SessionConfig>,
|
||||
initial_messages: &[Message],
|
||||
final_output_tool: &Arc<Mutex<Option<crate::agents::final_output_tool::FinalOutputTool>>>,
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::agents::tool_vectordb::ToolVectorDB;
|
||||
use crate::message::Message;
|
||||
use crate::conversation::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::{self, base::Provider};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
agents::extension::ExtensionConfig,
|
||||
agents::{extension_manager::ExtensionManager, Agent, TaskConfig},
|
||||
config::ExtensionConfigManager,
|
||||
message::{Message, MessageContent, ToolRequest},
|
||||
prompt_template::render_global_file,
|
||||
providers::errors::ProviderError,
|
||||
};
|
||||
@@ -13,6 +12,8 @@ use mcp_core::handler::ToolError;
|
||||
use rmcp::model::Tool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use serde_json::{self};
|
||||
use crate::conversation::message::{Message, MessageContent, ToolRequest};
|
||||
use crate::conversation::Conversation;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -41,7 +42,7 @@ pub struct SubAgentProgress {
|
||||
/// A specialized agent that can handle specific tasks independently
|
||||
pub struct SubAgent {
|
||||
pub id: String,
|
||||
pub conversation: Arc<Mutex<Vec<Message>>>,
|
||||
pub conversation: Arc<Mutex<Conversation>>,
|
||||
pub status: Arc<RwLock<SubAgentStatus>>,
|
||||
pub config: TaskConfig,
|
||||
pub turn_count: Arc<Mutex<usize>>,
|
||||
@@ -80,7 +81,7 @@ impl SubAgent {
|
||||
|
||||
let subagent = Arc::new(SubAgent {
|
||||
id: task_config.id.clone(),
|
||||
conversation: Arc::new(Mutex::new(Vec::new())),
|
||||
conversation: Arc::new(Mutex::new(Conversation::new_unvalidated(Vec::new()))),
|
||||
status: Arc::new(RwLock::new(SubAgentStatus::Ready)),
|
||||
config: task_config,
|
||||
turn_count: Arc::new(Mutex::new(0)),
|
||||
@@ -107,7 +108,7 @@ impl SubAgent {
|
||||
&self,
|
||||
message: String,
|
||||
task_config: TaskConfig,
|
||||
) -> Result<Vec<Message>, anyhow::Error> {
|
||||
) -> Result<Conversation, anyhow::Error> {
|
||||
debug!("Processing message for subagent {}", self.id);
|
||||
|
||||
// Get provider from task config
|
||||
@@ -128,7 +129,10 @@ impl SubAgent {
|
||||
}
|
||||
|
||||
// Get the current conversation for context
|
||||
let mut messages = self.get_conversation().await;
|
||||
let mut messages = {
|
||||
let conversation = self.conversation.lock().await;
|
||||
conversation.clone()
|
||||
};
|
||||
|
||||
// Get tools from the subagent's own extension manager
|
||||
let tools: Vec<Tool> = self
|
||||
@@ -156,7 +160,7 @@ impl SubAgent {
|
||||
match Agent::generate_response_from_provider(
|
||||
Arc::clone(provider),
|
||||
&system_prompt,
|
||||
&messages,
|
||||
messages.messages(),
|
||||
&tools,
|
||||
&toolshim_tools,
|
||||
)
|
||||
@@ -264,11 +268,6 @@ impl SubAgent {
|
||||
conversation.push(message);
|
||||
}
|
||||
|
||||
/// Get the full conversation history
|
||||
async fn get_conversation(&self) -> Vec<Message> {
|
||||
self.conversation.lock().await.clone()
|
||||
}
|
||||
|
||||
/// Build the system prompt for the subagent using the template
|
||||
async fn build_system_prompt(&self, available_tools: &[Tool]) -> Result<String, anyhow::Error> {
|
||||
let mut context = HashMap::new();
|
||||
|
||||
@@ -24,10 +24,10 @@ pub async fn run_complete_subagent_task(
|
||||
.flat_map(|message| {
|
||||
message.content.iter().filter_map(|content| {
|
||||
match content {
|
||||
crate::message::MessageContent::Text(text_content) => {
|
||||
crate::conversation::message::MessageContent::Text(text_content) => {
|
||||
Some(text_content.text.clone())
|
||||
}
|
||||
crate::message::MessageContent::ToolResponse(tool_response) => {
|
||||
crate::conversation::message::MessageContent::ToolResponse(tool_response) => {
|
||||
// Extract text from tool response
|
||||
if let Ok(contents) = &tool_response.tool_result {
|
||||
let texts: Vec<String> = contents
|
||||
|
||||
@@ -9,7 +9,6 @@ use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::config::permission::PermissionLevel;
|
||||
use crate::config::PermissionManager;
|
||||
use crate::message::{Message, ToolRequest};
|
||||
use crate::permission::Permission;
|
||||
use mcp_core::ToolResult;
|
||||
use rmcp::model::{Content, ServerNotification};
|
||||
@@ -32,6 +31,7 @@ impl From<ToolResult<Vec<Content>>> for ToolCallResult {
|
||||
|
||||
use super::agent::{tool_stream, ToolStream};
|
||||
use crate::agents::Agent;
|
||||
use crate::conversation::message::{Message, ToolRequest};
|
||||
|
||||
pub const DECLINED_RESPONSE: &str = "The user has declined to run this tool. \
|
||||
DO NOT attempt to call this tool again. \
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::agents::tool_execution::ToolCallResult;
|
||||
use crate::agents::tool_router_index_manager::ToolRouterIndexManager;
|
||||
use crate::agents::tool_vectordb::generate_table_id;
|
||||
use crate::config::Config;
|
||||
use crate::message::ToolRequest;
|
||||
use crate::conversation::message::ToolRequest;
|
||||
use crate::providers::base::Provider;
|
||||
use anyhow::{anyhow, Result};
|
||||
use mcp_core::ToolError;
|
||||
|
||||
Reference in New Issue
Block a user