Skip to content

Conversation

@jack-arturo
Copy link
Member

Summary

Add /graph/* API blueprint for memory visualization:

  • /graph/snapshot: Return full graph with nodes, edges, and visual properties
  • /graph/neighbors/{id}: Get graph and semantic neighbors for a node
  • /graph/stats: Return graph statistics for dashboard
  • /graph/types: Available memory types with colors
  • /graph/relations: Available relationship types with colors

Visual Properties

  • Type-based colors for nodes (Decision=blue, Pattern=emerald, etc.)
  • Importance-based radius scaling (0.5-2.0)
  • Confidence-based opacity (0.4-1.0)
  • Relationship strength for edge thickness

Testing

curl -s "http://localhost:8001/graph/snapshot?limit=50" -H "X-API-Key: $TOKEN" | jq

Test plan

  • Unit tests pass
  • API returns expected graph structure
  • Frontend visualization renders correctly

🤖 Generated with Claude Code

Add /graph/* API blueprint for memory visualization:
- /graph/snapshot: Return full graph with nodes, edges, and visual properties
- /graph/neighbors/{id}: Get graph and semantic neighbors for a node
- /graph/stats: Return graph statistics for dashboard
- /graph/types: Available memory types with colors
- /graph/relations: Available relationship types with colors

Visual properties include:
- Type-based colors for nodes
- Importance-based radius scaling
- Confidence-based opacity
- Relationship strength for edge thickness

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

📝 Walkthrough

Walkthrough

A new graph visualization API blueprint is introduced and wired into the Flask application. The module provides five RESTful endpoints for querying and visualizing memory graphs: snapshot view with optional filtering and layout, neighbor traversal up to configurable depth with optional semantic augmentation from Qdrant, and endpoints returning graph statistics and metadata. Visual properties (colors, sizes, opacity) are computed from node types and importance values.

Changes

Cohort / File(s) Summary
Blueprint Integration
app.py
Added import of create_graph_blueprint from automem.api.graph and instantiation of graph blueprint with dependencies (get_memory_graph, get_qdrant_client, _serialize_node, COLLECTION_NAME, logger), then registered the blueprint with Flask app.
Graph Visualization API
automem/api/graph.py
New module providing create_graph_blueprint() factory function that constructs a Blueprint with five endpoints: /snapshot (full graph view with filters), /neighbors/<memory_id> (localized neighbor graph), /stats (aggregated statistics), /types (type-to-color mapping), and /relations (relationship-to-color mapping). Includes visual encoding constants, graph assembly logic, and optional Qdrant semantic search integration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Graph assembly and traversal logic in /snapshot and /neighbors endpoints, particularly node/edge collection and filtering
  • Qdrant semantic neighbor integration with depth-based retrieval and vector store querying
  • Visual property computation (color mapping, radius/opacity calculations based on importance and confidence)
  • Error handling and edge cases (missing nodes, circular references, semantic augmentation failures)

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a graph visualization API with multiple endpoints.
Description check ✅ Passed The description is directly related to the changeset, providing clear details about the new graph API endpoints, visual properties, and testing approach.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/visualizer

Comment @coderabbitai help to get the list of available commands and usage tips.

@jack-arturo jack-arturo merged commit a0d0dd3 into main Dec 11, 2025
5 of 6 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
automem/api/graph.py (3)

8-18: Remove unused imports.

Several imports are not used in this module:

  • json (line 8)
  • defaultdict from collections (line 11)
  • datetime, timezone from datetime (line 12)
  • _serialize_node from automem.utils.graph (line 18) — the function is passed as a parameter to create_graph_blueprint
-import json
-import logging
+import logging
 import time
-from collections import defaultdict
-from datetime import datetime, timezone
 from typing import Any, Callable, Dict, List, Optional, Set
 
 from flask import Blueprint, abort, jsonify, request
 
 from automem.config import ALLOWED_RELATIONS, MEMORY_TYPES
-from automem.utils.graph import _serialize_node

202-204: Use logger.exception to capture stack traces.

Per static analysis (TRY400), use logger.exception() instead of logger.error() to automatically include the stack trace for debugging.

         except Exception as e:
-            logger.error(f"graph/snapshot failed: {e}")
+            logger.exception("graph/snapshot failed: %s", e)
             abort(500, description=str(e))

450-452: Use logger.exception to capture stack traces.

Per static analysis (TRY400), use logger.exception() for better debugging.

         except Exception as e:
-            logger.error(f"graph/stats failed: {e}")
+            logger.exception("graph/stats failed: %s", e)
             abort(500, description=str(e))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3e18f5 and b012bad.

📒 Files selected for processing (2)
  • app.py (2 hunks)
  • automem/api/graph.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
automem/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

automem/**/*.py: Ensure graph writes always succeed even if vector storage fails by implementing graceful degradation in embedding and vector store operations
Normalize all timestamps to UTC ISO format for storage and retrieval
Implement graph operations as atomic transactions with automatic rollback on errors
Log vector store errors but do not block graph writes when vector storage fails

Files:

  • automem/api/graph.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Run 'black .' to format Python code before committing
Run 'flake8' to lint Python code for quality issues

**/*.py: Use type hints in Python code
Use 4-space indentation in Python code
Enforce maximum line length of 100 characters (Black configuration)
Format Python code with Black
Sort imports with Isort using profile=black
Lint Python code with Flake8
Use snake_case for module and function names in Python
Use PascalCase for class names in Python
Use UPPER_SNAKE_CASE for constant names in Python

Files:

  • automem/api/graph.py
  • app.py
🧠 Learnings (1)
📚 Learning: 2025-12-09T02:15:22.291Z
Learnt from: CR
Repo: verygoodplugins/automem PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T02:15:22.291Z
Learning: Applies to automem/models/**/*.py : Use UUID for memory IDs and store in both graph (FalkorDB) and vector (Qdrant) databases for cross-referencing

Applied to files:

  • automem/api/graph.py
🧬 Code graph analysis (2)
automem/api/graph.py (3)
automem/utils/graph.py (1)
  • _serialize_node (8-20)
app.py (2)
  • get_memory_graph (1383-1385)
  • get_qdrant_client (1388-1390)
tests/test_api_endpoints.py (1)
  • retrieve (195-204)
app.py (3)
automem/api/graph.py (1)
  • create_graph_blueprint (50-474)
scripts/reembed_embeddings.py (1)
  • get_qdrant_client (64-71)
automem/utils/graph.py (1)
  • _serialize_node (8-20)
🪛 Ruff (0.14.8)
automem/api/graph.py

202-202: Do not catch blind exception: Exception

(BLE001)


203-203: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


338-338: Do not catch blind exception: Exception

(BLE001)


415-415: Do not catch blind exception: Exception

(BLE001)


450-450: Do not catch blind exception: Exception

(BLE001)


451-451: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🔇 Additional comments (7)
automem/api/graph.py (4)

22-47: LGTM!

Color constants are well-organized with appropriate fallback defaults and follow naming conventions.


50-58: LGTM!

The factory function follows a clean dependency injection pattern, making it testable. The parameter logger: Any could be more specific (e.g., logging.Logger), but Any is acceptable for flexibility.


295-339: Good graceful degradation for semantic search.

The semantic neighbor search correctly handles failures by logging a warning and continuing without blocking the graph response. This aligns with the coding guidelines for vector storage operations.


454-472: LGTM!

Simple configuration endpoints that return static data. Clean implementation with no error handling needed.

app.py (3)

3427-3427: LGTM!

Import follows the established pattern for blueprint imports.


3523-3529: LGTM!

Blueprint creation follows the established pattern with correct dependency injection.


3537-3537: LGTM!

Blueprint registration completes the wiring of the new graph visualization API.

Comment on lines +73 to +78
limit = min(int(request.args.get("limit", 500)), 2000)
min_importance = float(request.args.get("min_importance", 0.0))
types_filter = (
request.args.get("types", "").split(",") if request.args.get("types") else None
)
since = request.args.get("since")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add input validation for query parameters.

Parsing limit and min_importance with int()/float() directly will raise a ValueError and return a 500 error if the user provides non-numeric values. Wrap these in try/except for user-friendly 400 errors.

-        limit = min(int(request.args.get("limit", 500)), 2000)
-        min_importance = float(request.args.get("min_importance", 0.0))
+        try:
+            limit = min(int(request.args.get("limit", 500)), 2000)
+        except (TypeError, ValueError):
+            limit = 500
+
+        try:
+            min_importance = float(request.args.get("min_importance", 0.0))
+        except (TypeError, ValueError):
+            min_importance = 0.0

Comment on lines +219 to +221
depth = min(int(request.args.get("depth", 1)), 3)
include_semantic = request.args.get("include_semantic", "true").lower() == "true"
semantic_limit = min(int(request.args.get("semantic_limit", 5)), 20)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add input validation for query parameters.

Same issue as /snapshot - non-numeric values will cause 500 errors instead of user-friendly 400 responses.

-        depth = min(int(request.args.get("depth", 1)), 3)
-        include_semantic = request.args.get("include_semantic", "true").lower() == "true"
-        semantic_limit = min(int(request.args.get("semantic_limit", 5)), 20)
+        try:
+            depth = min(max(int(request.args.get("depth", 1)), 1), 3)
+        except (TypeError, ValueError):
+            depth = 1
+
+        include_semantic = request.args.get("include_semantic", "true").lower() == "true"
+
+        try:
+            semantic_limit = min(int(request.args.get("semantic_limit", 5)), 20)
+        except (TypeError, ValueError):
+            semantic_limit = 5
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
depth = min(int(request.args.get("depth", 1)), 3)
include_semantic = request.args.get("include_semantic", "true").lower() == "true"
semantic_limit = min(int(request.args.get("semantic_limit", 5)), 20)
try:
depth = min(max(int(request.args.get("depth", 1)), 1), 3)
except (TypeError, ValueError):
depth = 1
include_semantic = request.args.get("include_semantic", "true").lower() == "true"
try:
semantic_limit = min(int(request.args.get("semantic_limit", 5)), 20)
except (TypeError, ValueError):
semantic_limit = 5
🤖 Prompt for AI Agents
In automem/api/graph.py around lines 219 to 221, the request.args int
conversions for depth and semantic_limit and the boolean parsing for
include_semantic are not validated and will raise exceptions on non-numeric or
malformed inputs; wrap the parsing in validation logic that catches conversion
errors and range violations, returning a 400 Bad Request with a clear error
message for invalid values (e.g., non-integer depth/semantic_limit or
out-of-range values) and normalize boolean parsing safely (allow only
"true"/"false" or use a helper), ensuring default fallbacks remain but invalid
inputs produce user-friendly 400 responses instead of 500s.

jack-arturo added a commit that referenced this pull request Feb 6, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.10.0](v0.9.3...v0.10.0)
(2026-02-06)


### Features

* Add `exclude_tags` parameter to recall API
([#58](#58))
([8aeca2e](8aeca2e))
* add memory content size governance with auto-summarization
([e24a86b](e24a86b))
* add memory content size governance with auto-summarization
([876aabf](876aabf))
* Add real-time observability with SSE streaming and TUI monitor
([54d2701](54d2701))
* Add real-time observability with SSE streaming and TUI monitor
([c5f6370](c5f6370))
* Add real-time observability with SSE streaming and TUI monitor
([944168e](944168e))
* **api:** add graph visualization API endpoints
([#26](#26))
([a0d0dd3](a0d0dd3))
* **api:** add time-based sorting to recall API
([c063845](c063845))
* **api:** add time-based sorting to recall API
([a511f0c](a511f0c))
* create coderabbit.yml
([e05811f](e05811f))
* **embedding:** add Ollama embedding provider and expand docs
([#56](#56))
([b506a90](b506a90))
* **mcp-sse:** agent-friendly recall output + tests
([71201d3](71201d3))
* **mcp-sse:** improve recall tool output and options
([97c4f72](97c4f72))


### Bug Fixes

* Add TTL cleanup to InMemoryEventStore
([0631af6](0631af6))
* Address CodeRabbit security and lint issues
([a701c57](a701c57))
* **backup:** Fix GitHub Actions backup failing with connection error
([19ed1e8](19ed1e8))
* **backup:** Fix GitHub Actions backup failing with connection error
([10b37d0](10b37d0))
* **consolidation:** prevent accidental forgetting
([4330c20](4330c20))
* **consolidation:** prevent accidental forgetting
([39dee3e](39dee3e))
* Correct token parameter for gpt-5 models (max_output_tokens)
([0786705](0786705))
* **enrichment:** sync tags/tag_prefixes to Qdrant
([3ccf874](3ccf874))
* **enrichment:** sync tags/tag_prefixes to Qdrant
([7ba9d76](7ba9d76))
* Handle streamable HTTP client disconnects
([f15a7ee](f15a7ee))
* **mcp-sse:** update test for SDK 1.20+ Accept header behavior
([597909e](597909e))
* **mcp:** close transport/server on sweep and update Accept header test
([ce7f6fa](ce7f6fa))
* **mcp:** replace broken res.on('close') with TTL sweep for Streamable
HTTP sessions
([3d53263](3d53263))
* **mcp:** replace broken res.on('close') with TTL sweep for Streamable
HTTP sessions
([#59](#59))
([d4d3259](d4d3259))
* **mcp:** use Promise.resolve().catch() for async close() in session
sweep
([a8cc589](a8cc589))
* Production bugs - datetime tz, OpenAI tokens, Qdrant race
([14491c4](14491c4))
* Production bugs - datetime tz, OpenAI tokens, Qdrant race
([0771360](0771360))
* Refactor tests to use double quotes for consistency
([21da453](21da453))
* Refine summarization token limit and improve tests
([6cab145](6cab145))
* Update test for POST /mcp Accept header error
([67a5a10](67a5a10))
* YAML indentation in backup workflow and update docs
([a606b3e](a606b3e))


### Documentation

* add mermaid diagrams for architecture visualization
([0d70a2a](0d70a2a))
* Add Qdrant setup guide and update docs for AUTOMEM_API_URL
([5c440aa](5c440aa))
* Add Qdrant setup guide and update docs for AUTOMEM_API_URL
([123ff28](123ff28))
* Enhance README.md with formatted NPM bridge link
([09ac4ac](09ac4ac))
* merge mermaid diagrams from docs/add-mermaid-diagrams
([4e9ae4f](4e9ae4f))
* Update API documentation and configuration settings
([9281149](9281149))
* Update API documentation and configuration settings
([#51](#51))
([ad9500f](ad9500f))
* Update API endpoint count and project description
([9e71299](9e71299))
* update diagrams for Streamable HTTP transport
([1a2ce22](1a2ce22))
* Update docs to clarify MCP bridge and improve formatting
([af69a2f](af69a2f))
* Update MCP documentation and server implementation for Streamable HTTP
support
([8fdef09](8fdef09))
* Update MCP documentation and server implementation for Streamable HTTP
support
([335a78d](335a78d))
* update MCP_SSE.md to reflect MCP bridge terminology
([940a776](940a776))
* Update Railway deployment docs and improve README
([5480eb5](5480eb5))
* Update Railway deployment docs and improve README
([82c33b0](82c33b0))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jack Arturo <info@verygoodplugins.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant