prepare info display to user for review
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -27,12 +27,10 @@ impl EditorToolContext {
|
||||
///
|
||||
/// 通用 `Tool` 仍只负责参数校验;价格依赖 api-server 的运行时配置,不能下沉到
|
||||
/// `module-editor-agent`。实际执行和扣费仍由既有生成 BFF 负责。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) trait EditorAgentPricedTool: Tool {
|
||||
fn pricing(&self, pricing: &EditorGenerationPricingConfig, args: &<Self as Tool>::Args) -> u32;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn editor_agent_image_mud_points(
|
||||
pricing: &EditorGenerationPricingConfig,
|
||||
kind: Option<&str>,
|
||||
|
||||
@@ -24,11 +24,8 @@ pub struct GenerateVideoTool {
|
||||
pub context: crate::editor_agent::editor_tools::common::EditorToolContext,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
const DEFAULT_VIDEO_MODEL: &str = "seedance2.0-fast";
|
||||
#[allow(dead_code)]
|
||||
const DEFAULT_VIDEO_RESOLUTION: &str = "720p";
|
||||
#[allow(dead_code)]
|
||||
const DEFAULT_VIDEO_DURATION_SECONDS: u32 = 4;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -99,14 +99,60 @@ pub struct EditorAgentGeneratedAudio {
|
||||
pub audio_src: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorAgentToolCallDisplayArgs {
|
||||
pub string_args: Vec<EditorAgentToolCallStringArg>,
|
||||
pub image_args: Vec<EditorAgentToolCallImageArg>,
|
||||
pub extras: EditorAgentToolCallDisplayExtras,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorAgentToolCallDisplayExtras {
|
||||
pub price_mud_points: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorAgentToolCallStringArg {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorAgentToolCallImageArg {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub refs: Vec<EditorAgentToolCallImageRef>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorAgentToolCallImageRef {
|
||||
pub image_id: String,
|
||||
pub image_src: String,
|
||||
#[serde(default)]
|
||||
pub object_key: Option<String>,
|
||||
#[serde(default)]
|
||||
pub thumbnail_src: Option<String>,
|
||||
#[serde(default)]
|
||||
pub label: Option<String>,
|
||||
#[serde(default)]
|
||||
pub width: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub height: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct EditorAgentToolCall {
|
||||
pub tool_name: String,
|
||||
#[serde(default)]
|
||||
pub summary: String,
|
||||
pub status: EditorAgentToolCallStatus,
|
||||
pub args: serde_json::Value,
|
||||
pub display_args: EditorAgentToolCallDisplayArgs,
|
||||
#[serde(default)]
|
||||
pub images: Vec<EditorAgentGeneratedImage>,
|
||||
#[serde(default)]
|
||||
@@ -195,46 +241,79 @@ pub struct EditorAgentMessageResponse {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn tool_call_media_results_use_camel_case_and_default_for_existing_documents() {
|
||||
let legacy: EditorAgentToolCall = serde_json::from_value(json!({
|
||||
#[test]
|
||||
fn tool_call_display_args_and_media_use_camel_case() {
|
||||
let missing_display_args = serde_json::from_value::<EditorAgentToolCall>(json!({
|
||||
"toolName": "generate-video",
|
||||
"status": "completed",
|
||||
"args": {},
|
||||
"images": []
|
||||
}))
|
||||
.expect("existing message documents should remain readable");
|
||||
assert!(legacy.videos.is_empty());
|
||||
assert!(legacy.audios.is_empty());
|
||||
}));
|
||||
assert!(missing_display_args.is_err());
|
||||
|
||||
let tool_call = EditorAgentToolCall {
|
||||
tool_name: "generate-video".to_string(),
|
||||
summary: String::new(),
|
||||
status: EditorAgentToolCallStatus::Completed,
|
||||
args: json!({}),
|
||||
images: Vec::new(),
|
||||
videos: vec![EditorAgentGeneratedVideo {
|
||||
resource_id: Some("resource-video-1".to_string()),
|
||||
object_key: Some("generated/video.mp4".to_string()),
|
||||
asset_object_id: Some("asset-video-1".to_string()),
|
||||
video_src: "/generated-video.mp4".to_string(),
|
||||
thumbnail_src: Some("/generated-video.png".to_string()),
|
||||
width: Some(1280),
|
||||
height: Some(720),
|
||||
}],
|
||||
audios: vec![EditorAgentGeneratedAudio {
|
||||
resource_id: Some("resource-audio-1".to_string()),
|
||||
object_key: Some("generated/audio.mp3".to_string()),
|
||||
asset_object_id: Some("asset-audio-1".to_string()),
|
||||
audio_src: "/generated-audio.mp3".to_string(),
|
||||
}],
|
||||
error: None,
|
||||
};
|
||||
let payload = serde_json::to_value(tool_call).expect("tool call should serialize");
|
||||
assert_eq!(payload["videos"][0]["videoSrc"], "/generated-video.mp4");
|
||||
assert_eq!(payload["audios"][0]["audioSrc"], "/generated-audio.mp3");
|
||||
}
|
||||
let tool_call = EditorAgentToolCall {
|
||||
tool_name: "generate-video".to_string(),
|
||||
status: EditorAgentToolCallStatus::Completed,
|
||||
args: json!({}),
|
||||
display_args: EditorAgentToolCallDisplayArgs {
|
||||
string_args: vec![EditorAgentToolCallStringArg {
|
||||
name: "prompt".to_string(),
|
||||
label: "提示词".to_string(),
|
||||
value: "生成像素风角色".to_string(),
|
||||
}],
|
||||
image_args: vec![EditorAgentToolCallImageArg {
|
||||
name: "reference_image_ids".to_string(),
|
||||
label: "参考图片".to_string(),
|
||||
refs: vec![EditorAgentToolCallImageRef {
|
||||
image_id: "sha256:reference-1".to_string(),
|
||||
image_src: "/reference.png".to_string(),
|
||||
object_key: Some("generated/reference.png".to_string()),
|
||||
thumbnail_src: Some("/reference-thumbnail.png".to_string()),
|
||||
label: Some("角色参考".to_string()),
|
||||
width: Some(512),
|
||||
height: Some(512),
|
||||
}],
|
||||
}],
|
||||
extras: EditorAgentToolCallDisplayExtras {
|
||||
price_mud_points: 5,
|
||||
},
|
||||
},
|
||||
images: Vec::new(),
|
||||
videos: vec![EditorAgentGeneratedVideo {
|
||||
resource_id: Some("resource-video-1".to_string()),
|
||||
object_key: Some("generated/video.mp4".to_string()),
|
||||
asset_object_id: Some("asset-video-1".to_string()),
|
||||
video_src: "/generated-video.mp4".to_string(),
|
||||
thumbnail_src: Some("/generated-video.png".to_string()),
|
||||
width: Some(1280),
|
||||
height: Some(720),
|
||||
}],
|
||||
audios: vec![EditorAgentGeneratedAudio {
|
||||
resource_id: Some("resource-audio-1".to_string()),
|
||||
object_key: Some("generated/audio.mp3".to_string()),
|
||||
asset_object_id: Some("asset-audio-1".to_string()),
|
||||
audio_src: "/generated-audio.mp3".to_string(),
|
||||
}],
|
||||
error: None,
|
||||
};
|
||||
let payload = serde_json::to_value(tool_call).expect("tool call should serialize");
|
||||
assert_eq!(payload["videos"][0]["videoSrc"], "/generated-video.mp4");
|
||||
assert_eq!(payload["audios"][0]["audioSrc"], "/generated-audio.mp3");
|
||||
assert_eq!(
|
||||
payload["displayArgs"]["stringArgs"][0]["value"],
|
||||
"生成像素风角色"
|
||||
);
|
||||
assert_eq!(
|
||||
payload["displayArgs"]["imageArgs"][0]["refs"][0]["imageId"],
|
||||
"sha256:reference-1"
|
||||
);
|
||||
assert_eq!(
|
||||
payload["displayArgs"]["imageArgs"][0]["refs"][0]["objectKey"],
|
||||
"generated/reference.png"
|
||||
);
|
||||
assert_eq!(payload["displayArgs"]["extras"]["priceMudPoints"], 5);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user