moved code
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
use serde_json::Value;
|
||||
use module_editor_agent::agent::tool::Tool;
|
||||
use shared_contracts::assets::{EditorAudioGenerateResponse, EditorVideoGenerateResponse};
|
||||
use shared_contracts::editor_agent::{EditorAgentConversationMessagesDocument, EditorAgentMessage, EditorAgentToolCallStatus};
|
||||
use spacetime_client::{EditorAgentConversationRecord, ExternalGenerationJobGetRecordInput};
|
||||
use crate::editor_agent::editor_tools::common::EditorToolContext;
|
||||
use crate::editor_agent::editor_tools::edit_image::{EditImageTool, EditImageToolArgs, EditorImageEditResult};
|
||||
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::{EditorIconSpritesheetResult, GenerateIconSpritesheetTool, GenerateIconSpritesheetToolArgs};
|
||||
use crate::editor_agent::editor_tools::generate_image::{EditorImageGenerationResult, 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::resp_to_asset;
|
||||
use crate::http_error::AppError;
|
||||
use crate::state::AppState;
|
||||
|
||||
pub async fn reconcile_editor_agent_tool_calls(
|
||||
state: &AppState,
|
||||
conversation: &EditorAgentConversationRecord,
|
||||
document: &mut EditorAgentConversationMessagesDocument,
|
||||
) -> Result<Vec<EditorAgentMessage>, AppError> {
|
||||
let candidates = document
|
||||
.messages
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(message_index, message)| {
|
||||
let tool_call = message.tool_call.as_ref()?;
|
||||
if tool_call.status != EditorAgentToolCallStatus::NotCompleted {
|
||||
return None;
|
||||
}
|
||||
Some((message_index, tool_call.external_job_id.clone()?))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut reconciled = Vec::new();
|
||||
|
||||
for (message_index, job_id) in candidates {
|
||||
let job = match state
|
||||
.spacetime_client()
|
||||
.get_external_generation_job(ExternalGenerationJobGetRecordInput {
|
||||
job_id,
|
||||
owner_user_id: conversation.owner_user_id.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(job) => job,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let changed = match job.status.as_str() {
|
||||
"completed" => reconcile_completed_editor_agent_tool_call(
|
||||
&mut document.messages[message_index],
|
||||
job.result_payload_json.as_deref(),
|
||||
),
|
||||
"failed" => {
|
||||
let tool_call = document.messages[message_index]
|
||||
.tool_call
|
||||
.as_mut()
|
||||
.expect("candidate contains a tool call");
|
||||
let error = job
|
||||
.last_error_message
|
||||
.unwrap_or_else(|| "生成失败".to_string());
|
||||
tool_call.error = Some(error.clone());
|
||||
tool_call.status = EditorAgentToolCallStatus::Failed;
|
||||
document.messages[message_index].text =
|
||||
format!("[tool_call:{}] output: {error}", tool_call.tool_name);
|
||||
Ok(())
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
match changed {
|
||||
Ok(()) => reconciled.push(document.messages[message_index].clone()),
|
||||
Err(error) => {
|
||||
let tool_call = document.messages[message_index]
|
||||
.tool_call
|
||||
.as_mut()
|
||||
.expect("candidate contains a tool call");
|
||||
tool_call.error = Some(error.clone());
|
||||
tool_call.status = EditorAgentToolCallStatus::Failed;
|
||||
document.messages[message_index].text =
|
||||
format!("[tool_call:{}] output: {error}", tool_call.tool_name);
|
||||
reconciled.push(document.messages[message_index].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(reconciled)
|
||||
}
|
||||
|
||||
fn reconcile_completed_editor_agent_tool_call(
|
||||
message: &mut EditorAgentMessage,
|
||||
result_payload_json: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let response = result_payload_json
|
||||
.ok_or_else(|| "生成任务缺少结果数据".to_string())
|
||||
.and_then(|payload| serde_json::from_str::<Value>(payload).map_err(|e| e.to_string()))?
|
||||
.get("response")
|
||||
.cloned()
|
||||
.ok_or_else(|| "生成任务缺少 response 结果".to_string())?;
|
||||
let tool_call = message
|
||||
.tool_call
|
||||
.as_mut()
|
||||
.ok_or_else(|| "消息缺少工具调用".to_string())?;
|
||||
let tool_name = tool_call.tool_name.clone();
|
||||
|
||||
match tool_name.as_str() {
|
||||
GenerateImageTool::NAME | GenerateCharacterTool::NAME | GenerateUiDesignTool::NAME => {
|
||||
let args: GenerateImageToolArgs = parse_reconciled_value(&tool_call.args)?;
|
||||
let result: EditorImageGenerationResult = parse_reconciled_value(&response)?;
|
||||
message.text = match tool_name.as_str() {
|
||||
GenerateCharacterTool::NAME => GenerateCharacterTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(&args, &result),
|
||||
GenerateUiDesignTool::NAME => GenerateUiDesignTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(&args, &result),
|
||||
_ => GenerateImageTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(&args, &result),
|
||||
};
|
||||
tool_call.images = vec![resp_to_asset::editor_agent_generated_image(&result)];
|
||||
}
|
||||
EditImageTool::NAME => {
|
||||
let args: EditImageToolArgs = parse_reconciled_value(&tool_call.args)?;
|
||||
let result: EditorImageEditResult = parse_reconciled_value(&response)?;
|
||||
tool_call.images = vec![resp_to_asset::editor_agent_generated_edited_image(&result)];
|
||||
message.text = EditImageTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(args, result);
|
||||
}
|
||||
GenerateIconSpritesheetTool::NAME => {
|
||||
let args: GenerateIconSpritesheetToolArgs = parse_reconciled_value(&tool_call.args)?;
|
||||
let result: EditorIconSpritesheetResult = parse_reconciled_value(&response)?;
|
||||
message.text = GenerateIconSpritesheetTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(&args, &result);
|
||||
tool_call.images = vec![resp_to_asset::editor_agent_generated_spritesheet(&result)];
|
||||
}
|
||||
GenerateVideoTool::NAME => {
|
||||
let args: GenerateVideoToolArgs = parse_reconciled_value(&tool_call.args)?;
|
||||
let result: EditorVideoGenerateResponse = parse_reconciled_value(&response)?;
|
||||
message.text = GenerateVideoTool {
|
||||
context: EditorToolContext::default(),
|
||||
}
|
||||
.format_execute_message(&args, &result);
|
||||
tool_call.videos = vec![resp_to_asset::editor_agent_generated_video(&result)];
|
||||
}
|
||||
GenerateSoundEffectTool::NAME | GenerateBackgroundMusicTool::NAME => {
|
||||
let result: EditorAudioGenerateResponse = parse_reconciled_value(&response)?;
|
||||
message.text = if tool_name == GenerateSoundEffectTool::NAME {
|
||||
let args: GenerateSoundEffectToolArgs = parse_reconciled_value(&tool_call.args)?;
|
||||
GenerateSoundEffectTool.format_execute_message(&args, &result)
|
||||
} else {
|
||||
let args: GenerateBackgroundMusicToolArgs =
|
||||
parse_reconciled_value(&tool_call.args)?;
|
||||
GenerateBackgroundMusicTool.format_execute_message(&args, &result)
|
||||
};
|
||||
tool_call.audios = vec![resp_to_asset::editor_agent_generated_audio(&result)];
|
||||
}
|
||||
_ => return Err(format!("不支持回填工具 {tool_name}")),
|
||||
}
|
||||
tool_call.error = None;
|
||||
tool_call.status = EditorAgentToolCallStatus::Completed;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_reconciled_value<T: serde::de::DeserializeOwned>(value: &Value) -> Result<T, String> {
|
||||
serde_json::from_value(value.clone()).map_err(|error| error.to_string())
|
||||
}
|
||||
Reference in New Issue
Block a user