move context to tool own. fix memory not update.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use serde_json::Value;
|
||||
use tracing::log::info;
|
||||
use module_editor_agent::agent::agent::{Agent, LlmApiAdaptor};
|
||||
use module_editor_agent::agent::agent_builder::AgentBuilder;
|
||||
use module_editor_agent::agent::error::PromptError;
|
||||
@@ -6,16 +7,19 @@ use module_editor_agent::agent::hook::Hook;
|
||||
use module_editor_agent::agent::memory::AgentMemory;
|
||||
use module_editor_agent::agent::run::ToolCallFlow;
|
||||
use module_editor_agent::agent::tool::{Tool, ToolCall, ToolDyn};
|
||||
use platform_llm::{LlmClient, LlmMessage};
|
||||
use platform_llm::{LlmClient, LlmMessage, LlmMessageRole};
|
||||
|
||||
pub(crate) struct LlmCompletionModel {
|
||||
client: LlmClient,
|
||||
}
|
||||
|
||||
impl LlmApiAdaptor<LlmMessage> for LlmCompletionModel {
|
||||
async fn complete(&self, messages: &[LlmMessage]) -> Result<String, PromptError> {
|
||||
async fn complete<'a>(
|
||||
&self,
|
||||
messages: impl Iterator<Item = &'a LlmMessage> + Send,
|
||||
) -> Result<String, PromptError> {
|
||||
use platform_llm::LlmTextRequest;
|
||||
let request = LlmTextRequest::new(messages.to_vec()).with_request_timeout_ms(30_000);
|
||||
let request = LlmTextRequest::new(messages.cloned().collect()).with_request_timeout_ms(30_000);
|
||||
let response = self
|
||||
.client
|
||||
.request_text(request)
|
||||
@@ -40,7 +44,6 @@ pub struct LlmChatAgentBuilder {
|
||||
hooks: Vec<Box<dyn Hook>>,
|
||||
max_turns: usize,
|
||||
memory_data: Option<Box<dyn AgentMemory<LlmMessage>>>,
|
||||
context: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
@@ -54,7 +57,6 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
hooks: Vec::new(),
|
||||
max_turns: 10,
|
||||
memory_data: None,
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,11 +90,6 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
fn context(mut self, context: Value) -> Self {
|
||||
self.context = Some(context);
|
||||
self
|
||||
}
|
||||
|
||||
fn build(self) -> Agent<LlmCompletionModel, LlmMessage> {
|
||||
let model = LlmCompletionModel {
|
||||
client: self.client.expect("call .with_client() first"),
|
||||
@@ -113,7 +110,6 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
agent.hooks = self.hooks;
|
||||
agent.default_max_turns = self.max_turns;
|
||||
agent.memory = self.memory_data;
|
||||
agent.context = self.context;
|
||||
agent.system_prompt = Some(LlmMessage::system(&system_prompt));
|
||||
agent
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ use spacetime_client::{
|
||||
use crate::api_response::json_success_body;
|
||||
use crate::auth::AuthenticatedAccessToken;
|
||||
use crate::editor_agent::agent::LlmChatAgentBuilder;
|
||||
use crate::editor_agent::editor_tools::common::EditorToolContext;
|
||||
use crate::editor_agent::editor_tools::edit_image::{EditImageTool, EditImageToolArgs};
|
||||
use crate::editor_agent::utils::{EditorAgentMessageResponse, ImageId, ImageMetadata, build_editor_agent_canvas_completion, conversation_detail_from_record, conversation_summary_from_record, editor_agent_bad_request, empty_messages_document, ensure_editor_project_access, normalize_editor_agent_attachments, now_rfc3339, read_messages_document, require_editor_agent_sidebar_enabled, write_messages_document, IntoImageId};
|
||||
use crate::editor_project::{EditorGenerationCaller, current_utc_micros, map_editor_project_error};
|
||||
@@ -69,12 +70,22 @@ pub async fn editor_agent_message(
|
||||
let mut document: EditorAgentConversationMessagesDocument =
|
||||
read_messages_document(&state, &conversation).await?;
|
||||
|
||||
let now = now_rfc3339();
|
||||
if !attachments.is_empty() {
|
||||
let mut attachment_info = String::new();
|
||||
attachment_info.push_str("user has just uploaded attachments of the order: ");
|
||||
for a in &attachments {
|
||||
attachment_info.push_str(&format!("{} ,", a.clone().into_image_id()))
|
||||
}
|
||||
document.messages.push(EditorAgentMessage{
|
||||
id: 0,
|
||||
role: EditorAgentMessageRole::System,
|
||||
text: attachment_info,
|
||||
attachments:Vec::new(),
|
||||
tool_call: None,
|
||||
created_at: now.clone(),
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Build conversation history as LlmMessage vec
|
||||
@@ -90,7 +101,6 @@ pub async fn editor_agent_message(
|
||||
|
||||
// Save user message to document
|
||||
let was_empty = document.messages.is_empty();
|
||||
let user_now = now_rfc3339();
|
||||
// TODO tell agent info about attachments
|
||||
let user_message = EditorAgentMessage {
|
||||
id: document.messages.len(),
|
||||
@@ -98,7 +108,7 @@ pub async fn editor_agent_message(
|
||||
text: payload.text.trim().to_string(),
|
||||
attachments,
|
||||
tool_call: None,
|
||||
created_at: user_now,
|
||||
created_at: now,
|
||||
};
|
||||
document.messages.push(user_message.clone());
|
||||
write_messages_document(&state, &conversation, &document).await?;
|
||||
@@ -118,7 +128,7 @@ pub async fn editor_agent_message(
|
||||
}
|
||||
|
||||
// Build tool context from document
|
||||
let tool_context_value = build_tool_context(&document);
|
||||
let tool_context = build_tool_context(&document);
|
||||
|
||||
// Build and run agent
|
||||
let llm_client = state.llm_client().ok_or_else(|| {
|
||||
@@ -129,15 +139,14 @@ pub async fn editor_agent_message(
|
||||
|
||||
let memory = VecMemory::new(previous_messages);
|
||||
|
||||
let agent = LlmChatAgentBuilder::new()
|
||||
let mut agent = LlmChatAgentBuilder::new()
|
||||
.with_client(llm_client)
|
||||
.tool(EditImageTool {})
|
||||
.tool(EditImageTool { context: tool_context })
|
||||
.max_turns(3)
|
||||
.memory(memory)
|
||||
.context(tool_context_value)
|
||||
.build();
|
||||
|
||||
let agent_result = agent.prompt(LlmMessage::user("")).await;
|
||||
let agent_result = agent.prompt(LlmMessage::user(user_message.text)).await;
|
||||
|
||||
let assistant_now = now_rfc3339();
|
||||
|
||||
@@ -205,7 +214,7 @@ fn build_delta_messages(
|
||||
Ok(messages)
|
||||
}
|
||||
|
||||
fn build_tool_context(document: &EditorAgentConversationMessagesDocument) -> Value {
|
||||
fn build_tool_context(document: &EditorAgentConversationMessagesDocument) -> EditorToolContext {
|
||||
let mut images: HashMap<ImageId, ImageMetadata> = HashMap::new();
|
||||
|
||||
for msg in document.messages.iter().rev() {
|
||||
@@ -223,7 +232,7 @@ fn build_tool_context(document: &EditorAgentConversationMessagesDocument) -> Val
|
||||
}
|
||||
}
|
||||
|
||||
json!({ "images": images })
|
||||
EditorToolContext { images }
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -508,7 +517,8 @@ pub async fn confirm_editor_agent_tool_call(
|
||||
build_editor_agent_canvas_completion(&project, "edit-image", &title);
|
||||
|
||||
// Execute the real generation
|
||||
let edit_tool = EditImageTool {};
|
||||
let tool_context = build_tool_context(&document);
|
||||
let edit_tool = EditImageTool { context: tool_context };
|
||||
let result = edit_tool
|
||||
.execute(
|
||||
&state,
|
||||
|
||||
@@ -12,18 +12,18 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use shared_contracts::api::ApiSuccessEnvelope;
|
||||
use shared_contracts::assets::EditorCanvasGenerationCompletionPayload;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub struct EditImageTool {}
|
||||
pub struct EditImageTool {
|
||||
pub context: EditorToolContext,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EditImageError {
|
||||
ObjectImageNotProvided,
|
||||
PromptNotProvided,
|
||||
AssetNotFound(ImageId),
|
||||
ContextParseError(String),
|
||||
}
|
||||
|
||||
impl Display for EditImageError {
|
||||
@@ -36,9 +36,6 @@ impl Display for EditImageError {
|
||||
EditImageError::AssetNotFound(image_id) => {
|
||||
write!(f, "asset {image_id} not found in context")
|
||||
}
|
||||
EditImageError::ContextParseError(msg) => {
|
||||
write!(f, "failed to parse tool context: {msg}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,10 +98,9 @@ impl Tool for EditImageTool {
|
||||
fn call(
|
||||
&self,
|
||||
args: Self::Args,
|
||||
context: Value,
|
||||
) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send {
|
||||
async move {
|
||||
Self::validate_context_images(&args, &context)?;
|
||||
self.validate_context_images(&args)?;
|
||||
if let Some(error) = Self::validate_args(&args) {
|
||||
return Err(error);
|
||||
}
|
||||
@@ -116,9 +112,6 @@ impl Tool for EditImageTool {
|
||||
|
||||
fn classify_error(&self, error: &Self::Error) -> ToolFailure {
|
||||
match error {
|
||||
EditImageError::ContextParseError(_) => {
|
||||
ToolFailure::new(ToolFailureKind::Internal, error.to_string())
|
||||
}
|
||||
EditImageError::AssetNotFound(_) => {
|
||||
ToolFailure::new(ToolFailureKind::NotFound, error.to_string())
|
||||
}
|
||||
@@ -163,18 +156,15 @@ impl EditImageTool {
|
||||
|
||||
/// Validate that all referenced images exist in the context.
|
||||
fn validate_context_images(
|
||||
&self,
|
||||
args: &EditImageToolArgs,
|
||||
context: &Value,
|
||||
) -> Result<(), EditImageError> {
|
||||
let tool_context: EditorToolContext = serde_json::from_value(context.clone())
|
||||
.map_err(|e| EditImageError::ContextParseError(e.to_string()))?;
|
||||
|
||||
if !tool_context.contains_image(&args.object_image_id) {
|
||||
if !self.context.contains_image(&args.object_image_id) {
|
||||
return Err(EditImageError::AssetNotFound(args.object_image_id.clone()));
|
||||
}
|
||||
|
||||
for ref_id in &args.reference_image_ids {
|
||||
if !tool_context.contains_image(ref_id) {
|
||||
if !self.context.contains_image(ref_id) {
|
||||
return Err(EditImageError::AssetNotFound(ref_id.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
pub mod common;
|
||||
pub mod edit_image;
|
||||
mod generate_ui_design;
|
||||
mod generate_character;
|
||||
mod generate_image;
|
||||
mod generate_icon_spritesheet;
|
||||
mod generate_sound_effect;
|
||||
mod generate_background_music;
|
||||
mod generate_video;
|
||||
|
||||
@@ -11,7 +11,6 @@ pub struct Agent<M: LlmApiAdaptor<Message>, Message> {
|
||||
pub default_max_turns: usize,
|
||||
pub system_prompt: Option<Message>,
|
||||
pub memory: Option<Box<dyn AgentMemory<Message>>>,
|
||||
pub context: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl<M, Message> Agent<M, Message>
|
||||
@@ -27,7 +26,6 @@ where
|
||||
default_max_turns: 10,
|
||||
system_prompt: None,
|
||||
memory: None,
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +59,7 @@ where
|
||||
}
|
||||
|
||||
|
||||
pub fn prompt(&self, message: impl Into<Message> + Send) -> PromptRequest<'_, M, Message>
|
||||
pub fn prompt(&mut self, message: impl Into<Message> + Send) -> PromptRequest<'_, M, Message>
|
||||
where
|
||||
Message: 'static,
|
||||
{
|
||||
@@ -70,96 +68,13 @@ where
|
||||
}
|
||||
|
||||
pub trait LlmApiAdaptor<Message>: Send + Sync {
|
||||
fn complete(
|
||||
fn complete<'a>(
|
||||
&self,
|
||||
messages: &[Message],
|
||||
) -> impl Future<Output = Result<String, PromptError>> + Send;
|
||||
messages: impl Iterator<Item = &'a Message> + Send,
|
||||
) -> impl Future<Output = Result<String, PromptError>> + Send
|
||||
where
|
||||
Message: 'a;
|
||||
fn tool_result_message(&self, tool_name: &str, output: &str) -> Message;
|
||||
|
||||
fn build_assistant_message(&self, text: &str) -> Message;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::agent::tool::{ToolFailure, ToolFailureKind};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
struct TestModel;
|
||||
|
||||
impl LlmApiAdaptor<String> for TestModel {
|
||||
async fn complete(&self, _messages: &[String]) -> Result<String, PromptError> {
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn tool_result_message(&self, tool_name: &str, output: &str) -> String {
|
||||
format!("{tool_name}: {output}")
|
||||
}
|
||||
|
||||
fn build_assistant_message(&self, text: &str) -> String {
|
||||
text.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TestToolError {
|
||||
Recoverable,
|
||||
Fatal,
|
||||
}
|
||||
|
||||
impl Display for TestToolError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Recoverable => write!(f, "recoverable failure"),
|
||||
Self::Fatal => write!(f, "fatal failure"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TestToolError {}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TestArgs {
|
||||
fatal: bool,
|
||||
}
|
||||
|
||||
struct FailingTool;
|
||||
|
||||
impl Tool for FailingTool {
|
||||
const NAME: &'static str = "fail";
|
||||
type Error = TestToolError;
|
||||
type Args = TestArgs;
|
||||
type Output = ();
|
||||
|
||||
fn description(&self) -> String {
|
||||
"test failing tool".to_string()
|
||||
}
|
||||
|
||||
fn parameters(&self) -> serde_json::Value {
|
||||
json!({"type": "object"})
|
||||
}
|
||||
|
||||
async fn call(
|
||||
&self,
|
||||
args: Self::Args,
|
||||
_context: serde_json::Value,
|
||||
) -> Result<Self::Output, Self::Error> {
|
||||
if args.fatal {
|
||||
Err(TestToolError::Fatal)
|
||||
} else {
|
||||
Err(TestToolError::Recoverable)
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_error(&self, error: &Self::Error) -> ToolFailure {
|
||||
match error {
|
||||
TestToolError::Recoverable => ToolFailure::other(error.to_string()),
|
||||
TestToolError::Fatal => {
|
||||
ToolFailure::new(ToolFailureKind::Internal, error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,5 @@ pub trait AgentBuilder<Message, M: LlmApiAdaptor<Message>> {
|
||||
fn max_turns(self, n: usize) -> Self;
|
||||
fn memory(self, memory: impl AgentMemory<Message> + 'static) -> Self;
|
||||
|
||||
fn context(self, context: serde_json::Value) -> Self;
|
||||
fn build(self) -> Agent<M, Message>;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::agent::agent::Agent;
|
||||
use crate::agent::agent::LlmApiAdaptor;
|
||||
use crate::agent::error::PromptError;
|
||||
use crate::agent::hook::Hook;
|
||||
use crate::agent::memory::{AgentMemory, VecMemory};
|
||||
use crate::agent::memory::VecMemory;
|
||||
use crate::agent::run::PromptOutput::{Text, Tool};
|
||||
use crate::agent::tool::{ToolCall, ToolDyn, ToolExecutionResult, ToolFailure, ToolOutcome};
|
||||
use serde::Deserialize;
|
||||
@@ -36,12 +36,11 @@ pub enum ToolCallFlow {
|
||||
}
|
||||
|
||||
pub struct PromptRequest<'a, M: LlmApiAdaptor<Message> + 'a, Message: 'a> {
|
||||
agent: &'a Agent<M, Message>,
|
||||
agent: &'a mut Agent<M, Message>,
|
||||
message: Message,
|
||||
system_prompt: Option<Message>,
|
||||
hooks: Vec<Box<dyn Hook + 'static>>,
|
||||
max_turns: usize,
|
||||
context: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl<'a, M, Message> PromptRequest<'a, M, Message>
|
||||
@@ -49,14 +48,14 @@ where
|
||||
M: LlmApiAdaptor<Message> + 'a,
|
||||
Message: 'a,
|
||||
{
|
||||
pub fn new(agent: &'a Agent<M, Message>, message: Message) -> Self {
|
||||
pub fn new(agent: &'a mut Agent<M, Message>, message: Message) -> Self {
|
||||
let max_turns = agent.default_max_turns;
|
||||
Self {
|
||||
agent,
|
||||
message,
|
||||
system_prompt: None,
|
||||
hooks: Vec::new(),
|
||||
max_turns: agent.default_max_turns,
|
||||
context: agent.context.clone(),
|
||||
max_turns,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +78,7 @@ where
|
||||
impl<'a, M, Message> IntoFuture for PromptRequest<'a, M, Message>
|
||||
where
|
||||
M: LlmApiAdaptor<Message> + Send + Sync + 'a,
|
||||
Message: Send + Sync + Clone + 'a,
|
||||
Message: Send + Sync + Clone + 'a + 'static,
|
||||
{
|
||||
type Output = Result<Vec<PromptOutput>, PromptError>;
|
||||
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
|
||||
@@ -91,32 +90,28 @@ where
|
||||
let max_turns = self.max_turns;
|
||||
|
||||
Box::pin(async move {
|
||||
let mut memory: Box<dyn AgentMemory<Message>> = match &agent.memory {
|
||||
Some(m) => {
|
||||
let msgs: Vec<Message> = m.get_memory().iter().cloned().collect();
|
||||
Box::new(VecMemory::new(msgs))
|
||||
}
|
||||
None => Box::new(VecMemory::new(Vec::new())),
|
||||
};
|
||||
let memory = agent
|
||||
.memory
|
||||
.get_or_insert_with(|| Box::new(VecMemory::new(Vec::new())));
|
||||
memory.append_message(message);
|
||||
let mut prompt_result: Vec<PromptOutput> = Vec::new();
|
||||
|
||||
let mut history: Vec<Message> = Vec::new();
|
||||
if let Some(ref sp) = agent.system_prompt {
|
||||
history.push(sp.clone());
|
||||
}
|
||||
history.extend(memory.get_memory().iter().cloned());
|
||||
history.push(message);
|
||||
for _ in 0..max_turns {
|
||||
// TODO perf issue for copy cost
|
||||
let text = agent.model.complete(&history).await?;
|
||||
let text = {
|
||||
let messages = agent
|
||||
.system_prompt
|
||||
.iter()
|
||||
.chain(memory.get_memory().iter());
|
||||
agent.model.complete(messages).await?
|
||||
};
|
||||
|
||||
// Try to parse the LLM reply as JSON (handle Markdown fences)
|
||||
let cleaned = clean_json_response(&text);
|
||||
|
||||
match serde_json::from_str::<LlmJsonResponse>(&cleaned) {
|
||||
Ok(json_resp) => {
|
||||
// Check for <end/> marker — stop the _turn immediately
|
||||
let reply_has_end = json_resp.reply_text.contains("<end/>");
|
||||
// end cond: <end/> and no tool_calls
|
||||
let reply_has_end = json_resp.reply_text.contains("<end/>")
|
||||
&& json_resp.tool_calls.is_empty();
|
||||
let clean_text = json_resp
|
||||
.reply_text
|
||||
.replace("<end/>", "")
|
||||
@@ -152,10 +147,6 @@ where
|
||||
})
|
||||
.collect();
|
||||
|
||||
if tool_calls.is_empty() {
|
||||
return Ok(vec![Text(clean_text)]);
|
||||
}
|
||||
|
||||
for (tc_id, tc) in tool_calls.iter().enumerate() {
|
||||
// inline run_hooks: before_tool_call hook
|
||||
let mut should_skip = false;
|
||||
@@ -185,12 +176,9 @@ where
|
||||
let tools: Vec<&Box<dyn ToolDyn>> = agent.tools.iter().collect();
|
||||
let name = tc.name.clone();
|
||||
let args = tc.args.clone();
|
||||
let context = self.context.clone().unwrap_or_default();
|
||||
let fut = async move {
|
||||
match tools.iter().find(|tool| tool.tool_name() == name) {
|
||||
Some(tool) => {
|
||||
tool.call_with_context(args, context.clone()).await
|
||||
}
|
||||
Some(tool) => tool.call(args).await,
|
||||
None => ToolExecutionResult::failed(
|
||||
Value::Null,
|
||||
ToolFailure::invalid_args(format!(
|
||||
|
||||
@@ -138,7 +138,6 @@ pub trait Tool: Sized {
|
||||
fn call(
|
||||
&self,
|
||||
args: Self::Args,
|
||||
context: serde_json::Value,
|
||||
) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send;
|
||||
|
||||
fn classify_error(&self, error: &Self::Error) -> ToolFailure {
|
||||
@@ -151,10 +150,9 @@ pub trait ToolDyn: Send + Sync {
|
||||
fn tool_name(&self) -> &'static str;
|
||||
fn description(&self) -> String;
|
||||
fn parameters(&self) -> serde_json::Value;
|
||||
fn call_with_context(
|
||||
fn call(
|
||||
&self,
|
||||
args: serde_json::Value,
|
||||
_context: serde_json::Value,
|
||||
) -> Pin<Box<dyn Future<Output = ToolExecutionResult> + Send + '_>>;
|
||||
}
|
||||
|
||||
@@ -171,10 +169,9 @@ impl<T: Tool + Send + Sync> ToolDyn for T {
|
||||
self.parameters()
|
||||
}
|
||||
|
||||
fn call_with_context(
|
||||
fn call(
|
||||
&self,
|
||||
args: serde_json::Value,
|
||||
context: serde_json::Value,
|
||||
) -> Pin<Box<dyn Future<Output = ToolExecutionResult> + Send + '_>> {
|
||||
Box::pin(async move {
|
||||
let parsed: T::Args = match serde_json::from_value(args) {
|
||||
@@ -187,7 +184,7 @@ impl<T: Tool + Send + Sync> ToolDyn for T {
|
||||
}
|
||||
};
|
||||
|
||||
let output = match self.call(parsed, context).await {
|
||||
let output = match self.call(parsed).await {
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
return ToolExecutionResult::failed(
|
||||
|
||||
Reference in New Issue
Block a user