use serde::{Deserialize, Serialize}; use crate::creation_audio::CreationAudioAsset; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct CreateMatch3DAgentSessionRequest { #[serde(default)] pub seed_text: Option, #[serde(default)] pub theme_text: Option, #[serde(default)] pub reference_image_src: Option, #[serde(default)] pub clear_count: Option, #[serde(default)] pub difficulty: Option, #[serde(default)] pub asset_style_id: Option, #[serde(default)] pub asset_style_label: Option, #[serde(default)] pub asset_style_prompt: Option, #[serde(default)] pub generate_click_sound: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct SendMatch3DAgentMessageRequest { pub client_message_id: String, pub text: String, #[serde(default)] pub quick_fill_requested: Option, #[serde(default)] pub reference_image_src: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ExecuteMatch3DAgentActionRequest { pub action: String, #[serde(default)] pub game_name: Option, #[serde(default)] pub summary: Option, #[serde(default)] pub tags: Option>, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub clear_count: Option, #[serde(default)] pub difficulty: Option, #[serde(default)] pub generate_click_sound: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DCreatorConfigResponse { pub theme_text: String, #[serde(default)] pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, #[serde(default)] pub asset_style_id: Option, #[serde(default)] pub asset_style_label: Option, #[serde(default)] pub asset_style_prompt: Option, #[serde(default)] pub generate_click_sound: bool, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DResultDraftResponse { pub profile_id: String, pub game_name: String, pub theme_text: String, #[serde(default)] pub summary_text: Option, pub summary: String, pub tags: Vec, #[serde(default)] pub cover_image_src: Option, #[serde(default)] pub reference_image_src: Option, pub clear_count: u32, pub difficulty: u32, pub total_item_count: u32, pub publish_ready: bool, pub blockers: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_prompt: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_image_src: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub background_image_object_key: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub generated_background_asset: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub generated_item_assets: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedBackgroundAssetResponse { pub prompt: String, #[serde(default)] pub level_scene_prompt: Option, #[serde(default)] pub level_scene_image_src: Option, #[serde(default)] pub level_scene_image_object_key: Option, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, #[serde(default)] pub ui_spritesheet_prompt: Option, #[serde(default)] pub ui_spritesheet_image_src: Option, #[serde(default)] pub ui_spritesheet_image_object_key: Option, #[serde(default)] pub item_spritesheet_prompt: Option, #[serde(default)] pub item_spritesheet_image_src: Option, #[serde(default)] pub item_spritesheet_image_object_key: Option, #[serde(default)] pub container_prompt: Option, #[serde(default)] pub container_image_src: Option, #[serde(default)] pub container_image_object_key: Option, pub status: String, #[serde(default)] pub error: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedItemImageViewResponse { pub view_id: String, pub view_index: u32, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DGeneratedItemAssetResponse { pub item_id: String, pub item_name: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub item_size: Option, #[serde(default)] pub image_src: Option, #[serde(default)] pub image_object_key: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub image_views: Vec, #[serde(default)] pub model_src: Option, #[serde(default)] pub model_object_key: Option, #[serde(default)] pub model_file_name: Option, #[serde(default)] pub task_uuid: Option, #[serde(default)] pub subscription_key: Option, #[serde(default)] pub sound_prompt: Option, #[serde(default)] pub background_music_title: Option, #[serde(default)] pub background_music_style: Option, #[serde(default)] pub background_music_prompt: Option, #[serde(default)] pub background_music: Option, #[serde(default)] pub click_sound: Option, #[serde(default)] pub background_asset: Option, pub status: String, #[serde(default)] pub error: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DAnchorItemResponse { pub key: String, pub label: String, pub value: String, pub status: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DAnchorPackResponse { pub theme: Match3DAnchorItemResponse, pub clear_count: Match3DAnchorItemResponse, pub difficulty: Match3DAnchorItemResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Match3DAgentMessageResponse { pub id: String, pub role: String, pub kind: String, pub text: String, pub created_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DAgentSessionSnapshotResponse { pub session_id: String, pub current_turn: u32, pub progress_percent: u32, pub stage: String, pub anchor_pack: Match3DAnchorPackResponse, #[serde(default)] pub config: Option, #[serde(default)] pub draft: Option, pub messages: Vec, #[serde(default)] pub last_assistant_reply: Option, #[serde(default)] pub published_profile_id: Option, pub updated_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DAgentSessionResponse { pub session: Match3DAgentSessionSnapshotResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Match3DAgentActionResponse { pub session: Match3DAgentSessionSnapshotResponse, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn create_match3d_session_request_uses_camel_case() { let payload = serde_json::to_value(CreateMatch3DAgentSessionRequest { seed_text: Some("水果消除".to_string()), theme_text: Some("水果".to_string()), reference_image_src: Some("data:image/png;base64,abc".to_string()), clear_count: Some(4), difficulty: Some(3), asset_style_id: Some("flat-icon".to_string()), asset_style_label: Some("扁平图标".to_string()), asset_style_prompt: Some("干净扁平 2D 游戏道具图标风格".to_string()), generate_click_sound: Some(true), }) .expect("payload should serialize"); assert_eq!(payload["seedText"], json!("水果消除")); assert_eq!(payload["themeText"], json!("水果")); assert_eq!( payload["referenceImageSrc"], json!("data:image/png;base64,abc") ); assert_eq!(payload["clearCount"], json!(4)); assert_eq!(payload["assetStyleId"], json!("flat-icon")); assert_eq!(payload["generateClickSound"], json!(true)); } #[test] fn execute_match3d_action_request_serializes_click_sound_toggle() { let payload = serde_json::to_value(ExecuteMatch3DAgentActionRequest { action: "match3d_compile_draft".to_string(), game_name: None, summary: None, tags: None, cover_image_src: None, clear_count: None, difficulty: None, generate_click_sound: Some(true), }) .expect("payload should serialize"); assert_eq!(payload["action"], json!("match3d_compile_draft")); assert_eq!(payload["generateClickSound"], json!(true)); } }