fix: sync rust api-server runtime and bindings
This commit is contained in:
@@ -40,12 +40,19 @@ use spacetime_client::{
|
||||
use std::convert::Infallible;
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
|
||||
api_response::json_success_body,
|
||||
auth::AuthenticatedAccessToken,
|
||||
custom_world_agent_turn::{
|
||||
CustomWorldAgentTurnRequest, build_failed_finalize_record_input,
|
||||
build_finalize_record_input, run_custom_world_agent_turn,
|
||||
},
|
||||
request_context::RequestContext, state::AppState,
|
||||
custom_world_foundation_draft::{
|
||||
DraftFoundationPayloadError, build_draft_foundation_action_payload_json,
|
||||
generate_custom_world_foundation_draft,
|
||||
},
|
||||
http_error::AppError,
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
pub async fn get_custom_world_library(
|
||||
@@ -142,12 +149,16 @@ pub async fn put_custom_world_library_profile(
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let author_display_name = resolve_author_display_name(&authenticated);
|
||||
let author_display_name = resolve_author_display_name(&state, &authenticated);
|
||||
let author_public_user_code =
|
||||
resolve_author_public_user_code(&state, &authenticated, &request_context)?;
|
||||
let mutation = state
|
||||
.spacetime_client()
|
||||
.upsert_custom_world_profile(CustomWorldProfileUpsertRecordInput {
|
||||
profile_id: profile_id.clone(),
|
||||
owner_user_id: owner_user_id.clone(),
|
||||
public_work_code: None,
|
||||
author_public_user_code: Some(author_public_user_code),
|
||||
source_agent_session_id: payload.source_agent_session_id.clone(),
|
||||
world_name: metadata.world_name,
|
||||
subtitle: metadata.subtitle,
|
||||
@@ -240,7 +251,9 @@ pub async fn publish_custom_world_library_profile(
|
||||
.publish_custom_world_profile(
|
||||
profile_id,
|
||||
owner_user_id,
|
||||
resolve_author_display_name(&authenticated),
|
||||
None,
|
||||
resolve_author_public_user_code(&state, &authenticated, &request_context)?,
|
||||
resolve_author_display_name(&state, &authenticated),
|
||||
current_utc_micros(),
|
||||
)
|
||||
.await
|
||||
@@ -279,7 +292,7 @@ pub async fn unpublish_custom_world_library_profile(
|
||||
.unpublish_custom_world_profile(
|
||||
profile_id,
|
||||
owner_user_id,
|
||||
resolve_author_display_name(&authenticated),
|
||||
resolve_author_display_name(&state, &authenticated),
|
||||
current_utc_micros(),
|
||||
)
|
||||
.await
|
||||
@@ -350,6 +363,37 @@ pub async fn get_custom_world_gallery_detail(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_custom_world_gallery_detail_by_code(
|
||||
State(state): State<AppState>,
|
||||
Path(code): Path<String>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
) -> Result<Json<Value>, Response> {
|
||||
if code.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": "code is required",
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
let detail = state
|
||||
.spacetime_client()
|
||||
.get_custom_world_gallery_detail_by_code(code)
|
||||
.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(detail.entry),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn create_custom_world_agent_session(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -870,25 +914,88 @@ pub async fn execute_custom_world_agent_action(
|
||||
));
|
||||
}
|
||||
|
||||
let payload_json = serde_json::to_string(&payload).map_err(|error| {
|
||||
custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": format!("action payload JSON 序列化失败:{error}"),
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let owner_user_id = authenticated.claims().user_id().to_string();
|
||||
let submitted_at_micros = current_utc_micros();
|
||||
let payload_json = if action == "draft_foundation" {
|
||||
let session = state
|
||||
.spacetime_client()
|
||||
.get_custom_world_agent_session(session_id.clone(), owner_user_id.clone())
|
||||
.await
|
||||
.map_err(|error| {
|
||||
custom_world_error_response(&request_context, map_custom_world_client_error(error))
|
||||
})?;
|
||||
if session.progress_percent < 100 {
|
||||
return Err(custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": "draft_foundation requires progressPercent >= 100",
|
||||
})),
|
||||
));
|
||||
}
|
||||
let llm_client = state.llm_client().ok_or_else(|| {
|
||||
custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": "服务端尚未配置可用的 LLM API Key",
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
let draft_result = generate_custom_world_foundation_draft(llm_client, &session)
|
||||
.await
|
||||
.map_err(|message| {
|
||||
custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": message,
|
||||
})),
|
||||
)
|
||||
})?;
|
||||
build_draft_foundation_action_payload_json(&payload, &draft_result.draft_profile_json)
|
||||
.map_err(|error| {
|
||||
let (status, message) = match error {
|
||||
DraftFoundationPayloadError::SerializePayload(message) => {
|
||||
(StatusCode::BAD_REQUEST, message)
|
||||
}
|
||||
DraftFoundationPayloadError::InvalidPayloadShape => (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"action payload 必须是 object".to_string(),
|
||||
),
|
||||
DraftFoundationPayloadError::InvalidGeneratedDraft(message) => {
|
||||
(StatusCode::BAD_GATEWAY, message)
|
||||
}
|
||||
};
|
||||
custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(status).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": message,
|
||||
})),
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
serde_json::to_string(&payload).map_err(|error| {
|
||||
custom_world_error_response(
|
||||
&request_context,
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "custom-world-agent",
|
||||
"message": format!("action payload JSON 序列化失败:{error}"),
|
||||
})),
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
let result = state
|
||||
.spacetime_client()
|
||||
.execute_custom_world_agent_action(CustomWorldAgentActionExecuteRecordInput {
|
||||
session_id,
|
||||
owner_user_id: authenticated.claims().user_id().to_string(),
|
||||
owner_user_id,
|
||||
operation_id: build_prefixed_uuid_id("operation-"),
|
||||
action,
|
||||
payload_json: Some(payload_json),
|
||||
submitted_at_micros: current_utc_micros(),
|
||||
submitted_at_micros,
|
||||
})
|
||||
.await
|
||||
.map_err(|error| {
|
||||
@@ -909,6 +1016,8 @@ fn map_custom_world_library_entry_response(
|
||||
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,
|
||||
profile: entry.profile,
|
||||
visibility: entry.visibility,
|
||||
published_at: entry.published_at,
|
||||
@@ -930,6 +1039,8 @@ fn map_custom_world_gallery_card_response(
|
||||
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,
|
||||
visibility: entry.visibility,
|
||||
published_at: entry.published_at,
|
||||
updated_at: entry.updated_at,
|
||||
@@ -1213,8 +1324,48 @@ fn custom_world_sse_error_event_message(message: String) -> Event {
|
||||
Event::default().event("error").data(payload)
|
||||
}
|
||||
|
||||
fn resolve_author_display_name(_authenticated: &AuthenticatedAccessToken) -> String {
|
||||
"玩家".to_string()
|
||||
fn resolve_author_display_name(
|
||||
state: &AppState,
|
||||
authenticated: &AuthenticatedAccessToken,
|
||||
) -> String {
|
||||
state
|
||||
.auth_user_service()
|
||||
.get_user_by_id(authenticated.claims().user_id())
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|user| user.display_name)
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| "玩家".to_string())
|
||||
}
|
||||
|
||||
fn resolve_author_public_user_code(
|
||||
state: &AppState,
|
||||
authenticated: &AuthenticatedAccessToken,
|
||||
request_context: &RequestContext,
|
||||
) -> Result<String, Response> {
|
||||
state
|
||||
.auth_user_service()
|
||||
.get_user_by_id(authenticated.claims().user_id())
|
||||
.map_err(|error| {
|
||||
custom_world_error_response(
|
||||
request_context,
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
|
||||
"provider": "custom-world-library",
|
||||
"message": format!("作者叙世号读取失败:{error}"),
|
||||
})),
|
||||
)
|
||||
})?
|
||||
.map(|user| user.public_user_code)
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.ok_or_else(|| {
|
||||
custom_world_error_response(
|
||||
request_context,
|
||||
AppError::from_status(StatusCode::UNAUTHORIZED).with_details(json!({
|
||||
"provider": "custom-world-library",
|
||||
"message": "当前登录用户缺少叙世号",
|
||||
})),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn build_custom_world_agent_welcome_text(seed_text: &str) -> String {
|
||||
|
||||
Reference in New Issue
Block a user