feat: 统一记忆 scope_key 为 "default",简化上下文依赖

This commit is contained in:
ooodc 2026-06-06 14:52:54 +08:00
parent e36f66e23b
commit abb2d596f4
4 changed files with 31 additions and 26 deletions

View File

@ -17,8 +17,9 @@ pub use ports::{
}; };
pub use records::{ pub use records::{
allowed_namespace_names, get_namespace_description, is_valid_namespace, allowed_namespace_names, get_namespace_description, is_valid_namespace,
ALLOWED_MEMORY_NAMESPACES, MemoryRecord, MemoryUpsert, SchedulerJobRecord, SchedulerJobState, ALLOWED_MEMORY_NAMESPACES, GLOBAL_SCOPE_KEY, MemoryRecord, MemoryUpsert, SchedulerJobRecord,
SchedulerJobStatus, SchedulerJobUpsert, SessionRecord, SkillEventRecord, TopicRecord, SchedulerJobState, SchedulerJobStatus, SchedulerJobUpsert, SessionRecord, SkillEventRecord,
TopicRecord,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -210,6 +211,7 @@ impl SessionStore {
ensure_sessions_schema(&conn)?; ensure_sessions_schema(&conn)?;
ensure_messages_schema(&conn)?; ensure_messages_schema(&conn)?;
ensure_scheduler_schema(&conn)?; ensure_scheduler_schema(&conn)?;
ensure_memory_scope_key_migration(&conn)?;
Ok(Self { Ok(Self {
conn: Arc::new(Mutex::new(conn)), conn: Arc::new(Mutex::new(conn)),
@ -1726,6 +1728,14 @@ fn ensure_scheduler_schema(conn: &Connection) -> Result<(), StorageError> {
Ok(()) Ok(())
} }
fn ensure_memory_scope_key_migration(conn: &Connection) -> Result<(), StorageError> {
conn.execute(
"UPDATE memories SET scope_key = 'default' WHERE scope_key != 'default'",
[],
)?;
Ok(())
}
fn has_column( fn has_column(
conn: &Connection, conn: &Connection,
table_name: &str, table_name: &str,

View File

@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// 全局统一的记忆 scope_key所有渠道共享同一份记忆空间
pub const GLOBAL_SCOPE_KEY: &str = "default";
/// 允许的记忆命名空间列表 /// 允许的记忆命名空间列表
/// ///
/// 每个命名空间代表一类记忆内容,用于分类管理和检索。 /// 每个命名空间代表一类记忆内容,用于分类管理和检索。

View File

@ -187,12 +187,8 @@ fn build_memory_upsert(
}) })
} }
fn scope_key_from_context(context: &ToolContext) -> Result<String, ToolResult> { fn scope_key_from_context(_context: &ToolContext) -> Result<String, ToolResult> {
let channel_name = context Ok(crate::storage::GLOBAL_SCOPE_KEY.to_string())
.channel_name
.as_deref()
.ok_or_else(|| error_result("memory_manage requires channel_name in tool context"))?;
Ok(channel_name.to_string())
} }
fn memory_to_json(memory: MemoryRecord) -> serde_json::Value { fn memory_to_json(memory: MemoryRecord) -> serde_json::Value {
@ -260,22 +256,26 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn test_memory_manage_requires_context() { async fn test_memory_manage_works_with_default_context() {
let store = Arc::new(SessionStore::in_memory().unwrap()); let store = Arc::new(SessionStore::in_memory().unwrap());
let tool = MemoryManageTool::new(store); let tool = MemoryManageTool::new(store);
// scope_key 已全局统一为 "default",不再依赖 channel_name
let result = tool let result = tool
.execute_with_context( .execute_with_context(
&ToolContext::default(), &ToolContext::default(),
json!({ json!({
"action": "list" "action": "put",
"namespace": "user",
"key": "language",
"content": "Rust"
}), }),
) )
.await .await
.unwrap(); .unwrap();
assert!(!result.success); assert!(result.success);
assert!(result.error.unwrap().contains("channel_name")); assert!(result.output.contains("Rust"));
} }
#[tokio::test] #[tokio::test]

View File

@ -186,12 +186,8 @@ impl Tool for MemorySearchTool {
} }
} }
fn scope_key_from_context(context: &ToolContext) -> Result<String, ToolResult> { fn scope_key_from_context(_context: &ToolContext) -> Result<String, ToolResult> {
let channel_name = context Ok(crate::storage::GLOBAL_SCOPE_KEY.to_string())
.channel_name
.as_deref()
.ok_or_else(|| error_result("memory_search requires channel_name in tool context"))?;
Ok(channel_name.to_string())
} }
fn memory_to_json(memory: MemoryRecord) -> serde_json::Value { fn memory_to_json(memory: MemoryRecord) -> serde_json::Value {
@ -234,7 +230,7 @@ mod tests {
store store
.put_memory(&crate::storage::MemoryUpsert { .put_memory(&crate::storage::MemoryUpsert {
scope_kind: "user".to_string(), scope_kind: "user".to_string(),
scope_key: TEST_CHANNEL.to_string(), scope_key: crate::storage::GLOBAL_SCOPE_KEY.to_string(),
namespace: "user".to_string(), namespace: "user".to_string(),
memory_key: "language".to_string(), memory_key: "language".to_string(),
content: "User prefers Chinese responses".to_string(), content: "User prefers Chinese responses".to_string(),
@ -250,10 +246,6 @@ mod tests {
let tool = MemorySearchTool::new(store); let tool = MemorySearchTool::new(store);
let context = ToolContext { let context = ToolContext {
channel_name: Some(TEST_CHANNEL.to_string()), channel_name: Some(TEST_CHANNEL.to_string()),
chat_id: Some("chat-1".to_string()),
session_id: Some(format!("{}:chat-1", TEST_CHANNEL)),
message_id: Some("msg-2".to_string()),
message_seq: Some(2),
..ToolContext::default() ..ToolContext::default()
}; };
@ -287,18 +279,18 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn test_memory_search_is_read_only_and_requires_context() { async fn test_memory_search_is_read_only_and_works_with_default_context() {
let store = Arc::new(SessionStore::in_memory().unwrap()); let store = Arc::new(SessionStore::in_memory().unwrap());
let tool = MemorySearchTool::new(store); let tool = MemorySearchTool::new(store);
assert!(tool.read_only()); assert!(tool.read_only());
// scope_key 已全局统一为 "default",不再依赖 channel_name
let result = tool let result = tool
.execute_with_context(&ToolContext::default(), json!({ "action": "list" })) .execute_with_context(&ToolContext::default(), json!({ "action": "list" }))
.await .await
.unwrap(); .unwrap();
assert!(!result.success); assert!(result.success);
assert!(result.error.unwrap().contains("channel_name"));
} }
#[tokio::test] #[tokio::test]