Files
Genarrative/server-rs/crates/shared-contracts/src/puzzle_agent.rs
高物 7b37271f17 Puzzle: support history images & partial generation
Allow history-generated image paths to be submitted where Data URLs were previously required and avoid treating partial/result-page generations as blocking the whole draft. Backend: resolve history /generated-* references via resolve_puzzle_reference_image_as_data_url and convert to PuzzleDownloadedImage; add PuzzleDownloadedImage::from_resolved_reference_image; extend draft handling to apply generated level metadata (auto-naming) and normalize generation_status to treat levels with images as ready. API: add shouldAutoNameLevel to action contracts and use it to request/refine generated level names. Spacetime/module and mappers: normalize completed level statuses when saving/reading so result-page background or per-level generation doesn't mask completed drafts. Frontend: expose resolver helpers, only mark a work as generating when no usable cover or ready level exists, keep level controls enabled during UI-background regeneration, and add tests covering history-image submission, auto-naming, and UI-background/partial-generation behaviors.
2026-05-19 10:02:13 +08:00

272 lines
8.4 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::creation_audio::CreationAudioAsset;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CreatePuzzleAgentSessionRequest {
#[serde(default)]
pub seed_text: Option<String>,
#[serde(default)]
pub work_title: Option<String>,
#[serde(default)]
pub work_description: Option<String>,
#[serde(default)]
pub picture_description: Option<String>,
#[serde(default)]
pub reference_image_src: Option<String>,
#[serde(default)]
pub reference_image_srcs: Vec<String>,
#[serde(default)]
pub image_model: Option<String>,
#[serde(default)]
pub ai_redraw: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SendPuzzleAgentMessageRequest {
pub client_message_id: String,
pub text: String,
#[serde(default)]
pub quick_fill_requested: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ExecutePuzzleAgentActionRequest {
pub action: String,
#[serde(default)]
pub prompt_text: Option<String>,
#[serde(default)]
pub reference_image_src: Option<String>,
#[serde(default)]
pub reference_image_srcs: Vec<String>,
#[serde(default)]
pub image_model: Option<String>,
#[serde(default)]
pub ai_redraw: Option<bool>,
#[serde(default)]
pub candidate_count: Option<u32>,
#[serde(default)]
pub should_auto_name_level: Option<bool>,
#[serde(default)]
pub candidate_id: Option<String>,
#[serde(default)]
pub level_id: Option<String>,
#[serde(default)]
pub work_title: Option<String>,
#[serde(default)]
pub work_description: Option<String>,
#[serde(default)]
pub picture_description: Option<String>,
#[serde(default)]
pub level_name: Option<String>,
#[serde(default)]
pub summary: Option<String>,
#[serde(default)]
pub theme_tags: Option<Vec<String>>,
#[serde(default)]
pub levels_json: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleFormDraftResponse {
#[serde(default)]
pub work_title: Option<String>,
#[serde(default)]
pub work_description: Option<String>,
#[serde(default)]
pub picture_description: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAnchorItemResponse {
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 PuzzleAnchorPackResponse {
pub theme_promise: PuzzleAnchorItemResponse,
pub visual_subject: PuzzleAnchorItemResponse,
pub visual_mood: PuzzleAnchorItemResponse,
pub composition_hooks: PuzzleAnchorItemResponse,
pub tags_and_forbidden: PuzzleAnchorItemResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleGeneratedImageCandidateResponse {
pub candidate_id: String,
pub image_src: String,
pub asset_id: String,
pub prompt: String,
#[serde(default)]
pub actual_prompt: Option<String>,
pub source_type: String,
pub selected: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleCreatorIntentResponse {
pub source_mode: String,
pub raw_messages_summary: String,
pub theme_promise: String,
pub visual_subject: String,
pub visual_mood: Vec<String>,
pub composition_hooks: Vec<String>,
pub theme_tags: Vec<String>,
pub forbidden_directives: Vec<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleResultDraftResponse {
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
pub forbidden_directives: Vec<String>,
#[serde(default)]
pub creator_intent: Option<PuzzleCreatorIntentResponse>,
pub anchor_pack: PuzzleAnchorPackResponse,
pub candidates: Vec<PuzzleGeneratedImageCandidateResponse>,
#[serde(default)]
pub selected_candidate_id: Option<String>,
#[serde(default)]
pub cover_image_src: Option<String>,
#[serde(default)]
pub cover_asset_id: Option<String>,
pub generation_status: String,
pub levels: Vec<PuzzleDraftLevelResponse>,
#[serde(default)]
pub form_draft: Option<PuzzleFormDraftResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleDraftLevelResponse {
pub level_id: String,
pub level_name: String,
pub picture_description: String,
#[serde(default)]
pub picture_reference: Option<String>,
#[serde(default)]
pub ui_background_prompt: Option<String>,
#[serde(default)]
pub ui_background_image_src: Option<String>,
#[serde(default)]
pub ui_background_image_object_key: Option<String>,
#[serde(default)]
pub background_music: Option<CreationAudioAsset>,
pub candidates: Vec<PuzzleGeneratedImageCandidateResponse>,
#[serde(default)]
pub selected_candidate_id: Option<String>,
#[serde(default)]
pub cover_image_src: Option<String>,
#[serde(default)]
pub cover_asset_id: Option<String>,
pub generation_status: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentMessageResponse {
pub id: String,
pub role: String,
pub kind: String,
pub text: String,
pub created_at: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentSuggestedActionResponse {
pub id: String,
pub action_type: String,
pub label: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleResultPreviewBlockerResponse {
pub id: String,
pub code: String,
pub message: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleResultPreviewFindingResponse {
pub id: String,
pub severity: String,
pub code: String,
pub message: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleResultPreviewEnvelopeResponse {
pub draft: PuzzleResultDraftResponse,
pub blockers: Vec<PuzzleResultPreviewBlockerResponse>,
pub quality_findings: Vec<PuzzleResultPreviewFindingResponse>,
pub publish_ready: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentSessionSnapshotResponse {
pub session_id: String,
pub seed_text: String,
pub current_turn: u32,
pub progress_percent: u32,
pub stage: String,
pub anchor_pack: PuzzleAnchorPackResponse,
#[serde(default)]
pub draft: Option<PuzzleResultDraftResponse>,
pub messages: Vec<PuzzleAgentMessageResponse>,
#[serde(default)]
pub last_assistant_reply: Option<String>,
#[serde(default)]
pub published_profile_id: Option<String>,
pub suggested_actions: Vec<PuzzleAgentSuggestedActionResponse>,
#[serde(default)]
pub result_preview: Option<PuzzleResultPreviewEnvelopeResponse>,
pub updated_at: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentSessionResponse {
pub session: PuzzleAgentSessionSnapshotResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentOperationResponse {
pub operation_id: String,
pub operation_type: String,
pub status: String,
pub phase_label: String,
pub phase_detail: String,
pub progress: u32,
#[serde(default)]
pub error: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleAgentActionResponse {
pub operation: PuzzleAgentOperationResponse,
/// 操作完成后的最新会话快照,供前端直接更新界面,避免重复拉取完整 session。
pub session: PuzzleAgentSessionSnapshotResponse,
}