feat: connect memory tools to workspace authority
This commit is contained in:
@@ -12,9 +12,9 @@ use llm_engine::tool::{
|
||||
};
|
||||
use memory::backend::{
|
||||
MemoryBackendHttpResponse, MemoryBackendOperation, MemoryBackendOperationResult,
|
||||
MemoryConsolidateStagingOperation, MemoryConsolidationOutput, MemoryDeleteOperation,
|
||||
MemoryEditOperation, MemoryQueryOperation, MemoryReadOperation, MemoryStagingCloseOperation,
|
||||
MemoryStagingListOperation, MemoryStagingReadOperation, MemoryToolOutput, MemoryWriteOperation,
|
||||
MemoryConsolidateStagingOperation, MemoryConsolidationOutput, MemoryDocumentReadOperation,
|
||||
MemoryDocumentUpdateOperation, MemoryQueryOperation, MemoryStagingCloseOperation,
|
||||
MemoryStagingListOperation, MemoryStagingReadOperation, MemoryToolOutput,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::de::DeserializeOwned;
|
||||
@@ -175,47 +175,29 @@ pub fn workspace_http_memory_tools(
|
||||
let backend = WorkspaceHttpMemoryBackend::new(workspace_id, base_url);
|
||||
vec![
|
||||
memory_tool(
|
||||
"MemoryRead",
|
||||
READ_DESCRIPTION,
|
||||
read_schema(),
|
||||
"MemoryReadDocument",
|
||||
READ_DOCUMENT_DESCRIPTION,
|
||||
document_read_schema(),
|
||||
backend.clone(),
|
||||
|input| {
|
||||
Ok(MemoryBackendOperation::Read(parse_input::<
|
||||
MemoryReadOperation,
|
||||
>(input)?))
|
||||
Ok(MemoryBackendOperation::ReadDocument(parse_input::<
|
||||
MemoryDocumentReadOperation,
|
||||
>(
|
||||
input
|
||||
)?))
|
||||
},
|
||||
),
|
||||
memory_tool(
|
||||
"MemoryWrite",
|
||||
WRITE_DESCRIPTION,
|
||||
write_schema(),
|
||||
"MemoryUpdateDocument",
|
||||
UPDATE_DOCUMENT_DESCRIPTION,
|
||||
document_update_schema(),
|
||||
backend.clone(),
|
||||
|input| {
|
||||
Ok(MemoryBackendOperation::Write(parse_input::<
|
||||
MemoryWriteOperation,
|
||||
>(input)?))
|
||||
},
|
||||
),
|
||||
memory_tool(
|
||||
"MemoryEdit",
|
||||
EDIT_DESCRIPTION,
|
||||
edit_schema(),
|
||||
backend.clone(),
|
||||
|input| {
|
||||
Ok(MemoryBackendOperation::Edit(parse_input::<
|
||||
MemoryEditOperation,
|
||||
>(input)?))
|
||||
},
|
||||
),
|
||||
memory_tool(
|
||||
"MemoryDelete",
|
||||
DELETE_DESCRIPTION,
|
||||
delete_schema(),
|
||||
backend.clone(),
|
||||
|input| {
|
||||
Ok(MemoryBackendOperation::Delete(parse_input::<
|
||||
MemoryDeleteOperation,
|
||||
>(input)?))
|
||||
Ok(MemoryBackendOperation::UpdateDocument(parse_input::<
|
||||
MemoryDocumentUpdateOperation,
|
||||
>(
|
||||
input
|
||||
)?))
|
||||
},
|
||||
),
|
||||
memory_tool(
|
||||
@@ -339,72 +321,34 @@ fn tool_output(output: MemoryToolOutput) -> ToolOutput {
|
||||
}
|
||||
}
|
||||
|
||||
const READ_DESCRIPTION: &str = "Read a durable memory record through Workspace authority.";
|
||||
const WRITE_DESCRIPTION: &str =
|
||||
"Create or overwrite a durable memory record through Workspace authority.";
|
||||
const EDIT_DESCRIPTION: &str =
|
||||
"Replace text in a durable memory record through Workspace authority.";
|
||||
const DELETE_DESCRIPTION: &str = "Delete a durable memory record through Workspace authority.";
|
||||
const QUERY_DESCRIPTION: &str = "Query durable memory records through Workspace authority.";
|
||||
const READ_DOCUMENT_DESCRIPTION: &str =
|
||||
"Read the Workspace memory Markdown document through Workspace authority.";
|
||||
const UPDATE_DOCUMENT_DESCRIPTION: &str =
|
||||
"Replace the Workspace memory Markdown document through Workspace authority.";
|
||||
const QUERY_DESCRIPTION: &str = "Query the Workspace memory document through Workspace authority.";
|
||||
const STAGING_LIST_DESCRIPTION: &str =
|
||||
"List pending Memory staging candidates without loading full record payloads.";
|
||||
const STAGING_READ_DESCRIPTION: &str = "Read one pending Memory staging candidate by candidate_id.";
|
||||
const STAGING_CLOSE_DESCRIPTION: &str = "Close one staging candidate with a required reason; records disposition and deletes the staging record.";
|
||||
|
||||
fn kind_schema() -> serde_json::Value {
|
||||
json!({"type":"string","enum":["summary","decision","request"]})
|
||||
}
|
||||
|
||||
fn read_schema() -> serde_json::Value {
|
||||
fn document_read_schema() -> serde_json::Value {
|
||||
json!({
|
||||
"type":"object",
|
||||
"additionalProperties": false,
|
||||
"required":["kind"],
|
||||
"properties":{
|
||||
"kind": kind_schema(),
|
||||
"slug":{"type":["string","null"]},
|
||||
"offset":{"type":["integer","null"],"minimum":0},
|
||||
"limit":{"type":["integer","null"],"minimum":0}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn write_schema() -> serde_json::Value {
|
||||
fn document_update_schema() -> serde_json::Value {
|
||||
json!({
|
||||
"type":"object",
|
||||
"additionalProperties": false,
|
||||
"required":["kind","content"],
|
||||
"required":["body_md"],
|
||||
"properties":{
|
||||
"kind": kind_schema(),
|
||||
"slug":{"type":["string","null"]},
|
||||
"content":{"type":"string"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn edit_schema() -> serde_json::Value {
|
||||
json!({
|
||||
"type":"object",
|
||||
"additionalProperties": false,
|
||||
"required":["kind","old_string","new_string"],
|
||||
"properties":{
|
||||
"kind": kind_schema(),
|
||||
"slug":{"type":["string","null"]},
|
||||
"old_string":{"type":"string"},
|
||||
"new_string":{"type":"string"},
|
||||
"replace_all":{"type":"boolean","default":false}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_schema() -> serde_json::Value {
|
||||
json!({
|
||||
"type":"object",
|
||||
"additionalProperties": false,
|
||||
"required":["kind"],
|
||||
"properties":{
|
||||
"kind": kind_schema(),
|
||||
"slug":{"type":["string","null"]}
|
||||
"body_md":{"type":"string"}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -441,6 +385,12 @@ mod tests {
|
||||
));
|
||||
|
||||
assert!(names.contains(&"MemoryQuery".to_string()));
|
||||
assert!(names.contains(&"MemoryReadDocument".to_string()));
|
||||
assert!(names.contains(&"MemoryUpdateDocument".to_string()));
|
||||
assert!(!names.contains(&"MemoryRead".to_string()));
|
||||
assert!(!names.contains(&"MemoryWrite".to_string()));
|
||||
assert!(!names.contains(&"MemoryEdit".to_string()));
|
||||
assert!(!names.contains(&"MemoryDelete".to_string()));
|
||||
assert!(!names.contains(&"MemoryStagingList".to_string()));
|
||||
assert!(!names.contains(&"MemoryStagingRead".to_string()));
|
||||
assert!(!names.contains(&"MemoryStagingClose".to_string()));
|
||||
@@ -454,6 +404,8 @@ mod tests {
|
||||
));
|
||||
|
||||
assert!(names.contains(&"MemoryQuery".to_string()));
|
||||
assert!(names.contains(&"MemoryReadDocument".to_string()));
|
||||
assert!(names.contains(&"MemoryUpdateDocument".to_string()));
|
||||
assert!(names.contains(&"MemoryStagingList".to_string()));
|
||||
assert!(names.contains(&"MemoryStagingRead".to_string()));
|
||||
assert!(names.contains(&"MemoryStagingClose".to_string()));
|
||||
|
||||
@@ -201,10 +201,8 @@ impl<'a> SystemPromptContext<'a> {
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
struct ToolCapabilities {
|
||||
memory_query: bool,
|
||||
memory_read: bool,
|
||||
memory_write: bool,
|
||||
memory_edit: bool,
|
||||
memory_delete: bool,
|
||||
memory_read_document: bool,
|
||||
memory_update_document: bool,
|
||||
worker_spawn: bool,
|
||||
worker_send: bool,
|
||||
worker_read_output: bool,
|
||||
@@ -219,10 +217,8 @@ impl ToolCapabilities {
|
||||
for name in names {
|
||||
match name.as_str() {
|
||||
"MemoryQuery" => capabilities.memory_query = true,
|
||||
"MemoryRead" => capabilities.memory_read = true,
|
||||
"MemoryWrite" => capabilities.memory_write = true,
|
||||
"MemoryEdit" => capabilities.memory_edit = true,
|
||||
"MemoryDelete" => capabilities.memory_delete = true,
|
||||
"MemoryReadDocument" => capabilities.memory_read_document = true,
|
||||
"MemoryUpdateDocument" => capabilities.memory_update_document = true,
|
||||
"SpawnWorker" => capabilities.worker_spawn = true,
|
||||
"SendToWorker" => capabilities.worker_send = true,
|
||||
"ReadWorkerOutput" => capabilities.worker_read_output = true,
|
||||
@@ -236,11 +232,7 @@ impl ToolCapabilities {
|
||||
}
|
||||
|
||||
fn memory_records(self) -> bool {
|
||||
self.memory_query
|
||||
|| self.memory_read
|
||||
|| self.memory_write
|
||||
|| self.memory_edit
|
||||
|| self.memory_delete
|
||||
self.memory_query || self.memory_read_document || self.memory_update_document
|
||||
}
|
||||
|
||||
fn memory_any(self) -> bool {
|
||||
@@ -248,7 +240,7 @@ impl ToolCapabilities {
|
||||
}
|
||||
|
||||
fn memory_mutation(self) -> bool {
|
||||
self.memory_write || self.memory_edit || self.memory_delete
|
||||
self.memory_update_document
|
||||
}
|
||||
|
||||
fn worker_management(self) -> bool {
|
||||
@@ -265,10 +257,14 @@ impl ToolCapabilities {
|
||||
map.insert("memory_any", Value::from(self.memory_any()));
|
||||
map.insert("memory_records", Value::from(self.memory_records()));
|
||||
map.insert("memory_query", Value::from(self.memory_query));
|
||||
map.insert("memory_read", Value::from(self.memory_read));
|
||||
map.insert("memory_write", Value::from(self.memory_write));
|
||||
map.insert("memory_edit", Value::from(self.memory_edit));
|
||||
map.insert("memory_delete", Value::from(self.memory_delete));
|
||||
map.insert(
|
||||
"memory_read_document",
|
||||
Value::from(self.memory_read_document),
|
||||
);
|
||||
map.insert(
|
||||
"memory_update_document",
|
||||
Value::from(self.memory_update_document),
|
||||
);
|
||||
map.insert("memory_mutation", Value::from(self.memory_mutation()));
|
||||
map.insert("worker_management", Value::from(self.worker_management()));
|
||||
Value::from(map)
|
||||
@@ -407,16 +403,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn memory_tool_names() -> Vec<String> {
|
||||
[
|
||||
"MemoryQuery",
|
||||
"MemoryRead",
|
||||
"MemoryWrite",
|
||||
"MemoryEdit",
|
||||
"MemoryDelete",
|
||||
]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
["MemoryQuery", "MemoryReadDocument", "MemoryUpdateDocument"]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn worker_management_tool_names() -> Vec<String> {
|
||||
@@ -463,9 +453,9 @@ mod tests {
|
||||
assert!(rendered.contains("### Memory"));
|
||||
assert!(rendered.contains("small targeted `MemoryQuery`"));
|
||||
assert!(rendered.contains("Strong lookup triggers include"));
|
||||
assert!(rendered.contains("MemoryRead(kind=summary)"));
|
||||
assert!(rendered.contains("MemoryReadDocument"));
|
||||
assert!(rendered.contains("Do not query memory every turn"));
|
||||
assert!(rendered.contains("MemoryWrite"));
|
||||
assert!(rendered.contains("MemoryUpdateDocument"));
|
||||
assert!(rendered.contains("## Language"));
|
||||
assert!(rendered.contains("`language`: `match the user's language"));
|
||||
// Trailing section must be present.
|
||||
@@ -508,15 +498,15 @@ mod tests {
|
||||
.render(&ctx(
|
||||
dir.path(),
|
||||
&scope,
|
||||
vec!["MemoryQuery".into(), "MemoryRead".into()],
|
||||
vec!["MemoryQuery".into(), "MemoryReadDocument".into()],
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert!(rendered.contains("### Memory"));
|
||||
assert!(rendered.contains("small targeted `MemoryQuery`"));
|
||||
assert!(rendered.contains("MemoryRead(kind=summary)"));
|
||||
assert!(!rendered.contains("MemoryWrite"));
|
||||
assert!(rendered.contains("MemoryReadDocument"));
|
||||
assert!(!rendered.contains("MemoryUpdateDocument"));
|
||||
assert!(!rendered.contains("MemoryEdit"));
|
||||
assert!(!rendered.contains("MemoryDelete"));
|
||||
}
|
||||
@@ -555,7 +545,7 @@ mod tests {
|
||||
.render(&ctx(
|
||||
dir.path(),
|
||||
&scope,
|
||||
vec!["Read".into(), "Edit".into(), "MemoryRead".into()],
|
||||
vec!["Read".into(), "Edit".into(), "MemoryReadDocument".into()],
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user