feat: 经验复用 MCP 暴露——项目记忆列/建/废弃(注入与提取已存在,不重复造)
This commit is contained in:
1 parent
c10fda4e4a
commit
60283d55fc
2 files changed
+66
No files matched your search
@@ -106,6 +106,17 @@ TOOLS = [
|
||||
{"project_id": {"type": "string"}}, ["project_id"]),
|
||||
_tool("get_models", "取模型一览(价目与默认模型,服务端口径)。",
|
||||
{}, []),
|
||||
_tool("list_project_memories", "列出某项目的项目级记忆(此前任务沉淀的经验/约定,新任务会自动注入;top_k 可选,默认 20)。",
|
||||
{"project_id": {"type": "string"},
|
||||
"top_k": {"type": "integer"}}, ["project_id"]),
|
||||
_tool("create_project_memory", "给某项目记一条项目级记忆(kind 与 content 必填)。",
|
||||
{"project_id": {"type": "string"},
|
||||
"kind": {"type": "string"},
|
||||
"content": {"type": "string"}}, ["project_id", "kind", "content"]),
|
||||
_tool("supersede_project_memory", "废弃某项目的一条记忆,可同时给替代内容(不传则只废弃)。",
|
||||
{"project_id": {"type": "string"},
|
||||
"memory_id": {"type": "string"},
|
||||
"replacement_content": {"type": "string"}}, ["project_id", "memory_id"]),
|
||||
]
|
||||
|
||||
|
||||
@@ -239,6 +250,32 @@ class MCPServer:
|
||||
"GET", "/projects/" + args["project_id"] + "/members"))
|
||||
if name == "get_models":
|
||||
return self._wrap(*self.transport("GET", "/models"))
|
||||
if name == "list_project_memories":
|
||||
if not args.get("project_id"):
|
||||
return True, {"error": "project_id required"}
|
||||
path = "/projects/" + args["project_id"] + "/identity-memories"
|
||||
if args.get("top_k") is not None:
|
||||
path += "?" + urllib.parse.urlencode(
|
||||
{"top_k": args["top_k"]})
|
||||
return self._wrap(*self.transport("GET", path))
|
||||
if name == "create_project_memory":
|
||||
if not args.get("project_id") or not args.get("kind") \
|
||||
or not args.get("content"):
|
||||
return True, {"error": "project_id, kind and content required"}
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/projects/" + args["project_id"]
|
||||
+ "/identity-memories",
|
||||
{"kind": args["kind"], "content": args["content"]}))
|
||||
if name == "supersede_project_memory":
|
||||
if not args.get("project_id") or not args.get("memory_id"):
|
||||
return True, {"error": "project_id and memory_id required"}
|
||||
body = {}
|
||||
if args.get("replacement_content") is not None:
|
||||
body["replacement_content"] = args["replacement_content"]
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/projects/" + args["project_id"]
|
||||
+ "/identity-memories/" + args["memory_id"] + "/supersede",
|
||||
body))
|
||||
return True, {"error": "unknown tool: %s" % name}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -65,6 +65,13 @@ class FakeTransport:
|
||||
return 200, {"members": []}
|
||||
if path == "/models":
|
||||
return 200, {"models": []}
|
||||
if path == "/projects/p1/identity-memories":
|
||||
return 200, {"memories": [{"id": "m1", "content": "x"}]}
|
||||
if path == "/projects/p1/identity-memories?top_k=3":
|
||||
return 200, {"memories": []}
|
||||
if path == "/projects/p1/identity-memories/m1/supersede" \
|
||||
and method == "POST":
|
||||
return 200, {"id": "m1", "status": "superseded"}
|
||||
if path == "/tasks/nope":
|
||||
return 404, {"error": "not found"}
|
||||
return 400, {"error": "bad request"}
|
||||
@@ -179,6 +186,28 @@ class MCPServerTestCase(unittest.TestCase):
|
||||
self.assertFalse(is_error, name)
|
||||
self.assertEqual(self.transport.calls[-1][:2], expected, name)
|
||||
is_error, _ = self.server.call_tool("create_followup", {"task_id": "t1"})
|
||||
def test_project_memory_tools_translate_to_rest(self):
|
||||
is_error, payload = self.server.call_tool(
|
||||
"list_project_memories", {"project_id": "p1", "top_k": 3})
|
||||
self.assertFalse(is_error)
|
||||
self.assertEqual(self.transport.calls[-1][:2],
|
||||
("GET", "/projects/p1/identity-memories?top_k=3"))
|
||||
is_error, _ = self.server.call_tool(
|
||||
"create_project_memory",
|
||||
{"project_id": "p1", "kind": "convention", "content": "先跑测试"})
|
||||
self.assertFalse(is_error)
|
||||
self.assertEqual(self.transport.calls[-1][:2],
|
||||
("POST", "/projects/p1/identity-memories"))
|
||||
is_error, _ = self.server.call_tool(
|
||||
"supersede_project_memory",
|
||||
{"project_id": "p1", "memory_id": "m1",
|
||||
"replacement_content": "新约定"})
|
||||
self.assertFalse(is_error)
|
||||
self.assertEqual(self.transport.calls[-1][:2],
|
||||
("POST",
|
||||
"/projects/p1/identity-memories/m1/supersede"))
|
||||
is_error, _ = self.server.call_tool(
|
||||
"create_project_memory", {"project_id": "p1", "kind": "k"})
|
||||
self.assertTrue(is_error)
|
||||
|
||||
def test_missing_arg_is_error_not_rest_call(self):
|
||||
|
||||
Reference in new issue
Block a user