split code
This commit is contained in:
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(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user