fix: align puzzle and big fish persistence json fields
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use module_big_fish::{BigFishAnchorPack, BigFishCreationStage};
|
||||
use module_big_fish::{BigFishAnchorPack, BigFishAnchorStatus, BigFishCreationStage};
|
||||
use platform_llm::{LlmClient, LlmMessage, LlmStreamDelta, LlmTextRequest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value as JsonValue, json};
|
||||
@@ -226,40 +226,129 @@ fn build_chat_history(messages: &[BigFishAgentMessageRecord]) -> Vec<JsonValue>
|
||||
fn parse_big_fish_model_output(
|
||||
parsed: &JsonValue,
|
||||
) -> Result<BigFishAgentModelOutput, BigFishAgentTurnError> {
|
||||
serde_json::from_value::<BigFishAgentModelOutput>(parsed.clone())
|
||||
.map_err(|_| BigFishAgentTurnError::new("大鱼吃小鱼模型结果缺少必要内容,请稍后重试。"))
|
||||
let reply_text = parsed
|
||||
.get("replyText")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| BigFishAgentTurnError::new("大鱼吃小鱼聊天结果缺少有效回复,请稍后重试。"))?
|
||||
.to_string();
|
||||
let progress_percent = parsed
|
||||
.get("progressPercent")
|
||||
.and_then(JsonValue::as_u64)
|
||||
.map(|value| value.min(100) as u32)
|
||||
.unwrap_or(0);
|
||||
let next_anchor_pack_value = parsed
|
||||
.get("nextAnchorPack")
|
||||
.cloned()
|
||||
.ok_or_else(|| BigFishAgentTurnError::new("大鱼吃小鱼聊天结果缺少 nextAnchorPack。"))?;
|
||||
let next_anchor_pack = parse_big_fish_model_anchor_pack(&next_anchor_pack_value)?;
|
||||
Ok(BigFishAgentModelOutput {
|
||||
reply_text,
|
||||
progress_percent,
|
||||
next_anchor_pack,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_big_fish_model_anchor_pack(
|
||||
value: &JsonValue,
|
||||
) -> Result<BigFishAnchorPack, BigFishAgentTurnError> {
|
||||
Ok(BigFishAnchorPack {
|
||||
// LLM 与 HTTP 契约使用 camelCase;SpacetimeDB 持久化结构保持 Rust snake_case,边界处必须显式翻译。
|
||||
gameplay_promise: parse_big_fish_model_anchor_item(value, "gameplayPromise")?,
|
||||
ecology_visual_theme: parse_big_fish_model_anchor_item(value, "ecologyVisualTheme")?,
|
||||
growth_ladder: parse_big_fish_model_anchor_item(value, "growthLadder")?,
|
||||
risk_tempo: parse_big_fish_model_anchor_item(value, "riskTempo")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_big_fish_model_anchor_item(
|
||||
pack: &JsonValue,
|
||||
field_name: &str,
|
||||
) -> Result<module_big_fish::BigFishAnchorItem, BigFishAgentTurnError> {
|
||||
let value = pack.get(field_name).ok_or_else(|| {
|
||||
BigFishAgentTurnError::new(format!("大鱼吃小鱼 anchor pack 缺少 {field_name}。"))
|
||||
})?;
|
||||
let key = value
|
||||
.get("key")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|text| !text.is_empty())
|
||||
.unwrap_or(field_name)
|
||||
.to_string();
|
||||
let label = value
|
||||
.get("label")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|text| !text.is_empty())
|
||||
.unwrap_or_else(|| default_big_fish_anchor_label(field_name))
|
||||
.to_string();
|
||||
let item_value = value
|
||||
.get("value")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
let status = value
|
||||
.get("status")
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(parse_big_fish_anchor_status)
|
||||
.unwrap_or(BigFishAnchorStatus::Missing);
|
||||
|
||||
Ok(module_big_fish::BigFishAnchorItem {
|
||||
key,
|
||||
label,
|
||||
value: item_value,
|
||||
status,
|
||||
})
|
||||
}
|
||||
|
||||
fn default_big_fish_anchor_label(field_name: &str) -> &'static str {
|
||||
match field_name {
|
||||
"gameplayPromise" => "玩法承诺",
|
||||
"ecologyVisualTheme" => "生态视觉主题",
|
||||
"growthLadder" => "成长阶梯",
|
||||
"riskTempo" => "风险节奏",
|
||||
_ => "大鱼锚点",
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_record_anchor_pack(anchor_pack: &spacetime_client::BigFishAnchorPackRecord) -> String {
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"gameplayPromise": {
|
||||
"key": anchor_pack.gameplay_promise.key,
|
||||
"label": anchor_pack.gameplay_promise.label,
|
||||
"value": anchor_pack.gameplay_promise.value,
|
||||
"status": anchor_pack.gameplay_promise.status,
|
||||
},
|
||||
"ecologyVisualTheme": {
|
||||
"key": anchor_pack.ecology_visual_theme.key,
|
||||
"label": anchor_pack.ecology_visual_theme.label,
|
||||
"value": anchor_pack.ecology_visual_theme.value,
|
||||
"status": anchor_pack.ecology_visual_theme.status,
|
||||
},
|
||||
"growthLadder": {
|
||||
"key": anchor_pack.growth_ladder.key,
|
||||
"label": anchor_pack.growth_ladder.label,
|
||||
"value": anchor_pack.growth_ladder.value,
|
||||
"status": anchor_pack.growth_ladder.status,
|
||||
},
|
||||
"riskTempo": {
|
||||
"key": anchor_pack.risk_tempo.key,
|
||||
"label": anchor_pack.risk_tempo.label,
|
||||
"value": anchor_pack.risk_tempo.value,
|
||||
"status": anchor_pack.risk_tempo.status,
|
||||
},
|
||||
}))
|
||||
serde_json::to_string_pretty(&map_big_fish_record_anchor_pack(anchor_pack))
|
||||
.unwrap_or_else(|_| "{}".to_string())
|
||||
}
|
||||
|
||||
fn map_big_fish_record_anchor_pack(
|
||||
record: &spacetime_client::BigFishAnchorPackRecord,
|
||||
) -> BigFishAnchorPack {
|
||||
BigFishAnchorPack {
|
||||
gameplay_promise: map_big_fish_record_anchor_item(&record.gameplay_promise),
|
||||
ecology_visual_theme: map_big_fish_record_anchor_item(&record.ecology_visual_theme),
|
||||
growth_ladder: map_big_fish_record_anchor_item(&record.growth_ladder),
|
||||
risk_tempo: map_big_fish_record_anchor_item(&record.risk_tempo),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_big_fish_record_anchor_item(
|
||||
record: &spacetime_client::BigFishAnchorItemRecord,
|
||||
) -> module_big_fish::BigFishAnchorItem {
|
||||
module_big_fish::BigFishAnchorItem {
|
||||
key: record.key.clone(),
|
||||
label: record.label.clone(),
|
||||
value: record.value.clone(),
|
||||
status: parse_big_fish_anchor_status(record.status.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_big_fish_anchor_status(value: &str) -> BigFishAnchorStatus {
|
||||
match value {
|
||||
"confirmed" => BigFishAnchorStatus::Confirmed,
|
||||
"locked" => BigFishAnchorStatus::Locked,
|
||||
"inferred" => BigFishAnchorStatus::Inferred,
|
||||
_ => BigFishAnchorStatus::Missing,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_json_response_text(text: &str) -> Result<JsonValue, serde_json::Error> {
|
||||
if let Ok(value) = serde_json::from_str::<JsonValue>(text) {
|
||||
return Ok(value);
|
||||
|
||||
@@ -462,17 +462,7 @@ pub async fn execute_puzzle_agent_action(
|
||||
let candidates_json = serde_json::to_string(
|
||||
&candidates
|
||||
.iter()
|
||||
.map(|candidate| {
|
||||
json!({
|
||||
"candidateId": candidate.candidate_id,
|
||||
"imageSrc": candidate.image_src,
|
||||
"assetId": candidate.asset_id,
|
||||
"prompt": candidate.prompt,
|
||||
"actualPrompt": candidate.actual_prompt,
|
||||
"sourceType": candidate.source_type,
|
||||
"selected": candidate.selected,
|
||||
})
|
||||
})
|
||||
.map(to_puzzle_generated_image_candidate)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.map_err(|error| {
|
||||
@@ -1473,6 +1463,21 @@ struct PuzzleDownloadedImage {
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
fn to_puzzle_generated_image_candidate(
|
||||
candidate: &PuzzleGeneratedImageCandidateRecord,
|
||||
) -> PuzzleGeneratedImageCandidate {
|
||||
// SpacetimeDB ???????? module-puzzle ??????????? snake_case ????HTTP ????????? camelCase?
|
||||
PuzzleGeneratedImageCandidate {
|
||||
candidate_id: candidate.candidate_id.clone(),
|
||||
image_src: candidate.image_src.clone(),
|
||||
asset_id: candidate.asset_id.clone(),
|
||||
prompt: candidate.prompt.clone(),
|
||||
actual_prompt: candidate.actual_prompt.clone(),
|
||||
source_type: candidate.source_type.clone(),
|
||||
selected: candidate.selected,
|
||||
}
|
||||
}
|
||||
|
||||
struct GeneratedPuzzleAssetResponse {
|
||||
image_src: String,
|
||||
asset_id: String,
|
||||
|
||||
Reference in New Issue
Block a user