use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct BeginStorySessionRequest { pub runtime_session_id: String, pub world_profile_id: String, pub initial_prompt: String, #[serde(default)] pub opening_summary: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ContinueStoryRequest { pub story_session_id: String, pub narrative_text: String, #[serde(default)] pub choice_function_id: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct StorySessionPayload { pub story_session_id: String, pub runtime_session_id: String, pub actor_user_id: String, pub world_profile_id: String, pub initial_prompt: String, #[serde(default)] pub opening_summary: Option, pub latest_narrative_text: String, #[serde(default)] pub latest_choice_function_id: Option, pub status: String, pub version: u32, pub created_at: String, pub updated_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct StoryEventPayload { pub event_id: String, pub story_session_id: String, pub event_kind: String, pub narrative_text: String, #[serde(default)] pub choice_function_id: Option, pub created_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct StorySessionMutationResponse { pub story_session: StorySessionPayload, pub story_event: StoryEventPayload, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct StorySessionStateResponse { pub story_session: StorySessionPayload, pub story_events: Vec, } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn continue_story_request_uses_camel_case_fields() { let payload = serde_json::to_value(ContinueStoryRequest { story_session_id: "storysess_1".to_string(), narrative_text: "继续前进".to_string(), choice_function_id: Some("npc_chat".to_string()), }) .expect("payload should serialize"); assert_eq!( payload, json!({ "storySessionId": "storysess_1", "narrativeText": "继续前进", "choiceFunctionId": "npc_chat" }) ); } #[test] fn story_session_mutation_response_uses_camel_case_fields() { let payload = serde_json::to_value(StorySessionMutationResponse { story_session: StorySessionPayload { story_session_id: "storysess_1".to_string(), runtime_session_id: "runtime_1".to_string(), actor_user_id: "user_1".to_string(), world_profile_id: "profile_1".to_string(), initial_prompt: "进入营地".to_string(), opening_summary: Some("营地开场".to_string()), latest_narrative_text: "篝火正在燃烧。".to_string(), latest_choice_function_id: Some("talk".to_string()), status: "active".to_string(), version: 1, created_at: "1.000000Z".to_string(), updated_at: "1.000000Z".to_string(), }, story_event: StoryEventPayload { event_id: "storyevt_1".to_string(), story_session_id: "storysess_1".to_string(), event_kind: "session_started".to_string(), narrative_text: "篝火正在燃烧。".to_string(), choice_function_id: Some("talk".to_string()), created_at: "1.000000Z".to_string(), }, }) .expect("payload should serialize"); assert_eq!( payload["storySession"]["storySessionId"], json!("storysess_1") ); assert_eq!(payload["storyEvent"]["eventKind"], json!("session_started")); assert_eq!(payload["storyEvent"]["choiceFunctionId"], json!("talk")); } #[test] fn story_session_state_response_uses_camel_case_fields() { let payload = serde_json::to_value(StorySessionStateResponse { story_session: StorySessionPayload { story_session_id: "storysess_1".to_string(), runtime_session_id: "runtime_1".to_string(), actor_user_id: "user_1".to_string(), world_profile_id: "profile_1".to_string(), initial_prompt: "进入营地".to_string(), opening_summary: Some("营地开场".to_string()), latest_narrative_text: "你看见篝火边有人招手。".to_string(), latest_choice_function_id: Some("talk_to_npc".to_string()), status: "active".to_string(), version: 2, created_at: "1.000000Z".to_string(), updated_at: "2.000000Z".to_string(), }, story_events: vec![StoryEventPayload { event_id: "storyevt_2".to_string(), story_session_id: "storysess_1".to_string(), event_kind: "story_continued".to_string(), narrative_text: "你看见篝火边有人招手。".to_string(), choice_function_id: Some("talk_to_npc".to_string()), created_at: "2.000000Z".to_string(), }], }) .expect("payload should serialize"); assert_eq!( payload["storySession"]["latestChoiceFunctionId"], json!("talk_to_npc") ); assert_eq!( payload["storyEvents"][0]["eventKind"], json!("story_continued") ); } }