StateJar docs

Any LLM provider

StateJar as the memory layer in front of any model. It stores what each message says, and you put the memory a question needs in front of Claude, OpenAI, Gemini, a model on your own machine or any other provider, as its system message. StateJar never calls your model and never sees its key.

What it does

StateJar does not call your model. It sits before your own model call:

  1. recall(question) returns memory_context, the memory the question needs, and the handle (shm_…) it came from.
  2. Your code calls the model with memory_context as the system message and the user's message after it. Nothing from earlier turns is sent.
  3. ingest(message) stores the facts the message states as a new version, never the message itself.

So it works with any provider and any model: the code that calls the model stays yours, and so does the model's key. While your memory is small, all of it is sent. Once it passes about 400 tokens, only the fields the question needs are.

Install

pip install statejar

Then your provider's own package, if it has one: pip install anthropic, pip install openai or pip install google-genai. A model on your own machine needs nothing more than an HTTP call. Sign in once with statejar login, or set STATEJAR_API_KEY (getting started).

Set it up

Each example stores a message, recalls the memory for a question and puts it in front of the model. Each provider reads its own key from its own environment variable; StateJar never sees it.

Claude

import anthropic
from statejar import StateJar

sj = StateJar(session_tag="user-42")
sj.ingest("I'm Meera from Pune, I prefer email, and my budget is ₹3,500.")
memory = sj.recall("What is my budget?")       # only the fields the question needs

client = anthropic.Anthropic()                 # ANTHROPIC_API_KEY
reply = client.messages.create(
    model="claude-haiku-5-5", max_tokens=200,
    system=memory["memory_context"],           # the model sees this, never a transcript
    messages=[{"role": "user", "content": "What is my budget?"}],
)
print(reply.content[0].text)

OpenAI

from openai import OpenAI
from statejar import StateJar

sj = StateJar(session_tag="user-42")
sj.ingest("I'm Meera from Pune, I prefer email, and my budget is ₹3,500.")
memory = sj.recall("What is my budget?")

client = OpenAI()                              # OPENAI_API_KEY
reply = client.chat.completions.create(
    model="gpt-6-luna",
    messages=[
        {"role": "system", "content": memory["memory_context"]},
        {"role": "user", "content": "What is my budget?"},
    ],
)
print(reply.choices[0].message.content)

Gemini

from google import genai
from google.genai import types
from statejar import StateJar

sj = StateJar(session_tag="user-42")
sj.ingest("I'm Meera from Pune, I prefer email, and my budget is ₹3,500.")
memory = sj.recall("What is my budget?")

client = genai.Client()                        # GEMINI_API_KEY
reply = client.models.generate_content(
    model="gemini-3.8-flash",
    config=types.GenerateContentConfig(system_instruction=memory["memory_context"]),
    contents="What is my budget?",
)
print(reply.text)

A model on your own machine

import httpx
from statejar import StateJar

sj = StateJar(session_tag="user-42")
sj.ingest("I'm Meera from Pune, I prefer email, and my budget is ₹3,500.")
memory = sj.recall("What is my budget?")

# any local model: here Ollama on your own machine (`ollama run qwen3:4b`)
reply = httpx.post("http://localhost:11434/api/chat", timeout=120, json={
    "model": "qwen3:4b", "stream": False,
    "messages": [
        {"role": "system", "content": memory["memory_context"]},
        {"role": "user", "content": "What is my budget?"},
    ],
}).json()
print(reply["message"]["content"])

Any other provider

Any API that takes a system message works the same way: memory_context first, then the user's message. If the API has no system message, put memory_context before the user's message in the same text.

Check it works

  1. Run the example for your provider. The reply gives the budget (₹3,500), which only the memory held.
  2. The audit log lists the recall as a query disclosure, with the handle and the fields it sent.

What the model receives is memory_context, as the Python SDK prints it here, run against a local StateJar server:

The first-turn example run with python: the handle, then memory_context with the name, city, contact preference and budget
A first turn with the Python SDK. The first-turn example with the key statejar login stored, on statejar 0.7.0 as built from StateJar's repository (PyPI's 0.6.0 has the same calls): two messages in, one question out, then the handle and the memory_context your model gets, the whole state since it is small. The account already held these facts from the terminal, so nothing new is stored and the handle is the one statejar add printed. Recorded 27 Oct 2026 against a local StateJar server; the output is real, with pauses over 3 s cut to 3 s. Full sizeThe recording (.cast)

Each example on this page is run against StateJar's staging server by our docs check: the Claude, OpenAI and Gemini ones when we hold a key for that provider, and the local one when an Ollama server answers.

What it sends

Troubleshooting

This page as Markdown: /docs/any-llm.md