diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/common.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/common.rs index 9a2939460..0bc8556db 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/common.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/common.rs @@ -4,6 +4,9 @@ use crate::openai_image_generation::GPT_IMAGE_2_MODEL; use module_editor_agent::agent::tool::Tool; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use axum::http::StatusCode; +use serde_json::json; +use crate::http_error::AppError; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EditorToolContext { @@ -44,3 +47,10 @@ pub(crate) fn editor_agent_image_mud_points( }; pricing.image_generation_mud_points(kind, Some(GPT_IMAGE_2_MODEL), Some(normalized_image_size)) } + +pub fn image_not_found(image_id: &ImageId) -> AppError { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-agent", + "message": format!("asset {image_id} not found in context"), + })) +} \ No newline at end of file diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/edit_image.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/edit_image.rs index c10de7498..55bde5378 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/edit_image.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/edit_image.rs @@ -1,10 +1,10 @@ use crate::editor_agent::editor_tools::common::{ - EditorAgentPricedTool, EditorToolContext, editor_agent_image_mud_points, + editor_agent_image_mud_points, EditorAgentPricedTool, EditorToolContext, }; use crate::editor_agent::utils::ImageId; use crate::editor_generation_config::EditorGenerationPricingConfig; use crate::editor_project::{ - EditorGenerationCaller, EditorImageEditRequest, edit_editor_image_for_owner, + edit_editor_image_for_owner, EditorGenerationCaller, EditorImageEditRequest, }; use crate::http_error::AppError; use crate::request_context::RequestContext; @@ -12,11 +12,12 @@ use crate::state::AppState; use axum::http::StatusCode; use module_editor_agent::agent::tool::{Tool, ToolFailure, ToolFailureKind}; use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; +use serde_json::{json, Value}; use shared_contracts::api::ApiSuccessEnvelope; use shared_contracts::assets::EditorCanvasGenerationCompletionPayload; use std::error::Error; use std::fmt::Display; +use crate::editor_agent::editor_tools::common; pub struct EditImageTool { pub context: EditorToolContext, @@ -178,7 +179,6 @@ impl EditImageTool { Ok(()) } - // TODO should share with format in run.rs pub fn format_execute_message( &self, args: ::Args, @@ -199,83 +199,5 @@ impl EditImageTool { "[tool_call:{name}] args: {arg_json} output: edited result saved as image: {image_id}" ) } - - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: EditImageToolArgs, - model: Option, - project_id: Option, - generation_inputs: Option, - asset_label: Option, - source_resource_id: Option, - canvas_completion: Option, - ) -> Result { - let source_image_src = self - .context - .image_data_key(&args.object_image_id) - .ok_or_else(|| image_not_found(&args.object_image_id))? - .to_string(); - let reference_image_srcs: Result, AppError> = args - .reference_image_ids - .into_iter() - .map(|image_id| { - self.context - .image_data_key(&image_id) - .map(ToOwned::to_owned) - .ok_or_else(|| image_not_found(&image_id)) - }) - .collect(); - let reference_image_srcs = reference_image_srcs?; - - let result = edit_editor_image_for_owner( - state, - request_context, - caller, - EditorImageEditRequest { - prompt: args.prompt, - source_image_src, - size: None, - model, - reference_image_srcs: Some(reference_image_srcs), - project_id, - asset_kind: Some("editor_agent_edit_image".to_string()), - generation_inputs, - asset_folder_id: Some("project".to_string()), - asset_label, - source_resource_id, - target_layer_id: None, - canvas_completion, - }, - ) - .await? - .0; - - let data = if request_context.wants_envelope() { - serde_json::from_value::>(result) - .map_err(|e| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ - "message": format!("failed to parse success envelope: {e}"), - })) - })? - .data - } else { - result - }; - - serde_json::from_value::(data).map_err(|e| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ - "message": format!("failed to deserialize edit image result: {e}"), - })) - }) - } } -fn image_not_found(image_id: &ImageId) -> AppError { - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "editor-agent", - "message": format!("asset {image_id} not found in context"), - })) -} diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_background_music.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_background_music.rs index 5d59b4721..62357170f 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_background_music.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_background_music.rs @@ -1,23 +1,11 @@ use crate::editor_agent::editor_tools::common::EditorAgentPricedTool; -use crate::editor_agent::editor_tools::generate_sound_effect::{ - map_media_response_error, parse_media_response, pending_message, -}; use crate::editor_generation_config::{ - EDITOR_BACKGROUND_MUSIC_MODEL_SUNO, EditorGenerationPricingConfig, + EditorGenerationPricingConfig, EDITOR_BACKGROUND_MUSIC_MODEL_SUNO, }; -use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; -use crate::vector_engine_audio_generation::generate_editor_background_music_for_owner; -use axum::Json; -use axum::http::StatusCode; use module_editor_agent::agent::tool::{Tool, ToolFailure}; use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; -use shared_contracts::assets::{ - EditorAudioGenerateResponse, EditorBackgroundMusicGenerateRequest, - EditorCanvasGenerationCompletionPayload, -}; +use serde_json::{json, Value}; +use shared_contracts::assets::EditorAudioGenerateResponse; use std::error::Error; use std::fmt::Display; @@ -74,7 +62,7 @@ impl Tool for GenerateBackgroundMusicTool { return Err(GenerateBackgroundMusicError::PromptNotProvided); } Ok(GenerateBackgroundMusicToolOutput { - message: pending_message(), + message: "this tool call is pending user confirmation. if all is pending, just end this turn".to_string(), }) } } @@ -94,41 +82,6 @@ impl EditorAgentPricedTool for GenerateBackgroundMusicTool { } impl GenerateBackgroundMusicTool { - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - owner_user_id: String, - args: GenerateBackgroundMusicToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - if args.prompt.trim().is_empty() { - return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details( - json!({ "provider": "editor-agent", "message": "背景音乐提示词不能为空" }), - )); - } - let response = generate_editor_background_music_for_owner( - state.clone(), - request_context.clone(), - owner_user_id, - Ok(Json(EditorBackgroundMusicGenerateRequest { - gpt_description_prompt: args.prompt, - make_instrumental: args.make_instrumental, - project_id: Some(project_id), - canvas_completion: Some(canvas_completion), - generation_inputs, - asset_folder_id: Some("project".to_string()), - asset_label, - })), - ) - .await - .map_err(map_media_response_error)?; - parse_media_response(request_context, response.0) - } pub fn format_execute_message( &self, args: &GenerateBackgroundMusicToolArgs, diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_character.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_character.rs index d1ad2a541..f6a0b8711 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_character.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_character.rs @@ -1,18 +1,13 @@ use crate::editor_agent::editor_tools::common::{ - EditorAgentPricedTool, EditorToolContext, editor_agent_image_mud_points, + editor_agent_image_mud_points, EditorAgentPricedTool, EditorToolContext, }; use crate::editor_agent::editor_tools::generate_image::{ - EditorImageGenerationResult, GenerateImageError, GenerateImageTool, GenerateImageToolArgs, - GenerateImageToolOutput, + EditorImageGenerationResult, GenerateImageError, GenerateImageTool, GenerateImageToolArgs, + GenerateImageToolOutput, }; use crate::editor_generation_config::EditorGenerationPricingConfig; -use crate::editor_project::EditorGenerationCaller; -use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; use module_editor_agent::agent::tool::{Tool, ToolFailure}; -use serde_json::{Value, json}; -use shared_contracts::assets::EditorCanvasGenerationCompletionPayload; +use serde_json::{json, Value}; pub struct GenerateCharacterTool { pub context: EditorToolContext, @@ -75,60 +70,23 @@ impl EditorAgentPricedTool for GenerateCharacterTool { } impl GenerateCharacterTool { - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: GenerateImageToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - GenerateImageTool { - context: self.context.clone(), - } - .execute_with_kind( - state, - request_context, - caller, - args, - project_id, - generation_inputs, - asset_label, - canvas_completion, - Some("character"), - "character", - ) - .await - } - pub fn format_execute_message( &self, args: &GenerateImageToolArgs, result: &EditorImageGenerationResult, ) -> String { - format_tool_message(Self::NAME, args, result) + let tool_name = Self::NAME; + let args = serde_json::to_string(args).unwrap_or_default(); + let data_key = result + .object_key + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(|value| value.trim_start_matches('/').to_string()) + .unwrap_or_else(|| result.image_src.clone()); + let image_id = crate::editor_agent::utils::ImageId::from_data_key(data_key); + format!( + "[tool_call:{tool_name}] args: {args} output: generated result saved as image: {image_id}" + ) } } - -pub(crate) fn format_tool_message( - tool_name: &str, - args: &GenerateImageToolArgs, - result: &EditorImageGenerationResult, -) -> String { - let args = serde_json::to_string(args).unwrap_or_default(); - let data_key = result - .object_key - .as_deref() - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(|value| value.trim_start_matches('/').to_string()) - .unwrap_or_else(|| result.image_src.clone()); - let image_id = crate::editor_agent::utils::ImageId::from_data_key(data_key); - format!( - "[tool_call:{tool_name}] args: {args} output: generated result saved as image: {image_id}" - ) -} diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_icon_spritesheet.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_icon_spritesheet.rs index 3916b24bf..6b09e3abb 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_icon_spritesheet.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_icon_spritesheet.rs @@ -1,21 +1,13 @@ use crate::editor_agent::editor_tools::common::{ - EditorAgentPricedTool, EditorToolContext, editor_agent_image_mud_points, + editor_agent_image_mud_points, EditorAgentPricedTool, EditorToolContext, }; use crate::editor_agent::utils::ImageId; use crate::editor_generation_config::EditorGenerationPricingConfig; -use crate::editor_project::{ - EditorGenerationCaller, EditorIconSpritesheetGenerationRequest, - generate_editor_icon_spritesheet_for_owner, -}; use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; use axum::http::StatusCode; use module_editor_agent::agent::tool::{Tool, ToolFailure, ToolFailureKind}; use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; -use shared_contracts::api::ApiSuccessEnvelope; -use shared_contracts::assets::EditorCanvasGenerationCompletionPayload; +use serde_json::{json, Value}; use std::error::Error; use std::fmt::Display; @@ -160,79 +152,12 @@ impl GenerateIconSpritesheetTool { } Ok(()) } - - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: GenerateIconSpritesheetToolArgs, - project_id: String, - generation_inputs: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - self.validate_args(&args).map_err(|error| { - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "editor-agent", - "message": error.to_string(), - })) - })?; - let result = generate_editor_icon_spritesheet_for_owner( - state, - request_context, - caller, - EditorIconSpritesheetGenerationRequest { - reference_image_src: self - .context - .image_data_key(&args.reference_image_id) - .ok_or_else(|| image_not_found(&args.reference_image_id))? - .to_string(), - reference_image_srcs: Some( - args.reference_image_ids - .into_iter() - .map(|image_id| { - self.context - .image_data_key(&image_id) - .map(ToOwned::to_owned) - .ok_or_else(|| image_not_found(&image_id)) - }) - .collect::, _>>()?, - ), - icon_descriptions: args.icon_descriptions, - model: None, - screen_color: Some("auto".to_string()), - seg_model: Some("birefnet".to_string()), - aspect_ratio: args.aspect_ratio, - image_size: args.image_size, - project_id: Some(project_id), - generation_inputs, - asset_folder_id: Some("project".to_string()), - canvas_completion: Some(canvas_completion), - }, - ) - .await? - .0; - let data = if request_context.wants_envelope() { - serde_json::from_value::>(result) - .map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details( - json!({ "message": format!("failed to parse success envelope: {error}") }), - ) - })? - .data - } else { - result - }; - serde_json::from_value(data).map_err(|error| AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) - .with_details(json!({ "message": format!("failed to deserialize icon spritesheet result: {error}") }))) - } - pub fn format_execute_message( &self, args: &GenerateIconSpritesheetToolArgs, result: &EditorIconSpritesheetResult, ) -> String { + let tool_name = Self::NAME; let args = serde_json::to_string(args).unwrap_or_default(); let data_key = result .spritesheet_resource @@ -245,9 +170,7 @@ impl GenerateIconSpritesheetTool { .unwrap_or_else(|| result.spritesheet_image_src.clone()); let image_id = ImageId::from_data_key(data_key); format!( - "[tool_call:{}] args: {args} output: generated spritesheet saved as image: {}", - Self::NAME, - image_id + "[tool_call:{tool_name}] args: {args} output: generated spritesheet saved as image: {image_id}", ) } } diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_image.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_image.rs index 0b18b59c5..0047b91e0 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_image.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_image.rs @@ -180,119 +180,6 @@ impl GenerateImageTool { Self::NAME, ) } - - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: GenerateImageToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - self.execute_with_kind( - state, - request_context, - caller, - args, - project_id, - generation_inputs, - asset_label, - canvas_completion, - None, - "editor_agent_generated_image", - ) - .await - } - - #[allow(clippy::too_many_arguments)] - pub(crate) async fn execute_with_kind( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: GenerateImageToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - kind: Option<&str>, - asset_kind: &str, - ) -> Result { - self.validate_args(&args).map_err(|error| { - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "editor-agent", - "message": error.to_string(), - })) - })?; - - let result = generate_editor_image_for_owner( - state, - request_context, - caller, - EditorImageGenerationRequest { - prompt: args.prompt, - size: None, - kind: kind.map(ToOwned::to_owned), - model: None, - screen_color: kind - .is_some_and(|value| value == "character") - .then(|| "auto".to_string()), - seg_model: kind - .is_some_and(|value| value == "character") - .then(|| "birefnet".to_string()), - aspect_ratio: args.aspect_ratio, - image_size: args.image_size, - reference_image_srcs: Some(self.image_data_keys(args.reference_image_ids)?), - project_id: Some(project_id), - asset_kind: Some(asset_kind.to_string()), - generation_inputs, - asset_folder_id: Some("project".to_string()), - asset_label, - source_resource_id: None, - canvas_completion: Some(canvas_completion), - }, - ) - .await? - .0; - - let data = if request_context.wants_envelope() { - serde_json::from_value::>(result) - .map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ - "message": format!("failed to parse success envelope: {error}"), - })) - })? - .data - } else { - result - }; - - serde_json::from_value::(data).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ - "message": format!("failed to deserialize image generation result: {error}"), - })) - }) - } - - fn image_data_keys(&self, image_ids: Vec) -> Result, AppError> { - image_ids - .into_iter() - .map(|image_id| { - self.context - .image_data_key(&image_id) - .map(ToOwned::to_owned) - .ok_or_else(|| { - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "editor-agent", - "message": format!("asset {image_id} not found in context"), - })) - }) - }) - .collect() - } } #[cfg(test)] diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_sound_effect.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_sound_effect.rs index 893970c35..4931096e8 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_sound_effect.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_sound_effect.rs @@ -1,22 +1,11 @@ use crate::editor_agent::editor_tools::common::EditorAgentPricedTool; use crate::editor_generation_config::{ - EDITOR_SOUND_EFFECT_MODEL_VIDU, EditorGenerationPricingConfig, + EditorGenerationPricingConfig, EDITOR_SOUND_EFFECT_MODEL_VIDU, }; -use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; -use crate::vector_engine_audio_generation::generate_editor_sound_effect_for_owner; -use axum::Json; -use axum::http::StatusCode; -use axum::response::Response; use module_editor_agent::agent::tool::{Tool, ToolFailure}; use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; -use shared_contracts::api::ApiSuccessEnvelope; -use shared_contracts::assets::{ - EditorAudioGenerateResponse, EditorCanvasGenerationCompletionPayload, - EditorSoundEffectGenerateRequest, -}; +use serde_json::{json, Value}; +use shared_contracts::assets::EditorAudioGenerateResponse; use std::error::Error; use std::fmt::Display; @@ -81,7 +70,7 @@ impl Tool for GenerateSoundEffectTool { return Err(GenerateSoundEffectError::PromptNotProvided); } Ok(GenerateSoundEffectToolOutput { - message: pending_message(), + message: "this tool call is pending user confirmation. if all is pending, just end this turn".to_string(), }) } } @@ -108,43 +97,6 @@ impl EditorAgentPricedTool for GenerateSoundEffectTool { } impl GenerateSoundEffectTool { - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - owner_user_id: String, - args: GenerateSoundEffectToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - if args.prompt.trim().is_empty() { - return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details( - json!({ "provider": "editor-agent", "message": "音效提示词不能为空" }), - )); - } - let response = generate_editor_sound_effect_for_owner( - state.clone(), - request_context.clone(), - owner_user_id, - Ok(Json(EditorSoundEffectGenerateRequest { - prompt: args.prompt, - model: args.model, - duration: args.duration.unwrap_or(3), - project_id: Some(project_id), - canvas_completion: Some(canvas_completion), - generation_inputs, - asset_folder_id: Some("project".to_string()), - asset_label, - })), - ) - .await - .map_err(map_media_response_error)?; - parse_media_response(request_context, response.0) - } - pub fn format_execute_message( &self, args: &GenerateSoundEffectToolArgs, @@ -158,36 +110,3 @@ impl GenerateSoundEffectTool { ) } } - -pub(crate) fn pending_message() -> String { - "this tool call is pending user confirmation. if all is pending, just end this turn".to_string() -} - -pub(crate) fn map_media_response_error(response: Response) -> AppError { - AppError::from_status(response.status()).with_details(json!({ - "provider": "editor-agent", - "message": "媒体生成请求失败", - })) -} - -pub(crate) fn parse_media_response Deserialize<'de>>( - request_context: &RequestContext, - value: Value, -) -> Result { - let data = if request_context.wants_envelope() { - serde_json::from_value::>(value) - .map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details( - json!({ "message": format!("failed to parse success envelope: {error}") }), - ) - })? - .data - } else { - value - }; - serde_json::from_value(data).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details( - json!({ "message": format!("failed to deserialize media generation result: {error}") }), - ) - }) -} diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_ui_design.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_ui_design.rs index b6b45b85c..3cb0eae1a 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_ui_design.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_ui_design.rs @@ -1,19 +1,13 @@ use crate::editor_agent::editor_tools::common::{ EditorAgentPricedTool, EditorToolContext, editor_agent_image_mud_points, }; -use crate::editor_agent::editor_tools::generate_character::format_tool_message; use crate::editor_agent::editor_tools::generate_image::{ EditorImageGenerationResult, GenerateImageError, GenerateImageTool, GenerateImageToolArgs, GenerateImageToolOutput, }; use crate::editor_generation_config::EditorGenerationPricingConfig; -use crate::editor_project::EditorGenerationCaller; -use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; use module_editor_agent::agent::tool::{Tool, ToolFailure}; use serde_json::{Value, json}; -use shared_contracts::assets::EditorCanvasGenerationCompletionPayload; pub struct GenerateUiDesignTool { pub context: EditorToolContext, @@ -75,41 +69,23 @@ impl EditorAgentPricedTool for GenerateUiDesignTool { } impl GenerateUiDesignTool { - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - caller: EditorGenerationCaller, - args: GenerateImageToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - GenerateImageTool { - context: self.context.clone(), - } - .execute_with_kind( - state, - request_context, - caller, - args, - project_id, - generation_inputs, - asset_label, - canvas_completion, - Some("ui-design"), - "ui-design", - ) - .await - } - pub fn format_execute_message( &self, args: &GenerateImageToolArgs, result: &EditorImageGenerationResult, ) -> String { - format_tool_message(Self::NAME, args, result) + let tool_name = Self::NAME; + let args = serde_json::to_string(args).unwrap_or_default(); + let data_key = result + .object_key + .as_deref() + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(|value| value.trim_start_matches('/').to_string()) + .unwrap_or_else(|| result.image_src.clone()); + let image_id = crate::editor_agent::utils::ImageId::from_data_key(data_key); + format!( + "[tool_call:{tool_name}] args: {args} output: generated result saved as image: {image_id}" + ) } } diff --git a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_video.rs b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_video.rs index 42a539eca..7a8cccc6d 100644 --- a/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_video.rs +++ b/server-rs/crates/api-server/src/editor_agent/editor_tools/generate_video.rs @@ -1,22 +1,10 @@ -use crate::character_animation_assets::generate_editor_video_for_owner; use crate::editor_agent::editor_tools::common::EditorAgentPricedTool; -use crate::editor_agent::editor_tools::generate_sound_effect::{ - map_media_response_error, parse_media_response, pending_message, -}; use crate::editor_agent::utils::ImageId; use crate::editor_generation_config::EditorGenerationPricingConfig; -use crate::http_error::AppError; -use crate::request_context::RequestContext; -use crate::state::AppState; -use axum::Json; -use axum::http::StatusCode; use module_editor_agent::agent::tool::{Tool, ToolFailure, ToolFailureKind}; use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; -use shared_contracts::assets::{ - EditorCanvasGenerationCompletionPayload, EditorVideoGenerateRequest, - EditorVideoGenerateResponse, -}; +use serde_json::{json, Value}; +use shared_contracts::assets::EditorVideoGenerateResponse; use std::error::Error; use std::fmt::Display; @@ -99,7 +87,7 @@ impl Tool for GenerateVideoTool { } } Ok(GenerateVideoToolOutput { - message: pending_message(), + message: "this tool call is pending user confirmation. if all is pending, just end this turn".to_string(), }) } } @@ -139,63 +127,6 @@ impl EditorAgentPricedTool for GenerateVideoTool { } impl GenerateVideoTool { - #[allow(clippy::too_many_arguments)] - pub async fn execute( - &self, - state: &AppState, - request_context: &RequestContext, - owner_user_id: String, - args: GenerateVideoToolArgs, - project_id: String, - generation_inputs: Option, - asset_label: Option, - canvas_completion: EditorCanvasGenerationCompletionPayload, - ) -> Result { - if args.prompt.trim().is_empty() { - return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details( - json!({ "provider": "editor-agent", "message": "视频提示词不能为空" }), - )); - } - let reference_image_srcs: Result, AppError> = args - .reference_image_ids - .into_iter() - .map(|image_id| { - self.context - .image_data_key(&image_id) - .map(ToOwned::to_owned) - .ok_or_else(|| image_not_found(&image_id)) - }) - .collect(); - let reference_image_srcs = reference_image_srcs?; - let response = generate_editor_video_for_owner( - state.clone(), - request_context.clone(), - owner_user_id, - Ok(Json(EditorVideoGenerateRequest { - prompt: args.prompt, - model: args.model.unwrap_or_else(|| "seedance2.0-fast".to_string()), - aspect_ratio: args.aspect_ratio.unwrap_or_else(|| "16:9".to_string()), - duration_seconds: args.duration_seconds.unwrap_or(4), - resolution: args.resolution.unwrap_or_else(|| "720p".to_string()), - mode: "std".to_string(), - sound: args.sound.unwrap_or_else(|| "off".to_string()), - web_search_enabled: false, - reference_image_srcs, - reference_video_srcs: Vec::new(), - reference_audio_srcs: Vec::new(), - project_id: Some(project_id), - canvas_completion: Some(canvas_completion), - generation_inputs, - source_resource_id: None, - asset_kind: Some("video".to_string()), - asset_folder_id: Some("project".to_string()), - asset_label, - })), - ) - .await - .map_err(map_media_response_error)?; - parse_media_response(request_context, response.0) - } pub fn format_execute_message( &self, args: &GenerateVideoToolArgs, @@ -209,11 +140,4 @@ impl GenerateVideoTool { video_id ) } -} - -fn image_not_found(image_id: &ImageId) -> AppError { - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "editor-agent", - "message": format!("asset {image_id} not found in context"), - })) -} +} \ No newline at end of file