Merge branch 'master' of http://82.157.175.59:3000/GenarrativeAI/Genarrative
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-27 14:23:33 +08:00
50 changed files with 1908 additions and 270 deletions

View File

@@ -34,8 +34,8 @@ use crate::{
auth_sessions::auth_sessions,
big_fish::{
create_big_fish_session, delete_big_fish_work, execute_big_fish_action, get_big_fish_run,
get_big_fish_session, get_big_fish_works, start_big_fish_run, stream_big_fish_message,
submit_big_fish_input, submit_big_fish_message,
get_big_fish_session, get_big_fish_works, list_big_fish_gallery, start_big_fish_run,
stream_big_fish_message, submit_big_fish_input, submit_big_fish_message,
},
character_animation_assets::{
generate_character_animation, get_character_animation_job, get_character_workflow_cache,
@@ -566,6 +566,7 @@ pub fn build_router(state: AppState) -> Router {
require_bearer_auth,
)),
)
.route("/api/runtime/big-fish/gallery", get(list_big_fish_gallery))
.route(
"/api/runtime/big-fish/works/{session_id}",
delete(delete_big_fish_work).route_layer(middleware::from_fn_with_state(

View File

@@ -144,6 +144,29 @@ pub async fn get_big_fish_works(
))
}
pub async fn list_big_fish_gallery(
State(state): State<AppState>,
Extension(request_context): Extension<RequestContext>,
) -> Result<Json<Value>, Response> {
let items = state
.spacetime_client()
.list_big_fish_gallery()
.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(map_big_fish_work_summary_response)
.collect(),
},
))
}
pub async fn delete_big_fish_work(
State(state): State<AppState>,
Path(session_id): Path<String>,
@@ -919,6 +942,7 @@ fn map_big_fish_work_summary_response(
BigFishWorkSummaryResponse {
work_id: item.work_id,
source_session_id: item.source_session_id,
owner_user_id: item.owner_user_id,
title: item.title,
subtitle: item.subtitle,
summary: item.summary,

View File

@@ -257,6 +257,7 @@ pub struct BigFishSessionProcedureResult {
pub struct BigFishWorkSummarySnapshot {
pub work_id: String,
pub source_session_id: String,
pub owner_user_id: String,
pub title: String,
pub subtitle: String,
pub summary: String,
@@ -274,6 +275,7 @@ pub struct BigFishWorkSummarySnapshot {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BigFishWorksListInput {
pub owner_user_id: String,
pub published_only: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
@@ -747,6 +749,9 @@ pub fn validate_session_get_input(input: &BigFishSessionGetInput) -> Result<(),
}
pub fn validate_works_list_input(input: &BigFishWorksListInput) -> Result<(), BigFishFieldError> {
if input.published_only {
return Ok(());
}
if normalize_required_string(&input.owner_user_id).is_none() {
return Err(BigFishFieldError::MissingOwnerUserId);
}

View File

@@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize};
pub struct BigFishWorkSummaryResponse {
pub work_id: String,
pub source_session_id: String,
pub owner_user_id: String,
pub title: String,
pub subtitle: String,
pub summary: String,

View File

@@ -57,8 +57,28 @@ impl SpacetimeClient {
&self,
owner_user_id: String,
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
let procedure_input = BigFishWorksListInput { owner_user_id };
let procedure_input = BigFishWorksListInput {
owner_user_id,
published_only: false,
};
self.list_big_fish_works_with_input(procedure_input).await
}
pub async fn list_big_fish_gallery(
&self,
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
self.list_big_fish_works_with_input(BigFishWorksListInput {
owner_user_id: String::new(),
published_only: true,
})
.await
}
async fn list_big_fish_works_with_input(
&self,
procedure_input: BigFishWorksListInput,
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
self.call_after_connect(move |connection, sender| {
connection
.procedures()

View File

@@ -4590,6 +4590,7 @@ pub struct BigFishSessionRecord {
pub struct BigFishWorkSummaryRecord {
pub work_id: String,
pub source_session_id: String,
pub owner_user_id: String,
pub title: String,
pub subtitle: String,
pub summary: String,

View File

@@ -8,6 +8,7 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[sats(crate = __lib)]
pub struct BigFishWorksListInput {
pub owner_user_id: String,
pub published_only: bool,
}
impl __sdk::InModule for BigFishWorksListInput {

View File

@@ -112,13 +112,6 @@ pub(crate) fn generate_big_fish_asset_tx(
updated_at,
};
replace_big_fish_session(ctx, &session, next_session);
append_big_fish_system_message(
ctx,
&input.session_id,
format!("big-fish-message-asset-{}", input.generated_at_micros),
reply,
input.generated_at_micros,
);
get_big_fish_session_tx(
ctx,

View File

@@ -77,8 +77,12 @@ fn start_big_fish_run_tx(
.big_fish_creation_session()
.session_id()
.find(&input.session_id)
.filter(|row| row.owner_user_id == input.owner_user_id)
.ok_or_else(|| "big_fish_creation_session 不存在".to_string())?;
if session.owner_user_id != input.owner_user_id
&& session.stage != BigFishCreationStage::Published
{
return Err("big_fish_creation_session 不存在".to_string());
}
let draft = session
.draft_json
.as_deref()
@@ -124,8 +128,12 @@ fn submit_big_fish_input_tx(
.big_fish_creation_session()
.session_id()
.find(&run.session_id)
.filter(|row| row.owner_user_id == input.owner_user_id)
.ok_or_else(|| "big_fish_creation_session 不存在".to_string())?;
if session.owner_user_id != input.owner_user_id
&& session.stage != BigFishCreationStage::Published
{
return Err("big_fish_creation_session 不存在".to_string());
}
let draft = session
.draft_json
.as_deref()

View File

@@ -239,6 +239,10 @@ pub(crate) fn list_big_fish_works_tx(
.big_fish_creation_session()
.iter()
.filter(|row| {
if input.published_only {
return row.stage == BigFishCreationStage::Published;
}
row.owner_user_id == input.owner_user_id && should_include_big_fish_work(ctx, row)
})
.map(|row| build_big_fish_work_summary(ctx, &row))
@@ -330,6 +334,7 @@ pub(crate) fn delete_big_fish_work_tx(
ctx,
BigFishWorksListInput {
owner_user_id: input.owner_user_id,
published_only: false,
},
)
}
@@ -538,13 +543,6 @@ pub(crate) fn compile_big_fish_draft_tx(
updated_at: compiled_at,
};
replace_big_fish_session(ctx, &session, next_session);
append_big_fish_system_message(
ctx,
&input.session_id,
format!("big-fish-message-compile-{}", input.compiled_at_micros),
reply,
input.compiled_at_micros,
);
get_big_fish_session_tx(
ctx,
@@ -649,6 +647,7 @@ pub(crate) fn build_big_fish_work_summary(
Ok(BigFishWorkSummarySnapshot {
work_id: format!("big-fish-work-{}", row.session_id),
source_session_id: row.session_id.clone(),
owner_user_id: row.owner_user_id.clone(),
title,
subtitle,
summary,
@@ -682,32 +681,6 @@ pub(crate) fn replace_big_fish_session(
ctx.db.big_fish_creation_session().insert(next);
}
pub(crate) fn append_big_fish_system_message(
ctx: &ReducerContext,
session_id: &str,
message_id: String,
text: String,
created_at_micros: i64,
) {
if ctx
.db
.big_fish_agent_message()
.message_id()
.find(&message_id)
.is_some()
{
return;
}
ctx.db.big_fish_agent_message().insert(BigFishAgentMessage {
message_id,
session_id: session_id.to_string(),
role: BigFishAgentMessageRole::Assistant,
kind: BigFishAgentMessageKind::ActionResult,
text,
created_at: Timestamp::from_micros_since_unix_epoch(created_at_micros),
});
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -659,12 +659,6 @@ fn compile_puzzle_agent_draft_tx(
updated_at: compiled_at,
},
);
append_system_message(
ctx,
&row.session_id,
input.compiled_at_micros,
"拼图结果页草稿已生成。",
)?;
get_puzzle_agent_session_tx(
ctx,
PuzzleAgentSessionGetInput {
@@ -1260,25 +1254,6 @@ fn build_puzzle_suggested_actions(
}
}
fn append_system_message(
ctx: &TxContext,
session_id: &str,
created_at_micros: i64,
text: &str,
) -> Result<(), String> {
let message_id = format!("{session_id}-system-{created_at_micros}");
ensure_message_missing(ctx, &message_id)?;
ctx.db.puzzle_agent_message().insert(PuzzleAgentMessageRow {
message_id,
session_id: session_id.to_string(),
role: PuzzleAgentMessageRole::Assistant,
kind: PuzzleAgentMessageKind::ActionResult,
text: text.to_string(),
created_at: Timestamp::from_micros_since_unix_epoch(created_at_micros),
});
Ok(())
}
fn ensure_session_missing(ctx: &TxContext, session_id: &str) -> Result<(), String> {
if ctx
.db