SDK Reference
All methods are available on a GraphMemClient instance. Authenticate once — all calls use the same client.
from graphmem import GraphMemClient gm = GraphMemClient( "https://api.graphmem.site", api_key="gm_sk_...", )
conversations
A conversation is the top-level container. All memory nodes and topics belong to one.
Create a new conversation. Returns a conversation_id — pass this to all memory and topic calls.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| metadata | dict | None | None | Arbitrary key-value pairs stored with the conversation (e.g. user_id, session). |
conv_id = gm.conversations.create(metadata={"user_id": "u_123"}) # "3f7a1b2c-9e4d-..."
List all conversations for your API key.
convs = gm.conversations.list() for c in convs: print(c["conversation_id"], c["node_count"])
Graph statistics for a conversation — node count, edge count, density, topic count, average degree.
s = gm.conversations.stats(conv_id) print(f"{s['node_count']} nodes · {s['topic_count']} topics")
Export all memory nodes in a conversation as a flat list of dicts. Useful for migration or offline analysis.
Delete a conversation and all its nodes, edges, and topics. Irreversible.
memory
Memory nodes are prompt + response pairs. A node becomes complete once both parts are stored, at which point it is embedded and wired into the similarity graph asynchronously.
conversation_id: str,
content: str,
*,
timestamp: str | None = None,
metadata: dict | None = None,
) → str
Store a user prompt. Returns a memory_id — pass it to add_response() to complete the node. The node is pending and not searchable until a response is attached.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| conversation_id | str | required | The conversation this prompt belongs to. |
| content | str | required | The user's message text. |
| timestamp | str | None | None | ISO 8601 timestamp. Defaults to server time if omitted. |
| metadata | dict | None | None | Arbitrary key-value pairs attached to this node. |
memory_id of the new pending node.memory_id = gm.memory.add_prompt( conv_id, "How does async/await work in Python?", ) # "9e4c3d2a-..."
memory_id: str,
conversation_id: str,
content: str,
*,
timestamp: str | None = None,
) → dict
Attach the assistant's response to a pending node. This completes the node and immediately queues it for embedding + graph insertion (SQS → Lambda → Bedrock + Neptune). The node becomes searchable within seconds.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| memory_id | str | required | The memory_id returned by add_prompt(). |
| conversation_id | str | required | Must match the conversation used in add_prompt(). |
| content | str | required | The assistant's response text. |
| timestamp | str | None | None | ISO 8601 timestamp. Defaults to server time. |
user_msg = "How does async/await work in Python?" memory_id = gm.memory.add_prompt(conv_id, user_msg) reply = llm.complete(user_msg) gm.memory.add_response(memory_id, conv_id, reply) # node is now complete and queued for embedding
Add up to 100 complete prompt + response pairs in a single call. All nodes are immediately queued for embedding. Use this to pre-load existing conversation history.
Item schema
| Field | Type | Default | Description |
|---|---|---|---|
| conversation_id | str | required | Target conversation. |
| prompt | str | required | User message. |
| response | str | required | Assistant response. |
| timestamp_prompt | str | None | None | ISO 8601 timestamp for the prompt. |
| timestamp_response | str | None | None | ISO 8601 timestamp for the response. |
| metadata | dict | None | None | Arbitrary key-value pairs. |
ids = gm.memory.add_batch([ { "conversation_id": conv_id, "prompt": "What is a closure?", "response": "A closure captures variables from its enclosing scope...", }, # ...up to 100 items ]) # ["uuid1", "uuid2", ...]
conversation_id: str,
query: str,
*,
k: int = 5,
threshold: float = 0.4,
strategy: str = "semantic",
recency_alpha: float = 0.7,
recency_half_life: float | None = None,
always_include_recent: int = 0,
max_tokens: int | None = None,
) → dict
The core retrieval method. Embeds the query, runs a multi-set graph traversal, applies a token budget, and returns ordered context ready to inject into your LLM prompt. Internally returns the union of Set B (top-K cosine), Set A′ (threshold neighbours), Set C (Neptune 1-hop graph expansion), and Set R (recency guard).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| conversation_id | str | required | Which conversation to search. |
| query | str | required | The current user message or search query. |
| k | int | 5 | Top-K nodes for Set B (cosine search). |
| threshold | float | 0.4 | Cosine similarity floor for Set A′ neighbours. |
| strategy | "semantic" | "semantic_recency" | "semantic" | semantic_recency blends cosine score with a recency decay function. |
| recency_alpha | float | 0.7 | Weight of cosine vs recency when using semantic_recency. 1.0 = pure cosine, 0.0 = pure recency. |
| recency_half_life | float | None | None | Seconds for the recency score to halve. Defaults to server-side value if None. |
| always_include_recent | int | 0 | Always include last N nodes regardless of relevance (Set R). Exempt from token budget cuts. |
| max_tokens | int | None | None | Token budget for returned context. Nodes cut by relevance score first; Set R is never cut. |
prompt, response, score, set_membership, token_count.result = gm.memory.recall( conv_id, user_message, k=5, always_include_recent=2, max_tokens=2000, ) context = "\n\n".join( f"User: {m['prompt']}\nAssistant: {m['response']}" for m in result["messages"] ) # inject `context` into your LLM system prompt print(f"Retrieved {result['total_tokens']} tokens from {result['node_count']} nodes")
Fetch a single memory node by ID. Embedding vector is not included in the response.
memory_id: str,
*,
threshold: float = 0.7,
limit: int | None = None,
order: str = "relevance",
) → list[dict]
Return graph neighbours of a node — other nodes connected by a cosine-similarity edge at or above threshold. Useful for exploring related memories or debugging graph structure.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| threshold | float | 0.7 | Minimum cosine similarity edge weight to include. |
| limit | int | None | None | Maximum neighbours to return. None = all above threshold. |
| order | "relevance" | "timestamp" | "relevance" | Sort by edge weight (relevance) or insertion time. |
Delete a memory node and its edges. If the node belonged to a topic, that topic is marked dirty and re-evaluated on the next recompute.
topics
Topics are message clusters auto-detected via Louvain community detection every 20 nodes. Each is labeled and summarized by an LLM. Use them for fine-grained context control and maximum token efficiency.
conversation_id: str,
*,
status: str | None = None,
) → list[dict]
List all topics for a conversation. This is the entry point — get topic IDs here, then pass them to other methods.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| status | "active" | "dormant" | "resolved" | None | None | Filter by topic lifecycle status. None returns all. |
topics = gm.topics.list(conv_id, status="active") for t in topics: print(t["label"], t["message_count"])
conversation_id: str,
*,
mode: str = "centroid",
) → dict | None
Returns the topic the conversation is currently in — based on the last complete memory node. Use this for topic shift detection without sending a query.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| mode | "centroid" | "hybrid" | "llm" | "centroid" | hybrid blends centroid + summary. llm uses a full LLM inference — slower but most accurate. |
Returns "active", "dormant", or "resolved". Use this to decide which topics contribute context — skip resolved ones, inject dormant ones as summaries only.
active_topics = [ t for t in gm.topics.list(conv_id) if gm.topics.status(t["topic_id"]) == "active" ]
Returns an LLM-generated summary paragraph for the entire topic. When a topic is relevant but not the primary focus, inject the summary instead of all raw messages — same semantic coverage at a fraction of the tokens.
# An old topic has 20 raw messages (~3000 tokens). # Its summary is one paragraph (~80 tokens). summary = gm.topics.summary(old_topic_id) system_prompt += f"\n\nEarlier context: {summary}"
conversation_id: str,
query: str,
*,
top_n: int = 3,
) → list[dict]
Score all topics against a query and return the top-N most relevant, ranked by cosine similarity to their centroids. This is Stage 1 of two-stage retrieval — O(T) comparisons instead of O(N).
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| query | str | required | The prompt or search query to score topics against. |
| top_n | int | 3 | How many topics to return. |
topic_id: str,
conversation_id: str,
*,
query: str | None = None,
max_tokens: int | None = None,
) → list[dict]
Retrieve all memory nodes that belong to a single topic. Optionally narrow with a similarity query and apply a token budget.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| query | str | None | None | Further rank messages within the topic by similarity to this query. |
| max_tokens | int | None | None | Token budget. Least-relevant messages cut first. |
conversation_id: str,
query: str,
*,
top_n: int = 3,
max_tokens: int | None = None,
) → list[dict]
Full two-stage retrieval in one call. Internally runs relevance() to find top-N matching topics, then retrieves messages from each. Most token-efficient retrieval path for long conversations — compares T topic centroids, not N individual messages.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| query | str | required | The current user prompt or search query. |
| top_n | int | 3 | How many topics to pull messages from. |
| max_tokens | int | None | None | Total token budget across all retrieved messages. |
messages = gm.topics.retrieve( conv_id, user_message, top_n=3, max_tokens=3000, ) # Messages from the 3 most relevant topics, combined & token-budgeted
Full topic object — label, description, summary, coherence score, is_mixed flag, sub-themes, status, message count.
Manually trigger Louvain clustering + LLM labeling for a conversation. Topics auto-recompute every 20 nodes — use this to force early recompute after bulk deletions or large batch imports.
auth
Key management requires a Cognito JWT token, not an API key. Pass it as token= at client construction time.
name: str | None = None,
permissions: dict | None = None,
) → dict
Create an API key. The full key is returned once in the response — it cannot be retrieved again. Store it immediately.
List all API keys for the authenticated user. Full key values are never returned — only prefix, status, created_at, and metadata.
Revoke an API key immediately. Any request using it will return 401.
utils
Estimate token count for any string using the same tokenizer GraphMem uses internally for max_tokens budgeting.
n = gm.utils.count_tokens("How does async/await work in Python?") # 9
Liveness check. No auth required. Returns {"status": "ok"} when the API is up.