split code

This commit is contained in:
2026-07-13 17:38:57 +08:00
parent 97a1e1663e
commit 2128bb109c
5 changed files with 461 additions and 527 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
use crate::editor_agent::editor_tools::common::EditorToolContext;
use crate::editor_agent::utils::{ImageId, ImageMetadata, IntoDataKey};
use shared_contracts::editor_agent::EditorAgentConversationMessagesDocument;
use std::collections::HashMap;
pub fn build_tool_context(document: &EditorAgentConversationMessagesDocument) -> EditorToolContext {
let mut images: HashMap<ImageId, ImageMetadata> = HashMap::new();
for msg in document.messages.iter().rev() {
// tool gen
if let Some(tc) = &msg.tool_call {
for img in &tc.images {
let data_key = img.clone().into_data_key();
let image_id = ImageId::from_data_key(&data_key);
let metadata = ImageMetadata {
tag: String::new(),
data_key,
};
images.insert(image_id, metadata);
}
}
// user pointed
for a in &msg.attachments {
let data_key = a.clone().into_data_key();
let image_id = ImageId::from_data_key(&data_key);
let metadata = ImageMetadata {
tag: String::new(),
data_key,
};
images.insert(image_id, metadata);
}
}
EditorToolContext { images }
}
@@ -0,0 +1,314 @@
use serde_json::Value;
use module_editor_agent::agent::error::PromptError;
use module_editor_agent::agent::tool::Tool;
use shared_contracts::editor_agent::{EditorAgentConversationMessagesDocument, EditorAgentToolCallDisplayArgs, EditorAgentToolCallDisplayExtras, EditorAgentToolCallImageArg, EditorAgentToolCallImageRef, EditorAgentToolCallStringArg};
use crate::editor_agent::editor_tools::common::{EditorAgentPricedTool, EditorToolContext};
use crate::editor_agent::editor_tools::edit_image::{EditImageTool, EditImageToolArgs};
use crate::editor_agent::editor_tools::generate_background_music::{GenerateBackgroundMusicTool, GenerateBackgroundMusicToolArgs};
use crate::editor_agent::editor_tools::generate_character::GenerateCharacterTool;
use crate::editor_agent::editor_tools::generate_icon_spritesheet::{GenerateIconSpritesheetTool, GenerateIconSpritesheetToolArgs};
use crate::editor_agent::editor_tools::generate_image::{GenerateImageTool, GenerateImageToolArgs};
use crate::editor_agent::editor_tools::generate_sound_effect::{GenerateSoundEffectTool, GenerateSoundEffectToolArgs};
use crate::editor_agent::editor_tools::generate_ui_design::GenerateUiDesignTool;
use crate::editor_agent::editor_tools::generate_video::{GenerateVideoTool, GenerateVideoToolArgs};
use crate::editor_agent::utils::{ImageId, IntoDataKey, IntoImageId};
use crate::editor_generation_config::EditorGenerationPricingConfig;
pub fn build_tool_call_display_args(
tool_name: &str,
args: &Value,
document: &EditorAgentConversationMessagesDocument,
tool_context: &EditorToolContext,
pricing: &EditorGenerationPricingConfig,
) -> Result<EditorAgentToolCallDisplayArgs, PromptError> {
let mut display_args = EditorAgentToolCallDisplayArgs::default();
let price_mud_points = match tool_name {
EditImageTool::NAME => {
let args: EditImageToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = EditImageTool {
context: tool_context.clone(),
}
.pricing(pricing, &args);
push_string_display_arg(&mut display_args, "prompt", "修改要求", args.prompt);
push_image_display_arg(
&mut display_args,
document,
"object_image_id",
"目标图片",
std::slice::from_ref(&args.object_image_id),
)?;
push_image_display_arg(
&mut display_args,
document,
"reference_image_ids",
"参考图片",
args.reference_image_ids.as_slice(),
)?;
price_mud_points
}
GenerateImageTool::NAME | GenerateCharacterTool::NAME | GenerateUiDesignTool::NAME => {
let args: GenerateImageToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = match tool_name {
GenerateImageTool::NAME => GenerateImageTool {
context: tool_context.clone(),
}
.pricing(pricing, &args),
GenerateCharacterTool::NAME => GenerateCharacterTool {
context: tool_context.clone(),
}
.pricing(pricing, &args),
GenerateUiDesignTool::NAME => GenerateUiDesignTool {
context: tool_context.clone(),
}
.pricing(pricing, &args),
_ => unreachable!("tool name was matched above"),
};
push_string_display_arg(&mut display_args, "prompt", "提示词", args.prompt);
push_optional_string_display_arg(
&mut display_args,
"aspect_ratio",
"画面比例",
args.aspect_ratio,
);
push_optional_string_display_arg(
&mut display_args,
"image_size",
"图片尺寸",
args.image_size,
);
push_image_display_arg(
&mut display_args,
document,
"reference_image_ids",
"参考图片",
args.reference_image_ids.as_slice(),
)?;
price_mud_points
}
GenerateIconSpritesheetTool::NAME => {
let args: GenerateIconSpritesheetToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = GenerateIconSpritesheetTool {
context: tool_context.clone(),
}
.pricing(pricing, &args);
push_string_display_arg(
&mut display_args,
"icon_descriptions",
"图标描述",
args.icon_descriptions.join("\n"),
);
push_optional_string_display_arg(
&mut display_args,
"aspect_ratio",
"图集比例",
args.aspect_ratio,
);
push_optional_string_display_arg(
&mut display_args,
"image_size",
"图片尺寸",
args.image_size,
);
push_image_display_arg(
&mut display_args,
document,
"reference_image_id",
"规范参考图",
std::slice::from_ref(&args.reference_image_id),
)?;
push_image_display_arg(
&mut display_args,
document,
"reference_image_ids",
"额外参考图",
args.reference_image_ids.as_slice(),
)?;
price_mud_points
}
GenerateVideoTool::NAME => {
let args: GenerateVideoToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = GenerateVideoTool {
context: tool_context.clone(),
}
.pricing(pricing, &args);
push_string_display_arg(&mut display_args, "prompt", "视频描述", args.prompt);
push_optional_string_display_arg(
&mut display_args,
"aspect_ratio",
"画面比例",
args.aspect_ratio,
);
if let Some(duration_seconds) = args.duration_seconds {
push_string_display_arg(
&mut display_args,
"duration_seconds",
"时长(秒)",
duration_seconds.to_string(),
);
}
push_optional_string_display_arg(&mut display_args, "model", "模型", args.model);
push_optional_string_display_arg(
&mut display_args,
"resolution",
"分辨率",
args.resolution,
);
push_optional_string_display_arg(&mut display_args, "sound", "声音", args.sound);
push_image_display_arg(
&mut display_args,
document,
"reference_image_ids",
"参考图片",
args.reference_image_ids.as_slice(),
)?;
price_mud_points
}
GenerateSoundEffectTool::NAME => {
let args: GenerateSoundEffectToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = GenerateSoundEffectTool.pricing(pricing, &args);
push_string_display_arg(&mut display_args, "prompt", "音效描述", args.prompt);
if let Some(duration) = args.duration {
push_string_display_arg(
&mut display_args,
"duration",
"时长(秒)",
duration.to_string(),
);
}
push_optional_string_display_arg(&mut display_args, "model", "模型", args.model);
price_mud_points
}
GenerateBackgroundMusicTool::NAME => {
let args: GenerateBackgroundMusicToolArgs = parse_display_tool_args(tool_name, args)?;
let price_mud_points = GenerateBackgroundMusicTool.pricing(pricing, &args);
push_string_display_arg(&mut display_args, "prompt", "音乐描述", args.prompt);
push_string_display_arg(
&mut display_args,
"make_instrumental",
"纯音乐",
if args.make_instrumental { "" } else { "" },
);
price_mud_points
}
_ => {
return Err(PromptError::InternalError(format!(
"missing display argument mapping for tool {tool_name}"
)));
}
};
display_args.extras = EditorAgentToolCallDisplayExtras { price_mud_points };
Ok(display_args)
}
fn parse_display_tool_args<T>(tool_name: &str, args: &Value) -> Result<T, PromptError>
where
T: serde::de::DeserializeOwned,
{
serde_json::from_value(args.clone()).map_err(|error| {
PromptError::InternalError(format!(
"failed to build display arguments for {tool_name}: {error}"
))
})
}
fn push_string_display_arg(
display_args: &mut EditorAgentToolCallDisplayArgs,
name: &str,
label: &str,
value: impl Into<String>,
) {
let value = value.into();
if value.trim().is_empty() {
return;
}
display_args.string_args.push(EditorAgentToolCallStringArg {
name: name.to_string(),
label: label.to_string(),
value,
});
}
fn push_optional_string_display_arg(
display_args: &mut EditorAgentToolCallDisplayArgs,
name: &str,
label: &str,
value: Option<String>,
) {
if let Some(value) = value {
push_string_display_arg(display_args, name, label, value);
}
}
fn push_image_display_arg(
display_args: &mut EditorAgentToolCallDisplayArgs,
document: &EditorAgentConversationMessagesDocument,
name: &str,
label: &str,
image_ids: &[ImageId],
) -> Result<(), PromptError> {
if image_ids.is_empty() {
return Ok(());
}
let refs = image_ids
.iter()
.map(|image_id| {
resolve_tool_call_image_ref(document, image_id).ok_or_else(|| {
PromptError::InternalError(format!(
"display image reference {} is missing from conversation context",
image_id.id
))
})
})
.collect::<Result<Vec<_>, _>>()?;
display_args.image_args.push(EditorAgentToolCallImageArg {
name: name.to_string(),
label: label.to_string(),
refs,
});
Ok(())
}
pub fn resolve_tool_call_image_ref(
document: &EditorAgentConversationMessagesDocument,
image_id: &ImageId,
) -> Option<EditorAgentToolCallImageRef> {
for message in document.messages.iter().rev() {
for attachment in message.attachments.iter().rev() {
if attachment.clone().into_image_id() == *image_id {
return Some(EditorAgentToolCallImageRef {
image_id: image_id.id.clone(),
image_src: attachment.image_src.clone(),
object_key: attachment.object_key.clone(),
thumbnail_src: attachment.thumbnail_src.clone(),
label: attachment.label.clone(),
width: attachment.width,
height: attachment.height,
});
}
}
if let Some(tool_call) = &message.tool_call {
for image in tool_call.images.iter().rev() {
let data_key = image.clone().into_data_key();
if ImageId::from_data_key(data_key) == *image_id {
return Some(EditorAgentToolCallImageRef {
image_id: image_id.id.clone(),
image_src: image.image_src.clone(),
object_key: image.object_key.clone(),
thumbnail_src: image.thumbnail_src.clone(),
label: None,
width: image.width,
height: image.height,
});
}
}
}
}
None
}
@@ -2,6 +2,9 @@ mod editor_tools;
mod utils;
pub mod api;
mod agent;
mod display_args;
mod context;
mod resp_to_asset;
pub use api::{
create_editor_agent_conversation, delete_editor_agent_conversation,
@@ -0,0 +1,74 @@
use serde_json::Value;
use shared_contracts::assets::{EditorAudioGenerateResponse, EditorVideoGenerateResponse};
use shared_contracts::editor_agent::{EditorAgentGeneratedAudio, EditorAgentGeneratedImage, EditorAgentGeneratedVideo};
use crate::editor_agent::editor_tools::edit_image::EditorImageEditResult;
use crate::editor_agent::editor_tools::generate_icon_spritesheet::EditorIconSpritesheetResult;
use crate::editor_agent::editor_tools::generate_image::EditorImageGenerationResult;
fn value_string(value: Option<&Value>, field: &str) -> Option<String> {
value
.and_then(|resource| resource.get(field))
.and_then(Value::as_str)
.map(ToOwned::to_owned)
}
pub fn editor_agent_generated_image(result: &EditorImageGenerationResult) -> EditorAgentGeneratedImage {
EditorAgentGeneratedImage {
resource_id: value_string(result.resource.as_ref(), "resourceId"),
object_key: result.object_key.clone(),
asset_object_id: result.asset_object_id.clone(),
image_src: result.image_src.clone(),
thumbnail_src: None,
width: Some(result.width),
height: Some(result.height),
}
}
pub fn editor_agent_generated_edited_image(
result: &EditorImageEditResult,
) -> EditorAgentGeneratedImage {
EditorAgentGeneratedImage {
resource_id: value_string(result.resource.as_ref(), "resourceId"),
object_key: result.object_key.clone(),
asset_object_id: result.asset_object_id.clone(),
image_src: result.image_src.clone(),
thumbnail_src: None,
width: Some(result.width),
height: Some(result.height),
}
}
pub fn editor_agent_generated_spritesheet(
result: &EditorIconSpritesheetResult,
) -> EditorAgentGeneratedImage {
EditorAgentGeneratedImage {
resource_id: value_string(result.spritesheet_resource.as_ref(), "resourceId"),
object_key: value_string(result.spritesheet_resource.as_ref(), "objectKey"),
asset_object_id: value_string(result.spritesheet_resource.as_ref(), "assetObjectId"),
image_src: result.spritesheet_image_src.clone(),
thumbnail_src: None,
width: Some(result.spritesheet_width),
height: Some(result.spritesheet_height),
}
}
pub fn editor_agent_generated_video(result: &EditorVideoGenerateResponse) -> EditorAgentGeneratedVideo {
EditorAgentGeneratedVideo {
resource_id: value_string(result.resource.as_ref(), "resourceId"),
object_key: result.object_key.clone(),
asset_object_id: result.asset_object_id.clone(),
video_src: result.video_src.clone(),
thumbnail_src: result.thumbnail_src.clone(),
width: Some(result.width),
height: Some(result.height),
}
}
pub fn editor_agent_generated_audio(result: &EditorAudioGenerateResponse) -> EditorAgentGeneratedAudio {
EditorAgentGeneratedAudio {
resource_id: value_string(result.resource.as_ref(), "resourceId"),
object_key: result.object_key.clone(),
asset_object_id: result.asset_object_id.clone(),
audio_src: result.audio_src.clone(),
}
}