Skip to main content

Vity Python SDK

Long-term memory for AI applications, by Maximem AI.

Store facts and preferences, capture conversations, and recall the right context for every prompt, with a small, typed client for both synchronous and asyncio code.

from maximem_vity import VityClient

client = VityClient(channel="my-app")  # reads MAXIMEM_API_KEY

client.store("User prefers dark mode", memory_type="preference")
print(client.recall("What theme does the user like?"))

Using JavaScript or TypeScript? Install @maximem/vity-sdk from npm.

Contents

Installation

pip install maximem-vity-sdk

Requires Python 3.9 or later. The package is fully typed.

Quick start

Create an API key at app.maximem.ai/api-keys and export it:

export MAXIMEM_API_KEY="mx_..."

A typical agent turn recalls memory before answering and captures the exchange afterwards:

from maximem_vity import VityClient

with VityClient(channel="support-bot") as client:
    # Before answering: fetch relevant memory for the system prompt.
    context = client.recall("Can you book my usual table?")

    # After answering: capture the exchange so Vity can learn from it.
    client.capture(
        [
            {"role": "user", "content": "Can you book my usual table?"},
            {"role": "assistant", "content": "Booked the window table for 7pm."},
        ],
        session_key="conversation-8f2c",
    )

    # Store an explicit fact.
    client.store("Prefers window seats", memory_type="preference", importance="high")

    # Search memory directly.
    for hit in client.search("seating preferences", top_k=5):
        print(f"{hit['score']:.2f}  {hit['content']}")

Async

AsyncVityClient takes the same arguments and exposes the same methods as coroutines:

import asyncio
from maximem_vity import AsyncVityClient

async def main() -> None:
    async with AsyncVityClient(channel="support-bot") as client:
        context = await client.recall("What does the user like to eat?")

asyncio.run(main())

Concepts

Memory belongs to your account. All API keys on a Maximem account share the same memory. Rotating or adding keys never loses anything.

Channels and sessions. Set channel to the name of your integration ("slack", "support-bot"). Pass a session_key to capture() that stays the same for every turn of one conversation and changes for each new conversation.

store() or capture()?

store() capture()
Input One explicit fact A conversation (up to 50 messages)
Saved Immediately In the background, usually within seconds to minutes
What is kept Exactly what you send Durable facts only; small talk is ignored

A successful capture() means the conversation was accepted for processing.

recall() or search()? recall() returns a text block ready to drop into a prompt. search() returns scored memory records for your own logic.

Configuration

Argument Environment variable Default Description
api_key MAXIMEM_API_KEY required Your Maximem API key.
endpoint MAXIMEM_ENDPOINT Maximem API Only needed for a non-default environment.
channel "unspecified" The name of your integration. Recommended.
agent_id "unspecified" Tag recorded on stored memories.
timeout 30.0 Request timeout in seconds.
max_retries 2 Retries for temporary failures. 0 disables them.
http_client managed Your own httpx.Client / httpx.AsyncClient (proxies, custom TLS, tests).

Arguments take precedence over environment variables.

API reference

Every method is available on VityClient and, as a coroutine, on AsyncVityClient.

Memory

Method Returns
recall(current_prompt, recent_messages=None, max_tokens=1000, strategy="hybrid") str: prompt-ready context, or ""
capture(messages, agent_id="", session_key="", channel=None) CaptureResult
store(content, memory_type="fact", category="", importance="medium") StoreResult: {"id", "stored"}
search(query, top_k=10, limit=0, category="", min_score=0.0) list[SearchResult]: content, type, score, id, created_at (stored), chat_at (said, or None), happened_at (chat_at else created_at), all epoch ms
forget(query="", dry_run=True, delete_all=False) ForgetResult: {"count", "ids"}
capture_status(request_id) dict
  • strategy: "semantic", "recency" or "hybrid".
  • memory_type: fact, preference, emotion, episode, knowledge or profile. Use category (preference, fact, task, relationship, context) to set the category directly.
  • importance: "low", "medium" or "high".

Deleting memories

forget() only previews unless you pass dry_run=False. Deleting without a query removes all memories on the account, so it also requires delete_all=True:

client.forget("old employer")                    # preview what would be deleted
client.forget("old employer", dry_run=False)     # delete matching memories
client.forget(dry_run=False, delete_all=True)    # delete everything

Matching is by meaning, not exact text, so one query can match several memories, including related but different facts. Check the dry-run count and ids before deleting with dry_run=False.

Knowledge

Method Returns
knowledge(query="", scope="chat", limit=10, min_score=0.3, include_candidates=False, filters=None, offset=0, include=None, echo_slug=None) KnowledgeResult
knowledge_diff(input_text, purpose, ...) KnowledgeDiffResult

knowledge() returns consolidated knowledge about the user (topics, summaries, confidence), with pagination through limit and offset. Filter with keys such as topic_category, confidence_min, seen_after and seen_before; choose sections with include ("items", "facets", "profile").

knowledge_diff() returns the knowledge most relevant to a piece of text. purpose describes what you are looking for and why:

client.knowledge_diff(
    input_text="Draft: proposal to move our analytics to ClickHouse",
    purpose="the user's earlier decisions and opinions about analytics databases",
)

Meetings

Method Returns
meetings_enabled() bool: False if unavailable
meeting_ask(question, max_memories=12, meeting_type="", status="", seed_person_id="") dict
get_meeting(external_id) dict
list_meeting_reviews() list[dict]

Profile

Method Returns
get_profile_card() ProfileCard

get_profile_card() returns the user's Profile Card: who they are, built from their memories and kept up to date as they use Vity. It is a fast read with no LLM call.

card = client.get_profile_card()
name = card["identity"]["name"]              # {"value": "Ada", "confidence": 0.95, ...} or None
skills = [s["value"] for s in card["knowledge"]["skills"]]

Sections: identity (name, email), professional (current_company, current_role, and their history), personal (location, education, interests), knowledge (expertise, top_domains, skills), goals (active, completed) and preferences. Each value carries confidence (0 to 1) and the source_ids of the memories behind it. candidates lists speculative or ended values and is never current state. A field with no value is None or [].

get_profile() is deprecated and will be removed in 0.4.0: it returns a broad memory search, not the Profile Card.

LLM tool calling

as_tool() lets a model search and save memories itself. It provides two tools, search_memory and save_memory, in OpenAI or Anthropic format:

from openai import OpenAI
from maximem_vity import VityClient

llm, vity = OpenAI(), VityClient(channel="assistant")
memory = vity.as_tool(style="openai")       # or style="anthropic"

messages = [{"role": "user", "content": "What am I allergic to?"}]
response = llm.chat.completions.create(model="gpt-4o", messages=messages, tools=memory.definitions)

for call in response.choices[0].message.tool_calls or []:
    if memory.handles(call.function.name):
        result = memory.call(call.function.name, call.function.arguments)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
  • definitions is plain JSON and can be passed directly as tools.
  • call() accepts arguments as a JSON string or a dict, and returns a JSON string.
  • If the model sends invalid arguments, call() returns {"error": ...} so the model can retry. Other problems, such as an invalid API key, raise an exception.
  • Pass read_only=True to offer only search_memory.
  • The model chooses the query or the fact to save. It cannot change whose memory is used.

With AsyncVityClient, use await memory.call(...).

Error handling

All exceptions inherit from VityError:

Exception Raised when
VityInvalidInputError An argument is invalid. Usually raised before any request is sent.
VityAuthError The API key is missing, invalid or revoked.
VityInsufficientCreditsError The account has run out of credits.
VityNotFoundError The requested item does not exist.
VityRateLimitError Too many requests. Check retry_after.
VityUnavailableError A temporary service or network problem, or a timeout.

Each exception has message, status_code, code, request_id, retry_after and retryable. Include request_id when contacting support.

import time
from maximem_vity import VityError, VityInsufficientCreditsError, VityRateLimitError

try:
    client.store("User is vegetarian", memory_type="preference")
except VityInsufficientCreditsError:
    ...  # prompt the account owner to add credits
except VityRateLimitError as err:
    time.sleep(err.retry_after or 60)
except VityError as err:
    print(f"Vity request failed: {err} (request id: {err.request_id})")

Retries and timeouts

The client retries temporary failures automatically, up to max_retries times with exponential backoff:

  • Read operations (recall, search, knowledge, ...) are retried after temporary errors.
  • Write operations (store, capture, forget) are retried only when the request never reached the service. This prevents a memory from being saved twice.
  • The client never waits more than a few seconds before retrying. If the service asks for a longer wait, you get a VityRateLimitError with retry_after set and can decide what to do.

Input is validated before sending. For example, capture() accepts up to 50 messages per call and store() up to 10,000 characters, so mistakes fail fast with a clear message.

Logs go to the maximem_vity logger: retries at WARNING, requests at DEBUG. The API key is never logged.

Testing

Use an httpx.MockTransport to test your code without network access:

import httpx
from maximem_vity import VityClient

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"context": "User prefers dark mode", "sources": []})

client = VityClient(api_key="mx_test", http_client=httpx.Client(transport=httpx.MockTransport(handler)))
assert client.recall("theme?") == "User prefers dark mode"

Upgrading from 0.2.x

0.3.0 is backwards compatible for typical use. Check these changes:

  • user_id is deprecated. It is ignored because memory belongs to the account that owns the key. It will be removed in 0.4.0.
  • channel and agent_id default to "unspecified". Set channel to your integration's name.
  • Deleting everything requires confirmation: forget(dry_run=False, delete_all=True).
  • search(min_score=0.0) now means no minimum score, so you may see more results.
  • Invalid arguments raise VityInvalidInputError before any request is sent.
  • New exceptions VityInvalidInputError, VityInsufficientCreditsError and VityNotFoundError inherit from VityError, so existing except VityError blocks keep working.

License

MIT

Metadata

Release files for maximem-vity-sdk 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for maximem-vity-sdk 0.3.1
File Size Uploaded Provenance Actions
maximem_vity_sdk-0.3.1.tar.gz 43.7 kB Without provenance Details for maximem_vity_sdk-0.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for maximem-vity-sdk 0.3.1
File Interpreter ABI Platform Actions
maximem_vity_sdk-0.3.1-py3-none-any.whl Python 3 none any Details for maximem_vity_sdk-0.3.1-py3-none-any.whl

Total release size: 75.4 kB

Release files / maximem_vity_sdk-0.3.1.tar.gz

Download URL maximem_vity_sdk-0.3.1.tar.gz
Size 43.7 kB
Tags Source
SHA-256 checksum
How to use checksums
334d7d04e7b2fdeb3e17e60001fb301be0d83ec4531e9aa9fe15ac6e9e7b29ee
BLAKE2b-256 checksum
How to use checksums
855f3b549de25f0bfc83f21541ed4320365ea1429e72efbeb7ca5ec6aee3d05d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / maximem_vity_sdk-0.3.1-py3-none-any.whl

Download URL maximem_vity_sdk-0.3.1-py3-none-any.whl
Size 31.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
56aa18e4f7b3e4b6cbcfaf11bd8c3250845a51b17ebf6ab57bb6ac2f4a35d2da
BLAKE2b-256 checksum
How to use checksums
7a9b2455ef175df4d5e8d712e67c22e61d6ebc46a99444055d3d154a397fb780
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Security for maximem-vity-sdk 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page