persist error message in oss doc

This commit is contained in:
2026-07-16 17:18:08 +08:00
parent 9dc65e1d47
commit ca4bf6db5e
9 changed files with 134 additions and 37 deletions
@@ -18,11 +18,11 @@ use shared_contracts::assets::{
EditorVideoGenerateRequest,
};
use shared_contracts::editor_agent::{
CreateEditorAgentConversationRequest, EditorAgentConversationListResponse,
EditorAgentConversationMessagesDocument, EditorAgentConversationResponse,
EditorAgentConversationSummary, EditorAgentMessage, EditorAgentMessageRequest,
EditorAgentMessageResponse, EditorAgentMessageRole, EditorAgentToolCall,
EditorAgentToolCallStatus,
CreateEditorAgentConversationRequest, EDITOR_AGENT_ERROR_MESSAGE_PREFIX,
EditorAgentConversationListResponse, EditorAgentConversationMessagesDocument,
EditorAgentConversationResponse, EditorAgentConversationSummary, EditorAgentMessage,
EditorAgentMessageRequest, EditorAgentMessageResponse, EditorAgentMessageRole,
EditorAgentToolCall, EditorAgentToolCallStatus,
};
use spacetime_client::{
EditorAgentConversationCreateRecordInput, EditorAgentConversationDeleteRecordInput,
@@ -210,17 +210,30 @@ pub async fn editor_agent_message(
let tool_context = context::build_tool_context(&document);
// Build and run agent
let llm_client = state.creative_agent_gpt5_client().ok_or_else(|| {
AppError::from_status(axum::http::StatusCode::SERVICE_UNAVAILABLE)
.with_details(json!({ "message": "Creative Agent GPT-5 client not configured" }))
})?;
let Some(llm_client) = state.creative_agent_gpt5_client() else {
return persist_editor_agent_planning_error(
&state,
&conversation,
&mut document,
conversation_summary,
"Creative Agent GPT-5 client not configured",
)
.await;
};
let llm_client = llm_client.clone();
let pricing = state.editor_generation_pricing().await.map_err(|error| {
AppError::from_status(axum::http::StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
"provider": "editor-generation-pricing",
"message": error.to_string(),
}))
})?;
let pricing = match state.editor_generation_pricing().await {
Ok(pricing) => pricing,
Err(error) => {
return persist_editor_agent_planning_error(
&state,
&conversation,
&mut document,
conversation_summary,
format!("failed to load editor generation pricing: {error}"),
)
.await;
}
};
let memory = VecMemory::new(previous_messages);
@@ -265,11 +278,16 @@ pub async fn editor_agent_message(
&tool_context,
&pricing,
) {
Err(err) => Ok(Json(EditorAgentMessageResponse {
conversation: conversation_summary,
delta_messages: Vec::new(),
error_message: Some(err.to_string()),
})),
Err(error) => {
persist_editor_agent_planning_error(
&state,
&conversation,
&mut document,
conversation_summary,
error.to_string(),
)
.await
}
Ok(delta_messages) => {
for msg in &delta_messages {
document.messages.push(msg.clone());
@@ -285,6 +303,39 @@ pub async fn editor_agent_message(
}
}
fn build_editor_agent_error_message(
message_id: usize,
error: impl std::fmt::Display,
) -> EditorAgentMessage {
EditorAgentMessage {
id: message_id,
client_message_id: None,
role: EditorAgentMessageRole::System,
text: format!("{EDITOR_AGENT_ERROR_MESSAGE_PREFIX}{error}"),
attachments: Vec::new(),
tool_call: None,
created_at: now_rfc3339(),
}
}
async fn persist_editor_agent_planning_error(
state: &AppState,
conversation: &EditorAgentConversationRecord,
document: &mut EditorAgentConversationMessagesDocument,
conversation_summary: EditorAgentConversationSummary,
error: impl std::fmt::Display,
) -> Result<Json<EditorAgentMessageResponse>, AppError> {
let error_message = build_editor_agent_error_message(document.messages.len(), error);
document.messages.push(error_message.clone());
write_messages_document(state, conversation, document).await?;
Ok(Json(EditorAgentMessageResponse {
conversation: conversation_summary,
delta_messages: vec![error_message],
error_message: None,
}))
}
fn validate_editor_agent_message_request(
payload: &EditorAgentMessageRequest,
) -> Result<String, AppError> {
@@ -451,6 +502,16 @@ mod tests {
None,
);
}
#[test]
fn builds_system_error_message_with_wire_prefix() {
let message = build_editor_agent_error_message(3, "planning failed");
assert_eq!(message.id, 3);
assert_eq!(message.role, EditorAgentMessageRole::System);
assert_eq!(message.text, "ERROR planning failed");
assert!(message.tool_call.is_none());
}
}
fn editor_agent_system_prompt() -> &'static str {
r#"
@@ -5,6 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize};
use serde_json::json;
pub const EDITOR_AGENT_MAX_ATTACHMENTS: usize = 9;
pub const EDITOR_AGENT_ERROR_MESSAGE_PREFIX: &str = "ERROR ";
pub const EDITOR_AGENT_TITLE_MAX_CHARS: usize = 20;
pub const EDITOR_AGENT_DEFAULT_CONVERSATION_TITLE: &str = "新对话";
pub const EDITOR_AGENT_MESSAGES_DOCUMENT_VERSION: u32 = 2;