moved code

This commit is contained in:
2026-07-14 11:49:33 +08:00
parent 18381ec283
commit 696d6935b4
@@ -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())
}