Repository navigation
feat: integrate memsearch for agent memory system #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Memsearch MCP Service package.""" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,153 @@ | ||||||
| import json | ||||||
| import os | ||||||
| import sys | ||||||
| import asyncio | ||||||
| import logging | ||||||
| from pathlib import Path | ||||||
|
|
||||||
| # Suppress verbose gRPC / absl warnings from milvus-lite | ||||||
| os.environ["GRPC_VERBOSITY"] = "ERROR" | ||||||
| os.environ["GLOG_minloglevel"] = "2" | ||||||
| logging.getLogger("absl").setLevel(logging.ERROR) | ||||||
|
|
||||||
| def _ensure_proto_gen() -> None: | ||||||
| d = Path(__file__).resolve().parent | ||||||
| while d.parent != d: | ||||||
| pg = d / "proto_gen" | ||||||
| if pg.is_dir() and (pg / "robonix_runtime_pb2.py").exists(): | ||||||
| sys.path.insert(0, str(pg)) | ||||||
| return | ||||||
| d = d.parent | ||||||
|
|
||||||
| _ensure_proto_gen() | ||||||
| import grpc | ||||||
| import robonix_runtime_pb2 as pb | ||||||
| import robonix_runtime_pb2_grpc as pb_grpc | ||||||
|
|
||||||
| from mcp.server.fastmcp import FastMCP | ||||||
| from memsearch import MemSearch | ||||||
|
|
||||||
| mcp = FastMCP("memsearch_provider") | ||||||
|
|
||||||
| # Initialize MemSearch | ||||||
| MEMORY_DIR = os.environ.get("AGENT_MEMORY_DIR", "./agent_memory") | ||||||
| MILVUS_URI = os.environ.get("AGENT_MILVUS_URI", "./agent_milvus.db") | ||||||
|
|
||||||
| mem = MemSearch( | ||||||
| paths=[MEMORY_DIR], | ||||||
| embedding_provider="onnx", | ||||||
| milvus_uri=MILVUS_URI | ||||||
| ) | ||||||
|
|
||||||
| @mcp.tool() | ||||||
| async def search_memory(query: str) -> str: | ||||||
| """Search the agent's long-term memory for relevant past context, decisions, or user preferences.""" | ||||||
| results = await mem.search(query, top_k=2) | ||||||
| if not results: | ||||||
| return "No relevant memories found." | ||||||
| context = "\n\n".join(f"- {m['content']}" for m in results) | ||||||
| return f"Relevant memories:\n{context}" | ||||||
|
|
||||||
| @mcp.tool() | ||||||
| async def compact_memory() -> str: | ||||||
| """Compact and summarize recent memories. Call this at the end of a session.""" | ||||||
| # Note: requires llm_provider setup for compacting, defaulting to openai if not set | ||||||
| try: | ||||||
| summary_path = await mem.compact(llm_provider="openai") | ||||||
| return f"Memory compacted successfully to {summary_path}." | ||||||
| except Exception as e: | ||||||
| return f"Failed to compact memory: {e}" | ||||||
|
|
||||||
| @mcp.tool() | ||||||
| async def save_memory(content: str) -> str: | ||||||
| """Save an important fact, user preference, or decision to long-term memory.""" | ||||||
| from datetime import date | ||||||
| p = Path(MEMORY_DIR) / f"{date.today()}_notes.md" | ||||||
| p.parent.mkdir(parents=True, exist_ok=True) | ||||||
| with open(p, "a") as f: | ||||||
|
||||||
| with open(p, "a") as f: | |
| with open(p, "a", encoding="utf-8") as f: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| mcp | ||
| fastmcp | ||
| memsearch[onnx] | ||
| grpcio | ||
| protobuf | ||
| uvicorn |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||
| manifestVersion: 1 | ||||||
|
|
||||||
| build: | ||||||
| script: scripts/build.sh | ||||||
|
|
||||||
| package: | ||||||
| id: com.robonix.example.memsearch_service | ||||||
| name: memsearch_service | ||||||
| version: 0.1.0 | ||||||
| vendor: robonix | ||||||
| description: Memsearch MCP Server for Agent memory | ||||||
| license: MulanPSL-2.0 | ||||||
|
|
||||||
| nodes: | ||||||
| - id: com.robonix.services.memsearch | ||||||
| type: python | ||||||
| start: | | ||||||
| export PYTHONPATH="$(pwd):$(pwd)/../../proto_gen:${PYTHONPATH:-}" | ||||||
| # FastMCP internal logs can be verbose, redirect them to a file or suppress | ||||||
| exec python -m memsearch_service.service > memsearch.log 2>&1 | ||||||
|
||||||
| exec python -m memsearch_service.service > memsearch.log 2>&1 | |
| exec "${PYTHON_EXEC:-python3}" -m memsearch_service.service > memsearch.log 2>&1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MulanPSL-2.0 | ||
| set -euo pipefail | ||
| PKG="${RBNX_PACKAGE_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" | ||
| if [[ "${RBNX_BUILD_CLEAN:-}" == "1" ]]; then | ||
| rm -rf "$PKG/rbnx-build" | ||
| fi | ||
| EXAMPLES="$(cd "$PKG/../.." && pwd)" | ||
| PROTO_GEN="$EXAMPLES/proto_gen" | ||
| mkdir -p "$PKG/rbnx-build/ws/install" | ||
| cat >"$PKG/rbnx-build/ws/install/setup.bash" <<EOF | ||
| export PYTHONPATH="$PKG:$PROTO_GEN:\${PYTHONPATH:-}" | ||
| EOF |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,4 +16,6 @@ package: | |||||
| nodes: | ||||||
| - id: com.robonix.services.vlm | ||||||
| type: python | ||||||
| start: exec python3 -m vlm_service.service | ||||||
| start: | | ||||||
| export PYTHONPATH="$(pwd):$(pwd)/../../proto_gen:${PYTHONPATH:-}" | ||||||
| exec python -m vlm_service.service | ||||||
|
||||||
| exec python -m vlm_service.service | |
| exec "${PYTHON:-python3}" -m vlm_service.service |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,17 @@ | ||
| # Robonix examples (vlm_service, tiago_node, smoke scripts). | ||
| # grpcio-tools requires protobuf<7 (see https://github.com/grpc/grpc/issues/39750 — tooling lags protobuf 7). | ||
| # proto_gen/*_grpc.py is generated with grpcio>=1.78; older grpcio raises RuntimeError at import. | ||
| grpcio>=1.78.0 | ||
| grpcio-tools>=1.78.0 | ||
| # proto_gen/*_grpc.py is generated with grpcio>=1.78; older grpcio raises RuntimeError at import.# Core | ||
| grpcio>=1.60.0 | ||
| grpcio-tools>=1.60.0 | ||
| protobuf>=6.30,<7 | ||
| openai>=1.0 | ||
| mcp>=1.0 | ||
| jinja2 | ||
| typeguard | ||
|
|
||
| # Memory Service | ||
| fastmcp>=0.1.0 | ||
| mcp>=1.0.0 | ||
| memsearch[onnx] | ||
| numpy | ||
| Pillow | ||
| uvicorn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compact_memory hardcodes llm_provider="openai"; if openai isn’t installed/configured this tool will always fail (currently only returning an exception string). Consider either adding an explicit dependency/config requirement or detecting missing configuration and returning a clearer actionable message.