1
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PuzzleTemplatePricingUnit {
|
||||
Point,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PuzzleSupportedLevelMode {
|
||||
Single,
|
||||
Multi,
|
||||
SingleOrMulti,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PuzzleLevelGenerationMode {
|
||||
SingleLevel,
|
||||
MultiLevel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleTemplateCostRange {
|
||||
pub min_points: u32,
|
||||
pub max_points: u32,
|
||||
pub pricing_unit: PuzzleTemplatePricingUnit,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum PuzzleDraftEditableFieldPath {
|
||||
#[serde(rename = "workTitle")]
|
||||
WorkTitle,
|
||||
#[serde(rename = "workDescription")]
|
||||
WorkDescription,
|
||||
#[serde(rename = "workTags")]
|
||||
WorkTags,
|
||||
#[serde(rename = "levels[].levelName")]
|
||||
LevelName,
|
||||
#[serde(rename = "levels[].pictureDescription")]
|
||||
LevelPictureDescription,
|
||||
#[serde(rename = "levels[].pictureReference")]
|
||||
LevelPictureReference,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleTemplateImageGenerationPolicy {
|
||||
pub allow_uploaded_image_directly: bool,
|
||||
pub allow_generated_images: bool,
|
||||
pub allow_per_level_reference_image: bool,
|
||||
pub default_candidate_count_per_level: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleCreativeTemplateProtocol {
|
||||
pub template_id: String,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
#[serde(default)]
|
||||
pub preview_image_src: Option<String>,
|
||||
pub supported_level_mode: PuzzleSupportedLevelMode,
|
||||
pub min_level_count: u32,
|
||||
pub max_level_count: u32,
|
||||
pub default_level_count: u32,
|
||||
pub cost_range: PuzzleTemplateCostRange,
|
||||
pub required_draft_fields: Vec<PuzzleDraftEditableFieldPath>,
|
||||
pub image_policy: PuzzleTemplateImageGenerationPolicy,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleCreativeTemplateSelection {
|
||||
pub template_id: String,
|
||||
pub title: String,
|
||||
pub reason: String,
|
||||
pub cost_range: PuzzleTemplateCostRange,
|
||||
pub supported_level_mode: PuzzleSupportedLevelMode,
|
||||
pub selected_level_mode: PuzzleLevelGenerationMode,
|
||||
pub planned_level_count: u32,
|
||||
pub requires_user_confirmation: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreativePuzzleLevelDraftInput {
|
||||
pub level_name: String,
|
||||
pub picture_description: String,
|
||||
/// 任务 A 冻结:Phase 1 采用正式字段方案,后续拼图草稿落地需补正式 pictureReference 字段。
|
||||
#[serde(default)]
|
||||
pub picture_reference: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreativePuzzleDraftToolInput {
|
||||
pub template_id: String,
|
||||
pub template_cost_range: PuzzleTemplateCostRange,
|
||||
pub work_title: String,
|
||||
pub work_description: String,
|
||||
pub work_tags: Vec<String>,
|
||||
pub levels: Vec<CreativePuzzleLevelDraftInput>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleImageGenerationPlanLevel {
|
||||
pub level_id: String,
|
||||
pub level_name: String,
|
||||
pub picture_description: String,
|
||||
pub image_prompt: String,
|
||||
#[serde(default)]
|
||||
pub picture_reference: Option<String>,
|
||||
pub candidate_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleImageGenerationPlan {
|
||||
pub mode: PuzzleLevelGenerationMode,
|
||||
pub template_id: String,
|
||||
pub estimated_cost_range: PuzzleTemplateCostRange,
|
||||
pub levels: Vec<PuzzleImageGenerationPlanLevel>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PuzzleDraftFieldPatchOperation {
|
||||
Set,
|
||||
Append,
|
||||
Replace,
|
||||
Remove,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleDraftFieldPatch {
|
||||
pub field_path: PuzzleDraftEditableFieldPath,
|
||||
pub operation: PuzzleDraftFieldPatchOperation,
|
||||
#[serde(default)]
|
||||
pub level_id: Option<String>,
|
||||
pub value: serde_json::Value,
|
||||
pub rationale: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
fn cost_range() -> PuzzleTemplateCostRange {
|
||||
PuzzleTemplateCostRange {
|
||||
min_points: 2,
|
||||
max_points: 12,
|
||||
pricing_unit: PuzzleTemplatePricingUnit::Point,
|
||||
reason: "按关卡数和每关图片生成次数估算".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creative_agent_puzzle_template_protocol_uses_camel_case() {
|
||||
let payload = serde_json::to_value(PuzzleCreativeTemplateProtocol {
|
||||
template_id: "puzzle.default-creative".to_string(),
|
||||
title: "创意拼图".to_string(),
|
||||
summary: "把图文灵感做成拼图".to_string(),
|
||||
preview_image_src: None,
|
||||
supported_level_mode: PuzzleSupportedLevelMode::SingleOrMulti,
|
||||
min_level_count: 1,
|
||||
max_level_count: 6,
|
||||
default_level_count: 1,
|
||||
cost_range: cost_range(),
|
||||
required_draft_fields: vec![
|
||||
PuzzleDraftEditableFieldPath::WorkTitle,
|
||||
PuzzleDraftEditableFieldPath::LevelPictureReference,
|
||||
],
|
||||
image_policy: PuzzleTemplateImageGenerationPolicy {
|
||||
allow_uploaded_image_directly: true,
|
||||
allow_generated_images: true,
|
||||
allow_per_level_reference_image: true,
|
||||
default_candidate_count_per_level: 1,
|
||||
},
|
||||
})
|
||||
.expect("template should serialize");
|
||||
|
||||
assert_eq!(payload["templateId"], json!("puzzle.default-creative"));
|
||||
assert_eq!(payload["previewImageSrc"], json!(null));
|
||||
assert_eq!(payload["supportedLevelMode"], json!("single_or_multi"));
|
||||
assert_eq!(payload["costRange"]["pricingUnit"], json!("point"));
|
||||
assert_eq!(
|
||||
payload["requiredDraftFields"],
|
||||
json!(["workTitle", "levels[].pictureReference"])
|
||||
);
|
||||
assert_eq!(
|
||||
payload["imagePolicy"]["allowPerLevelReferenceImage"],
|
||||
json!(true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creative_agent_puzzle_image_plan_roundtrips() {
|
||||
let plan = PuzzleImageGenerationPlan {
|
||||
mode: PuzzleLevelGenerationMode::MultiLevel,
|
||||
template_id: "puzzle.default-creative".to_string(),
|
||||
estimated_cost_range: cost_range(),
|
||||
levels: vec![PuzzleImageGenerationPlanLevel {
|
||||
level_id: "level-1".to_string(),
|
||||
level_name: "第一关".to_string(),
|
||||
picture_description: "温暖的家庭照片".to_string(),
|
||||
image_prompt: "pixel puzzle, warm family photo".to_string(),
|
||||
picture_reference: Some("asset-ref-1".to_string()),
|
||||
candidate_count: 1,
|
||||
}],
|
||||
};
|
||||
|
||||
let payload = serde_json::to_value(&plan).expect("plan should serialize");
|
||||
assert_eq!(payload["mode"], json!("multi_level"));
|
||||
assert_eq!(
|
||||
payload["levels"][0]["pictureReference"],
|
||||
json!("asset-ref-1")
|
||||
);
|
||||
|
||||
let decoded: PuzzleImageGenerationPlan =
|
||||
serde_json::from_value(payload).expect("plan should deserialize");
|
||||
assert_eq!(decoded, plan);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user