778 lines
22 KiB
Rust
778 lines
22 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value;
|
|
use std::collections::BTreeMap;
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelSourceMode {
|
|
Idea,
|
|
Document,
|
|
Blank,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelCharacterRole {
|
|
Protagonist,
|
|
Main,
|
|
Supporting,
|
|
Antagonist,
|
|
Background,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelAssetSource {
|
|
PlatformAsset,
|
|
Generated,
|
|
External,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelAudioGenerationKind {
|
|
BackgroundMusic,
|
|
SoundEffect,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelSceneAvailability {
|
|
Opening,
|
|
Always,
|
|
PhaseLocked,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelAttributePanelMode {
|
|
Off,
|
|
PlatformWhitelist,
|
|
TemplateConfig,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelValidationSeverity {
|
|
Error,
|
|
Warning,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelAgentStatus {
|
|
Collecting,
|
|
Drafting,
|
|
Ready,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelAgentMessageRole {
|
|
User,
|
|
Assistant,
|
|
System,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelAgentMessageKind {
|
|
Chat,
|
|
Summary,
|
|
ActionResult,
|
|
Warning,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelAgentActionKind {
|
|
GenerateDraft,
|
|
PatchWorld,
|
|
PatchCharacter,
|
|
PatchScene,
|
|
PatchStoryPhase,
|
|
GenerateSceneImage,
|
|
GenerateCharacterImage,
|
|
CompileWorkProfile,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelAgentPhase {
|
|
Perception,
|
|
Reasoning,
|
|
Drafting,
|
|
Reflection,
|
|
Finalizing,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelRunMode {
|
|
Test,
|
|
Play,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelRunStatus {
|
|
Active,
|
|
Completed,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum VisualNovelRuntimeActionKind {
|
|
Choice,
|
|
FreeText,
|
|
Continue,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelTransitionKind {
|
|
Fade,
|
|
Cut,
|
|
Flash,
|
|
None,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum VisualNovelHistorySource {
|
|
Player,
|
|
Assistant,
|
|
System,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(untagged)]
|
|
pub enum VisualNovelFlagValue {
|
|
String(String),
|
|
Number(f64),
|
|
Bool(bool),
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelDraftPatch {
|
|
pub path: String,
|
|
pub op: String,
|
|
#[serde(default)]
|
|
pub value: Option<Value>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelValidationIssue {
|
|
pub issue_id: String,
|
|
pub code: String,
|
|
pub severity: VisualNovelValidationSeverity,
|
|
pub path: String,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelChoiceDraft {
|
|
pub choice_id: String,
|
|
pub text: String,
|
|
#[serde(default)]
|
|
pub action_hint: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelCharacterImageAsset {
|
|
pub asset_id: String,
|
|
pub image_src: String,
|
|
#[serde(default)]
|
|
pub expression: Option<String>,
|
|
pub source: VisualNovelAssetSource,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelWorldDraft {
|
|
pub title: String,
|
|
pub summary: String,
|
|
pub background: String,
|
|
pub premise: String,
|
|
pub literary_style: String,
|
|
pub player_role: String,
|
|
pub default_tone: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelCharacterDraft {
|
|
pub character_id: String,
|
|
pub name: String,
|
|
#[serde(default)]
|
|
pub gender: Option<String>,
|
|
pub role: VisualNovelCharacterRole,
|
|
pub appearance: String,
|
|
pub personality: String,
|
|
pub tone: String,
|
|
pub background: String,
|
|
pub relationship_to_player: String,
|
|
pub image_assets: Vec<VisualNovelCharacterImageAsset>,
|
|
#[serde(default)]
|
|
pub default_expression: Option<String>,
|
|
pub is_player_visible: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelSceneDraft {
|
|
pub scene_id: String,
|
|
pub name: String,
|
|
pub description: String,
|
|
#[serde(default)]
|
|
pub background_image_src: Option<String>,
|
|
#[serde(default)]
|
|
pub music_src: Option<String>,
|
|
#[serde(default)]
|
|
pub ambient_sound_src: Option<String>,
|
|
pub availability: VisualNovelSceneAvailability,
|
|
pub phase_ids: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelStoryPhaseDraft {
|
|
pub phase_id: String,
|
|
pub title: String,
|
|
pub goal: String,
|
|
pub summary: String,
|
|
pub entry_condition: String,
|
|
pub exit_condition: String,
|
|
pub scene_ids: Vec<String>,
|
|
pub character_ids: Vec<String>,
|
|
pub suggested_choices: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelOpeningDraft {
|
|
#[serde(default)]
|
|
pub scene_id: Option<String>,
|
|
pub narration: String,
|
|
#[serde(default)]
|
|
pub speaker_character_id: Option<String>,
|
|
#[serde(default)]
|
|
pub first_dialogue: Option<String>,
|
|
pub initial_choices: Vec<VisualNovelChoiceDraft>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelRuntimeConfigDraft {
|
|
pub text_mode_enabled: bool,
|
|
pub default_text_mode: bool,
|
|
pub max_history_entries: u32,
|
|
pub max_assistant_step_count_per_turn: u32,
|
|
pub allow_free_text_action: bool,
|
|
pub allow_history_regeneration: bool,
|
|
pub attribute_panel_mode: VisualNovelAttributePanelMode,
|
|
pub save_archive_enabled: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelResultDraft {
|
|
#[serde(default)]
|
|
pub profile_id: Option<String>,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub work_tags: Vec<String>,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub source_mode: VisualNovelSourceMode,
|
|
pub source_asset_ids: Vec<String>,
|
|
pub world: VisualNovelWorldDraft,
|
|
pub characters: Vec<VisualNovelCharacterDraft>,
|
|
pub scenes: Vec<VisualNovelSceneDraft>,
|
|
pub story_phases: Vec<VisualNovelStoryPhaseDraft>,
|
|
pub opening: VisualNovelOpeningDraft,
|
|
pub runtime_config: VisualNovelRuntimeConfigDraft,
|
|
pub publish_ready: bool,
|
|
pub validation_issues: Vec<VisualNovelValidationIssue>,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelAgentMessage {
|
|
pub id: String,
|
|
pub role: VisualNovelAgentMessageRole,
|
|
pub kind: VisualNovelAgentMessageKind,
|
|
pub text: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelAgentPendingAction {
|
|
pub action_id: String,
|
|
pub kind: VisualNovelAgentActionKind,
|
|
pub label: String,
|
|
#[serde(default)]
|
|
pub target_id: Option<String>,
|
|
#[serde(default)]
|
|
pub payload: Option<Value>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelAgentSessionSnapshot {
|
|
pub session_id: String,
|
|
pub owner_user_id: String,
|
|
pub source_mode: VisualNovelSourceMode,
|
|
pub status: VisualNovelAgentStatus,
|
|
pub messages: Vec<VisualNovelAgentMessage>,
|
|
#[serde(default)]
|
|
pub draft: Option<VisualNovelResultDraft>,
|
|
#[serde(default)]
|
|
pub pending_action: Option<VisualNovelAgentPendingAction>,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CreateVisualNovelSessionRequest {
|
|
pub source_mode: VisualNovelSourceMode,
|
|
#[serde(default)]
|
|
pub seed_text: Option<String>,
|
|
#[serde(default)]
|
|
pub source_asset_ids: Vec<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelSessionResponse {
|
|
pub session: VisualNovelAgentSessionSnapshot,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelWorkSummary {
|
|
pub runtime_kind: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
pub title: String,
|
|
pub description: String,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub tags: Vec<String>,
|
|
pub publish_status: String,
|
|
pub publish_ready: bool,
|
|
pub play_count: u32,
|
|
pub updated_at: String,
|
|
#[serde(default)]
|
|
pub published_at: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelWorkDetail {
|
|
pub work_id: String,
|
|
pub summary: VisualNovelWorkSummary,
|
|
#[serde(default)]
|
|
pub source_session_id: Option<String>,
|
|
pub author_display_name: String,
|
|
pub source_asset_ids: Vec<String>,
|
|
pub draft: VisualNovelResultDraft,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelWorksResponse {
|
|
pub works: Vec<VisualNovelWorkSummary>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelWorkResponse {
|
|
pub work: VisualNovelWorkDetail,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct UpdateVisualNovelWorkRequest {
|
|
pub draft: VisualNovelResultDraft,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelCompileResponse {
|
|
pub session: VisualNovelAgentSessionSnapshot,
|
|
pub work: VisualNovelWorkDetail,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CreateVisualNovelBackgroundMusicRequest {
|
|
pub prompt: String,
|
|
pub title: String,
|
|
#[serde(default)]
|
|
pub tags: Option<String>,
|
|
#[serde(default)]
|
|
pub model: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct CreateVisualNovelSoundEffectRequest {
|
|
pub prompt: String,
|
|
#[serde(default)]
|
|
pub duration: Option<u8>,
|
|
#[serde(default)]
|
|
pub seed: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelAudioGenerationTaskResponse {
|
|
pub kind: VisualNovelAudioGenerationKind,
|
|
pub task_id: String,
|
|
pub provider: String,
|
|
pub status: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PublishVisualNovelGeneratedAudioAssetRequest {
|
|
pub scene_id: String,
|
|
#[serde(default)]
|
|
pub profile_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelGeneratedAudioAssetResponse {
|
|
pub kind: VisualNovelAudioGenerationKind,
|
|
pub task_id: String,
|
|
pub provider: String,
|
|
pub status: String,
|
|
#[serde(default)]
|
|
pub asset_object_id: Option<String>,
|
|
#[serde(default)]
|
|
pub asset_kind: Option<String>,
|
|
#[serde(default)]
|
|
pub audio_src: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct SendVisualNovelMessageRequest {
|
|
pub client_message_id: String,
|
|
pub text: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ExecuteVisualNovelAgentActionRequest {
|
|
#[serde(default)]
|
|
pub action_id: Option<String>,
|
|
pub kind: VisualNovelAgentActionKind,
|
|
#[serde(default)]
|
|
pub target_id: Option<String>,
|
|
#[serde(default)]
|
|
pub payload: Option<Value>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(
|
|
tag = "type",
|
|
rename_all = "snake_case",
|
|
rename_all_fields = "camelCase"
|
|
)]
|
|
pub enum VisualNovelAgentStreamEvent {
|
|
Start {
|
|
session_id: String,
|
|
},
|
|
Phase {
|
|
phase: VisualNovelAgentPhase,
|
|
},
|
|
TextDelta {
|
|
text: String,
|
|
},
|
|
DraftPatch {
|
|
patch: VisualNovelDraftPatch,
|
|
},
|
|
ActionRequired {
|
|
action: VisualNovelAgentPendingAction,
|
|
},
|
|
Complete {
|
|
session: VisualNovelAgentSessionSnapshot,
|
|
},
|
|
Error {
|
|
message: String,
|
|
retryable: bool,
|
|
},
|
|
Done {},
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(
|
|
tag = "type",
|
|
rename_all = "snake_case",
|
|
rename_all_fields = "camelCase"
|
|
)]
|
|
pub enum VisualNovelRuntimeStep {
|
|
SceneChange {
|
|
scene_id: String,
|
|
#[serde(default)]
|
|
background_image_src: Option<String>,
|
|
#[serde(default)]
|
|
music_src: Option<String>,
|
|
},
|
|
Narration {
|
|
text: String,
|
|
},
|
|
Dialogue {
|
|
character_id: String,
|
|
character_name: String,
|
|
#[serde(default)]
|
|
expression: Option<String>,
|
|
text: String,
|
|
},
|
|
Transition {
|
|
transition_kind: VisualNovelTransitionKind,
|
|
#[serde(default)]
|
|
text: Option<String>,
|
|
},
|
|
Choice {
|
|
choices: Vec<VisualNovelChoiceDraft>,
|
|
},
|
|
Flag {
|
|
key: String,
|
|
value: VisualNovelFlagValue,
|
|
},
|
|
Metric {
|
|
key: String,
|
|
delta: f64,
|
|
},
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelHistoryEntry {
|
|
pub entry_id: String,
|
|
pub run_id: String,
|
|
pub turn_index: u32,
|
|
pub source: VisualNovelHistorySource,
|
|
#[serde(default)]
|
|
pub action_text: Option<String>,
|
|
pub steps: Vec<VisualNovelRuntimeStep>,
|
|
#[serde(default)]
|
|
pub snapshot_before_hash: Option<String>,
|
|
#[serde(default)]
|
|
pub snapshot_after_hash: Option<String>,
|
|
pub created_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelRunSnapshot {
|
|
pub run_id: String,
|
|
pub owner_user_id: String,
|
|
pub profile_id: String,
|
|
pub mode: VisualNovelRunMode,
|
|
pub status: VisualNovelRunStatus,
|
|
#[serde(default)]
|
|
pub current_scene_id: Option<String>,
|
|
#[serde(default)]
|
|
pub current_phase_id: Option<String>,
|
|
pub visible_character_ids: Vec<String>,
|
|
pub flags: BTreeMap<String, VisualNovelFlagValue>,
|
|
pub metrics: BTreeMap<String, f64>,
|
|
pub history: Vec<VisualNovelHistoryEntry>,
|
|
pub available_choices: Vec<VisualNovelChoiceDraft>,
|
|
pub text_mode_enabled: bool,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelRuntimeActionRequest {
|
|
pub action_kind: VisualNovelRuntimeActionKind,
|
|
#[serde(default)]
|
|
pub choice_id: Option<String>,
|
|
#[serde(default)]
|
|
pub text: Option<String>,
|
|
pub client_event_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelStartRunRequest {
|
|
pub profile_id: String,
|
|
pub mode: VisualNovelRunMode,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelRunResponse {
|
|
pub run: VisualNovelRunSnapshot,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelHistoryResponse {
|
|
pub history: Vec<VisualNovelHistoryEntry>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelRegenerateRequest {
|
|
pub history_entry_id: String,
|
|
pub client_event_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct VisualNovelSaveArchiveState {
|
|
pub runtime_kind: String,
|
|
pub profile_id: String,
|
|
pub run_id: String,
|
|
#[serde(default)]
|
|
pub current_scene_id: Option<String>,
|
|
#[serde(default)]
|
|
pub current_phase_id: Option<String>,
|
|
pub history_cursor: u32,
|
|
#[serde(default)]
|
|
pub snapshot_hash: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(
|
|
tag = "type",
|
|
rename_all = "snake_case",
|
|
rename_all_fields = "camelCase"
|
|
)]
|
|
pub enum VisualNovelRuntimeStreamEvent {
|
|
Start { run_id: String },
|
|
RawText { text: String },
|
|
Step { step: VisualNovelRuntimeStep },
|
|
Snapshot { run: VisualNovelRunSnapshot },
|
|
Complete { run: VisualNovelRunSnapshot },
|
|
Error { message: String, retryable: bool },
|
|
Done {},
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn result_draft_and_runtime_step_use_contract_case() {
|
|
let draft = VisualNovelResultDraft {
|
|
profile_id: Some("vn-profile-1".to_string()),
|
|
work_title: "雨夜书店".to_string(),
|
|
work_description: "一段视觉小说测试底稿".to_string(),
|
|
work_tags: vec!["悬疑".to_string()],
|
|
cover_image_src: None,
|
|
source_mode: VisualNovelSourceMode::Idea,
|
|
source_asset_ids: Vec::new(),
|
|
world: VisualNovelWorldDraft {
|
|
title: "雨夜书店".to_string(),
|
|
summary: "主角在雨夜进入一间只在午夜出现的书店。".to_string(),
|
|
background: "城市边缘的旧街区。".to_string(),
|
|
premise: "找回遗失的名字。".to_string(),
|
|
literary_style: "细腻、轻悬疑".to_string(),
|
|
player_role: "误入书店的读者".to_string(),
|
|
default_tone: "克制而温柔".to_string(),
|
|
},
|
|
characters: Vec::new(),
|
|
scenes: Vec::new(),
|
|
story_phases: Vec::new(),
|
|
opening: VisualNovelOpeningDraft {
|
|
scene_id: None,
|
|
narration: "雨声落下。".to_string(),
|
|
speaker_character_id: None,
|
|
first_dialogue: None,
|
|
initial_choices: Vec::new(),
|
|
},
|
|
runtime_config: VisualNovelRuntimeConfigDraft {
|
|
text_mode_enabled: true,
|
|
default_text_mode: false,
|
|
max_history_entries: 80,
|
|
max_assistant_step_count_per_turn: 8,
|
|
allow_free_text_action: true,
|
|
allow_history_regeneration: true,
|
|
attribute_panel_mode: VisualNovelAttributePanelMode::Off,
|
|
save_archive_enabled: true,
|
|
},
|
|
publish_ready: false,
|
|
validation_issues: Vec::new(),
|
|
updated_at: "2026-05-05T00:00:00Z".to_string(),
|
|
};
|
|
|
|
let payload = serde_json::to_value(draft).expect("draft should serialize");
|
|
assert_eq!(payload["profileId"], json!("vn-profile-1"));
|
|
assert_eq!(payload["sourceMode"], json!("idea"));
|
|
assert_eq!(payload["runtimeConfig"]["attributePanelMode"], json!("off"));
|
|
|
|
let step = VisualNovelRuntimeStep::SceneChange {
|
|
scene_id: "scene-1".to_string(),
|
|
background_image_src: None,
|
|
music_src: None,
|
|
};
|
|
let step_payload = serde_json::to_value(step).expect("step should serialize");
|
|
assert_eq!(step_payload["type"], json!("scene_change"));
|
|
assert_eq!(step_payload["sceneId"], json!("scene-1"));
|
|
}
|
|
|
|
#[test]
|
|
fn audio_generation_contracts_use_camel_case_fields() {
|
|
let request = CreateVisualNovelSoundEffectRequest {
|
|
prompt: "雨声".to_string(),
|
|
duration: Some(5),
|
|
seed: Some(12),
|
|
};
|
|
let payload = serde_json::to_value(request).expect("request should serialize");
|
|
assert_eq!(payload["duration"], json!(5));
|
|
assert_eq!(payload["seed"], json!(12));
|
|
|
|
let response = VisualNovelGeneratedAudioAssetResponse {
|
|
kind: VisualNovelAudioGenerationKind::SoundEffect,
|
|
task_id: "task-1".to_string(),
|
|
provider: "vector-engine-vidu".to_string(),
|
|
status: "completed".to_string(),
|
|
asset_object_id: Some("assetobj_1".to_string()),
|
|
asset_kind: Some("visual_novel_ambient_sound".to_string()),
|
|
audio_src: Some("/generated-custom-world-scenes/a.wav".to_string()),
|
|
};
|
|
let payload = serde_json::to_value(response).expect("response should serialize");
|
|
assert_eq!(payload["kind"], json!("sound_effect"));
|
|
assert_eq!(payload["taskId"], json!("task-1"));
|
|
assert_eq!(payload["assetObjectId"], json!("assetobj_1"));
|
|
assert_eq!(
|
|
payload["audioSrc"],
|
|
json!("/generated-custom-world-scenes/a.wav")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_stream_event_uses_tagged_envelope() {
|
|
let event = VisualNovelRuntimeStreamEvent::Step {
|
|
step: VisualNovelRuntimeStep::Narration {
|
|
text: "门铃响了。".to_string(),
|
|
},
|
|
};
|
|
|
|
let payload = serde_json::to_value(event).expect("event should serialize");
|
|
assert_eq!(payload["type"], json!("step"));
|
|
assert_eq!(payload["step"]["type"], json!("narration"));
|
|
assert_eq!(payload["step"]["text"], json!("门铃响了。"));
|
|
}
|
|
}
|