Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.

refactor: memory CLI uses REST endpoints instead of direct handler imports#587

Merged
zonk1024 merged 1 commit intomainfrom
refactor/memory-rest-endpoints
Feb 28, 2026
Merged

refactor: memory CLI uses REST endpoints instead of direct handler imports#587
zonk1024 merged 1 commit intomainfrom
refactor/memory-rest-endpoints

Conversation

@zonk1024
Copy link
Contributor

Summary

Adds REST endpoints for all memory operations and refactors the CLI to use them via HTTP instead of importing MCP handlers directly.

Changes

  • New: src/valence/server/endpoints/memory.py — REST endpoints for store, list, search, status, forget
  • New: memory_list() handler function extracted from CLI into mcp/handlers/memory.py
  • Refactored: All memory CLI commands now use ValenceClient HTTP calls
  • Updated: CLI tests mock get_client instead of direct handler imports
  • Routes: Registered 5 new routes in app.py

Architecture

Before: Plugin → CLI → (direct handler import) → DB
After: Plugin → CLI → REST → handlers → DB

Testing

1716 passed, 0 failed

Closes ourochronos/tracking#74

…ports

Adds REST endpoints for memory operations:
  POST   /api/v1/memory             — store
  GET    /api/v1/memory             — list
  GET    /api/v1/memory/search      — recall/search
  GET    /api/v1/memory/status      — statistics
  POST   /api/v1/memory/{id}/forget — soft-delete

Refactors all memory CLI commands (import, import-dir, list, search,
store, forget, status) to use ValenceClient HTTP calls instead of
importing MCP handlers directly.

Also extracts memory_list() into a reusable handler function.

Architecture: Plugin → CLI → REST → handlers → DB
Closes #74
Copilot AI review requested due to automatic review settings February 28, 2026 22:24
@zonk1024 zonk1024 merged commit 0c96716 into main Feb 28, 2026
5 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces first-class REST API endpoints for “memory” operations and refactors the CLI to call the server over HTTP (via ValenceClient) instead of importing MCP handler functions directly, aligning memory workflows with the rest of the Valence CLI/server architecture.

Changes:

  • Added src/valence/server/endpoints/memory.py implementing REST endpoints for store/list/search/status/forget.
  • Extracted memory_list() into src/valence/mcp/handlers/memory.py for reuse by REST and other callers.
  • Refactored src/valence/cli/commands/memory.py and updated tests/cli/test_memory.py to use mocked HTTP client calls.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/cli/test_memory.py Updates CLI tests to mock get_client and assert REST-style calls/params.
src/valence/server/endpoints/memory.py New REST endpoints for memory operations with auth/scope checks.
src/valence/server/app.py Registers new /api/v1/memory* routes.
src/valence/mcp/handlers/memory.py Adds memory_list() DB-backed handler used by the REST list endpoint.
src/valence/cli/commands/memory.py Refactors memory CLI commands to call REST endpoints via ValenceClient.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


output_result(result)
return 0 if result.get("success") else 1
return 0 if result.get("success", True) else 1
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using result.get("success", True) defaults missing/invalid responses to success. Since the CLI HTTP client already raises for HTTP error codes, and the REST endpoints are expected to return a JSON body with success, it's safer to either (a) treat a missing success key as failure (default False) or (b) just return 0 after output_result(result) and rely on HTTP status handling for errors. The current default-True can hide unexpected server responses.

Suggested change
return 0 if result.get("success", True) else 1
return 0 if result.get("success", False) else 1

Copilot uses AI. Check for mistakes.
Comment on lines +245 to 249
result = client.post("/memory", body=body)

if result.get("success"):
if result.get("success", True):
imported.append(
{
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if result.get("success", True): treats missing success as a successful import, which can incorrectly count failures as imported when the server returns an unexpected payload shape. Consider checking result.get("success") is True (or defaulting to False) so only explicit successes are counted as imported.

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +75
async def memory_store_endpoint(request: Request) -> JSONResponse:
"""POST /api/v1/memory — Store a memory."""
err, _ = _require_auth_write(request)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds new REST endpoints for memory operations, but there don't appear to be corresponding server endpoint tests under tests/server/ (other endpoint modules like parity/session/substrate have dedicated tests). Adding coverage for auth (401/403), basic success paths, and validation/error cases (e.g., missing query/content, invalid limit/min_confidence) would help prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +10
POST /api/v1/memory — store a memory
GET /api/v1/memory/search — recall memories
GET /api/v1/memory/status — memory system stats
POST /api/v1/memory/{id}/forget — soft-delete a memory
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The routes list in the module docstring is missing the GET /api/v1/memory list endpoint, and the forget path placeholder uses {id} while the actual route uses {memory_id}. Update the docstring so it accurately reflects the implemented routes/parameters.

Suggested change
POST /api/v1/memorystore a memory
GET /api/v1/memory/searchrecall memories
GET /api/v1/memory/statusmemory system stats
POST /api/v1/memory/{id}/forgetsoft-delete a memory
POST /api/v1/memorystore a memory
GET /api/v1/memorylist memories
GET /api/v1/memory/searchrecall memories
GET /api/v1/memory/statusmemory system stats
POST /api/v1/memory/{memory_id}/forgetsoft-delete a memory

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +121
limit = int(request.query_params.get("limit", "5"))
min_confidence_str = request.query_params.get("min_confidence")
min_confidence = float(min_confidence_str) if min_confidence_str else None
tags_str = request.query_params.get("tags")
tags = tags_str.split(",") if tags_str else None
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

limit = int(...) / float(...) parsing will raise on malformed query params (e.g., ?limit=foo), which then gets converted into a 500 via the broad except Exception. Other endpoints use endpoint_utils._parse_int/_parse_float to parse safely and cap values; consider using those helpers here (and optionally enforce min/max bounds) so bad inputs return a 400-style validation response or at least a safe default.

Copilot uses AI. Check for mistakes.
tags=tags,
)

return _json_response(result)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint always returns HTTP 200 even when memory_recall() returns {"success": False, ...} (e.g., if the underlying knowledge search fails). For consistency with other REST endpoints (and with memory_store_endpoint/memory_forget_endpoint in this same module), set the response status code based on result.get("success") (200 vs 400).

Suggested change
return _json_response(result)
status_code = 200 if result.get("success") else 400
return _json_response(result, status_code=status_code)

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +200
limit = int(request.query_params.get("limit", "20"))
tags_str = request.query_params.get("tags")
tags = tags_str.split(",") if tags_str else None

try:
from ...mcp.handlers.memory import memory_list

result = await asyncio.to_thread(memory_list, limit=limit, tags=tags)
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

limit = int(request.query_params.get(...)) can raise ValueError for malformed input and becomes a 500 due to the broad exception handler. Use the shared _parse_int helper (and cap to the same 1-200 range as memory_list()) so invalid query params don't crash the endpoint.

Copilot uses AI. Check for mistakes.
Comment on lines +414 to +430
metadata = row.get("metadata", {})
if isinstance(metadata, str):
metadata = _json.loads(metadata)

content = row.get("content", "")
if len(content) > snippet_len:
content = content[: snippet_len - 3] + "..."

memories.append(
{
"memory_id": str(row["id"]),
"title": row.get("title"),
"content_preview": content,
"importance": metadata.get("importance", 0.5),
"context": metadata.get("context"),
"tags": metadata.get("tags", []),
"created_at": row["created_at"].isoformat() if row.get("created_at") else None,
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata = row.get("metadata", {}) and content = row.get("content", "") can both still be None (the schema allows NULLs), which will crash on metadata.get(...) or len(content). Normalize metadata to {} when falsy/non-dict and content to "" when None before using them so listing memories doesn't fail for redacted/legacy rows.

Copilot uses AI. Check for mistakes.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants