Skip to content

Commit d3d1de1

Browse files
authored
feat: add proactive example (#268)
1 parent 16ae534 commit d3d1de1

8 files changed

Lines changed: 408 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
memorize_config = {
2+
"memory_types": [
3+
"record",
4+
],
5+
"memory_type_prompts": {
6+
"record": {
7+
"objective": {
8+
"ordinal": 10,
9+
"prompt": "# Task Objective\nYou will be given a conversation between a user and an coding agent. Your goal is to extract detailed records for what are planed to do, and what have been done.",
10+
},
11+
"workflow": {
12+
"ordinal": 10,
13+
"prompt": "# Workflow\nRead through the conversation and extract records. You should expecially focus on:\n- What the user ask the agent to do\n- What plan does the agent suggest\n- What the agent has done",
14+
},
15+
"rules": {
16+
"ordinal": -1,
17+
"prompt": None,
18+
},
19+
"examples": {
20+
"ordinal": 60,
21+
"prompt": "# Example\n## Output\n<item>\n <memory>\n <content>The user ask the agent to generate a code example for fastapi</content>\n <categories>\n <category>todo</category>\n </categories>\n </memory>\n <memory>\n <content>The agent suggest to use the code example from the document</content>\n <categories>\n <category>todo</category>\n </categories>\n </memory>\n <memory>\n <content>The agent ask the user to specify the response type</content>\n <categories>\n <category>todo</category>\n </categories>\n </memory>\n</item>",
22+
},
23+
}
24+
},
25+
"memory_categories": [
26+
{
27+
"name": "todo",
28+
"description": "This file traces the latest status of the task. All records should be included in this file.",
29+
"target_length": None,
30+
"custom_prompt": {
31+
"objective": {
32+
"ordinal": 10,
33+
"prompt": "# Task Objective\nYou are a specialist in task management. You should update the markdown file to reflect the latest status of the task.",
34+
},
35+
"workflow": {
36+
"ordinal": 20,
37+
"prompt": "# Workflow\nRead through the existing markdown file and the new records. Then update the markdown file to reflect:\n- What existing tasks are completed\n- What new tasks are added\n- What tasks are still in progress",
38+
},
39+
"rules": {
40+
"ordinal": -1,
41+
"prompt": None,
42+
},
43+
"examples": {
44+
"ordinal": 50,
45+
"prompt": "# Example\n## Output\n```markdown\n# Task\n## Task Objective\nThe user ask the agent to generate a code example for fastapi\n## Workflow\nThe agent suggest to use the code example from the document\nThe agent ask the user to specify the response type\n```",
46+
},
47+
},
48+
}
49+
],
50+
}

examples/proactive/memory/local/__init__.py

Whitespace-only changes.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import json
2+
import os
3+
from pathlib import Path
4+
from typing import Any
5+
6+
import pendulum
7+
8+
from memu.app import MemoryService
9+
10+
from ..config import memorize_config
11+
12+
USER_ID = "claude_user"
13+
14+
15+
async def dump_conversation_resource(
16+
conversation_messages: list[dict[str, Any]],
17+
) -> str:
18+
resource_data = {
19+
"content": [
20+
{
21+
"role": message.get("role", "system"),
22+
"content": {"text": message.get("content", "")},
23+
"created_at": message.get("timestamp", pendulum.now().isoformat()),
24+
}
25+
for message in conversation_messages
26+
]
27+
}
28+
time_string = pendulum.now().format("YYYYMMDD_HHmmss")
29+
resource_url = Path(__file__).parent / "data" / f"conv_{time_string}.json"
30+
with open(resource_url, "w") as f:
31+
json.dump(resource_data, f, indent=4, ensure_ascii=False)
32+
return resource_url.as_posix()
33+
34+
35+
async def memorize(conversation_messages: list[dict[str, Any]]) -> str | None:
36+
api_key = os.getenv("OPENAI_API_KEY")
37+
if not api_key:
38+
msg = "Please set OPENAI_API_KEY environment variable"
39+
raise ValueError(msg)
40+
41+
memory_service = MemoryService(
42+
llm_profiles={
43+
"default": {
44+
"api_key": api_key,
45+
"chat_model": "gpt-4o-mini",
46+
},
47+
},
48+
memorize_config=memorize_config,
49+
)
50+
51+
resource_url = await dump_conversation_resource(conversation_messages)
52+
result = await memory_service.memorize(
53+
resource_url=resource_url, modality="conversation", user={"user_id": USER_ID}
54+
)
55+
return result
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import os
2+
from typing import Any
3+
4+
from claude_agent_sdk import create_sdk_mcp_server, tool
5+
6+
from memu.app import MemoryService
7+
8+
USER_ID = "claude_user"
9+
10+
11+
@tool("memu_memory", "Retrieve memory based on a query", {"query": str})
12+
async def get_memory(args: dict[str, Any]) -> dict[str, Any]:
13+
"""Retrieve memory from the memory API based on the provided query."""
14+
query = {"role": "user", "content": args["query"]}
15+
16+
api_key = os.getenv("OPENAI_API_KEY")
17+
if not api_key:
18+
msg = "Please set OPENAI_API_KEY environment variable"
19+
raise ValueError(msg)
20+
21+
memory_service = MemoryService(
22+
llm_profiles={
23+
"default": {
24+
"api_key": api_key,
25+
"chat_model": "gpt-4o-mini",
26+
},
27+
},
28+
retrieve_config={
29+
"method": "rag",
30+
"route_intention": False,
31+
"sufficiency_check": False,
32+
"category": {
33+
"enabled": False,
34+
},
35+
"item": {
36+
"enabled": True,
37+
"top_k": 10,
38+
},
39+
"resource": {
40+
"enabled": False,
41+
},
42+
},
43+
)
44+
45+
result = await memory_service.retrieve(query, where={"user_id": USER_ID})
46+
47+
return {"content": [{"type": "text", "text": str(result)}]}
48+
49+
50+
async def _get_todos() -> str:
51+
memory_service = MemoryService()
52+
53+
result = await memory_service.list_memory_categories(where={"user_id": USER_ID})
54+
55+
categories = result["categories"]
56+
todos = ""
57+
for category in categories:
58+
if category["name"] == "todo":
59+
todos = category["summary"]
60+
return todos
61+
62+
63+
@tool("memu_todos", "Retrieve todos for the user", {})
64+
async def get_todos() -> dict[str, Any]:
65+
"""Retrieve todos from the memory API."""
66+
todos = await _get_todos()
67+
return {"content": [{"type": "text", "text": str(todos)}]}
68+
69+
70+
memu_server = create_sdk_mcp_server(name="memu", version="1.0.0", tools=[get_memory, get_todos])

examples/proactive/memory/platform/__init__.py

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Any
2+
3+
import aiohttp
4+
5+
from ..config import memorize_config
6+
7+
BASE_URL = "https://api.memu.so"
8+
API_KEY = "your memu api key"
9+
USER_ID = "claude_user"
10+
AGENT_ID = "claude_agent"
11+
12+
13+
async def memorize(conversation_messages: list[dict[str, Any]]) -> str | None:
14+
payload = {
15+
"conversation": conversation_messages,
16+
"user_id": USER_ID,
17+
"agent_id": AGENT_ID,
18+
"override_config": memorize_config,
19+
}
20+
21+
async with (
22+
aiohttp.ClientSession() as session,
23+
session.post(
24+
f"{BASE_URL}/api/v3/memory/memorize",
25+
headers={"Authorization": f"Bearer {API_KEY}"},
26+
json=payload,
27+
) as response,
28+
):
29+
result = await response.json()
30+
task_id = result["task_id"]
31+
return task_id
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from typing import Any
2+
3+
import aiohttp
4+
from claude_agent_sdk import create_sdk_mcp_server, tool
5+
6+
BASE_URL = "https://api.memu.so"
7+
API_KEY = "your memu api key"
8+
USER_ID = "claude_user"
9+
AGENT_ID = "claude_agent"
10+
11+
12+
@tool("memu_memory", "Retrieve memory based on a query", {"query": str})
13+
async def get_memory(args: dict[str, Any]) -> dict[str, Any]:
14+
"""Retrieve memory from the memory API based on the provided query."""
15+
query = args["query"]
16+
url = f"{BASE_URL}/api/v3/memory/retrieve"
17+
headers = {"Authorization": f"Bearer {API_KEY}"}
18+
data = {"user_id": USER_ID, "agent_id": AGENT_ID, "query": query}
19+
20+
async with aiohttp.ClientSession() as session, session.post(url, headers=headers, json=data) as response:
21+
result = await response.json()
22+
23+
return {"content": [{"type": "text", "text": str(result)}]}
24+
25+
26+
async def _get_todos() -> str:
27+
url = f"{BASE_URL}/api/v3/memory/categories"
28+
headers = {"Authorization": f"Bearer {API_KEY}"}
29+
data = {
30+
"user_id": USER_ID,
31+
"agent_id": AGENT_ID,
32+
}
33+
async with aiohttp.ClientSession() as session, session.post(url, headers=headers, json=data) as response:
34+
result = await response.json()
35+
36+
categories = result["categories"]
37+
todos = ""
38+
for category in categories:
39+
if category["name"] == "todo":
40+
todos = category["summary"]
41+
return todos
42+
43+
44+
@tool("memu_todos", "Retrieve todos for the user", {})
45+
async def get_todos() -> dict[str, Any]:
46+
"""Retrieve todos from the memory API."""
47+
todos = await _get_todos()
48+
return {"content": [{"type": "text", "text": str(todos)}]}
49+
50+
51+
# Create the MCP server with the tool
52+
memu_server = create_sdk_mcp_server(name="memu", version="1.0.0", tools=[get_memory, get_todos])

0 commit comments

Comments
 (0)