switch api server sse to axum builtin

This commit is contained in:
2026-04-22 15:34:17 +08:00
parent 28ba990123
commit d5627c536d
6 changed files with 100 additions and 324 deletions

View File

@@ -2,7 +2,10 @@ use axum::{
Json,
extract::{Extension, Path, State, rejection::JsonRejection},
http::StatusCode,
response::Response,
response::{
IntoResponse, Response,
sse::{Event, Sse},
},
};
use module_custom_world::{
CustomWorldThemeMode, empty_agent_anchor_content_json, empty_agent_asset_coverage_json,
@@ -34,10 +37,11 @@ use spacetime_client::{
CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord,
CustomWorldWorkSummaryRecord, SpacetimeClientError,
};
use std::convert::Infallible;
use crate::{
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
request_context::RequestContext, sse::SseEventBuffer, state::AppState,
request_context::RequestContext, state::AppState,
};
pub async fn get_custom_world_library(
@@ -576,17 +580,23 @@ pub async fn stream_custom_world_agent_message(
let session_response = map_custom_world_agent_session_response(session);
let reply_text = resolve_stream_reply_text(&session_response);
// 这里先用“一次性构造完整 SSE 文本”的最小兼容方案,
// 复用 Stage 7 的同步 deterministic 写表逻辑,保证前端当前的 reader 协议可直接消费
let mut sse = SseEventBuffer::new();
sse.push_json("reply_delta", &json!({ "text": reply_text }))
.map_err(|error| custom_world_error_response(&request_context, error))?;
sse.push_json("session", &json!({ "session": session_response }))
.map_err(|error| custom_world_error_response(&request_context, error))?;
sse.push_json("done", &json!({ "ok": true }))
.map_err(|error| custom_world_error_response(&request_context, error))?;
// 这里仍保持“一次性返回完整事件序列”的兼容语义;
// SSE 编码、标准响应头与 body frame 交给 Axum 内建实现维护
let events = vec![
custom_world_sse_json_event("reply_delta", json!({ "text": reply_text }))
.map_err(|error| custom_world_error_response(&request_context, error))?,
custom_world_sse_json_event("session", json!({ "session": session_response }))
.map_err(|error| custom_world_error_response(&request_context, error))?,
custom_world_sse_json_event("done", json!({ "ok": true }))
.map_err(|error| custom_world_error_response(&request_context, error))?,
];
let stream = tokio_stream::iter(
events
.into_iter()
.map(|event| Ok::<Event, Infallible>(event)),
);
Ok(sse.into_response())
Ok(Sse::new(stream).into_response())
}
pub async fn get_custom_world_agent_operation(
@@ -983,6 +993,18 @@ fn custom_world_error_response(request_context: &RequestContext, error: AppError
error.into_response_with_context(Some(request_context))
}
fn custom_world_sse_json_event(event_name: &str, payload: Value) -> Result<Event, AppError> {
Event::default()
.event(event_name)
.json_data(payload)
.map_err(|error| {
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
"provider": "sse",
"message": format!("SSE payload 序列化失败:{error}"),
}))
})
}
fn resolve_author_display_name(_authenticated: &AuthenticatedAccessToken) -> String {
"玩家".to_string()
}