This commit is contained in:
2026-04-30 17:49:07 +08:00
parent 805d6f8cae
commit 9d684cb7b3
615 changed files with 15368 additions and 6172 deletions

View File

@@ -1,7 +1,7 @@
use axum::{
Router,
body::Body,
extract::Extension,
extract::{DefaultBodyLimit, Extension},
http::Request,
middleware,
routing::{delete, get, post},
@@ -34,8 +34,9 @@ use crate::{
auth_sessions::auth_sessions,
big_fish::{
create_big_fish_session, delete_big_fish_work, execute_big_fish_action,
get_big_fish_session, get_big_fish_works, list_big_fish_gallery, record_big_fish_play,
remix_big_fish_gallery_work, stream_big_fish_message, submit_big_fish_message,
get_big_fish_session, get_big_fish_works, list_big_fish_gallery,
record_big_fish_gallery_like, record_big_fish_play, remix_big_fish_gallery_work,
stream_big_fish_message, submit_big_fish_message,
},
character_animation_assets::{
generate_character_animation, get_character_animation_job, get_character_workflow_cache,
@@ -56,9 +57,9 @@ use crate::{
get_custom_world_gallery_detail_by_code, get_custom_world_library,
get_custom_world_library_detail, get_custom_world_works, list_custom_world_gallery,
publish_custom_world_library_profile, put_custom_world_library_profile,
record_custom_world_gallery_play, remix_custom_world_gallery_profile,
stream_custom_world_agent_message, submit_custom_world_agent_message,
unpublish_custom_world_library_profile,
record_custom_world_gallery_like, record_custom_world_gallery_play,
remix_custom_world_gallery_profile, stream_custom_world_agent_message,
submit_custom_world_agent_message, unpublish_custom_world_library_profile,
},
custom_world_ai::{
generate_custom_world_cover_image, generate_custom_world_entity,
@@ -85,9 +86,10 @@ use crate::{
advance_local_puzzle_next_level, advance_puzzle_next_level, create_puzzle_agent_session,
delete_puzzle_work, execute_puzzle_agent_action, get_puzzle_agent_session,
get_puzzle_gallery_detail, get_puzzle_run, get_puzzle_work_detail, get_puzzle_works,
list_puzzle_gallery, put_puzzle_work, remix_puzzle_gallery_work, start_puzzle_run,
stream_puzzle_agent_message, submit_puzzle_agent_message, submit_puzzle_leaderboard,
swap_puzzle_pieces, update_puzzle_run_pause, use_puzzle_runtime_prop,
list_puzzle_gallery, put_puzzle_work, record_puzzle_gallery_like,
remix_puzzle_gallery_work, start_puzzle_run, stream_puzzle_agent_message,
submit_puzzle_agent_message, submit_puzzle_leaderboard, swap_puzzle_pieces,
update_puzzle_run_pause, use_puzzle_runtime_prop,
},
refresh_session::refresh_session,
request_context::{attach_request_context, resolve_request_id},
@@ -126,6 +128,8 @@ use crate::{
wechat_auth::{bind_wechat_phone, handle_wechat_callback, start_wechat_login},
};
const PUZZLE_REFERENCE_IMAGE_BODY_LIMIT_BYTES: usize = 12 * 1024 * 1024;
// 统一由这里构造 Axum 路由树,后续再逐项挂接中间件与业务路由。
pub fn build_router(state: AppState) -> Router {
let slow_request_threshold_ms = state.config.slow_request_threshold_ms;
@@ -544,6 +548,13 @@ pub fn build_router(state: AppState) -> Router {
require_bearer_auth,
)),
)
.route(
"/api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}/like",
post(record_custom_world_gallery_like).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/custom-world-gallery/by-code/{code}",
get(get_custom_world_gallery_detail_by_code),
@@ -663,6 +674,13 @@ pub fn build_router(state: AppState) -> Router {
require_bearer_auth,
)),
)
.route(
"/api/runtime/big-fish/gallery/{session_id}/like",
post(record_big_fish_gallery_like).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/big-fish/works/{session_id}",
delete(delete_big_fish_work).route_layer(middleware::from_fn_with_state(
@@ -686,10 +704,15 @@ pub fn build_router(state: AppState) -> Router {
)
.route(
"/api/runtime/puzzle/agent/sessions",
post(create_puzzle_agent_session).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
post(create_puzzle_agent_session)
// 中文注释:拼图表单会携带单张参考图 Data URL需只给该写入入口放宽 body 上限。
.layer(DefaultBodyLimit::max(
PUZZLE_REFERENCE_IMAGE_BODY_LIMIT_BYTES,
))
.route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/puzzle/agent/sessions/{session_id}",
@@ -714,10 +737,15 @@ pub fn build_router(state: AppState) -> Router {
)
.route(
"/api/runtime/puzzle/agent/sessions/{session_id}/actions",
post(execute_puzzle_agent_action).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
post(execute_puzzle_agent_action)
// 中文注释:生成草稿/重新出图会复用 referenceImageSrc避免默认 2MB JSON limit 拦截。
.layer(DefaultBodyLimit::max(
PUZZLE_REFERENCE_IMAGE_BODY_LIMIT_BYTES,
))
.route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/puzzle/works",
@@ -748,6 +776,13 @@ pub fn build_router(state: AppState) -> Router {
require_bearer_auth,
)),
)
.route(
"/api/runtime/puzzle/gallery/{profile_id}/like",
post(record_puzzle_gallery_like).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/puzzle/runs",
post(start_puzzle_run).route_layer(middleware::from_fn_with_state(
@@ -1239,6 +1274,30 @@ mod tests {
.await
}
fn sign_test_user_token(
state: &AppState,
user: &module_auth::AuthUser,
session_id: &str,
) -> String {
let claims = AccessTokenClaims::from_input(
AccessTokenClaimsInput {
user_id: user.id.clone(),
session_id: session_id.to_string(),
provider: AuthProvider::Password,
roles: vec!["user".to_string()],
token_version: user.token_version,
phone_verified: false,
binding_status: BindingStatus::Active,
display_name: Some(user.display_name.clone()),
},
state.auth_jwt_config(),
OffsetDateTime::now_utc(),
)
.expect("claims should build");
sign_access_token(&claims, state.auth_jwt_config()).expect("token should sign")
}
async fn password_login_request(
app: Router,
phone_number: &str,
@@ -1496,6 +1555,88 @@ mod tests {
);
}
#[tokio::test]
async fn puzzle_agent_actions_accept_reference_image_body_above_default_limit() {
let state = AppState::new(AppConfig::default()).expect("state should build");
let seed_user = seed_phone_user_with_password(&state, "13800138024", TEST_PASSWORD).await;
let token = sign_test_user_token(&state, &seed_user, "sess_puzzle_reference_body");
let app = build_router(state);
let reference_image_src = format!("data:image/png;base64,{}", "A".repeat(3 * 1024 * 1024));
let request_body = serde_json::json!({
"action": "unsupported_large_reference_test",
"referenceImageSrc": reference_image_src,
})
.to_string();
assert!(request_body.len() > 2 * 1024 * 1024);
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/api/runtime/puzzle/agent/sessions/puzzle-session-large/actions")
.header("authorization", format!("Bearer {token}"))
.header("content-type", "application/json")
.body(Body::from(request_body))
.expect("request should build"),
)
.await
.expect("request should succeed");
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response
.into_body()
.collect()
.await
.expect("response body should collect")
.to_bytes();
let body_text = String::from_utf8_lossy(&body);
assert!(
body_text.contains("unsupported_large_reference_test"),
"handler should parse the oversized reference payload before rejecting the action: {body_text}"
);
assert!(!body_text.contains("length limit exceeded"));
}
#[tokio::test]
async fn puzzle_agent_session_creation_accepts_reference_image_body_above_default_limit() {
let state = AppState::new(AppConfig::default()).expect("state should build");
let seed_user = seed_phone_user_with_password(&state, "13800138025", TEST_PASSWORD).await;
let token = sign_test_user_token(&state, &seed_user, "sess_puzzle_form_reference_body");
let app = build_router(state);
let request_body = format!(
"{{\"seedText\":\"大参考图拼图\",\"pictureDescription\":\"一张用于验证 body limit 的参考图。\",\"referenceImageSrc\":\"data:image/png;base64,{}\"",
"A".repeat(3 * 1024 * 1024)
);
assert!(request_body.len() > 2 * 1024 * 1024);
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/api/runtime/puzzle/agent/sessions")
.header("authorization", format!("Bearer {token}"))
.header("content-type", "application/json")
.body(Body::from(request_body))
.expect("request should build"),
)
.await
.expect("request should succeed");
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
let body = response
.into_body()
.collect()
.await
.expect("response body should collect")
.to_bytes();
let body_text = String::from_utf8_lossy(&body);
assert!(
body_text.contains("EOF") || body_text.contains("expected"),
"handler should parse the oversized form payload before rejecting malformed JSON: {body_text}"
);
assert!(!body_text.contains("length limit exceeded"));
}
#[tokio::test]
async fn password_entry_rejects_unknown_phone_without_registration() {
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));

View File

@@ -85,8 +85,13 @@ async fn refund_asset_operation_points(
}
pub(crate) fn map_asset_operation_wallet_error(error: SpacetimeClientError) -> AppError {
let message = error.to_string();
tracing::warn!(
provider = "profile-wallet",
error = %message,
"资产操作陶泥币预扣失败"
);
let status = match &error {
SpacetimeClientError::Runtime(_) => StatusCode::BAD_REQUEST,
SpacetimeClientError::Procedure(message) if message.contains("陶泥币余额不足") => {
StatusCode::CONFLICT
}
@@ -95,7 +100,7 @@ pub(crate) fn map_asset_operation_wallet_error(error: SpacetimeClientError) -> A
AppError::from_status(status).with_details(json!({
"provider": "profile-wallet",
"message": error.to_string(),
"message": message,
}))
}

View File

@@ -33,9 +33,10 @@ use spacetime_client::{
BigFishAgentMessageRecord, BigFishAnchorItemRecord, BigFishAnchorPackRecord,
BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord,
BigFishBackgroundBlueprintRecord, BigFishDraftCompileRecordInput, BigFishGameDraftRecord,
BigFishLevelBlueprintRecord, BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput,
BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput, BigFishSessionRecord,
BigFishWorkRemixRecordInput, BigFishWorkSummaryRecord, SpacetimeClientError,
BigFishLevelBlueprintRecord, BigFishLikeReportRecordInput, BigFishMessageSubmitRecordInput,
BigFishPlayReportRecordInput, BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput,
BigFishSessionRecord, BigFishWorkRemixRecordInput, BigFishWorkSummaryRecord,
SpacetimeClientError,
};
use tokio::time::sleep;
@@ -60,6 +61,7 @@ use crate::{
http_error::AppError,
request_context::RequestContext,
state::AppState,
work_author::resolve_work_author_by_user_id,
};
pub async fn create_big_fish_session(
@@ -144,7 +146,7 @@ pub async fn get_big_fish_works(
BigFishWorksResponse {
items: items
.into_iter()
.map(map_big_fish_work_summary_response)
.map(|item| map_big_fish_work_summary_response(&state, item))
.collect(),
},
))
@@ -176,7 +178,7 @@ pub async fn list_big_fish_gallery(
BigFishWorksResponse {
items: items
.into_iter()
.map(map_big_fish_work_summary_response)
.map(|item| map_big_fish_work_summary_response(&state, item))
.collect(),
},
))
@@ -203,7 +205,7 @@ pub async fn delete_big_fish_work(
BigFishWorksResponse {
items: items
.into_iter()
.map(map_big_fish_work_summary_response)
.map(|item| map_big_fish_work_summary_response(&state, item))
.collect(),
},
))
@@ -245,7 +247,38 @@ pub async fn record_big_fish_play(
BigFishWorksResponse {
items: items
.into_iter()
.map(map_big_fish_work_summary_response)
.map(|item| map_big_fish_work_summary_response(&state, item))
.collect(),
},
))
}
pub async fn record_big_fish_gallery_like(
State(state): State<AppState>,
Path(session_id): Path<String>,
Extension(request_context): Extension<RequestContext>,
Extension(authenticated): Extension<AuthenticatedAccessToken>,
) -> Result<Json<Value>, Response> {
ensure_non_empty(&request_context, &session_id, "sessionId")?;
let items = state
.spacetime_client()
.record_big_fish_like(BigFishLikeReportRecordInput {
session_id,
user_id: authenticated.claims().user_id().to_string(),
liked_at_micros: current_utc_micros(),
})
.await
.map_err(|error| {
big_fish_error_response(&request_context, map_big_fish_client_error(error))
})?;
Ok(json_success_body(
Some(&request_context),
BigFishWorksResponse {
items: items
.into_iter()
.map(|item| map_big_fish_work_summary_response(&state, item))
.collect(),
},
))
@@ -924,12 +957,15 @@ fn map_big_fish_agent_message_response(
}
fn map_big_fish_work_summary_response(
state: &AppState,
item: BigFishWorkSummaryRecord,
) -> BigFishWorkSummaryResponse {
let author = resolve_work_author_by_user_id(state, &item.owner_user_id, None, None);
BigFishWorkSummaryResponse {
work_id: item.work_id,
source_session_id: item.source_session_id,
owner_user_id: item.owner_user_id,
author_display_name: author.display_name,
title: item.title,
subtitle: item.subtitle,
summary: item.summary,

View File

@@ -7,6 +7,7 @@ use serde::Deserialize;
use serde_json::Value as JsonValue;
use crate::creation_agent_llm_turn::parse_json_response_text;
use crate::llm_model_routing::CREATION_TEMPLATE_LLM_MODEL;
const BIG_FISH_DRAFT_JSON_ONLY_SYSTEM_PROMPT: &str = r#"你是一个负责把“大鱼吃小鱼”玩法锚点编译成首版可实现草稿的中文玩法策划。
@@ -108,10 +109,15 @@ async fn request_big_fish_json_stage(
empty_response_message: &str,
) -> Result<JsonValue, BigFishDraftCompileError> {
let response = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(BIG_FISH_DRAFT_JSON_ONLY_SYSTEM_PROMPT),
LlmMessage::user(user_prompt),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(BIG_FISH_DRAFT_JSON_ONLY_SYSTEM_PROMPT),
LlmMessage::user(user_prompt),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(true),
)
.await
.map_err(|error| {
BigFishDraftCompileError::new(format!("{debug_label} LLM 请求失败:{error}"))
@@ -124,12 +130,16 @@ async fn request_big_fish_json_stage(
Ok(value) => Ok(value),
Err(_) => {
let repaired = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(BIG_FISH_DRAFT_JSON_REPAIR_SYSTEM_PROMPT),
LlmMessage::user(format!(
"请把下面这段文本修复成单个合法 JSON 对象,不要补充额外解释:\n\n{text}"
)),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(BIG_FISH_DRAFT_JSON_REPAIR_SYSTEM_PROMPT),
LlmMessage::user(format!(
"请把下面这段文本修复成单个合法 JSON 对象,不要补充额外解释:\n\n{text}"
)),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api(),
)
.await
.map_err(|error| {
BigFishDraftCompileError::new(format!(

View File

@@ -1,6 +1,8 @@
use platform_llm::{LlmClient, LlmMessage, LlmStreamDelta, LlmTextRequest};
use serde_json::Value as JsonValue;
use crate::llm_model_routing::CREATION_TEMPLATE_LLM_MODEL;
#[derive(Clone, Copy, Debug)]
pub(crate) struct CreationAgentLlmTurnErrorMessages<'a> {
pub model_unavailable: &'a str,
@@ -69,6 +71,8 @@ fn build_creation_agent_llm_request(
LlmMessage::system(system_prompt),
LlmMessage::user(user_prompt),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(enable_web_search)
}
@@ -79,10 +83,14 @@ pub(crate) async fn request_creation_agent_json_turn<E>(
build_error: impl Fn(String) -> E,
) -> Result<JsonValue, E> {
let response = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(system_prompt),
LlmMessage::user(user_prompt),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(system_prompt),
LlmMessage::user(user_prompt),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api(),
)
.await
.map_err(|error| build_error(error.to_string()))?;
parse_json_response_text(response.content.as_str())
@@ -160,6 +168,8 @@ fn read_reply_text(parsed: &JsonValue) -> Option<String> {
#[cfg(test)]
mod tests {
use crate::llm_model_routing::CREATION_TEMPLATE_LLM_MODEL;
use super::{
build_creation_agent_llm_request, extract_reply_text_from_partial_json,
parse_json_response_text,
@@ -188,6 +198,8 @@ mod tests {
build_creation_agent_llm_request("系统提示".to_string(), "用户提示".to_string(), true);
assert!(request.enable_web_search);
assert_eq!(request.model.as_deref(), Some(CREATION_TEMPLATE_LLM_MODEL));
assert_eq!(request.protocol, platform_llm::LlmTextProtocol::Responses);
assert_eq!(request.messages.len(), 2);
}
}

View File

@@ -38,10 +38,10 @@ use spacetime_client::{
CustomWorldAgentSessionRecord, CustomWorldDraftCardDetailRecord,
CustomWorldDraftCardDetailSectionRecord, CustomWorldDraftCardRecord,
CustomWorldGalleryEntryRecord, CustomWorldLibraryEntryRecord,
CustomWorldProfilePlayReportRecordInput, CustomWorldProfileRemixRecordInput,
CustomWorldProfileUpsertRecordInput, CustomWorldPublishGateRecord,
CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord,
CustomWorldWorkSummaryRecord, SpacetimeClientError,
CustomWorldProfileLikeReportRecordInput, CustomWorldProfilePlayReportRecordInput,
CustomWorldProfileRemixRecordInput, CustomWorldProfileUpsertRecordInput,
CustomWorldPublishGateRecord, CustomWorldResultPreviewBlockerRecord,
CustomWorldSupportedActionRecord, CustomWorldWorkSummaryRecord, SpacetimeClientError,
};
use std::{collections::BTreeSet, convert::Infallible, sync::Arc, time::Instant};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
@@ -73,6 +73,7 @@ use crate::{
},
request_context::RequestContext,
state::AppState,
work_author::resolve_work_author_by_user_id,
};
const DRAFT_ASSET_GENERATION_MAX_ATTEMPTS: u32 = 3;
@@ -414,7 +415,6 @@ pub async fn get_custom_world_library(
Extension(authenticated): Extension<AuthenticatedAccessToken>,
) -> Result<Json<Value>, Response> {
let owner_user_id = authenticated.claims().user_id().to_string();
let author_display_name = resolve_author_display_name(&state, &authenticated);
let entries = state
.spacetime_client()
.list_custom_world_works(owner_user_id.clone())
@@ -430,9 +430,9 @@ pub async fn get_custom_world_library(
.into_iter()
.filter_map(|item| {
map_custom_world_library_entry_response_from_work_summary(
&state,
item,
&owner_user_id,
&author_display_name,
)
})
.collect(),
@@ -467,7 +467,7 @@ pub async fn get_custom_world_library_detail(
Ok(json_success_body(
Some(&request_context),
CustomWorldGalleryDetailResponse {
entry: map_custom_world_library_entry_response(detail.entry),
entry: map_custom_world_library_entry_response(&state, detail.entry),
},
))
}
@@ -548,8 +548,11 @@ pub async fn put_custom_world_library_profile(
Ok(json_success_body(
Some(&request_context),
CustomWorldLibraryMutationResponse {
entry: map_custom_world_library_entry_response(mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(mutation.entry)],
entry: map_custom_world_library_entry_response(&state, mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(
&state,
mutation.entry,
)],
},
))
}
@@ -584,7 +587,7 @@ pub async fn delete_custom_world_library_profile(
CustomWorldLibraryResponse {
entries: entries
.into_iter()
.map(map_custom_world_library_entry_response)
.map(|entry| map_custom_world_library_entry_response(&state, entry))
.collect(),
},
))
@@ -636,8 +639,11 @@ pub async fn publish_custom_world_library_profile(
Ok(json_success_body(
Some(&request_context),
CustomWorldLibraryMutationResponse {
entry: map_custom_world_library_entry_response(mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(mutation.entry)],
entry: map_custom_world_library_entry_response(&state, mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(
&state,
mutation.entry,
)],
},
))
}
@@ -675,8 +681,11 @@ pub async fn unpublish_custom_world_library_profile(
Ok(json_success_body(
Some(&request_context),
CustomWorldLibraryMutationResponse {
entry: map_custom_world_library_entry_response(mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(mutation.entry)],
entry: map_custom_world_library_entry_response(&state, mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(
&state,
mutation.entry,
)],
},
))
}
@@ -698,7 +707,7 @@ pub async fn list_custom_world_gallery(
CustomWorldGalleryResponse {
entries: entries
.into_iter()
.map(map_custom_world_gallery_card_response)
.map(|entry| map_custom_world_gallery_card_response(&state, entry))
.collect(),
},
))
@@ -730,7 +739,7 @@ pub async fn get_custom_world_gallery_detail(
Ok(json_success_body(
Some(&request_context),
CustomWorldGalleryDetailResponse {
entry: map_custom_world_library_entry_response(detail.entry),
entry: map_custom_world_library_entry_response(&state, detail.entry),
},
))
}
@@ -761,7 +770,7 @@ pub async fn get_custom_world_gallery_detail_by_code(
Ok(json_success_body(
Some(&request_context),
CustomWorldGalleryDetailResponse {
entry: map_custom_world_library_entry_response(detail.entry),
entry: map_custom_world_library_entry_response(&state, detail.entry),
},
))
}
@@ -800,8 +809,11 @@ pub async fn remix_custom_world_gallery_profile(
Ok(json_success_body(
Some(&request_context),
CustomWorldLibraryMutationResponse {
entry: map_custom_world_library_entry_response(mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(mutation.entry)],
entry: map_custom_world_library_entry_response(&state, mutation.entry.clone()),
entries: vec![map_custom_world_library_entry_response(
&state,
mutation.entry,
)],
},
))
}
@@ -837,7 +849,44 @@ pub async fn record_custom_world_gallery_play(
Ok(json_success_body(
Some(&request_context),
CustomWorldGalleryDetailResponse {
entry: map_custom_world_library_entry_response(mutation.entry),
entry: map_custom_world_library_entry_response(&state, mutation.entry),
},
))
}
pub async fn record_custom_world_gallery_like(
State(state): State<AppState>,
Path((owner_user_id, profile_id)): Path<(String, String)>,
Extension(request_context): Extension<RequestContext>,
Extension(authenticated): Extension<AuthenticatedAccessToken>,
) -> Result<Json<Value>, Response> {
if owner_user_id.trim().is_empty() || profile_id.trim().is_empty() {
return Err(custom_world_error_response(
&request_context,
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": "custom-world-gallery",
"message": "ownerUserId and profileId are required",
})),
));
}
let mutation = state
.spacetime_client()
.record_custom_world_profile_like(CustomWorldProfileLikeReportRecordInput {
owner_user_id,
profile_id,
user_id: authenticated.claims().user_id().to_string(),
liked_at_micros: current_utc_micros(),
})
.await
.map_err(|error| {
custom_world_error_response(&request_context, map_custom_world_client_error(error))
})?;
Ok(json_success_body(
Some(&request_context),
CustomWorldGalleryDetailResponse {
entry: map_custom_world_library_entry_response(&state, mutation.entry),
},
))
}
@@ -2697,18 +2746,25 @@ async fn upsert_custom_world_draft_foundation_progress(
}
fn map_custom_world_library_entry_response(
state: &AppState,
entry: CustomWorldLibraryEntryRecord,
) -> CustomWorldLibraryEntryResponse {
let author = resolve_work_author_by_user_id(
state,
&entry.owner_user_id,
Some(&entry.author_display_name),
entry.author_public_user_code.as_deref(),
);
CustomWorldLibraryEntryResponse {
owner_user_id: entry.owner_user_id,
profile_id: entry.profile_id,
public_work_code: entry.public_work_code,
author_public_user_code: entry.author_public_user_code,
author_public_user_code: author.public_user_code.or(entry.author_public_user_code),
profile: entry.profile,
visibility: entry.visibility,
published_at: entry.published_at,
updated_at: entry.updated_at,
author_display_name: entry.author_display_name,
author_display_name: author.display_name,
world_name: entry.world_name,
subtitle: entry.subtitle,
summary_text: entry.summary_text,
@@ -2724,23 +2780,24 @@ fn map_custom_world_library_entry_response(
}
fn map_custom_world_library_entry_response_from_work_summary(
state: &AppState,
item: CustomWorldWorkSummaryRecord,
owner_user_id: &str,
author_display_name: &str,
) -> Option<CustomWorldLibraryEntryResponse> {
let profile_id = item.profile_id.as_ref()?.clone();
let profile = build_custom_world_library_list_profile_payload(&item, &profile_id);
let author = resolve_work_author_by_user_id(state, owner_user_id, None, None);
Some(CustomWorldLibraryEntryResponse {
owner_user_id: owner_user_id.to_string(),
public_work_code: (item.status == "published")
.then(|| build_public_work_code_from_profile_id(&profile_id)),
profile_id,
author_public_user_code: None,
author_public_user_code: author.public_user_code,
profile,
visibility: item.status,
published_at: item.published_at,
updated_at: item.updated_at,
author_display_name: author_display_name.to_string(),
author_display_name: author.display_name,
world_name: item.title,
subtitle: item.subtitle,
summary_text: item.summary,
@@ -2803,17 +2860,26 @@ fn build_custom_world_library_list_profile_payload(
}
fn map_custom_world_gallery_card_response(
state: &AppState,
entry: CustomWorldGalleryEntryRecord,
) -> CustomWorldGalleryCardResponse {
let author = resolve_work_author_by_user_id(
state,
&entry.owner_user_id,
Some(&entry.author_display_name),
Some(&entry.author_public_user_code),
);
CustomWorldGalleryCardResponse {
owner_user_id: entry.owner_user_id,
profile_id: entry.profile_id,
public_work_code: entry.public_work_code,
author_public_user_code: entry.author_public_user_code,
author_public_user_code: author
.public_user_code
.unwrap_or(entry.author_public_user_code),
visibility: entry.visibility,
published_at: entry.published_at,
updated_at: entry.updated_at,
author_display_name: entry.author_display_name,
author_display_name: author.display_name,
world_name: entry.world_name,
subtitle: entry.subtitle,
summary_text: entry.summary_text,

View File

@@ -1,6 +1,8 @@
use platform_llm::{LlmClient, LlmMessage, LlmTextRequest};
use serde_json::{Map as JsonMap, Value as JsonValue};
use shared_contracts::runtime::ExecuteCustomWorldAgentActionRequest;
use crate::llm_model_routing::CREATION_TEMPLATE_LLM_MODEL;
use spacetime_client::CustomWorldAgentSessionRecord;
const CUSTOM_WORLD_AGENT_CHARACTER_EXPANSION_SYSTEM_PROMPT: &str =
@@ -92,10 +94,15 @@ pub async fn generate_custom_world_agent_entities(
};
let response = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(system_prompt),
LlmMessage::user(user_prompt),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(system_prompt),
LlmMessage::user(user_prompt),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(true),
)
.await
.map_err(|error| format!("{action} LLM 请求失败:{error}"))?;
let generated_entities = parse_json_array_response(response.content.as_str())

View File

@@ -35,6 +35,7 @@ use crate::{
build_result_scene_npc_system_prompt, build_result_scene_npc_user_prompt,
},
http_error::AppError,
llm_model_routing::CREATION_TEMPLATE_LLM_MODEL,
prompt::scene_background::{
DEFAULT_CUSTOM_WORLD_SCENE_IMAGE_NEGATIVE_PROMPT, SceneImagePromptLandmark,
SceneImagePromptParams, SceneImagePromptProfile, build_custom_world_scene_image_prompt,
@@ -1039,7 +1040,10 @@ async fn generate_entity_with_fallback(state: &AppState, profile: &Value, kind:
let request = LlmTextRequest::new(vec![
LlmMessage::system(build_result_entity_system_prompt()),
LlmMessage::user(build_result_entity_user_prompt(profile, kind, &fallback)),
]);
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(true);
llm_client
.request_text(request)
@@ -1065,7 +1069,10 @@ async fn generate_scene_npc_with_fallback(
landmark_id,
&fallback,
)),
]);
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(true);
llm_client
.request_text(request)

View File

@@ -11,6 +11,8 @@ use serde_json::{Map as JsonMap, Value as JsonValue, json};
use shared_contracts::runtime::ExecuteCustomWorldAgentActionRequest;
use spacetime_client::CustomWorldAgentSessionRecord;
use crate::llm_model_routing::CREATION_TEMPLATE_LLM_MODEL;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldFoundationDraftResult {
pub draft_profile_json: String,
@@ -174,10 +176,15 @@ where
F: Fn(&str) -> String,
{
let response = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(FOUNDATION_JSON_ONLY_SYSTEM_PROMPT),
LlmMessage::user(user_prompt),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(FOUNDATION_JSON_ONLY_SYSTEM_PROMPT),
LlmMessage::user(user_prompt),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api()
.with_web_search(true),
)
.await
.map_err(|error| format!("{debug_label} LLM 请求失败:{error}"))?;
let text = response.content.trim();
@@ -188,10 +195,14 @@ where
Ok(value) => Ok(value),
Err(_) => {
let repaired = llm_client
.request_text(LlmTextRequest::new(vec![
LlmMessage::system(FOUNDATION_JSON_REPAIR_SYSTEM_PROMPT),
LlmMessage::user(repair_prompt_builder(text)),
]))
.request_text(
LlmTextRequest::new(vec![
LlmMessage::system(FOUNDATION_JSON_REPAIR_SYSTEM_PROMPT),
LlmMessage::user(repair_prompt_builder(text)),
])
.with_model(CREATION_TEMPLATE_LLM_MODEL)
.with_responses_api(),
)
.await
.map_err(|error| format!("{repair_debug_label} LLM 请求失败:{error}"))?;
parse_json_response_text(repaired.content.as_str())

View File

@@ -4,7 +4,7 @@ use axum::{
http::StatusCode,
response::Response,
};
use platform_llm::{LlmError, LlmMessage, LlmMessageRole, LlmTextRequest};
use platform_llm::{LlmError, LlmMessage, LlmMessageRole, LlmTextProtocol, LlmTextRequest};
use serde_json::Value;
use shared_contracts::llm::{
LlmChatCompletionRequest, LlmChatCompletionResponse, LlmChatMessagePayload, LlmChatMessageRole,
@@ -39,6 +39,7 @@ pub async fn proxy_llm_chat_completions(
let request = LlmTextRequest {
model: payload.model,
protocol: LlmTextProtocol::ChatCompletions,
messages: payload
.messages
.into_iter()

View File

@@ -0,0 +1,2 @@
pub(crate) const RPG_STORY_LLM_MODEL: &str = "doubao-seed-character-251128";
pub(crate) const CREATION_TEMPLATE_LLM_MODEL: &str = "deepseek-v3-2-251201";

View File

@@ -36,6 +36,7 @@ mod health;
mod http_error;
mod legacy_generated_assets;
mod llm;
mod llm_model_routing;
mod login_options;
mod logout;
mod logout_all;
@@ -63,6 +64,7 @@ mod story_battles;
mod story_sessions;
mod wechat_auth;
mod wechat_provider;
mod work_author;
use shared_logging::init_tracing;
use tokio::net::TcpListener;

View File

@@ -1,7 +1,7 @@
pub(crate) mod big_fish;
pub(crate) mod character_animation;
pub(crate) mod character_visual;
pub(crate) mod puzzle_image;
pub(crate) mod puzzle;
pub(crate) mod rpg;
pub(crate) mod scene_background;

View File

@@ -0,0 +1,212 @@
use module_puzzle::{PuzzleAnchorPack, PuzzleAnchorStatus, empty_anchor_pack};
use serde_json::{Value as JsonValue, json};
use spacetime_client::{
PuzzleAgentMessageRecord, PuzzleAgentSessionRecord, PuzzleAnchorPackRecord,
};
use crate::creation_agent_anchor_templates::{
get_creation_agent_anchor_template, render_anchor_question_block,
};
use crate::creation_agent_chat::render_quick_fill_extra_rules;
/// 拼图共创 Agent 的系统提示词。
///
/// 这里作为拼图聊天提示词主源,业务文件只负责调用 LLM、解析结果和写回状态。
pub(crate) const PUZZLE_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和陶泥主共创拼图画面的中文创意策划。
你要帮助用户把一句灵感逐步收束成可以发布成拼图关卡的视觉方案。
你必须同时输出:
1. 一段直接发给用户的中文回复 replyText
2. 当前进度 progressPercent
3. 下一轮完整可用的 nextAnchorPack
硬约束:
1. 只能输出 JSON不能输出代码块或解释
2. nextAnchorPack 必须是完整对象,不能只输出 patch
3. replyText 必须是自然中文不能提“字段”“锚点”“结构”“JSON”等内部词
4. replyText 一次最多推进一个最关键问题
5. 如果用户已经给出明确方向,就优先吸收和收束,不要机械反问
6. progressPercent 范围只能是 0 到 100
7. status 只能使用 missing / inferred / confirmed / locked
"#;
/// 拼图共创 Agent 单轮 JSON 输出契约。
const PUZZLE_AGENT_OUTPUT_CONTRACT: &str = r#"请严格按以下 JSON 输出,不要输出其他文字:
{
"replyText": "",
"progressPercent": 0,
"nextAnchorPack": {
"themePromise": {
"key": "themePromise",
"label": "题材承诺",
"value": "",
"status": "missing"
},
"visualSubject": {
"key": "visualSubject",
"label": "画面主体",
"value": "",
"status": "missing"
},
"visualMood": {
"key": "visualMood",
"label": "视觉气质",
"value": "",
"status": "missing"
},
"compositionHooks": {
"key": "compositionHooks",
"label": "拼图记忆点",
"value": "",
"status": "missing"
},
"tagsAndForbidden": {
"key": "tagsAndForbidden",
"label": "标签与禁忌",
"value": "",
"status": "missing"
}
}
}"#;
/// 拼图共创 Agent 的用户提示词,用于触发模型按系统约定返回单轮 JSON。
pub(crate) const PUZZLE_AGENT_JSON_TURN_USER_PROMPT: &str = "请按约定输出这一轮的 JSON。";
/// 拼图草稿生成对话提示词脚本。
pub(crate) fn build_puzzle_agent_prompt(
session: &PuzzleAgentSessionRecord,
quick_fill_requested: bool,
) -> String {
let anchor_question_block = get_creation_agent_anchor_template("puzzle")
.map(render_anchor_question_block)
.unwrap_or_else(|| "模板目标:收束成可以发布为拼图关卡的视觉方案。".to_string());
let quick_fill_rules = if quick_fill_requested {
format!(
"\n\n{}",
render_quick_fill_extra_rules(
"当前题材方向里的拼图关键词",
"不要要求用户再提供素材、风格或禁忌",
"输出完整 nextAnchorPack直接补齐 value 为空或 status 为 missing 的项",
"生成结果页",
)
)
} else {
String::new()
};
format!(
"{anchor_question_block}{quick_fill_rules}\n\n当前是第 {turn} 轮,当前进度 {progress}% 。\n\n是否要求自动补充剩余关键字:{quick_fill_requested_text}\n\n当前 anchor pack\n{anchor_pack}\n\n最近聊天记录:\n{chat_history}\n\n{contract}",
anchor_question_block = anchor_question_block,
quick_fill_rules = quick_fill_rules,
turn = session.current_turn.saturating_add(1),
progress = session.progress_percent,
quick_fill_requested_text = if quick_fill_requested { "" } else { "" },
anchor_pack = serialize_puzzle_record_anchor_pack(&session.anchor_pack),
chat_history =
serde_json::to_string_pretty(&build_chat_history(session.messages.as_slice()))
.unwrap_or_else(|_| "[]".to_string()),
contract = PUZZLE_AGENT_OUTPUT_CONTRACT,
)
}
/// 将 SpacetimeDB 记录态锚点序列化成提示词可读 JSON。
pub(crate) fn serialize_puzzle_record_anchor_pack(record: &PuzzleAnchorPackRecord) -> String {
serde_json::to_string_pretty(&map_puzzle_record_anchor_pack(record)).unwrap_or_else(|_| {
serde_json::to_string_pretty(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string())
})
}
fn build_chat_history(messages: &[PuzzleAgentMessageRecord]) -> Vec<JsonValue> {
messages
.iter()
.map(|message| {
json!({
"role": message.role,
"kind": message.kind,
"content": message.text,
})
})
.collect()
}
fn map_puzzle_record_anchor_pack(record: &PuzzleAnchorPackRecord) -> PuzzleAnchorPack {
PuzzleAnchorPack {
theme_promise: map_puzzle_record_anchor_item(&record.theme_promise),
visual_subject: map_puzzle_record_anchor_item(&record.visual_subject),
visual_mood: map_puzzle_record_anchor_item(&record.visual_mood),
composition_hooks: map_puzzle_record_anchor_item(&record.composition_hooks),
tags_and_forbidden: map_puzzle_record_anchor_item(&record.tags_and_forbidden),
}
}
fn map_puzzle_record_anchor_item(
record: &spacetime_client::PuzzleAnchorItemRecord,
) -> module_puzzle::PuzzleAnchorItem {
module_puzzle::PuzzleAnchorItem {
key: record.key.clone(),
label: record.label.clone(),
value: record.value.clone(),
status: parse_puzzle_anchor_status(record.status.as_str()),
}
}
fn parse_puzzle_anchor_status(value: &str) -> PuzzleAnchorStatus {
match value {
"confirmed" => PuzzleAnchorStatus::Confirmed,
"locked" => PuzzleAnchorStatus::Locked,
"inferred" => PuzzleAnchorStatus::Inferred,
_ => PuzzleAnchorStatus::Missing,
}
}
#[cfg(test)]
mod tests {
use super::build_puzzle_agent_prompt;
fn anchor_item(
key: &str,
label: &str,
value: &str,
status: &str,
) -> spacetime_client::PuzzleAnchorItemRecord {
spacetime_client::PuzzleAnchorItemRecord {
key: key.to_string(),
label: label.to_string(),
value: value.to_string(),
status: status.to_string(),
}
}
fn empty_session_record() -> spacetime_client::PuzzleAgentSessionRecord {
spacetime_client::PuzzleAgentSessionRecord {
session_id: "puzzle-session-test".to_string(),
seed_text: "雨夜猫咪遗迹".to_string(),
current_turn: 2,
progress_percent: 60,
stage: "collecting_anchors".to_string(),
anchor_pack: spacetime_client::PuzzleAnchorPackRecord {
theme_promise: anchor_item("themePromise", "题材承诺", "雨夜猫咪遗迹", "confirmed"),
visual_subject: anchor_item("visualSubject", "画面主体", "", "missing"),
visual_mood: anchor_item("visualMood", "视觉气质", "", "missing"),
composition_hooks: anchor_item("compositionHooks", "拼图记忆点", "", "missing"),
tags_and_forbidden: anchor_item("tagsAndForbidden", "标签与禁忌", "", "missing"),
},
draft: None,
messages: Vec::new(),
last_assistant_reply: None,
published_profile_id: None,
suggested_actions: Vec::new(),
result_preview: None,
updated_at: "2026-04-24T10:00:00.000Z".to_string(),
}
}
#[test]
fn quick_fill_prompt_forbids_follow_up_questions() {
let prompt = build_puzzle_agent_prompt(&empty_session_record(), true);
assert!(prompt.contains("用户刚刚主动要求你自动补充剩余关键字"));
assert!(prompt.contains("不要再继续提问"));
assert!(prompt.contains("progressPercent 直接输出为 100"));
}
}

View File

@@ -0,0 +1,106 @@
/// 拼图图片生成的默认反向提示词。
///
/// 这里单独收口拼图图片提示词,避免图片生成链路、候选图持久化和 DashScope 请求编排
/// 混在同一个脚本里,后续调画风或资产约束时只需要改这一处。
pub(crate) const PUZZLE_DEFAULT_NEGATIVE_PROMPT: &str =
"低清晰度,低质量,文字水印,畸形构图,过度模糊,重复肢体,画面脏污";
/// wan2.2 / wan2.1 文生图旧协议的正向 prompt 上限。
///
/// 中文注释DashScope 旧 text2image 接口会把超长 prompt 判成请求参数不合法,
/// 所以这里先在拼图提示词模块内压缩,保证固定玩法约束不会被用户长描述挤掉。
pub(crate) const PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS: usize = 500;
const PUZZLE_IMAGE_LEVEL_NAME_MAX_CHARS: usize = 40;
const PUZZLE_IMAGE_PROMPT_FALLBACK: &str = "清晰、有辨识度的拼图画面";
/// 根据拼图关卡名和陶泥主输入构造最终发给图片模型的提示词。
pub(crate) fn build_puzzle_image_prompt(level_name: &str, prompt: &str) -> String {
let level_name =
truncate_puzzle_prompt_segment(level_name.trim(), PUZZLE_IMAGE_LEVEL_NAME_MAX_CHARS);
let prompt = prompt.trim();
let prompt = if prompt.is_empty() {
PUZZLE_IMAGE_PROMPT_FALLBACK
} else {
prompt
};
let template_chars = build_puzzle_image_prompt_text(level_name.as_str(), "")
.chars()
.count();
let prompt_max_chars = PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS.saturating_sub(template_chars);
let prompt = truncate_puzzle_prompt_segment(prompt, prompt_max_chars);
let image_prompt = build_puzzle_image_prompt_text(level_name.as_str(), prompt.as_str());
debug_assert!(
image_prompt.chars().count() <= PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS,
"puzzle image prompt should fit DashScope wan2.2 limit"
);
image_prompt
}
fn build_puzzle_image_prompt_text(level_name: &str, prompt: &str) -> String {
format!(
concat!(
"请生成一张高清插画。",
"关卡名:{level_name}。",
"画面主体:{prompt}。",
"画面要求1:1 正方形拼图关卡,适配 3x3 或 4x4 拼图切块,",
"主体要清晰集中,前中后景层次明确,局部细节丰富但不要杂乱,",
"避免文字、水印、边框和 UI 元素。"
),
level_name = level_name,
prompt = prompt,
)
}
fn truncate_puzzle_prompt_segment(value: &str, max_chars: usize) -> String {
if value.chars().count() <= max_chars {
return value.to_string();
}
const MARKER: &str = "...";
if max_chars <= MARKER.chars().count() {
return value.chars().take(max_chars).collect();
}
let keep_chars = max_chars - MARKER.chars().count();
format!(
"{}{MARKER}",
value.chars().take(keep_chars).collect::<String>()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_puzzle_image_prompt_keeps_puzzle_asset_constraints() {
let prompt = build_puzzle_image_prompt("雨夜神庙", "猫咪在发光遗迹前寻找线索");
assert!(prompt.contains("雨夜神庙"));
assert!(prompt.contains("猫咪在发光遗迹前寻找线索"));
assert!(prompt.contains("1:1 正方形拼图关卡"));
assert!(prompt.contains("3x3 或 4x4"));
assert!(prompt.contains("避免文字、水印、边框和 UI 元素"));
}
#[test]
fn build_puzzle_image_prompt_trims_long_user_description_for_wan22() {
let long_level_name = "雨夜神庙".repeat(20);
let long_description =
"发光遗迹、猫咪、漂浮碎片、雨水反光、远处灯塔、适合拼图切块。".repeat(50);
let prompt = build_puzzle_image_prompt(long_level_name.as_str(), long_description.as_str());
assert!(prompt.chars().count() <= PUZZLE_TEXT_TO_IMAGE_PROMPT_MAX_CHARS);
assert!(prompt.contains("1:1 正方形拼图关卡"));
assert!(prompt.contains("3x3 或 4x4"));
assert!(prompt.contains("避免文字、水印、边框和 UI 元素"));
}
#[test]
fn default_negative_prompt_blocks_text_and_low_quality_assets() {
assert!(PUZZLE_DEFAULT_NEGATIVE_PROMPT.contains("低清晰度"));
assert!(PUZZLE_DEFAULT_NEGATIVE_PROMPT.contains("文字水印"));
}
}

View File

@@ -0,0 +1,2 @@
pub(crate) mod agent_chat;
pub(crate) mod image;

View File

@@ -1,44 +0,0 @@
/// 拼图图片生成的默认反向提示词。
///
/// 这里单独收口拼图图片提示词,避免图片生成链路、候选图持久化和 DashScope 请求编排
/// 混在同一个脚本里,后续调画风或资产约束时只需要改这一处。
pub(crate) const PUZZLE_DEFAULT_NEGATIVE_PROMPT: &str =
"低清晰度,低质量,文字水印,畸形构图,过度模糊,重复肢体,画面脏污";
/// 根据拼图关卡名和陶泥主输入构造最终发给图片模型的提示词。
pub(crate) fn build_puzzle_image_prompt(level_name: &str, prompt: &str) -> String {
format!(
concat!(
"请生成一张适合 1:1 正方形拼图关卡的高清插画。",
"关卡名:{level_name}。",
"画面主体:{prompt}。",
"画面要求1:1 正方形画布,适配 3x3 或 4x4 拼图切块,",
"主体要清晰集中,前中后景层次明确,局部细节丰富但不要杂乱,",
"避免文字、水印、边框和 UI 元素。"
),
level_name = level_name,
prompt = prompt,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_puzzle_image_prompt_keeps_puzzle_asset_constraints() {
let prompt = build_puzzle_image_prompt("雨夜神庙", "猫咪在发光遗迹前寻找线索");
assert!(prompt.contains("雨夜神庙"));
assert!(prompt.contains("猫咪在发光遗迹前寻找线索"));
assert!(prompt.contains("1:1 正方形拼图关卡"));
assert!(prompt.contains("3x3 或 4x4"));
assert!(prompt.contains("避免文字、水印、边框和 UI 元素"));
}
#[test]
fn default_negative_prompt_blocks_text_and_low_quality_assets() {
assert!(PUZZLE_DEFAULT_NEGATIVE_PROMPT.contains("低清晰度"));
assert!(PUZZLE_DEFAULT_NEGATIVE_PROMPT.contains("文字水印"));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,16 @@
use module_puzzle::{PuzzleAgentStage, PuzzleAnchorPack, PuzzleAnchorStatus, empty_anchor_pack};
use platform_llm::LlmClient;
use serde::{Deserialize, Serialize};
use serde_json::{Value as JsonValue, json};
use spacetime_client::{
PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord, PuzzleAgentSessionRecord,
};
use serde_json::Value as JsonValue;
use spacetime_client::{PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentSessionRecord};
use crate::creation_agent_anchor_templates::{
get_creation_agent_anchor_template, render_anchor_question_block,
};
use crate::creation_agent_chat::render_quick_fill_extra_rules;
use crate::creation_agent_llm_turn::{
CreationAgentLlmTurnErrorMessages, stream_creation_agent_json_turn,
};
use crate::prompt::puzzle::agent_chat::{
PUZZLE_AGENT_JSON_TURN_USER_PROMPT, PUZZLE_AGENT_SYSTEM_PROMPT, build_puzzle_agent_prompt,
serialize_puzzle_record_anchor_pack,
};
#[derive(Clone, Debug)]
pub(crate) struct PuzzleAgentTurnRequest<'a> {
@@ -60,63 +58,6 @@ struct PuzzleAgentModelOutput {
next_anchor_pack: PuzzleAnchorPack,
}
const PUZZLE_AGENT_SYSTEM_PROMPT: &str = r#"你是一个负责和陶泥主共创拼图画面的中文创意策划。
你要帮助用户把一句灵感逐步收束成可以发布成拼图关卡的视觉方案。
你必须同时输出:
1. 一段直接发给用户的中文回复 replyText
2. 当前进度 progressPercent
3. 下一轮完整可用的 nextAnchorPack
硬约束:
1. 只能输出 JSON不能输出代码块或解释
2. nextAnchorPack 必须是完整对象,不能只输出 patch
3. replyText 必须是自然中文不能提“字段”“锚点”“结构”“JSON”等内部词
4. replyText 一次最多推进一个最关键问题
5. 如果用户已经给出明确方向,就优先吸收和收束,不要机械反问
6. progressPercent 范围只能是 0 到 100
7. status 只能使用 missing / inferred / confirmed / locked
"#;
const PUZZLE_AGENT_OUTPUT_CONTRACT: &str = r#"请严格按以下 JSON 输出,不要输出其他文字:
{
"replyText": "",
"progressPercent": 0,
"nextAnchorPack": {
"themePromise": {
"key": "themePromise",
"label": "题材承诺",
"value": "",
"status": "missing"
},
"visualSubject": {
"key": "visualSubject",
"label": "画面主体",
"value": "",
"status": "missing"
},
"visualMood": {
"key": "visualMood",
"label": "视觉气质",
"value": "",
"status": "missing"
},
"compositionHooks": {
"key": "compositionHooks",
"label": "拼图记忆点",
"value": "",
"status": "missing"
},
"tagsAndForbidden": {
"key": "tagsAndForbidden",
"label": "标签与禁忌",
"value": "",
"status": "missing"
}
}
}"#;
pub(crate) async fn run_puzzle_agent_turn<F>(
request: PuzzleAgentTurnRequest<'_>,
on_reply_update: F,
@@ -128,7 +69,7 @@ where
let turn_output = stream_creation_agent_json_turn(
request.llm_client,
format!("{PUZZLE_AGENT_SYSTEM_PROMPT}\n\n{prompt}"),
"请按约定输出这一轮的 JSON。",
PUZZLE_AGENT_JSON_TURN_USER_PROMPT,
request.enable_web_search,
CreationAgentLlmTurnErrorMessages {
model_unavailable: "当前模型不可用,请稍后重试。",
@@ -185,10 +126,6 @@ pub(crate) fn build_failed_finalize_record_input(
error_message: String,
updated_at_micros: i64,
) -> PuzzleAgentMessageFinalizeRecordInput {
let anchor_pack_json = serde_json::to_string(&map_record_anchor_pack(&session.anchor_pack))
.unwrap_or_else(|_| {
serde_json::to_string(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string())
});
PuzzleAgentMessageFinalizeRecordInput {
session_id,
owner_user_id,
@@ -196,61 +133,12 @@ pub(crate) fn build_failed_finalize_record_input(
assistant_reply_text: None,
stage: session.stage.clone(),
progress_percent: session.progress_percent,
anchor_pack_json,
anchor_pack_json: serialize_puzzle_record_anchor_pack(&session.anchor_pack),
error_message: Some(error_message),
updated_at_micros,
}
}
fn build_puzzle_agent_prompt(
session: &PuzzleAgentSessionRecord,
quick_fill_requested: bool,
) -> String {
let anchor_question_block = get_creation_agent_anchor_template("puzzle")
.map(render_anchor_question_block)
.unwrap_or_else(|| "模板目标:收束成可以发布为拼图关卡的视觉方案。".to_string());
let quick_fill_rules = if quick_fill_requested {
format!(
"\n\n{}",
render_quick_fill_extra_rules(
"当前题材方向里的拼图关键词",
"不要要求用户再提供素材、风格或禁忌",
"输出完整 nextAnchorPack直接补齐 value 为空或 status 为 missing 的项",
"生成结果页",
)
)
} else {
String::new()
};
format!(
"{anchor_question_block}{quick_fill_rules}\n\n当前是第 {turn} 轮,当前进度 {progress}% 。\n\n是否要求自动补充剩余关键字:{quick_fill_requested_text}\n\n当前 anchor pack\n{anchor_pack}\n\n最近聊天记录:\n{chat_history}\n\n{contract}",
anchor_question_block = anchor_question_block,
quick_fill_rules = quick_fill_rules,
turn = session.current_turn.saturating_add(1),
progress = session.progress_percent,
quick_fill_requested_text = if quick_fill_requested { "" } else { "" },
anchor_pack = serde_json::to_string_pretty(&map_record_anchor_pack(&session.anchor_pack))
.unwrap_or_else(|_| "{}".to_string()),
chat_history =
serde_json::to_string_pretty(&build_chat_history(session.messages.as_slice()))
.unwrap_or_else(|_| "[]".to_string()),
contract = PUZZLE_AGENT_OUTPUT_CONTRACT,
)
}
fn build_chat_history(messages: &[PuzzleAgentMessageRecord]) -> Vec<JsonValue> {
messages
.iter()
.map(|message| {
json!({
"role": message.role,
"kind": message.kind,
"content": message.text,
})
})
.collect()
}
fn parse_model_output(parsed: &JsonValue) -> Result<PuzzleAgentModelOutput, PuzzleAgentTurnError> {
let reply_text = parsed
.get("replyText")
@@ -348,27 +236,6 @@ fn resolve_puzzle_agent_stage(progress_percent: u32) -> PuzzleAgentStage {
}
}
fn map_record_anchor_pack(record: &spacetime_client::PuzzleAnchorPackRecord) -> PuzzleAnchorPack {
PuzzleAnchorPack {
theme_promise: map_record_anchor_item(&record.theme_promise),
visual_subject: map_record_anchor_item(&record.visual_subject),
visual_mood: map_record_anchor_item(&record.visual_mood),
composition_hooks: map_record_anchor_item(&record.composition_hooks),
tags_and_forbidden: map_record_anchor_item(&record.tags_and_forbidden),
}
}
fn map_record_anchor_item(
record: &spacetime_client::PuzzleAnchorItemRecord,
) -> module_puzzle::PuzzleAnchorItem {
module_puzzle::PuzzleAnchorItem {
key: record.key.clone(),
label: record.label.clone(),
value: record.value.clone(),
status: parse_anchor_status(record.status.as_str()),
}
}
fn parse_anchor_status(value: &str) -> PuzzleAnchorStatus {
match value {
"confirmed" => PuzzleAnchorStatus::Confirmed,
@@ -383,57 +250,9 @@ mod tests {
use module_puzzle::PuzzleAnchorStatus;
use serde_json::json;
use super::{build_puzzle_agent_prompt, parse_model_output};
use super::parse_model_output;
use crate::creation_agent_llm_turn::extract_reply_text_from_partial_json;
fn empty_session_record() -> spacetime_client::PuzzleAgentSessionRecord {
spacetime_client::PuzzleAgentSessionRecord {
session_id: "puzzle-session-test".to_string(),
current_turn: 2,
progress_percent: 60,
stage: "collecting_anchors".to_string(),
anchor_pack: spacetime_client::PuzzleAnchorPackRecord {
theme_promise: spacetime_client::PuzzleAnchorItemRecord {
key: "themePromise".to_string(),
label: "题材承诺".to_string(),
value: "雨夜猫咪遗迹".to_string(),
status: "confirmed".to_string(),
},
visual_subject: spacetime_client::PuzzleAnchorItemRecord {
key: "visualSubject".to_string(),
label: "画面主体".to_string(),
value: String::new(),
status: "missing".to_string(),
},
visual_mood: spacetime_client::PuzzleAnchorItemRecord {
key: "visualMood".to_string(),
label: "视觉气质".to_string(),
value: String::new(),
status: "missing".to_string(),
},
composition_hooks: spacetime_client::PuzzleAnchorItemRecord {
key: "compositionHooks".to_string(),
label: "拼图记忆点".to_string(),
value: String::new(),
status: "missing".to_string(),
},
tags_and_forbidden: spacetime_client::PuzzleAnchorItemRecord {
key: "tagsAndForbidden".to_string(),
label: "标签与禁忌".to_string(),
value: String::new(),
status: "missing".to_string(),
},
},
draft: None,
messages: Vec::new(),
last_assistant_reply: None,
published_profile_id: None,
suggested_actions: Vec::new(),
result_preview: None,
updated_at: "2026-04-24T10:00:00.000Z".to_string(),
}
}
#[test]
fn extract_reply_text_from_partial_json_preserves_chinese_characters() {
let partial_json = r#"{"replyText":"夜雨猫咪遗迹","progressPercent":42"#;
@@ -498,13 +317,4 @@ mod tests {
"雨夜、猫咪、神庙遗迹;禁止文字水印"
);
}
#[test]
fn quick_fill_prompt_forbids_follow_up_questions() {
let prompt = build_puzzle_agent_prompt(&empty_session_record(), true);
assert!(prompt.contains("用户刚刚主动要求你自动补充剩余关键字"));
assert!(prompt.contains("不要再继续提问"));
assert!(prompt.contains("progressPercent 直接输出为 100"));
}
}

View File

@@ -22,6 +22,7 @@ use module_runtime_story_compat::{
use crate::{
auth::AuthenticatedAccessToken,
http_error::AppError,
llm_model_routing::RPG_STORY_LLM_MODEL,
prompt::runtime_chat::{
NPC_CHAT_TURN_REPLY_SYSTEM_PROMPT, NPC_CHAT_TURN_SUGGESTION_SYSTEM_PROMPT,
NpcChatTurnPromptInput, build_deterministic_chat_suggestions,
@@ -227,6 +228,7 @@ where
]);
reply_request.max_tokens = Some(700);
reply_request.enable_web_search = state.config.rpg_llm_web_search_enabled;
reply_request.model = Some(RPG_STORY_LLM_MODEL.to_string());
let reply_response = llm_client
.stream_text(reply_request, |delta| {
@@ -254,6 +256,7 @@ where
]);
suggestion_request.max_tokens = Some(200);
suggestion_request.enable_web_search = state.config.rpg_llm_web_search_enabled;
suggestion_request.model = Some(RPG_STORY_LLM_MODEL.to_string());
let suggestion_text = llm_client
.request_text(suggestion_request)
.await

View File

@@ -15,7 +15,8 @@ use std::convert::Infallible;
use crate::{
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
prompt::runtime_chat::*, request_context::RequestContext, state::AppState,
llm_model_routing::RPG_STORY_LLM_MODEL, prompt::runtime_chat::*,
request_context::RequestContext, state::AppState,
};
use module_runtime_story_compat::{
RuntimeStoryPromptContextExtras, build_runtime_story_prompt_context, current_world_type,
@@ -587,6 +588,7 @@ async fn request_runtime_plain_text(
]);
request.max_tokens = Some(400);
request.enable_web_search = state.config.rpg_llm_web_search_enabled;
request.model = Some(RPG_STORY_LLM_MODEL.to_string());
llm_client
.request_text(request)
@@ -618,6 +620,7 @@ fn stream_plain_text_response<'a>(
]);
request.max_tokens = Some(700);
request.enable_web_search = enable_web_search;
request.model = Some(RPG_STORY_LLM_MODEL.to_string());
let response = llm_client
.stream_text(request, |_| {})

View File

@@ -1,4 +1,5 @@
use super::*;
use crate::llm_model_routing::RPG_STORY_LLM_MODEL;
use crate::prompt::runtime_chat::{
RuntimeNpcDialoguePromptParams, RuntimeReasonedStoryPromptParams, RuntimeStoryTextPromptParams,
build_runtime_npc_dialogue_user_prompt, build_runtime_reasoned_story_user_prompt,
@@ -100,6 +101,7 @@ pub(super) async fn generate_action_story_payload(
fn apply_rpg_web_search(state: &AppState, request: &mut LlmTextRequest) {
request.enable_web_search = state.config.rpg_llm_web_search_enabled;
request.model = Some(RPG_STORY_LLM_MODEL.to_string());
}
pub(super) async fn generate_npc_dialogue_payload(
@@ -144,6 +146,7 @@ pub(super) async fn generate_npc_dialogue_payload(
]);
llm_request.max_tokens = Some(700);
llm_request.enable_web_search = enable_web_search;
llm_request.model = Some(RPG_STORY_LLM_MODEL.to_string());
let dialogue_text = llm_client
.request_text(llm_request)
@@ -195,6 +198,7 @@ pub(super) async fn generate_reasoned_story_payload(
]);
llm_request.max_tokens = Some(700);
llm_request.enable_web_search = enable_web_search;
llm_request.model = Some(RPG_STORY_LLM_MODEL.to_string());
let story_text = llm_client
.request_text(llm_request)

View File

@@ -271,11 +271,18 @@ pub(super) fn build_active_npc_runtime_story_options(
game_state: &Value,
npc_id: &str,
) -> Vec<RuntimeStoryOptionView> {
if read_current_npc_affinity(game_state) < 0 {
return vec![build_npc_runtime_story_option(
"npc_chat",
"继续交谈",
npc_id,
"chat",
)];
}
let mut options = vec![
build_npc_runtime_story_option("npc_chat", "继续交谈", npc_id, "chat"),
build_npc_help_runtime_story_option(game_state, npc_id),
build_npc_runtime_story_option("npc_spar", "点到为止切磋", npc_id, "spar"),
build_npc_runtime_story_option("npc_fight", "与对方战斗", npc_id, "fight"),
];
if current_npc_inventory_items(game_state)
@@ -332,12 +339,6 @@ pub(super) fn build_active_npc_runtime_story_options(
));
}
options.push(build_npc_runtime_story_option(
"npc_leave",
"离开当前角色",
npc_id,
"leave",
));
options
}

View File

@@ -1112,12 +1112,9 @@ fn runtime_story_state_compiler_builds_active_npc_options_with_trade_gift_and_he
vec![
"npc_chat",
"npc_help",
"npc_spar",
"npc_fight",
"npc_trade",
"npc_gift",
"npc_quest_accept",
"npc_leave"
"npc_quest_accept"
]
);
assert_eq!(
@@ -1129,11 +1126,11 @@ fn runtime_story_state_compiler_builds_active_npc_options_with_trade_gift_and_he
Some("当前 NPC 的一次性援手已经用完了。")
);
assert!(matches!(
response.view_model.available_options[4].interaction,
response.view_model.available_options[2].interaction,
Some(RuntimeStoryOptionInteraction::Npc { ref action, .. }) if action == "trade"
));
assert!(matches!(
response.view_model.available_options[5].interaction,
response.view_model.available_options[3].interaction,
Some(RuntimeStoryOptionInteraction::Npc { ref action, .. }) if action == "gift"
));
let npc_interaction = response
@@ -1154,6 +1151,35 @@ fn runtime_story_state_compiler_builds_active_npc_options_with_trade_gift_and_he
);
}
#[test]
fn runtime_story_state_compiler_limits_negative_affinity_active_npc_to_chat() {
let mut game_state = build_runtime_story_boundary_game_state_fixture();
write_bool_field(&mut game_state, "npcInteractionActive", true);
write_current_npc_state_i32_field(&mut game_state, "affinity", -8);
let response = build_runtime_story_state_response(
"runtime-main",
Some(0),
RuntimeStorySnapshotPayload {
saved_at: None,
bottom_tab: "adventure".to_string(),
game_state,
current_story: None,
},
);
let function_ids = response
.view_model
.available_options
.iter()
.map(|option| option.function_id.as_str())
.collect::<Vec<_>>();
assert_eq!(function_ids, vec!["npc_chat"]);
assert!(matches!(
response.view_model.available_options[0].interaction,
Some(RuntimeStoryOptionInteraction::Npc { ref action, .. }) if action == "chat"
));
}
#[test]
fn runtime_story_equipment_equip_updates_loadout_and_build_toast() {
let request = RuntimeStoryActionRequest {

View File

@@ -0,0 +1,54 @@
use module_auth::AuthUser;
use crate::state::AppState;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WorkAuthorSummary {
pub display_name: String,
pub public_user_code: Option<String>,
}
/// 中文注释:作品作者的真相源是 owner_user_id历史昵称字段只作为账号资料不可读时的兼容回退。
pub fn resolve_work_author_by_user_id(
state: &AppState,
owner_user_id: &str,
fallback_display_name: Option<&str>,
fallback_public_user_code: Option<&str>,
) -> WorkAuthorSummary {
let fallback_display_name =
normalize_optional_text(fallback_display_name).unwrap_or_else(|| "玩家".to_string());
let fallback_public_user_code = normalize_optional_text(fallback_public_user_code);
let Some(owner_user_id) = normalize_optional_text(Some(owner_user_id)) else {
return WorkAuthorSummary {
display_name: fallback_display_name,
public_user_code: fallback_public_user_code,
};
};
match state.auth_user_service().get_user_by_id(&owner_user_id) {
Ok(Some(user)) => map_auth_user_to_work_author_summary(user, fallback_display_name),
Ok(None) | Err(_) => WorkAuthorSummary {
display_name: fallback_display_name,
public_user_code: fallback_public_user_code,
},
}
}
fn map_auth_user_to_work_author_summary(
user: AuthUser,
fallback_display_name: String,
) -> WorkAuthorSummary {
WorkAuthorSummary {
display_name: normalize_optional_text(Some(user.display_name.as_str()))
.unwrap_or(fallback_display_name),
public_user_code: normalize_optional_text(Some(user.public_user_code.as_str())),
}
}
fn normalize_optional_text(value: Option<&str>) -> Option<String> {
value
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
}

View File

@@ -347,6 +347,14 @@ pub struct BigFishPlayRecordInput {
pub played_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BigFishWorkLikeRecordInput {
pub session_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BigFishFieldError {
MissingSessionId,

View File

@@ -510,6 +510,15 @@ pub struct CustomWorldProfilePlayRecordInput {
pub played_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CustomWorldProfileLikeRecordInput {
pub owner_user_id: String,
pub profile_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CustomWorldAgentSessionCreateInput {

View File

@@ -113,9 +113,26 @@ pub struct PuzzleGeneratedImageCandidate {
pub selected: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleDraftLevel {
pub level_id: String,
pub level_name: String,
pub picture_description: String,
pub candidates: Vec<PuzzleGeneratedImageCandidate>,
pub selected_candidate_id: Option<String>,
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub generation_status: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleResultDraft {
#[serde(default)]
pub work_title: String,
#[serde(default)]
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
@@ -127,6 +144,18 @@ pub struct PuzzleResultDraft {
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub generation_status: String,
#[serde(default)]
pub levels: Vec<PuzzleDraftLevel>,
#[serde(default)]
pub form_draft: Option<PuzzleFormDraft>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleFormDraft {
pub work_title: Option<String>,
pub work_description: Option<String>,
pub picture_description: Option<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
@@ -202,11 +231,17 @@ pub struct PuzzleWorkProfile {
pub owner_user_id: String,
pub source_session_id: Option<String>,
pub author_display_name: String,
#[serde(default)]
pub work_title: String,
#[serde(default)]
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
#[serde(default)]
pub levels: Vec<PuzzleDraftLevel>,
pub publication_status: PuzzlePublicationStatus,
pub updated_at_micros: i64,
pub published_at_micros: Option<i64>,
@@ -334,6 +369,15 @@ pub struct PuzzleAgentSessionCreateInput {
pub created_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleFormDraftSaveInput {
pub session_id: String,
pub owner_user_id: String,
pub seed_text: String,
pub saved_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleAgentSessionGetInput {
@@ -378,6 +422,7 @@ pub struct PuzzleDraftCompileInput {
pub struct PuzzleGeneratedImagesSaveInput {
pub session_id: String,
pub owner_user_id: String,
pub level_id: Option<String>,
pub candidates_json: String,
pub saved_at_micros: i64,
}
@@ -387,6 +432,7 @@ pub struct PuzzleGeneratedImagesSaveInput {
pub struct PuzzleSelectCoverImageInput {
pub session_id: String,
pub owner_user_id: String,
pub level_id: Option<String>,
pub candidate_id: String,
pub selected_at_micros: i64,
}
@@ -399,9 +445,12 @@ pub struct PuzzlePublishInput {
pub work_id: String,
pub profile_id: String,
pub author_display_name: String,
pub work_title: Option<String>,
pub work_description: Option<String>,
pub level_name: Option<String>,
pub summary: Option<String>,
pub theme_tags: Option<Vec<String>>,
pub levels_json: Option<String>,
pub published_at_micros: i64,
}
@@ -429,11 +478,14 @@ pub struct PuzzleWorkDeleteInput {
pub struct PuzzleWorkUpsertInput {
pub profile_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub levels_json: Option<String>,
pub updated_at_micros: i64,
}
@@ -450,12 +502,21 @@ pub struct PuzzleWorkRemixInput {
pub remixed_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleWorkLikeRecordInput {
pub profile_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleRunStartInput {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub level_id: Option<String>,
pub started_at_micros: i64,
}
@@ -690,10 +751,17 @@ pub fn empty_anchor_pack() -> PuzzleAnchorPack {
pub fn infer_anchor_pack(seed_text: &str, latest_message: Option<&str>) -> PuzzleAnchorPack {
let source = normalize_required_string(latest_message.unwrap_or(seed_text))
.or_else(|| normalize_required_string(seed_text))
.unwrap_or_else(|| "童话森林里的发光猫咪遗迹".to_string());
if let Some((title, picture_description)) = parse_form_seed_text(&source) {
return build_form_anchor_pack(title.as_str(), picture_description.as_str());
.or_else(|| normalize_required_string(seed_text));
let Some(source) = source else {
return empty_anchor_pack();
};
if let Some(form_seed) = parse_form_seed_text(&source) {
if form_seed.has_any_value() {
return build_form_anchor_pack(
form_seed.work_title.as_deref().unwrap_or(""),
form_seed.picture_description.as_deref().unwrap_or(""),
);
}
}
let mut pack = empty_anchor_pack();
@@ -711,22 +779,26 @@ pub fn infer_anchor_pack(seed_text: &str, latest_message: Option<&str>) -> Puzzl
}
pub fn build_form_anchor_pack(title: &str, picture_description: &str) -> PuzzleAnchorPack {
let normalized_title =
normalize_required_string(title).unwrap_or_else(|| "奇景拼图".to_string());
let normalized_description =
normalize_required_string(picture_description).unwrap_or_else(|| normalized_title.clone());
let normalized_title = normalize_required_string(title);
let normalized_description = normalize_required_string(picture_description);
let mut pack = empty_anchor_pack();
pack.theme_promise.value = normalized_title.clone();
pack.theme_promise.status = PuzzleAnchorStatus::Locked;
pack.visual_subject.value = normalized_description.clone();
pack.visual_subject.status = PuzzleAnchorStatus::Locked;
if let Some(title) = normalized_title.as_ref() {
pack.theme_promise.value = title.clone();
pack.theme_promise.status = PuzzleAnchorStatus::Locked;
}
if let Some(description) = normalized_description.as_ref() {
pack.visual_subject.value = description.clone();
pack.visual_subject.status = PuzzleAnchorStatus::Locked;
}
pack.visual_mood.value = "清晰、适合拼图切块".to_string();
pack.visual_mood.status = PuzzleAnchorStatus::Inferred;
pack.composition_hooks.value = "主体轮廓、色块分区、局部细节".to_string();
pack.composition_hooks.status = PuzzleAnchorStatus::Inferred;
pack.tags_and_forbidden.value =
build_form_tags_and_forbidden(normalized_title.as_str(), normalized_description.as_str());
pack.tags_and_forbidden.value = build_form_tags_and_forbidden(
normalized_title.as_deref().unwrap_or(""),
normalized_description.as_deref().unwrap_or(""),
);
pack.tags_and_forbidden.status = PuzzleAnchorStatus::Inferred;
pack
@@ -766,13 +838,37 @@ pub fn build_creator_intent(
pub fn compile_result_draft(
anchor_pack: &PuzzleAnchorPack,
messages: &[PuzzleAgentMessageSnapshot],
) -> PuzzleResultDraft {
compile_result_draft_from_seed(anchor_pack, messages, None)
}
pub fn compile_result_draft_from_seed(
anchor_pack: &PuzzleAnchorPack,
messages: &[PuzzleAgentMessageSnapshot],
seed_text: Option<&str>,
) -> PuzzleResultDraft {
let creator_intent = build_creator_intent(anchor_pack, messages);
let normalized_tags = normalize_theme_tags(creator_intent.theme_tags.clone());
let level_name = build_level_name(anchor_pack, &normalized_tags);
let work_title = build_work_title(anchor_pack);
let work_description = resolve_work_description(seed_text, anchor_pack);
let picture_description = fallback_text(&anchor_pack.visual_subject.value, "画面主体");
let level_name =
build_level_name_from_picture(picture_description.as_str(), &normalized_tags, 1);
let level = PuzzleDraftLevel {
level_id: "puzzle-level-1".to_string(),
level_name: level_name.clone(),
picture_description,
candidates: Vec::new(),
selected_candidate_id: None,
cover_image_src: None,
cover_asset_id: None,
generation_status: "idle".to_string(),
};
PuzzleResultDraft {
work_title,
work_description: work_description.clone(),
level_name,
summary: build_result_summary(anchor_pack),
summary: work_description,
theme_tags: normalized_tags,
forbidden_directives: creator_intent.forbidden_directives.clone(),
creator_intent: Some(creator_intent),
@@ -782,6 +878,79 @@ pub fn compile_result_draft(
cover_image_src: None,
cover_asset_id: None,
generation_status: "idle".to_string(),
levels: vec![level],
form_draft: None,
}
}
pub fn build_form_draft_from_seed(
anchor_pack: &PuzzleAnchorPack,
seed_text: Option<&str>,
) -> PuzzleResultDraft {
let form_seed = seed_text.and_then(parse_form_seed_text);
build_form_draft_from_parts(
anchor_pack,
form_seed.as_ref().and_then(|seed| seed.work_title.clone()),
form_seed
.as_ref()
.and_then(|seed| seed.work_description.clone()),
form_seed.and_then(|seed| seed.picture_description),
)
}
pub fn build_form_draft_from_parts(
anchor_pack: &PuzzleAnchorPack,
work_title: Option<String>,
work_description: Option<String>,
picture_description: Option<String>,
) -> PuzzleResultDraft {
let work_title = work_title.and_then(|value| normalize_required_string(&value));
let work_description = work_description.and_then(|value| normalize_required_string(&value));
let picture_description = picture_description.and_then(|value| normalize_required_string(&value));
let title_for_tags = work_title.as_deref().unwrap_or("");
let picture_for_tags = picture_description.as_deref().unwrap_or("");
let mut tags = normalize_theme_tags(derive_form_theme_tags(title_for_tags, picture_for_tags));
if tags.is_empty() {
tags = vec!["拼图".to_string(), "插画".to_string(), "清晰构图".to_string()];
}
let level_name = picture_description
.as_deref()
.map(|value| build_level_name_from_picture(value, &tags, 1))
.or_else(|| work_title.clone())
.unwrap_or_else(|| "未命名拼图".to_string());
let summary = work_description.clone().unwrap_or_default();
let level = PuzzleDraftLevel {
level_id: "puzzle-level-1".to_string(),
level_name: level_name.clone(),
picture_description: picture_description.clone().unwrap_or_default(),
candidates: Vec::new(),
selected_candidate_id: None,
cover_image_src: None,
cover_asset_id: None,
generation_status: "idle".to_string(),
};
// 中文注释:这是生成前的表单草稿,只用于创作中心恢复和表单回填,不进入发布就绪判断。
PuzzleResultDraft {
work_title: work_title.clone().unwrap_or_default(),
work_description: summary.clone(),
level_name,
summary,
theme_tags: tags,
forbidden_directives: Vec::new(),
creator_intent: None,
anchor_pack: anchor_pack.clone(),
candidates: Vec::new(),
selected_candidate_id: None,
cover_image_src: None,
cover_asset_id: None,
generation_status: "idle".to_string(),
levels: vec![level],
form_draft: Some(PuzzleFormDraft {
work_title,
work_description,
picture_description,
}),
}
}
@@ -849,13 +1018,155 @@ pub fn apply_selected_candidate(
Ok(draft)
}
pub fn normalize_puzzle_draft(mut draft: PuzzleResultDraft) -> PuzzleResultDraft {
if draft.work_title.trim().is_empty() {
draft.work_title = fallback_text(&draft.anchor_pack.theme_promise.value, &draft.level_name);
}
if draft.work_description.trim().is_empty() {
draft.work_description = draft.summary.clone();
}
if draft.levels.is_empty() {
draft.levels = vec![PuzzleDraftLevel {
level_id: "puzzle-level-1".to_string(),
level_name: draft.level_name.clone(),
picture_description: fallback_text(
&draft.anchor_pack.visual_subject.value,
&draft.summary,
),
candidates: draft.candidates.clone(),
selected_candidate_id: draft.selected_candidate_id.clone(),
cover_image_src: draft.cover_image_src.clone(),
cover_asset_id: draft.cover_asset_id.clone(),
generation_status: draft.generation_status.clone(),
}];
}
sync_primary_level_fields(&mut draft);
draft
}
pub fn sync_primary_level_fields(draft: &mut PuzzleResultDraft) {
if let Some(primary_level) = draft.levels.first() {
draft.level_name = primary_level.level_name.clone();
draft.candidates = primary_level.candidates.clone();
draft.selected_candidate_id = primary_level.selected_candidate_id.clone();
draft.cover_image_src = primary_level.cover_image_src.clone();
draft.cover_asset_id = primary_level.cover_asset_id.clone();
draft.generation_status = primary_level.generation_status.clone();
}
if draft.work_description.trim().is_empty() {
draft.work_description = draft.summary.clone();
}
draft.summary = draft.work_description.clone();
if draft.form_draft.is_some() {
draft.form_draft = Some(PuzzleFormDraft {
work_title: normalize_required_string(&draft.work_title),
work_description: normalize_required_string(&draft.work_description),
picture_description: draft
.levels
.first()
.and_then(|level| normalize_required_string(&level.picture_description)),
});
}
}
pub fn selected_puzzle_level(
draft: &PuzzleResultDraft,
level_id: Option<&str>,
) -> Option<PuzzleDraftLevel> {
let normalized = normalize_puzzle_draft(draft.clone());
let requested_level_id = level_id.and_then(normalize_required_string);
requested_level_id
.as_deref()
.and_then(|target_id| {
normalized
.levels
.iter()
.find(|level| level.level_id == target_id)
.cloned()
})
.or_else(|| normalized.levels.first().cloned())
}
pub fn replace_puzzle_level(
draft: &PuzzleResultDraft,
level: PuzzleDraftLevel,
) -> Result<PuzzleResultDraft, PuzzleFieldError> {
let mut next_draft = normalize_puzzle_draft(draft.clone());
let Some(index) = next_draft
.levels
.iter()
.position(|entry| entry.level_id == level.level_id)
else {
return Err(PuzzleFieldError::InvalidOperation);
};
next_draft.levels[index] = level;
sync_primary_level_fields(&mut next_draft);
Ok(next_draft)
}
pub fn append_blank_puzzle_level(draft: &PuzzleResultDraft) -> PuzzleResultDraft {
let mut next_draft = normalize_puzzle_draft(draft.clone());
let next_index = next_draft.levels.len() + 1;
let picture_description = next_draft
.levels
.first()
.map(|level| level.picture_description.clone())
.filter(|value| !value.trim().is_empty())
.unwrap_or_else(|| fallback_text(&next_draft.anchor_pack.visual_subject.value, "画面主体"));
next_draft.levels.push(PuzzleDraftLevel {
level_id: format!("puzzle-level-{next_index}"),
level_name: build_level_name_from_picture(
picture_description.as_str(),
&next_draft.theme_tags,
next_index,
),
picture_description,
candidates: Vec::new(),
selected_candidate_id: None,
cover_image_src: None,
cover_asset_id: None,
generation_status: "idle".to_string(),
});
sync_primary_level_fields(&mut next_draft);
next_draft
}
pub fn remove_puzzle_level(
draft: &PuzzleResultDraft,
level_id: &str,
) -> Result<PuzzleResultDraft, PuzzleFieldError> {
let mut next_draft = normalize_puzzle_draft(draft.clone());
if next_draft.levels.len() <= 1 {
return Err(PuzzleFieldError::InvalidOperation);
}
let normalized_level_id =
normalize_required_string(level_id).ok_or(PuzzleFieldError::InvalidOperation)?;
next_draft
.levels
.retain(|level| level.level_id != normalized_level_id);
if next_draft.levels.is_empty() {
return Err(PuzzleFieldError::InvalidOperation);
}
sync_primary_level_fields(&mut next_draft);
Ok(next_draft)
}
pub fn build_result_preview(
draft: &PuzzleResultDraft,
author_display_name: Option<&str>,
) -> PuzzleResultPreviewEnvelope {
let blockers = validate_publish_requirements(draft, author_display_name);
let normalized_draft = normalize_puzzle_draft(draft.clone());
if normalized_draft.form_draft.is_some() {
return PuzzleResultPreviewEnvelope {
draft: normalized_draft,
blockers: Vec::new(),
quality_findings: Vec::new(),
publish_ready: false,
};
}
let blockers = validate_publish_requirements(&normalized_draft, author_display_name);
PuzzleResultPreviewEnvelope {
draft: draft.clone(),
draft: normalized_draft,
blockers,
quality_findings: Vec::new(),
publish_ready: validate_publish_requirements(draft, author_display_name).is_empty(),
@@ -866,27 +1177,44 @@ pub fn validate_publish_requirements(
draft: &PuzzleResultDraft,
author_display_name: Option<&str>,
) -> Vec<PuzzleResultPreviewBlocker> {
let draft = normalize_puzzle_draft(draft.clone());
let mut blockers = Vec::new();
if normalize_required_string(&draft.level_name).is_none() {
if normalize_required_string(&draft.work_title).is_none() {
blockers.push(PuzzleResultPreviewBlocker {
id: "missing-level-name".to_string(),
code: "MISSING_LEVEL_NAME".to_string(),
message: "关卡名不能为空".to_string(),
id: "missing-work-title".to_string(),
code: "MISSING_WORK_TITLE".to_string(),
message: "作品名称不能为空".to_string(),
});
}
if draft
.cover_image_src
.as_deref()
.map(str::trim)
.unwrap_or("")
.is_empty()
{
if normalize_required_string(&draft.work_description).is_none() {
blockers.push(PuzzleResultPreviewBlocker {
id: "missing-cover-image".to_string(),
code: "MISSING_COVER_IMAGE".to_string(),
message: "正式拼图图片尚未确定".to_string(),
id: "missing-work-description".to_string(),
code: "MISSING_WORK_DESCRIPTION".to_string(),
message: "作品描述不能为空".to_string(),
});
}
for level in &draft.levels {
if normalize_required_string(&level.level_name).is_none() {
blockers.push(PuzzleResultPreviewBlocker {
id: format!("missing-level-name-{}", level.level_id),
code: "MISSING_LEVEL_NAME".to_string(),
message: "关卡名不能为空".to_string(),
});
}
if level
.cover_image_src
.as_deref()
.map(str::trim)
.unwrap_or("")
.is_empty()
{
blockers.push(PuzzleResultPreviewBlocker {
id: format!("missing-cover-image-{}", level.level_id),
code: "MISSING_COVER_IMAGE".to_string(),
message: "正式拼图图片尚未确定".to_string(),
});
}
}
if draft.theme_tags.len() < PUZZLE_MIN_TAG_COUNT
|| draft.theme_tags.len() > PUZZLE_MAX_TAG_COUNT
{
@@ -917,18 +1245,22 @@ pub fn create_work_profile(
) -> Result<PuzzleWorkProfile, PuzzleFieldError> {
let author_display_name = normalize_required_string(author_display_name)
.ok_or(PuzzleFieldError::MissingAuthorDisplayName)?;
let preview = build_result_preview(draft, Some(&author_display_name));
let draft = normalize_puzzle_draft(draft.clone());
let preview = build_result_preview(&draft, Some(&author_display_name));
Ok(PuzzleWorkProfile {
work_id,
profile_id,
owner_user_id,
source_session_id,
author_display_name,
work_title: draft.work_title.clone(),
work_description: draft.work_description.clone(),
level_name: draft.level_name.clone(),
summary: draft.summary.clone(),
theme_tags: normalize_theme_tags(draft.theme_tags.clone()),
cover_image_src: draft.cover_image_src.clone(),
cover_asset_id: draft.cover_asset_id.clone(),
levels: draft.levels.clone(),
publication_status: PuzzlePublicationStatus::Draft,
updated_at_micros,
published_at_micros: None,
@@ -946,14 +1278,18 @@ pub fn publish_work_profile(
draft: &PuzzleResultDraft,
published_at_micros: i64,
) -> Result<PuzzleWorkProfile, PuzzleFieldError> {
if !validate_publish_requirements(draft, Some(&profile.author_display_name)).is_empty() {
let draft = normalize_puzzle_draft(draft.clone());
if !validate_publish_requirements(&draft, Some(&profile.author_display_name)).is_empty() {
return Err(PuzzleFieldError::InvalidOperation);
}
profile.work_title = draft.work_title.clone();
profile.work_description = draft.work_description.clone();
profile.level_name = draft.level_name.clone();
profile.summary = draft.summary.clone();
profile.theme_tags = normalize_theme_tags(draft.theme_tags.clone());
profile.cover_image_src = draft.cover_image_src.clone();
profile.cover_asset_id = draft.cover_asset_id.clone();
profile.levels = draft.levels.clone();
profile.publication_status = PuzzlePublicationStatus::Published;
profile.publish_ready = true;
profile.updated_at_micros = published_at_micros;
@@ -965,22 +1301,39 @@ pub fn publish_work_profile(
/// 这里只允许覆盖 PRD 明确要求的关卡名、摘要与标签,不额外扩到更多结果页元数据。
pub fn apply_publish_overrides_to_draft(
draft: &PuzzleResultDraft,
work_title: Option<String>,
work_description: Option<String>,
level_name: Option<String>,
summary: Option<String>,
theme_tags: Option<Vec<String>>,
levels: Option<Vec<PuzzleDraftLevel>>,
) -> Result<PuzzleResultDraft, PuzzleFieldError> {
let mut next_draft = draft.clone();
let mut next_draft = normalize_puzzle_draft(draft.clone());
if let Some(next_work_title) = work_title
&& let Some(normalized_work_title) = normalize_required_string(&next_work_title)
{
next_draft.work_title = normalized_work_title;
}
if let Some(next_work_description) = work_description
&& let Some(normalized_work_description) = normalize_required_string(&next_work_description)
{
next_draft.work_description = normalized_work_description;
}
if let Some(next_level_name) = level_name
&& let Some(normalized_level_name) = normalize_required_string(&next_level_name)
{
next_draft.level_name = normalized_level_name;
if let Some(primary_level) = next_draft.levels.first_mut() {
primary_level.level_name = normalized_level_name;
}
}
if let Some(next_summary) = summary
&& let Some(normalized_summary) = normalize_required_string(&next_summary)
{
next_draft.summary = normalized_summary;
next_draft.work_description = normalized_summary;
}
if let Some(next_theme_tags) = theme_tags {
@@ -993,9 +1346,41 @@ pub fn apply_publish_overrides_to_draft(
next_draft.theme_tags = normalized_theme_tags;
}
if let Some(next_levels) = levels {
let normalized_levels = normalize_puzzle_levels(next_levels, &next_draft.theme_tags)?;
next_draft.levels = normalized_levels;
}
sync_primary_level_fields(&mut next_draft);
Ok(next_draft)
}
pub fn normalize_puzzle_levels(
levels: Vec<PuzzleDraftLevel>,
theme_tags: &[String],
) -> Result<Vec<PuzzleDraftLevel>, PuzzleFieldError> {
let mut normalized_levels = Vec::new();
for (index, mut level) in levels.into_iter().enumerate() {
let level_id = normalize_required_string(&level.level_id)
.unwrap_or_else(|| format!("puzzle-level-{}", index + 1));
let picture_description = normalize_required_string(&level.picture_description)
.unwrap_or_else(|| format!("{}关画面", index + 1));
let level_name = normalize_required_string(&level.level_name).unwrap_or_else(|| {
build_level_name_from_picture(picture_description.as_str(), theme_tags, index + 1)
});
level.level_id = level_id;
level.level_name = level_name;
level.picture_description = picture_description;
level.generation_status = normalize_required_string(&level.generation_status)
.unwrap_or_else(|| "idle".to_string());
normalized_levels.push(level);
}
if normalized_levels.is_empty() {
return Err(PuzzleFieldError::InvalidOperation);
}
Ok(normalized_levels)
}
pub fn resolve_puzzle_grid_size(cleared_level_count: u32) -> u32 {
if cleared_level_count >= 3 { 4 } else { 3 }
}
@@ -1683,25 +2068,54 @@ fn infer_tags_and_forbidden(source: &str) -> String {
}
}
fn parse_form_seed_text(source: &str) -> Option<(String, String)> {
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct PuzzleFormSeedParts {
work_title: Option<String>,
work_description: Option<String>,
picture_description: Option<String>,
}
impl PuzzleFormSeedParts {
fn has_any_value(&self) -> bool {
self.work_title.is_some()
|| self.work_description.is_some()
|| self.picture_description.is_some()
}
}
fn parse_form_seed_text(source: &str) -> Option<PuzzleFormSeedParts> {
let normalized_source = source.trim();
let title_marker = "拼图标题:";
let description_marker = "画面描述:";
let title_start = normalized_source.find(title_marker)? + title_marker.len();
let description_start = normalized_source.find(description_marker)?;
if description_start <= title_start {
if normalized_source.is_empty() {
return None;
}
let title = normalize_required_string(&normalized_source[title_start..description_start]);
let picture_description = normalize_required_string(
&normalized_source[description_start + description_marker.len()..],
);
let title_marker = if normalized_source.contains("作品名称:") {
"作品名称:"
} else {
"拼图标题:"
};
let parts = PuzzleFormSeedParts {
work_title: extract_form_seed_value(normalized_source, title_marker),
work_description: extract_form_seed_value(normalized_source, "作品描述:"),
picture_description: extract_form_seed_value(normalized_source, "画面描述:"),
};
match (title, picture_description) {
(Some(title), Some(picture_description)) => Some((title, picture_description)),
_ => None,
}
parts.has_any_value().then_some(parts)
}
fn extract_form_seed_value(source: &str, marker: &str) -> Option<String> {
let value_start = source.find(marker)? + marker.len();
let value_end = ["作品名称:", "拼图标题:", "作品描述:", "画面描述:"]
.into_iter()
.filter(|next_marker| *next_marker != marker)
.filter_map(|next_marker| {
source[value_start..]
.find(next_marker)
.map(|index| value_start + index)
})
.min()
.unwrap_or(source.len());
normalize_required_string(&source[value_start..value_end])
}
fn build_form_tags_and_forbidden(title: &str, picture_description: &str) -> String {
@@ -1777,6 +2191,22 @@ fn build_result_summary(anchor_pack: &PuzzleAnchorPack) -> String {
)
}
fn resolve_work_description(seed_text: Option<&str>, anchor_pack: &PuzzleAnchorPack) -> String {
seed_text
.and_then(parse_form_seed_text)
.and_then(|parts| {
parts
.work_description
.or(parts.picture_description)
.or(parts.work_title)
})
.unwrap_or_else(|| build_result_summary(anchor_pack))
}
fn build_work_title(anchor_pack: &PuzzleAnchorPack) -> String {
fallback_text(&anchor_pack.theme_promise.value, "奇景拼图")
}
fn extract_forbidden_directive(source: &str) -> String {
if let Some((_, tail)) = source.split_once('') {
return normalize_required_string(tail).unwrap_or_else(|| "禁止标题字".to_string());
@@ -1784,20 +2214,24 @@ fn extract_forbidden_directive(source: &str) -> String {
"禁止标题字".to_string()
}
fn build_level_name(anchor_pack: &PuzzleAnchorPack, normalized_tags: &[String]) -> String {
if is_form_anchor_pack(anchor_pack)
&& let Some(title) = normalize_required_string(&anchor_pack.theme_promise.value)
{
return title;
fn build_level_name_from_picture(
picture_description: &str,
normalized_tags: &[String],
level_index: usize,
) -> String {
let source = normalize_required_string(picture_description).unwrap_or_default();
for keyword in [
"", "", "神庙", "遗迹", "森林", "雨夜", "城市", "机械", "", "", "", "", "",
"",
] {
if source.contains(keyword) {
return format!("{keyword}画面");
}
}
if let Some(tag) = normalized_tags.first() {
return format!("{tag}拼图");
return format!("{tag}{level_index}");
}
if let Some(subject) = normalize_required_string(&anchor_pack.visual_subject.value) {
return subject.chars().take(8).collect::<String>();
}
"奇景拼图".to_string()
format!("{level_index}")
}
fn fallback_text(value: &str, fallback: &str) -> String {
@@ -2432,15 +2866,28 @@ mod tests {
owner_user_id: owner_user_id.to_string(),
source_session_id: None,
author_display_name: "作者".to_string(),
work_title: format!("{profile_id} 作品"),
work_description: "summary".to_string(),
level_name: format!("{profile_id} 关"),
summary: "summary".to_string(),
theme_tags: tags.into_iter().map(|value| value.to_string()).collect(),
cover_image_src: Some("/cover.png".to_string()),
cover_asset_id: Some("asset-1".to_string()),
levels: vec![PuzzleDraftLevel {
level_id: "puzzle-level-1".to_string(),
level_name: format!("{profile_id} 关"),
picture_description: "summary".to_string(),
candidates: Vec::new(),
selected_candidate_id: None,
cover_image_src: Some("/cover.png".to_string()),
cover_asset_id: Some("asset-1".to_string()),
generation_status: "ready".to_string(),
}],
publication_status: PuzzlePublicationStatus::Published,
updated_at_micros: 100,
published_at_micros: Some(100),
play_count: 0,
recent_play_count_7d: 0,
remix_count: 0,
like_count: 0,
publish_ready: true,
@@ -2488,10 +2935,16 @@ mod tests {
#[test]
fn form_seed_locks_title_and_picture_description_as_primary_anchors() {
let anchor_pack = infer_anchor_pack(
"拼图标题:暖灯猫街\n画面描述:一只猫在雨夜灯牌下回头。",
"作品名称:暖灯猫街\n作品描述:一套雨夜猫街主题拼图。\n画面描述:一只猫在雨夜灯牌下回头。",
None,
);
let draft = compile_result_draft(&anchor_pack, &[]);
let draft = compile_result_draft_from_seed(
&anchor_pack,
&[],
Some(
"作品名称:暖灯猫街\n作品描述:一套雨夜猫街主题拼图。\n画面描述:一只猫在雨夜灯牌下回头。",
),
);
assert_eq!(anchor_pack.theme_promise.value, "暖灯猫街");
assert_eq!(anchor_pack.theme_promise.status, PuzzleAnchorStatus::Locked);
@@ -2500,8 +2953,14 @@ mod tests {
anchor_pack.visual_subject.status,
PuzzleAnchorStatus::Locked
);
assert_eq!(draft.level_name, "暖灯猫街");
assert_eq!(draft.summary, "只猫在雨夜灯牌下回头");
assert_eq!(draft.work_title, "暖灯猫街");
assert_eq!(draft.work_description, "套雨夜猫街主题拼图");
assert_eq!(draft.summary, "一套雨夜猫街主题拼图。");
assert_eq!(draft.level_name, "猫画面");
assert_eq!(
draft.levels[0].picture_description,
"一只猫在雨夜灯牌下回头。"
);
assert_eq!(
draft
.creator_intent
@@ -2525,9 +2984,10 @@ mod tests {
"一只猫在雨夜灯牌下回头。\n远处有暖色霓虹和玻璃雨滴。"
);
assert_eq!(
draft.summary,
draft.levels[0].picture_description,
"一只猫在雨夜灯牌下回头。\n远处有暖色霓虹和玻璃雨滴。"
);
assert_eq!(draft.summary, draft.work_description);
assert!(draft.theme_tags.iter().any(|tag| tag == "猫咪"));
assert!(draft.theme_tags.iter().any(|tag| tag == "雨夜"));
}
@@ -2909,6 +3369,8 @@ mod tests {
let updated = apply_publish_overrides_to_draft(
&draft,
Some("雨夜猫塔作品".to_string()),
Some("作品描述。".to_string()),
Some("雨夜猫塔".to_string()),
Some("一张更聚焦猫咪塔楼的夜景拼图。".to_string()),
Some(vec![
@@ -2916,10 +3378,12 @@ mod tests {
"猫咪".to_string(),
"遗迹".to_string(),
]),
None,
)
.expect("publish overrides should succeed");
assert_eq!(updated.level_name, "雨夜猫塔");
assert_eq!(updated.work_title, "雨夜猫塔作品");
assert_eq!(updated.summary, "一张更聚焦猫咪塔楼的夜景拼图。");
assert_eq!(
updated.theme_tags,
@@ -2935,9 +3399,16 @@ mod tests {
fn apply_publish_overrides_rejects_invalid_tag_count() {
let anchor_pack = infer_anchor_pack("蒸汽城市", Some("蒸汽城市"));
let draft = compile_result_draft(&anchor_pack, &[]);
let error =
apply_publish_overrides_to_draft(&draft, None, None, Some(vec!["蒸汽".to_string()]))
.expect_err("invalid tag count should fail");
let error = apply_publish_overrides_to_draft(
&draft,
None,
None,
None,
None,
Some(vec!["蒸汽".to_string()]),
None,
)
.expect_err("invalid tag count should fail");
assert_eq!(error, PuzzleFieldError::InvalidTagCount);
}

View File

@@ -18,6 +18,7 @@ pub const DEFAULT_REQUEST_TIMEOUT_MS: u64 = 30_000;
pub const DEFAULT_MAX_RETRIES: u32 = 1;
pub const DEFAULT_RETRY_BACKOFF_MS: u64 = 500;
pub const CHAT_COMPLETIONS_PATH: &str = "/chat/completions";
pub const RESPONSES_PATH: &str = "/responses";
const DEFAULT_LLM_RAW_LOG_DIR: &str = "logs/llm-raw";
static LLM_RAW_LOG_SEQUENCE: AtomicU64 = AtomicU64::new(1);
@@ -66,6 +67,14 @@ pub struct LlmTextRequest {
pub messages: Vec<LlmMessage>,
pub max_tokens: Option<u32>,
pub enable_web_search: bool,
pub protocol: LlmTextProtocol,
}
// 文本协议必须由业务请求显式选择,避免全局默认模型把不同场景混到同一上游形态。
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LlmTextProtocol {
ChatCompletions,
Responses,
}
// 上层在流式消费时拿到的是“累计文本 + 当前增量”,避免每层重新自己拼接。
@@ -117,9 +126,16 @@ pub struct LlmClient {
}
#[derive(Serialize)]
struct ChatCompletionsRequestBody<'a> {
model: &'a str,
messages: &'a [LlmMessage],
#[serde(untagged)]
enum LlmRequestBody {
ChatCompletions(ChatCompletionsRequestBody),
Responses(ResponsesRequestBody),
}
#[derive(Serialize)]
struct ChatCompletionsRequestBody {
model: String,
messages: Vec<LlmMessage>,
stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
max_tokens: Option<u32>,
@@ -130,10 +146,42 @@ struct ChatCompletionsRequestBody<'a> {
#[derive(Serialize)]
struct ChatCompletionsWebSearchOptions {}
#[derive(Serialize)]
struct ResponsesRequestBody {
model: String,
stream: bool,
input: Vec<ResponsesInputMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
max_output_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
tools: Option<Vec<ResponsesWebSearchTool>>,
}
#[derive(Serialize)]
struct ResponsesInputMessage {
role: &'static str,
content: Vec<ResponsesInputContentPart>,
}
#[derive(Serialize)]
struct ResponsesInputContentPart {
#[serde(rename = "type")]
part_type: &'static str,
text: String,
}
#[derive(Serialize)]
struct ResponsesWebSearchTool {
#[serde(rename = "type")]
tool_type: &'static str,
max_keyword: u8,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct LlmRawFailureInputLog<'a> {
provider: &'static str,
protocol: &'static str,
model: &'a str,
stream: bool,
attempt: u32,
@@ -181,10 +229,48 @@ struct ChatCompletionsContentPart {
text: Option<String>,
}
#[derive(Default)]
#[derive(Deserialize)]
struct ResponsesResponseEnvelope {
id: Option<String>,
model: Option<String>,
#[serde(default)]
output_text: Option<String>,
#[serde(default)]
output: Vec<ResponsesOutputItem>,
#[serde(default)]
status: Option<String>,
usage: Option<ResponsesUsage>,
}
#[derive(Deserialize)]
struct ResponsesOutputItem {
#[serde(default)]
content: Vec<ResponsesOutputContentPart>,
}
#[derive(Deserialize)]
struct ResponsesOutputContentPart {
#[serde(rename = "type")]
#[allow(dead_code)]
part_type: Option<String>,
#[serde(default)]
text: Option<String>,
}
#[derive(Deserialize)]
struct ResponsesUsage {
#[serde(default)]
input_tokens: u64,
#[serde(default)]
output_tokens: u64,
#[serde(default)]
total_tokens: u64,
}
struct OpenAiCompatibleSseParser {
buffer: String,
raw_text: String,
protocol: LlmTextProtocol,
}
#[derive(Debug)]
@@ -282,6 +368,14 @@ impl LlmConfig {
CHAT_COMPLETIONS_PATH.trim_start_matches('/')
)
}
pub fn responses_url(&self) -> String {
format!(
"{}/{}",
self.base_url.trim_end_matches('/'),
RESPONSES_PATH.trim_start_matches('/')
)
}
}
impl LlmMessage {
@@ -312,6 +406,7 @@ impl LlmTextRequest {
messages,
max_tokens: None,
enable_web_search: false,
protocol: LlmTextProtocol::ChatCompletions,
}
}
@@ -337,6 +432,11 @@ impl LlmTextRequest {
self
}
pub fn with_responses_api(mut self) -> Self {
self.protocol = LlmTextProtocol::Responses;
self
}
fn validate(&self) -> Result<(), LlmError> {
if self.messages.is_empty() {
return Err(LlmError::InvalidRequest(
@@ -372,6 +472,15 @@ impl LlmTextRequest {
}
}
impl LlmTextProtocol {
fn as_str(self) -> &'static str {
match self {
Self::ChatCompletions => "chat_completions",
Self::Responses => "responses",
}
}
}
impl fmt::Display for LlmError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -430,18 +539,23 @@ impl LlmClient {
llm_error
})?;
parse_chat_completions_response(self.config.provider(), &resolved_model, raw_text.as_str())
.map_err(|error| {
log_llm_raw_failure(
&self.config,
&request,
false,
1,
"parse_response_failed",
raw_text.as_str(),
);
error
})
parse_text_response(
request.protocol,
self.config.provider(),
&resolved_model,
raw_text.as_str(),
)
.map_err(|error| {
log_llm_raw_failure(
&self.config,
&request,
false,
1,
"parse_response_failed",
raw_text.as_str(),
);
error
})
}
pub async fn request_single_message_text(
@@ -470,7 +584,7 @@ impl LlmClient {
.and_then(|value| value.to_str().ok())
.map(str::to_string);
let mut parser = OpenAiCompatibleSseParser::default();
let mut parser = OpenAiCompatibleSseParser::new(request.protocol);
let mut accumulated_text = String::new();
let mut finish_reason = None;
let mut undecoded_chunk_bytes = Vec::new();
@@ -658,29 +772,27 @@ impl LlmClient {
request: &LlmTextRequest,
stream: bool,
) -> Result<reqwest::Response, LlmError> {
let request_body = ChatCompletionsRequestBody {
model: request.resolved_model(self.config.model()),
messages: request.messages.as_slice(),
stream,
max_tokens: request.max_tokens,
web_search_options: request
.enable_web_search
.then_some(ChatCompletionsWebSearchOptions {}),
let request_body = build_request_body(request, self.config.model(), stream);
let model = request.resolved_model(self.config.model());
let url = match request.protocol {
LlmTextProtocol::ChatCompletions => self.config.chat_completions_url(),
LlmTextProtocol::Responses => self.config.responses_url(),
};
let max_attempts = self.config.max_retries().saturating_add(1);
for attempt in 1..=max_attempts {
debug!(
"platform-llm request started: provider={}, stream={}, attempt={}, model={}",
"platform-llm request started: provider={}, protocol={}, stream={}, attempt={}, model={}",
self.config.provider().as_str(),
request.protocol.as_str(),
stream,
attempt,
request_body.model
model
);
let send_result = self
.http_client
.post(self.config.chat_completions_url())
.post(url.as_str())
.bearer_auth(self.config.api_key())
.json(&request_body)
.timeout(Duration::from_millis(self.config.request_timeout_ms()))
@@ -690,8 +802,9 @@ impl LlmClient {
match send_result {
Ok(response) if response.status().is_success() => {
debug!(
"platform-llm request succeeded: provider={}, stream={}, attempt={}, status={}",
"platform-llm request succeeded: provider={}, protocol={}, stream={}, attempt={}, status={}",
self.config.provider().as_str(),
request.protocol.as_str(),
stream,
attempt,
response.status().as_u16()
@@ -705,8 +818,9 @@ impl LlmClient {
if should_retry_status(status) && attempt < max_attempts {
warn!(
"platform-llm request retrying after upstream status: provider={}, attempt={}, status={}, message={}",
"platform-llm request retrying after upstream status: provider={}, protocol={}, attempt={}, status={}, message={}",
self.config.provider().as_str(),
request.protocol.as_str(),
attempt,
status.as_u16(),
message
@@ -731,8 +845,9 @@ impl LlmClient {
Err(error) if error.is_timeout() => {
if attempt < max_attempts {
warn!(
"platform-llm request retrying after timeout: provider={}, attempt={}",
"platform-llm request retrying after timeout: provider={}, protocol={}, attempt={}",
self.config.provider().as_str(),
request.protocol.as_str(),
attempt
);
self.sleep_before_retry(attempt).await;
@@ -754,8 +869,9 @@ impl LlmClient {
let message = error.to_string();
if attempt < max_attempts {
warn!(
"platform-llm request retrying after connectivity failure: provider={}, attempt={}, error={}",
"platform-llm request retrying after connectivity failure: provider={}, protocol={}, attempt={}, error={}",
self.config.provider().as_str(),
request.protocol.as_str(),
attempt,
message
);
@@ -810,6 +926,14 @@ impl LlmClient {
}
impl OpenAiCompatibleSseParser {
fn new(protocol: LlmTextProtocol) -> Self {
Self {
buffer: String::new(),
raw_text: String::new(),
protocol,
}
}
fn push_chunk(&mut self, chunk: &str) -> Result<Vec<ParsedStreamEvent>, LlmError> {
self.raw_text.push_str(chunk);
self.buffer.push_str(chunk);
@@ -837,7 +961,7 @@ impl OpenAiCompatibleSseParser {
let block = self.buffer[..boundary].to_string();
self.buffer = self.buffer[(boundary + 2)..].to_string();
if let Some(event) = parse_sse_event_block(block.as_str())? {
if let Some(event) = parse_sse_event_block(self.protocol, block.as_str())? {
events.push(event);
}
}
@@ -855,6 +979,55 @@ fn normalize_non_empty(value: String, error_message: &str) -> Result<String, Llm
Ok(trimmed)
}
fn build_request_body(
request: &LlmTextRequest,
fallback_model: &str,
stream: bool,
) -> LlmRequestBody {
match request.protocol {
LlmTextProtocol::ChatCompletions => {
LlmRequestBody::ChatCompletions(ChatCompletionsRequestBody {
model: request.resolved_model(fallback_model).to_string(),
messages: request.messages.clone(),
stream,
max_tokens: request.max_tokens,
web_search_options: request
.enable_web_search
.then_some(ChatCompletionsWebSearchOptions {}),
})
}
LlmTextProtocol::Responses => LlmRequestBody::Responses(ResponsesRequestBody {
model: request.resolved_model(fallback_model).to_string(),
stream,
input: map_responses_input_messages(request.messages.as_slice()),
max_output_tokens: request.max_tokens,
tools: request.enable_web_search.then(|| {
vec![ResponsesWebSearchTool {
tool_type: "web_search",
max_keyword: 3,
}]
}),
}),
}
}
fn map_responses_input_messages(messages: &[LlmMessage]) -> Vec<ResponsesInputMessage> {
messages
.iter()
.map(|message| ResponsesInputMessage {
role: match message.role {
LlmMessageRole::System => "system",
LlmMessageRole::User => "user",
LlmMessageRole::Assistant => "assistant",
},
content: vec![ResponsesInputContentPart {
part_type: "input_text",
text: message.content.clone(),
}],
})
.collect()
}
fn log_llm_raw_failure(
config: &LlmConfig,
request: &LlmTextRequest,
@@ -890,6 +1063,7 @@ fn write_llm_raw_failure(
let model = request.resolved_model(config.model());
let input_log = LlmRawFailureInputLog {
provider: config.provider().as_str(),
protocol: request.protocol.as_str(),
model,
stream,
attempt,
@@ -936,6 +1110,20 @@ fn sanitize_log_file_segment(value: &str) -> String {
}
}
fn parse_text_response(
protocol: LlmTextProtocol,
provider: LlmProvider,
fallback_model: &str,
raw_text: &str,
) -> Result<LlmTextResponse, LlmError> {
match protocol {
LlmTextProtocol::ChatCompletions => {
parse_chat_completions_response(provider, fallback_model, raw_text)
}
LlmTextProtocol::Responses => parse_responses_response(provider, fallback_model, raw_text),
}
}
fn parse_chat_completions_response(
provider: LlmProvider,
fallback_model: &str,
@@ -967,6 +1155,56 @@ fn parse_chat_completions_response(
})
}
fn parse_responses_response(
provider: LlmProvider,
fallback_model: &str,
raw_text: &str,
) -> Result<LlmTextResponse, LlmError> {
let parsed: ResponsesResponseEnvelope = serde_json::from_str(raw_text).map_err(|error| {
LlmError::Deserialize(format!("解析 LLM Responses JSON 响应失败:{error}"))
})?;
let content = extract_responses_text(&parsed)
.ok_or(LlmError::EmptyResponse)?
.trim()
.to_string();
if content.is_empty() {
return Err(LlmError::EmptyResponse);
}
Ok(LlmTextResponse {
provider,
model: parsed.model.unwrap_or_else(|| fallback_model.to_string()),
content,
finish_reason: parsed.status,
response_id: parsed.id,
usage: parsed.usage.map(|usage| LlmTokenUsage {
prompt_tokens: usage.input_tokens,
completion_tokens: usage.output_tokens,
total_tokens: usage.total_tokens,
}),
})
}
fn extract_responses_text(parsed: &ResponsesResponseEnvelope) -> Option<String> {
parsed
.output_text
.as_deref()
.map(str::to_string)
.filter(|text| !text.is_empty())
.or_else(|| {
let text = parsed
.output
.iter()
.flat_map(|item| item.content.iter())
.filter_map(|part| part.text.as_deref())
.collect::<Vec<_>>()
.join("");
if text.is_empty() { None } else { Some(text) }
})
}
fn extract_message_text(choice: &ChatCompletionsChoice) -> Option<String> {
choice
.message
@@ -1016,7 +1254,10 @@ fn decode_utf8_stream_chunk(bytes: &[u8]) -> Result<(String, Vec<u8>), LlmError>
}
}
fn parse_sse_event_block(block: &str) -> Result<Option<ParsedStreamEvent>, LlmError> {
fn parse_sse_event_block(
protocol: LlmTextProtocol,
block: &str,
) -> Result<Option<ParsedStreamEvent>, LlmError> {
let data_lines = block
.lines()
.filter_map(|line| line.trim().strip_prefix("data:"))
@@ -1032,6 +1273,10 @@ fn parse_sse_event_block(block: &str) -> Result<Option<ParsedStreamEvent>, LlmEr
return Ok(None);
}
if protocol == LlmTextProtocol::Responses {
return parse_responses_sse_event(data.as_str());
}
let parsed: ChatCompletionsResponseEnvelope = serde_json::from_str(data.as_str())
.map_err(|error| LlmError::Deserialize(format!("解析 LLM SSE 事件失败:{error}")))?;
let first_choice = parsed
@@ -1045,6 +1290,44 @@ fn parse_sse_event_block(block: &str) -> Result<Option<ParsedStreamEvent>, LlmEr
}))
}
fn parse_responses_sse_event(data: &str) -> Result<Option<ParsedStreamEvent>, LlmError> {
let parsed: serde_json::Value = serde_json::from_str(data).map_err(|error| {
LlmError::Deserialize(format!("解析 LLM Responses SSE 事件失败:{error}"))
})?;
let event_type = parsed
.get("type")
.and_then(serde_json::Value::as_str)
.unwrap_or_default();
match event_type {
"response.output_text.delta" => Ok(Some(ParsedStreamEvent {
delta_text: parsed
.get("delta")
.and_then(serde_json::Value::as_str)
.map(str::to_string),
finish_reason: None,
})),
"response.completed" => Ok(Some(ParsedStreamEvent {
delta_text: None,
finish_reason: Some("completed".to_string()),
})),
"response.failed" | "error" => {
let message = parsed
.get("error")
.and_then(|error| error.get("message"))
.and_then(serde_json::Value::as_str)
.or_else(|| parsed.get("message").and_then(serde_json::Value::as_str))
.unwrap_or("LLM Responses SSE 返回失败事件")
.to_string();
Err(LlmError::Upstream {
status_code: 502,
message,
})
}
_ => Ok(None),
}
}
fn should_retry_status(status: StatusCode) -> bool {
status == StatusCode::REQUEST_TIMEOUT
|| status == StatusCode::TOO_MANY_REQUESTS
@@ -1151,11 +1434,12 @@ mod tests {
config.chat_completions_url(),
"https://example.com/base/chat/completions"
);
assert_eq!(config.responses_url(), "https://example.com/base/responses");
}
#[test]
fn sse_parser_handles_split_chunks_and_done_marker() {
let mut parser = OpenAiCompatibleSseParser::default();
let mut parser = OpenAiCompatibleSseParser::new(LlmTextProtocol::ChatCompletions);
let events_a = parser
.push_chunk("data: {\"choices\":[{\"delta\":{\"content\":\"\"}}]}\r\n\r\n")
.expect("first chunk should parse");
@@ -1170,6 +1454,24 @@ mod tests {
assert_eq!(events_b[0].finish_reason.as_deref(), Some("stop"));
}
#[test]
fn responses_sse_parser_only_emits_output_text_delta() {
let mut parser = OpenAiCompatibleSseParser::new(LlmTextProtocol::Responses);
let events = parser
.push_chunk(concat!(
"data: {\"type\":\"response.created\"}\n\n",
"data: {\"type\":\"response.output_text.delta\",\"delta\":\"\"}\n\n",
"data: {\"type\":\"response.output_text.delta\",\"delta\":\"\"}\n\n",
"data: {\"type\":\"response.completed\"}\n\n",
))
.expect("responses stream should parse");
assert_eq!(events.len(), 3);
assert_eq!(events[0].delta_text.as_deref(), Some(""));
assert_eq!(events[1].delta_text.as_deref(), Some(""));
assert_eq!(events[2].finish_reason.as_deref(), Some("completed"));
}
#[test]
fn decode_utf8_stream_chunk_preserves_incomplete_multibyte_suffix() {
let full_bytes = "你好".as_bytes();
@@ -1284,6 +1586,72 @@ mod tests {
assert_eq!(request_json["web_search_options"], serde_json::json!({}));
}
#[tokio::test]
async fn request_text_sends_responses_body_with_web_search_tool() {
let listener = TcpListener::bind("127.0.0.1:0").expect("listener should bind");
let address = listener.local_addr().expect("listener should have addr");
let server_handle = thread::spawn(move || {
let (mut stream, _) = listener.accept().expect("request should connect");
let request_text = read_request(&mut stream);
write_response(
&mut stream,
MockResponse {
status_line: "200 OK",
content_type: "application/json; charset=utf-8",
body: r#"{"id":"resp_responses","model":"deepseek-v3-2-251201","output_text":"Responses ","status":"completed","usage":{"input_tokens":9,"output_tokens":4,"total_tokens":13}}"#.to_string(),
extra_headers: Vec::new(),
},
);
request_text
});
let client = build_test_client(format!("http://{address}"), 0);
let response = client
.request_text(
LlmTextRequest::single_turn("系统", "用户")
.with_model("deepseek-v3-2-251201")
.with_responses_api()
.with_web_search(true)
.with_max_tokens(128),
)
.await
.expect("responses request_text should succeed");
let request_text = server_handle.join().expect("server thread should join");
let request_line = request_text.lines().next().unwrap_or_default();
let request_body = request_text
.split("\r\n\r\n")
.nth(1)
.expect("request body should exist");
let request_json: serde_json::Value =
serde_json::from_str(request_body).expect("request body should be json");
assert!(request_line.contains("POST /responses HTTP/1.1"));
assert_eq!(response.content, "Responses 成功");
assert_eq!(response.model, "deepseek-v3-2-251201");
assert_eq!(
response.usage,
Some(LlmTokenUsage {
prompt_tokens: 9,
completion_tokens: 4,
total_tokens: 13,
})
);
assert_eq!(
request_json["model"],
serde_json::json!("deepseek-v3-2-251201")
);
assert_eq!(request_json["stream"], serde_json::json!(false));
assert_eq!(
request_json["tools"],
serde_json::json!([{ "type": "web_search", "max_keyword": 3 }])
);
assert_eq!(
request_json["input"][0]["content"][0],
serde_json::json!({ "type": "input_text", "text": "系统" })
);
}
#[tokio::test]
async fn stream_text_accumulates_sse_response() {
let server_url = spawn_mock_server(vec![MockResponse {
@@ -1314,6 +1682,41 @@ mod tests {
assert_eq!(response.response_id.as_deref(), Some("req_stream_01"));
}
#[tokio::test]
async fn stream_text_accumulates_responses_sse_response() {
let server_url = spawn_mock_server(vec![MockResponse {
status_line: "200 OK",
content_type: "text/event-stream; charset=utf-8",
body: concat!(
"data: {\"type\":\"response.output_text.delta\",\"delta\":\"\"}\n\n",
"data: {\"type\":\"response.output_text.delta\",\"delta\":\"\"}\n\n",
"data: {\"type\":\"response.completed\"}\n\n"
)
.to_string(),
extra_headers: vec![("x-request-id", "req_responses_stream_01")],
}]);
let client = build_test_client(server_url, 0);
let mut updates = Vec::new();
let response = client
.stream_text(
LlmTextRequest::single_turn("系统", "用户").with_responses_api(),
|delta| {
updates.push(delta.accumulated_text.clone());
},
)
.await
.expect("responses stream_text should succeed");
assert_eq!(updates, vec!["".to_string(), "你好".to_string()]);
assert_eq!(response.content, "你好");
assert_eq!(response.finish_reason.as_deref(), Some("completed"));
assert_eq!(
response.response_id.as_deref(),
Some("req_responses_stream_01")
);
}
#[tokio::test]
async fn request_text_writes_raw_failure_logs_after_parse_error() {
let log_dir = std::env::temp_dir().join(format!(

View File

@@ -6,6 +6,7 @@ pub struct BigFishWorkSummaryResponse {
pub work_id: String,
pub source_session_id: String,
pub owner_user_id: String,
pub author_display_name: String,
pub title: String,
pub subtitle: String,
pub summary: String,

View File

@@ -6,6 +6,10 @@ 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>,
@@ -33,11 +37,32 @@ pub struct ExecutePuzzleAgentActionRequest {
#[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)]
@@ -88,6 +113,8 @@ pub struct PuzzleCreatorIntentResponse {
#[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>,
@@ -103,6 +130,25 @@ pub struct PuzzleResultDraftResponse {
#[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,
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)]
@@ -153,6 +199,7 @@ pub struct PuzzleResultPreviewEnvelopeResponse {
#[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,

View File

@@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize};
#[serde(rename_all = "camelCase")]
pub struct StartPuzzleRunRequest {
pub profile_id: String,
#[serde(default)]
pub level_id: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]

View File

@@ -1,10 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::puzzle_agent::PuzzleAnchorPackResponse;
use crate::puzzle_agent::{PuzzleAnchorPackResponse, PuzzleDraftLevelResponse};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PutPuzzleWorkRequest {
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
@@ -12,6 +14,8 @@ pub struct PutPuzzleWorkRequest {
pub cover_image_src: Option<String>,
#[serde(default)]
pub cover_asset_id: Option<String>,
#[serde(default)]
pub levels: Vec<PuzzleDraftLevelResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -23,6 +27,8 @@ pub struct PuzzleWorkSummaryResponse {
#[serde(default)]
pub source_session_id: Option<String>,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
@@ -50,6 +56,7 @@ pub struct PuzzleWorkProfileResponse {
#[serde(flatten)]
pub summary: PuzzleWorkSummaryResponse,
pub anchor_pack: PuzzleAnchorPackResponse,
pub levels: Vec<PuzzleDraftLevelResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]

View File

@@ -1,6 +1,7 @@
use super::*;
use crate::mapper::*;
use crate::module_bindings::delete_big_fish_work_procedure::delete_big_fish_work;
use crate::module_bindings::record_big_fish_like_procedure::record_big_fish_like;
use crate::module_bindings::record_big_fish_play_procedure::record_big_fish_play;
use crate::module_bindings::remix_big_fish_work_procedure::remix_big_fish_work;
use module_big_fish::PUBLIC_BIG_FISH_GALLERY_OWNER_USER_ID;
@@ -294,6 +295,29 @@ impl SpacetimeClient {
.await
}
pub async fn record_big_fish_like(
&self,
input: BigFishLikeReportRecordInput,
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
let procedure_input = BigFishWorkLikeRecordInput {
session_id: input.session_id,
user_id: input.user_id,
liked_at_micros: input.liked_at_micros,
};
self.call_after_connect(move |connection, sender| {
connection
.procedures()
.record_big_fish_like_then(procedure_input, move |_, result| {
let mapped = result
.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
.and_then(|result| map_big_fish_works_procedure_result(result, None));
send_once(&sender, mapped);
});
})
.await
}
pub async fn remix_big_fish_work(
&self,
input: BigFishWorkRemixRecordInput,

View File

@@ -1,6 +1,7 @@
use super::*;
use crate::mapper::*;
use crate::module_bindings::delete_custom_world_agent_session_procedure::delete_custom_world_agent_session;
use crate::module_bindings::record_custom_world_profile_like_procedure::record_custom_world_profile_like;
use crate::module_bindings::record_custom_world_profile_play_procedure::record_custom_world_profile_play;
use crate::module_bindings::remix_custom_world_profile_procedure::remix_custom_world_profile;
@@ -261,6 +262,30 @@ impl SpacetimeClient {
.await
}
pub async fn record_custom_world_profile_like(
&self,
input: CustomWorldProfileLikeReportRecordInput,
) -> Result<CustomWorldLibraryMutationRecord, SpacetimeClientError> {
let procedure_input = CustomWorldProfileLikeRecordInput {
owner_user_id: input.owner_user_id,
profile_id: input.profile_id,
user_id: input.user_id,
liked_at_micros: input.liked_at_micros,
};
self.call_after_connect(move |connection, sender| {
connection
.procedures()
.record_custom_world_profile_like_then(procedure_input, move |_, result| {
let mapped = result
.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
.and_then(map_custom_world_library_mutation_result);
send_once(&sender, mapped);
});
})
.await
}
pub async fn publish_custom_world_world(
&self,
input: CustomWorldPublishWorldRecordInput,

View File

@@ -3,42 +3,44 @@
pub mod module_bindings;
mod mapper;
pub(crate) use mapper::*;
use mapper::*;
pub use mapper::{
AiResultReferenceRecord, AiTaskMutationRecord, AiTaskRecord, AiTaskStageRecord,
AiTextChunkRecord, BattleStateRecord, BigFishAgentMessageRecord, BigFishAnchorItemRecord,
BigFishAnchorPackRecord, BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput,
BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord, BigFishDraftCompileRecordInput,
BigFishGameDraftRecord, BigFishLevelBlueprintRecord, BigFishMessageFinalizeRecordInput,
BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput, BigFishRuntimeParamsRecord,
BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishWorkRemixRecordInput,
BigFishWorkSummaryRecord, CustomWorldAgentActionExecuteRecord,
CustomWorldAgentActionExecuteRecordInput, CustomWorldAgentCheckpointRecord,
CustomWorldAgentMessageFinalizeRecordInput, CustomWorldAgentMessageRecord,
CustomWorldAgentMessageSubmitRecordInput, CustomWorldAgentOperationProgressRecordInput,
CustomWorldAgentOperationRecord, CustomWorldAgentSessionCreateRecordInput,
CustomWorldAgentSessionRecord, CustomWorldCheckpointRecord, CustomWorldDraftCardDetailRecord,
BigFishGameDraftRecord, BigFishLevelBlueprintRecord, BigFishLikeReportRecordInput,
BigFishMessageFinalizeRecordInput, BigFishMessageSubmitRecordInput,
BigFishPlayReportRecordInput, BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput,
BigFishSessionRecord, BigFishWorkRemixRecordInput, BigFishWorkSummaryRecord,
CustomWorldAgentActionExecuteRecord, CustomWorldAgentActionExecuteRecordInput,
CustomWorldAgentCheckpointRecord, CustomWorldAgentMessageFinalizeRecordInput,
CustomWorldAgentMessageRecord, CustomWorldAgentMessageSubmitRecordInput,
CustomWorldAgentOperationProgressRecordInput, CustomWorldAgentOperationRecord,
CustomWorldAgentSessionCreateRecordInput, CustomWorldAgentSessionRecord,
CustomWorldCheckpointRecord, CustomWorldDraftCardDetailRecord,
CustomWorldDraftCardDetailSectionRecord, CustomWorldDraftCardRecord,
CustomWorldGalleryEntryRecord, CustomWorldLibraryEntryRecord, CustomWorldLibraryMutationRecord,
CustomWorldProfilePlayReportRecordInput, CustomWorldProfileRemixRecordInput,
CustomWorldProfileUpsertRecordInput, CustomWorldPublishGateRecord,
CustomWorldPublishWorldRecord, CustomWorldPublishWorldRecordInput,
CustomWorldPublishedProfileCompileRecord, CustomWorldResultPreviewBlockerRecord,
CustomWorldSupportedActionRecord, CustomWorldWorkSummaryRecord, NpcBattleInteractionRecord,
NpcInteractionRecord, NpcStateRecord, PuzzleAgentMessageFinalizeRecordInput,
PuzzleAgentMessageRecord, PuzzleAgentMessageSubmitRecordInput,
PuzzleAgentSessionCreateRecordInput, PuzzleAgentSessionRecord,
PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAnchorPackRecord,
PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleCreatorIntentRecord,
CustomWorldProfileLikeReportRecordInput, CustomWorldProfilePlayReportRecordInput,
CustomWorldProfileRemixRecordInput, CustomWorldProfileUpsertRecordInput,
CustomWorldPublishGateRecord, CustomWorldPublishWorldRecord,
CustomWorldPublishWorldRecordInput, CustomWorldPublishedProfileCompileRecord,
CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord,
CustomWorldWorkSummaryRecord, NpcBattleInteractionRecord, NpcInteractionRecord, NpcStateRecord,
PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord,
PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
PuzzleAnchorPackRecord, PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleCreatorIntentRecord,
PuzzleDraftLevelRecord, PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput,
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
PuzzleLeaderboardEntryRecord, PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord,
PuzzlePieceStateRecord, PuzzlePublishRecordInput, PuzzleResultDraftRecord,
PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord, PuzzleResultPreviewRecord,
PuzzleRunDragRecordInput, PuzzleRunNextLevelRecordInput, PuzzleRunPauseRecordInput,
PuzzleRunPropRecordInput, PuzzleRunRecord, PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput,
PuzzleRuntimeLevelRecord, PuzzleSelectCoverImageRecordInput, PuzzleWorkProfileRecord,
PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput, ResolveCombatActionRecord,
ResolveNpcBattleInteractionInput,
PuzzleRuntimeLevelRecord, PuzzleSelectCoverImageRecordInput, PuzzleWorkLikeReportRecordInput,
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
ResolveCombatActionRecord, ResolveNpcBattleInteractionInput,
};
pub mod ai;
@@ -108,7 +110,7 @@ use module_puzzle::{
PuzzleAnchorItem as DomainPuzzleAnchorItem, PuzzleAnchorPack as DomainPuzzleAnchorPack,
PuzzleBoardSnapshot as DomainPuzzleBoardSnapshot,
PuzzleCellPosition as DomainPuzzleCellPosition,
PuzzleCreatorIntent as DomainPuzzleCreatorIntent,
PuzzleCreatorIntent as DomainPuzzleCreatorIntent, PuzzleDraftLevel as DomainPuzzleDraftLevel,
PuzzleGeneratedImageCandidate as DomainPuzzleGeneratedImageCandidate,
PuzzleMergedGroupState as DomainPuzzleMergedGroupState,
PuzzlePieceState as DomainPuzzlePieceState, PuzzleResultDraft as DomainPuzzleResultDraft,

View File

@@ -1917,7 +1917,7 @@ pub(crate) fn map_custom_world_gallery_entry_snapshot(
play_count: snapshot.play_count,
remix_count: snapshot.remix_count,
like_count: snapshot.like_count,
recent_play_count_7d: snapshot.recent_play_count_7d,
recent_play_count_7d: snapshot.recent_play_count_7_d,
})
}
@@ -2223,6 +2223,7 @@ pub(crate) fn map_puzzle_agent_session_snapshot(
) -> PuzzleAgentSessionRecord {
PuzzleAgentSessionRecord {
session_id: snapshot.session_id,
seed_text: snapshot.seed_text,
current_turn: snapshot.current_turn,
progress_percent: snapshot.progress_percent,
stage: snapshot.stage.as_str().to_string(),
@@ -2268,6 +2269,8 @@ pub(crate) fn map_puzzle_result_draft(
snapshot: DomainPuzzleResultDraft,
) -> PuzzleResultDraftRecord {
PuzzleResultDraftRecord {
work_title: snapshot.work_title,
work_description: snapshot.work_description,
level_name: snapshot.level_name,
summary: snapshot.summary,
theme_tags: snapshot.theme_tags,
@@ -2283,6 +2286,39 @@ pub(crate) fn map_puzzle_result_draft(
cover_image_src: snapshot.cover_image_src,
cover_asset_id: snapshot.cover_asset_id,
generation_status: snapshot.generation_status,
levels: snapshot
.levels
.into_iter()
.map(map_puzzle_draft_level)
.collect(),
form_draft: snapshot.form_draft.map(map_puzzle_form_draft),
}
}
pub(crate) fn map_puzzle_form_draft(
snapshot: module_puzzle::PuzzleFormDraft,
) -> PuzzleFormDraftRecord {
PuzzleFormDraftRecord {
work_title: snapshot.work_title,
work_description: snapshot.work_description,
picture_description: snapshot.picture_description,
}
}
pub(crate) fn map_puzzle_draft_level(snapshot: DomainPuzzleDraftLevel) -> PuzzleDraftLevelRecord {
PuzzleDraftLevelRecord {
level_id: snapshot.level_id,
level_name: snapshot.level_name,
picture_description: snapshot.picture_description,
candidates: snapshot
.candidates
.into_iter()
.map(map_puzzle_generated_image_candidate)
.collect(),
selected_candidate_id: snapshot.selected_candidate_id,
cover_image_src: snapshot.cover_image_src,
cover_asset_id: snapshot.cover_asset_id,
generation_status: snapshot.generation_status,
}
}
@@ -2386,6 +2422,8 @@ pub(crate) fn map_puzzle_work_profile(
owner_user_id: snapshot.owner_user_id,
source_session_id: snapshot.source_session_id,
author_display_name: snapshot.author_display_name,
work_title: snapshot.work_title,
work_description: snapshot.work_description,
level_name: snapshot.level_name,
summary: snapshot.summary,
theme_tags: snapshot.theme_tags,
@@ -2400,6 +2438,11 @@ pub(crate) fn map_puzzle_work_profile(
recent_play_count_7d: snapshot.recent_play_count_7d,
publish_ready: snapshot.publish_ready,
anchor_pack: map_puzzle_anchor_pack(snapshot.anchor_pack),
levels: snapshot
.levels
.into_iter()
.map(map_puzzle_draft_level)
.collect(),
}
}
@@ -4215,6 +4258,14 @@ pub struct CustomWorldProfilePlayReportRecordInput {
pub played_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldProfileLikeReportRecordInput {
pub owner_user_id: String,
pub profile_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CustomWorldPublishWorldRecordInput {
pub session_id: String,
@@ -4314,6 +4365,14 @@ pub struct PuzzleAgentSessionCreateRecordInput {
pub created_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleFormDraftSaveRecordInput {
pub session_id: String,
pub owner_user_id: String,
pub seed_text: String,
pub saved_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleAgentMessageSubmitRecordInput {
pub session_id: String,
@@ -4340,6 +4399,7 @@ pub struct PuzzleAgentMessageFinalizeRecordInput {
pub struct PuzzleGeneratedImagesSaveRecordInput {
pub session_id: String,
pub owner_user_id: String,
pub level_id: Option<String>,
pub candidates_json: String,
pub saved_at_micros: i64,
}
@@ -4348,6 +4408,7 @@ pub struct PuzzleGeneratedImagesSaveRecordInput {
pub struct PuzzleSelectCoverImageRecordInput {
pub session_id: String,
pub owner_user_id: String,
pub level_id: Option<String>,
pub candidate_id: String,
pub selected_at_micros: i64,
}
@@ -4359,9 +4420,12 @@ pub struct PuzzlePublishRecordInput {
pub work_id: String,
pub profile_id: String,
pub author_display_name: String,
pub work_title: Option<String>,
pub work_description: Option<String>,
pub level_name: Option<String>,
pub summary: Option<String>,
pub theme_tags: Option<Vec<String>>,
pub levels_json: Option<String>,
pub published_at_micros: i64,
}
@@ -4369,11 +4433,14 @@ pub struct PuzzlePublishRecordInput {
pub struct PuzzleWorkUpsertRecordInput {
pub profile_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub levels_json: Option<String>,
pub updated_at_micros: i64,
}
@@ -4389,11 +4456,19 @@ pub struct PuzzleWorkRemixRecordInput {
pub remixed_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleWorkLikeReportRecordInput {
pub profile_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleRunStartRecordInput {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub level_id: Option<String>,
pub started_at_micros: i64,
}
@@ -4447,6 +4522,13 @@ pub struct BigFishPlayReportRecordInput {
pub reported_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BigFishLikeReportRecordInput {
pub session_id: String,
pub user_id: String,
pub liked_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BigFishWorkRemixRecordInput {
pub source_session_id: String,
@@ -4498,6 +4580,8 @@ pub struct PuzzleGeneratedImageCandidateRecord {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleResultDraftRecord {
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
@@ -4509,6 +4593,27 @@ pub struct PuzzleResultDraftRecord {
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub generation_status: String,
pub levels: Vec<PuzzleDraftLevelRecord>,
pub form_draft: Option<PuzzleFormDraftRecord>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleFormDraftRecord {
pub work_title: Option<String>,
pub work_description: Option<String>,
pub picture_description: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleDraftLevelRecord {
pub level_id: String,
pub level_name: String,
pub picture_description: String,
pub candidates: Vec<PuzzleGeneratedImageCandidateRecord>,
pub selected_candidate_id: Option<String>,
pub cover_image_src: Option<String>,
pub cover_asset_id: Option<String>,
pub generation_status: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -4553,6 +4658,7 @@ pub struct PuzzleResultPreviewRecord {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PuzzleAgentSessionRecord {
pub session_id: String,
pub seed_text: String,
pub current_turn: u32,
pub progress_percent: u32,
pub stage: String,
@@ -4573,6 +4679,8 @@ pub struct PuzzleWorkProfileRecord {
pub owner_user_id: String,
pub source_session_id: Option<String>,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub level_name: String,
pub summary: String,
pub theme_tags: Vec<String>,
@@ -4587,6 +4695,7 @@ pub struct PuzzleWorkProfileRecord {
pub recent_play_count_7d: u32,
pub publish_ready: bool,
pub anchor_pack: PuzzleAnchorPackRecord,
pub levels: Vec<PuzzleDraftLevelRecord>,
}
#[derive(Clone, Debug, PartialEq, Eq)]

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::quest_record_input_type::QuestRecordInput;
@@ -14,8 +19,10 @@ pub(super) struct AcceptQuestArgs {
impl From<AcceptQuestArgs> for super::Reducer {
fn from(args: AcceptQuestArgs) -> Self {
Self::AcceptQuest { input: args.input }
}
Self::AcceptQuest {
input: args.input,
}
}
}
impl __sdk::InModule for AcceptQuestArgs {
@@ -33,8 +40,9 @@ pub trait accept_quest {
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`accept_quest:accept_quest_then`] to run a callback after the reducer completes.
fn accept_quest(&self, input: QuestRecordInput) -> __sdk::Result<()> {
self.accept_quest_then(input, |_, _| {})
fn accept_quest(&self, input: QuestRecordInput,
) -> __sdk::Result<()> {
self.accept_quest_then(input, |_, _| {})
}
/// Request that the remote module invoke the reducer `accept_quest` to run as soon as possible,
@@ -62,7 +70,7 @@ impl accept_quest for super::RemoteReducers {
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp
.invoke_reducer_with_callback(AcceptQuestArgs { input }, callback)
self.imp.invoke_reducer_with_callback(AcceptQuestArgs { input, }, callback)
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::quest_completion_ack_input_type::QuestCompletionAckInput;
@@ -14,8 +19,10 @@ pub(super) struct AcknowledgeQuestCompletionArgs {
impl From<AcknowledgeQuestCompletionArgs> for super::Reducer {
fn from(args: AcknowledgeQuestCompletionArgs) -> Self {
Self::AcknowledgeQuestCompletion { input: args.input }
}
Self::AcknowledgeQuestCompletion {
input: args.input,
}
}
}
impl __sdk::InModule for AcknowledgeQuestCompletionArgs {
@@ -33,8 +40,9 @@ pub trait acknowledge_quest_completion {
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`acknowledge_quest_completion:acknowledge_quest_completion_then`] to run a callback after the reducer completes.
fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput) -> __sdk::Result<()> {
self.acknowledge_quest_completion_then(input, |_, _| {})
fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput,
) -> __sdk::Result<()> {
self.acknowledge_quest_completion_then(input, |_, _| {})
}
/// Request that the remote module invoke the reducer `acknowledge_quest_completion` to run as soon as possible,
@@ -62,7 +70,7 @@ impl acknowledge_quest_completion for super::RemoteReducers {
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp
.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input }, callback)
self.imp.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input, }, callback)
}
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::runtime_profile_redeem_code_admin_disable_input_type::RuntimeProfileRedeemCodeAdminDisableInput;
use super::runtime_profile_redeem_code_admin_procedure_result_type::RuntimeProfileRedeemCodeAdminProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AdminDisableProfileRedeemCodeArgs {
struct AdminDisableProfileRedeemCodeArgs {
pub input: RuntimeProfileRedeemCodeAdminDisableInput,
}
impl __sdk::InModule for AdminDisableProfileRedeemCodeArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for AdminDisableProfileRedeemCodeArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait admin_disable_profile_redeem_code {
fn admin_disable_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminDisableInput) {
self.admin_disable_profile_redeem_code_then(input, |_, _| {});
fn admin_disable_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminDisableInput,
) {
self.admin_disable_profile_redeem_code_then(input, |_, _| {});
}
fn admin_disable_profile_redeem_code_then(
&self,
input: RuntimeProfileRedeemCodeAdminDisableInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl admin_disable_profile_redeem_code for super::RemoteProcedures {
&self,
input: RuntimeProfileRedeemCodeAdminDisableInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>(
"admin_disable_profile_redeem_code",
AdminDisableProfileRedeemCodeArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>(
"admin_disable_profile_redeem_code",
AdminDisableProfileRedeemCodeArgs { input, },
__callback,
);
}
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::runtime_profile_redeem_code_admin_procedure_result_type::RuntimeProfileRedeemCodeAdminProcedureResult;
use super::runtime_profile_redeem_code_admin_upsert_input_type::RuntimeProfileRedeemCodeAdminUpsertInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AdminUpsertProfileRedeemCodeArgs {
struct AdminUpsertProfileRedeemCodeArgs {
pub input: RuntimeProfileRedeemCodeAdminUpsertInput,
}
impl __sdk::InModule for AdminUpsertProfileRedeemCodeArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for AdminUpsertProfileRedeemCodeArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait admin_upsert_profile_redeem_code {
fn admin_upsert_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminUpsertInput) {
self.admin_upsert_profile_redeem_code_then(input, |_, _| {});
fn admin_upsert_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminUpsertInput,
) {
self.admin_upsert_profile_redeem_code_then(input, |_, _| {});
}
fn admin_upsert_profile_redeem_code_then(
&self,
input: RuntimeProfileRedeemCodeAdminUpsertInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl admin_upsert_profile_redeem_code for super::RemoteProcedures {
&self,
input: RuntimeProfileRedeemCodeAdminUpsertInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>(
"admin_upsert_profile_redeem_code",
AdminUpsertProfileRedeemCodeArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>(
"admin_upsert_profile_redeem_code",
AdminUpsertProfileRedeemCodeArgs { input, },
__callback,
);
}
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AdvancePuzzleNextLevelArgs {
struct AdvancePuzzleNextLevelArgs {
pub input: PuzzleRunNextLevelInput,
}
impl __sdk::InModule for AdvancePuzzleNextLevelArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for AdvancePuzzleNextLevelArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait advance_puzzle_next_level {
fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput) {
self.advance_puzzle_next_level_then(input, |_, _| {});
fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput,
) {
self.advance_puzzle_next_level_then(input, |_, _| {});
}
fn advance_puzzle_next_level_then(
&self,
input: PuzzleRunNextLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<PuzzleRunProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl advance_puzzle_next_level for super::RemoteProcedures {
&self,
input: PuzzleRunNextLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<PuzzleRunProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>(
"advance_puzzle_next_level",
AdvancePuzzleNextLevelArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>(
"advance_puzzle_next_level",
AdvancePuzzleNextLevelArgs { input, },
__callback,
);
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_result_reference_kind_type::AiResultReferenceKind;
@@ -12,10 +17,12 @@ pub struct AiResultReferenceInput {
pub task_id: String,
pub reference_kind: AiResultReferenceKind,
pub reference_id: String,
pub label: Option<String>,
pub label: Option::<String>,
pub created_at_micros: i64,
}
impl __sdk::InModule for AiResultReferenceInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -19,8 +24,12 @@ pub enum AiResultReferenceKind {
RuntimeItemRecord,
AssetObject,
}
impl __sdk::InModule for AiResultReferenceKind {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_result_reference_kind_type::AiResultReferenceKind;
@@ -13,10 +18,12 @@ pub struct AiResultReferenceSnapshot {
pub task_id: String,
pub reference_kind: AiResultReferenceKind,
pub reference_id: String,
pub label: Option<String>,
pub label: Option::<String>,
pub created_at_micros: i64,
}
impl __sdk::InModule for AiResultReferenceSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_result_reference_kind_type::AiResultReferenceKind;
@@ -14,14 +19,16 @@ pub struct AiResultReference {
pub task_id: String,
pub reference_kind: AiResultReferenceKind,
pub reference_id: String,
pub label: Option<String>,
pub label: Option::<String>,
pub created_at: __sdk::Timestamp,
}
impl __sdk::InModule for AiResultReference {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AiResultReference`.
///
/// Provides typed access to columns for query building.
@@ -31,7 +38,7 @@ pub struct AiResultReferenceCols {
pub task_id: __sdk::__query_builder::Col<AiResultReference, String>,
pub reference_kind: __sdk::__query_builder::Col<AiResultReference, AiResultReferenceKind>,
pub reference_id: __sdk::__query_builder::Col<AiResultReference, String>,
pub label: __sdk::__query_builder::Col<AiResultReference, Option<String>>,
pub label: __sdk::__query_builder::Col<AiResultReference, Option::<String>>,
pub created_at: __sdk::__query_builder::Col<AiResultReference, __sdk::Timestamp>,
}
@@ -39,16 +46,14 @@ impl __sdk::__query_builder::HasCols for AiResultReference {
type Cols = AiResultReferenceCols;
fn cols(table_name: &'static str) -> Self::Cols {
AiResultReferenceCols {
result_reference_row_id: __sdk::__query_builder::Col::new(
table_name,
"result_reference_row_id",
),
result_reference_row_id: __sdk::__query_builder::Col::new(table_name, "result_reference_row_id"),
result_ref_id: __sdk::__query_builder::Col::new(table_name, "result_ref_id"),
task_id: __sdk::__query_builder::Col::new(table_name, "task_id"),
reference_kind: __sdk::__query_builder::Col::new(table_name, "reference_kind"),
reference_id: __sdk::__query_builder::Col::new(table_name, "reference_id"),
label: __sdk::__query_builder::Col::new(table_name, "label"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
}
}
}
@@ -65,13 +70,12 @@ impl __sdk::__query_builder::HasIxCols for AiResultReference {
type IxCols = AiResultReferenceIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
AiResultReferenceIxCols {
result_reference_row_id: __sdk::__query_builder::IxCol::new(
table_name,
"result_reference_row_id",
),
result_reference_row_id: __sdk::__query_builder::IxCol::new(table_name, "result_reference_row_id"),
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AiResultReference {}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -11,12 +16,14 @@ use super::ai_task_stage_kind_type::AiTaskStageKind;
pub struct AiStageCompletionInput {
pub task_id: String,
pub stage_kind: AiTaskStageKind,
pub text_output: Option<String>,
pub structured_payload_json: Option<String>,
pub warning_messages: Vec<String>,
pub text_output: Option::<String>,
pub structured_payload_json: Option::<String>,
pub warning_messages: Vec::<String>,
pub completed_at_micros: i64,
}
impl __sdk::InModule for AiStageCompletionInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,6 +17,8 @@ pub struct AiTaskCancelInput {
pub completed_at_micros: i64,
}
impl __sdk::InModule for AiTaskCancelInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_kind_type::AiTaskKind;
use super::ai_task_stage_blueprint_type::AiTaskStageBlueprint;
@@ -15,12 +20,14 @@ pub struct AiTaskCreateInput {
pub owner_user_id: String,
pub request_label: String,
pub source_module: String,
pub source_entity_id: Option<String>,
pub request_payload_json: Option<String>,
pub stages: Vec<AiTaskStageBlueprint>,
pub source_entity_id: Option::<String>,
pub request_payload_json: Option::<String>,
pub stages: Vec::<AiTaskStageBlueprint>,
pub created_at_micros: i64,
}
impl __sdk::InModule for AiTaskCreateInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -12,6 +18,8 @@ pub struct AiTaskFailureInput {
pub completed_at_micros: i64,
}
impl __sdk::InModule for AiTaskFailureInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,6 +17,8 @@ pub struct AiTaskFinishInput {
pub completed_at_micros: i64,
}
impl __sdk::InModule for AiTaskFinishInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -19,8 +24,12 @@ pub enum AiTaskKind {
QuestIntent,
RuntimeItemIntent,
}
impl __sdk::InModule for AiTaskKind {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_snapshot_type::AiTaskSnapshot;
use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot;
@@ -11,11 +16,13 @@ use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot;
#[sats(crate = __lib)]
pub struct AiTaskProcedureResult {
pub ok: bool,
pub task: Option<AiTaskSnapshot>,
pub text_chunk: Option<AiTextChunkSnapshot>,
pub error_message: Option<String>,
pub task: Option::<AiTaskSnapshot>,
pub text_chunk: Option::<AiTextChunkSnapshot>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AiTaskProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,12 +2,17 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_result_reference_snapshot_type::AiResultReferenceSnapshot;
use super::ai_task_kind_type::AiTaskKind;
use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot;
use super::ai_task_status_type::AiTaskStatus;
use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot;
use super::ai_result_reference_snapshot_type::AiResultReferenceSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -17,21 +22,23 @@ pub struct AiTaskSnapshot {
pub owner_user_id: String,
pub request_label: String,
pub source_module: String,
pub source_entity_id: Option<String>,
pub request_payload_json: Option<String>,
pub source_entity_id: Option::<String>,
pub request_payload_json: Option::<String>,
pub status: AiTaskStatus,
pub failure_message: Option<String>,
pub stages: Vec<AiTaskStageSnapshot>,
pub result_references: Vec<AiResultReferenceSnapshot>,
pub latest_text_output: Option<String>,
pub latest_structured_payload_json: Option<String>,
pub failure_message: Option::<String>,
pub stages: Vec::<AiTaskStageSnapshot>,
pub result_references: Vec::<AiResultReferenceSnapshot>,
pub latest_text_output: Option::<String>,
pub latest_structured_payload_json: Option::<String>,
pub version: u32,
pub created_at_micros: i64,
pub started_at_micros: Option<i64>,
pub completed_at_micros: Option<i64>,
pub started_at_micros: Option::<i64>,
pub completed_at_micros: Option::<i64>,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AiTaskSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -15,6 +20,8 @@ pub struct AiTaskStageBlueprint {
pub order: u32,
}
impl __sdk::InModule for AiTaskStageBlueprint {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -17,8 +22,12 @@ pub enum AiTaskStageKind {
NormalizeResult,
PersistResult,
}
impl __sdk::InModule for AiTaskStageKind {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
use super::ai_task_stage_status_type::AiTaskStageStatus;
@@ -15,13 +20,15 @@ pub struct AiTaskStageSnapshot {
pub detail: String,
pub order: u32,
pub status: AiTaskStageStatus,
pub text_output: Option<String>,
pub structured_payload_json: Option<String>,
pub warning_messages: Vec<String>,
pub started_at_micros: Option<i64>,
pub completed_at_micros: Option<i64>,
pub text_output: Option::<String>,
pub structured_payload_json: Option::<String>,
pub warning_messages: Vec::<String>,
pub started_at_micros: Option::<i64>,
pub completed_at_micros: Option::<i64>,
}
impl __sdk::InModule for AiTaskStageSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -14,6 +19,8 @@ pub struct AiTaskStageStartInput {
pub started_at_micros: i64,
}
impl __sdk::InModule for AiTaskStageStartInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -15,8 +20,12 @@ pub enum AiTaskStageStatus {
Completed,
Skipped,
}
impl __sdk::InModule for AiTaskStageStatus {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
use super::ai_task_stage_status_type::AiTaskStageStatus;
@@ -17,17 +22,19 @@ pub struct AiTaskStage {
pub detail: String,
pub stage_order: u32,
pub status: AiTaskStageStatus,
pub text_output: Option<String>,
pub structured_payload_json: Option<String>,
pub warning_messages: Vec<String>,
pub started_at: Option<__sdk::Timestamp>,
pub completed_at: Option<__sdk::Timestamp>,
pub text_output: Option::<String>,
pub structured_payload_json: Option::<String>,
pub warning_messages: Vec::<String>,
pub started_at: Option::<__sdk::Timestamp>,
pub completed_at: Option::<__sdk::Timestamp>,
}
impl __sdk::InModule for AiTaskStage {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AiTaskStage`.
///
/// Provides typed access to columns for query building.
@@ -39,11 +46,11 @@ pub struct AiTaskStageCols {
pub detail: __sdk::__query_builder::Col<AiTaskStage, String>,
pub stage_order: __sdk::__query_builder::Col<AiTaskStage, u32>,
pub status: __sdk::__query_builder::Col<AiTaskStage, AiTaskStageStatus>,
pub text_output: __sdk::__query_builder::Col<AiTaskStage, Option<String>>,
pub structured_payload_json: __sdk::__query_builder::Col<AiTaskStage, Option<String>>,
pub warning_messages: __sdk::__query_builder::Col<AiTaskStage, Vec<String>>,
pub started_at: __sdk::__query_builder::Col<AiTaskStage, Option<__sdk::Timestamp>>,
pub completed_at: __sdk::__query_builder::Col<AiTaskStage, Option<__sdk::Timestamp>>,
pub text_output: __sdk::__query_builder::Col<AiTaskStage, Option::<String>>,
pub structured_payload_json: __sdk::__query_builder::Col<AiTaskStage, Option::<String>>,
pub warning_messages: __sdk::__query_builder::Col<AiTaskStage, Vec::<String>>,
pub started_at: __sdk::__query_builder::Col<AiTaskStage, Option::<__sdk::Timestamp>>,
pub completed_at: __sdk::__query_builder::Col<AiTaskStage, Option::<__sdk::Timestamp>>,
}
impl __sdk::__query_builder::HasCols for AiTaskStage {
@@ -58,13 +65,11 @@ impl __sdk::__query_builder::HasCols for AiTaskStage {
stage_order: __sdk::__query_builder::Col::new(table_name, "stage_order"),
status: __sdk::__query_builder::Col::new(table_name, "status"),
text_output: __sdk::__query_builder::Col::new(table_name, "text_output"),
structured_payload_json: __sdk::__query_builder::Col::new(
table_name,
"structured_payload_json",
),
structured_payload_json: __sdk::__query_builder::Col::new(table_name, "structured_payload_json"),
warning_messages: __sdk::__query_builder::Col::new(table_name, "warning_messages"),
started_at: __sdk::__query_builder::Col::new(table_name, "started_at"),
completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"),
}
}
}
@@ -83,8 +88,10 @@ impl __sdk::__query_builder::HasIxCols for AiTaskStage {
AiTaskStageIxCols {
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
task_stage_id: __sdk::__query_builder::IxCol::new(table_name, "task_stage_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AiTaskStage {}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,6 +17,8 @@ pub struct AiTaskStartInput {
pub started_at_micros: i64,
}
impl __sdk::InModule for AiTaskStartInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -17,8 +22,12 @@ pub enum AiTaskStatus {
Failed,
Cancelled,
}
impl __sdk::InModule for AiTaskStatus {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_kind_type::AiTaskKind;
use super::ai_task_status_type::AiTaskStatus;
@@ -15,23 +20,25 @@ pub struct AiTask {
pub owner_user_id: String,
pub request_label: String,
pub source_module: String,
pub source_entity_id: Option<String>,
pub request_payload_json: Option<String>,
pub source_entity_id: Option::<String>,
pub request_payload_json: Option::<String>,
pub status: AiTaskStatus,
pub failure_message: Option<String>,
pub latest_text_output: Option<String>,
pub latest_structured_payload_json: Option<String>,
pub failure_message: Option::<String>,
pub latest_text_output: Option::<String>,
pub latest_structured_payload_json: Option::<String>,
pub version: u32,
pub created_at: __sdk::Timestamp,
pub started_at: Option<__sdk::Timestamp>,
pub completed_at: Option<__sdk::Timestamp>,
pub started_at: Option::<__sdk::Timestamp>,
pub completed_at: Option::<__sdk::Timestamp>,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for AiTask {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AiTask`.
///
/// Provides typed access to columns for query building.
@@ -41,16 +48,16 @@ pub struct AiTaskCols {
pub owner_user_id: __sdk::__query_builder::Col<AiTask, String>,
pub request_label: __sdk::__query_builder::Col<AiTask, String>,
pub source_module: __sdk::__query_builder::Col<AiTask, String>,
pub source_entity_id: __sdk::__query_builder::Col<AiTask, Option<String>>,
pub request_payload_json: __sdk::__query_builder::Col<AiTask, Option<String>>,
pub source_entity_id: __sdk::__query_builder::Col<AiTask, Option::<String>>,
pub request_payload_json: __sdk::__query_builder::Col<AiTask, Option::<String>>,
pub status: __sdk::__query_builder::Col<AiTask, AiTaskStatus>,
pub failure_message: __sdk::__query_builder::Col<AiTask, Option<String>>,
pub latest_text_output: __sdk::__query_builder::Col<AiTask, Option<String>>,
pub latest_structured_payload_json: __sdk::__query_builder::Col<AiTask, Option<String>>,
pub failure_message: __sdk::__query_builder::Col<AiTask, Option::<String>>,
pub latest_text_output: __sdk::__query_builder::Col<AiTask, Option::<String>>,
pub latest_structured_payload_json: __sdk::__query_builder::Col<AiTask, Option::<String>>,
pub version: __sdk::__query_builder::Col<AiTask, u32>,
pub created_at: __sdk::__query_builder::Col<AiTask, __sdk::Timestamp>,
pub started_at: __sdk::__query_builder::Col<AiTask, Option<__sdk::Timestamp>>,
pub completed_at: __sdk::__query_builder::Col<AiTask, Option<__sdk::Timestamp>>,
pub started_at: __sdk::__query_builder::Col<AiTask, Option::<__sdk::Timestamp>>,
pub completed_at: __sdk::__query_builder::Col<AiTask, Option::<__sdk::Timestamp>>,
pub updated_at: __sdk::__query_builder::Col<AiTask, __sdk::Timestamp>,
}
@@ -64,22 +71,17 @@ impl __sdk::__query_builder::HasCols for AiTask {
request_label: __sdk::__query_builder::Col::new(table_name, "request_label"),
source_module: __sdk::__query_builder::Col::new(table_name, "source_module"),
source_entity_id: __sdk::__query_builder::Col::new(table_name, "source_entity_id"),
request_payload_json: __sdk::__query_builder::Col::new(
table_name,
"request_payload_json",
),
request_payload_json: __sdk::__query_builder::Col::new(table_name, "request_payload_json"),
status: __sdk::__query_builder::Col::new(table_name, "status"),
failure_message: __sdk::__query_builder::Col::new(table_name, "failure_message"),
latest_text_output: __sdk::__query_builder::Col::new(table_name, "latest_text_output"),
latest_structured_payload_json: __sdk::__query_builder::Col::new(
table_name,
"latest_structured_payload_json",
),
latest_structured_payload_json: __sdk::__query_builder::Col::new(table_name, "latest_structured_payload_json"),
version: __sdk::__query_builder::Col::new(table_name, "version"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
started_at: __sdk::__query_builder::Col::new(table_name, "started_at"),
completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
@@ -102,8 +104,10 @@ impl __sdk::__query_builder::HasIxCols for AiTask {
status: __sdk::__query_builder::IxCol::new(table_name, "status"),
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
task_kind: __sdk::__query_builder::IxCol::new(table_name, "task_kind"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AiTask {}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -16,6 +21,8 @@ pub struct AiTextChunkAppendInput {
pub created_at_micros: i64,
}
impl __sdk::InModule for AiTextChunkAppendInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -17,6 +22,8 @@ pub struct AiTextChunkSnapshot {
pub created_at_micros: i64,
}
impl __sdk::InModule for AiTextChunkSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_stage_kind_type::AiTaskStageKind;
@@ -18,10 +23,12 @@ pub struct AiTextChunk {
pub created_at: __sdk::Timestamp,
}
impl __sdk::InModule for AiTextChunk {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AiTextChunk`.
///
/// Provides typed access to columns for query building.
@@ -46,6 +53,7 @@ impl __sdk::__query_builder::HasCols for AiTextChunk {
sequence: __sdk::__query_builder::Col::new(table_name, "sequence"),
delta_text: __sdk::__query_builder::Col::new(table_name, "delta_text"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
}
}
}
@@ -64,8 +72,10 @@ impl __sdk::__query_builder::HasIxCols for AiTextChunk {
AiTextChunkIxCols {
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
text_chunk_row_id: __sdk::__query_builder::IxCol::new(table_name, "text_chunk_row_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AiTextChunk {}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
use super::ai_text_chunk_append_input_type::AiTextChunkAppendInput;
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AppendAiTextChunkAndReturnArgs {
struct AppendAiTextChunkAndReturnArgs {
pub input: AiTextChunkAppendInput,
}
impl __sdk::InModule for AppendAiTextChunkAndReturnArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for AppendAiTextChunkAndReturnArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait append_ai_text_chunk_and_return {
fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput) {
self.append_ai_text_chunk_and_return_then(input, |_, _| {});
fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput,
) {
self.append_ai_text_chunk_and_return_then(input, |_, _| {});
}
fn append_ai_text_chunk_and_return_then(
&self,
input: AiTextChunkAppendInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<AiTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl append_ai_text_chunk_and_return for super::RemoteProcedures {
&self,
input: AiTextChunkAppendInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<AiTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
"append_ai_text_chunk_and_return",
AppendAiTextChunkAndReturnArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
"append_ai_text_chunk_and_return",
AppendAiTextChunkAndReturnArgs { input, },
__callback,
);
}
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput;
use super::chapter_progression_procedure_result_type::ChapterProgressionProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct ApplyChapterProgressionLedgerEntryAndReturnArgs {
struct ApplyChapterProgressionLedgerEntryAndReturnArgs {
pub input: ChapterProgressionLedgerInput,
}
impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs {
type Module = super::RemoteModule;
}
@@ -22,22 +28,16 @@ impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait apply_chapter_progression_ledger_entry_and_return {
fn apply_chapter_progression_ledger_entry_and_return(
&self,
input: ChapterProgressionLedgerInput,
) {
self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {});
fn apply_chapter_progression_ledger_entry_and_return(&self, input: ChapterProgressionLedgerInput,
) {
self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {});
}
fn apply_chapter_progression_ledger_entry_and_return_then(
&self,
input: ChapterProgressionLedgerInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<ChapterProgressionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<ChapterProgressionProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -46,17 +46,13 @@ impl apply_chapter_progression_ledger_entry_and_return for super::RemoteProcedur
&self,
input: ChapterProgressionLedgerInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<ChapterProgressionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<ChapterProgressionProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>(
"apply_chapter_progression_ledger_entry_and_return",
ApplyChapterProgressionLedgerEntryAndReturnArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>(
"apply_chapter_progression_ledger_entry_and_return",
ApplyChapterProgressionLedgerEntryAndReturnArgs { input, },
__callback,
);
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput;
@@ -14,8 +19,10 @@ pub(super) struct ApplyChapterProgressionLedgerEntryArgs {
impl From<ApplyChapterProgressionLedgerEntryArgs> for super::Reducer {
fn from(args: ApplyChapterProgressionLedgerEntryArgs) -> Self {
Self::ApplyChapterProgressionLedgerEntry { input: args.input }
}
Self::ApplyChapterProgressionLedgerEntry {
input: args.input,
}
}
}
impl __sdk::InModule for ApplyChapterProgressionLedgerEntryArgs {
@@ -33,11 +40,9 @@ pub trait apply_chapter_progression_ledger_entry {
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`apply_chapter_progression_ledger_entry:apply_chapter_progression_ledger_entry_then`] to run a callback after the reducer completes.
fn apply_chapter_progression_ledger_entry(
&self,
input: ChapterProgressionLedgerInput,
) -> __sdk::Result<()> {
self.apply_chapter_progression_ledger_entry_then(input, |_, _| {})
fn apply_chapter_progression_ledger_entry(&self, input: ChapterProgressionLedgerInput,
) -> __sdk::Result<()> {
self.apply_chapter_progression_ledger_entry_then(input, |_, _| {})
}
/// Request that the remote module invoke the reducer `apply_chapter_progression_ledger_entry` to run as soon as possible,
@@ -65,9 +70,7 @@ impl apply_chapter_progression_ledger_entry for super::RemoteReducers {
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp.invoke_reducer_with_callback(
ApplyChapterProgressionLedgerEntryArgs { input },
callback,
)
self.imp.invoke_reducer_with_callback(ApplyChapterProgressionLedgerEntryArgs { input, }, callback)
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::inventory_mutation_input_type::InventoryMutationInput;
@@ -14,8 +19,10 @@ pub(super) struct ApplyInventoryMutationArgs {
impl From<ApplyInventoryMutationArgs> for super::Reducer {
fn from(args: ApplyInventoryMutationArgs) -> Self {
Self::ApplyInventoryMutation { input: args.input }
}
Self::ApplyInventoryMutation {
input: args.input,
}
}
}
impl __sdk::InModule for ApplyInventoryMutationArgs {
@@ -33,8 +40,9 @@ pub trait apply_inventory_mutation {
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`apply_inventory_mutation:apply_inventory_mutation_then`] to run a callback after the reducer completes.
fn apply_inventory_mutation(&self, input: InventoryMutationInput) -> __sdk::Result<()> {
self.apply_inventory_mutation_then(input, |_, _| {})
fn apply_inventory_mutation(&self, input: InventoryMutationInput,
) -> __sdk::Result<()> {
self.apply_inventory_mutation_then(input, |_, _| {})
}
/// Request that the remote module invoke the reducer `apply_inventory_mutation` to run as soon as possible,
@@ -62,7 +70,7 @@ impl apply_inventory_mutation for super::RemoteReducers {
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp
.invoke_reducer_with_callback(ApplyInventoryMutationArgs { input }, callback)
self.imp.invoke_reducer_with_callback(ApplyInventoryMutationArgs { input, }, callback)
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::quest_signal_apply_input_type::QuestSignalApplyInput;
@@ -14,8 +19,10 @@ pub(super) struct ApplyQuestSignalArgs {
impl From<ApplyQuestSignalArgs> for super::Reducer {
fn from(args: ApplyQuestSignalArgs) -> Self {
Self::ApplyQuestSignal { input: args.input }
}
Self::ApplyQuestSignal {
input: args.input,
}
}
}
impl __sdk::InModule for ApplyQuestSignalArgs {
@@ -33,8 +40,9 @@ pub trait apply_quest_signal {
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`apply_quest_signal:apply_quest_signal_then`] to run a callback after the reducer completes.
fn apply_quest_signal(&self, input: QuestSignalApplyInput) -> __sdk::Result<()> {
self.apply_quest_signal_then(input, |_, _| {})
fn apply_quest_signal(&self, input: QuestSignalApplyInput,
) -> __sdk::Result<()> {
self.apply_quest_signal_then(input, |_, _| {})
}
/// Request that the remote module invoke the reducer `apply_quest_signal` to run as soon as possible,
@@ -62,7 +70,7 @@ impl apply_quest_signal for super::RemoteReducers {
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp
.invoke_reducer_with_callback(ApplyQuestSignalArgs { input }, callback)
self.imp.invoke_reducer_with_callback(ApplyQuestSignalArgs { input, }, callback)
}
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -13,11 +19,13 @@ pub struct AssetEntityBindingInput {
pub entity_id: String,
pub slot: String,
pub asset_kind: String,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AssetEntityBindingInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot;
@@ -10,10 +15,12 @@ use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot;
#[sats(crate = __lib)]
pub struct AssetEntityBindingProcedureResult {
pub ok: bool,
pub record: Option<AssetEntityBindingSnapshot>,
pub error_message: Option<String>,
pub record: Option::<AssetEntityBindingSnapshot>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AssetEntityBindingProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -13,12 +19,14 @@ pub struct AssetEntityBindingSnapshot {
pub entity_id: String,
pub slot: String,
pub asset_kind: String,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub created_at_micros: i64,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AssetEntityBindingSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -13,16 +19,18 @@ pub struct AssetEntityBinding {
pub entity_id: String,
pub slot: String,
pub asset_kind: String,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub created_at: __sdk::Timestamp,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for AssetEntityBinding {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AssetEntityBinding`.
///
/// Provides typed access to columns for query building.
@@ -33,8 +41,8 @@ pub struct AssetEntityBindingCols {
pub entity_id: __sdk::__query_builder::Col<AssetEntityBinding, String>,
pub slot: __sdk::__query_builder::Col<AssetEntityBinding, String>,
pub asset_kind: __sdk::__query_builder::Col<AssetEntityBinding, String>,
pub owner_user_id: __sdk::__query_builder::Col<AssetEntityBinding, Option<String>>,
pub profile_id: __sdk::__query_builder::Col<AssetEntityBinding, Option<String>>,
pub owner_user_id: __sdk::__query_builder::Col<AssetEntityBinding, Option::<String>>,
pub profile_id: __sdk::__query_builder::Col<AssetEntityBinding, Option::<String>>,
pub created_at: __sdk::__query_builder::Col<AssetEntityBinding, __sdk::Timestamp>,
pub updated_at: __sdk::__query_builder::Col<AssetEntityBinding, __sdk::Timestamp>,
}
@@ -53,6 +61,7 @@ impl __sdk::__query_builder::HasCols for AssetEntityBinding {
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
@@ -71,8 +80,10 @@ impl __sdk::__query_builder::HasIxCols for AssetEntityBinding {
AssetEntityBindingIxCols {
asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"),
binding_id: __sdk::__query_builder::IxCol::new(table_name, "binding_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AssetEntityBinding {}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -10,13 +16,15 @@ pub struct AssetHistoryEntrySnapshot {
pub asset_object_id: String,
pub asset_kind: String,
pub image_src: String,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub entity_id: Option<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub entity_id: Option::<String>,
pub created_at_micros: i64,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AssetHistoryEntrySnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,6 +17,8 @@ pub struct AssetHistoryListInput {
pub limit: u32,
}
impl __sdk::InModule for AssetHistoryListInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_history_entry_snapshot_type::AssetHistoryEntrySnapshot;
@@ -10,10 +15,12 @@ use super::asset_history_entry_snapshot_type::AssetHistoryEntrySnapshot;
#[sats(crate = __lib)]
pub struct AssetHistoryListResult {
pub ok: bool,
pub entries: Vec<AssetHistoryEntrySnapshot>,
pub error_message: Option<String>,
pub entries: Vec::<AssetHistoryEntrySnapshot>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AssetHistoryListResult {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,8 +16,12 @@ pub enum AssetObjectAccessPolicy {
Private,
PublicRead,
}
impl __sdk::InModule for AssetObjectAccessPolicy {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot;
@@ -10,10 +15,12 @@ use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot;
#[sats(crate = __lib)]
pub struct AssetObjectProcedureResult {
pub ok: bool,
pub record: Option<AssetObjectUpsertSnapshot>,
pub error_message: Option<String>,
pub record: Option::<AssetObjectUpsertSnapshot>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AssetObjectProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
@@ -13,23 +18,25 @@ pub struct AssetObject {
pub bucket: String,
pub object_key: String,
pub access_policy: AssetObjectAccessPolicy,
pub content_type: Option<String>,
pub content_type: Option::<String>,
pub content_length: u64,
pub content_hash: Option<String>,
pub content_hash: Option::<String>,
pub version: u32,
pub source_job_id: Option<String>,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub entity_id: Option<String>,
pub source_job_id: Option::<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub entity_id: Option::<String>,
pub asset_kind: String,
pub created_at: __sdk::Timestamp,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for AssetObject {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AssetObject`.
///
/// Provides typed access to columns for query building.
@@ -38,14 +45,14 @@ pub struct AssetObjectCols {
pub bucket: __sdk::__query_builder::Col<AssetObject, String>,
pub object_key: __sdk::__query_builder::Col<AssetObject, String>,
pub access_policy: __sdk::__query_builder::Col<AssetObject, AssetObjectAccessPolicy>,
pub content_type: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub content_type: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub content_length: __sdk::__query_builder::Col<AssetObject, u64>,
pub content_hash: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub content_hash: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub version: __sdk::__query_builder::Col<AssetObject, u32>,
pub source_job_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub owner_user_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub profile_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub entity_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
pub source_job_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub owner_user_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub profile_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub entity_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
pub asset_kind: __sdk::__query_builder::Col<AssetObject, String>,
pub created_at: __sdk::__query_builder::Col<AssetObject, __sdk::Timestamp>,
pub updated_at: __sdk::__query_builder::Col<AssetObject, __sdk::Timestamp>,
@@ -70,6 +77,7 @@ impl __sdk::__query_builder::HasCols for AssetObject {
asset_kind: __sdk::__query_builder::Col::new(table_name, "asset_kind"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
@@ -88,8 +96,10 @@ impl __sdk::__query_builder::HasIxCols for AssetObject {
AssetObjectIxCols {
asset_kind: __sdk::__query_builder::IxCol::new(table_name, "asset_kind"),
asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AssetObject {}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
@@ -13,18 +18,20 @@ pub struct AssetObjectUpsertInput {
pub bucket: String,
pub object_key: String,
pub access_policy: AssetObjectAccessPolicy,
pub content_type: Option<String>,
pub content_type: Option::<String>,
pub content_length: u64,
pub content_hash: Option<String>,
pub content_hash: Option::<String>,
pub version: u32,
pub source_job_id: Option<String>,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub entity_id: Option<String>,
pub source_job_id: Option::<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub entity_id: Option::<String>,
pub asset_kind: String,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AssetObjectUpsertInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
@@ -13,19 +18,21 @@ pub struct AssetObjectUpsertSnapshot {
pub bucket: String,
pub object_key: String,
pub access_policy: AssetObjectAccessPolicy,
pub content_type: Option<String>,
pub content_type: Option::<String>,
pub content_length: u64,
pub content_hash: Option<String>,
pub content_hash: Option::<String>,
pub version: u32,
pub source_job_id: Option<String>,
pub owner_user_id: Option<String>,
pub profile_id: Option<String>,
pub entity_id: Option<String>,
pub source_job_id: Option::<String>,
pub owner_user_id: Option::<String>,
pub profile_id: Option::<String>,
pub entity_id: Option::<String>,
pub asset_kind: String,
pub created_at_micros: i64,
pub updated_at_micros: i64,
}
impl __sdk::InModule for AssetObjectUpsertSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::ai_result_reference_input_type::AiResultReferenceInput;
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
use super::ai_result_reference_input_type::AiResultReferenceInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AttachAiResultReferenceAndReturnArgs {
struct AttachAiResultReferenceAndReturnArgs {
pub input: AiResultReferenceInput,
}
impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait attach_ai_result_reference_and_return {
fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput) {
self.attach_ai_result_reference_and_return_then(input, |_, _| {});
fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput,
) {
self.attach_ai_result_reference_and_return_then(input, |_, _| {});
}
fn attach_ai_result_reference_and_return_then(
&self,
input: AiResultReferenceInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<AiTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl attach_ai_result_reference_and_return for super::RemoteProcedures {
&self,
input: AiResultReferenceInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<AiTaskProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
"attach_ai_result_reference_and_return",
AttachAiResultReferenceAndReturnArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
"attach_ai_result_reference_and_return",
AttachAiResultReferenceAndReturnArgs { input, },
__callback,
);
}
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,16 +17,18 @@ pub struct AuthIdentity {
pub user_id: String,
pub provider: String,
pub provider_uid: String,
pub provider_union_id: Option<String>,
pub phone_e_164: Option<String>,
pub display_name: Option<String>,
pub avatar_url: Option<String>,
pub provider_union_id: Option::<String>,
pub phone_e_164: Option::<String>,
pub display_name: Option::<String>,
pub avatar_url: Option::<String>,
}
impl __sdk::InModule for AuthIdentity {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AuthIdentity`.
///
/// Provides typed access to columns for query building.
@@ -29,10 +37,10 @@ pub struct AuthIdentityCols {
pub user_id: __sdk::__query_builder::Col<AuthIdentity, String>,
pub provider: __sdk::__query_builder::Col<AuthIdentity, String>,
pub provider_uid: __sdk::__query_builder::Col<AuthIdentity, String>,
pub provider_union_id: __sdk::__query_builder::Col<AuthIdentity, Option<String>>,
pub phone_e_164: __sdk::__query_builder::Col<AuthIdentity, Option<String>>,
pub display_name: __sdk::__query_builder::Col<AuthIdentity, Option<String>>,
pub avatar_url: __sdk::__query_builder::Col<AuthIdentity, Option<String>>,
pub provider_union_id: __sdk::__query_builder::Col<AuthIdentity, Option::<String>>,
pub phone_e_164: __sdk::__query_builder::Col<AuthIdentity, Option::<String>>,
pub display_name: __sdk::__query_builder::Col<AuthIdentity, Option::<String>>,
pub avatar_url: __sdk::__query_builder::Col<AuthIdentity, Option::<String>>,
}
impl __sdk::__query_builder::HasCols for AuthIdentity {
@@ -47,6 +55,7 @@ impl __sdk::__query_builder::HasCols for AuthIdentity {
phone_e_164: __sdk::__query_builder::Col::new(table_name, "phone_e_164"),
display_name: __sdk::__query_builder::Col::new(table_name, "display_name"),
avatar_url: __sdk::__query_builder::Col::new(table_name, "avatar_url"),
}
}
}
@@ -65,8 +74,10 @@ impl __sdk::__query_builder::HasIxCols for AuthIdentity {
AuthIdentityIxCols {
identity_id: __sdk::__query_builder::IxCol::new(table_name, "identity_id"),
user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AuthIdentity {}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord;
@@ -10,10 +15,12 @@ use super::auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord
#[sats(crate = __lib)]
pub struct AuthStoreSnapshotImportProcedureResult {
pub ok: bool,
pub record: Option<AuthStoreSnapshotImportRecord>,
pub error_message: Option<String>,
pub record: Option::<AuthStoreSnapshotImportRecord>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AuthStoreSnapshotImportProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -12,6 +18,8 @@ pub struct AuthStoreSnapshotImportRecord {
pub imported_refresh_session_count: u32,
}
impl __sdk::InModule for AuthStoreSnapshotImportRecord {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::auth_store_snapshot_record_type::AuthStoreSnapshotRecord;
@@ -10,10 +15,12 @@ use super::auth_store_snapshot_record_type::AuthStoreSnapshotRecord;
#[sats(crate = __lib)]
pub struct AuthStoreSnapshotProcedureResult {
pub ok: bool,
pub record: Option<AuthStoreSnapshotRecord>,
pub error_message: Option<String>,
pub record: Option::<AuthStoreSnapshotRecord>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for AuthStoreSnapshotProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,15 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct AuthStoreSnapshotRecord {
pub snapshot_json: Option<String>,
pub updated_at_micros: Option<i64>,
pub snapshot_json: Option::<String>,
pub updated_at_micros: Option::<i64>,
}
impl __sdk::InModule for AuthStoreSnapshotRecord {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -12,10 +18,12 @@ pub struct AuthStoreSnapshot {
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for AuthStoreSnapshot {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `AuthStoreSnapshot`.
///
/// Provides typed access to columns for query building.
@@ -32,6 +40,7 @@ impl __sdk::__query_builder::HasCols for AuthStoreSnapshot {
snapshot_id: __sdk::__query_builder::Col::new(table_name, "snapshot_id"),
snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
@@ -48,8 +57,10 @@ impl __sdk::__query_builder::HasIxCols for AuthStoreSnapshot {
fn ix_cols(table_name: &'static str) -> Self::IxCols {
AuthStoreSnapshotIxCols {
snapshot_id: __sdk::__query_builder::IxCol::new(table_name, "snapshot_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for AuthStoreSnapshot {}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,6 +17,8 @@ pub struct AuthStoreSnapshotUpsertInput {
pub updated_at_micros: i64,
}
impl __sdk::InModule for AuthStoreSnapshotUpsertInput {
type Module = super::RemoteModule;
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::database_migration_authorize_operator_input_type::DatabaseMigrationAuthorizeOperatorInput;
use super::database_migration_operator_procedure_result_type::DatabaseMigrationOperatorProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AuthorizeDatabaseMigrationOperatorArgs {
struct AuthorizeDatabaseMigrationOperatorArgs {
pub input: DatabaseMigrationAuthorizeOperatorInput,
}
impl __sdk::InModule for AuthorizeDatabaseMigrationOperatorArgs {
type Module = super::RemoteModule;
}
@@ -22,22 +28,16 @@ impl __sdk::InModule for AuthorizeDatabaseMigrationOperatorArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait authorize_database_migration_operator {
fn authorize_database_migration_operator(
&self,
input: DatabaseMigrationAuthorizeOperatorInput,
) {
self.authorize_database_migration_operator_then(input, |_, _| {});
fn authorize_database_migration_operator(&self, input: DatabaseMigrationAuthorizeOperatorInput,
) {
self.authorize_database_migration_operator_then(input, |_, _| {});
}
fn authorize_database_migration_operator_then(
&self,
input: DatabaseMigrationAuthorizeOperatorInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<DatabaseMigrationOperatorProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<DatabaseMigrationOperatorProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -46,17 +46,13 @@ impl authorize_database_migration_operator for super::RemoteProcedures {
&self,
input: DatabaseMigrationAuthorizeOperatorInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<DatabaseMigrationOperatorProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<DatabaseMigrationOperatorProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>(
"authorize_database_migration_operator",
AuthorizeDatabaseMigrationOperatorArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>(
"authorize_database_migration_operator",
AuthorizeDatabaseMigrationOperatorArgs { input, },
__callback,
);
}
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -11,8 +16,12 @@ pub enum BattleMode {
Fight,
Spar,
}
impl __sdk::InModule for BattleMode {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::battle_mode_type::BattleMode;
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
@@ -14,7 +19,7 @@ pub struct BattleStateInput {
pub story_session_id: String,
pub runtime_session_id: String,
pub actor_user_id: String,
pub chapter_id: Option<String>,
pub chapter_id: Option::<String>,
pub target_npc_id: String,
pub target_name: String,
pub battle_mode: BattleMode,
@@ -25,10 +30,12 @@ pub struct BattleStateInput {
pub target_hp: i32,
pub target_max_hp: i32,
pub experience_reward: u32,
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
pub created_at_micros: i64,
}
impl __sdk::InModule for BattleStateInput {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::battle_state_snapshot_type::BattleStateSnapshot;
@@ -10,10 +15,12 @@ use super::battle_state_snapshot_type::BattleStateSnapshot;
#[sats(crate = __lib)]
pub struct BattleStateProcedureResult {
pub ok: bool,
pub snapshot: Option<BattleStateSnapshot>,
pub error_message: Option<String>,
pub snapshot: Option::<BattleStateSnapshot>,
pub error_message: Option::<String>,
}
impl __sdk::InModule for BattleStateProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -10,6 +16,8 @@ pub struct BattleStateQueryInput {
pub battle_state_id: String,
}
impl __sdk::InModule for BattleStateQueryInput {
type Module = super::RemoteModule;
}

View File

@@ -2,12 +2,17 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::battle_mode_type::BattleMode;
use super::battle_status_type::BattleStatus;
use super::combat_outcome_type::CombatOutcome;
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
use super::combat_outcome_type::CombatOutcome;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -16,7 +21,7 @@ pub struct BattleStateSnapshot {
pub story_session_id: String,
pub runtime_session_id: String,
pub actor_user_id: String,
pub chapter_id: Option<String>,
pub chapter_id: Option::<String>,
pub target_npc_id: String,
pub target_name: String,
pub battle_mode: BattleMode,
@@ -28,11 +33,11 @@ pub struct BattleStateSnapshot {
pub target_hp: i32,
pub target_max_hp: i32,
pub experience_reward: u32,
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
pub turn_index: u32,
pub last_action_function_id: Option<String>,
pub last_action_text: Option<String>,
pub last_result_text: Option<String>,
pub last_action_function_id: Option::<String>,
pub last_action_text: Option::<String>,
pub last_result_text: Option::<String>,
pub last_damage_dealt: i32,
pub last_damage_taken: i32,
pub last_outcome: CombatOutcome,
@@ -41,6 +46,8 @@ pub struct BattleStateSnapshot {
pub updated_at_micros: i64,
}
impl __sdk::InModule for BattleStateSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -2,12 +2,17 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::battle_mode_type::BattleMode;
use super::battle_status_type::BattleStatus;
use super::combat_outcome_type::CombatOutcome;
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
use super::combat_outcome_type::CombatOutcome;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -16,7 +21,7 @@ pub struct BattleState {
pub story_session_id: String,
pub runtime_session_id: String,
pub actor_user_id: String,
pub chapter_id: Option<String>,
pub chapter_id: Option::<String>,
pub target_npc_id: String,
pub target_name: String,
pub battle_mode: BattleMode,
@@ -28,11 +33,11 @@ pub struct BattleState {
pub target_hp: i32,
pub target_max_hp: i32,
pub experience_reward: u32,
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
pub turn_index: u32,
pub last_action_function_id: Option<String>,
pub last_action_text: Option<String>,
pub last_result_text: Option<String>,
pub last_action_function_id: Option::<String>,
pub last_action_text: Option::<String>,
pub last_result_text: Option::<String>,
pub last_damage_dealt: i32,
pub last_damage_taken: i32,
pub last_outcome: CombatOutcome,
@@ -41,10 +46,12 @@ pub struct BattleState {
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for BattleState {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `BattleState`.
///
/// Provides typed access to columns for query building.
@@ -53,7 +60,7 @@ pub struct BattleStateCols {
pub story_session_id: __sdk::__query_builder::Col<BattleState, String>,
pub runtime_session_id: __sdk::__query_builder::Col<BattleState, String>,
pub actor_user_id: __sdk::__query_builder::Col<BattleState, String>,
pub chapter_id: __sdk::__query_builder::Col<BattleState, Option<String>>,
pub chapter_id: __sdk::__query_builder::Col<BattleState, Option::<String>>,
pub target_npc_id: __sdk::__query_builder::Col<BattleState, String>,
pub target_name: __sdk::__query_builder::Col<BattleState, String>,
pub battle_mode: __sdk::__query_builder::Col<BattleState, BattleMode>,
@@ -65,11 +72,11 @@ pub struct BattleStateCols {
pub target_hp: __sdk::__query_builder::Col<BattleState, i32>,
pub target_max_hp: __sdk::__query_builder::Col<BattleState, i32>,
pub experience_reward: __sdk::__query_builder::Col<BattleState, u32>,
pub reward_items: __sdk::__query_builder::Col<BattleState, Vec<RuntimeItemRewardItemSnapshot>>,
pub reward_items: __sdk::__query_builder::Col<BattleState, Vec::<RuntimeItemRewardItemSnapshot>>,
pub turn_index: __sdk::__query_builder::Col<BattleState, u32>,
pub last_action_function_id: __sdk::__query_builder::Col<BattleState, Option<String>>,
pub last_action_text: __sdk::__query_builder::Col<BattleState, Option<String>>,
pub last_result_text: __sdk::__query_builder::Col<BattleState, Option<String>>,
pub last_action_function_id: __sdk::__query_builder::Col<BattleState, Option::<String>>,
pub last_action_text: __sdk::__query_builder::Col<BattleState, Option::<String>>,
pub last_result_text: __sdk::__query_builder::Col<BattleState, Option::<String>>,
pub last_damage_dealt: __sdk::__query_builder::Col<BattleState, i32>,
pub last_damage_taken: __sdk::__query_builder::Col<BattleState, i32>,
pub last_outcome: __sdk::__query_builder::Col<BattleState, CombatOutcome>,
@@ -100,10 +107,7 @@ impl __sdk::__query_builder::HasCols for BattleState {
experience_reward: __sdk::__query_builder::Col::new(table_name, "experience_reward"),
reward_items: __sdk::__query_builder::Col::new(table_name, "reward_items"),
turn_index: __sdk::__query_builder::Col::new(table_name, "turn_index"),
last_action_function_id: __sdk::__query_builder::Col::new(
table_name,
"last_action_function_id",
),
last_action_function_id: __sdk::__query_builder::Col::new(table_name, "last_action_function_id"),
last_action_text: __sdk::__query_builder::Col::new(table_name, "last_action_text"),
last_result_text: __sdk::__query_builder::Col::new(table_name, "last_result_text"),
last_damage_dealt: __sdk::__query_builder::Col::new(table_name, "last_damage_dealt"),
@@ -112,6 +116,7 @@ impl __sdk::__query_builder::HasCols for BattleState {
version: __sdk::__query_builder::Col::new(table_name, "version"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
@@ -132,13 +137,12 @@ impl __sdk::__query_builder::HasIxCols for BattleState {
BattleStateIxCols {
actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"),
battle_state_id: __sdk::__query_builder::IxCol::new(table_name, "battle_state_id"),
runtime_session_id: __sdk::__query_builder::IxCol::new(
table_name,
"runtime_session_id",
),
runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"),
story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for BattleState {}

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
@@ -13,8 +18,12 @@ pub enum BattleStatus {
Resolved,
Aborted,
}
impl __sdk::InModule for BattleStatus {
type Module = super::RemoteModule;
}

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::story_session_input_type::StorySessionInput;
use super::story_session_procedure_result_type::StorySessionProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct BeginStorySessionAndReturnArgs {
struct BeginStorySessionAndReturnArgs {
pub input: StorySessionInput,
}
impl __sdk::InModule for BeginStorySessionAndReturnArgs {
type Module = super::RemoteModule;
}
@@ -22,19 +28,16 @@ impl __sdk::InModule for BeginStorySessionAndReturnArgs {
///
/// Implemented for [`super::RemoteProcedures`].
pub trait begin_story_session_and_return {
fn begin_story_session_and_return(&self, input: StorySessionInput) {
self.begin_story_session_and_return_then(input, |_, _| {});
fn begin_story_session_and_return(&self, input: StorySessionInput,
) {
self.begin_story_session_and_return_then(input, |_, _| {});
}
fn begin_story_session_and_return_then(
&self,
input: StorySessionInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<StorySessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<StorySessionProcedureResult, __sdk::InternalError>) + Send + 'static,
);
}
@@ -43,17 +46,13 @@ impl begin_story_session_and_return for super::RemoteProcedures {
&self,
input: StorySessionInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<StorySessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
__callback: impl FnOnce(&super::ProcedureEventContext, Result<StorySessionProcedureResult, __sdk::InternalError>) + Send + 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, StorySessionProcedureResult>(
"begin_story_session_and_return",
BeginStorySessionAndReturnArgs { input },
__callback,
);
self.imp.invoke_procedure_with_callback::<_, StorySessionProcedureResult>(
"begin_story_session_and_return",
BeginStorySessionAndReturnArgs { input, },
__callback,
);
}
}

Some files were not shown because too many files have changed in this diff Show More