修复 Agent 音频失败脱敏
复用外部生成任务的用户可见错误投影 按 Agent 工具名称映射音效任务并在回填前脱敏 补充音效和背景音乐失败回归测试
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
use crate::http_error::AppError;
|
||||
use crate::state::AppState;
|
||||
use crate::{
|
||||
editor_agent::tool::editor_agent_audio_generation_job_kind,
|
||||
external_generation::user_visible_external_generation_error, http_error::AppError,
|
||||
state::AppState,
|
||||
};
|
||||
use platform_editor_agent::agent::tools::context::EditorToolContext;
|
||||
use serde_json::Value;
|
||||
use shared_contracts::editor_agent::{
|
||||
@@ -87,10 +90,18 @@ pub async fn reconcile_editor_agent_tool_calls(
|
||||
}
|
||||
}
|
||||
"failed" => {
|
||||
let tool_name = document.messages[message_index]
|
||||
.tool_call
|
||||
.as_ref()
|
||||
.expect("reconcile candidate contains a tool call")
|
||||
.tool_name
|
||||
.clone();
|
||||
mark_job_failed(
|
||||
&mut document.messages[message_index],
|
||||
job.last_error_message
|
||||
.unwrap_or_else(|| "生成失败".to_string()),
|
||||
editor_agent_job_failure_message(
|
||||
tool_name.as_str(),
|
||||
job.last_error_message,
|
||||
),
|
||||
);
|
||||
last_reconcile_error = None;
|
||||
reconciled.push(document.messages[message_index].clone());
|
||||
@@ -124,6 +135,14 @@ fn should_retry_result_reconcile(retry_count: u8) -> bool {
|
||||
retry_count < EDITOR_AGENT_RESULT_RECONCILE_MAX_RETRIES
|
||||
}
|
||||
|
||||
fn editor_agent_job_failure_message(tool_name: &str, last_error_message: Option<String>) -> String {
|
||||
let Some(job_kind) = editor_agent_audio_generation_job_kind(tool_name) else {
|
||||
return last_error_message.unwrap_or_else(|| "生成失败".to_string());
|
||||
};
|
||||
user_visible_external_generation_error(job_kind, last_error_message)
|
||||
.unwrap_or_else(|| "生成失败".to_string())
|
||||
}
|
||||
|
||||
fn mark_job_failed(message: &mut EditorAgentMessage, error: String) {
|
||||
let tool_call = message
|
||||
.tool_call
|
||||
@@ -219,6 +238,7 @@ pub(crate) fn reconcile_completed_editor_agent_tool_call_for_test(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use platform_editor_agent::agent::tools::generate_background_music::GenerateBackgroundMusicTool;
|
||||
use platform_editor_agent::agent::tools::generate_sound_effect::GenerateSoundEffectTool;
|
||||
use platform_editor_agent::framework::tool::Tool;
|
||||
use serde_json::json;
|
||||
@@ -310,6 +330,46 @@ mod tests {
|
||||
assert_eq!(tool_call.external_job_id.as_deref(), Some("job-1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_sound_effect_jobs_hide_internal_reason_codes_in_agent_tool_calls() {
|
||||
for internal_error in [
|
||||
"translation_invalid",
|
||||
"elevenlabs_http_failed",
|
||||
"ELEVENLABS_BASE_URL 未配置",
|
||||
] {
|
||||
let mut message = pending_tool_message();
|
||||
mark_job_failed(
|
||||
&mut message,
|
||||
editor_agent_job_failure_message(
|
||||
GenerateSoundEffectTool::NAME,
|
||||
Some(internal_error.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
let tool_call = message.tool_call.expect("tool call should remain present");
|
||||
assert_eq!(tool_call.status, EditorAgentToolCallStatus::Failed);
|
||||
assert_eq!(
|
||||
tool_call.error.as_deref(),
|
||||
Some("音效生成失败,请稍后重试。")
|
||||
);
|
||||
assert!(
|
||||
!message.text.contains(internal_error),
|
||||
"Agent 会话文本不应保留内部失败码:{internal_error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_background_music_jobs_use_the_same_user_visible_error_policy() {
|
||||
assert_eq!(
|
||||
editor_agent_job_failure_message(
|
||||
GenerateBackgroundMusicTool::NAME,
|
||||
Some("请求 Vector Engine 背景音乐任务失败:connection refused".to_string()),
|
||||
),
|
||||
"背景音乐生成失败,请稍后重试。"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completed_legacy_tool_call_with_null_defaults_still_reconciles() {
|
||||
let mut message: EditorAgentMessage = serde_json::from_value(json!({
|
||||
|
||||
@@ -86,6 +86,17 @@ impl EditorAgentToolError {
|
||||
}
|
||||
}
|
||||
|
||||
/// `get_external_generation_job_generated_artifacts` 的精简读取结果不带 `job_kind`;
|
||||
/// Agent 会话回填只能从已持久化的 tool name 恢复音频任务类型,以复用普通任务读取侧的
|
||||
/// 用户可见错误投影。
|
||||
pub(crate) fn editor_agent_audio_generation_job_kind(tool_name: &str) -> Option<&'static str> {
|
||||
match tool_name {
|
||||
GenerateSoundEffectTool::NAME => Some(EDITOR_SOUND_EFFECT_GENERATION_JOB_KIND),
|
||||
GenerateBackgroundMusicTool::NAME => Some(EDITOR_BACKGROUND_MUSIC_GENERATION_JOB_KIND),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EditorAgentToolError {
|
||||
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -207,7 +207,10 @@ fn map_external_generation_job_status(
|
||||
/// 任务的原始失败文本可能包含内部 provider、分割模型、请求端点或 HTTP 传输层细节,因此
|
||||
/// 只能在普通用户读取边界替换成稳定文案。
|
||||
/// 原始值仍留在任务记录、tracing 和后台审计路径中。
|
||||
fn user_visible_external_generation_error(job_kind: &str, error: Option<String>) -> Option<String> {
|
||||
pub(crate) fn user_visible_external_generation_error(
|
||||
job_kind: &str,
|
||||
error: Option<String>,
|
||||
) -> Option<String> {
|
||||
if job_kind == EDITOR_BACKGROUND_REMOVAL_JOB_KIND && error.is_some() {
|
||||
return Some("去除背景失败,请稍后重试。".to_string());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user