2fa05cd605
要求用户消息包含非空文本,附件仅作为消息上下文。 同步后端领域校验、接口错误和请求测试。 禁用前端纯附件发送并更新交互回归测试。 保留发送失败时的草稿与附件恢复行为。
58 lines
2.4 KiB
Rust
58 lines
2.4 KiB
Rust
//! 画布Agent对话领域模型。
|
|
//!
|
|
//! 本 crate 只描述会话元数据、消息与附件的纯领域事实与规则;
|
|
//! LLM、SSE、OSS 读写和 SpacetimeDB 写表均留在外层 adapter。
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
#[cfg(feature = "spacetime-types")]
|
|
use spacetimedb::SpacetimeType;
|
|
|
|
pub const EDITOR_AGENT_CONVERSATION_ID_PREFIX: &str = "editor-agent-conv-";
|
|
pub const EDITOR_AGENT_MESSAGE_ID_PREFIX: &str = "editor-agent-message-";
|
|
/// 会话消息 OSS 对象目录前缀,完整键见 [`editor_agent_messages_object_key`]。
|
|
pub const EDITOR_AGENT_MESSAGES_OBJECT_PREFIX: &str = "editor-agent/";
|
|
/// 单条消息附件上限(超出由前端拦截、后端兜底拒绝)。
|
|
pub const EDITOR_AGENT_MAX_ATTACHMENTS: usize = 9;
|
|
/// 会话标题取首条用户消息的前 N 个字符。
|
|
pub const EDITOR_AGENT_TITLE_MAX_CHARS: usize = 20;
|
|
/// 新会话默认标题。
|
|
pub const EDITOR_AGENT_DEFAULT_CONVERSATION_TITLE: &str = "新对话";
|
|
|
|
/// 会话元数据快照:SpacetimeDB 表只存这些字段,消息正文整体存 OSS。
|
|
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct EditorAgentConversationSnapshot {
|
|
pub conversation_id: String,
|
|
pub project_id: String,
|
|
pub owner_user_id: String,
|
|
pub title: String,
|
|
pub messages_object_key: String,
|
|
pub deleted: bool,
|
|
pub created_at_micros: i64,
|
|
pub updated_at_micros: i64,
|
|
}
|
|
|
|
/// 会话消息 OSS 对象键:`editor-agent/{conversationId}.json`,会话粒度整体读写。
|
|
pub fn editor_agent_messages_object_key(conversation_id: &str) -> String {
|
|
format!("{EDITOR_AGENT_MESSAGES_OBJECT_PREFIX}{conversation_id}.json")
|
|
}
|
|
|
|
/// 从首条用户消息推导会话标题:去掉首尾空白与换行后截取前 N 个字符;
|
|
/// 空文本退回默认标题,供尚未发送消息的新会话使用。
|
|
pub fn derive_conversation_title(first_message_text: &str) -> String {
|
|
let normalized: String = first_message_text
|
|
.chars()
|
|
.map(|c| if c == '\n' || c == '\r' { ' ' } else { c })
|
|
.collect::<String>()
|
|
.split_whitespace()
|
|
.collect::<Vec<_>>()
|
|
.join(" ");
|
|
if normalized.is_empty() {
|
|
return EDITOR_AGENT_DEFAULT_CONVERSATION_TITLE.to_string();
|
|
}
|
|
normalized
|
|
.chars()
|
|
.take(EDITOR_AGENT_TITLE_MAX_CHARS)
|
|
.collect()
|
|
}
|