Skip to content

About

FastAPI reference app for Statewave memory integration. Wraps every chat turn with get_context before the LLM call and record_episode after; seeded demo users show compiled facts and token-budgeted context. Self-hosted, Postgres + pgvector.

Topics

Resources

Stars

28 stars

Watchers

0 watching

Forks

Repository files navigation

personal assistant memory

statewave-personal-assistant

Python 3.11+ FastAPI Statewave License: MIT Tests OpenAI Compatible

A working reference implementation that shows how to add persistent, cross-session memory to any Python chat application using Statewave and FastAPI.

Clone it, run it, and see the difference in under five minutes.

Part of Statewave — the open-source memory runtime for AI agents.

📦 Core runtime · 🐍 Python SDK · 🟦 TypeScript SDK · 🔌 Connectors · 📘 Docs · 💡 Examples · 🖥️ Admin · 🌐 statewave.ai

📋 Issues & feature requests: statewave/issues (centralized tracker)


5-Minute Path

Want to see real compiled memory before setting up an LLM key? This path skips the LLM entirely — it only needs Python and the OSS Statewave server.

# 1. Boot Statewave locally (API + admin console + Postgres) — one line, no clone needed
npx @statewavedev/statewave

# 2. In a new terminal, clone and install this repo
git clone https://github.com/smaramwbc/statewave-personal-assistant.git
cd statewave-personal-assistant
python -m venv .venv && source .venv/bin/activate   # .venv\Scripts\activate on Windows
pip install -e ".[dev]"

# 3. Seed two demo users with realistic conversation history
python -m scripts.seed

# 4. Ask Statewave what it knows about one of them — no LLM call, no API key required
curl http://localhost:8000/api/v1/memory/dev_alice

That last call returns the compiled, ranked memory facts Statewave extracted from dev_alice's seeded sessions — the same context that would otherwise get injected into an LLM prompt. If you want to see it actually change an LLM's response, continue to the full Quick Start below and add an LLM_API_KEY to try POST /api/v1/compare.


The Problem This Solves

Every session-based chat application faces the same wall. When a user returns after closing the tab, the assistant remembers nothing. The standard workaround is to inject the full conversation history into every prompt, but this breaks quickly:

  • After 8 to 10 turns, the context window is full
  • Earlier facts are silently dropped to make room for recent ones
  • The more sessions a user has, the worse the problem gets
  • There is no ranking signal: a throwaway comment gets as much weight as a critical bug report

This is not a model limitation. It is an architecture problem.


The Solution: Statewave

Statewave is an open-source, self-hosted memory runtime for AI agents. Instead of passing raw conversation history to the LLM, you pass conversations through Statewave, which:

  1. Extracts structured facts from each conversation turn (user role, procedures, artifact references, session summaries)
  2. Ranks those facts by relevance and recency
  3. Returns only the highest-signal context that fits within your token budget, as a ready-to-inject string

The result is an assistant that knows who the user is, what they have been building, and what is still unresolved, across every session, using a fraction of the tokens that raw history would consume.

Statewave is self-hosted (Apache 2.0), runs on PostgreSQL with pgvector, and requires no cloud account or external API key to operate.


Core Concepts

If you are new to Statewave, these are the five terms you will encounter throughout this codebase.

Episode

An episode is a single recorded conversation turn: one user message and one assistant response. Episodes are the raw input to Statewave. You record them after each turn using POST /v1/episodes.

Think of episodes as the raw log. Statewave reads from this log to build memories.

Memory

A memory is a structured fact that Statewave extracts from one or more episodes. Memories are typed:

Kind What it stores
profile_fact Stable facts about the user (role, language preference, timezone)
episode_summary A compressed summary of what happened in a session
procedure Multi-step instructions or workflows the user follows
artifact_ref References to external artifacts, files, or documents

Memories are created by calling POST /v1/memories/compile. This is an explicit step, not automatic.

Subject

A subject is the entity whose memory is being tracked. In a chat application, each user has their own subject. The subject_id is just a string you choose, for example dev_alice or user_123. All episodes and memories are scoped to a subject.

Context Bundle

When a new message arrives, you ask Statewave for a context bundle via POST /v1/context. Statewave ranks all compiled memories for that subject, selects the highest-scored facts that fit within your token budget, and returns them as a single pre-assembled string called assembled_context. You inject this string directly into the LLM system prompt.

Receipt

A receipt is an immutable, HMAC-signed audit record of every context assembly call. It captures exactly which facts were returned and why. Useful for debugging and compliance, not required for the core integration.


How the Integration Works

Every chat request goes through a three-step loop. Two HTTP calls to Statewave wrap the LLM call: one before to fetch ranked context, one after to record the episode.

Architecture diagram

The integration surface is two API calls: get_context before the LLM call and record_episode after. That is the entire memory layer. See app/services/statewave.py for the implementation.


Prerequisites

Before you begin, make sure you have:

  • Python 3.11 or newer (download)
  • Docker and Docker Compose (needed to run Statewave locally)
  • An API key for an OpenAI-compatible LLM provider (OpenAI, Azure OpenAI, local Ollama, etc.)
  • Git for cloning repositories

If you are unsure whether you have these installed, run the following in your terminal:

python --version    # should print 3.11.x or higher
docker --version    # should print Docker version ...
git --version       # should print git version ...

Quick Start

Step 1: Clone and install this repository

git clone https://github.com/smaramwbc/statewave-personal-assistant.git
cd statewave-personal-assistant

# Create an isolated Python environment (recommended)
python -m venv .venv

# Activate it
source .venv/bin/activate        # macOS / Linux
.venv\Scripts\activate           # Windows (Command Prompt)
# .venv\Scripts\Activate.ps1    # Windows (PowerShell)

# Install the application and its dependencies
pip install -e ".[dev]"

Step 2: Configure your environment

cp .env.example .env

Open the .env file in any text editor and set your LLM API key:

LLM_API_KEY=your-api-key-here

Everything else has sensible defaults. A full list of available variables is in the Environment Variables section at the bottom of this page.

Step 3: Start Statewave locally

Statewave is a separate server that runs alongside your application. It's fully open source (Apache-2.0) — pick whichever way you want to run it:

Fastest — one line to a running server:

npx @statewavedev/statewave

This boots the same open-source Statewave server (API, admin console, and Postgres) locally and wires it into your MCP clients. Equivalent one-liner: curl -fsSL https://www.statewave.ai/install | sh.

Prefer to run it yourself from source? Clone and start it with Docker Compose:

# In a new terminal window or tab, go up one level from this repo
cd ..

git clone https://github.com/smaramwbc/statewave
cd statewave
docker compose up -d

What is docker compose up -d doing? It starts a PostgreSQL database (with the pgvector extension for vector search) and the Statewave API server in the background. The -d flag means "detached" so the containers run without blocking your terminal.

Statewave starts at http://localhost:8100. You can verify it is running:

curl http://localhost:8100/healthz
# Expected output: {"status":"ok"}

Go back to this repository's directory:

cd ../statewave-personal-assistant

Step 4: Seed demo users

This step populates Statewave with two fictional developer profiles that have realistic conversation histories. It is what makes the demo useful out of the box.

python -m scripts.seed

You will see output like:

 Statewave Memory Quickstart  Seed Script
Target: http://localhost:8100
Seeding 2 demo users...

  Seeding dev_alice  Alice Chen...   done
  Seeding dev_bob  Bob Martinez...   done

User ID    Name           Episodes    Memories Compiled    Errors
dev_alice  Alice Chen     4           12                   0
dev_bob    Bob Martinez   6           18                   0

Done. Open http://localhost:8000 after starting the server.

What are dev_alice and dev_bob? Alice is an indie developer building a RAG-based code assistant. Bob is a developer building a Slack bot for his team. Their conversation histories cover realistic LLM API challenges: rate limits, chunking strategies, streaming, function calling, error handling, and token counting. When you chat with the demo, the assistant can reference these past conversations.

Step 5: Start the application

uvicorn app.main:app --reload

Open http://localhost:8000 in your browser.

The --reload flag makes the server restart automatically when you edit source files, which is useful during development.


Using the Web Interface

The chat interface at http://localhost:8000 is designed to make the memory layer visible, not just functional.

+---------------------------+------------------------------+
|  dev_alice  [switcher]    |  Memory State     [refresh]  |
+---------------------------+------------------------------+
|                           |                              |
|  [chat panel]             |  [memory panel]              |
|                           |                              |
|  Messages appear here.    |  Every compiled fact about   |
|  Each assistant reply     |  the current user appears    |
|  shows:                   |  here, grouped by kind:      |
|  - memory / no memory     |                              |
|  - episode ID             |  profile_fact   score: 1.00  |
|  - token count used       |  procedure      confidence: 0.94  |
|                           |  episode_summary confidence: 0.87 |
+---------------------------+------------------------------+
|  [hint chips]  What was the bug I was debugging last...  |
+----------------------------------------------------------+
|  Type a message...                          [Send]       |
+----------------------------------------------------------+

Left panel (chat): This is where you talk to the assistant. Every reply from the assistant shows a badge indicating whether memory context was used, the episode ID of this turn, and how many tokens the memory context consumed.

Right panel (memory state): This shows every compiled memory fact Statewave has for the selected user. Facts are grouped by kind, sorted by score, and each one shows which episode it came from. This panel refreshes automatically after each assistant reply so you can watch memory accumulate in real time.

User switcher (top bar): Switch between dev_alice, dev_bob, or type any custom user ID. The memory panel updates immediately to show that user's compiled facts.

Hint chips: Pre-written prompts that are good starting points for testing memory recall. Click one to send it directly.


CLI Demo Commands

If you prefer the terminal, four commands demonstrate the memory pattern:

Compare: memory vs. no memory

python -m scripts.compare compare --user dev_alice \
  --message "What was the bug I was debugging last session?"

Sends the same message to two assistants simultaneously and prints the responses side by side. The left side has no memory. The right side has Statewave context injected.

  User message  dev_alice
  What was the bug I was debugging last session?

+----------------------------------+  +----------------------------------+
|  Stateless                       |  |  Statewave-backed                |
|  No memory                       |  |  3 memories  ~280 tokens         |
|                                  |  |                                  |
|  I don't have any record of      |  |  Last session you were hitting   |
|  previous conversations. Could   |  |  429s on batch embedding jobs.   |
|  you describe the issue?         |  |  There is an undocumented per-IP |
|                                  |  |  burst limit (~500 req/10s).     |
|                                  |  |  Did the tenacity backoff fix    |
|                                  |  |  resolve it?                     |
+----------------------------------+  +----------------------------------+

Same model. Same prompt structure. Only the memory layer differs.

Inspect: see what the agent knows

python -m scripts.compare inspect --user dev_alice

Prints every compiled memory fact for a user. This is useful for understanding what context will be injected before a conversation starts.

3 memories compiled.  profile_fact: 1  procedure: 1  episode_summary: 1

+--------------------------------------------------------------------------+
| [profile_fact]  confidence=1.00  source=ep_001  tags=['role', 'python']  |
|                                                                          |
| Indie developer building a RAG-based code assistant. Python only.        |
+--------------------------------------------------------------------------+

+--------------------------------------------------------------------------+
| [procedure]  confidence=0.94  source=ep_002  tags=['embeddings', '429']  |
|                                                                          |
| Intermittent 429 errors on batch embed jobs. Likely undocumented         |
| per-IP burst limit. Workaround: tenacity retry with exponential backoff. |
+--------------------------------------------------------------------------+

confidence is Statewave's confidence in the memory fact. source is the episode ID that generated it. When the token budget is tight, higher-confidence facts are always injected first.

Budget: token budget enforcement

python -m scripts.compare budget --user dev_bob

Calls Statewave three times with different token budgets and shows how many memories fit at each limit.

+------------+----------------+-------------------+--------------------------+
| max_tokens | token_estimate | memories_returned | context preview          |
+------------+----------------+-------------------+--------------------------+
|        200 |            178 |                 2 | Dev building Slack bot   |
|        500 |            443 |                 4 | Dev building Slack bot   |
|        800 |            761 |                 6 | Dev building Slack bot   |
+------------+----------------+-------------------+--------------------------+

dev_bob has 6 sessions  ~2,800 tokens of raw history
Statewave at max_tokens=800  ~761 tokens of ranked signal

This demonstrates the core value proposition: raw history grows linearly with no ranking. Statewave always fits within your budget and always returns the highest-value facts first.

Chat: interactive terminal session

python -m scripts.compare chat --user dev_alice

Opens an interactive terminal chat session backed by Statewave. Each response shows how many memories were used and the token estimate. Each turn is recorded as a new episode, so memory accumulates as you chat.


API Reference

The application exposes four HTTP endpoints.

POST /api/v1/chat

Send a user message and receive a memory-backed assistant response.

Request body

{
  "user_id": "dev_alice",
  "message": "How do I handle 429s in batch jobs?"
}
Field Type Required Description
user_id string Yes The subject ID. Any string. Created automatically on first use.
message string Yes The user's message.

Response body

{
  "user_id": "dev_alice",
  "message": "How do I handle 429s in batch jobs?",
  "response": "You hit this last session. Use tenacity with exponential backoff...",
  "episode_id": "ep_0012",
  "context_used": true,
  "token_estimate": 283
}
Field Type Description
response string The assistant's reply.
episode_id string The Statewave episode ID for this turn.
context_used boolean true if at least one memory fact was injected.
token_estimate integer Tokens consumed by the assembled memory context.

GET /api/v1/memory/{user_id}

Returns all compiled memory facts for a user. Useful for debugging what the assistant knows before a conversation starts.

curl http://localhost:8000/api/v1/memory/dev_alice

GET /health

Returns { "status": "ok" }. Used by the UI health indicator in the top bar.


Project Structure

statewave-personal-assistant/
|
|-- app/                            Main application package
|   |-- api/
|   |   +-- routes.py              Three endpoints: POST /chat, GET /memory/{user_id}, POST /compare
|   |-- core/
|   |   +-- config.py              All settings loaded from .env via Pydantic
|   |-- data/
|   |   +-- profiles.py            dev_alice and dev_bob episode histories (seed data)
|   |-- models/
|   |   |-- chat.py                ChatRequest and ChatResponse Pydantic schemas
|   |   +-- memory.py              ContextBundle, MemoryState Pydantic schemas
|   |-- services/
|   |   |-- llm.py                 Async OpenAI wrapper; injects assembled_context
|   |   +-- statewave.py           Statewave HTTP client (record, compile, context, search)
|   |-- static/
|   |   +-- index.html             Single-page chat UI (no build step required)
|   +-- main.py                    FastAPI app; mounts static files; defines GET /
|
|-- scripts/
|   |-- seed.py                    Reads profiles.py, writes episodes, compiles memories
|   +-- compare.py                 CLI commands: compare / inspect / budget / chat
|
|-- tests/                         24 tests; all HTTP calls are mocked
|   |-- conftest.py                Shared fixtures (mock Statewave, mock OpenAI)
|   |-- test_routes.py             Route-level integration tests
|   |-- test_statewave_client.py   Unit tests for the Statewave client
|   +-- test_models.py             Pydantic model validation tests
|
|-- .env.example                   Copy this to .env and fill in your keys
|-- pyproject.toml                 Dependencies and project metadata
+-- README.md

Key files to read first

If you want to understand the memory pattern and adapt it to your own project, read these three files in order:

  1. app/services/statewave.py -- The Statewave client. Four methods: record_episode, compile_memories, get_context, list_memories. This is the complete memory integration surface.
  2. app/api/routes.py -- The chat route. Shows exactly where get_context is called (before the LLM) and where record_episode is called (after).
  3. app/services/llm.py -- The LLM wrapper. Shows how assembled_context is inserted into the system prompt.

Extending This

Swap the LLM provider

Replace app/services/llm.py with any OpenAI-compatible provider (Azure OpenAI, Anthropic via a compatibility layer, local Ollama). The Statewave layer is completely provider-agnostic. assembled_context is a plain string that goes into any system prompt.

Add a real user

Call POST /api/v1/chat with any user_id string. Statewave creates the subject on the first episode write. No registration or setup step is needed. After a few turns, call POST /v1/memories/compile (via the Statewave API directly, or through your own scheduled job) to compile memories from their episodes.

Replace the demo data with your own

Edit app/data/profiles.py and re-run python -m scripts.seed. Each profile is a list of { user_message, assistant_response } pairs. Statewave extracts structured memory facts from each pair automatically during compilation.

Run memory compilation on a schedule

In production, you would not compile memories after every single episode. Instead, run compilation on a schedule: after every N episodes, or on a daily cron job. The Statewave API supports async compilation via POST /v1/memories/compile with "async": true, which returns a job_id you can poll.

Deploy Statewave in production

Statewave runs on any PostgreSQL instance with the pgvector extension enabled. Point STATEWAVE_BASE_URL at your deployed instance. If you enable Statewave's optional API key authentication, set STATEWAVE_API_KEY in your environment. The client in app/services/statewave.py already handles the X-Api-Key header.


Integration Guide: Other Frameworks

The entire memory layer is two calls: _get_context before the LLM/agent call, _record_episode after. This is already implemented, framework-agnostic, and copy-paste ready in statewave_integration.py — the snippets below adapt its memory_chat pattern to two other stacks. In both cases, the Statewave calls themselves do not change; only how you call the model does.

Claude (Anthropic Messages API)

The Anthropic Messages API takes system as a top-level parameter, not a {"role": "system"} entry in the messages list — that is the one real difference from the OpenAI-shaped code in statewave_integration.py.

from anthropic import AsyncAnthropic
from statewave_integration import _get_context, _record_episode

_client = AsyncAnthropic()  # reads ANTHROPIC_API_KEY from the environment

async def memory_chat_claude(user_id: str, message: str) -> str:
    # 1. Same as the OpenAI pattern — fetch ranked, token-bounded memory.
    context = await _get_context(user_id, message)
    system = "You are a helpful assistant."
    if context.strip():
        system += f"\n\n## What you know about this user\n{context}"

    # 2. Framework-specific part: Claude wants `system` outside `messages`.
    response = await _client.messages.create(
        model="claude-sonnet-5",
        max_tokens=512,
        system=system,
        messages=[{"role": "user", "content": message}],
    )
    reply = response.content[0].text

    # 3. Same as the OpenAI pattern — record the turn.
    await _record_episode(user_id, message, reply)
    return reply

Identical to the OpenAI case: both _get_context and _record_episode calls. Framework-specific: system is a top-level messages.create() argument, and the reply is read from response.content[0].text instead of completion.choices[0].message.content.

CrewAI

CrewAI has no single "system prompt" injection point the way a raw chat completion call does — a crew is built from Agents with a backstory and Tasks with a description, and you call crew.kickoff() once. The natural integration point is injecting assembled_context into the agent's backstory at construction time, and recording the episode from the final crew output.

from crewai import Agent, Crew, Task
from statewave_integration import _get_context, _record_episode

async def memory_chat_crewai(user_id: str, message: str) -> str:
    # 1. Same as the OpenAI pattern — fetch ranked, token-bounded memory.
    context = await _get_context(user_id, message)
    backstory = "You are a helpful assistant."
    if context.strip():
        backstory += f"\n\n## What you know about this user\n{context}"

    # 2. Framework-specific part: memory goes into the agent's backstory,
    #    not a chat message — CrewAI has no per-call system prompt.
    assistant = Agent(role="Assistant", goal="Help the user", backstory=backstory)
    task = Task(description=message, agent=assistant, expected_output="A helpful reply")
    crew = Crew(agents=[assistant], tasks=[task])

    result = crew.kickoff()
    reply = str(result)

    # 3. Same as the OpenAI pattern — record the turn.
    await _record_episode(user_id, message, reply)
    return reply

Identical to the OpenAI case: both _get_context and _record_episode calls. Framework-specific: memory is injected via Agent(backstory=...) at construction time rather than a per-request system message, and crew.kickoff() is synchronous — wrap it in asyncio.to_thread if you need it off the event loop in a real async app.


Running Tests

No API keys or running servers are required to run the test suite. All HTTP calls to both the LLM and Statewave are mocked.

# Run all 24 tests
pytest -v

# Run tests for a specific file
pytest tests/test_routes.py -v

# Run a single test by name
pytest tests/test_routes.py::test_chat_endpoint -v

# Lint the codebase
ruff check .

# Type check
mypy app scripts

Environment Variables

Copy .env.example to .env and configure the variables below. Only LLM_API_KEY is required to get started.

Variable Required Default Description
LLM_API_KEY Yes API key for your LLM provider. Works with any OpenAI-compatible API.
OPENAI_MODEL No gpt-4o-mini Model identifier passed to the LLM API.
STATEWAVE_BASE_URL No http://localhost:8100 Base URL of the Statewave server. Change this for production deployments.
STATEWAVE_MAX_TOKENS No 800 Maximum tokens Statewave may use for an assembled context bundle. Lower values = fewer facts injected but less LLM cost per request.
STATEWAVE_API_KEY No API key for Statewave, if you have enabled authentication on your Statewave instance. Leave empty for local development.
HOST No 0.0.0.0 Address the application server binds to.
PORT No 8000 Port the application server listens on.
LOG_LEVEL No info Logging verbosity. One of debug, info, warning, error, critical.
CORS_ORIGINS No http://localhost:8000,... Comma-separated list of allowed CORS origins. Set this for production deployments.

Operational Notes

Latency and cost on the hot path

get_context is awaited directly in the request path (app/api/routes.py, in both chat() and compare()) — it blocks the LLM call from starting. Every memory-backed request pays one extra network round-trip compared to a no-memory baseline. The actual added latency depends entirely on your Statewave deployment (co-located with your app vs. cross-region, cold vs. warm connection pool), so don't take a number from anyone's README — measure it in your own environment. Every response already carries an X-Request-ID header (set by request_id_middleware in app/main.py), which you can correlate with your own timing/logging to see the real number for your setup.

Concurrent writes to the same subject

Episodes are appended, not merged, so concurrent record_episode calls for the same subject_id are safe — there's no read-modify-write race on the episode log itself. Memory compilation (compile_memories / compile_memories_wait) is different: it processes a subject's episode backlog into compiled memories, and in this reference app it's only triggered explicitly (from scripts/seed.py), not on every request. If your app triggers compilation from request handlers, simultaneous compilation calls for the same subject need external coordination (a lock, or a single scheduled job) — this is exactly why the "Run memory compilation on a schedule" guidance above recommends a periodic job rather than compiling inline.

Troubleshooting

Symptom Cause Fix
curl: (7) Failed to connect to localhost:8100, or app logs "Cannot connect to Statewave" Statewave server isn't running Start it — npx @statewavedev/statewave (see 5-Minute Path) or docker compose up -d in the cloned statewave repo.
uvicorn crashes on startup with TypeError: Router.__init__() got an unexpected keyword argument 'on_startup' (or a similar FastAPI/Starlette internals error) Your shell's venv isn't activated, so uvicorn/python resolve to a global Python install with a mismatched FastAPI/Starlette pair instead of this project's pinned versions Activate the venv first — .venv\Scripts\Activate.ps1 (Windows PowerShell) or source .venv/bin/activate (macOS/Linux/Git Bash) — or invoke it explicitly: .venv\Scripts\python.exe -m uvicorn app.main:app --reload.
GET /api/v1/memory/{user_id} returns entries: [] even after chatting Episodes were recorded but never compiled — memory compilation is a separate, explicit step from episode recording Re-run python -m scripts.seed, or trigger compile_memories for that subject yourself.
The assistant's response doesn't reflect something the user just said Same root cause as above: get_context only returns already-compiled memories, not raw uncompiled episodes Compile memories after enough new episodes accumulate (see "Episode" vs. "Memory" in Core Concepts) rather than expecting every episode to be immediately queryable.

Frequently Asked Questions

Do I need a Statewave account? No. Statewave is self-hosted and open source (Apache 2.0). You run it yourself with Docker. There is no SaaS plan, no sign-up, and no vendor API key.

Does this only work with OpenAI? No. The LLM service in app/services/llm.py uses the OpenAI Python SDK, but any provider that is compatible with the OpenAI API format will work. The Statewave layer is completely decoupled from the LLM provider.

What happens if Statewave is down? The record_episode call at the end of each request is fire-and-forget: if it fails, the chat response is still returned to the user. The get_context call at the start of the request will return an empty context on failure, so the assistant responds without memory rather than returning an error.

How do memories get updated over time? New episodes are recorded after every conversation turn. Memories are compiled from episodes when you call POST /v1/memories/compile. In this demo, compilation happens during the seed step. In production, you would run compilation periodically, for example after every 5 new episodes or on a nightly cron job.

Can I see exactly what context was injected for a given response? Yes. Each ChatResponse includes episode_id and token_estimate. You can call GET /api/v1/memory/{user_id} to see all compiled memories, and the right panel of the web UI shows the same information live after each response.


License

MIT. See LICENSE.


Related Resources

About

FastAPI reference app for Statewave memory integration. Wraps every chat turn with get_context before the LLM call and record_episode after; seeded demo users show compiled facts and token-budgeted context. Self-hosted, Postgres + pgvector.

Topics

Resources

Stars

28 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages