Compare commits

...
Author SHA1 Message Date
Nicolò Boschi ec137fad33 docs: remove versioned docs for 0.5 and lower
Drop Docusaurus versioned snapshots for 0.3, 0.4, and 0.5
(versioned_docs + versioned_sidebars) and remove their entries from
versions.json. Keeps 0.6, 0.7, and 0.8.

docusaurus.config.ts reads versions.json dynamically, so no config
changes are required.
2026-06-08 17:40:25 +02:00
103 changed files with 1 additions and 25096 deletions
@@ -1,192 +0,0 @@
# Admin CLI
The `hindsight-admin` CLI provides administrative commands for managing your Hindsight deployment, including database migrations, backup, and restore operations.
## Installation
The admin CLI is included with the `hindsight-api` package:
```bash
pip install hindsight-api
# or
uv add hindsight-api
```
## Commands
### run-db-migration
Run database migrations to the latest version. This is useful when you want to run migrations separately from API startup (e.g., in CI/CD pipelines or before deploying a new version).
```bash
hindsight-admin run-db-migration [OPTIONS]
```
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to run migrations on | `public` |
**Examples:**
```bash
# Run migrations on the default public schema
hindsight-admin run-db-migration
# Run migrations on a specific tenant schema
hindsight-admin run-db-migration --schema tenant_acme
```
:::tip Disabling Auto-Migrations
To disable automatic migrations on API startup, set `HINDSIGHT_API_RUN_MIGRATIONS_ON_STARTUP=false`. This is useful when you want to run migrations as a separate step in your deployment pipeline.
:::
---
### backup
Create a backup of all Hindsight data to a zip file.
```bash
hindsight-admin backup OUTPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `OUTPUT` | Output file path (will add `.zip` extension if not present) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to backup | `public` |
**Examples:**
```bash
# Backup to a file
hindsight-admin backup /backups/hindsight-2024-01-15.zip
# Backup a specific tenant schema
hindsight-admin backup /backups/tenant-acme.zip --schema tenant_acme
```
The backup includes:
- Memory banks and their configuration
- Documents and chunks
- Entities and their relationships
- Memory units (facts, experiences, opinions, observations)
- Entity cooccurrences and memory links
:::note Consistency
Backups are created within a database transaction with `REPEATABLE READ` isolation, ensuring a consistent snapshot across all tables.
:::
---
### restore
Restore data from a backup file. **Warning: This deletes all existing data in the target schema.**
```bash
hindsight-admin restore INPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `INPUT` | Input backup file (.zip) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to restore to | `public` |
| `--yes`, `-y` | Skip confirmation prompt | `false` |
**Examples:**
```bash
# Restore with confirmation prompt
hindsight-admin restore /backups/hindsight-2024-01-15.zip
# Restore without confirmation (for scripts)
hindsight-admin restore /backups/hindsight-2024-01-15.zip --yes
# Restore to a specific tenant schema
hindsight-admin restore /backups/tenant-acme.zip --schema tenant_acme --yes
```
:::warning Data Loss
Restore will **delete all existing data** in the target schema before importing the backup. Always verify you have a recent backup before performing a restore.
:::
---
### decommission-worker
Release all tasks owned by a worker, resetting them from "processing" back to "pending" status so they can be picked up by other workers.
```bash
hindsight-admin decommission-worker WORKER_ID [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `WORKER_ID` | ID of the worker to decommission |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema | `public` |
**Examples:**
```bash
# Before scaling down - release tasks from workers being removed
hindsight-admin decommission-worker hindsight-worker-4
hindsight-admin decommission-worker hindsight-worker-3
# Release tasks from a crashed worker
hindsight-admin decommission-worker worker-2
# For a specific tenant schema
hindsight-admin decommission-worker worker-1 --schema tenant_acme
```
**When to Use:**
- **Scaling down**: Before removing worker replicas in Kubernetes
- **Graceful removal**: When taking a worker offline for maintenance
- **Crash recovery**: If a worker crashed while processing tasks
- **Stuck worker**: When a worker is unresponsive
:::tip Finding Worker IDs
Worker IDs default to the hostname. In Kubernetes StatefulSets, this is the pod name (e.g., `hindsight-worker-0`). You can also set a custom ID with `HINDSIGHT_API_WORKER_ID` or `--worker-id`.
:::
---
## Environment Variables
The admin CLI uses the same environment variables as the API service. The most important one is:
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | `pg0` (embedded) |
**Example:**
```bash
# Use a specific database
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
hindsight-admin backup /backups/mybackup.zip
```
@@ -1,152 +0,0 @@
---
sidebar_position: 8
---
# Documents
Track and manage document sources in your memory bank. Documents provide traceability — knowing where memories came from.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import documentsPy from '!!raw-loader!@site/examples/api/documents.py';
import documentsMjs from '!!raw-loader!@site/examples/api/documents.mjs';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## What Are Documents?
Documents are containers for retained content. They help you:
- **Track sources** — Know which PDF, conversation, or file a memory came from
- **Update content** — Re-retain a document to update its facts
- **Delete in bulk** — Remove all memories from a document at once
- **Organize memories** — Group related facts by source
## Chunks
When you retain content, Hindsight splits it into chunks before extracting facts. These chunks are stored alongside the extracted memories, preserving the original text segments.
**Why chunks matter:**
- **Context preservation** — Chunks contain the raw text that generated facts, useful when you need the exact wording
- **Richer recall** — Including chunks in recall provides surrounding context for matched facts
:::tip Include Chunks in Recall
Use `include_chunks=True` in your recall calls to get the original text chunks alongside fact results. See [Recall](./recall) for details.
:::
## Retain with Document ID
Associate retained content with a document:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Retain content with document ID
hindsight memory retain my-bank "Meeting notes content..." --doc-id notes-2024-03-15
# Batch retain from files
hindsight memory retain-files my-bank docs/
```
</TabItem>
</Tabs>
## Update Documents
Re-retaining with the same document_id **replaces** the old content:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-update" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-update" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Original
hindsight memory retain my-bank "Project deadline: March 31" --doc-id project-plan
# Update
hindsight memory retain my-bank "Project deadline: April 15 (extended)" --doc-id project-plan
```
</TabItem>
</Tabs>
## Get Document
Retrieve a document's original text and metadata. This is useful for expanding document context after a recall operation returns memories with document references.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-get" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-get" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document get my-bank meeting-2024-03-15
```
</TabItem>
</Tabs>
## Delete Document
Remove a document and all its associated memories:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-delete" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-delete" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document delete my-bank meeting-2024-03-15
```
</TabItem>
</Tabs>
:::warning
Deleting a document permanently removes all memories extracted from it. This action cannot be undone.
:::
## Document Response Format
```json
{
"id": "meeting-2024-03-15",
"bank_id": "my-bank",
"original_text": "Alice presented the Q4 roadmap...",
"content_hash": "abc123def456",
"memory_unit_count": 12,
"created_at": "2024-03-15T14:00:00Z",
"updated_at": "2024-03-15T14:00:00Z"
}
```
## Next Steps
- [**Entities**](./entities) — Track people, places, and concepts
- [**Operations**](./operations) — Monitor background tasks
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,112 +0,0 @@
---
sidebar_position: 7
---
# Entities
Entities are the people, organizations, places, and concepts that Hindsight automatically extracts and tracks across your memory bank.
:::info Automatic Feature
You don't need to do anything to use entities—Hindsight extracts them automatically when you call `retain`. However, understanding how entities work is important because they power key features in [recall](./recall) and [reflect](./reflect).
:::
## Why Entities Matter
Entities improve recall quality in two ways:
1. **Co-occurrence tracking** — When entities appear together in facts, Hindsight builds a graph of relationships. This enables graph-based recall to find indirect connections.
2. **Observations** — Hindsight synthesizes high-level summaries about each entity from multiple facts. Including entity observations in recall provides richer context.
## What Gets Extracted?
When you retain information, the LLM extracts named entities from each fact:
- **People** — Names like "Alice", "Dr. Smith", "CEO John"
- **Organizations** — Companies, teams, institutions
- **Places** — Cities, countries, specific locations
- **Products/Objects** — Software, tools, significant items
- **Concepts** — Abstract themes like "career growth", "friendship"
**Example:**
```
Content: "Alice works at Google in Mountain View. She specializes in TensorFlow."
Entities extracted:
- Alice (person)
- Google (organization)
- Mountain View (location)
- TensorFlow (product)
```
## Entity Resolution
When the same entity is mentioned multiple times (possibly with different names), Hindsight resolves them to a single canonical entity using a scoring algorithm:
### Resolution Factors
1. **Name similarity (50%)** — How closely the text matches existing entity names. Handles variations like "Alice" vs "Alice Chen" or partial matches.
2. **Co-occurrence (30%)** — Entities that frequently appear together are more likely to be the same. If "Alice" always appears with "Google" and "TensorFlow", a new mention of "Alice" near those entities scores higher for matching.
3. **Temporal proximity (20%)** — Recent mentions are weighted more heavily. If an entity was seen in the last 7 days, new similar mentions are more likely to match.
### Resolution Threshold
A match requires a combined score above **0.6** (60%). Below this threshold, Hindsight creates a new entity rather than risk merging distinct entities.
This means:
- Exact name matches with recent co-occurring entities → strong match
- Partial name matches without context → likely creates new entity
- Same name in completely different contexts → may create separate entities
## Entity Observations
Observations are **derived state**—high-level summaries that Hindsight automatically synthesizes from the facts associated with an entity. They provide a condensed view of what the system knows about important entities.
**Example:**
Facts about Alice:
- "Alice works at Google"
- "Alice is a software engineer"
- "Alice specializes in ML"
- "Alice joined Google in 2020"
- "Alice leads the search team"
Observation created:
- "Alice is a software engineer at Google who joined in 2020, specializes in ML, and leads the search team"
### How Observations Work
Observations are **not generated for every entity**. When you retain new documents:
1. **Top entities selected** — Hindsight identifies the top 5 most-mentioned entities in the batch
2. **Threshold check** — Only entities with at least 5 facts get observations
3. **Regeneration** — Observations are regenerated using the entity's most recent 50 facts
4. **Old observations replaced** — Previous observations are deleted and new ones created
This means:
- Frequently mentioned entities get observations; rarely mentioned ones don't
- Observations stay up-to-date as new information is retained
- The system prioritizes entities that matter most to your memory bank
### Observations vs Opinions
Observations are **objective summaries**—they synthesize facts without any bias or perspective. This is different from [opinions](./opinions), which are influenced by the memory bank's disposition.
| | Observations | Opinions |
|---|---|---|
| **Purpose** | Summarize what's known about an entity | Express the bank's perspective on a topic |
| **Disposition influence** | No | Yes |
| **Scope** | Per-entity | Any topic |
| **Generation** | Automatic (top entities) | On-demand via reflect |
### Using Observations
Observations are included in recall results when you set `include_entities=True`. They provide quick context about key entities without retrieving all underlying facts.
## Next Steps
- [**Recall**](./recall) — Use entities in memory retrieval
- [**Reflect**](./reflect) — Get entity-aware responses
@@ -1,141 +0,0 @@
---
sidebar_position: 2
---
# Main Methods
Hindsight provides three core operations: **retain**, **recall**, and **reflect**.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mainMethodsPy from '!!raw-loader!@site/examples/api/main-methods.py';
import mainMethodsMjs from '!!raw-loader!@site/examples/api/main-methods.mjs';
:::tip Prerequisites
Make sure you've [installed Hindsight](../installation) and completed the [Quick Start](./quickstart).
:::
## Retain: Store Information
Store conversations, documents, and facts into a memory bank.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Store a single fact
hindsight retain my-bank "Alice joined Google in March 2024 as a Senior ML Engineer"
# Store from a file
hindsight retain my-bank --file conversation.txt --context "Daily standup"
# Store multiple files
hindsight retain my-bank --files docs/*.md
```
</TabItem>
</Tabs>
**What happens:** Content is processed by an LLM to extract rich facts, identify entities, and build connections in a knowledge graph.
**See:** [Retain Details](./retain) for advanced options and parameters.
---
## Recall: Search Memories
Search for relevant memories using multi-strategy retrieval.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-recall" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-recall" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic search
hindsight recall my-bank "What does Alice do at Google?"
# Search with options
hindsight recall my-bank "What happened last spring?" \
--budget high \
--max-tokens 8192 \
--fact-type world
# Verbose output (shows weights and sources)
hindsight recall my-bank "Tell me about Alice" -v
```
</TabItem>
</Tabs>
**What happens:** Four search strategies (semantic, keyword, graph, temporal) run in parallel, results are fused and reranked.
**See:** [Recall Details](./recall) for tuning quality vs latency.
---
## Reflect: Reason with Disposition
Generate disposition-aware responses that form opinions based on evidence.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-reflect" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-reflect" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic reflect
hindsight reflect my-bank "Should we adopt TypeScript for our backend?"
# Verbose output (shows sources and opinions)
hindsight reflect my-bank "What are Alice's strengths for the team lead role?" -v
# With higher reasoning budget
hindsight reflect my-bank "Analyze our tech stack" --budget high
```
</TabItem>
</Tabs>
**What happens:** Memories are recalled, bank disposition is loaded, LLM reasons through evidence, new opinions are formed and stored.
**See:** [Reflect Details](./reflect) for disposition configuration.
---
## Comparison
| Feature | Retain | Recall | Reflect |
|---------|--------|--------|---------|
| **Purpose** | Store information | Find information | Reason about information |
| **Input** | Raw text/documents | Search query | Question/prompt |
| **Output** | Memory IDs | Ranked facts | Reasoned response + opinions |
| **Uses LLM** | Yes (extraction) | No | Yes (generation) |
| **Forms opinions** | No | No | Yes |
| **Disposition** | No | No | Yes |
---
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Tuning search quality and performance
- [**Reflect**](./reflect) — Configuring disposition and opinions
- [**Memory Banks**](./memory-banks) — Managing memory bank disposition
@@ -1,88 +0,0 @@
---
sidebar_position: 6
---
# Memory Banks
Memory banks are isolated containers that store all memory-related data for a specific context or use case.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
import memoryBanksMjs from '!!raw-loader!@site/examples/api/memory-banks.mjs';
## What is a Memory Bank?
A memory bank is a complete, isolated storage unit containing:
- **Memories** — Facts and information retained from conversations
- **Documents** — Files and content indexed for retrieval
- **Entities** — People, places, concepts extracted from memories
- **Relationships** — Connections between entities in the knowledge graph
Banks are completely isolated from each other — memories stored in one bank are not visible to another.
You don't need to pre-create a bank. Hindsight will automatically create it with default settings when you first use it.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Creating a Memory Bank
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="create-bank" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="create-bank" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Set background
hindsight bank background my-bank "I am a research assistant specializing in ML"
# Set disposition
hindsight bank disposition my-bank \
--skepticism 4 \
--literalism 3 \
--empathy 3
```
</TabItem>
</Tabs>
## Background and Disposition
Background and disposition are optional settings that influence how the bank forms opinions during [reflect](./reflect) operations.
:::info
Background and disposition only affect the `reflect` operation (opinion formation). They do not impact `retain`, `recall`, or other memory operations.
:::
### Background
The background is a first-person narrative providing context for opinion formation:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="bank-background" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="bank-background" language="javascript" />
</TabItem>
</Tabs>
### Disposition Traits
Disposition traits influence how opinions are formed during reflection. Each trait is scored 1 to 5:
| Trait | Low (1) | High (5) |
|-------|---------|----------|
| **Skepticism** | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | Flexible interpretation, reads between the lines | Literal interpretation, takes things exactly as stated |
| **Empathy** | Detached, focuses on facts and logic | Empathetic, considers emotional context |
@@ -1,97 +0,0 @@
---
sidebar_position: 9
---
# Operations
Background tasks that Hindsight executes asynchronously.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## How Operations Work
Hindsight processes several types of tasks in the background to maintain memory quality and consistency. These operations run automatically—you don't need to trigger them manually.
By default, all background operations are executed in-process within the API service.
:::note Kafka Integration
Support for external streaming platforms like Kafka for scale-out processing is planned but **not available out of the box** in the current release.
:::
## Operation Types
| Operation | Trigger | Description |
|-----------|---------|-------------|
| **batch_retain** | `retain_batch` with `async=True` | Processes large content batches in the background |
| **form_opinion** | After each `reflect` call | Extracts and stores new opinions formed during reflection |
| **reinforce_opinion** | After `retain` | Updates opinion confidence based on new supporting evidence |
| **regenerate_observations** | Bank profile update | Regenerates entity observations when disposition changes |
## Async Retain Example
When retaining large batches of memories, use `async=true` to process in the background. The response includes an `operation_id` that you can use to poll for completion.
### 1. Submit async retain request
```bash
curl -X POST "http://localhost:8000/v1/default/banks/my-bank/memories" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"content": "Alice joined Google in 2023"},
{"content": "Bob prefers Python over JavaScript"}
],
"async": true
}'
```
Response:
```json
{
"success": true,
"bank_id": "my-bank",
"items_count": 2,
"async": true,
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
### 2. Poll for operation status
```bash
curl "http://localhost:8000/v1/default/banks/my-bank/operations"
```
Response:
```json
{
"bank_id": "my-bank",
"operations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"task_type": "retain",
"items_count": 2,
"document_id": null,
"created_at": "2024-01-15T10:30:00Z",
"status": "completed",
"error_message": null
}
]
}
```
### Operation Status Values
| Status | Description |
|--------|-------------|
| `pending` | Operation is queued and waiting to be processed |
| `completed` | Operation finished successfully |
| `failed` | Operation failed (check `error_message` for details) |
## Next Steps
- [**Documents**](./documents) — Track document sources
- [**Entities**](./entities) — Monitor entity tracking
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,135 +0,0 @@
---
sidebar_position: 5
---
# Opinions
How memory banks form, store, and evolve beliefs.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import opinionsPy from '!!raw-loader!@site/examples/api/legacy/opinions.py';
import opinionsMjs from '!!raw-loader!@site/examples/api/legacy/opinions.mjs';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## What Are Opinions?
Opinions are beliefs formed by the memory bank based on evidence and disposition. Unlike world facts (objective information received) or experience (conversations and events), opinions are **judgments** with confidence scores.
| Type | Example | Confidence |
|------|---------|------------|
| World Fact | "Python was created in 1991" | — |
| Experience | "I recommended Python to Bob" | — |
| Opinion | "Python is the best language for data science" | 0.85 |
## How Opinions Form
Opinions are created during `reflect` operations when the memory bank:
1. Retrieves relevant facts
2. Applies disposition traits
3. Forms a judgment
4. Assigns a confidence score
```mermaid
graph LR
F[Facts] --> D[Disposition Filter]
D --> J[Judgment]
J --> O[Opinion + Confidence]
O --> S[(Store)]
```
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-form" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-form" language="javascript" />
</TabItem>
</Tabs>
## Searching Opinions
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-search" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-search" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight recall my-bank "programming" --types opinion
```
</TabItem>
</Tabs>
## Opinion Evolution
Opinions change as new evidence arrives:
| Evidence Type | Effect |
|---------------|--------|
| **Reinforcing** | Confidence increases (+0.1) |
| **Weakening** | Confidence decreases (-0.15) |
| **Contradicting** | Opinion revised, confidence reset |
**Example evolution:**
```
t=0: "Python is best for data science" (0.70)
↓ New evidence: Python dominates ML libraries
t=1: "Python is best for data science" (0.85)
↓ New evidence: Julia is 10x faster for numerical computing
t=2: "Python is best for data science, though Julia is faster" (0.75)
↓ New evidence: Most teams still use Python
t=3: "Python is best for data science" (0.82)
```
## Disposition Influence
Different dispositions form different opinions from the same facts:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-disposition" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-disposition" language="javascript" />
</TabItem>
</Tabs>
## Opinions in Reflect Responses
When `reflect` uses opinions, they appear in `based_on`:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={opinionsPy} section="opinion-in-reflect" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={opinionsMjs} section="opinion-in-reflect" language="javascript" />
</TabItem>
</Tabs>
## Confidence Thresholds
Opinions below a confidence threshold may be:
- Excluded from responses
- Marked as uncertain
- Revised more easily
```python
# Low confidence opinions are held loosely
# "I think Python might be good for this" (0.45)
# High confidence opinions are stated firmly
# "Python is definitely the right choice" (0.92)
```
@@ -1,109 +0,0 @@
---
sidebar_position: 0
---
# Quick Start
Get up and running with Hindsight in 60 seconds.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import quickstartPy from '!!raw-loader!@site/examples/api/quickstart.py';
import quickstartMjs from '!!raw-loader!@site/examples/api/quickstart.mjs';
import quickstartSh from '!!raw-loader!@site/examples/api/quickstart.sh';
## Start the API Server
<Tabs>
<TabItem value="pip" label="pip (API only)">
```bash
pip install hindsight-api
export OPENAI_API_KEY=sk-xxx
export HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY
hindsight-api
```
API available at [http://localhost:8888](http://localhost:8888/docs)
</TabItem>
<TabItem value="docker" label="Docker (Full Experience)">
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
</TabItem>
</Tabs>
:::tip LLM Provider
Hindsight requires an LLM with structured output support. Recommended: **Groq** with `gpt-oss-20b` for fast, cost-effective inference.
See [LLM Providers](/developer/models#llm) for more details.
:::
---
## Use the Client
<Tabs>
<TabItem value="python" label="Python">
```bash
pip install hindsight-client
```
<CodeSnippet code={quickstartPy} section="quickstart-full" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
```bash
npm install @vectorize-io/hindsight-client
```
<CodeSnippet code={quickstartMjs} section="quickstart-full" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
<CodeSnippet code={quickstartSh} section="quickstart-full" language="bash" />
</TabItem>
</Tabs>
---
## What's Happening
| Operation | What it does |
|-----------|--------------|
| **Retain** | Content is processed, facts are extracted, entities are identified and linked in a knowledge graph |
| **Recall** | Four search strategies (semantic, keyword, graph, temporal) run in parallel to find relevant memories |
| **Reflect** | Retrieved memories are used to generate a disposition-aware response |
---
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Search and retrieval strategies
- [**Reflect**](./reflect) — Disposition-aware reasoning
- [**Memory Banks**](./memory-banks) — Configure disposition and background
- [**Server Deployment**](/developer/installation) — Docker Compose, Helm, and production setup
@@ -1,179 +0,0 @@
---
sidebar_position: 2
---
# Recall Memories
Retrieve memories using multi-strategy recall.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import recallMjs from '!!raw-loader!@site/examples/api/recall.mjs';
import recallSh from '!!raw-loader!@site/examples/api/recall.sh';
:::info How Recall Works
Learn about the four retrieval strategies (semantic, keyword, graph, temporal) and RRF fusion in the [Recall Architecture](/developer/retrieval) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Recall
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-basic" language="bash" />
</TabItem>
</Tabs>
## Recall Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | required | Natural language query |
| `types` | list | all | Filter: `world`, `experience`, `opinion` |
| `budget` | string | "mid" | Budget level: `low`, `mid`, `high` |
| `max_tokens` | int | 4096 | Token budget for results |
| `trace` | bool | false | Enable trace output for debugging |
| `include_entities` | bool | false | Include entity observations |
| `max_entity_tokens` | int | 500 | Token budget for entity observations |
| `tags` | list | None | Filter memories by tags (see [Tag Filtering](#filter-by-tags)) |
| `tags_match` | string | "any" | How to match tags: `any`, `all`, `any_strict`, `all_strict` |
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-with-options" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-with-options" language="javascript" />
</TabItem>
</Tabs>
## Filter by Fact Type
Recall specific memory types:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-world-only" language="python" />
<CodeSnippet code={recallPy} section="recall-experience-only" language="python" />
<CodeSnippet code={recallPy} section="recall-opinions-only" language="python" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-fact-type" language="bash" />
</TabItem>
</Tabs>
:::warning About Opinions
Opinions are beliefs formed during [reflect](/developer/api/reflect) operations. Unlike world facts and experience, opinions are subjective interpretations and may not represent objective truth. Depending on your use case:
- **Exclude opinions** (`types=["world", "experience"]`) when you need factual, verifiable information
- **Include opinions** when you want the agent's perspective or formed beliefs
- **Use opinions alone** (`types=["opinion"]`) only when specifically asking about the agent's views
:::
## Token Budget Management
Hindsight is built for AI agents, not humans. Traditional retrieval systems return "top-k" results, but agents don't think in terms of result counts—they think in tokens. An agent's context window is measured in tokens, and that's exactly how Hindsight measures results.
The `max_tokens` parameter lets you control how much of your agent's context budget to spend on memories:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-token-budget" language="python" />
</TabItem>
</Tabs>
This design means you never have to guess whether 10 results or 50 results will fit your context. Just specify the token budget and Hindsight returns as many relevant memories as will fit.
## Include Related Context
Beyond the core memory results, you can optionally retrieve additional context—each with its own token budget:
| Option | Parameter | Description |
|--------|-----------|-------------|
| **Chunks** | `include_chunks`, `max_chunk_tokens` | Raw text chunks that generated the memories |
| **Entity Observations** | `include_entities`, `max_entity_tokens` | Related observations about entities mentioned in results |
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-include-entities" language="python" />
</TabItem>
</Tabs>
This gives your agent richer context while maintaining precise control over total token consumption.
## Budget Levels
The `budget` parameter controls graph traversal depth:
- **"low"**: Fast, shallow retrieval — good for simple lookups
- **"mid"**: Balanced — default for most queries
- **"high"**: Deep exploration — finds indirect connections
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-budget-levels" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-budget-levels" language="javascript" />
</TabItem>
</Tabs>
## Filter by Tags
Tags enable **visibility scoping**—filter memories based on tags assigned during [retain](./retain#tagging-memories). This is essential for multi-user agents where each user should only see their own memories.
### Basic Tag Filtering
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-with-tags" language="python" />
</TabItem>
</Tabs>
### Tag Match Modes
The `tags_match` parameter controls how tags are matched:
| Mode | Behavior | Untagged Memories |
|------|----------|-------------------|
| `any` | OR: memory has ANY of the specified tags | **Included** |
| `all` | AND: memory has ALL of the specified tags | **Included** |
| `any_strict` | OR: memory has ANY of the specified tags | **Excluded** |
| `all_strict` | AND: memory has ALL of the specified tags | **Excluded** |
**Strict modes** are useful when you want to ensure only tagged memories are returned:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-strict" language="python" />
</TabItem>
</Tabs>
**AND matching** requires all specified tags to be present:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all" language="python" />
</TabItem>
</Tabs>
### Use Cases
| Scenario | Tags | Mode | Result |
|----------|------|------|--------|
| User A's memories only | `["user:alice"]` | `any_strict` | Only memories tagged `user:alice` |
| Support + feedback | `["support", "feedback"]` | `any` | Memories with either tag + untagged |
| Multi-user room | `["user:alice", "room:general"]` | `all_strict` | Only memories with both tags |
| Global + user-specific | `["user:alice"]` | `any` | Alice's memories + shared (untagged) |
@@ -1,272 +0,0 @@
---
sidebar_position: 3
---
# Reflect
Generate disposition-aware responses using retrieved memories.
When you call **reflect**, Hindsight performs a multi-step reasoning process:
1. **Recalls** relevant memories from the bank based on your query
2. **Applies** the bank's disposition traits to shape the reasoning style
3. **Generates** a contextual answer grounded in the retrieved facts
4. **Forms opinions** in the background based on the reasoning (available in subsequent calls)
The response includes the generated answer along with the facts that were used, providing full transparency into how the answer was derived.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import reflectPy from '!!raw-loader!@site/examples/api/reflect.py';
import reflectMjs from '!!raw-loader!@site/examples/api/reflect.mjs';
import reflectSh from '!!raw-loader!@site/examples/api/reflect.sh';
:::info How Reflect Works
Learn about disposition-driven reasoning and opinion formation in the [Reflect Architecture](/developer/reflect) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Usage
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-basic" language="bash" />
</TabItem>
</Tabs>
## Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | required | Question or prompt |
| `budget` | string | "low" | Budget level: `low`, `mid`, `high` |
| `context` | string | None | Additional context for the query |
| `max_tokens` | int | 4096 | Maximum tokens for the response |
| `response_schema` | object | None | JSON Schema for [structured output](#structured-output) |
| `tags` | list | None | Filter memories by tags during reflection |
| `tags_match` | string | "any" | How to match tags: `any`, `all`, `any_strict`, `all_strict` |
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `text` | string | The generated answer text |
| `based_on` | array | Facts used to generate the response |
| `structured_output` | object | Parsed structured output (when `response_schema` provided) |
| `usage` | TokenUsage | Token usage metrics for the LLM call |
The `usage` field contains:
- `input_tokens`: Number of input/prompt tokens consumed
- `output_tokens`: Number of output/completion tokens generated
- `total_tokens`: Sum of input and output tokens
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-params" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-params" language="javascript" />
</TabItem>
</Tabs>
## The Role of Context
The `context` parameter steers how the reflection is performed without impacting the memory recall. It provides situational information that helps shape the reasoning and response.
**How context is used:**
- **Shapes reasoning**: Helps understand the situation when formulating an answer
- **Disambiguates intent**: Clarifies what aspect of the query matters most
- **Does not affect recall**: The same memories are retrieved regardless of context
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-context" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-context" language="javascript" />
</TabItem>
</Tabs>
## Opinion Formation
When reflect reasons about a question, it may form new **opinions** based on the evidence in the memory bank. These opinions are created in the background and become available in subsequent `reflect` and `recall` calls.
**Why opinions matter:**
- **Consistent thinking**: Opinions ensure the memory bank maintains a coherent perspective over time
- **Evolving viewpoints**: As more information is retained, opinions can be refined or updated
- **Grounded reasoning**: Opinions are always derived from factual evidence in the memory bank
Opinions are stored as a special memory type and are automatically retrieved when relevant to future queries. This creates a natural evolution of the bank's perspective, similar to how humans form and refine their views based on accumulated experience.
## Disposition Influence
The bank's disposition affects reflect responses:
| Trait | Low (1) | High (5) |
|-------|---------|----------|
| **Skepticism** | Trusting, accepts claims | Questions and doubts claims |
| **Literalism** | Flexible interpretation | Exact, literal interpretation |
| **Empathy** | Detached, fact-focused | Considers emotional context |
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-disposition" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-disposition" language="javascript" />
</TabItem>
</Tabs>
## Using Sources
The `based_on` field shows which memories informed the response:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-sources" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-sources" language="javascript" />
</TabItem>
</Tabs>
This enables:
- **Transparency** — users see why the bank said something
- **Verification** — check if the response is grounded in facts
- **Debugging** — understand retrieval quality
## Structured Output
For applications that need to process responses programmatically, you can request structured output by providing a JSON Schema via `response_schema`. When provided, the response includes a `structured_output` field with the LLM response parsed according to the schema. The `text` field will be empty since only a single LLM call is made for efficiency.
The easiest way to define a schema is using **Pydantic models**:
<Tabs>
<TabItem value="python" label="Python">
```python
from pydantic import BaseModel
from hindsight_client import Hindsight
# Define your response structure with Pydantic
class HiringRecommendation(BaseModel):
recommendation: str
confidence: str # "low", "medium", "high"
key_factors: list[str]
risks: list[str] = []
with Hindsight() as client:
response = client.reflect(
bank_id="hiring-team",
query="Should we hire Alice for the ML team lead position?",
response_schema=HiringRecommendation.model_json_schema(),
)
# Parse structured output into Pydantic model
result = HiringRecommendation.model_validate(response.structured_output)
print(f"Recommendation: {result.recommendation}")
print(f"Confidence: {result.confidence}")
print(f"Key factors: {result.key_factors}")
```
</TabItem>
<TabItem value="node" label="Node.js">
```javascript
import { Hindsight } from "@anthropic-ai/hindsight";
const client = new Hindsight();
// Define JSON schema directly
const responseSchema = {
type: "object",
properties: {
recommendation: { type: "string" },
confidence: { type: "string", enum: ["low", "medium", "high"] },
key_factors: { type: "array", items: { type: "string" } },
risks: { type: "array", items: { type: "string" } },
},
required: ["recommendation", "confidence", "key_factors"],
};
const response = await client.reflect({
bankId: "hiring-team",
query: "Should we hire Alice for the ML team lead position?",
responseSchema: responseSchema,
});
// Structured output
console.log(response.structuredOutput.recommendation);
console.log(response.structuredOutput.keyFactors);
```
</TabItem>
<TabItem value="cli" label="CLI">
First, create a JSON schema file `schema.json`:
```json
{
"type": "object",
"properties": {
"recommendation": {"type": "string"},
"confidence": {"type": "string", "enum": ["low", "medium", "high"]},
"key_factors": {"type": "array", "items": {"type": "string"}}
},
"required": ["recommendation", "confidence", "key_factors"]
}
```
Then use the `--schema` flag:
```bash
hindsight memory reflect hiring-team \
"Should we hire Alice for the ML team lead position?" \
--schema schema.json
```
</TabItem>
</Tabs>
| Use Case | Why Structured Output Helps |
|----------|----------------------------|
| **Decision pipelines** | Parse recommendations into workflow systems |
| **Dashboards** | Extract confidence scores, risk factors for visualization |
| **Multi-agent systems** | Pass structured data between agents |
| **Auditing** | Log structured decisions with clear reasoning |
**Tips:**
- Use Pydantic's `model_json_schema()` for type-safe schema generation
- Use `model_validate()` to parse the response back into your Pydantic model
- Keep schemas focused — extract only what you need
- Use `Optional` fields for data that may not always be available
## Filter by Tags
Like [recall](./recall#filter-by-tags), reflect supports tag filtering to scope which memories are considered during reasoning. This is essential for multi-user scenarios where reflection should only consider memories relevant to a specific user.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-tags" language="python" />
</TabItem>
</Tabs>
The `tags_match` parameter works the same as in recall:
| Mode | Behavior |
|------|----------|
| `any` | OR matching, includes untagged memories |
| `all` | AND matching, includes untagged memories |
| `any_strict` | OR matching, excludes untagged memories |
| `all_strict` | AND matching, excludes untagged memories |
See [Retain API](./retain#tagging-memories) for how to tag memories and [Recall API](./recall#filter-by-tags) for more details on tag matching modes.
@@ -1,183 +0,0 @@
---
sidebar_position: 2
---
# Ingest Data
Store documents, conversations, and raw content into Hindsight to automatically extract and create memories.
When you **retain** content, Hindsight doesn't just store the raw text—it intelligently analyzes the content to extract meaningful facts, identify entities, and build a connected knowledge graph. This process transforms unstructured information into structured, queryable memories.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import retainPy from '!!raw-loader!@site/examples/api/retain.py';
import retainMjs from '!!raw-loader!@site/examples/api/retain.mjs';
import retainSh from '!!raw-loader!@site/examples/api/retain.sh';
:::info How Retain Works
Learn about fact extraction, entity resolution, and graph construction in the [Retain Architecture](/developer/retain) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Store a Single Memory
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-basic" language="bash" />
</TabItem>
</Tabs>
## The Importance of Context
The `context` parameter is crucial for guiding how Hindsight extracts memories from your content. Think of it as providing a lens through which the system interprets the information.
**Why context matters:**
- **Steers memory extraction**: Context tells the memory bank what type of information to focus on and how to interpret ambiguous content
- **Improves relevance**: Memories extracted with proper context are more accurately categorized and easier to retrieve
- **Disambiguates meaning**: The same sentence can have different implications depending on context (e.g., "the project was terminated" means different things in a career vs. product context)
## Store with Context and Date
Always provide context and event dates for optimal memory extraction:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-with-context" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-with-context" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-with-context" language="bash" />
</TabItem>
</Tabs>
The `timestamp` defaults to the current time if not specified. Providing explicit timestamps enables temporal queries like "What happened last spring?"
### Response Fields
The retain response includes:
| Field | Type | Description |
|-------|------|-------------|
| `success` | bool | Whether the operation succeeded |
| `bank_id` | string | The memory bank ID |
| `items_count` | int | Number of items processed |
| `async` | bool | Whether processed asynchronously |
| `usage` | TokenUsage | Token usage metrics for LLM calls (synchronous only) |
The `usage` field contains token metrics for cost tracking:
- `input_tokens`: Tokens consumed by prompts
- `output_tokens`: Tokens generated by the LLM
- `total_tokens`: Sum of input and output tokens
Note: `usage` is only present for synchronous operations. Async operations (`async: true`) do not return usage metrics.
## Batch Ingestion
Store multiple items in a single request. **Batch ingestion is the recommended approach** as it significantly improves performance by reducing network overhead and allowing Hindsight to optimize the memory extraction process across related content.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-batch" language="javascript" />
</TabItem>
</Tabs>
The `document_id` groups related memories for later management.
## Store from Files
<Tabs>
<TabItem value="cli" label="CLI">
```bash
# Single file
hindsight memory retain-files my-bank document.txt
# Directory (recursive by default)
hindsight memory retain-files my-bank ./documents/
```
</TabItem>
</Tabs>
## Async Ingestion
For large batches, use async ingestion to avoid blocking:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-async" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-async" language="javascript" />
</TabItem>
</Tabs>
## Tagging Memories
Tags enable **visibility scoping**—useful when one memory bank serves multiple users but each should only see relevant memories. For example, an agent that chats with multiple users can tag memories by user ID and filter during recall.
### Tag Individual Items
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-with-tags" language="python" />
</TabItem>
</Tabs>
### Apply Tags to All Items in a Batch
Use `document_tags` to apply the same tags to all items in a request:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-with-document-tags" language="python" />
</TabItem>
</Tabs>
When both `document_tags` and item-level `tags` are provided, they are merged together.
### Tag Naming Conventions
Use consistent naming patterns for tags:
| Pattern | Example | Use Case |
|---------|---------|----------|
| `user:<id>` | `user:alice` | Multi-user agent filtering |
| `session:<id>` | `session:123` | Session-based scoping |
| `room:<id>` | `room:general` | Chat room isolation |
| `topic:<name>` | `topic:feedback` | Topic categorization |
### Listing Tags
Use the list tags API to discover existing tags, useful for UI autocomplete or wildcard expansion:
```python
# List all tags in a bank
tags = client.list_tags(bank_id="my-bank")
for tag in tags.items:
print(f"{tag.tag}: {tag.count} memories")
# Search with wildcards (* matches any characters)
user_tags = client.list_tags(bank_id="my-bank", q="user:*")
admin_tags = client.list_tags(bank_id="my-bank", q="*-admin")
```
See [Recall API](./recall#filter-by-tags) for filtering memories by tags during retrieval.
@@ -1,411 +0,0 @@
# Configuration
Complete reference for configuring Hindsight services through environment variables.
Hindsight has two services, each with its own configuration prefix:
| Service | Prefix | Description |
|---------|--------|-------------|
| **API Service** | `HINDSIGHT_API_*` | Core memory engine |
| **Control Plane** | `HINDSIGHT_CP_*` | Web UI |
---
## API Service
The API service handles all memory operations (retain, recall, reflect).
### Database
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | `pg0` (embedded) |
| `HINDSIGHT_API_RUN_MIGRATIONS_ON_STARTUP` | Run database migrations on API startup | `true` |
If not provided, the server uses embedded `pg0` — convenient for development but not recommended for production.
### Database Connection Pool
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DB_POOL_MIN_SIZE` | Minimum connections in the pool | `5` |
| `HINDSIGHT_API_DB_POOL_MAX_SIZE` | Maximum connections in the pool | `100` |
| `HINDSIGHT_API_DB_COMMAND_TIMEOUT` | PostgreSQL command timeout in seconds | `60` |
| `HINDSIGHT_API_DB_ACQUIRE_TIMEOUT` | Connection acquisition timeout in seconds | `30` |
For high-concurrency workloads, increase `DB_POOL_MAX_SIZE`. Each concurrent recall/think operation can use 2-4 connections.
To run migrations manually (e.g., before starting the API), use the admin CLI:
```bash
hindsight-admin run-db-migration
# Or for a specific schema:
hindsight-admin run-db-migration --schema tenant_acme
```
### LLM Provider
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_LLM_PROVIDER` | Provider: `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio` | `openai` |
| `HINDSIGHT_API_LLM_API_KEY` | API key for LLM provider | - |
| `HINDSIGHT_API_LLM_MODEL` | Model name | `gpt-5-mini` |
| `HINDSIGHT_API_LLM_BASE_URL` | Custom LLM endpoint | Provider default |
| `HINDSIGHT_API_LLM_MAX_CONCURRENT` | Max concurrent LLM requests | `32` |
| `HINDSIGHT_API_LLM_TIMEOUT` | LLM request timeout in seconds | `120` |
| `HINDSIGHT_API_LLM_GROQ_SERVICE_TIER` | Groq service tier: `on_demand`, `flex`, `auto` | `auto` |
**Provider Examples**
```bash
# Groq (recommended for fast inference)
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
# For free tier users: override to on_demand if you get service_tier errors
# export HINDSIGHT_API_LLM_GROQ_SERVICE_TIER=on_demand
# OpenAI
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o
# Gemini
export HINDSIGHT_API_LLM_PROVIDER=gemini
export HINDSIGHT_API_LLM_API_KEY=xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
# Anthropic
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Ollama (local, no API key)
export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
export HINDSIGHT_API_LLM_MODEL=llama3
# LM Studio (local, no API key)
export HINDSIGHT_API_LLM_PROVIDER=lmstudio
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
export HINDSIGHT_API_LLM_MODEL=your-local-model
# OpenAI-compatible endpoint
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_BASE_URL=https://your-endpoint.com/v1
export HINDSIGHT_API_LLM_API_KEY=your-api-key
export HINDSIGHT_API_LLM_MODEL=your-model-name
```
### Per-Operation LLM Configuration
Different memory operations have different requirements. **Retain** (fact extraction) benefits from models with strong structured output capabilities, while **Reflect** (reasoning/response generation) can use lighter, faster models. Configure separate LLM models for each operation to optimize for cost and performance.
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_RETAIN_LLM_PROVIDER` | LLM provider for retain operations | Falls back to `HINDSIGHT_API_LLM_PROVIDER` |
| `HINDSIGHT_API_RETAIN_LLM_API_KEY` | API key for retain LLM | Falls back to `HINDSIGHT_API_LLM_API_KEY` |
| `HINDSIGHT_API_RETAIN_LLM_MODEL` | Model for retain operations | Falls back to `HINDSIGHT_API_LLM_MODEL` |
| `HINDSIGHT_API_RETAIN_LLM_BASE_URL` | Base URL for retain LLM | Falls back to `HINDSIGHT_API_LLM_BASE_URL` |
| `HINDSIGHT_API_REFLECT_LLM_PROVIDER` | LLM provider for reflect operations | Falls back to `HINDSIGHT_API_LLM_PROVIDER` |
| `HINDSIGHT_API_REFLECT_LLM_API_KEY` | API key for reflect LLM | Falls back to `HINDSIGHT_API_LLM_API_KEY` |
| `HINDSIGHT_API_REFLECT_LLM_MODEL` | Model for reflect operations | Falls back to `HINDSIGHT_API_LLM_MODEL` |
| `HINDSIGHT_API_REFLECT_LLM_BASE_URL` | Base URL for reflect LLM | Falls back to `HINDSIGHT_API_LLM_BASE_URL` |
:::tip When to Use Per-Operation Config
- **Retain**: Use models with strong structured output (e.g., GPT-4o, Claude) for accurate fact extraction
- **Reflect**: Use faster/cheaper models (e.g., GPT-4o-mini, Groq) for reasoning and response generation
- **Recall**: Does not use LLM (pure retrieval), so no configuration needed
:::
**Example: Separate Models for Retain and Reflect**
```bash
# Default LLM (used as fallback)
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o
# Use GPT-4o for retain (strong structured output)
export HINDSIGHT_API_RETAIN_LLM_MODEL=gpt-4o
# Use faster/cheaper model for reflect
export HINDSIGHT_API_REFLECT_LLM_PROVIDER=groq
export HINDSIGHT_API_REFLECT_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_REFLECT_LLM_MODEL=llama-3.3-70b-versatile
```
### Embeddings
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_EMBEDDINGS_PROVIDER` | Provider: `local`, `tei`, `openai`, `cohere`, or `litellm` | `local` |
| `HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL` | Model for local provider | `BAAI/bge-small-en-v1.5` |
| `HINDSIGHT_API_EMBEDDINGS_TEI_URL` | TEI server URL | - |
| `HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY` | OpenAI API key (falls back to `HINDSIGHT_API_LLM_API_KEY`) | - |
| `HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL` | OpenAI embedding model | `text-embedding-3-small` |
| `HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL` | Custom base URL for OpenAI-compatible API (e.g., Azure OpenAI) | - |
| `HINDSIGHT_API_COHERE_API_KEY` | Cohere API key (shared for embeddings and reranker) | - |
| `HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL` | Cohere embedding model | `embed-english-v3.0` |
| `HINDSIGHT_API_EMBEDDINGS_COHERE_BASE_URL` | Custom base URL for Cohere-compatible API (e.g., Azure-hosted) | - |
| `HINDSIGHT_API_LITELLM_API_BASE` | LiteLLM proxy base URL (shared for embeddings and reranker) | `http://localhost:4000` |
| `HINDSIGHT_API_LITELLM_API_KEY` | LiteLLM proxy API key (optional, depends on proxy config) | - |
| `HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL` | LiteLLM embedding model (use provider prefix, e.g., `cohere/embed-english-v3.0`) | `text-embedding-3-small` |
```bash
# Local (default) - uses SentenceTransformers
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=local
export HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5
# OpenAI - cloud-based embeddings
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxxxxxxxxxx # or reuses HINDSIGHT_API_LLM_API_KEY
export HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small # 1536 dimensions
# Azure OpenAI - embeddings via Azure endpoint
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=your-azure-api-key
export HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small
export HINDSIGHT_API_EMBEDDINGS_OPENAI_BASE_URL=https://your-resource.openai.azure.com/openai/deployments/your-deployment
# TEI - HuggingFace Text Embeddings Inference (recommended for production)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=tei
export HINDSIGHT_API_EMBEDDINGS_TEI_URL=http://localhost:8080
# Cohere - cloud-based embeddings
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL=embed-english-v3.0 # 1024 dimensions
# Azure-hosted Cohere - embeddings via custom endpoint
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-azure-api-key
export HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL=embed-english-v3.0
export HINDSIGHT_API_EMBEDDINGS_COHERE_BASE_URL=https://your-azure-cohere-endpoint.com
# LiteLLM proxy - unified gateway for multiple providers
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_LITELLM_API_KEY=your-litellm-key # optional
export HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL=text-embedding-3-small # or cohere/embed-english-v3.0
```
#### Embedding Dimensions
Hindsight automatically detects the embedding dimension from the model at startup and adjusts the database schema accordingly. The default model (`BAAI/bge-small-en-v1.5`) produces 384-dimensional vectors, while OpenAI models produce 1536 or 3072 dimensions.
:::warning Dimension Changes
Once memories are stored, you cannot change the embedding dimension without losing data. If you need to switch to a model with different dimensions:
1. **Empty database**: The schema is adjusted automatically on startup
2. **Existing data**: Either delete all memories first, or use a model with matching dimensions
Supported OpenAI embedding dimensions:
- `text-embedding-3-small`: 1536 dimensions
- `text-embedding-3-large`: 3072 dimensions
- `text-embedding-ada-002`: 1536 dimensions (legacy)
:::
### Reranker
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_RERANKER_PROVIDER` | Provider: `local`, `tei`, `cohere`, `flashrank`, `litellm`, or `rrf` | `local` |
| `HINDSIGHT_API_RERANKER_LOCAL_MODEL` | Model for local provider | `cross-encoder/ms-marco-MiniLM-L-6-v2` |
| `HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT` | Max concurrent local reranking (prevents CPU thrashing under load) | `4` |
| `HINDSIGHT_API_RERANKER_TEI_URL` | TEI server URL | - |
| `HINDSIGHT_API_RERANKER_TEI_BATCH_SIZE` | Batch size for TEI reranking | `128` |
| `HINDSIGHT_API_RERANKER_TEI_MAX_CONCURRENT` | Max concurrent TEI reranking requests | `8` |
| `HINDSIGHT_API_RERANKER_COHERE_MODEL` | Cohere rerank model | `rerank-english-v3.0` |
| `HINDSIGHT_API_RERANKER_COHERE_BASE_URL` | Custom base URL for Cohere-compatible API (e.g., Azure-hosted) | - |
| `HINDSIGHT_API_RERANKER_LITELLM_MODEL` | LiteLLM rerank model (use provider prefix, e.g., `cohere/rerank-english-v3.0`) | `cohere/rerank-english-v3.0` |
```bash
# Local (default) - uses SentenceTransformers CrossEncoder
export HINDSIGHT_API_RERANKER_PROVIDER=local
export HINDSIGHT_API_RERANKER_LOCAL_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
# TEI - for high-performance inference
export HINDSIGHT_API_RERANKER_PROVIDER=tei
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
# Cohere - cloud-based reranking
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key # shared with embeddings
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
# Azure-hosted Cohere - reranking via custom endpoint
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-azure-api-key
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-azure-cohere-endpoint.com
# LiteLLM proxy - unified gateway for multiple reranking providers
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_LITELLM_API_KEY=your-litellm-key # optional
export HINDSIGHT_API_RERANKER_LITELLM_MODEL=cohere/rerank-english-v3.0 # or voyage/rerank-2, together_ai/...
```
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
- Cohere (`cohere/rerank-english-v3.0`, `cohere/rerank-multilingual-v3.0`)
- Together AI (`together_ai/...`)
- Voyage AI (`voyage/rerank-2`)
- Jina AI (`jina_ai/...`)
- AWS Bedrock (`bedrock/...`)
### Authentication
By default, Hindsight runs without authentication. For production deployments, enable API key authentication using the built-in tenant extension:
```bash
# Enable the built-in API key authentication
export HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
export HINDSIGHT_API_TENANT_API_KEY=your-secret-api-key
```
When enabled, all requests must include the API key in the `Authorization` header:
```bash
curl -H "Authorization: Bearer your-secret-api-key" \
http://localhost:8888/v1/default/banks
```
Requests without a valid API key receive a `401 Unauthorized` response.
:::tip Custom Authentication
For advanced authentication (JWT, OAuth, multi-tenant schemas), implement a custom `TenantExtension`. See the [Extensions documentation](./extensions.md) for details.
:::
### Server
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_HOST` | Bind address | `0.0.0.0` |
| `HINDSIGHT_API_PORT` | Server port | `8888` |
| `HINDSIGHT_API_WORKERS` | Number of uvicorn worker processes | `1` |
| `HINDSIGHT_API_LOG_LEVEL` | Log level: `debug`, `info`, `warning`, `error` | `info` |
| `HINDSIGHT_API_MCP_ENABLED` | Enable MCP server at `/mcp/{bank_id}/` | `true` |
### Retrieval
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_GRAPH_RETRIEVER` | Graph retrieval algorithm | `link_expansion` |
| `HINDSIGHT_API_RECALL_MAX_CONCURRENT` | Max concurrent recall operations per worker (backpressure) | `32` |
| `HINDSIGHT_API_RERANKER_MAX_CANDIDATES` | Max candidates to rerank per recall (RRF pre-filters the rest) | `300` |
#### Graph Retrieval Algorithm
- **`link_expansion`** (default): Fast graph expansion from semantic seeds via entity co-occurrence, semantic kNN, and causal links. Target latency under 100ms.
### Entity Observations
Controls when the system generates entity observations (summaries about entities mentioned in retained content).
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_OBSERVATION_MIN_FACTS` | Minimum facts about an entity before generating observations | `5` |
| `HINDSIGHT_API_OBSERVATION_TOP_ENTITIES` | Max entities to process per retain batch | `5` |
### Retain
Controls the retain (memory ingestion) pipeline.
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS` | Max completion tokens for fact extraction LLM calls | `64000` |
| `HINDSIGHT_API_RETAIN_CHUNK_SIZE` | Max characters per chunk for fact extraction. Larger chunks extract fewer LLM calls but may lose context. | `3000` |
| `HINDSIGHT_API_RETAIN_EXTRACTION_MODE` | Fact extraction mode: `concise` (selective, fewer high-quality facts) or `verbose` (detailed, more facts) | `concise` |
| `HINDSIGHT_API_RETAIN_EXTRACT_CAUSAL_LINKS` | Extract causal relationships between facts | `true` |
| `HINDSIGHT_API_RETAIN_OBSERVATIONS_ASYNC` | Run entity observation generation asynchronously (after retain completes) | `false` |
#### Extraction Modes
The extraction mode controls how aggressively facts are extracted from content:
- **`concise`** (default): Selective extraction that focuses on significant, long-term valuable facts. Filters out greetings, filler, and trivial information. Produces fewer but higher-quality facts with better performance.
- **`verbose`**: Detailed extraction that captures every piece of information with maximum verbosity. Produces more facts with extensive detail but slower performance and higher token usage.
### Local MCP Server
Configuration for the local MCP server (`hindsight-local-mcp` command).
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_MCP_LOCAL_BANK_ID` | Memory bank ID for local MCP | `mcp` |
| `HINDSIGHT_API_MCP_INSTRUCTIONS` | Additional instructions appended to retain/recall tool descriptions | - |
```bash
# Example: instruct MCP to also store assistant actions
export HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, including tool calls and decisions made."
```
### Distributed Workers
Configuration for background task processing. By default, the API processes tasks internally. For high-throughput deployments, run dedicated workers. See [Services - Worker Service](./services#worker-service) for details.
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_WORKER_ENABLED` | Enable internal worker in API process | `true` |
| `HINDSIGHT_API_WORKER_ID` | Unique worker identifier | hostname |
| `HINDSIGHT_API_WORKER_POLL_INTERVAL_MS` | Database polling interval in milliseconds | `500` |
| `HINDSIGHT_API_WORKER_BATCH_SIZE` | Tasks to claim per poll cycle | `10` |
| `HINDSIGHT_API_WORKER_MAX_RETRIES` | Max retries before marking task failed | `3` |
| `HINDSIGHT_API_WORKER_HTTP_PORT` | HTTP port for worker metrics/health (worker CLI only) | `8889` |
### Performance Optimization
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_SKIP_LLM_VERIFICATION` | Skip LLM connection check on startup | `false` |
| `HINDSIGHT_API_LAZY_RERANKER` | Lazy-load reranker model (faster startup) | `false` |
### Programmatic Configuration
You can also configure the API programmatically using `MemoryEngine.from_env()`:
```python
from hindsight_api import MemoryEngine
memory = MemoryEngine.from_env()
await memory.initialize()
```
---
## Control Plane
The Control Plane is the web UI for managing memory banks.
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_CP_DATAPLANE_API_URL` | URL of the API service | `http://localhost:8888` |
```bash
# Point Control Plane to a remote API service
export HINDSIGHT_CP_DATAPLANE_API_URL=http://api.example.com:8888
```
---
## Example .env File
```bash
# API Service
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
HINDSIGHT_API_LLM_PROVIDER=groq
HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
# Authentication (optional, recommended for production)
# HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
# HINDSIGHT_API_TENANT_API_KEY=your-secret-api-key
# Control Plane
HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888
```
---
For configuration issues not covered here, please [open an issue](https://github.com/vectorize-io/hindsight/issues) on GitHub.
@@ -1,149 +0,0 @@
---
sidebar_position: 7
---
# Development Guide
Guide to setting up a local development environment for contributing to Hindsight.
## Prerequisites
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) - Fast Python package manager
- Docker and Docker Compose
- An LLM API key (OpenAI, Groq, or Ollama)
## Local Development Setup
### 1. Clone the Repository
```bash
git clone https://github.com/vectorize-io/hindsight.git
cd hindsight
```
### 2. Install Dependencies
```bash
uv sync
```
### 3. Start PostgreSQL
Start only the database via Docker:
```bash
cd docker && docker-compose up -d postgres
```
### 4. Configure Environment
```bash
cp .env.example .env
```
Edit `.env` with your LLM API key:
```bash
# Database (connects to Docker postgres)
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
# LLM Provider (choose one)
HINDSIGHT_API_LLM_PROVIDER=groq
HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile
```
### 5. Start the API Server
```bash
./scripts/start-server.sh --env local
```
The server will be available at http://localhost:8888.
## Running Tests
```bash
# Run all tests
uv run pytest
# Run specific test file
uv run pytest tests/test_retrieval.py
# Run with verbose output
uv run pytest -v
```
## Code Generation
### Regenerate API Clients
When you modify the OpenAPI spec, regenerate the clients:
```bash
./scripts/generate-clients.sh
```
This generates:
- Python client in `hindsight-clients/python/`
- TypeScript client in `hindsight-clients/typescript/`
### Export OpenAPI Schema
```bash
./scripts/export-openapi.sh
```
## Project Structure
```
hindsight/
├── hindsight-api/ # Main API server
│ ├── hindsight_api/
│ │ ├── api/ # HTTP endpoints
│ │ ├── engine/ # Memory engine, retrieval, reasoning
│ │ └── web/ # Server entry point
│ └── tests/
├── hindsight-clients/ # Generated SDK clients
│ ├── python/
│ └── typescript/
├── hindsight-control-plane/ # Admin UI (Next.js)
├── docker/ # Docker Compose setup
└── scripts/ # Development scripts
```
## Contributing
1. Create a feature branch from `main`
2. Make your changes
3. Run tests: `uv run pytest`
4. Submit a pull request
## Troubleshooting
### Database Connection Issues
Ensure PostgreSQL is running:
```bash
docker-compose ps
```
Check database connectivity:
```bash
psql postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
```
### ML Model Download
On first run, Hindsight downloads embedding and reranking models. This may take a few minutes. Models are cached in `~/.cache/huggingface/`.
### Port Conflicts
If port 8888 is in use:
```bash
HINDSIGHT_API_PORT=8889 ./scripts/start-server.sh --env local
```
@@ -1,226 +0,0 @@
# Extensions
Extensions allow you to customize and extend Hindsight behavior without modifying core code. They enable multi-tenancy, custom authentication, additional HTTP endpoints, and operation hooks.
---
## Available Extensions
### TenantExtension
Handles multi-tenancy and API key authentication. Validates incoming requests and determines which PostgreSQL schema to use for database operations, enabling tenant isolation at the database level.
**Built-in: ApiKeyTenantExtension**
A simple implementation that validates API keys against an environment variable and uses the `public` schema for all authenticated requests.
```bash
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
HINDSIGHT_API_TENANT_API_KEY=your-secret-key
```
For multi-tenant setups with separate schemas per tenant (e.g., JWT-based auth with per-tenant schemas), implement a custom `TenantExtension`.
---
### HttpExtension
Adds custom HTTP endpoints under the `/ext/` path prefix. Useful for adding domain-specific APIs that integrate with Hindsight's memory engine.
**No built-in implementation** - implement your own to add custom endpoints.
```bash
HINDSIGHT_API_HTTP_EXTENSION=mypackage.ext:MyHttpExtension
```
---
### OperationValidatorExtension
Hooks into retain/recall/reflect operations for validation and monitoring. Use cases include:
- Rate limiting and quota enforcement
- Permission checks and content filtering
- Audit logging and usage tracking
- Custom metrics collection
**No built-in implementation** - implement your own based on your requirements.
```bash
HINDSIGHT_API_OPERATION_VALIDATOR_EXTENSION=mypackage.validators:MyValidator
```
---
## Writing Custom Extensions
### Extension Basics
Extensions are Python classes loaded via environment variables:
```bash
HINDSIGHT_API_<TYPE>_EXTENSION=mypackage.module:MyExtensionClass
```
Configuration is passed via prefixed environment variables:
```bash
HINDSIGHT_API_<TYPE>_SOME_CONFIG=value
# Extension receives: {"some_config": "value"}
```
All extensions support lifecycle hooks:
- `on_startup()` - Called when the application starts
- `on_shutdown()` - Called when the application shuts down
Extensions have access to an `ExtensionContext` that provides:
- `run_migration(schema)` - Run database migrations for a schema
- `get_memory_engine()` - Get the MemoryEngine interface
### Example: Custom TenantExtension with JWT
```python
import jwt
from hindsight_api.extensions import TenantExtension, TenantContext, AuthenticationError
class JwtTenantExtension(TenantExtension):
def __init__(self, config: dict[str, str]):
super().__init__(config)
self.jwt_secret = config.get("jwt_secret")
if not self.jwt_secret:
raise ValueError("HINDSIGHT_API_TENANT_JWT_SECRET is required")
async def authenticate(self, context: RequestContext) -> TenantContext:
token = context.api_key
if not token:
raise AuthenticationError("Bearer token required")
try:
payload = jwt.decode(token, self.jwt_secret, algorithms=["HS256"])
tenant_id = payload.get("tenant_id")
if not tenant_id:
raise AuthenticationError("Missing tenant_id in token")
return TenantContext(schema_name=f"tenant_{tenant_id}")
except jwt.InvalidTokenError as e:
raise AuthenticationError(str(e))
```
### Example: Custom HttpExtension
```python
from fastapi import APIRouter
from hindsight_api.extensions import HttpExtension
class MyHttpExtension(HttpExtension):
def get_router(self, memory: MemoryEngine) -> APIRouter:
router = APIRouter()
@router.get("/hello")
async def hello():
return {"message": "Hello from extension!"}
@router.post("/custom/{bank_id}/action")
async def custom_action(bank_id: str):
# Access memory engine for database operations
pool = await memory._get_pool()
# ... custom logic
return {"status": "ok"}
return router
```
Routes are available at `/ext/hello`, `/ext/custom/{bank_id}/action`, etc.
### Example: Custom OperationValidatorExtension
```python
from hindsight_api.extensions import (
OperationValidatorExtension,
ValidationResult,
RetainContext,
RecallContext,
ReflectContext,
RetainResult,
)
class MyValidator(OperationValidatorExtension):
# Pre-operation validation (required)
async def validate_retain(self, ctx: RetainContext) -> ValidationResult:
# Implement your validation logic
return ValidationResult.accept()
# Or reject: return ValidationResult.reject("Reason")
async def validate_recall(self, ctx: RecallContext) -> ValidationResult:
return ValidationResult.accept()
async def validate_reflect(self, ctx: ReflectContext) -> ValidationResult:
return ValidationResult.accept()
# Post-operation hooks (optional)
async def on_retain_complete(self, result: RetainResult) -> None:
# Log usage, update metrics, send notifications, etc.
pass
```
---
## Deploying Custom Extensions
### With Docker
Mount your extension package as a volume and set the environment variable:
```yaml
# docker-compose.yml
services:
hindsight-api:
image: vectorize/hindsight-api:latest
volumes:
- ./my_extensions:/app/my_extensions
environment:
- HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
- HINDSIGHT_API_TENANT_JWT_SECRET=${JWT_SECRET}
- PYTHONPATH=/app
```
Or build a custom image with your extensions:
```dockerfile
FROM vectorize/hindsight-api:latest
COPY my_extensions /app/my_extensions
ENV PYTHONPATH=/app
```
### Bare Metal
Install your extension package in the same Python environment as Hindsight:
```bash
# Install Hindsight
pip install hindsight-api
# Install your extension package
pip install ./my-extensions
# or
pip install my-extensions-package
# Configure
export HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
export HINDSIGHT_API_TENANT_JWT_SECRET=your-secret
# Run
hindsight-api
```
---
## Contributing Extensions
Custom extensions that solve common use cases are welcome contributions to the Hindsight project. If you've built an extension for:
- Authentication providers (OAuth, SAML, API gateways)
- Rate limiting or quota management
- Audit logging integrations
- Metrics exporters (Datadog, New Relic, etc.)
- Custom HTTP endpoints for specific platforms
Consider contributing it to the `hindsight_api.extensions.builtin` package. Open an issue or pull request on [GitHub](https://github.com/vectorize-io/hindsight) to discuss your extension.
@@ -1,121 +0,0 @@
---
sidebar_position: 1
slug: /
---
# Overview
## Why Hindsight?
AI agents forget everything between sessions. Every conversation starts from zero—no context about who you are, what you've discussed, or what the assistant has learned. This isn't just an implementation detail; it fundamentally limits what AI Agents can do.
**The problem is harder than it looks:**
- **Simple vector search isn't enough** — "What did Alice do last spring?" requires temporal reasoning, not just semantic similarity
- **Facts get disconnected** — Knowing "Alice works at Google" and "Google is in Mountain View" should let you answer "Where does Alice work?" even if you never stored that directly
- **AI Agents needs to form opinions** — A coding assistant that remembers "the user prefers functional programming" should weigh that when making recommendations
- **Context matters** — The same information means different things to different memory banks with different personalities
Hindsight solves these problems with a memory system designed specifically for AI agents.
## What Hindsight Does
```mermaid
graph TB
subgraph app["<b>Your Application</b>"]
Agent[AI Agent]
end
subgraph hindsight["<b>Hindsight</b>"]
API[API Server]
subgraph bank["<b>Memory Bank</b>"]
Documents[Documents]
Memories[Memories]
Entities[Entities]
end
end
Agent -->|retain| API
Agent -->|recall| API
Agent -->|reflect| API
API --> Documents
API --> Memories
API --> Entities
```
**Your AI agent** stores information via `retain()`, searches with `recall()`, and reasons with `reflect()` — all interactions with its dedicated **memory bank**
## Key Components
### Three Memory Types
Hindsight separates memories by type for epistemic clarity:
| Type | What it stores | Example |
|------|----------------|---------|
| **World** | Objective facts received | "Alice works at Google" |
| **Bank** | Bank's own actions | "I recommended Python to Bob" |
| **Opinion** | Formed beliefs + confidence | "Python is best for ML" (0.85) |
### Multi-Strategy Retrieval (TEMPR)
Four search strategies run in parallel:
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
| Strategy | Best for |
|----------|----------|
| **Semantic** | Conceptual similarity, paraphrasing |
| **Keyword (BM25)** | Names, technical terms, exact matches |
| **Graph** | Related entities, indirect connections |
| **Temporal** | "last spring", "in June", time ranges |
### Disposition Traits
Memory banks have disposition traits that influence how opinions are formed during Reflect:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting | Skeptical |
| **Literalism** | 1-5 | Flexible interpretation | Literal interpretation |
| **Empathy** | 1-5 | Detached | Empathetic |
These traits only affect the `reflect` operation, not `recall`.
## Next Steps
### Getting Started
- [**Quick Start**](/developer/api/quickstart) — Install and get up and running in 60 seconds
- [**RAG vs Hindsight**](/developer/rag-vs-hindsight) — See how Hindsight differs from traditional RAG with real examples
### Core Concepts
- [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts
- [**Recall**](/developer/retrieval) — How TEMPR's 4-way search retrieves memories
- [**Reflect**](/developer/reflect) — How disposition influences reasoning and opinion formation
### API Methods
- [**Retain**](/developer/api/retain) — Store information in memory banks
- [**Recall**](/developer/api/recall) — Search and retrieve memories
- [**Reflect**](/developer/api/reflect) — Reason with disposition
- [**Memory Banks**](/developer/api/memory-banks) — Configure disposition and background
- [**Documents**](/developer/api/documents) — Manage document sources
- [**Operations**](/developer/api/operations) — Monitor async tasks
### Deployment
- [**Server Setup**](/developer/installation) — Deploy with Docker Compose, Helm, or pip
@@ -1,184 +0,0 @@
# Installation
Hindsight can be deployed in several ways depending on your infrastructure and requirements.
:::tip Don't want to manage infrastructure?
**[Hindsight Cloud](https://ui.hindsight.vectorize.io/signup)** is a fully managed service that handles all infrastructure, scaling, and maintenance — [sign up here](https://ui.hindsight.vectorize.io/signup).
:::
## Prerequisites
### PostgreSQL with pgvector
Hindsight requires PostgreSQL with the **pgvector** extension for vector similarity search.
**By default**, Hindsight uses **pg0** — an embedded PostgreSQL that runs locally on your machine. This is convenient for development but **not recommended for production**.
**For production**, use an external PostgreSQL with pgvector:
- **Supabase** — Managed PostgreSQL with pgvector built-in
- **Neon** — Serverless PostgreSQL with pgvector
- **AWS RDS** / **Cloud SQL** / **Azure** — With pgvector extension enabled
- **Self-hosted** — PostgreSQL 14+ with pgvector installed
### LLM Provider
You need an LLM API key for fact extraction, entity resolution, and answer generation:
- **Groq** (recommended): Fast inference with `gpt-oss-20b`
- **OpenAI**: GPT-4o, GPT-4o-mini
- **Ollama**: Run models locally
See [Models](./models) for detailed comparison and configuration.
---
## Docker
**Best for**: Quick start, development, small deployments
Run everything in one container with embedded PostgreSQL:
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API Server**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
---
## Helm / Kubernetes
**Best for**: Production deployments, auto-scaling, cloud environments
```bash
# Install with built-in PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=true
# Or use external PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=false \
--set api.database.url=postgresql://user:[email protected]:5432/hindsight
# Install a specific version
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
# Upgrade to latest
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
```
**Requirements**:
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
- Helm 3.8+
### Distributed Workers
For high-throughput deployments, enable dedicated worker pods to scale task processing independently:
```bash
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set worker.enabled=true \
--set worker.replicaCount=3
```
See [Services - Worker Service](./services#worker-service) for configuration details and architecture.
See the [Helm chart values.yaml](https://github.com/vectorize-io/hindsight/tree/main/helm/hindsight/values.yaml) for all chart options.
---
## Bare Metal (pip)
**Best for**: Custom deployments, integration into existing Python applications
### Install
```bash
pip install hindsight-all
```
### Run with Embedded Database
For development and testing, Hindsight can run with an embedded PostgreSQL (pg0):
```bash
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
This creates a database in `~/.hindsight/data/` and starts the API on http://localhost:8888.
### Run with External PostgreSQL
For production, connect to your own PostgreSQL instance:
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
**Note**: The database must exist and have pgvector enabled (`CREATE EXTENSION vector;`).
### CLI Options
```bash
hindsight-api --port 9000 # Custom port (default: 8888)
hindsight-api --host 127.0.0.1 # Bind to localhost only
hindsight-api --workers 4 # Multiple worker processes
hindsight-api --log-level debug # Verbose logging
```
### Control Plane
The Control Plane (Web UI) can be run standalone using npx:
```bash
npx @vectorize-io/hindsight-control-plane --api-url http://localhost:8888
```
This connects to your running API server and provides a visual interface for managing memory banks, exploring entities, and testing queries.
#### Options
| Option | Environment Variable | Default | Description |
|--------|---------------------|---------|-------------|
| `-p, --port` | `PORT` | 9999 | Port to listen on |
| `-H, --hostname` | `HOSTNAME` | 0.0.0.0 | Hostname to bind to |
| `-a, --api-url` | `HINDSIGHT_CP_DATAPLANE_API_URL` | http://localhost:8888 | Hindsight API URL |
#### Examples
```bash
# Run on custom port
npx @vectorize-io/hindsight-control-plane --port 9999 --api-url http://localhost:8888
# Using environment variables
export HINDSIGHT_CP_DATAPLANE_API_URL=http://api.example.com
npx @vectorize-io/hindsight-control-plane
# Production deployment
PORT=80 HINDSIGHT_CP_DATAPLANE_API_URL=https://api.hindsight.io npx @vectorize-io/hindsight-control-plane
```
---
## Next Steps
- [Configuration](./configuration.md) — Environment variables and settings
- [Models](./models.md) — ML models and providers
- [Monitoring](./monitoring.md) — Metrics and observability
@@ -1,129 +0,0 @@
---
sidebar_position: 5
---
# MCP Server
Hindsight includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that allows AI assistants to store and retrieve memories directly.
## Access
The MCP server is **enabled by default** and mounted at `/mcp` on the API server. Each memory bank has its own MCP endpoint:
```
http://localhost:8888/mcp/{bank_id}/
```
For example, to connect to the memory bank `alice`:
```
http://localhost:8888/mcp/alice/
```
To disable the MCP server, set the environment variable:
```bash
export HINDSIGHT_API_MCP_ENABLED=false
```
## Per-Bank Endpoints
Unlike traditional MCP servers where tools require explicit identifiers, Hindsight uses **per-bank endpoints**. The `bank_id` is part of the URL path, so tools don't need to specify which bank to use—it's implicit from the connection.
This design:
- **Simplifies tool usage** — no need to pass `bank_id` with every call
- **Enforces isolation** — each MCP connection is scoped to a single bank
- **Enables multi-tenant setups** — connect different users to different endpoints
---
## Available Tools
### retain
Store information to long-term memory.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | Yes | The fact or memory to store |
| `context` | string | No | Category for the memory (default: `general`) |
**Example:**
```json
{
"name": "retain",
"arguments": {
"content": "User prefers Python over JavaScript for backend development",
"context": "programming_preferences"
}
}
```
**When to use:**
- User shares personal facts, preferences, or interests
- Important events or milestones are mentioned
- Decisions, opinions, or goals are stated
- Work context or project details are discussed
---
### recall
Search memories to provide personalized responses.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | Natural language search query |
| `max_results` | integer | No | Maximum results to return (default: 10) |
**Example:**
```json
{
"name": "recall",
"arguments": {
"query": "What are the user's programming language preferences?"
}
}
```
**Response:**
```json
{
"results": [
{
"id": "fact_abc123",
"text": "User prefers Python over JavaScript for backend development",
"type": "world",
"context": "programming_preferences",
"event_date": null
}
]
}
```
**When to use:**
- Start of conversation to recall relevant context
- Before making recommendations
- When user asks about something they may have mentioned before
- To provide continuity across conversations
---
## Integration with AI Assistants
The MCP server can be used with any MCP-compatible AI assistant.
### Claude Desktop Configuration
To connect Claude Desktop to a specific memory bank:
```json
{
"mcpServers": {
"hindsight-alice": {
"url": "http://localhost:8888/mcp/alice/"
}
}
}
```
Each user can have their own MCP server configuration pointing to their personal memory bank.
@@ -1,237 +0,0 @@
# Models
Hindsight uses several machine learning models for different tasks.
## Overview
| Model Type | Purpose | Default | Configurable |
|------------|---------|---------|--------------|
| **LLM** | Fact extraction, reasoning, generation | Provider-specific | Yes |
| **Embedding** | Vector representations for semantic search | `BAAI/bge-small-en-v1.5` | Yes |
| **Cross-Encoder** | Reranking search results | `cross-encoder/ms-marco-MiniLM-L-6-v2` | Yes |
All local models (embedding, cross-encoder) are automatically downloaded from HuggingFace on first run.
---
## LLM
Used for fact extraction, entity resolution, opinion generation, and answer synthesis.
**Supported providers:** OpenAI, Anthropic, Gemini, Groq, Ollama, LM Studio, and **any OpenAI-compatible API**
:::tip OpenAI-Compatible Providers
Hindsight works with any provider that exposes an OpenAI-compatible API (e.g., Azure OpenAI). Simply set `HINDSIGHT_API_LLM_PROVIDER=openai` and configure `HINDSIGHT_API_LLM_BASE_URL` to point to your provider's endpoint.
See [Configuration](./configuration#llm-provider) for setup examples.
:::
### Tested Models
The following models have been tested and verified to work correctly with Hindsight:
| Provider | Model |
|----------|-------|
| **OpenAI** | `gpt-5.2` |
| **OpenAI** | `gpt-5` |
| **OpenAI** | `gpt-5-mini` |
| **OpenAI** | `gpt-5-nano` |
| **OpenAI** | `gpt-4.1-mini` |
| **OpenAI** | `gpt-4.1-nano` |
| **OpenAI** | `gpt-4o-mini` |
| **Anthropic** | `claude-sonnet-4-20250514` |
| **Anthropic** | `claude-3-5-sonnet-20241022` |
| **Gemini** | `gemini-3-pro-preview` |
| **Gemini** | `gemini-2.5-flash` |
| **Gemini** | `gemini-2.5-flash-lite` |
| **Groq** | `openai/gpt-oss-120b` |
| **Groq** | `openai/gpt-oss-20b` |
### Using Other Models
Other LLM models not listed above may work with Hindsight, but they must support **at least 65,000 output tokens** to ensure reliable fact extraction. If you need support for a specific model that doesn't meet this requirement, please [open an issue](https://github.com/hindsight-ai/hindsight/issues) to request an exception.
### Configuration
```bash
# Groq (recommended)
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
# OpenAI
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o
# Gemini
export HINDSIGHT_API_LLM_PROVIDER=gemini
export HINDSIGHT_API_LLM_API_KEY=xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
# Anthropic
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Ollama (local)
export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
export HINDSIGHT_API_LLM_MODEL=llama3
# LM Studio (local)
export HINDSIGHT_API_LLM_PROVIDER=lmstudio
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
export HINDSIGHT_API_LLM_MODEL=your-local-model
```
**Note:** The LLM is the primary bottleneck for retain operations. See [Performance](./performance) for optimization strategies.
---
## Embedding Model
Converts text into dense vector representations for semantic similarity search.
**Default:** `BAAI/bge-small-en-v1.5` (384 dimensions, ~130MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers (default) | Development, low latency |
| `openai` | OpenAI embeddings API | Production, high quality |
| `cohere` | Cohere embeddings API | Production, multilingual |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
### Local Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `BAAI/bge-small-en-v1.5` | 384 | Default, fast, good quality |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
### OpenAI Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `text-embedding-3-small` | 1536 | Default OpenAI, cost-effective |
| `text-embedding-3-large` | 3072 | Higher quality, more expensive |
| `text-embedding-ada-002` | 1536 | Legacy model |
### Cohere Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `embed-english-v3.0` | 1024 | English text |
| `embed-multilingual-v3.0` | 1024 | 100+ languages |
:::warning Embedding Dimensions
Hindsight automatically detects the embedding dimension at startup and adjusts the database schema. Once memories are stored, you cannot change dimensions without losing data.
:::
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=local
export HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5
# OpenAI
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small
# Cohere
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL=embed-english-v3.0
# TEI (self-hosted)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=tei
export HINDSIGHT_API_EMBEDDINGS_TEI_URL=http://localhost:8080
# LiteLLM proxy
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL=text-embedding-3-small
```
See [Configuration](./configuration#embeddings) for all options including Azure OpenAI and custom endpoints.
---
## Cross-Encoder (Reranker)
Reranks initial search results to improve precision.
**Default:** `cross-encoder/ms-marco-MiniLM-L-6-v2` (~85MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers CrossEncoder (default) | Development, low latency |
| `cohere` | Cohere rerank API | Production, high quality |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `flashrank` | FlashRank (lightweight, fast) | Resource-constrained environments |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
| `rrf` | RRF-only (no neural reranking) | Testing, minimal resources |
### Local Models
| Model | Use Case |
|-------|----------|
| `cross-encoder/ms-marco-MiniLM-L-6-v2` | Default, fast |
| `cross-encoder/ms-marco-MiniLM-L-12-v2` | Higher accuracy |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | Multilingual |
### Cohere Models
| Model | Use Case |
|-------|----------|
| `rerank-english-v3.0` | English text |
| `rerank-multilingual-v3.0` | 100+ languages |
### LiteLLM Supported Providers
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
| Provider | Model Example |
|----------|---------------|
| Cohere | `cohere/rerank-english-v3.0` |
| Together AI | `together_ai/...` |
| Voyage AI | `voyage/rerank-2` |
| Jina AI | `jina_ai/...` |
| AWS Bedrock | `bedrock/...` |
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_RERANKER_PROVIDER=local
export HINDSIGHT_API_RERANKER_LOCAL_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
# Cohere
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
# TEI (self-hosted)
export HINDSIGHT_API_RERANKER_PROVIDER=tei
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
# FlashRank (lightweight)
export HINDSIGHT_API_RERANKER_PROVIDER=flashrank
# LiteLLM proxy
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_RERANKER_LITELLM_MODEL=cohere/rerank-english-v3.0
# RRF-only (no neural reranking)
export HINDSIGHT_API_RERANKER_PROVIDER=rrf
```
See [Configuration](./configuration#reranker) for all options including Azure-hosted endpoints and batch settings.
@@ -1,199 +0,0 @@
# Monitoring
Hindsight provides comprehensive monitoring through Prometheus metrics and pre-built Grafana dashboards.
## Local Development
For local metrics visualization, a convenience script downloads and runs Prometheus and Grafana:
```bash
./scripts/dev/start-monitoring.sh
```
This will start:
- **Grafana**: http://localhost:8890 (anonymous access enabled)
- **Prometheus**: http://localhost:8889
- **API Metrics**: http://localhost:8888/metrics
:::note Production Deployment
The local monitoring script is for development only. In production, you need to install and configure Prometheus and Grafana separately, then point Prometheus to scrape your Hindsight API's `/metrics` endpoint.
:::
## Grafana Dashboards
Pre-built dashboards are available in [`monitoring/grafana/dashboards/`](https://github.com/anthropics/hindsight/tree/main/monitoring/grafana/dashboards). Import these JSON files into your Grafana instance:
| Dashboard | Description |
|-----------|-------------|
| **Hindsight Operations** | Operation rates, latency percentiles, per-bank metrics |
| **Hindsight LLM Metrics** | LLM calls, token usage, latency by scope/provider |
| **Hindsight API Service** | HTTP requests, error rates, DB pool, process metrics |
The dashboards are automatically provisioned when using the monitoring stack script.
## Metrics Endpoint
Hindsight exposes Prometheus metrics at `/metrics`:
```bash
curl http://localhost:8888/metrics
```
## Available Metrics
### Operation Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.operation.duration` | Histogram | operation, bank_id, source, budget, max_tokens, success | Duration of operations in seconds |
| `hindsight.operation.total` | Counter | operation, bank_id, source, budget, max_tokens, success | Total number of operations executed |
**Labels:**
- `operation`: Operation type (`retain`, `recall`, `reflect`)
- `bank_id`: Memory bank identifier
- `source`: Where the operation was triggered from (`api`, `reflect`, `internal`)
- `budget`: Budget level if specified (`low`, `mid`, `high`)
- `max_tokens`: Max tokens if specified
- `success`: Whether the operation succeeded (`true`, `false`)
The `source` label allows distinguishing between:
- `api`: Direct API calls from clients
- `reflect`: Internal recall calls made during reflect operations
- `internal`: Other internal operations
### LLM Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.llm.duration` | Histogram | provider, model, scope, success | Duration of LLM API calls in seconds |
| `hindsight.llm.calls.total` | Counter | provider, model, scope, success | Total number of LLM API calls |
| `hindsight.llm.tokens.input` | Counter | provider, model, scope, success, token_bucket | Input tokens for LLM calls |
| `hindsight.llm.tokens.output` | Counter | provider, model, scope, success, token_bucket | Output tokens from LLM calls |
**Labels:**
- `provider`: LLM provider (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio`)
- `model`: Model name (e.g., `gpt-4`, `claude-3-sonnet`)
- `scope`: What the LLM call is for (`memory`, `reflect`, `entity_observation`, `answer`)
- `success`: Whether the call succeeded (`true`, `false`)
- `token_bucket`: Token count bucket for cardinality control (`0-100`, `100-500`, `500-1k`, `1k-5k`, `5k-10k`, `10k-50k`, `50k+`)
### HTTP Request Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.http.duration` | Histogram | method, endpoint, status_code, status_class | Duration of HTTP requests in seconds |
| `hindsight.http.requests.total` | Counter | method, endpoint, status_code, status_class | Total number of HTTP requests |
| `hindsight.http.requests.in_progress` | UpDownCounter | method, endpoint | Number of HTTP requests currently being processed |
**Labels:**
- `method`: HTTP method (`GET`, `POST`, `PUT`, `DELETE`)
- `endpoint`: Request path (normalized to reduce cardinality - UUIDs replaced with `{id}`)
- `status_code`: HTTP status code (`200`, `400`, `500`, etc.)
- `status_class`: Status code class (`2xx`, `4xx`, `5xx`)
### Database Pool Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.db.pool.size` | Gauge | - | Current number of connections in the pool |
| `hindsight.db.pool.idle` | Gauge | - | Number of idle connections in the pool |
| `hindsight.db.pool.min` | Gauge | - | Minimum pool size |
| `hindsight.db.pool.max` | Gauge | - | Maximum pool size |
### Process Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.process.cpu.seconds` | Gauge | type | Process CPU time in seconds |
| `hindsight.process.memory.bytes` | Gauge | type | Process memory usage in bytes |
| `hindsight.process.open_fds` | Gauge | - | Number of open file descriptors |
| `hindsight.process.threads` | Gauge | - | Number of active threads |
**Labels:**
- `type` (CPU): `user` or `system`
- `type` (Memory): `rss_max` (maximum resident set size)
### Histogram Buckets
Custom bucket boundaries are configured for better percentile accuracy:
**Operation Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 60.0, 120.0
```
**LLM Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0, 15.0, 30.0, 60.0, 120.0
```
**HTTP Duration Buckets (seconds):**
```
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0
```
## Prometheus Configuration
```yaml
scrape_configs:
- job_name: 'hindsight'
static_configs:
- targets: ['localhost:8888']
```
## Example Queries
### Average operation latency by type
```promql
rate(hindsight_operation_duration_sum[5m]) / rate(hindsight_operation_duration_count[5m])
```
### LLM calls per minute by provider
```promql
rate(hindsight_llm_calls_total[1m]) * 60
```
### P95 LLM latency
```promql
histogram_quantile(0.95, rate(hindsight_llm_duration_bucket[5m]))
```
### Total tokens consumed by model
```promql
sum by (model) (hindsight_llm_tokens_input_total + hindsight_llm_tokens_output_total)
```
### Internal vs API recall operations
```promql
sum by (source) (rate(hindsight_operation_total{operation="recall"}[5m]))
```
### HTTP requests per second by endpoint
```promql
sum by (endpoint) (rate(hindsight_http_requests_total[1m]))
```
### HTTP error rate (5xx)
```promql
sum(rate(hindsight_http_requests_total{status_class="5xx"}[5m])) / sum(rate(hindsight_http_requests_total[5m]))
```
### P95 HTTP latency
```promql
histogram_quantile(0.95, sum by (le) (rate(hindsight_http_duration_seconds_bucket[5m])))
```
### Database pool utilization
```promql
hindsight_db_pool_size / hindsight_db_pool_max
```
### Active database connections
```promql
hindsight_db_pool_size - hindsight_db_pool_idle
```
### CPU usage rate
```promql
rate(hindsight_process_cpu_seconds{type="user"}[1m])
```
@@ -1,217 +0,0 @@
---
sidebar_position: 5
---
# Multilingual Support
Hindsight automatically detects the language of your input and responds in the same language. This means facts, entities, and reflections are preserved in their original language without translation to English.
## How It Works
```mermaid
graph LR
A[Chinese Input] --> B[Language Detection]
B --> C[Extract Facts in Chinese]
C --> D[Chinese Entities]
D --> E[Chinese Response]
```
When you retain content or reflect on a query, Hindsight:
1. **Detects the input language** automatically from the content
2. **Extracts facts in the original language** - preserving nuance and meaning
3. **Stores entities in their native script** - 张伟 stays 张伟, not "Zhang Wei"
4. **Responds in the same language** - queries in Chinese get Chinese answers
---
## Retain with Non-English Content
When you retain content in any language, Hindsight extracts and stores facts in that same language.
### Example: Chinese Content
```python
from hindsight import Hindsight
hindsight = Hindsight()
# Retain Chinese content
hindsight.retain(
bank_id="user-123",
content="""
张伟是一位资深软件工程师,在腾讯工作了五年。
他专门研究分布式系统,并领导了公司微服务架构的开发。
""",
context="团队概述"
)
# Query in Chinese - get Chinese results
results = hindsight.recall(
bank_id="user-123",
query="告诉我关于张伟的信息"
)
# Facts are returned in Chinese:
# - 张伟是一位资深软件工程师,在腾讯工作了五年
# - 张伟专门研究分布式系统,并领导了公司微服务架构的开发
```
### Example: Japanese Content
```python
hindsight.retain(
bank_id="user-123",
content="""
田中さんはソフトウェアエンジニアで、東京のスタートアップで働いています。
彼女はPythonとTypeScriptが得意で、毎日コードレビューをしています。
""",
context="チームプロフィール"
)
# Query in Japanese
results = hindsight.recall(
bank_id="user-123",
query="田中さんについて教えてください"
)
```
---
## Reflect with Non-English Queries
The `reflect` operation also respects the input language, generating thoughtful responses in the same language as the query.
### Example: Chinese Reflection
```python
# Store facts about team members (in Chinese)
hindsight.retain(
bank_id="team-eval",
content="张伟是一位优秀的软件工程师,完成了五个重大项目。他总是按时交付,代码整洁有良好的文档。",
context="绩效评估"
)
hindsight.retain(
bank_id="team-eval",
content="李明最近加入团队。他错过了第一个截止日期,代码有很多bug。",
context="绩效评估"
)
# Reflect in Chinese
result = hindsight.reflect(
bank_id="team-eval",
query="谁是更可靠的工程师?"
)
# Response is in Chinese:
# "我认为张伟更可靠。张伟完成了五个重大项目,按时交付,代码质量高..."
```
---
## Mixed Language Content
Hindsight handles mixed-language content gracefully, preserving both languages where appropriate.
### Example: Chinese Text with English Company Names
```python
hindsight.retain(
bank_id="user-123",
content="""
王芳在Google北京办公室工作,她是一名高级产品经理。
之前她在Microsoft和Amazon工作过。
她负责管理YouTube在中国市场的推广策略。
""",
context="员工资料"
)
# Facts preserve both languages:
# - 王芳在Google北京办公室工作,担任高级产品经理
# - 王芳曾在Microsoft和Amazon工作过
# - 王芳负责管理YouTube在中国市场的推广策略
```
---
## Supported Languages
**Hindsight's multilingual support depends entirely on your LLM's language capabilities.** Hindsight instructs the LLM to detect the input language and respond in that same language. If your LLM supports a language, Hindsight will work with it.
Most modern LLMs (GPT-4, Claude, Gemini, Llama 3, etc.) support dozens of languages including:
- **East Asian**: Chinese (Simplified/Traditional), Japanese, Korean
- **European**: Spanish, French, German, Italian, Portuguese, Dutch, Polish, Russian
- **Middle Eastern**: Arabic, Hebrew, Turkish
- **South Asian**: Hindi, Bengali, Tamil
- **Southeast Asian**: Thai, Vietnamese, Indonesian
**To verify support for your target language**, test your LLM directly with content in that language. If the LLM can understand and generate text in the language, Hindsight will preserve it correctly.
---
## Configuring for Multilingual Use
For optimal multilingual performance, you should configure all three components of the pipeline:
### 1. LLM (Required)
Your LLM must support the target languages. Most modern LLMs do, but verify with your specific model.
### 2. Embedding Model (Recommended)
The default embedding model (`BAAI/bge-small-en-v1.5`) is **English-only**. For multilingual content, use a multilingual embedding model:
```bash
# In your .env file
HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-m3
```
**Recommended multilingual embedding models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-m3` | 100+ | Best overall multilingual performance |
| `intfloat/multilingual-e5-large` | 100+ | Good alternative |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 50+ | Lighter weight |
### 3. Reranker Model (Recommended)
The default reranker (`cross-encoder/ms-marco-MiniLM-L-6-v2`) is **English-only**. For multilingual content, use a multilingual reranker:
```bash
# In your .env file
HINDSIGHT_API_RERANKER_LOCAL_MODEL=BAAI/bge-reranker-v2-m3
```
**Recommended multilingual reranker models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-reranker-v2-m3` | 100+ | Best multilingual reranking |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | 14 | Lighter alternative |
---
## Best Practices
### 1. Use Multilingual Models for Non-English Content
If you primarily work with non-English content, configure multilingual embedding and reranker models. English-only models will still store your content correctly, but semantic search quality will be degraded.
### 2. Keep Content in One Language Per Retain Call
While mixed content works, keeping each `retain` call in a single language produces more consistent results.
### 3. Query in the Same Language as Your Content
For best results, query using the same language as your stored content. Cross-language queries (e.g., English query for Chinese content) may work but results can vary depending on your embedding model.
---
## Technical Details
Multilingual support is implemented through LLM prompt instructions rather than external language detection libraries. This approach:
- **Requires no additional dependencies**
- **Works with any LLM** that supports multiple languages
- **Handles edge cases** like mixed-language content naturally
- **Preserves semantic meaning** better than rule-based translation
The LLM is instructed to:
1. Detect the input language
2. Extract all facts, entities, and descriptions in that same language
3. Never translate to English unless the input is in English
@@ -1,133 +0,0 @@
# Performance
Hindsight is designed for high-performance semantic memory operations at scale. This page covers performance characteristics, optimization strategies, and best practices.
## Overview
Hindsight's performance is optimized across three key operations:
- **Retain (Ingestion)**: Batch processing with async operations for large-scale memory storage
- **Recall (Search)**: Sub-second semantic search with configurable thinking budgets
- **Reflect (Reasoning)**: Disposition-aware answer generation with controllable compute
## Design Philosophy: Optimized for Fast Reads
Hindsight is **architected from the ground up to prioritize read performance over write performance**. This design decision reflects the typical usage pattern of memory systems: memories are written once but read many times.
The system makes deliberate trade-offs to ensure **sub-second recall operations**:
- **Pre-computed embeddings**: All memory embeddings are generated and indexed during retention
- **Optimized vector search**: HNSW indexes enable fast approximate nearest neighbor search
- **Fact extraction at write time**: Complex LLM-based fact extraction happens during retention, not retrieval
- **Structured memory graphs**: Relationships and temporal information are resolved upfront
This means **Recall (search) operations are blazingly fast** because all the heavy lifting has already been done.
### Performance Comparison
| Operation | Typical Latency | Primary Bottleneck | Optimization Strategy |
|-----------|----------------|-------------------|----------------------------------|
| **Recall** | 100-600ms | Re-ranker (on CPU) | Use GPU for re-ranking, or reduce budget |
| **Reflect** | 800-3000ms | LLM generation | Use faster LLM |
| **Retain** | 500ms-2000ms per batch | **LLM fact extraction** | Use high-throughput LLM provider |
Hindsight is designed to ensure your **application's read path (recall/reflect) is always fast**, even if it means spending more time upfront during writes. This is the right trade-off for memory systems where:
- Memories are retained in background processes or during low-traffic periods
- Memories are queried frequently in user-facing, latency-sensitive contexts
- The ratio of reads to writes is high (typically 10:1 or higher)
---
## Retain Performance
**Retain (write) operations are inherently slower** because they involve LLM-based fact extraction, entity recognition, temporal reasoning, relationship mapping, and embedding generation. **The LLM is the primary bottleneck for write latency.**
### Hindsight Doesn't Need a Smart Model
The fact extraction process is structured and well-defined, so smaller, faster models work extremely well. Our recommended model is `gpt-oss-20b` (available via Groq and other providers).
To maximize retention throughput:
1. **Use high-throughput LLM providers**: Choose providers with high requests-per-minute (RPM) limits and low latency
- **Fast**: [Groq](https://groq.com) with `gpt-oss-20b` or other openai-oss models, self-hosted models on GPU clusters (vLLM, TGI)
- **Slow**: Standard cloud LLM providers with rate limits
2. **Batch your operations**: Group related content into batch requests. The only limit is the HTTP payload size — Hindsight automatically splits large batches into smaller, optimized chunks under the hood, so you don't have to worry about it.
3. **Use async mode for large datasets**: Queue operations in the background
4. **Parallel processing**: For very large datasets, use multiple concurrent retention requests with different `document_id` values
### Throughput
Factors affecting throughput:
- Document size and complexity
- LLM provider rate limits (for fact extraction)
- Database write performance
- Available CPU/memory resources
---
## Recall Performance
### Budget
The `budget` parameter controls the search depth and quality. Choose based on query complexity — comprehensive questions that need thorough analysis benefit from higher budgets:
| Budget | Use Case |
|--------|----------|
| `low` | Quick lookups, real-time chat |
| `mid` | Standard queries, balanced performance |
| `high` | Comprehensive questions, thorough analysis |
### Optimization
1. **Appropriate budgets**: Use lower budgets for simple queries, higher for comprehensive reasoning
2. **Limit result tokens**: Set `max_tokens` to control response size (default: 4096)
3. **Include entities/chunks**: Use `include_entities` and `include_chunks` to retrieve additional context when needed — each has its own token budget
### Database Performance
Hindsight uses PostgreSQL with pgvector for efficient vector search:
- **Index type**: HNSW for approximate nearest neighbor search
- **Typical query time**: 10-50ms for vector search on 100K+ facts
- **Scalability**: Tested with millions of facts per bank
## Reflect Performance
### Performance Characteristics
| Component | Latency | Description |
|-----------|----------------|-------------|
| Memory search | 100-600ms | Based on budget (low/mid/high) |
| LLM generation | 500-2000ms | Depends on provider and response length |
| **Total** | **600-2600ms** | Typical end-to-end latency |
### Optimization Strategies
1. **Budget selection**: Use lower budgets when context is sufficient
2. **Context provision**: Provide relevant `context` to reduce recall requirements and steer towards more focused answers
## Best Practices
### Operations
- **Use appropriate budgets**: Don't over-provision for simple queries; use higher budgets for comprehensive reasoning
- **Batch retain operations**: Group related content together for better efficiency
- **Cache frequent queries**: Cache at the application level for repeated queries
- **Profile with trace**: Use the `trace` parameter to identify slow operations
### Scaling
- **Horizontal scaling**: Deploy multiple API instances behind a load balancer with shared PostgreSQL
- **Concurrency**: 100+ simultaneous requests supported; memory search scales with CPU cores
- **LLM rate limits**: Distribute load across multiple API keys/providers (typically 60-500 RPM per key)
### Cost Optimization
- **Use efficient models**: `gpt-oss-20b` via Groq for retain — Hindsight doesn't need frontier models
- **Control token budgets**: Limit `max_tokens` for recall, use lower budgets when possible
- **Optimize chunks**: Larger chunks (1000-2000 tokens) are more efficient than many small ones
### Monitoring
- **Prometheus metrics**: Available at `/metrics` — track latency percentiles, throughput, and error rates
- **Key metrics**: `hindsight_recall_duration_seconds`, `hindsight_reflect_duration_seconds`, `hindsight_retain_items_total`
@@ -1,110 +0,0 @@
---
sidebar_position: 2
---
# RAG vs Memory
Traditional RAG (Retrieval-Augmented Generation) retrieves documents similar to a query. Hindsight provides structured memory with temporal reasoning, entity understanding, and belief formation.
## Capability Comparison
| Capability | RAG | Hindsight |
|------------|-----|-----------|
| **Search strategy** | Semantic similarity only | Semantic + keyword + graph + temporal |
| **Multi-hop reasoning** | Limited to retrieved chunks | Graph traversal across entity relationships |
| **Temporal queries** | Keyword matching ("spring") | Date parsing and range filtering |
| **Entity understanding** | None | Entity resolution, observations, co-occurrence |
| **Belief formation** | Stateless | Opinions with confidence scores that evolve |
| **Disposition** | None | 3 traits (skepticism, literalism, empathy) influence interpretation |
## Architecture Comparison
### RAG
| Step | Operation |
|------|-----------|
| 1 | Embed query |
| 2 | Vector similarity search |
| 3 | Return top-k chunks |
| 4 | Generate response |
Single retrieval strategy. No state between queries.
### Hindsight
| Step | Operation |
|------|-----------|
| 1 | Parse query (extract temporal expressions, entities) |
| 2 | Execute 4 parallel retrievals: semantic, BM25, graph, temporal |
| 3 | Fuse results with RRF |
| 4 | Rerank with cross-encoder |
| 5 | Apply disposition traits |
| 6 | Generate response |
Multiple retrieval strategies. Persistent state across sessions.
## Example Scenarios
### Multi-Hop Reasoning
**Stored facts:**
- "Alice is the tech lead on Project Atlas"
- "Project Atlas uses Kubernetes"
- "Kubernetes cluster had an outage Tuesday"
**Query:** "Was Alice affected by recent issues?"
| System | Result |
|--------|--------|
| RAG | Retrieves facts about Alice only (no semantic similarity to "issues") |
| Hindsight | Traverses Alice → Project Atlas → Kubernetes → outage via entity links |
### Temporal Queries
**Stored facts with timestamps:**
- March: "Alice started microservices migration"
- April: "Alice completed auth service"
- October: "Alice focusing on performance"
**Query:** "What did Alice do last spring?"
| System | Result |
|--------|--------|
| RAG | Returns all Alice facts regardless of date |
| Hindsight | Parses "last spring" → March-May, filters to that range |
### Entity Understanding
**Stored facts about a user across sessions:**
- "Pro subscription"
- "Mobile app crashes in settings"
- "Switched to annual billing"
- "Desktop app working fine"
**Query:** "What do you know about my account?"
| System | Result |
|--------|--------|
| RAG | Lists disconnected facts |
| Hindsight | Returns synthesized entity observations: subscription status, billing, known issues |
### Belief Evolution
**Week 1:** User struggles with async Python, succeeds with threads
**Week 3:** User asks about asyncio, implements async database calls
| System | Behavior |
|--------|----------|
| RAG | No memory of progression |
| Hindsight | Forms opinion "user prefers sync" (0.7) → updates to "user growing comfortable with async" (0.6) |
## When to Use Each
| Use Case | Recommended |
|----------|-------------|
| Document Q&A over static corpus | RAG |
| Search with no temporal requirements | RAG |
| AI assistants with persistent memory | Hindsight |
| Applications requiring entity tracking | Hindsight |
| Systems needing consistent disposition | Hindsight |
| Temporal queries ("last month", "in 2023") | Hindsight |
@@ -1,186 +0,0 @@
---
sidebar_position: 4
---
# Reflect: How Hindsight Reasons with Disposition
When you call `reflect()`, Hindsight doesn't just retrieve facts — it **reasons** about them through the lens of the bank's unique disposition, forming new opinions and generating contextual responses.
```mermaid
graph LR
A[Query] --> B[Recall Memories]
B --> C[Load Disposition]
C --> D[Reason]
D --> E[Form Opinions]
E --> F[Response]
```
---
## Why Reflect?
Most AI systems can retrieve facts, but they can't **reason** about them in a consistent way. Every response is generated fresh without a stable perspective or evolving beliefs.
### The Problem
Without reflect:
- **No consistent character**: "Should we adopt remote work?" gets a different answer each time based on the LLM's randomness
- **No opinion formation**: The system never develops beliefs based on accumulated evidence
- **No reasoning context**: Responses don't reflect what the bank has learned or its perspective
- **Generic responses**: Every AI sounds the same — no disposition, no point of view
### The Value
With reflect:
- **Consistent character**: A bank configured as "detail-oriented, cautious" will consistently emphasize risks and thorough planning
- **Evolving opinions**: As the bank learns more about a topic, its opinions strengthen, weaken, or change — just like a real expert
- **Contextual reasoning**: Responses reflect the bank's accumulated knowledge and perspective: "Based on what I know about your team's remote work success..."
- **Differentiated behavior**: Customer support bots sound diplomatic, code reviewers sound direct, creative assistants sound open-minded
### When to Use Reflect
| Use `recall()` when... | Use `reflect()` when... |
|------------------------|-------------------------|
| You need raw facts | You need reasoned interpretation |
| You're building your own reasoning | You want disposition-consistent responses |
| You need maximum control | You want the bank to "think" for itself |
| Simple fact lookup | Forming recommendations or opinions |
**Example:**
- `recall("Alice")` → Returns all Alice facts
- `reflect("Should we hire Alice?")` → Reasons about Alice's fit based on accumulated knowledge, weighs evidence, forms opinion
---
## Disposition Traits
When you create a memory bank, you can configure its disposition using three traits. These traits influence how the bank interprets information and forms opinions during `reflect()`:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | 1-5 | Flexible interpretation, reads between the lines | Literal interpretation, takes things at face value |
| **Empathy** | 1-5 | Detached, focuses on facts | Empathetic, considers emotional context |
### Background: Natural Language Identity
Beyond numeric traits, you can provide a natural language **background** that describes the bank's identity:
```python
client.create_bank(
bank_id="my-bank",
background="I am a senior software architect with 15 years of distributed "
"systems experience. I prefer simplicity over cutting-edge technology.",
disposition={
"skepticism": 4, # Questions new technologies
"literalism": 4, # Focuses on concrete specs
"empathy": 2 # Prioritizes technical facts
}
)
```
The background provides context that shapes how disposition traits are applied:
- "I prefer simplicity" + high skepticism → questions complex solutions
- "15 years experience" → responses reference this expertise
- First-person perspective → creates consistent voice
---
## Opinion Formation
When `reflect()` encounters a question that warrants forming an opinion, disposition shapes the response.
### Same Facts, Different Opinions
Two banks with different dispositions, given identical facts about remote work:
**Bank A** (low skepticism, high empathy):
> "Remote work enables flexibility and work-life balance. The team seems happier and more productive when they can choose their environment."
**Bank B** (high skepticism, low empathy):
> "Remote work claims need verification. What are the actual productivity metrics? The anecdotal benefits may not translate to measurable outcomes."
**Same facts → Different conclusions** because disposition shapes interpretation.
---
## Opinion Evolution
Opinions aren't static — they evolve as new evidence arrives. Here's a real-world example with a database library:
| Event | What the bank learns | Opinion formed |
|-------|---------------------|----------------|
| **Day 1** | "Redis is open source under BSD license" | "Redis is excellent for caching — fast, reliable, and OSS-friendly" (confidence: 0.85) |
| **Day 2** | "Redis has great community support and documentation" | Opinion reinforced (confidence: 0.90) |
| **Day 30** | "Redis changed license to SSPL, restricting cloud usage" | "Redis is still technically strong, but license concerns for cloud deployments" (confidence: 0.65) |
| **Day 45** | "Valkey forked Redis under BSD license with Linux Foundation backing" | "Consider Valkey for new projects requiring true OSS; Redis for existing deployments" (confidence: 0.80) |
**Before the license change:**
> "Should we use Redis for our caching layer?"
> → "Yes, Redis is the industry standard — fast, battle-tested, and fully open source."
**After the license change:**
> "Should we use Redis for our caching layer?"
> → "It depends. For cloud deployments, consider Valkey (the BSD-licensed fork). For on-premise, Redis remains excellent technically."
This **continuous learning** ensures recommendations stay current with real-world changes.
---
## Disposition Presets by Use Case
Different use cases benefit from different disposition configurations:
| Use Case | Recommended Traits | Why |
|----------|-------------------|-----|
| **Customer Support** | skepticism: 2, literalism: 2, empathy: 5 | Trusting, flexible, understanding |
| **Code Review** | skepticism: 4, literalism: 5, empathy: 2 | Questions assumptions, precise, direct |
| **Legal Analysis** | skepticism: 5, literalism: 5, empathy: 2 | Highly skeptical, exact interpretation |
| **Therapist/Coach** | skepticism: 2, literalism: 2, empathy: 5 | Supportive, reads between lines |
| **Research Assistant** | skepticism: 4, literalism: 3, empathy: 3 | Questions claims, balanced interpretation |
---
## What You Get from Reflect
When you call `reflect()`:
**Returns:**
- **Response text** — Disposition-influenced answer
- **Based on** — Which memories were used (with relevance scores)
**Example:**
```json
{
"text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
"based_on": {
"world": [
{"text": "Alice works at Google...", "weight": 0.95},
{"text": "Alice specializes in ML...", "weight": 0.88}
]
}
}
```
**Note:** New opinions are formed asynchronously in the background. They'll influence future `reflect()` calls but aren't returned directly.
---
## Why Disposition Matters
Without disposition, all AI assistants sound the same. With disposition:
- **Customer support bots** can be diplomatic and empathetic
- **Code review assistants** can be direct and thorough
- **Creative assistants** can be open to unconventional ideas
- **Risk analysts** can be appropriately cautious
Disposition creates **consistent character** across conversations while allowing opinions to **evolve with evidence**.
---
## Next Steps
- [**Retain**](./retain) — How rich facts are stored
- [**Recall**](./retrieval) — How multi-strategy search works
- [**Reflect API**](./api/reflect) — Code examples, parameters, and tag filtering
@@ -1,201 +0,0 @@
---
sidebar_position: 2
---
# Retain: How Hindsight Stores Memories
When you call `retain()`, Hindsight transforms conversations and documents into structured, searchable memories that preserve meaning and context.
## What Retain Does
```mermaid
graph LR
A[Your Content] --> B[Extract Facts]
B --> C[Identify Entities]
C --> D[Build Connections]
D --> E[Memory Bank]
```
---
## Rich Fact Extraction
Hindsight doesn't just store what was said — it captures **why**, **how**, and **what it means**.
### What Gets Captured
When you retain "Alice joined Google last spring and was thrilled about the research opportunities", Hindsight extracts:
**The core facts:**
- Alice joined Google
- This happened last spring
**The emotions and meaning:**
- She was thrilled
- It represented an important opportunity
**The reasoning:**
- She chose it for the research opportunities
This rich extraction means you can later ask "Why did Alice join Google?" and get a meaningful answer, not just "she joined Google."
### Preserving Context
Traditional systems fragment information:
- "Bob suggested Summer Vibes"
- "Alice wanted something unique"
- "They chose Beach Beats"
Hindsight preserves the full narrative:
- "Alice and Bob discussed naming their summer party playlist. Bob suggested 'Summer Vibes' because it's catchy, but Alice wanted something unique. They ultimately decided on 'Beach Beats' for its playful tone."
This means search results include the full context, not disconnected fragments.
---
## Two Types of Facts
Hindsight distinguishes between **world** facts (about others) and **experience** (conversations and events):
| Type | Description | Example |
|-----------------|-----------------------------------|---------|
| **world** | Facts about people, places, things | "Alice works at Google" |
| **experience** | Conversations and events | "I recommended Python to Alice" |
**Note:** Opinions aren't created during `retain()` — only during `reflect()` when the bank forms beliefs.
This separation is important for `reflect()` — the bank can reason about what it knows versus what happened in conversations.
---
## Entity Recognition
Hindsight automatically identifies and tracks **entities** — the people, organizations, and concepts that matter.
### What Gets Recognized
- **People:** "Alice", "Dr. Smith", "Bob Chen"
- **Organizations:** "Google", "MIT", "OpenAI"
- **Places:** "Paris", "Central Park", "California"
- **Products & Concepts:** "Python", "TensorFlow", "machine learning"
### Entity Resolution
The same entity mentioned different ways gets unified:
- "Alice" + "Alice Chen" + "Alice C." → one person
- "Bob" + "Robert Chen" → one person (nickname resolution)
**Why it matters:** You can ask "What do I know about Alice?" and get everything, even if she was mentioned as "Alice Chen" in some conversations.
### Context-Aware Disambiguation
If "Alice" appears with "Google" and "Stanford" multiple times, a new "Alice" mentioning those is likely the same person. Hindsight uses co-occurrence patterns to disambiguate common names.
---
## Building Connections
Memories aren't isolated — Hindsight creates a **knowledge graph** with four types of connections:
### Entity Connections
All facts mentioning the same entity are linked together.
**Enables:** "Tell me everything about Alice" → retrieves all Alice-related facts
### Time-Based Connections
Facts close in time are connected, with stronger links for closer dates.
**Enables:** "What else happened around then?" → finds contextually related events
### Meaning-Based Connections
Semantically similar facts are linked, even if they use different words.
**Enables:** "Tell me about similar topics" → finds thematically related information
### Causal Connections
Cause-effect relationships are explicitly tracked.
**Enables:** "Why did this happen?" → trace reasoning chains
**Example:** "Alice felt burned out" ← caused by ← "She worked 80-hour weeks"
---
## Understanding Time
Hindsight tracks **two temporal dimensions**:
### When It Happened
For events (meetings, trips, milestones), Hindsight records when they occurred.
- "Alice got married in June 2024" → occurred in June 2024
For general facts (preferences, characteristics), there's no specific occurrence time.
- "Alice prefers Python" → ongoing preference
### When You Learned It
Hindsight also tracks when you told it each fact.
**Why both?**
Imagine in January 2025, someone tells you "Alice got married in June 2024":
- **Historical queries** work: "What did Alice do in 2024?" → finds the marriage
- **Recency ranking** works: Recent mentions get priority in search
- **Temporal reasoning** works: "What happened before her marriage?" → finds earlier events
Without this distinction, old information would either be unsearchable by date or treated as irrelevant.
---
## Entity Observations
As facts accumulate about an entity, Hindsight synthesizes **observations** — high-level summaries that capture what's known:
**From multiple facts:**
- "Alice works at Google"
- "Alice is a software engineer"
- "Alice specializes in ML"
**Hindsight creates:**
- "Alice is a software engineer at Google specializing in ML"
**Why it helps:** You can quickly understand an entity without reading through dozens of individual facts.
---
## Tagging Memories
Tags enable visibility scoping—useful when one memory bank serves multiple users but each should only see relevant memories.
- **Item tags**: Tag individual memories with specific scopes
- **Document tags**: Apply tags to all items in a batch
- **Tag filtering**: Filter during recall/reflect by tags
See [Retain API](./api/retain) for code examples and [Recall API](./api/recall) for filtering options.
---
## What You Get
After `retain()` completes:
- **Structured facts** that preserve meaning, emotions, and reasoning
- **Unified entities** that resolve different name variations
- **Knowledge graph** with entity, temporal, semantic, and causal links
- **Temporal grounding** for both historical and recency-based queries
- **Background processing** that generates entity summaries
- **Optional tags** for filtering during recall
All stored in your isolated **memory bank**, ready for `recall()` and `reflect()`.
---
## Next Steps
- [**Recall**](./retrieval) — How multi-strategy search retrieves relevant memories
- [**Reflect**](./reflect) — How disposition influences reasoning and opinion formation
- [**Retain API**](./api/retain) — Code examples and parameters
@@ -1,246 +0,0 @@
---
sidebar_position: 3
---
# Recall: How Hindsight Retrieves Memories
When you call `recall()`, Hindsight uses multiple search strategies in parallel to find the most relevant memories, regardless of how you phrase your query.
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
---
## The Challenge of Memory Recall
Different queries need different search approaches:
- **"Alice works at Google"** → needs exact name matching
- **"Where does Alice work?"** → needs semantic understanding
- **"What did Alice do last spring?"** → needs temporal reasoning
- **"Why did Alice leave?"** → needs causal relationship tracing
No single search method handles all these well. Hindsight solves this with **TEMPR** — four complementary strategies that run in parallel.
---
## Four Search Strategies
### Semantic Search
**What it does:** Understands the *meaning* behind words, not just the words themselves.
**Best for:**
- Conceptual matches: "Alice's job" → "Alice works as a software engineer"
- Paraphrasing: "Bob's expertise" → "Bob specializes in machine learning"
- Synonyms: "meeting" matches "conference", "discussion", "gathering"
**Why it matters:** You can ask questions naturally without matching exact keywords.
---
### Keyword Search
**What it does:** Finds exact terms and names, even when they're spelled uniquely.
**Best for:**
- Proper nouns: "Google", "Alice Chen", "MIT"
- Technical terms: "PostgreSQL", "HNSW", "TensorFlow"
- Unique identifiers: URLs, product names, specific phrases
**Why it matters:** Ensures you never miss results that mention specific names or terms, even if they're semantically distant from your query.
---
### Graph Traversal
**What it does:** Follows connections between entities to find indirectly related information.
**Best for:**
- Indirect relationships: "What does Alice do?" → Alice → Google → Google's products
- Entity exploration: "Bob's colleagues" → Bob → co-workers → shared projects
- Multi-hop reasoning: "Alice's team's achievements"
**Why it matters:** Retrieves facts that aren't semantically or lexically similar but are **structurally connected** through the knowledge graph.
**Example:** Even if Alice and her manager are never mentioned together, graph traversal can find the manager through shared projects or team relationships.
---
### Temporal Search
**What it does:** Understands time expressions and filters by when events occurred.
**Best for:**
- Historical queries: "What did Alice do in 2023?"
- Time ranges: "What happened last spring?"
- Relative time: "What did Bob work on last year?"
- Before/after: "What happened before Alice joined Google?"
**How it works:** Combines semantic understanding with time filtering to find events within specific periods.
**Why it matters:** Enables precise historical queries without losing old information.
---
## Result Fusion
After the four strategies run, results are **fused together**:
- Memories appearing in **multiple strategies** rank higher (consensus)
- **Rank matters more than score** (robust across different scoring systems)
- Final results are **re-ranked** using a neural model that considers query-memory interaction
**Why fusion matters:** A fact that's both semantically similar AND mentions the right entity will rank higher than one that's only semantically similar.
---
## Why Multiple Strategies?
Consider the query: **"What did Alice think about Python last spring?"**
- **Semantic** finds facts about Alice's opinions on programming
- **Keyword** ensures "Python" is actually mentioned
- **Graph** connects Alice → opinions → programming languages
- **Temporal** filters to "last spring" timeframe
The **fusion** of all four gives you exactly what you're looking for, even though no single strategy would suffice.
---
## Token Budget Management
Hindsight is built for AI agents, not humans. Traditional search systems return "top-k" results, but agents don't think in terms of result counts—they think in tokens. An agent's context window is measured in tokens, and that's exactly how Hindsight measures results.
**How it works:**
- Top-ranked memories selected first
- Stops when token budget is exhausted
- You specify context budget, Hindsight fills it with the most relevant memories
**Parameters you control:**
- `max_tokens`: How much memory content to return (default: 4096 tokens)
- `budget`: Search depth level (low, mid, high)
- `types`: Filter by world, experience, opinion, or all
- `tags`: Filter memories by visibility tags
- `tags_match`: How to match tags (see [Recall API](./api/recall) for all options)
### Expanding Context: Chunks and Entity Observations
Memories are distilled facts—concise but sometimes missing nuance. When your agent needs deeper context, you can optionally retrieve the source material and related knowledge:
| Option | Parameters | When to Use |
|--------|------------|-------------|
| **Chunks** | `include_chunks`, `max_chunk_tokens` | Need exact quotes, original phrasing, or surrounding context |
| **Entity Observations** | `include_entities`, `max_entity_tokens` | Need broader knowledge about people/things mentioned in results |
**Chunks** return the raw text that generated each memory—useful when the distilled fact loses important nuance:
```
Memory: "Alice prefers Python over JavaScript"
Chunk: "Alice mentioned she prefers Python over JavaScript, mainly because
of its data science ecosystem, though she admits JS is better for
frontend work and she's been learning TypeScript lately."
```
**Entity Observations** pull in related facts about entities mentioned in your results. If a memory mentions "Alice", you automatically get her role, skills, and other relevant context—without needing a separate query:
```
Query: "What programming languages does Alice like?"
Memory: "Alice prefers Python over JavaScript"
Entity Observations (Alice):
- "Alice is a senior data scientist at Google"
- "Alice specializes in machine learning"
- "Alice has been learning TypeScript"
```
**When to include them:**
- **Chunks**: When generating responses that need verbatim quotes or when context matters (e.g., "What exactly did Alice say about the project?")
- **Entity Observations**: When building complete profiles or when the conversation might reference multiple aspects of an entity (e.g., "Tell me about Alice's work")
Each has its own token budget, giving you precise control over total context size.
---
## Tuning Recall: Quality vs Latency
Different use cases require different trade-offs between **recall quality** and **response speed**. Two parameters control this:
### Budget: Search Depth
Controls how thoroughly Hindsight explores the memory bank—affecting graph traversal depth, candidate pool size, and cross-encoder re-ranking:
| Budget | Best For | Trade-off |
|--------|----------|-----------|
| **low** | Quick lookups, simple queries | Fast, may miss indirect connections |
| **mid** | Most queries, balanced | Good coverage, reasonable speed |
| **high** | Complex queries requiring deep exploration | Thorough, slower |
**Example:** "What did Alice's manager's team work on?" benefits from high budget to traverse multiple hops (Alice → manager → team → projects) and evaluate more candidates.
### Max Tokens: Context Window Size
Controls how much memory content to return:
| Max Tokens | ~Pages of Text | Best For | Trade-off |
|------------|----------------|----------|-----------|
| **2048** | ~2 pages | Focused answers, fast LLM | Fewer memories, faster |
| **4096** (default) | ~4 pages | Balanced context | Good coverage, standard |
| **8192** | ~8 pages | Comprehensive context | More memories, slower LLM |
**Example:** "Summarize everything about Alice" benefits from higher max_tokens to include more facts.
### Two Independent Dimensions
Budget and max_tokens control different aspects of recall:
| Parameter | What it controls | Latency impact | Example |
|-----------|------------------|----------------|---------|
| **Budget** | How thoroughly to explore memories | Search time | High budget finds Alice → manager → team → projects |
| **Max Tokens** | How much context to return | LLM processing time | High tokens returns more memories to the agent |
**They're independent.** Common combinations:
| Budget | Max Tokens | Use Case |
|--------|------------|----------|
| high | low | Deep search, return only the best results |
| low | high | Quick search, return everything found |
| high | high | Comprehensive research queries |
| low | low | Fast chatbot responses |
### Recommended Configurations
| Use Case | Budget | Max Tokens | Why |
|----------|--------|------------|-----|
| **Chatbot replies** | low | 2048 | Fast responses, focused context |
| **Document Q&A** | mid | 4096 | Balanced coverage and speed |
| **Research queries** | high | 8192 | Comprehensive, multi-hop reasoning |
| **Real-time search** | low | 2048 | Minimize latency |
---
## Graph Retrieval Algorithms
Hindsight supports multiple graph traversal algorithms. The default (`link_expansion`) is optimized for fast retrieval with target latency under 100ms.
See [Configuration → Retrieval](./configuration#retrieval) for available algorithms and how to configure them.
---
## Next Steps
- [**Retain**](./retain) — How memories are stored with rich context
- [**Reflect**](./reflect) — How disposition influences reasoning
- [**Recall API**](./api/recall) — Code examples, parameters, and tag filtering
@@ -1,66 +0,0 @@
# Services
Hindsight consists of three services that can run together or separately depending on your deployment needs.
## API Service
The core memory engine. Handles all memory operations:
- **Retain**: Ingests content, extracts facts, builds knowledge graph
- **Recall**: Semantic search across memories
- **Reflect**: Disposition-aware answer generation
```bash
hindsight-api # Default port: 8888
```
The API service is stateless and can be horizontally scaled behind a load balancer. All state is stored in PostgreSQL.
By default, the API also processes background tasks (opinion formation, entity observations) internally. For high-throughput deployments, you can disable this and run dedicated workers instead.
## Worker Service
Dedicated task processor for background operations. Uses the **same package and Docker image** as the API service, just with a different entry point.
```bash
hindsight-worker # Default metrics port: 8889
```
Workers use PostgreSQL as a task broker, polling for pending tasks. Multiple workers can run simultaneously without conflicts.
| Deployment | Internal Worker | Dedicated Workers |
|------------|-----------------|-------------------|
| **Development** | ✅ Simple, all-in-one | ❌ Overkill |
| **Small production** | ✅ Less infrastructure | ❌ Overkill |
| **High throughput** | ❌ API bottleneck | ✅ Scale independently |
| **Long-running tasks** | ❌ Blocks API resources | ✅ Isolated processing |
To use dedicated workers, disable the internal worker in the API and start worker processes:
```bash
# Disable internal worker in API
HINDSIGHT_API_WORKER_ENABLED=false hindsight-api
# Start dedicated workers (run multiple instances)
hindsight-worker --worker-id worker-1
hindsight-worker --worker-id worker-2
```
Each worker exposes `/health` and `/metrics` endpoints for monitoring.
Before scaling down or removing workers, release their tasks with `hindsight-admin decommission-worker <worker-id>`.
See [Configuration - Distributed Workers](./configuration#distributed-workers) for all worker settings and [Installation - Helm](./installation#distributed-workers) for Kubernetes deployment.
## Control Plane
Web UI for managing and exploring your memory banks:
- Browse agents and memory banks
- Explore entities and relationships
- View ingestion history and operations
- Test recall queries interactively
The Control Plane connects to the API service and provides a visual interface for development and debugging.
For bare metal deployments, you can run the Control Plane standalone using npx. See [Installation - Bare Metal](./installation#control-plane) for details.
@@ -1,79 +0,0 @@
# Storage
Hindsight uses PostgreSQL as its sole storage backend.
## Why PostgreSQL?
PostgreSQL provides all capabilities required for a semantic memory system in a single database:
| Capability | Implementation |
|------------|----------------|
| Vector search | pgvector extension with HNSW indexes |
| Full-text search | Built-in tsvector with GIN indexes |
| Relational data | Native PostgreSQL |
| JSON documents | JSONB with indexing |
| Graph queries | Recursive CTEs |
### Reduced System Dependencies
Building exclusively for PostgreSQL simplifies deployment and operations:
- Single connection string to configure
- Single backup and restore strategy
- Single monitoring target
- ACID transactions across all data types
- Single upgrade path
### No Storage Abstraction
Hindsight does not abstract storage behind a generic interface. This is a deliberate trade-off.
We believe PostgreSQL is becoming the standard database API. Its popularity, extension ecosystem, and modularity mean that PostgreSQL-compatible interfaces are appearing everywhere—from serverless offerings to distributed databases. Building for PostgreSQL today means compatibility with a growing ecosystem tomorrow.
Supporting multiple databases would increase flexibility but conflict with our core goals: Hindsight is fully open source and designed to be as simple as possible to run and use. Adding database abstractions introduces complexity in code, testing, documentation, and operations—complexity that we pass on to users.
By committing to PostgreSQL, we keep the system simple:
- One set of deployment instructions
- One set of performance characteristics to understand
- One codebase optimized for one backend
- No configuration decisions about which database to use
## Development with pg0
For local development, Hindsight uses **[pg0](https://github.com/vectorize-io/pg0)**—an embedded PostgreSQL distribution.
### What is pg0?
pg0 is a single binary containing:
- PostgreSQL server
- pgvector extension (pre-installed)
- Automatic initialization
### Behavior
When no `DATABASE_URL` is configured, Hindsight:
1. Starts an embedded PostgreSQL instance on port 5555
2. Initializes the schema
3. Stores data in `~/.hindsight/pg0/`
### Environments
| Environment | Database | Configuration |
|-------------|----------|---------------|
| Development | pg0 (embedded) | Automatic |
| Production | PostgreSQL 15+ | `DATABASE_URL` environment variable |
## Requirements
- PostgreSQL 15 or later
- pgvector 0.5.0 or later
Any PostgreSQL instance that satisfies these requirements should work. If you encounter issues with a specific setup, [open a GitHub issue](https://github.com/hindsight-ai/hindsight/issues).
### Tested Managed Services
- AWS RDS (PostgreSQL 15+)
- Google Cloud SQL
- Azure Database for PostgreSQL
- Supabase
- Neon
@@ -1,246 +0,0 @@
---
sidebar_position: 3
---
# CLI Reference
The Hindsight CLI provides command-line access to memory operations and bank management.
## Installation
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
## Configuration
Configure the API URL:
```bash
# Interactive configuration
hindsight configure
# Or set directly
hindsight configure --api-url http://localhost:8888
# Or use environment variable (highest priority)
export HINDSIGHT_API_URL=http://localhost:8888
```
## Core Commands
### Retain (Store Memory)
Store a single memory:
```bash
hindsight memory retain <bank_id> "Alice works at Google as a software engineer"
# With context
hindsight memory retain <bank_id> "Bob loves hiking" --context "hobby discussion"
# Queue for background processing
hindsight memory retain <bank_id> "Meeting notes" --async
```
### Retain Files
Bulk import from files:
```bash
# Single file
hindsight memory retain-files <bank_id> notes.txt
# Directory (recursive by default)
hindsight memory retain-files <bank_id> ./documents/
# With context
hindsight memory retain-files <bank_id> meeting-notes.txt --context "team meeting"
# Background processing
hindsight memory retain-files <bank_id> ./data/ --async
```
### Recall (Search)
Search memories using semantic similarity:
```bash
hindsight memory recall <bank_id> "What does Alice do?"
# With options
hindsight memory recall <bank_id> "hiking recommendations" \
--budget high \
--max-tokens 8192
# Filter by fact type
hindsight memory recall <bank_id> "query" --fact-type world,opinion
# Show trace information
hindsight memory recall <bank_id> "query" --trace
```
### Reflect (Generate Response)
Generate a response using memories and bank disposition:
```bash
hindsight memory reflect <bank_id> "What do you know about Alice?"
# With additional context
hindsight memory reflect <bank_id> "Should I learn Python?" --context "career advice"
# Higher budget for complex questions
hindsight memory reflect <bank_id> "Summarize my week" --budget high
```
## Bank Management
### List Banks
```bash
hindsight bank list
```
### View Disposition
```bash
hindsight bank disposition <bank_id>
```
### View Statistics
```bash
hindsight bank stats <bank_id>
```
### Set Bank Name
```bash
hindsight bank name <bank_id> "My Assistant"
```
### Set Background
```bash
hindsight bank background <bank_id> "I am a helpful AI assistant interested in technology"
# Skip automatic disposition inference
hindsight bank background <bank_id> "Background text" --no-update-disposition
```
## Document Management
```bash
# List documents
hindsight document list <bank_id>
# Get document details
hindsight document get <bank_id> <document_id>
# Delete document and its memories
hindsight document delete <bank_id> <document_id>
```
## Entity Management
```bash
# List entities
hindsight entity list <bank_id>
# Get entity details
hindsight entity get <bank_id> <entity_id>
# Regenerate entity observations
hindsight entity regenerate <bank_id> <entity_id>
```
## Output Formats
```bash
# Pretty (default)
hindsight memory recall <bank_id> "query"
# JSON
hindsight memory recall <bank_id> "query" -o json
# YAML
hindsight memory recall <bank_id> "query" -o yaml
```
## Global Options
| Flag | Description |
|------|-------------|
| `-v, --verbose` | Show detailed output including request/response |
| `-o, --output <format>` | Output format: pretty, json, yaml |
| `--help` | Show help |
| `--version` | Show version |
## Control Plane UI
Launch the web-based Control Plane UI directly from the CLI:
```bash
hindsight ui
```
This runs the Control Plane locally on port 9999 using the API URL from your configuration. The UI provides:
- **Memory bank management** — Browse and manage all your banks
- **Entity explorer** — Visualize the knowledge graph
- **Query testing** — Interactive recall and reflect testing
- **Operation history** — View ingestion and processing logs
:::tip
The UI command requires Node.js to be installed. It automatically downloads and runs the `@vectorize-io/hindsight-control-plane` package via npx.
:::
## Interactive Explorer
Launch the TUI explorer for visual navigation of your memory banks:
```bash
hindsight explore
```
The explorer provides an interactive terminal interface to:
- **Browse memory banks** — View all banks and their statistics
- **Search memories** — Run recall queries with real-time results
- **Inspect entities** — Explore the knowledge graph and entity relationships
- **View facts** — Browse world facts, experiences, and opinions
- **Navigate documents** — See source documents and their extracted memories
### Keyboard Shortcuts
| Key | Action |
|-----|--------|
| `↑/↓` | Navigate items |
| `Enter` | Select / Expand |
| `Tab` | Switch panels |
| `/` | Search |
| `q` | Quit |
<!-- Screenshot placeholder: explore command TUI -->
## Example Workflow
```bash
# Configure API URL
hindsight configure --api-url http://localhost:8888
# Store some memories
hindsight memory retain demo "Alice works at Google"
hindsight memory retain demo "Bob is a data scientist"
hindsight memory retain demo "Alice and Bob are colleagues"
# Search memories
hindsight memory recall demo "Who works with Alice?"
# Generate a response
hindsight memory reflect demo "What do you know about the team?"
# Check bank disposition
hindsight bank disposition demo
```
@@ -1,129 +0,0 @@
---
sidebar_position: 2
---
# TypeScript Client
Official TypeScript/JavaScript client for the Hindsight API.
## Installation
```bash
npm install @vectorize-io/hindsight-client
```
## Quick Start
```typescript
const { HindsightClient } = require('@vectorize-io/hindsight-client');
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Retain a memory
await client.retain('my-bank', 'Alice works at Google');
// Recall memories
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(r.text);
}
// Reflect - generate response with disposition
const answer = await client.reflect('my-bank', 'Tell me about Alice');
console.log(answer.text);
```
## Client Initialization
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({
baseUrl: 'http://localhost:8888',
});
```
## Core Operations
### Retain (Store Memory)
```typescript
// Simple
await client.retain('my-bank', 'Alice works at Google');
// With options
await client.retain('my-bank', 'Alice got promoted', {
timestamp: new Date('2024-01-15'),
context: 'career update',
metadata: { source: 'slack' },
async: false, // Set true for background processing
});
```
### Retain Batch
```typescript
await client.retainBatch('my-bank', [
{ content: 'Alice works at Google', context: 'career' },
{ content: 'Bob is a data scientist', context: 'career' },
], {
async: false,
});
```
### Recall (Search)
```typescript
// Simple - returns RecallResponse
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(`${r.text} (type: ${r.type})`);
}
// With options
const response = await client.recall('my-bank', 'What does Alice do?', {
types: ['world', 'opinion'], // Filter by fact type
maxTokens: 4096,
budget: 'high', // 'low', 'mid', or 'high'
});
```
### Reflect (Generate Response)
```typescript
const answer = await client.reflect('my-bank', 'What should I know about Alice?', {
budget: 'low', // 'low', 'mid', or 'high'
context: 'preparing for a meeting',
});
console.log(answer.text); // Generated response
```
## Bank Management
### Create Bank
```typescript
await client.createBank('my-bank', {
name: 'Assistant',
background: 'I am a helpful AI assistant',
disposition: {
skepticism: 3, // 1-5: trusting to skeptical
literalism: 3, // 1-5: flexible to literal
empathy: 3, // 1-5: detached to empathetic
},
});
```
### List Memories
```typescript
const response = await client.listMemories('my-bank', {
type: 'world', // Optional filter
q: 'Alice', // Optional text search
limit: 100,
offset: 0,
});
console.log(response)
```
@@ -1,262 +0,0 @@
---
sidebar_position: 1
---
# Python Client
Official Python client for the Hindsight API.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
## Installation
<Tabs>
<TabItem value="all-in-one" label="All-in-One (Recommended)">
The `hindsight-all` package includes embedded PostgreSQL, HTTP API server, and client:
```bash
pip install hindsight-all
```
</TabItem>
<TabItem value="client-only" label="Client Only">
If you already have a Hindsight server running:
```bash
pip install hindsight-client
```
</TabItem>
</Tabs>
## Quick Start
<Tabs>
<TabItem value="all-in-one" label="All-in-One">
```python
import os
from hindsight import HindsightServer, HindsightClient
with HindsightServer(
llm_provider="openai",
llm_model="gpt-4.1-mini",
llm_api_key=os.environ["OPENAI_API_KEY"]
) as server:
client = HindsightClient(base_url=server.url)
# Retain a memory
client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r.text)
# Reflect - generate response with disposition
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
```
</TabItem>
<TabItem value="client-only" label="Client Only">
```python
from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888")
# Retain a memory
client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r.text)
# Reflect - generate response with disposition
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
```
</TabItem>
</Tabs>
## Client Initialization
```python
from hindsight_client import Hindsight
client = Hindsight(
base_url="http://localhost:8888", # Hindsight API URL
timeout=30.0, # Request timeout in seconds
)
```
## Core Operations
### Retain (Store Memory)
```python
# Simple
client.retain(
bank_id="my-bank",
content="Alice works at Google as a software engineer",
)
# With options
from datetime import datetime
client.retain(
bank_id="my-bank",
content="Alice got promoted",
context="career update",
timestamp=datetime(2024, 1, 15),
document_id="conversation_001",
metadata={"source": "slack"},
)
```
### Retain Batch
```python
client.retain_batch(
bank_id="my-bank",
items=[
{"content": "Alice works at Google", "context": "career"},
{"content": "Bob is a data scientist", "context": "career"},
],
document_id="conversation_001",
retain_async=False, # Set True for background processing
)
```
### Recall (Search)
```python
# Simple - returns list of RecallResult
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
)
for r in results.results:
print(f"{r.text} (type: {r.type})")
# With options
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "opinion"], # Filter by fact type
max_tokens=4096,
budget="high", # low, mid, or high
)
```
### Recall with Full Response
```python
# Returns RecallResponse with entities and chunks
response = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "experience"],
budget="mid",
max_tokens=4096,
include_entities=True,
max_entity_tokens=500
)
print(f"Found {len(response.results)} memories")
for r in response.results:
print(f" - {r.text}")
# Access entities
if response.entities:
for entity in response.entities:
print(f"Entity: {entity.name}")
```
### Reflect (Generate Response)
```python
answer = client.reflect(
bank_id="my-bank",
query="What should I know about Alice?",
budget="low", # low, mid, or high
context="preparing for a meeting",
)
print(answer.text) # Generated response
```
## Bank Management
### Create Bank
```python
client.create_bank(
bank_id="my-bank",
name="Assistant",
background="I am a helpful AI assistant",
disposition={
"skepticism": 3, # 1-5: trusting to skeptical
"literalism": 3, # 1-5: flexible to literal
"empathy": 3, # 1-5: detached to empathetic
},
)
```
### List Memories
```python
client.list_memories(
bank_id="my-bank",
type="world", # Optional: filter by type
search_query="Alice", # Optional: text search
limit=100,
offset=0,
)
```
## Async Support
All methods have async versions prefixed with `a`:
```python
import asyncio
from hindsight_client import Hindsight
async def main():
client = Hindsight(base_url="http://localhost:8888")
# Async retain
await client.aretain(bank_id="my-bank", content="Hello world")
# Async recall
results = await client.arecall(bank_id="my-bank", query="Hello")
for r in results:
print(r.text)
# Async reflect
answer = await client.areflect(bank_id="my-bank", query="What did I say?")
print(answer.text)
client.close()
asyncio.run(main())
```
## Context Manager
```python
from hindsight_client import Hindsight
with Hindsight(base_url="http://localhost:8888") as client:
client.retain(bank_id="my-bank", content="Hello")
results = client.recall(bank_id="my-bank", query="Hello")
# Client automatically closed
```
@@ -1,191 +0,0 @@
# Admin CLI
The `hindsight-admin` CLI provides administrative commands for managing your Hindsight deployment, including database migrations, backup, and restore operations.
## Installation
The admin CLI is included with the `hindsight-api` package:
```bash
pip install hindsight-api
# or
uv add hindsight-api
```
## Commands
### run-db-migration
Run database migrations to the latest version. By default this migrates the base schema plus all tenant schemas discovered by the tenant extension. Use `--schema` for targeted migration of one schema. This is useful when you want to run migrations separately from API startup (e.g., in CI/CD pipelines or before deploying a new version).
```bash
hindsight-admin run-db-migration [OPTIONS]
```
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to run migrations on. If omitted, migrate the base schema plus all discovered tenant schemas. | All schemas |
**Examples:**
```bash
# Run migrations on the base schema plus all discovered tenant schemas
hindsight-admin run-db-migration
# Run migrations on a specific tenant schema
hindsight-admin run-db-migration --schema tenant_acme
```
:::tip Disabling Auto-Migrations
To disable automatic migrations on API startup, set `HINDSIGHT_API_RUN_MIGRATIONS_ON_STARTUP=false`. This is useful when you want to run migrations as a separate step in your deployment pipeline.
:::
---
### backup
Create a backup of all Hindsight data to a zip file.
```bash
hindsight-admin backup OUTPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `OUTPUT` | Output file path (will add `.zip` extension if not present) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to backup | `public` |
**Examples:**
```bash
# Backup to a file
hindsight-admin backup /backups/hindsight-2024-01-15.zip
# Backup a specific tenant schema
hindsight-admin backup /backups/tenant-acme.zip --schema tenant_acme
```
The backup includes:
- Memory banks and their configuration
- Documents and chunks
- Entities and their relationships
- Memory units (facts, experiences, observations)
- Entity cooccurrences and memory links
:::note Consistency
Backups are created within a database transaction with `REPEATABLE READ` isolation, ensuring a consistent snapshot across all tables.
:::
---
### restore
Restore data from a backup file. **Warning: This deletes all existing data in the target schema.**
```bash
hindsight-admin restore INPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `INPUT` | Input backup file (.zip) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to restore to | `public` |
| `--yes`, `-y` | Skip confirmation prompt | `false` |
**Examples:**
```bash
# Restore with confirmation prompt
hindsight-admin restore /backups/hindsight-2024-01-15.zip
# Restore without confirmation (for scripts)
hindsight-admin restore /backups/hindsight-2024-01-15.zip --yes
# Restore to a specific tenant schema
hindsight-admin restore /backups/tenant-acme.zip --schema tenant_acme --yes
```
:::warning Data Loss
Restore will **delete all existing data** in the target schema before importing the backup. Always verify you have a recent backup before performing a restore.
:::
---
### decommission-worker
Release all tasks owned by a worker, resetting them from "processing" back to "pending" status so they can be picked up by other workers.
```bash
hindsight-admin decommission-worker WORKER_ID [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `WORKER_ID` | ID of the worker to decommission |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema | `public` |
**Examples:**
```bash
# Before scaling down - release tasks from workers being removed
hindsight-admin decommission-worker hindsight-worker-4
hindsight-admin decommission-worker hindsight-worker-3
# Release tasks from a crashed worker
hindsight-admin decommission-worker worker-2
# For a specific tenant schema
hindsight-admin decommission-worker worker-1 --schema tenant_acme
```
**When to Use:**
- **Scaling down**: Before removing worker replicas in Kubernetes
- **Graceful removal**: When taking a worker offline for maintenance
- **Crash recovery**: If a worker crashed while processing tasks
- **Stuck worker**: When a worker is unresponsive
:::tip Finding Worker IDs
Worker IDs default to the hostname. In Kubernetes StatefulSets, this is the pod name (e.g., `hindsight-worker-0`). You can also set a custom ID with `HINDSIGHT_API_WORKER_ID` or `--worker-id`.
:::
---
## Environment Variables
The admin CLI uses the same environment variables as the API service. The most important one is:
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | `pg0` (embedded) |
**Example:**
```bash
# Use a specific database
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
hindsight-admin backup /backups/mybackup.zip
```
@@ -1,243 +0,0 @@
---
sidebar_position: 8
---
# Documents
Track and manage document sources in your memory bank. Documents provide traceability — knowing where memories came from.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import documentsPy from '!!raw-loader!@site/examples/api/documents.py';
import documentsMjs from '!!raw-loader!@site/examples/api/documents.mjs';
import documentsGo from '!!raw-loader!@site/examples/api/documents.go';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## What Are Documents?
Documents are containers for retained content. They help you:
- **Track sources** — Know which PDF, conversation, or file a memory came from
- **Update content** — Re-retain a document to update its facts
- **Delete in bulk** — Remove all memories from a document at once
- **Organize memories** — Group related facts by source
## Chunks
When you retain content, Hindsight splits it into chunks before extracting facts. These chunks are stored alongside the extracted memories, preserving the original text segments.
**Why chunks matter:**
- **Context preservation** — Chunks contain the raw text that generated facts, useful when you need the exact wording
- **Richer recall** — Including chunks in recall provides surrounding context for matched facts
:::tip Include Chunks in Recall
Use `include_chunks=True` in your recall calls to get the original text chunks alongside fact results. See [Recall](./recall) for details.
:::
## Retain with Document ID
Associate retained content with a document:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Retain content with document ID
hindsight memory retain my-bank "Meeting notes content..." --doc-id notes-2024-03-15
# Batch retain from files
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-retain" language="go" />
</TabItem>
</Tabs>
## Update Documents
Re-retaining with the same document_id **replaces** the old content:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-update" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-update" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Original
hindsight memory retain my-bank "Project deadline: March 31" --doc-id project-plan
# Update
hindsight memory retain my-bank "Project deadline: April 15 (extended)" --doc-id project-plan
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
## Get Document
Retrieve a document's original text and metadata. This is useful for expanding document context after a recall operation returns memories with document references.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-get" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-get" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document get my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-get" language="go" />
</TabItem>
</Tabs>
## Update Document
Update mutable fields on an existing document without re-processing the content. Currently supports updating `tags`.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-update" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-update" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Replace tags with new values
hindsight document update-tags my-bank meeting-2024-03-15 --tags team-a --tags team-b
# Remove all tags
hindsight document update-tags my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
:::info Observations are re-consolidated
When tags change, any consolidated observations derived from the document's memories are invalidated and queued for re-consolidation under the new tags. Co-source memories from other documents that shared those observations are also reset.
:::
## Delete Document
Remove a document and all its associated memories:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-delete" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-delete" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document delete my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-delete" language="go" />
</TabItem>
</Tabs>
:::warning
Deleting a document permanently removes all memories extracted from it. This action cannot be undone.
:::
## List Documents
List documents in a bank with optional filtering by ID and tags.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-list" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-list" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# List all documents
hindsight document list my-bank
# Filter by ID substring
hindsight document list my-bank --q report
# Filter by tags
hindsight document list my-bank --tags team-a --tags team-b
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-list" language="go" />
</TabItem>
</Tabs>
### Filtering Options
| Parameter | Description |
|---|---|
| `q` | Case-insensitive substring match on document ID. `report` matches `report-2024`, `annual-report`, etc. |
| `tags` | Filter by document tags. Accepts multiple values. |
| `tags_match` | How to match tags (default: `any_strict`). See below. |
| `limit` / `offset` | Pagination. Default limit is 100. |
**`tags_match` modes:**
| Mode | Behaviour |
|---|---|
| `any_strict` *(default)* | Document must have **at least one** of the specified tags. Untagged docs excluded. |
| `any` | Same as `any_strict` but also includes untagged documents. |
| `all_strict` | Document must have **all** specified tags. Untagged docs excluded. |
| `all` | Same as `all_strict` but also includes untagged documents. |
## Document Response Format
```json
{
"id": "meeting-2024-03-15",
"bank_id": "my-bank",
"original_text": "Alice presented the Q4 roadmap...",
"content_hash": "abc123def456",
"memory_unit_count": 12,
"created_at": "2024-03-15T14:00:00Z",
"updated_at": "2024-03-15T14:00:00Z"
}
```
## Next Steps
- [**Operations**](./operations) — Monitor background tasks
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,148 +0,0 @@
---
sidebar_position: 2
---
# Main Methods
Hindsight provides three core operations: **retain**, **recall**, and **reflect**.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mainMethodsPy from '!!raw-loader!@site/examples/api/main-methods.py';
import mainMethodsMjs from '!!raw-loader!@site/examples/api/main-methods.mjs';
import mainMethodsGo from '!!raw-loader!@site/examples/api/main-methods.go';
:::tip Prerequisites
Make sure you've [installed Hindsight](../installation) and completed the [Quick Start](./quickstart).
:::
## Retain: Store Information
Store conversations, documents, and facts into a memory bank.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Store a single fact
hindsight memory retain my-bank "Alice joined Google in March 2024 as a Senior ML Engineer"
# Store from a file
hindsight memory retain-files my-bank conversation.txt --context "Daily standup"
# Store multiple files
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-retain" language="go" />
</TabItem>
</Tabs>
**What happens:** Content is processed by an LLM to extract rich facts, identify entities, and build connections in a knowledge graph.
**See:** [Retain Details](./retain) for advanced options and parameters.
---
## Recall: Search Memories
Search for relevant memories using multi-strategy retrieval.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-recall" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-recall" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic search
hindsight memory recall my-bank "What does Alice do at Google?"
# Search with options
hindsight memory recall my-bank "What happened last spring?" \
--budget high \
--max-tokens 8192 \
--fact-type world,experience
# Verbose output
hindsight memory recall my-bank "Tell me about Alice" -v
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-recall" language="go" />
</TabItem>
</Tabs>
**What happens:** Four search strategies (semantic, keyword, graph, temporal) run in parallel, results are fused and reranked.
**See:** [Recall Details](./recall) for tuning quality vs latency.
---
## Reflect: Reason with Disposition
Generate disposition-aware responses using memories and observations.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-reflect" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-reflect" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic reflect
hindsight memory reflect my-bank "Should we adopt TypeScript for our backend?"
# With higher reasoning budget
hindsight memory reflect my-bank "Analyze our tech stack" --budget high
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-reflect" language="go" />
</TabItem>
</Tabs>
**What happens:** Memories and observations are recalled, bank disposition is applied, and the LLM reasons through the evidence to generate a response.
**See:** [Reflect Details](./reflect) for disposition configuration.
---
## Comparison
| Feature | Retain | Recall | Reflect |
|---------|--------|--------|---------|
| **Purpose** | Store information | Find information | Reason about information |
| **Input** | Raw text/documents | Search query | Question/prompt |
| **Output** | Memory IDs | Ranked facts + observations | Reasoned response |
| **Uses LLM** | Yes (extraction) | No | Yes (generation) |
| **Uses observations** | No | Yes | Yes |
| **Disposition** | No | No | Yes |
---
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Tuning search quality and performance
- [**Reflect**](./reflect) — Configuring disposition
- [**Memory Banks**](./memory-banks) — Managing memory bank disposition
@@ -1,433 +0,0 @@
---
sidebar_position: 6
---
# Memory Banks
Memory banks are isolated containers that store all memory-related data for a specific context or use case.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
import memoryBanksMjs from '!!raw-loader!@site/examples/api/memory-banks.mjs';
import memoryBanksSh from '!!raw-loader!@site/examples/api/memory-banks.sh';
import memoryBanksGo from '!!raw-loader!@site/examples/api/memory-banks.go';
import directivesPy from '!!raw-loader!@site/examples/api/directives.py';
import directivesMjs from '!!raw-loader!@site/examples/api/directives.mjs';
import directivesSh from '!!raw-loader!@site/examples/api/directives.sh';
import directivesGo from '!!raw-loader!@site/examples/api/directives.go';
## What is a Memory Bank?
A memory bank is a complete, isolated storage unit containing:
- **Memories** — Facts and information retained from conversations
- **Documents** — Files and content indexed for retrieval
- **Entities** — People, places, concepts extracted from memories
- **Relationships** — Connections between entities in the knowledge graph
- **Directives** — Hard rules the agent must follow during reflect operations
Banks are completely isolated from each other — memories stored in one bank are not visible to another.
You don't need to pre-create a bank. Hindsight will automatically create it with default settings when you first use it.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Creating a Memory Bank
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="create-bank" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="create-bank" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="create-bank" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="create-bank" language="go" />
</TabItem>
</Tabs>
## Bank Configuration
Each memory bank can be configured independently per operation. Configuration can be set via the [bank config API](#updating-configuration), the [Control Plane UI](/), or [server-wide environment variables](/developer/configuration).
### retain_mission {#retain-configuration}
A plain-language description of what this bank should pay attention to during extraction. The mission is injected into the extraction prompt alongside the built-in rules — it steers focus without replacing the extraction logic.
```
e.g. Always include technical decisions, API design choices, and architectural trade-offs.
Ignore meeting logistics, greetings, and social exchanges.
```
Works alongside any extraction mode. Leave blank for general-purpose extraction.
### retain_extraction_mode
Controls how aggressively facts are extracted:
| Mode | Description |
|------|-------------|
| `concise` *(default)* | Selective — only facts worth remembering long-term |
| `verbose` | Captures more detail per fact; slower and uses more tokens |
| `custom` | Write your own extraction rules via `retain_custom_instructions` |
### retain_custom_instructions
Only active when `retain_extraction_mode` is `custom`. Replaces the built-in extraction rules entirely with your own instructions.
### retain_chunk_size
Maximum number of characters per chunk when splitting content for fact extraction. Larger chunks mean fewer LLM calls but may reduce extraction quality on long inputs; smaller chunks improve granularity at the cost of more calls.
Default: `3000`
See [Retain configuration](/developer/configuration#retain) for environment variable names and defaults.
### entity_labels {#entity-labels}
Defines a controlled vocabulary of `key:value` classification labels extracted at retain time and stored as entities. Because labels become entities, they automatically link memories in the knowledge graph (two memories with `pedagogy:scaffolding` are linked), improve semantic and BM25 retrieval, and optionally filter memories via the standard `tags`/`tags_match` API when `tag: true` is set on a group.
Each entry in `entity_labels` is a **label group** — one classification dimension:
```json
{
"entity_labels": [
{
"key": "engagement",
"description": "Student engagement level during the session",
"type": "value",
"optional": true,
"values": [
{ "value": "active", "description": "Student is actively participating" },
{ "value": "passive", "description": "Student is listening but not participating" }
]
},
{
"key": "pedagogy",
"description": "Teaching strategies used",
"type": "multi-values",
"values": [
{ "value": "scaffolding", "description": "Breaking complex tasks into smaller steps" },
{ "value": "direct_instruction", "description": "Explicit explanation by the teacher" },
{ "value": "socratic_questioning", "description": "Guiding through questions rather than answers" }
]
}
]
}
```
| Field | Default | Description |
|-------|---------|-------------|
| `key` | — | Label group identifier. Becomes the prefix in `key:value` entities. |
| `description` | `""` | Shown to the LLM to guide label assignment. |
| `type` | `"value"` | `"value"` → pick one enum value; `"multi-values"` → pick multiple; `"text"` → free-form string. |
| `values` | `[]` | Allowed values for `"value"` and `"multi-values"` types. Ignored for `"text"`. |
| `optional` | `true` | When `true` the LLM may skip the label if not applicable. When `false` the LLM must always assign a value. Has no effect on `"multi-values"` groups (always optional). |
| `tag` | `false` | When `true`, extracted `key:value` labels are also written as tags on the memory unit, enabling filtering via `tags`/`tags_match` in recall/reflect. |
**Enum groups** (`type: "value"` or `type: "multi-values"`): the LLM picks from the predefined `values` list; anything outside the list is silently dropped. Vocabulary stays stable and graph links stay tight. Use `"multi-values"` when a fact can belong to several values at once.
**Free-text groups** (`type: "text"`): the LLM writes any string. Use the `description` field to provide examples and guidance. Graph clustering is less reliable than with enum groups because the model may phrase the same concept differently across sessions.
```json
{
"key": "topic",
"description": "Specific subject being discussed. Examples: algebra, quadratic equations, geometry.",
"type": "text",
"optional": true,
"values": []
}
```
### entities_allow_free_form
By default, entity labels are extracted **alongside** regular named entities (people, places, concepts). Set to `false` to disable free-form extraction so only label entities are stored:
```json
{
"entity_labels": [...],
"entities_allow_free_form": false
}
```
### enable_observations {#observations-configuration}
Toggles automatic observation consolidation on or off. Defaults to `true` when the observations feature is enabled on the server.
### observations_mission
Defines what this bank should synthesise into durable observations. Replaces the built-in consolidation rules entirely — leave blank to use the server default.
```
e.g. Observations are stable facts about people and projects.
Always include preferences, skills, and recurring patterns.
Ignore one-off events and ephemeral state.
```
### consolidation_llm_batch_size
Number of facts sent to the LLM in a single consolidation call. Higher values reduce LLM calls and improve throughput at the cost of larger prompts. Set to `1` to disable batching. Leave unset to use the server default (`8`).
### consolidation_source_facts_max_tokens
Total token budget for source facts included with observations in the consolidation prompt. Source facts give the LLM evidence to compare new facts against existing observations. `-1` = unlimited. Leave unset to use the server default (`-1`).
### consolidation_source_facts_max_tokens_per_observation
Per-observation token cap for source facts in the consolidation prompt. Each observation independently gets at most this many tokens of source facts, preventing a single observation with many source facts from consuming the entire budget. `-1` = unlimited. Leave unset to use the server default (`256`).
See [Observations configuration](/developer/configuration#observations) for environment variable names and defaults.
### reflect_mission
A first-person narrative that provides identity and framing context for `reflect`. The agent uses this to ground its reasoning and apply a consistent perspective.
```
e.g. You are a senior engineering assistant.
Always ground answers in documented decisions and rationale.
Ignore speculation. Be direct and precise.
```
### disposition_skepticism
How skeptical vs trusting the bank is when evaluating claims during `reflect`. Scale 15.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="bank-with-disposition" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="bank-with-disposition" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="bank-with-disposition" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="bank-with-disposition" language="go" />
</TabItem>
</Tabs>
| Value | Behaviour |
|-------|-----------|
| `1` | Trusting — accepts information at face value |
| `3` *(default)* | Balanced |
| `5` | Skeptical — questions and doubts claims |
### disposition_literalism
How literally to interpret information during `reflect`. Scale 15.
| Value | Behaviour |
|-------|-----------|
| `1` | Flexible — reads between the lines, considers context |
| `3` *(default)* | Balanced |
| `5` | Literal — takes things exactly as stated |
### disposition_empathy
How much to weight emotional context when reasoning during `reflect`. Scale 15.
| Value | Behaviour |
|-------|-----------|
| `1` | Detached — focuses on facts and logic |
| `3` *(default)* | Balanced |
| `5` | Empathetic — considers emotional context |
:::info
Disposition traits and `reflect_mission` only affect the `reflect` operation. `retain_mission` and `observations_mission` are separate per-operation settings.
:::
### mcp_enabled_tools
An allowlist of MCP tool names that are enabled for this bank. When set, only the listed tools can be invoked; any tool not in the list returns an error (tools still appear in the MCP tools list for protocol compatibility). Set to `null` (or omit) to allow all tools.
```json
["recall", "reflect"]
```
Available tool names: `retain`, `recall`, `reflect`, `list_banks`, `create_bank`, `list_mental_models`, `get_mental_model`, `create_mental_model`, `update_mental_model`, `delete_mental_model`, `refresh_mental_model`, `list_directives`, `create_directive`, `delete_directive`, `list_memories`, `get_memory`, `delete_memory`, `list_documents`, `get_document`, `delete_document`, `list_operations`, `get_operation`, `cancel_operation`, `list_tags`, `get_bank`, `get_bank_stats`, `update_bank`, `delete_bank`, `clear_memories`.
### llm_gemini_safety_settings
Controls content filtering thresholds for Gemini and VertexAI providers. Accepts a list of safety setting objects in the [Google AI safety settings format](https://ai.google.dev/api/generate-content#v1beta.SafetySetting). When `null` (default), Gemini's built-in safety defaults are used.
```json
[
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}
]
```
Only applies when `HINDSIGHT_API_LLM_PROVIDER` is `gemini` or `vertexai`.
---
## Updating Configuration
Bank configuration fields (retain mission, extraction mode, observations mission, etc.) are managed via a **separate config API**, not the `create_bank` call. This lets you change operational settings independently from the bank's identity and disposition.
### Setting Configuration Overrides
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="update-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="update-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="update-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="update-bank-config" language="go" />
</TabItem>
</Tabs>
You can update any subset of fields — only the keys you provide are changed.
### Reading the Current Configuration
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="get-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="get-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="get-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="get-bank-config" language="go" />
</TabItem>
</Tabs>
The response distinguishes:
- **`config`** — the fully resolved configuration (server defaults merged with bank overrides)
- **`overrides`** — only the fields explicitly overridden for this bank
### Resetting to Defaults
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="reset-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="reset-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="reset-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="reset-bank-config" language="go" />
</TabItem>
</Tabs>
This removes all bank-level overrides. The bank reverts to server-wide defaults (set via environment variables).
You can also update configuration directly from the [Control Plane UI](/) — navigate to a bank and open the **Configuration** tab.
---
## Directives
Directives are hard rules that the agent must follow during [reflect](./reflect) operations. Unlike disposition traits which influence *how* the agent reasons, directives are explicit instructions that are *always* enforced.
:::info
Directives only affect the `reflect` operation. They are injected into prompts and the agent is required to comply with them in all responses.
:::
### When to Use Directives
Use directives for rules that must never be violated:
- **Language/style constraints**: "Always respond in formal English"
- **Privacy rules**: "Never share personal data with third parties"
- **Domain constraints**: "Prefer conservative investment recommendations"
- **Behavioral guardrails**: "Always cite sources when making claims"
### Creating Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="create-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="create-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="create-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="create-directive" language="go" />
</TabItem>
</Tabs>
### Listing Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="list-directives" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="list-directives" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="list-directives" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="list-directives" language="go" />
</TabItem>
</Tabs>
### Updating Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="update-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="update-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="update-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="update-directive" language="go" />
</TabItem>
</Tabs>
### Deleting Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="delete-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="delete-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="delete-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="delete-directive" language="go" />
</TabItem>
</Tabs>
### Directives vs Disposition
| Aspect | Directives | Disposition |
|--------|------------|-------------|
| **Nature** | Hard rules, must be followed | Soft influence on reasoning style |
| **Enforcement** | Strict — responses are rejected if violated | Flexible — shapes interpretation |
| **Use case** | Compliance, guardrails, constraints | Personality, character, tone |
| **Example** | "Never recommend specific stocks" | High skepticism: questions claims |
@@ -1,350 +0,0 @@
---
sidebar_position: 4
---
# Mental Models
User-curated summaries that provide high-quality, pre-computed answers for common queries.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mentalModelsPy from '!!raw-loader!@site/examples/api/mental-models.py';
import mentalModelsMjs from '!!raw-loader!@site/examples/api/mental-models.mjs';
import mentalModelsSh from '!!raw-loader!@site/examples/api/mental-models.sh';
import mentalModelsGo from '!!raw-loader!@site/examples/api/mental-models.go';
## What Are Mental Models?
Mental models are **saved reflect responses** that you curate for your memory bank. When you create a mental model, Hindsight runs a reflect operation with your source query and stores the result. During future reflect calls, these pre-computed summaries are checked first — providing faster, more consistent answers.
```mermaid
graph LR
A[Create Mental Model] --> B[Run Reflect]
B --> C[Store Result]
C --> D[Future Queries]
D --> E{Match Found?}
E -->|Yes| F[Return Mental Model]
E -->|No| G[Run Full Reflect]
```
### Why Use Mental Models?
| Benefit | Description |
|---------|-------------|
| **Consistency** | Same answer every time for common questions |
| **Speed** | Pre-computed responses are returned instantly |
| **Quality** | Manually curated summaries you've reviewed |
| **Control** | Define exactly how key topics should be answered |
### Hierarchical Retrieval
During reflect, the agent checks sources in priority order:
1. **Mental Models** — User-curated summaries (highest priority)
2. **Observations** — Consolidated knowledge
3. **Raw Facts** — Ground truth memories
Mental models are checked first because they represent your explicitly curated knowledge.
---
## Create a Mental Model
Creating a mental model runs a reflect operation in the background and saves the result:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model" language="go" />
</TabItem>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query to run to generate content |
| `id` | string | No | Custom ID for the mental model (alphanumeric lowercase with hyphens). Auto-generated if omitted. |
| `tags` | list | No | Tags for filtering during retrieval |
| `max_tokens` | int | No | Maximum tokens for the mental model content |
| `trigger` | object | No | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
---
## Create with Custom ID
Assign a stable, human-readable ID to a mental model so you can retrieve or update it by name instead of relying on the auto-generated UUID:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-id" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-id" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-id" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-id" language="go" />
</TabItem>
</Tabs>
:::tip
Custom IDs must be lowercase alphanumeric and may contain hyphens (e.g. `team-policies`, `q4-status`). If a mental model with that ID already exists, the request is rejected.
:::
---
## Automatic Refresh
Mental models can be configured to **automatically refresh** when observations are updated. This keeps them in sync with the latest knowledge without manual intervention.
### Trigger Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `refresh_after_consolidation` | bool | false | Automatically refresh after observations consolidation |
When `refresh_after_consolidation` is enabled, the mental model will be re-generated every time the bank's observations are consolidated — ensuring it always reflects the latest synthesized knowledge.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-trigger" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-trigger" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-trigger" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-trigger" language="go" />
</TabItem>
</Tabs>
### When to Use Automatic Refresh
| Use Case | Automatic Refresh | Why |
|----------|-------------------|-----|
| **Real-time dashboards** | ✅ Enabled | Status should always be current |
| **Policy summaries** | ❌ Disabled | Policies change infrequently, manual refresh preferred |
| **User preferences** | ✅ Enabled | Preferences evolve with new interactions |
| **FAQ answers** | ❌ Disabled | Answers are curated, should be reviewed before updating |
:::tip
Enable automatic refresh for mental models that need to stay current. Disable it for curated content where you want to review changes before they go live.
:::
---
## List Mental Models
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="list-mental-models" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="list-mental-models" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="list-mental-models" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="list-mental-models" language="go" />
</TabItem>
</Tabs>
---
## Get a Mental Model
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="get-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model" language="go" />
</TabItem>
</Tabs>
### Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique mental model ID |
| `bank_id` | string | Memory bank ID |
| `name` | string | Human-readable name |
| `source_query` | string | The query used to generate content |
| `content` | string | The generated mental model text |
| `tags` | list | Tags for filtering |
| `last_refreshed_at` | string | When the mental model was last updated |
| `created_at` | string | When the mental model was created |
| `reflect_response` | object | Full reflect response including `based_on` facts |
---
## Refresh a Mental Model
Re-run the source query to update the mental model with current knowledge:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="refresh-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="refresh-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="refresh-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="refresh-mental-model" language="go" />
</TabItem>
</Tabs>
Refreshing is useful when:
- New memories have been retained that affect the topic
- Observations have been updated
- You want to ensure the mental model reflects current knowledge
---
## Update a Mental Model
Update the mental model's name:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="update-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="update-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="update-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="update-mental-model" language="go" />
</TabItem>
</Tabs>
---
## Delete a Mental Model
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="delete-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="delete-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="delete-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="delete-mental-model" language="go" />
</TabItem>
</Tabs>
---
## Tags and Visibility
Mental models support the same tag system as memories. When you assign tags to a mental model, those tags control both **which memories it reads** during refresh and **when it is surfaced** during reflect.
### How tags affect mental model refresh
When a mental model is refreshed (manually or automatically), it runs an internal reflect call to regenerate its content. If the mental model has tags, that reflect call uses `all_strict` tag matching — meaning it will only read memories that carry **all** of the mental model's tags. Untagged memories are excluded.
```
Mental model tags: ["user:alice"]
During refresh, it reads:
✅ "Alice prefers async communication" — has "user:alice"
✅ "Team uses Slack for announcements" — has "user:alice" (plus other tags)
❌ "Company policy: no meetings on Fridays" — untagged, excluded
❌ "Bob dislikes long meetings" — no "user:alice" tag
```
This means a mental model tagged `["user:alice"]` will also pick up memories tagged `["user:alice", "team"]` — extra tags on a memory don't disqualify it. Only the mental model's own tags are required to be present.
### How tags affect mental model lookup during reflect
When you call `reflect` with tags, those same tags are used to filter which mental models the agent can see. A mental model is visible only if its tags overlap with the tags on the reflect request.
For more details on tag matching modes (`any`, `any_strict`, `all`, `all_strict`) and worked examples, see the [Recall tags reference](./recall#tags).
---
## History
Every time a mental model's content changes (via refresh or manual update), the previous version is saved with a timestamp. You can retrieve the full change log with the history endpoint:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model-history" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model-history" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="get-mental-model-history" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model-history" language="go" />
</TabItem>
</Tabs>
### Response
The endpoint returns a list of history entries, most recent first:
| Field | Type | Description |
|-------|------|-------------|
| `previous_content` | string \| null | The content before this change (`null` if not available) |
| `changed_at` | string | ISO 8601 timestamp of when the change occurred |
Each entry captures the **content before the change** and when it happened. The current content is returned by the standard [Get a Mental Model](#get-a-mental-model) endpoint.
:::note
History tracking is enabled by default. Set `HINDSIGHT_API_ENABLE_MENTAL_MODEL_HISTORY=false` to disable it.
:::
---
## Use Cases
| Use Case | Example |
|----------|---------|
| **FAQ Answers** | Pre-compute answers to common customer questions |
| **Onboarding Summaries** | "What should new team members know?" |
| **Status Reports** | "What's the current project status?" refreshed weekly |
| **Policy Summaries** | "What are our security policies?" |
---
## Next Steps
- [**Reflect**](./reflect) — How the agentic loop uses mental models
- [**Observations**](/developer/observations) — How knowledge is consolidated
- [**Operations**](./operations) — Track async mental model creation
@@ -1,121 +0,0 @@
---
sidebar_position: 9
---
# Operations
Background tasks that Hindsight executes asynchronously.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## How Operations Work
Hindsight processes several types of tasks in the background to maintain memory quality and consistency. These operations run automatically—you don't need to trigger them manually.
By default, all background operations are executed in-process within the API service.
:::note Kafka Integration
Support for external streaming platforms like Kafka for scale-out processing is planned but **not available out of the box** in the current release.
:::
## Operation Types
| Operation | Trigger | Description |
|-----------|---------|-------------|
| **batch_retain** | `retain_batch` with `async=True` | Processes large content batches in the background |
| **consolidate** | After `retain` | Consolidates new facts into observations |
## Async Retain Example
When retaining large batches of memories, use `async=true` to process in the background. The response includes an `operation_id` that you can use to poll for completion.
### 1. Submit async retain request
```bash
curl -X POST "http://localhost:8000/v1/default/banks/my-bank/memories" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"content": "Alice joined Google in 2023"},
{"content": "Bob prefers Python over JavaScript"}
],
"async": true
}'
```
Response:
```json
{
"success": true,
"bank_id": "my-bank",
"items_count": 2,
"async": true,
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
### 2. Poll for operation status
```bash
curl "http://localhost:8000/v1/default/banks/my-bank/operations"
```
Response:
```json
{
"bank_id": "my-bank",
"operations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"task_type": "retain",
"items_count": 2,
"document_id": null,
"created_at": "2024-01-15T10:30:00Z",
"status": "completed",
"error_message": null
}
]
}
```
### Operation Status Values
| Status | Description |
|--------|-------------|
| `pending` | Operation is queued and waiting to be processed |
| `completed` | Operation finished successfully |
| `failed` | Operation failed (check `error_message` for details) |
## Managing Operations
### Cancel a pending operation
```bash
curl -X DELETE "http://localhost:8000/v1/default/banks/my-bank/operations/550e8400-e29b-41d4-a716-446655440000"
```
### Retry a failed operation
If an operation fails, you can manually re-queue it for execution:
```bash
curl -X POST "http://localhost:8000/v1/default/banks/my-bank/operations/550e8400-e29b-41d4-a716-446655440000/retry"
```
Response:
```json
{
"success": true,
"message": "Operation 550e8400-e29b-41d4-a716-446655440000 queued for retry",
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
The operation status resets to `pending` and the worker picks it up again. Returns `409` if the operation is not in `failed` state.
## Next Steps
- [**Documents**](./documents) — Track document sources
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,128 +0,0 @@
---
sidebar_position: 0
---
# Quick Start
Get up and running with Hindsight in 60 seconds.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
import {ClientsGrid} from '@site/src/components/SupportedGrids';
{/* Import raw source files */}
import quickstartPy from '!!raw-loader!@site/examples/api/quickstart.py';
import quickstartMjs from '!!raw-loader!@site/examples/api/quickstart.mjs';
import quickstartSh from '!!raw-loader!@site/examples/api/quickstart.sh';
import quickstartGo from '!!raw-loader!@site/examples/api/quickstart.go';
## Clients
<ClientsGrid />
## Start the API Server
<Tabs>
<TabItem value="pip" label="pip (API only)">
```bash
pip install hindsight-api
export OPENAI_API_KEY=sk-xxx
export HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY
hindsight-api
```
API available at [http://localhost:8888](http://localhost:8888/docs)
</TabItem>
<TabItem value="docker" label="Docker (Full Experience)">
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
</TabItem>
</Tabs>
:::tip LLM Provider
Hindsight requires an LLM with structured output support. Recommended: **Groq** with `gpt-oss-20b` for fast, cost-effective inference.
See [LLM Providers](/developer/models#llm) for more details.
:::
---
## Use the Client
<Tabs>
<TabItem value="python" label="Python">
```bash
pip install hindsight-client
```
<CodeSnippet code={quickstartPy} section="quickstart-full" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
```bash
npm install @vectorize-io/hindsight-client
```
<CodeSnippet code={quickstartMjs} section="quickstart-full" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
<CodeSnippet code={quickstartSh} section="quickstart-full" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
```bash
go get github.com/vectorize-io/hindsight/hindsight-clients/go
```
<CodeSnippet code={quickstartGo} section="quickstart-full" language="go" />
</TabItem>
</Tabs>
---
## What's Happening
| Operation | What it does |
|-----------|--------------|
| **Retain** | Content is processed, facts are extracted, entities are identified and linked in a knowledge graph |
| **Recall** | Four search strategies (semantic, keyword, graph, temporal) run in parallel to find relevant memories |
| **Reflect** | Retrieved memories are used to generate a disposition-aware response |
---
## Integrations
Browse all supported integrations in the [Integrations Hub](/integrations).
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Search and retrieval strategies
- [**Reflect**](./reflect) — Disposition-aware reasoning
- [**Memory Banks**](./memory-banks) — Configure disposition and mission
- [**Server Deployment**](/developer/installation) — Docker Compose, Helm, and production setup
@@ -1,414 +0,0 @@
---
sidebar_position: 2
---
# Recall Memories
Retrieve memories from a bank using multi-strategy recall.
When you **recall**, Hindsight runs four retrieval strategies in parallel — semantic similarity, keyword (BM25), graph traversal, and temporal — then fuses and reranks the results into a single ranked list. The response contains structured facts, not raw documents.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import recallMjs from '!!raw-loader!@site/examples/api/recall.mjs';
import recallSh from '!!raw-loader!@site/examples/api/recall.sh';
import recallGo from '!!raw-loader!@site/examples/api/recall.go';
:::info How Recall Works
Learn about the four retrieval strategies (semantic, keyword, graph, temporal) and RRF fusion in the [Recall Architecture](/developer/retrieval) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Recall
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-basic" language="go" />
</TabItem>
</Tabs>
---
## Parameters
### query
The natural language question or statement to search for. This is the only required field. The query drives all four retrieval strategies simultaneously: it is embedded for semantic search, tokenized for BM25 keyword search, used to seed graph traversal, and parsed for temporal expressions. After retrieval, the raw query text is also passed to the cross-encoder reranker to re-score every candidate. Queries exceeding 500 tokens are rejected.
### types
Controls which categories of memory facts are searched. Accepted values are `world` (objective facts), `experience` (events and conversations), and `observation` (consolidated knowledge synthesized over time). When omitted, all three types are searched.
Each type runs the full four-strategy retrieval pipeline independently, so narrowing `types` reduces both the result set and query cost.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-world-only" language="python" />
<CodeSnippet code={recallPy} section="recall-experience-only" language="python" />
<CodeSnippet code={recallPy} section="recall-observations-only" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-world-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-experience-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-observations-only" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-fact-type" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-world-only" language="go" />
<CodeSnippet code={recallGo} section="recall-experience-only" language="go" />
<CodeSnippet code={recallGo} section="recall-observations-only" language="go" />
</TabItem>
</Tabs>
:::tip About Observations
Observations are consolidated knowledge synthesized from multiple facts over time — patterns, preferences, and learnings the memory bank has built up. They are created automatically in the background after retain operations.
:::
### budget
Controls retrieval depth and breadth. Accepted values are `low`, `mid` (default), and `high`. Use `low` for fast simple lookups, `mid` for balanced everyday queries, and `high` when you need to find indirect connections or exhaustive coverage.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-budget-levels" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-budget-levels" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-budget-levels" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-budget-levels" language="go" />
</TabItem>
</Tabs>
### max_tokens
The maximum number of tokens the returned facts can collectively occupy. Defaults to `4096`. Only the `text` field of each fact is counted toward this budget — metadata, tags, entities, and other fields are not included. After reranking, facts are included in relevance order until this budget is exhausted — so you always get the most relevant memories that fit. Hindsight is designed for agents, which think in tokens rather than result counts: set `max_tokens` to however much of your context window you want to allocate to memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-token-budget" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-token-budget" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-token-budget" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-token-budget" language="go" />
</TabItem>
</Tabs>
### query_timestamp
An ISO 8601 datetime representing when the query is being asked, from the user's perspective. When provided, it is used as the anchor for resolving relative temporal expressions in the query — for example, if the query says "last month" and `query_timestamp` is `2023-05-30`, the temporal search window becomes approximately April 2023. Without it, the server's current time is used as the anchor. This field matters most for replaying historical conversations or building agents that need time-anchored recall.
### include
An optional object controlling supplementary data returned alongside the main facts.
#### chunks
When enabled, the response includes the raw source text chunks from which each fact was extracted. Chunks are fetched before the `max_tokens` filter, so setting `max_tokens=0` returns no facts but can still return chunks. The `max_tokens` sub-option (default `8192`) controls the total chunk token budget independently of the main fact budget. This is useful when agents need surrounding context beyond the extracted fact text.
:::note
When `include_chunks` is enabled, chunks are fetched based on the top-scored reranked results before token filtering. The last chunk is truncated (not dropped) to fit exactly within the budget, and each chunk carries a `truncated` flag indicating whether it was cut.
:::
#### source_facts
When enabled and `types` includes `observation`, each observation result is accompanied by the original contributing facts it was synthesized from. Source facts are returned in a top-level `source_facts` dict keyed by fact ID, and each observation result carries a `source_fact_ids` list for cross-referencing. Facts are deduplicated across observations. The `max_tokens` sub-option (default `4096`) limits the total token budget for source facts.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-source-facts" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-source-facts" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-source-facts" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-source-facts" language="go" />
</TabItem>
</Tabs>
#### entities
Enabled by default. When active, each returned fact includes the canonical names of entities associated with it. Set to `null` to skip the entity JOIN query and reduce response size. The `max_tokens` sub-option (default `500`) is a future-facing guard for entity data.
### tags
Filters recall to only memories that match the specified tags. When omitted, all memories regardless of tags are eligible. Tag filtering is applied at the database level across all four retrieval strategies, not as a post-processing step.
The `tags_match` parameter controls the filtering logic:
| Mode | Untagged memories | Match condition |
|------|-------------------|-----------------|
| `any` (default) | Included | Memory has **at least one** of the specified tags |
| `any_strict` | Excluded | Memory has **at least one** of the specified tags |
| `all` | Included | Memory has **all** of the specified tags |
| `all_strict` | Excluded | Memory has **all** of the specified tags |
#### Scenario setup
Consider a bank with these four memories:
| Memory | Tags |
|--------|------|
| "Alice prefers async communication" | `["user:alice"]` |
| "Bob dislikes long meetings" | `["user:bob"]` |
| "Team uses Slack for announcements" | `["user:alice", "team"]` |
| "Company policy: no meetings on Fridays" | *(untagged)* |
#### `any` — OR matching, includes untagged (default)
Returns memories that have **at least one** matching tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-with-tags" language="go" />
</TabItem>
</Tabs>
Use this for **shared global knowledge + user-specific** patterns, where untagged memories represent information everyone should see.
#### `any_strict` — OR matching, excludes untagged
Same as `any` but untagged memories are excluded.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-strict" language="go" />
</TabItem>
</Tabs>
Use this when memories are **fully partitioned by tags** and untagged memories should never be visible.
#### `all` — AND matching, includes untagged
Returns memories that have **every** specified tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-mode" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-mode" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-mode" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all-mode" language="go" />
</TabItem>
</Tabs>
Use this when memories must belong to a **specific intersection** of scopes (e.g., only memories relevant to both a user and a project), while still surfacing shared global knowledge.
#### `all_strict` — AND matching, excludes untagged
Returns memories that have **every** specified tag, and excludes untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all" language="go" />
</TabItem>
</Tabs>
Use this for strict scope enforcement where a memory must explicitly belong to **all** specified contexts.
:::tip Extra tags are fine
A memory with tags `["user:alice", "team", "project:x"]` will still match a filter of `["user:alice", "team"]` under `all_strict` — extra tags on the memory are not a problem. The filter only requires the memory to contain **at least** the specified tags.
:::
### tag_groups
`tag_groups` is a list of compound boolean tag filters. The groups in the list are AND-ed together at the top level. Each group is a recursive boolean expression: a **leaf** node `{tags, match}`, or a **compound** node `{and: [...]}`, `{or: [...]}`, or `{not: ...}`.
`tag_groups` and `tags` / `tags_match` can be used simultaneously — they are AND-ed together.
#### Leaf node
```json
{ "tags": ["step:5", "step:8"], "match": "any_strict" }
```
`match` accepts the same values as `tags_match`: `any`, `all`, `any_strict`, `all_strict`. Defaults to `any_strict`.
#### Compound nodes
```json
{ "and": [ <TagGroup>, <TagGroup>, ... ] }
{ "or": [ <TagGroup>, <TagGroup>, ... ] }
{ "not": <TagGroup> }
```
#### Examples
**Step filter AND user scope** — two top-level groups AND-ed:
```json
{
"tag_groups": [
{ "tags": ["step:5", "step:8", "step:12"], "match": "any_strict" },
{ "tags": ["user:ep_42"], "match": "all_strict" }
]
}
```
**Nested OR inside AND** — user must match, plus either step OR priority:
```json
{
"tag_groups": [
{ "tags": ["user:alice"], "match": "all_strict" },
{ "or": [
{ "tags": ["step:5"], "match": "any_strict" },
{ "tags": ["priority:high"], "match": "all_strict" }
]}
]
}
```
**Exclusion** — user must match, but archived memories are excluded:
```json
{
"tag_groups": [
{ "tags": ["user:alice"], "match": "all_strict" },
{ "not": { "tags": ["archived"], "match": "any_strict" } }
]
}
```
### trace
When set to `true`, the response includes a detailed debug trace covering the query embedding, entry points, per-strategy retrieval results, RRF fusion candidates, reranked results, temporal constraints detected, and per-phase timings. Has no effect on the retrieval logic itself. Useful for understanding why specific memories were or were not returned.
---
## Response
### results
The main list of recalled facts, ordered by relevance. Relevance is computed by running four retrieval strategies in parallel — semantic similarity, BM25 keyword, graph traversal, and temporal — fusing their rankings with Reciprocal Rank Fusion (RRF), then re-scoring the merged candidates with a cross-encoder reranker against the original query.
Results do not include a numeric score. Raw retrieval scores are not meaningful on an absolute scale — a score of 0.8 from one query tells you nothing useful compared to a score of 0.8 from another. What matters is the relative ordering, which is already reflected in the list order. Agents should consume memories in order and let `max_tokens` determine how many fit, rather than filtering by score.
Each item in `results` has the following fields:
#### id
The unique identifier of this fact. Use it to cross-reference with `source_facts` or for application-level deduplication.
#### text
The extracted fact text as stored in the memory bank.
#### type
The fact category: `world` for objective information, `experience` for events and conversations, or `observation` for consolidated knowledge synthesized over time.
#### context
The context label provided when the fact was retained (e.g., `"team meeting"`, `"slack"`). `null` if none was set.
#### metadata
The key-value string pairs attached when the fact was retained. `null` if none were set.
#### tags
The visibility-scoping tags attached to this fact.
#### entities
A list of canonical entity name strings linked to this fact. Only populated when `include.entities` is enabled (the default). `null` otherwise.
#### occurred_start / occurred_end
ISO 8601 datetimes representing when the described event started and ended. Extracted by the LLM from the content during retain. `null` if the content had no temporal information.
#### mentioned_at
ISO 8601 datetime of when this fact was retained into the bank.
#### document_id
The document ID this fact belongs to, as set during retain.
#### chunk_id
The ID of the source text chunk this fact was extracted from. Used to cross-reference with `chunks` in the response when `include.chunks` is enabled.
#### source_fact_ids
For `observation`-type results only: the IDs of the original facts this observation was synthesized from. Cross-references with `source_facts` in the response. `null` for other types or when `include.source_facts` is not enabled.
---
### source_facts
A dict keyed by fact ID containing full `RecallResult` objects for the source facts that contributed to observation results. Only present when `include.source_facts` is enabled. Facts are deduplicated — if two observations share a source fact, it appears once.
### chunks
A dict keyed by chunk ID containing the raw source text chunks associated with the returned facts. Only present when `include.chunks` is enabled. Each chunk has `id`, `text`, `chunk_index`, and `truncated` (whether the text was cut to fit the token budget).
### entities
A dict keyed by canonical entity name containing entity state objects. Only present when `include.entities` is enabled. Each entry has `entity_id`, `canonical_name`, and `observations`.
### trace
A debug object present only when `trace: true` was set in the request. Contains per-phase timings, retrieval breakdowns, and RRF fusion details.
@@ -1,171 +0,0 @@
---
sidebar_position: 3
---
# Reflect
Generate a grounded, disposition-aware response using an agentic reasoning loop.
When you call **reflect**, Hindsight runs an agentic loop that autonomously searches the memory bank using multiple retrieval tools, applies the bank's disposition traits to shape the reasoning style, and produces a final answer grounded in what it found. Unlike recall — which returns raw facts — reflect returns a synthesized response written by the LLM.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import reflectPy from '!!raw-loader!@site/examples/api/reflect.py';
import reflectMjs from '!!raw-loader!@site/examples/api/reflect.mjs';
import reflectSh from '!!raw-loader!@site/examples/api/reflect.sh';
import reflectGo from '!!raw-loader!@site/examples/api/reflect.go';
:::info How Reflect Works
Learn about disposition-driven reasoning in the [Reflect Architecture](/developer/reflect) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Usage
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-basic" language="go" />
</TabItem>
</Tabs>
---
## Parameters
### query
The question or prompt to reflect on. This is the only required field. If you have situational context that should influence the answer, include it directly in the query rather than as a separate field.
### budget
Controls how thoroughly the agent explores the memory bank before answering. Accepted values are `low` (default), `mid`, and `high`. At `low`, the agent does a shallow search optimized for speed. At `mid`, it checks multiple sources when the question warrants it. At `high`, it performs deep exploration across all knowledge levels and may use multiple query variations to find indirect connections. Use `high` for complex questions that require synthesizing information from many sources.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-params" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-params" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-params" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-params" language="go" />
</TabItem>
</Tabs>
### max_tokens
Limits the length of the final generated response. Defaults to `4096`. This does not affect how much the agent can retrieve during the agentic loop — only the final answer length.
### response_schema
An optional JSON Schema object. When provided, the LLM generates a response that conforms to the schema and the response includes a `structured_output` field with the result parsed accordingly. The `text` field will be empty since only a single structured LLM call is made. Use this when you need to process the response programmatically rather than display it as prose.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-structured-output" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-structured-output" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-structured-output" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-structured-output" language="go" />
</TabItem>
</Tabs>
### tags
Filters which memories the agent can access during reflection. Works identically to [recall tags](./recall#tags) — only memories matching the specified tags are considered. The `tags_match` parameter controls the matching logic (`any`, `all`, `any_strict`, `all_strict`) with the same semantics as recall.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-tags" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-tags" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-tags" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-tags" language="go" />
</TabItem>
</Tabs>
### include
Controls optional supplementary data returned alongside the main response.
#### include.facts
When enabled, the response includes a `based_on` object listing the memories, mental models, and directives the agent actually used to construct the answer. Only sources retrieved during the agent loop can appear here — citations are validated to prevent hallucinated references. Useful for transparency and verification.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-sources" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-sources" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-sources" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-sources" language="go" />
</TabItem>
</Tabs>
#### include.tool_calls
When enabled, the response includes a `trace` object with the full execution log of every tool call and LLM call made during the agentic loop, including inputs, outputs, and durations. Set `output: false` to include only tool inputs for a smaller payload. Useful for debugging why the agent reached a particular conclusion.
---
## Response
### text
The synthesized answer as a well-formatted markdown string. This is the primary output of reflect. Empty when `response_schema` is provided (use `structured_output` instead in that case).
### structured_output
The LLM's response parsed according to the `response_schema` provided in the request. Only present when `response_schema` was set. `null` otherwise.
### based_on
The sources the agent used to construct the answer. Only present when `include.facts` was enabled. Contains three fields:
- `memories` — a list of memory facts (world, experience, observation) that were retrieved and cited. Each item has `id`, `text`, `type`, `context`, `occurred_start`, and `occurred_end`.
- `mental_models` — a list of mental models that were used. Each item has `id`, `text`, and `context`.
- `directives` — a list of directives that were enforced during reasoning. Each item has `id`, `name`, and `content`.
### usage
Token usage for all LLM calls made during the agentic loop: `input_tokens`, `output_tokens`, and `total_tokens`. Useful for cost tracking.
### trace
The full execution log of the agentic loop. Only present when `include.tool_calls` was enabled. Contains:
- `tool_calls` — each tool invocation with `tool` name (`lookup`, `recall`, `learn`, `expand`), `input`, `output` (if `output: true`), `duration_ms`, and `iteration` number.
- `llm_calls` — each LLM call with `scope` (e.g., `"agent_1"`, `"final"`) and `duration_ms`.
@@ -1,309 +0,0 @@
---
sidebar_position: 2
---
# Ingest Data
Store documents, conversations, and raw content into Hindsight to automatically extract and create memories.
When you **retain** content, Hindsight doesn't just store the raw text—it intelligently analyzes the content to extract meaningful facts, identify entities, and build a connected knowledge graph. This process transforms unstructured information into structured, queryable memories.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import retainPy from '!!raw-loader!@site/examples/api/retain.py';
import retainMjs from '!!raw-loader!@site/examples/api/retain.mjs';
import retainSh from '!!raw-loader!@site/examples/api/retain.sh';
import retainGo from '!!raw-loader!@site/examples/api/retain.go';
:::info How Retain Works
Learn about fact extraction, entity resolution, and graph construction in the [Retain Architecture](/developer/retain) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Store a Document
A single retain call accepts one or more **items**. Each item is a piece of raw content — a conversation, a document, a note — that Hindsight will analyze and decompose into one or many memories. The content itself is never stored verbatim; what gets stored are the structured facts the LLM extracts from it.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-basic" language="go" />
</TabItem>
</Tabs>
### Retaining a Conversation
A full conversation should be retained as a single item. The LLM can parse any format — plain text, JSON, Markdown, or any structured representation — as long as it clearly conveys who said what and when. The example below uses a simple `Name (timestamp): text` format.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-conversation" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-conversation" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-conversation" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-conversation" language="go" />
</TabItem>
</Tabs>
When the conversation grows — a new message arrives — just retain again with the full updated content and the same `document_id`. Hindsight will delete the previous version and reprocess from scratch, so memories always reflect the latest state of the conversation.
---
## Parameters
### content
The raw text to store. This is the only required field. Hindsight chunks the content, sends each chunk to the LLM for fact extraction, and stores the resulting structured facts — not the original text. A single `content` value can produce many memories depending on how much information it contains.
### timestamp
When the event described in the content actually occurred. Three forms are accepted:
| Value | Behaviour |
|-------|-----------|
| Omitted / `null` | Defaults to the current time at ingestion. |
| ISO 8601 string (e.g. `"2024-01-15T10:30:00Z"`) | Uses the provided datetime. |
| `"unset"` | Stores the content **without any timestamp**. Use this for timeless material such as reference documents, books, or fictional content where no real event time exists. |
The timestamp is injected into the LLM fact-extraction prompt so the model can resolve relative temporal references in the content — for example, if the content says "last Monday", the model uses the provided timestamp as the anchor to pin down the actual date. When `"unset"` is passed the prompt shows `Event Date: Unknown`, allowing the model to correctly return `N/A` for the `when` field of every extracted fact. Providing a real timestamp also enables temporal recall queries like "What happened last spring?" to work correctly.
### context
A short label describing the source or situation — for example `"team meeting"`, `"slack"`, or `"support ticket"`. It is injected directly into the LLM prompt, so it actively shapes how facts are extracted. The same sentence can mean something very different depending on context: "the project was terminated" in a `"performance review"` context versus a `"product roadmap"` context produces different memories.
Providing context consistently is one of the highest-leverage things you can do to improve memory quality.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-with-context" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-with-context" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-with-context" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-with-context" language="go" />
</TabItem>
</Tabs>
### metadata
Arbitrary key-value string pairs that provide context about this item. For example: `{"source": "slack", "channel": "engineering", "thread_id": "T123"}`. Metadata is included in the fact extraction prompt, so the LLM can use it as additional context when extracting facts — for instance, knowing the document title or source can improve accuracy. It is also stored on each memory unit and returned with every recalled memory, letting you do client-side filtering or static enrichment without extra lookups — for example, linking a memory back to its source URL, thread ID, or any application-specific identifier.
### document_id
A caller-supplied string that groups one or more items under a logical document. This field is the key to making retain **idempotent**.
When you provide a `document_id`, Hindsight upserts the document: if a document with that ID already exists in the bank, it and all its associated memories are deleted before the new content is processed and inserted. This means you can safely re-run retain on updated content — for example, a chat thread that grew since last time — without accumulating duplicate memories.
If you omit `document_id`, Hindsight assigns a random UUID per request, so re-ingesting the same content will create duplicate memories.
### entities
A list of entities you want to guarantee are recognized, merged with any entities the LLM extracts automatically. Each entry has a `text` field (the entity name) and an optional `type` (e.g., `"PERSON"`, `"ORG"`, `"CONCEPT"` — defaults to `"CONCEPT"` if omitted).
Use this when you know certain entities are important but the LLM might miss them or refer to them inconsistently across different parts of the content. Providing entities explicitly ensures they are always linked in the knowledge graph.
### tags and document_tags
Tags control **visibility scoping** — which memories are visible during recall. A memory is only returned if its tags intersect with the tags filter provided in the recall request. This makes tags useful when a single memory bank serves multiple users or sessions and each should only see their own memories.
Use consistent naming patterns to keep tag filtering predictable. Common conventions: `user:<id>` for per-user scoping, `session:<id>` for session isolation, `room:<id>` for chat rooms, `topic:<name>` for category filtering. The bank also exposes a list-tags endpoint that returns all tags with their memory counts, useful for UI autocomplete or wildcard expansion.
See [Recall API](./recall#tags) for filtering by tags during retrieval.
### observation_scopes
Controls which [observations](../observations) this memory contributes to during consolidation. Each scope runs an independent pass, creating or updating observations tagged with only that scope's tags.
:::info Scope isolation
During consolidation, Hindsight uses `all_strict` matching to find existing observations to update — only observations whose tags exactly match the current scope are considered. This keeps scopes isolated: a memory consolidated under `["student:alice"]` will never bleed into an observation tagged `["student:alice", "teacher:bob"]`.
:::
The examples below use a lesson transcript retained with `tags: ["student:alice", "teacher:bob", "session-id:s1"]`.
#### combined *(default)*
One consolidation pass using all tags together. The resulting observation is tagged with the full set.
- Observations created: `["student:alice", "teacher:bob", "session-id:s1"]`
- ✗ *"What does Alice struggle with across all her sessions?"* — no match, because no observation was ever built for `student:alice` alone
- ✗ *"How does Bob teach?"* — no match for `teacher:bob` alone
- ✓ *"What happened in session s1 with Alice and Bob?"* — exact match
**Use when** the memory is meaningful only as a whole and you never need to query any single tag in isolation.
#### per_tag
One consolidation pass per individual tag. Each tag gets its own isolated observation that grows with every new memory sharing that tag.
- Observations created: `["student:alice"]` · `["teacher:bob"]` · `["session-id:s1"]`
- ✓ *"What does Alice struggle with across all her sessions?"*
- ✓ *"How does Bob teach?"*
- ✓ *"What happened in session s1?"*
- ✗ *"How does Alice perform specifically with Bob?"* — no observation for the `["student:alice", "teacher:bob"]` combination
- ✗ *"How does Bob teach in online sessions?"* — no observation for `["teacher:bob", "session-id:s1"]`
**Use when** content involves multiple tags that each represent an independent subject — the most common choice for multi-party content like conversations, lessons, or support sessions.
#### all_combinations
One pass per subset of tags — singles, pairs, triples, and so on. For 3 tags that is 7 passes.
- Observations created: all `"per_tag"` scopes above, plus `["student:alice", "teacher:bob"]` · `["student:alice", "session-id:s1"]` · `["teacher:bob", "session-id:s1"]` · `["student:alice", "teacher:bob", "session-id:s1"]`
- ✓ All questions from `"per_tag"` above
- ✓ *"How does Alice perform specifically with Bob?"* — matched by `["student:alice", "teacher:bob"]`
**Use when** you need observations at every granularity — per tag, per pair, per group.
#### custom
Pass an explicit list of tag sets. Each inner list is one scope.
```json
[["student:alice"], ["teacher:bob"], ["teacher:bob", "session-id:s1"]]
```
- Observations created: exactly those three scopes — nothing more
- ✓ *"What does Alice struggle with?"*
- ✓ *"How does Bob teach?"*
- ✓ *"How does Bob teach in session s1 specifically?"*
- ✗ *"What happened in session s1 regardless of teacher?"* — `["session-id:s1"]` alone was not included
**Use when** you know exactly which combinations are meaningful and want to avoid unnecessary passes.
### Response
The synchronous retain response includes:
- `success` — whether the operation completed without errors
- `bank_id` — the memory bank that received the content
- `items_count` — number of items processed
- `async` — whether processing ran asynchronously
- `usage` — token usage for the LLM calls (`input_tokens`, `output_tokens`, `total_tokens`), only present for synchronous operations
---
## Batch Ingestion
Multiple items can be submitted in a single request. Batch ingestion is the recommended approach — it reduces network overhead and lets Hindsight optimize extraction across related content.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-batch" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-batch" language="go" />
</TabItem>
</Tabs>
---
## Files
Upload files directly — Hindsight converts them to text and extracts memories automatically. File processing always runs asynchronously and returns operation IDs for tracking.
**Supported formats:** PDF, DOCX, DOC, PPTX, PPT, XLSX, XLS, images (JPG, PNG, GIF, etc. — OCR), audio (MP3, WAV, FLAC, etc. — transcription), HTML, and plain text formats (TXT, MD, CSV, JSON, YAML, etc.)
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
The file retain endpoint always returns asynchronously. The response contains `operation_ids` — one per uploaded file — which you can poll via `GET /v1/default/banks/{bank_id}/operations` to track progress.
Upload up to 10 files per request (max 100 MB total). Each file becomes a separate document with optional per-file metadata:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
:::info File Storage
Uploaded files are stored server-side (PostgreSQL by default, or S3/GCS/Azure for production). Configure storage via `HINDSIGHT_API_FILE_STORAGE_TYPE`. See [Configuration](../configuration#file-processing) for details.
:::
---
## Async Ingestion
For large batches, use async ingestion to avoid blocking your application:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-async" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-async" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-async" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-async" language="go" />
</TabItem>
</Tabs>
When `async: true`, the call returns immediately with an `operation_id`. Processing runs in the background via the worker service. No `usage` metrics are returned for async operations.
### Cut Costs 50% with Provider Batch APIs
When using async retain, enable the provider Batch API to reduce LLM fact-extraction costs by 50%. OpenAI and Groq both offer this discount in exchange for a processing window of up to 24 hours — a trade-off that's typically invisible when retain already runs in the background.
```bash
export HINDSIGHT_API_RETAIN_BATCH_ENABLED=true
```
Hindsight submits fact extraction calls as a batch job to the provider, polls for completion, and processes results automatically. No changes to your API calls are needed.
:::note
Batch API cost savings require `async=true` in your retain request and a compatible provider (OpenAI or Groq).
:::
@@ -1,96 +0,0 @@
---
sidebar_position: 10
---
# Webhooks
Hindsight can notify your application in real-time when memory events occur by sending HTTP POST requests to a URL you configure.
## Delivery and Retries
Webhooks are registered per memory bank and fire automatically when matching events occur. Each delivery attempt is tracked, and failed deliveries are retried with exponential backoff:
| Attempt | Delay after failure |
|---------|---------------------|
| 1 | 5 seconds |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 5 hours |
| 6 | Permanent failure |
A delivery is considered failed if your endpoint returns a non-2xx status code or does not respond within the configured timeout (default 30 seconds). After 6 failed attempts, the delivery is marked as permanently failed and no further retries are made.
:::info At-least-once delivery
Webhook delivery tasks are queued in the same database transaction as the primary operation (e.g. the retain or consolidation write). This means if the server crashes after committing but before sending, the delivery task survives and will be retried. As a result, **your endpoint may receive the same event more than once** — use the `operation_id` field to deduplicate if needed.
:::
## Event Types
### `consolidation.completed`
Fired after Hindsight finishes consolidating new memories into observations for a bank.
**Payload:**
```json
{
"event": "consolidation.completed",
"bank_id": "my-bank",
"operation_id": "a1b2c3d4e5f6",
"status": "completed",
"timestamp": "2026-03-04T12:00:00Z",
"data": {
"observations_created": 3,
"observations_updated": 1,
"observations_deleted": null,
"error_message": null
}
}
```
**`data` fields:**
| Field | Type | Description |
|-------|------|-------------|
| `observations_created` | `integer \| null` | Number of new observations created |
| `observations_updated` | `integer \| null` | Number of existing observations updated |
| `observations_deleted` | `integer \| null` | Number of observations deleted |
| `error_message` | `string \| null` | Set when `status` is `"failed"` |
**`status` values:** `"completed"` or `"failed"`
---
### `retain.completed`
Fired once per document after a retain operation completes (both synchronous and asynchronous). When retaining a batch of N documents, N separate events are fired.
**Payload:**
```json
{
"event": "retain.completed",
"bank_id": "my-bank",
"operation_id": "a1b2c3d4e5f6",
"status": "completed",
"timestamp": "2026-03-04T12:00:01Z",
"data": {
"document_id": "doc-abc123",
"tags": ["meeting", "q1-2026"]
}
}
```
**`data` fields:**
| Field | Type | Description |
|-------|------|-------------|
| `document_id` | `string \| null` | The document ID if one was provided in the retain request |
| `tags` | `string[] \| null` | Document-level tags applied during retain |
**Notes:**
- For async retain (`async: true`), `operation_id` matches the `operation_id` returned by the retain API.
- For sync retain, `operation_id` is a generated identifier for tracing purposes.
- One event is fired per content item in the retain request.
File diff suppressed because it is too large Load Diff
@@ -1,149 +0,0 @@
---
sidebar_position: 7
---
# Development Guide
Guide to setting up a local development environment for contributing to Hindsight.
## Prerequisites
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) - Fast Python package manager
- Docker and Docker Compose
- An LLM API key (OpenAI, Groq, or Ollama)
## Local Development Setup
### 1. Clone the Repository
```bash
git clone https://github.com/vectorize-io/hindsight.git
cd hindsight
```
### 2. Install Dependencies
```bash
uv sync
```
### 3. Start PostgreSQL
Start only the database via Docker:
```bash
cd docker && docker-compose up -d postgres
```
### 4. Configure Environment
```bash
cp .env.example .env
```
Edit `.env` with your LLM API key:
```bash
# Database (connects to Docker postgres)
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
# LLM Provider (choose one)
HINDSIGHT_API_LLM_PROVIDER=groq
HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile
```
### 5. Start the API Server
```bash
./scripts/start-server.sh --env local
```
The server will be available at http://localhost:8888.
## Running Tests
```bash
# Run all tests
uv run pytest
# Run specific test file
uv run pytest tests/test_retrieval.py
# Run with verbose output
uv run pytest -v
```
## Code Generation
### Regenerate API Clients
When you modify the OpenAPI spec, regenerate the clients:
```bash
./scripts/generate-clients.sh
```
This generates:
- Python client in `hindsight-clients/python/`
- TypeScript client in `hindsight-clients/typescript/`
### Export OpenAPI Schema
```bash
./scripts/export-openapi.sh
```
## Project Structure
```
hindsight/
├── hindsight-api/ # Main API server
│ ├── hindsight_api/
│ │ ├── api/ # HTTP endpoints
│ │ ├── engine/ # Memory engine, retrieval, reasoning
│ │ └── web/ # Server entry point
│ └── tests/
├── hindsight-clients/ # Generated SDK clients
│ ├── python/
│ └── typescript/
├── hindsight-control-plane/ # Admin UI (Next.js)
├── docker/ # Docker Compose setup
└── scripts/ # Development scripts
```
## Contributing
1. Create a feature branch from `main`
2. Make your changes
3. Run tests: `uv run pytest`
4. Submit a pull request
## Troubleshooting
### Database Connection Issues
Ensure PostgreSQL is running:
```bash
docker-compose ps
```
Check database connectivity:
```bash
psql postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
```
### ML Model Download
On first run, Hindsight downloads embedding and reranking models. This may take a few minutes. Models are cached in `~/.cache/huggingface/`.
### Port Conflicts
If port 8888 is in use:
```bash
HINDSIGHT_API_PORT=8889 ./scripts/start-server.sh --env local
```
@@ -1,294 +0,0 @@
# Extensions
Extensions allow you to customize and extend Hindsight behavior without modifying core code. They enable multi-tenancy, custom authentication, additional HTTP endpoints, and operation hooks.
---
## Available Extensions
### TenantExtension
Handles multi-tenancy and API key authentication. Validates incoming requests and determines which PostgreSQL schema to use for database operations, enabling tenant isolation at the database level.
**Built-in: ApiKeyTenantExtension**
A simple implementation that validates API keys against an environment variable and uses the `public` schema for all authenticated requests.
```bash
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
HINDSIGHT_API_TENANT_API_KEY=your-secret-key
```
**Built-in: SupabaseTenantExtension**
Validates [Supabase](https://supabase.com) JWTs and provides multi-tenant memory isolation. Each authenticated user gets their own PostgreSQL schema (`{prefix}_{user_id}`), ensuring complete data separation. Performs local JWT verification using JWKS for optimal performance (no network call per request).
```bash
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.supabase_tenant:SupabaseTenantExtension
HINDSIGHT_API_TENANT_SUPABASE_URL=https://your-project.supabase.co
# Optional - only needed for legacy HS256 projects or health check
HINDSIGHT_API_TENANT_SUPABASE_SERVICE_KEY=your-service-role-key
```
See the [source code](https://github.com/vectorize-io/hindsight/blob/main/hindsight-api-slim/hindsight_api/extensions/builtin/supabase_tenant.py) for complete configuration options and implementation details.
For other multi-tenant setups with separate schemas per tenant (e.g., custom JWT-based auth), implement a custom `TenantExtension`.
---
### HttpExtension
Adds custom HTTP endpoints under the `/ext/` path prefix. Useful for adding domain-specific APIs that integrate with Hindsight's memory engine.
Provides two router methods:
- `get_router(memory)` — returns a FastAPI router mounted at `/ext/`
- `get_root_router(memory)` — returns a FastAPI router mounted at the application root (for well-known endpoints or other paths that must be at specific locations). Returns `None` by default.
**No built-in implementation** - implement your own to add custom endpoints.
```bash
HINDSIGHT_API_HTTP_EXTENSION=mypackage.ext:MyHttpExtension
```
---
### OperationValidatorExtension
Hooks into retain/recall/reflect operations for validation and monitoring. Use cases include:
- Rate limiting and quota enforcement
- Permission checks and content filtering
- Audit logging and usage tracking
- Custom metrics collection
**No built-in implementation** - implement your own based on your requirements.
```bash
HINDSIGHT_API_OPERATION_VALIDATOR_EXTENSION=mypackage.validators:MyValidator
```
---
### MCPExtension
Registers additional MCP (Model Context Protocol) tools on the Hindsight MCP server. Enables external packages to add custom tools without modifying core code.
**No built-in implementation** - implement your own to add custom MCP tools.
```bash
HINDSIGHT_API_MCP_EXTENSION=mypackage.mcp:MyMCPExtension
```
---
## Writing Custom Extensions
### Extension Basics
Extensions are Python classes loaded via environment variables:
```bash
HINDSIGHT_API_<TYPE>_EXTENSION=mypackage.module:MyExtensionClass
```
Configuration is passed via prefixed environment variables:
```bash
HINDSIGHT_API_<TYPE>_SOME_CONFIG=value
# Extension receives: {"some_config": "value"}
```
All extensions support lifecycle hooks:
- `on_startup()` - Called when the application starts
- `on_shutdown()` - Called when the application shuts down
Extensions have access to an `ExtensionContext` that provides:
- `run_migration(schema)` - Run database migrations for a schema
- `get_memory_engine()` - Get the MemoryEngine interface
### Example: Custom TenantExtension with JWT
```python
import jwt
from hindsight_api.extensions import TenantExtension, TenantContext, AuthenticationError
class JwtTenantExtension(TenantExtension):
def __init__(self, config: dict[str, str]):
super().__init__(config)
self.jwt_secret = config.get("jwt_secret")
if not self.jwt_secret:
raise ValueError("HINDSIGHT_API_TENANT_JWT_SECRET is required")
async def authenticate(self, context: RequestContext) -> TenantContext:
token = context.api_key
if not token:
# Optional headers dict is forwarded in HTTP/MCP error responses
raise AuthenticationError("Bearer token required")
try:
payload = jwt.decode(token, self.jwt_secret, algorithms=["HS256"])
tenant_id = payload.get("tenant_id")
if not tenant_id:
raise AuthenticationError("Missing tenant_id in token")
return TenantContext(schema_name=f"tenant_{tenant_id}")
except jwt.InvalidTokenError as e:
raise AuthenticationError(str(e))
```
`AuthenticationError` accepts an optional `headers` dict that is forwarded in both HTTP and MCP error responses. This is useful for returning custom headers like `WWW-Authenticate`:
```python
raise AuthenticationError(
"Authorization required",
headers={"WWW-Authenticate": 'Bearer realm="example"'},
)
```
### Example: Custom HttpExtension
```python
from fastapi import APIRouter
from hindsight_api.extensions import HttpExtension
class MyHttpExtension(HttpExtension):
def get_router(self, memory: MemoryEngine) -> APIRouter:
router = APIRouter()
@router.get("/hello")
async def hello():
return {"message": "Hello from extension!"}
@router.post("/custom/{bank_id}/action")
async def custom_action(bank_id: str):
# Access memory engine for database operations
pool = await memory._get_pool()
# ... custom logic
return {"status": "ok"}
return router
def get_root_router(self, memory: MemoryEngine) -> APIRouter | None:
"""Optional: mount routes at the application root (not under /ext/)."""
router = APIRouter()
@router.get("/.well-known/my-metadata")
async def metadata():
return {"version": "1.0"}
return router
```
Routes from `get_router` are available at `/ext/hello`, `/ext/custom/{bank_id}/action`, etc.
Routes from `get_root_router` are mounted at the app root (e.g., `/.well-known/my-metadata`).
### Example: Custom OperationValidatorExtension
```python
from hindsight_api.extensions import (
OperationValidatorExtension,
ValidationResult,
RetainContext,
RecallContext,
ReflectContext,
RetainResult,
)
class MyValidator(OperationValidatorExtension):
# Pre-operation validation (required)
async def validate_retain(self, ctx: RetainContext) -> ValidationResult:
# Implement your validation logic
return ValidationResult.accept()
# Or reject: return ValidationResult.reject("Reason")
async def validate_recall(self, ctx: RecallContext) -> ValidationResult:
return ValidationResult.accept()
async def validate_reflect(self, ctx: ReflectContext) -> ValidationResult:
return ValidationResult.accept()
# Post-operation hooks (optional)
async def on_retain_complete(self, result: RetainResult) -> None:
# Log usage, update metrics, send notifications, etc.
pass
```
### Example: Custom MCPExtension
```python
from mcp.server.fastmcp import FastMCP
from hindsight_api.extensions import MCPExtension
from hindsight_api.engine import MemoryEngine
class MyMCPExtension(MCPExtension):
async def register_tools(self, mcp: FastMCP, memory: MemoryEngine) -> None:
@mcp.tool()
async def custom_search(query: str) -> str:
"""Custom MCP tool for specialized search."""
# Access memory engine for operations
pool = await memory._get_pool()
# ... custom logic
return f"Results for: {query}"
```
---
## Deploying Custom Extensions
### With Docker
Mount your extension package as a volume and set the environment variable:
```yaml
# docker-compose.yml
services:
hindsight-api:
image: vectorize/hindsight-api:latest
volumes:
- ./my_extensions:/app/my_extensions
environment:
- HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
- HINDSIGHT_API_TENANT_JWT_SECRET=${JWT_SECRET}
- PYTHONPATH=/app
```
Or build a custom image with your extensions:
```dockerfile
FROM vectorize/hindsight-api:latest
COPY my_extensions /app/my_extensions
ENV PYTHONPATH=/app
```
### Bare Metal
Install your extension package in the same Python environment as Hindsight:
```bash
# Install Hindsight
pip install hindsight-api
# Install your extension package
pip install ./my-extensions
# or
pip install my-extensions-package
# Configure
export HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
export HINDSIGHT_API_TENANT_JWT_SECRET=your-secret
# Run
hindsight-api
```
---
## Contributing Extensions
Custom extensions that solve common use cases are welcome contributions to the Hindsight project. If you've built an extension for:
- Authentication providers (OAuth, SAML, API gateways)
- Rate limiting or quota management
- Audit logging integrations
- Metrics exporters (Datadog, New Relic, etc.)
- Custom HTTP endpoints for specific platforms
Consider contributing it to the `hindsight_api.extensions.builtin` package. Open an issue or pull request on [GitHub](https://github.com/vectorize-io/hindsight) to discuss your extension.
@@ -1,148 +0,0 @@
---
sidebar_position: 1
slug: /
---
import {ClientsGrid} from '@site/src/components/SupportedGrids';
# Overview
## Why Hindsight?
AI agents forget everything between sessions. Every conversation starts from zero—no context about who you are, what you've discussed, or what the assistant has learned. This isn't just an implementation detail; it fundamentally limits what AI Agents can do.
**The problem is harder than it looks:**
- **Simple vector search isn't enough** — "What did Alice do last spring?" requires temporal reasoning, not just semantic similarity
- **Facts get disconnected** — Knowing "Alice works at Google" and "Google is in Mountain View" should let you answer "Where does Alice work?" even if you never stored that directly
- **AI Agents need to consolidate knowledge** — A coding assistant that remembers "the user prefers functional programming" should consolidate this into an observation and weigh it when making recommendations
- **Context matters** — The same information means different things to different memory banks with different personalities
Hindsight solves these problems with a memory system designed specifically for AI agents.
## What Hindsight Does
```mermaid
graph LR
subgraph app["<b>Your Application</b>"]
Agent[AI Agent]
end
subgraph hindsight["<b>Hindsight</b>"]
API[API Server]
subgraph bank["<b>Memory Bank</b>"]
direction TB
MentalModels[Mental Models]
Observations[Observations]
MemEnt[Memories & Entities]
Chunks[Chunks]
Documents[Documents]
MentalModels --> Observations --> MemEnt --> Chunks --> Documents
end
end
Agent -->|retain| API
Agent -->|recall| API
Agent -->|reflect| API
API --> bank
```
**Your AI agent** stores information via `retain()`, searches with `recall()`, and reasons with `reflect()` — all interactions with its dedicated **memory bank**
## Key Components
### Memory Types
Hindsight organizes knowledge into a hierarchy of facts and consolidated knowledge:
| Type | What it stores | Example |
|------|----------------|---------|
| **Mental Model** | User-curated summaries for common queries | "Team communication best practices" |
| **Observation** | Automatically consolidated knowledge from facts | "User was a React enthusiast but has now switched to Vue" (captures history) |
| **World Fact** | Objective facts received | "Alice works at Google" |
| **Experience Fact** | Bank's own actions and interactions | "I recommended Python to Bob" |
During reflect, the agent checks sources in priority order: **Mental Models → Observations → Raw Facts**.
### Multi-Strategy Retrieval (TEMPR)
Four search strategies run in parallel:
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
| Strategy | Best for |
|----------|----------|
| **Semantic** | Conceptual similarity, paraphrasing |
| **Keyword (BM25)** | Names, technical terms, exact matches |
| **Graph** | Related entities, indirect connections |
| **Temporal** | "last spring", "in June", time ranges |
### Observation Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **observations** — synthesized knowledge representations that capture patterns and learnings:
- **Automatic synthesis**: New facts are analyzed and consolidated into existing or new observations
- **Evidence tracking**: Each observation tracks which facts support it
- **Continuous refinement**: Observations evolve as new evidence arrives
### Mission, Directives & Disposition
Memory banks can be configured to shape how the agent reasons during `reflect`:
| Configuration | Purpose | Example |
|---------------|---------|---------|
| **Mission** | Natural language identity for the bank | "I am a research assistant specializing in ML. I prefer simplicity over cutting-edge." |
| **Directives** | Hard rules the agent must follow | "Never recommend specific stocks", "Always cite sources" |
| **Disposition** | Soft traits that influence reasoning style | Skepticism, literalism, empathy (1-5 scale) |
The **mission** tells Hindsight what knowledge to prioritize and provides context for reasoning. **Directives** are guardrails and compliance rules that must never be violated. **Disposition traits** subtly influence interpretation style.
These settings only affect the `reflect` operation, not `recall`.
## Clients & Languages
<ClientsGrid />
## Integrations
Browse all supported integrations in the [Integrations Hub](/integrations).
## Next Steps
### Getting Started
- [**Quick Start**](/developer/api/quickstart) — Install and get up and running in 60 seconds
- [**RAG vs Hindsight**](/developer/rag-vs-hindsight) — See how Hindsight differs from traditional RAG with real examples
### Core Concepts
- [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts
- [**Recall**](/developer/retrieval) — How TEMPR's 4-way search retrieves memories
- [**Reflect**](/developer/reflect) — How mission, directives, and disposition shape reasoning
### API Methods
- [**Retain**](/developer/api/retain) — Store information in memory banks
- [**Recall**](/developer/api/recall) — Search and retrieve memories
- [**Reflect**](/developer/api/reflect) — Agentic reasoning with memory
- [**Mental Models**](/developer/api/mental-models) — User-curated summaries for common queries
- [**Memory Banks**](/developer/api/memory-banks) — Configure mission, directives, and disposition
- [**Documents**](/developer/api/documents) — Manage document sources
- [**Operations**](/developer/api/operations) — Monitor async tasks
### Deployment
- [**Server Setup**](/developer/installation) — Deploy with Docker Compose, Helm, or pip
@@ -1,323 +0,0 @@
# Installation
Hindsight can be deployed in several ways depending on your infrastructure and requirements.
:::tip Don't want to manage infrastructure?
**[Hindsight Cloud](https://ui.hindsight.vectorize.io/signup)** is a fully managed service that handles all infrastructure, scaling, and maintenance — [sign up here](https://ui.hindsight.vectorize.io/signup).
:::
## Supported Platforms
Hindsight runs on **Linux**, **macOS**, and **Windows**:
| Platform | Docker | Bare Metal (pip) | Embedded DB (pg0) | Notes |
|----------|--------|------------------|--------------------|-------|
| **Linux** (x86_64, ARM64) | ✅ | ✅ | ✅ | Fully supported, recommended for production |
| **macOS** (Apple Silicon, Intel) | ✅ | ✅ | ✅ | Fully supported |
| **Windows** (x86_64) | ✅ | ✅ | ✅ | Fully supported — see [Windows setup](#windows) for external PostgreSQL option |
All platforms support the embedded database (pg0) for development. On Windows, you can also use an external PostgreSQL installation — see the [Windows](#windows) section for a step-by-step guide.
---
## Prerequisites
### PostgreSQL
Hindsight requires PostgreSQL 14+ with a vector extension for similarity search. The supported extensions are:
- **pgvector** (default)
- **pgvectorscale**
- **vchord**
Configure which one to use with `HINDSIGHT_API_VECTOR_EXTENSION`. See [Configuration](./configuration) for details.
**By default**, Hindsight uses **pg0** — an embedded PostgreSQL that runs locally on your machine. This is convenient for development but **not recommended for production**.
**For production**, use an external PostgreSQL with one of the supported vector extensions:
- **Supabase** — Managed PostgreSQL with pgvector built-in
- **Neon** — Serverless PostgreSQL with pgvector
- **Azure Database for PostgreSQL** — With pgvector and pgvectorscale support
- **AWS RDS** / **Cloud SQL** — With pgvector extension enabled
- **Self-hosted** — PostgreSQL 14+ with your preferred vector extension
### LLM Provider
You need an LLM API key for fact extraction, entity resolution, and answer generation. See [Models](./models) for supported providers, model recommendations, and configuration.
---
## Docker
**Best for**: Quick start, development, small deployments
Run everything in one container with embedded PostgreSQL:
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API Server**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
### Docker Image Variants
| Variant | Size (AMD64) | Size (ARM64) | When to use |
|---------|--------------|--------------|-------------|
| **Full** (`latest`) | ~9 GB | ~3.7 GB | Default. Works out of the box with no external services except the LLM. |
| **Slim** (`slim`) | ~500 MB | ~500 MB | Use when you already rely on external services for embeddings and reranking (OpenAI, Cohere, TEI). Significantly smaller image, faster deploys. Requires [external providers](./configuration#embeddings). |
The slim image corresponds to the [`hindsight-api-slim`](#bare-metal-pip) pip package. See [Configuration](./configuration#embeddings) for external provider options.
### Available Tags
```bash
# Standalone (API + Control Plane)
ghcr.io/vectorize-io/hindsight:latest # Full, latest release
ghcr.io/vectorize-io/hindsight:latest-slim # Slim, latest release
ghcr.io/vectorize-io/hindsight:0.4.9 # Full, specific version
ghcr.io/vectorize-io/hindsight:0.4.9-slim # Slim, specific version
# API only
ghcr.io/vectorize-io/hindsight-api:latest
ghcr.io/vectorize-io/hindsight-api:latest-slim
# Control Plane only
ghcr.io/vectorize-io/hindsight-control-plane:latest
```
---
## Helm / Kubernetes
**Best for**: Production deployments, auto-scaling, cloud environments
```bash
# Install with built-in PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=true
# Or use external PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=false \
--set api.database.url=postgresql://user:[email protected]:5432/hindsight
# Install a specific version
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
# Upgrade to latest
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
```
**Requirements**:
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
- Helm 3.8+
### Distributed Workers
For high-throughput deployments, enable dedicated worker pods to scale task processing independently:
```bash
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set worker.enabled=true \
--set worker.replicaCount=3
```
See [Services - Worker Service](./services#worker-service) for configuration details and architecture.
See the [Helm chart values.yaml](https://github.com/vectorize-io/hindsight/tree/main/helm/hindsight/values.yaml) for all chart options.
---
## Bare Metal (pip)
**Best for**: Running Hindsight as a standalone service on a host machine.
### Install
```bash
pip install hindsight-api # Full — works out of the box
pip install hindsight-api-slim # Slim — requires external services for embeddings, reranking, and the database
```
When using `hindsight-api-slim`, you must configure external providers for all model operations. See [Configuration](./configuration#embeddings) for details.
### Run with Embedded Database
For development and testing, Hindsight can run with an embedded PostgreSQL (pg0):
```bash
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
This creates a database in `~/.hindsight/data/` and starts the API on http://localhost:8888.
### Run with External PostgreSQL
For production, connect to your own PostgreSQL instance:
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
**Note**: The database must exist and have pgvector enabled (`CREATE EXTENSION vector;`).
### CLI Options
```bash
hindsight-api --port 9000 # Custom port (default: 8888)
hindsight-api --host 127.0.0.1 # Bind to localhost only
hindsight-api --workers 4 # Multiple worker processes
hindsight-api --log-level debug # Verbose logging
```
### Control Plane
The Control Plane (Web UI) can be run standalone using npx:
```bash
npx @vectorize-io/hindsight-control-plane --api-url http://localhost:8888
```
This connects to your running API server and provides a visual interface for managing memory banks, exploring entities, and testing queries.
#### Options
| Option | Environment Variable | Default | Description |
|--------|---------------------|---------|-------------|
| `-p, --port` | `PORT` | 9999 | Port to listen on |
| `-H, --hostname` | `HOSTNAME` | 0.0.0.0 | Hostname to bind to |
| `-a, --api-url` | `HINDSIGHT_CP_DATAPLANE_API_URL` | http://localhost:8888 | Hindsight API URL |
#### Examples
```bash
# Run on custom port
npx @vectorize-io/hindsight-control-plane --port 9999 --api-url http://localhost:8888
# Using environment variables
export HINDSIGHT_CP_DATAPLANE_API_URL=http://api.example.com
npx @vectorize-io/hindsight-control-plane
# Production deployment
PORT=80 HINDSIGHT_CP_DATAPLANE_API_URL=https://api.hindsight.io npx @vectorize-io/hindsight-control-plane
```
---
## Windows
**Best for**: Running Hindsight natively on Windows without Docker
Hindsight works on Windows with the embedded database (pg0) out of the box — just install and run:
```powershell
pip install hindsight-api
set HINDSIGHT_API_LLM_PROVIDER=openai
set HINDSIGHT_API_LLM_API_KEY=sk-xxx
set HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
hindsight-api
```
### Using External PostgreSQL (optional)
If you prefer to use your own PostgreSQL instance instead of the embedded database:
```powershell
# Install PostgreSQL
winget install PostgreSQL.PostgreSQL.17
# Build pgvector (requires Visual Studio Build Tools)
git clone https://github.com/pgvector/pgvector.git
cd pgvector
# Open "x64 Native Tools Command Prompt for VS" and run:
set PGROOT=C:\Program Files\PostgreSQL\17
nmake /F Makefile.win
nmake /F Makefile.win install
# Create the database and enable the vector extension
psql -U postgres -c "CREATE DATABASE hindsight;"
psql -U postgres -d hindsight -c "CREATE EXTENSION vector;"
```
Then run Hindsight pointing to your database:
```powershell
pip install hindsight-api
set HINDSIGHT_API_DATABASE_URL=postgresql://postgres@localhost:5432/hindsight
set HINDSIGHT_API_LLM_PROVIDER=openai
set HINDSIGHT_API_LLM_API_KEY=sk-xxx
set HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
hindsight-api
```
- **API Server**: http://localhost:8888
:::tip
You can also use the slim package (`pip install hindsight-api-slim`) if you configure external providers for embeddings and reranking. See [Configuration](./configuration#embeddings) for details.
:::
---
## Embedded in a Python Application
**Best for**: Using Hindsight programmatically from Python without running a separate server process.
```bash
pip install hindsight-all # Full — works out of the box
pip install hindsight-all-slim # Slim — requires external services for embeddings, reranking, and the database
```
`hindsight-all` supports two modes of embedding:
**In-process** (`HindsightServer`): the server runs in a background thread inside your application. Best when you want the tightest integration and are already managing your own process lifecycle.
```python
from hindsight import HindsightServer, HindsightClient
with HindsightServer(llm_provider="openai", llm_api_key="sk-xxx") as server:
client = HindsightClient(base_url=server.url)
client.retain(bank_id="alice", content="Alice prefers concise answers.")
results = client.recall(bank_id="alice", query="How should I respond to Alice?")
```
**Managed subprocess** (`HindsightEmbedded`): the server runs as a background daemon process, shared across multiple Python processes or sessions. The daemon starts on first use and shuts down automatically after an idle timeout.
```python
from hindsight import HindsightEmbedded
client = HindsightEmbedded(llm_provider="openai", llm_api_key="sk-xxx")
client.retain(bank_id="alice", content="Alice prefers concise answers.")
results = client.recall(bank_id="alice", query="How should I respond to Alice?")
```
See the [Python SDK](../sdks/python.md) for the full API reference.
---
## Next Steps
- [Configuration](./configuration.md) — Environment variables and settings
- [Models](./models.mdx) — ML models and providers
- [Monitoring](./monitoring.md) — Metrics and observability
@@ -1,502 +0,0 @@
---
sidebar_position: 5
---
# MCP Server
Hindsight includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that allows AI assistants to store and retrieve memories directly.
## Access
The MCP server is **enabled by default** and mounted at `/mcp` on the API server. Each memory bank has its own MCP endpoint:
```
http://localhost:8888/mcp/{bank_id}/
```
For example, to connect to the memory bank `alice`:
```
http://localhost:8888/mcp/alice/
```
To disable the MCP server, set the environment variable:
```bash
export HINDSIGHT_API_MCP_ENABLED=false
```
## Authentication
By default, the MCP endpoint is **open** (no authentication required).
To enable authentication, configure the API key tenant extension:
```bash
export HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
export HINDSIGHT_API_TENANT_API_KEY=your-secret-key
```
When authentication is enabled, include your API key in the `Authorization` header:
### Claude Code
```bash
claude mcp add --transport http hindsight http://localhost:8888/mcp \
--header "Authorization: Bearer your-secret-key" \
--header "X-Bank-Id: my-bank"
```
### Claude Desktop
Add to `~/.claude_desktop_config.json`:
```json
{
"mcpServers": {
"hindsight": {
"url": "http://localhost:8888/mcp",
"headers": {
"Authorization": "Bearer your-secret-key",
"X-Bank-Id": "my-bank"
}
}
}
}
```
### Direct HTTP Request
```bash
curl -X POST http://localhost:8888/mcp \
-H "Authorization: Bearer your-secret-key" \
-H "X-Bank-Id: my-bank" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
```
If the key is missing or invalid, requests will receive a `401 Unauthorized` response.
## Bank Selection
The memory bank is resolved in this priority order:
1. **URL path** (highest priority): `http://localhost:8888/mcp/my-bank/`
2. **X-Bank-Id header**: `--header "X-Bank-Id: my-bank"`
3. **Default**: Uses `HINDSIGHT_MCP_BANK_ID` env var (default: "default")
## Per-Bank Endpoints
Unlike traditional MCP servers where tools require explicit identifiers, Hindsight uses **per-bank endpoints**. The `bank_id` is part of the URL path, so tools don't need to specify which bank to use—it's implicit from the connection.
This design:
- **Simplifies tool usage** — no need to pass `bank_id` with every call
- **Enforces isolation** — each MCP connection is scoped to a single bank
- **Enables multi-tenant setups** — connect different users to different endpoints
## Two Modes
The MCP server operates in two modes depending on the URL:
| Mode | URL | Tools | bank_id |
|------|-----|-------|---------|
| **Single-bank** | `/mcp/{bank_id}/` | 26 tools (memory, mental models, directives, documents, operations, tags, bank management) | Implicit from URL |
| **Multi-bank** | `/mcp/` | All 29 tools including `list_banks`, `create_bank`, `get_bank_stats` | Explicit `bank_id` parameter on each tool |
**Single-bank mode** (recommended) scopes all operations to the bank in the URL. Tools don't expose a `bank_id` parameter.
**Multi-bank mode** exposes all tools with an optional `bank_id` parameter, plus bank management tools (`list_banks`, `create_bank`, `get_bank_stats`).
---
## Available Tools
### retain
Store information to long-term memory.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | Yes | The fact or memory to store |
| `context` | string | No | Category for the memory (default: `general`) |
| `timestamp` | string | No | ISO 8601 timestamp for when the event occurred |
| `tags` | list[string] | No | Tags for organizing and filtering this memory |
| `metadata` | object | No | Key-value metadata to attach (e.g., `{"source": "slack"}`) |
| `document_id` | string | No | Associate this memory with an existing document |
**Example:**
```json
{
"name": "retain",
"arguments": {
"content": "User prefers Python over JavaScript for backend development",
"context": "programming_preferences",
"tags": ["user:alice", "preferences"]
}
}
```
**When to use:**
- User shares personal facts, preferences, or interests
- Important events or milestones are mentioned
- Decisions, opinions, or goals are stated
- Work context or project details are discussed
---
### recall
Search memories to provide personalized responses.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | Natural language search query |
| `max_tokens` | integer | No | Maximum tokens to return (default: 4096) |
| `budget` | string | No | Search thoroughness: `low`, `mid`, or `high` (default: `high`) |
| `types` | list[string] | No | Filter by fact type: `world`, `experience`, `opinion`. Defaults to all |
| `tags` | list[string] | No | Filter memories by tags |
| `tags_match` | string | No | Tag matching mode: `any` (default) or `all` |
| `query_timestamp` | string | No | ISO 8601 timestamp — recall as if asking at this point in time |
**Example:**
```json
{
"name": "recall",
"arguments": {
"query": "What are the user's programming language preferences?",
"tags": ["preferences"],
"budget": "high"
}
}
```
**When to use:**
- Start of conversation to recall relevant context
- Before making recommendations
- When user asks about something they may have mentioned before
- To provide continuity across conversations
---
### reflect
Generate thoughtful analysis by synthesizing stored memories with the bank's personality.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | The question or topic to reflect on |
| `context` | string | No | Optional context about why this reflection is needed |
| `budget` | string | No | Search budget: `low`, `mid`, or `high` (default: `low`) |
| `max_tokens` | integer | No | Maximum tokens in the response (default: 4096) |
| `response_schema` | object | No | JSON Schema for structured output. When provided, the response includes a `structured_output` field |
| `tags` | list[string] | No | Filter memories by tags before reflecting |
| `tags_match` | string | No | Tag matching mode: `any` (default) or `all` |
**Example:**
```json
{
"name": "reflect",
"arguments": {
"query": "Based on my past decisions, what architectural style do I prefer?",
"budget": "mid",
"tags": ["architecture"]
}
}
```
**When to use:**
- When reasoned analysis is needed, not just fact retrieval
- Questions like "What should I do?" rather than "What did I say?"
- Synthesizing patterns across multiple memories
---
### create_mental_model
Create a mental model — a living document that stays current with your memories. Mental models are pre-computed reflections that get automatically refreshed as new memories are stored.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query used to generate and refresh the model |
| `mental_model_id` | string | No | Custom ID (alphanumeric lowercase with hyphens). Auto-generated if not provided |
| `tags` | list[string] | No | Tags for organizing and filtering models |
| `max_tokens` | integer | No | Maximum tokens for model content (default: 2048) |
| `trigger_refresh_after_consolidation` | boolean | No | Auto-refresh this model after memory consolidation (default: `false`) |
**Example:**
```json
{
"name": "create_mental_model",
"arguments": {
"name": "Team Directory",
"source_query": "Who works here and what do they do?",
"tags": ["team", "people"]
}
}
```
Content generation runs asynchronously. The response includes an `operation_id` to track progress.
---
### list_mental_models
List all mental models in a bank, optionally filtered by tags.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | list[string] | No | Filter models by tags |
---
### get_mental_model
Retrieve a specific mental model by ID, including its full content.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to retrieve |
---
### update_mental_model
Update a mental model's metadata or settings.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to update |
| `name` | string | No | New name |
| `source_query` | string | No | New source query |
| `tags` | list[string] | No | New tags |
| `max_tokens` | integer | No | New max tokens |
| `trigger_refresh_after_consolidation` | boolean | No | Auto-refresh after consolidation. Only set when you want to change this setting |
---
### delete_mental_model
Permanently delete a mental model.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to delete |
---
### refresh_mental_model
Re-generate a mental model's content from the latest memories. Runs asynchronously.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to refresh |
---
### list_banks (multi-bank mode only)
List all available memory banks.
---
### create_bank (multi-bank mode only)
Create a new memory bank or retrieve an existing one.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bank_id` | string | Yes | The ID for the new bank |
| `name` | string | No | Human-friendly name for the bank |
| `mission` | string | No | Mission describing who the agent is and what they're trying to accomplish |
---
### list_directives
List all directives in a bank. Directives are instructions that guide how the memory system processes and responds to queries.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | list[string] | No | Filter directives by tags |
| `active_only` | boolean | No | Only return active directives (default: `true`) |
---
### create_directive
Create a new directive in a bank.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the directive |
| `content` | string | Yes | The directive content/instruction |
| `priority` | integer | No | Priority level (higher = more important) |
| `is_active` | boolean | No | Whether the directive is active (default: `true`) |
| `tags` | list[string] | No | Tags for organizing directives |
---
### delete_directive
Delete a directive by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `directive_id` | string | Yes | The ID of the directive to delete |
---
### list_memories
Browse stored memories with optional filtering and pagination.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Filter by fact type: `world`, `experience`, or `opinion` |
| `q` | string | No | Search query to filter memories |
| `limit` | integer | No | Maximum number of results (default: 100) |
| `offset` | integer | No | Number of results to skip for pagination (default: 0) |
---
### get_memory
Retrieve a specific memory by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `memory_id` | string | Yes | The ID of the memory to retrieve |
---
### delete_memory
Permanently delete a specific memory.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `memory_id` | string | Yes | The ID of the memory to delete |
---
### list_documents
List documents that have been ingested into the memory bank.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | No | Search query to filter documents |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_document
Retrieve a specific document by ID, including its metadata.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `document_id` | string | Yes | The ID of the document to retrieve |
---
### delete_document
Delete a document and all memories linked to it.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `document_id` | string | Yes | The ID of the document to delete |
---
### list_operations
List async operations (retain processing, mental model refresh, etc.) with optional status filtering.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by status: `pending`, `running`, `completed`, `failed`, `cancelled` |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_operation
Get the status and details of an async operation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation_id` | string | Yes | The ID of the operation to check |
---
### cancel_operation
Cancel a pending or running async operation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation_id` | string | Yes | The ID of the operation to cancel |
---
### list_tags
List all unique tags used in a bank, optionally filtered by pattern.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | No | Glob pattern to filter tags (e.g., `project:*`) |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_bank
Get information about a memory bank, including its name, mission, and disposition.
---
### get_bank_stats (multi-bank mode only)
Get statistics for a memory bank (node/link counts).
---
### update_bank
Update a memory bank's metadata.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | New human-friendly name for the bank |
| `mission` | string | No | New mission describing who the agent is and what they're trying to accomplish |
---
### delete_bank
Permanently delete a memory bank and all its data (memories, documents, entities, mental models).
---
### clear_memories
Clear all memories from a bank without deleting the bank itself. Optionally filter by fact type to only clear specific kinds of memories.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Fact type to clear: `world`, `experience`, or `opinion`. If not specified, clears all |
---
## Integration with AI Assistants
The MCP server can be used with any MCP-compatible AI assistant. See the [Authentication](#authentication) section above for Claude Code and Claude Desktop configuration examples.
Each user can have their own configuration pointing to their personal memory bank using either:
- A bank-specific URL path like `/mcp/alice/` (recommended)
- The `X-Bank-Id` header
@@ -1,525 +0,0 @@
import {LLMProvidersGrid} from '@site/src/components/SupportedGrids';
# Models
Hindsight uses several machine learning models for different tasks.
## Overview
- **LLM** — Fact extraction, reasoning, and generation. Provider-specific, fully configurable.
- **Embedding** — Vector representations for semantic search. Default: `BAAI/bge-small-en-v1.5`.
- **Cross-Encoder** — Reranking search results. Default: `cross-encoder/ms-marco-MiniLM-L-6-v2`.
Embedding and cross-encoder models are downloaded automatically from HuggingFace on first run.
---
## LLM
Used for fact extraction, entity resolution, mental model consolidation, and answer synthesis.
**Supported providers:**
<LLMProvidersGrid />
Also supports **any OpenAI-compatible API** (e.g., Azure OpenAI, Together AI, Fireworks) and **100+ providers via LiteLLM** (e.g., AWS Bedrock, Azure OpenAI, Together AI).
:::tip OpenAI-Compatible Providers
Hindsight works with any provider that exposes an OpenAI-compatible API (e.g., Azure OpenAI). Simply set `HINDSIGHT_API_LLM_PROVIDER=openai` and configure `HINDSIGHT_API_LLM_BASE_URL` to point to your provider's endpoint.
See [Configuration](./configuration#llm-provider) for setup examples.
:::
:::tip AWS Bedrock
Set `HINDSIGHT_API_LLM_PROVIDER=bedrock` to use AWS Bedrock models directly. Model names use Bedrock model IDs (e.g., `us.amazon.nova-2-lite-v1:0`). No API key is required — authentication uses AWS credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION_NAME`) or IAM roles.
See [Configuration](./configuration#llm-provider) for setup examples.
:::
:::tip LiteLLM Provider (Azure, Together AI, and more)
Set `HINDSIGHT_API_LLM_PROVIDER=litellm` to use any model supported by [LiteLLM](https://docs.litellm.ai/docs/providers), including **Azure OpenAI**, **Together AI**, **Fireworks AI**, and many more. Model names use LiteLLM's provider prefix format (e.g., `azure/gpt-4o`).
See [Configuration](./configuration#llm-provider) for setup examples.
:::
### Benchmarks
Not sure which model to use? The **[Model Leaderboard](https://benchmarks.hindsight.vectorize.io/)** benchmarks models across accuracy, speed, cost, and reliability for retain, reflect, and observation consolidation so you can pick the right trade-off for your use case.
[![Model Leaderboard](/img/leaderboard.png)](https://benchmarks.hindsight.vectorize.io/)
### Tested Models
The following models have been tested and verified to work correctly with Hindsight:
| Provider | Model |
|----------|-------|
| **OpenAI** | `gpt-5.2` |
| **OpenAI** | `gpt-5` |
| **OpenAI** | `gpt-5-mini` |
| **OpenAI** | `gpt-5-nano` |
| **OpenAI** | `gpt-4.1-mini` |
| **OpenAI** | `gpt-4.1-nano` |
| **OpenAI** | `gpt-4o-mini` |
| **Anthropic** | `claude-sonnet-4-20250514` |
| **Anthropic** | `claude-3-5-sonnet-20241022` |
| **Gemini** | `gemini-3-pro-preview` |
| **Gemini** | `gemini-2.5-flash` |
| **Gemini** | `gemini-2.5-flash-lite` |
| **Groq** | `openai/gpt-oss-120b` |
| **Groq** | `openai/gpt-oss-20b` |
### Provider Default Models
Each provider has a recommended default model that's used when `HINDSIGHT_API_LLM_MODEL` is not explicitly set. This makes configuration simpler - just specify the provider and get a sensible default:
| Provider | Default Model |
|----------|--------------|
| `openai` | `gpt-4o-mini` |
| `anthropic` | `claude-haiku-4-5-20251001` |
| `gemini` | `gemini-2.5-flash` |
| `groq` | `openai/gpt-oss-120b` |
| `minimax` | `MiniMax-M2.7` |
| `ollama` | `gemma3:12b` |
| `lmstudio` | `local-model` |
| `vertexai` | `gemini-2.0-flash-001` |
| `openai-codex` | `gpt-5.2-codex` |
| `claude-code` | `claude-sonnet-4-5-20250929` |
| `bedrock` | `us.amazon.nova-2-lite-v1:0` |
| `volcano` | `doubao-pro-32k` |
| `litellm` | `gpt-4o-mini` |
**Example:** Setting just the provider uses its default model:
```bash
# Uses claude-haiku-4-5-20251001 automatically
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
```
You can override the default by explicitly setting `HINDSIGHT_API_LLM_MODEL`:
```bash
# Override to use Sonnet instead
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-5-20250929
```
This also applies to per-operation overrides:
```bash
# Global: OpenAI gpt-4o-mini (default)
export HINDSIGHT_API_LLM_PROVIDER=openai
# Retain: Anthropic claude-haiku-4-5-20251001 (default)
export HINDSIGHT_API_RETAIN_LLM_PROVIDER=anthropic
```
### Using Other Models
Other LLM models not listed above may work with Hindsight, but they must support **at least 65,000 output tokens** to ensure reliable fact extraction. If you need support for a specific model that doesn't meet this requirement, please [open an issue](https://github.com/hindsight-ai/hindsight/issues) to request an exception.
:::tip Models with Limited Output Tokens
If your model only supports 32k or fewer output tokens (e.g., some older models), you can reduce the retain completion token limit:
```bash
# For models that support 32k output tokens
export HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS=32000
# For models that support 16k output tokens
export HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS=16000
```
**Important:** `HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS` must be greater than `HINDSIGHT_API_RETAIN_CHUNK_SIZE` (default: 3000). The system will validate this on startup and provide an error message if the configuration is invalid.
:::
### Configuration
```bash
# Groq (recommended)
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
# OpenAI
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o
# Gemini
export HINDSIGHT_API_LLM_PROVIDER=gemini
export HINDSIGHT_API_LLM_API_KEY=xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
# Anthropic
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Ollama (local)
export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
export HINDSIGHT_API_LLM_MODEL=llama3
# LM Studio (local)
export HINDSIGHT_API_LLM_PROVIDER=lmstudio
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
export HINDSIGHT_API_LLM_MODEL=your-local-model
# MiniMax (1M context window)
export HINDSIGHT_API_LLM_PROVIDER=minimax
export HINDSIGHT_API_LLM_API_KEY=your-minimax-api-key
export HINDSIGHT_API_LLM_MODEL=MiniMax-M2.7
# Vertex AI (Google Cloud)
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-gcp-project-id
# Optional: region (default: us-central1)
# export HINDSIGHT_API_LLM_VERTEXAI_REGION=us-central1
# Optional: service account key (otherwise uses ADC)
# export HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY=/path/to/key.json
```
**Note:** The LLM is the primary bottleneck for retain operations. See [Performance](./performance) for optimization strategies.
---
### OpenAI Codex Setup (ChatGPT Plus/Pro)
Use your ChatGPT Plus or Pro subscription for Hindsight without separate OpenAI Platform API costs.
**Prerequisites:**
- Active ChatGPT Plus or Pro subscription
- Node.js/npm installed (for Codex CLI)
**Setup Steps:**
1. **Install Codex CLI:**
```bash
npm install -g @openai/codex
```
2. **Login with ChatGPT credentials:**
```bash
codex auth login
```
This opens a browser window to authenticate with your ChatGPT account and saves OAuth tokens to `~/.codex/auth.json`.
3. **Verify authentication:**
```bash
ls ~/.codex/auth.json # Should show the auth file exists
```
4. **Configure Hindsight:**
```bash
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
# export HINDSIGHT_API_LLM_MODEL=gpt-5.1-codex # defaults to gpt-5.2-codex
# No API key needed - reads from ~/.codex/auth.json automatically
```
5. **Start Hindsight:**
```bash
hindsight-api
```
You can use any model supported by OpenAI Codex CLI
**Important Notes:**
- OAuth tokens are stored in `~/.codex/auth.json`
- Tokens refresh automatically when needed
- Usage is billed to your ChatGPT subscription (not separate API costs)
- For personal development use only (see ChatGPT Terms of Service)
---
### Claude Code Setup (Claude Pro/Max)
Use your Claude Pro or Max subscription for Hindsight without separate Anthropic API costs.
:::warning Terms of Service Notice
This integration uses the Claude Agent SDK with your personal Claude Pro/Max subscription
credentials. You must be logged into Claude Code on your own machine before using this provider.
**Please be aware:**
- Anthropic's [Agent SDK documentation](https://docs.claude.com/en/api/agent-sdk/overview)
states that third-party developers should not offer claude.ai login or rate limits for
their products. Hindsight does **not** perform any login on your behalf — it uses
credentials you've already authenticated via `claude auth login`.
- In January 2026, Anthropic [enforced restrictions](https://paddo.dev/blog/anthropic-walled-garden-crackdown/)
against third-party tools using Claude subscription OAuth tokens. Those restrictions
targeted tools that **spoofed the Claude Code client identity** — Hindsight uses the
official Claude Agent SDK instead.
- This provider is intended for **local, personal development use only**. Do not use it
in production deployments or shared environments.
- Anthropic's terms may change. If you want guaranteed compliance, use the `anthropic`
provider with an API key instead.
- Usage counts against your Claude Pro/Max subscription limits.
For production or team use, we recommend using `HINDSIGHT_API_LLM_PROVIDER=anthropic` with
an API key from the [Anthropic Console](https://console.anthropic.com/).
:::
**Prerequisites:**
- Active Claude Pro or Max subscription
- Claude Code CLI installed
**Setup Steps:**
1. **Install Claude Code CLI:**
```bash
npm install -g @anthropics/claude-code
# Or via Homebrew
brew install anthropics/claude-code/claude-code
```
2. **Login with Claude credentials:**
```bash
claude auth login
```
This opens a browser window to authenticate with your Claude account. Authentication is automatically managed by the Claude Agent SDK.
3. **Verify authentication:**
```bash
claude --version
# Should show version without errors
```
4. **Configure Hindsight:**
```bash
export HINDSIGHT_API_LLM_PROVIDER=claude-code
# No API key needed - uses claude auth login credentials
```
5. **Start Hindsight:**
```bash
hindsight-api
```
You can use any model supported by Claude Code CLI.
**Important Notes:**
- Authentication handled by Claude Agent SDK (uses bundled CLI)
- Credentials managed securely by Claude Code
- Usage billed to your Claude subscription (not separate API costs)
- For personal development use only (see Claude Terms of Service)
---
### Vertex AI Setup (Google Cloud)
Google Cloud's Vertex AI provides access to Gemini models via the native Google GenAI SDK.
**Prerequisites:**
- GCP project with Vertex AI API enabled
- IAM role `roles/aiplatform.user` for your credentials
**Environment Variables:**
| Variable | Description | Required |
|----------|-------------|----------|
| `HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID` | Your GCP project ID | Yes |
| `HINDSIGHT_API_LLM_VERTEXAI_REGION` | GCP region (e.g., `us-central1`) | No (default: `us-central1`) |
| `HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY` | Path to service account JSON key file | No (uses ADC if not set) |
**Authentication Methods:**
1. **Application Default Credentials (ADC)** - Recommended for development
```bash
# Setup ADC
gcloud auth application-default login
# Configure Hindsight
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-project-id
```
2. **Service Account Key** - Recommended for production
```bash
# Create service account and download key
gcloud iam service-accounts create hindsight-api
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:[email protected]" \
--role="roles/aiplatform.user"
gcloud iam service-accounts keys create key.json \
--iam-account=hindsight-api@your-project-id.iam.gserviceaccount.com
# Configure Hindsight
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-project-id
export HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY=/path/to/key.json
```
**Notes:**
- Model names can optionally include the `google/` prefix (e.g., `google/gemini-2.0-flash-001`) — it will be stripped automatically
- The native SDK handles token refresh automatically
- Uses service account credentials if provided, otherwise falls back to ADC
---
## Embedding Model
Converts text into dense vector representations for semantic similarity search.
**Default:** `BAAI/bge-small-en-v1.5` (384 dimensions, ~130MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers (default) | Development, low latency |
| `openai` | OpenAI embeddings API | Production, high quality |
| `cohere` | Cohere embeddings API | Production, multilingual |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
### Local Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `BAAI/bge-small-en-v1.5` | 384 | Default, fast, good quality |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
### OpenAI Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `text-embedding-3-small` | 1536 | Default OpenAI, cost-effective |
| `text-embedding-3-large` | 3072 | Higher quality, more expensive |
| `text-embedding-ada-002` | 1536 | Legacy model |
### Cohere Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `embed-english-v3.0` | 1024 | English text |
| `embed-multilingual-v3.0` | 1024 | 100+ languages |
:::warning Embedding Dimensions
Hindsight automatically detects the embedding dimension at startup and adjusts the database schema. Once memories are stored, you cannot change dimensions without losing data.
:::
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=local
export HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5
# OpenAI
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small
# Cohere
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL=embed-english-v3.0
# TEI (self-hosted)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=tei
export HINDSIGHT_API_EMBEDDINGS_TEI_URL=http://localhost:8080
# LiteLLM proxy
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL=text-embedding-3-small
```
See [Configuration](./configuration#embeddings) for all options including Azure OpenAI and custom endpoints.
---
## Cross-Encoder (Reranker)
Reranks initial search results to improve precision.
**Default:** `cross-encoder/ms-marco-MiniLM-L-6-v2` (~85MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers CrossEncoder (default) | Development, low latency |
| `cohere` | Cohere rerank API | Production, high quality |
| `zeroentropy` | ZeroEntropy rerank API (zerank-2) | Production, state-of-the-art accuracy |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `flashrank` | FlashRank (lightweight, fast) | Resource-constrained environments |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
| `litellm-sdk` | LiteLLM SDK (direct API, no proxy) | Multi-provider, simpler setup |
| `rrf` | RRF-only (no neural reranking) | Testing, minimal resources |
### Local Models
| Model | Use Case |
|-------|----------|
| `cross-encoder/ms-marco-MiniLM-L-6-v2` | Default, fast |
| `cross-encoder/ms-marco-MiniLM-L-12-v2` | Higher accuracy |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | Multilingual |
### Cohere Models
| Model | Use Case |
|-------|----------|
| `rerank-english-v3.0` | English text |
| `rerank-multilingual-v3.0` | 100+ languages |
### ZeroEntropy Models
| Model | Use Case |
|-------|----------|
| `zerank-2` | Flagship multilingual reranker (default) |
| `zerank-2-small` | Faster, lighter variant |
### LiteLLM Supported Providers
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
| Provider | Model Example |
|----------|---------------|
| Cohere | `cohere/rerank-english-v3.0` |
| Together AI | `together_ai/...` |
| Voyage AI | `voyage/rerank-2` |
| Jina AI | `jina_ai/...` |
| AWS Bedrock | `bedrock/...` |
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_RERANKER_PROVIDER=local
export HINDSIGHT_API_RERANKER_LOCAL_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
# Cohere
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
# ZeroEntropy (state-of-the-art accuracy)
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # default, can omit
# TEI (self-hosted)
export HINDSIGHT_API_RERANKER_PROVIDER=tei
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
# FlashRank (lightweight)
export HINDSIGHT_API_RERANKER_PROVIDER=flashrank
# LiteLLM proxy
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_RERANKER_LITELLM_MODEL=cohere/rerank-english-v3.0
# RRF-only (no neural reranking)
export HINDSIGHT_API_RERANKER_PROVIDER=rrf
```
See [Configuration](./configuration#reranker) for all options including Azure-hosted endpoints and batch settings.
@@ -1,270 +0,0 @@
# Monitoring
Hindsight provides comprehensive observability through Prometheus metrics, OpenTelemetry distributed tracing, and pre-built Grafana dashboards.
## Local Development
For local observability, use the Grafana LGTM (Loki, Grafana, Tempo, Mimir) all-in-one stack:
```bash
./scripts/dev/start-monitoring.sh
```
This starts a single Docker container providing:
- **Grafana UI**: http://localhost:3000 (anonymous admin access)
- **Traces (Tempo)**: OTLP endpoint at http://localhost:4318 (HTTP) and http://localhost:4317 (gRPC)
- **Metrics (Prometheus/Mimir)**: Scrapes http://localhost:8888/metrics automatically
- **Logs (Loki)**: Available for log aggregation
- **Pre-built Dashboards**: Hindsight Operations, LLM Metrics, API Service
**Enable tracing in your API:**
```bash
export HINDSIGHT_API_OTEL_TRACES_ENABLED=true
export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
```
:::note Production Deployment
The local monitoring stack is for development only. In production, deploy Grafana LGTM separately or use commercial platforms (Grafana Cloud, DataDog, New Relic, etc.).
:::
## Grafana Dashboards
Pre-built dashboards are available in [`monitoring/grafana/dashboards/`](https://github.com/anthropics/hindsight/tree/main/monitoring/grafana/dashboards). Import these JSON files into your Grafana instance:
| Dashboard | Description |
|-----------|-------------|
| **Hindsight Operations** | Operation rates, latency percentiles, per-bank metrics |
| **Hindsight LLM Metrics** | LLM calls, token usage, latency by scope/provider |
| **Hindsight API Service** | HTTP requests, error rates, DB pool, process metrics |
The dashboards are automatically provisioned when using the monitoring stack script.
## Metrics Endpoint
Hindsight exposes Prometheus metrics at `/metrics`:
```bash
curl http://localhost:8888/metrics
```
## Available Metrics
### Operation Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.operation.duration` | Histogram | operation, bank_id, source, budget, max_tokens, success | Duration of operations in seconds |
| `hindsight.operation.total` | Counter | operation, bank_id, source, budget, max_tokens, success | Total number of operations executed |
**Labels:**
- `operation`: Operation type (`retain`, `recall`, `reflect`)
- `bank_id`: Memory bank identifier
- `source`: Where the operation was triggered from (`api`, `reflect`, `internal`)
- `budget`: Budget level if specified (`low`, `mid`, `high`)
- `max_tokens`: Max tokens if specified
- `success`: Whether the operation succeeded (`true`, `false`)
The `source` label allows distinguishing between:
- `api`: Direct API calls from clients
- `reflect`: Internal recall calls made during reflect operations
- `internal`: Other internal operations
### LLM Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.llm.duration` | Histogram | provider, model, scope, success | Duration of LLM API calls in seconds |
| `hindsight.llm.calls.total` | Counter | provider, model, scope, success | Total number of LLM API calls |
| `hindsight.llm.tokens.input` | Counter | provider, model, scope, success, token_bucket | Input tokens for LLM calls |
| `hindsight.llm.tokens.output` | Counter | provider, model, scope, success, token_bucket | Output tokens from LLM calls |
**Labels:**
- `provider`: LLM provider (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio`, `bedrock`, `litellm`)
- `model`: Model name (e.g., `gpt-4`, `claude-3-sonnet`)
- `scope`: What the LLM call is for (`memory`, `reflect`, `consolidation`, `answer`)
- `success`: Whether the call succeeded (`true`, `false`)
- `token_bucket`: Token count bucket for cardinality control (`0-100`, `100-500`, `500-1k`, `1k-5k`, `5k-10k`, `10k-50k`, `50k+`)
### HTTP Request Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.http.duration` | Histogram | method, endpoint, status_code, status_class | Duration of HTTP requests in seconds |
| `hindsight.http.requests.total` | Counter | method, endpoint, status_code, status_class | Total number of HTTP requests |
| `hindsight.http.requests.in_progress` | UpDownCounter | method, endpoint | Number of HTTP requests currently being processed |
**Labels:**
- `method`: HTTP method (`GET`, `POST`, `PUT`, `DELETE`)
- `endpoint`: Request path (normalized to reduce cardinality - UUIDs replaced with `{id}`)
- `status_code`: HTTP status code (`200`, `400`, `500`, etc.)
- `status_class`: Status code class (`2xx`, `4xx`, `5xx`)
### Database Pool Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.db.pool.size` | Gauge | - | Current number of connections in the pool |
| `hindsight.db.pool.idle` | Gauge | - | Number of idle connections in the pool |
| `hindsight.db.pool.min` | Gauge | - | Minimum pool size |
| `hindsight.db.pool.max` | Gauge | - | Maximum pool size |
### Process Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.process.cpu.seconds` | Gauge | type | Process CPU time in seconds |
| `hindsight.process.memory.bytes` | Gauge | type | Process memory usage in bytes |
| `hindsight.process.open_fds` | Gauge | - | Number of open file descriptors |
| `hindsight.process.threads` | Gauge | - | Number of active threads |
**Labels:**
- `type` (CPU): `user` or `system`
- `type` (Memory): `rss_max` (maximum resident set size)
### Histogram Buckets
Custom bucket boundaries are configured for better percentile accuracy:
**Operation Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 60.0, 120.0
```
**LLM Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0, 15.0, 30.0, 60.0, 120.0
```
**HTTP Duration Buckets (seconds):**
```
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0
```
## Prometheus Configuration
```yaml
scrape_configs:
- job_name: 'hindsight'
static_configs:
- targets: ['localhost:8888']
```
## Example Queries
### Average operation latency by type
```promql
rate(hindsight_operation_duration_sum[5m]) / rate(hindsight_operation_duration_count[5m])
```
### LLM calls per minute by provider
```promql
rate(hindsight_llm_calls_total[1m]) * 60
```
### P95 LLM latency
```promql
histogram_quantile(0.95, rate(hindsight_llm_duration_bucket[5m]))
```
### Total tokens consumed by model
```promql
sum by (model) (hindsight_llm_tokens_input_total + hindsight_llm_tokens_output_total)
```
### Internal vs API recall operations
```promql
sum by (source) (rate(hindsight_operation_total{operation="recall"}[5m]))
```
### HTTP requests per second by endpoint
```promql
sum by (endpoint) (rate(hindsight_http_requests_total[1m]))
```
### HTTP error rate (5xx)
```promql
sum(rate(hindsight_http_requests_total{status_class="5xx"}[5m])) / sum(rate(hindsight_http_requests_total[5m]))
```
### P95 HTTP latency
```promql
histogram_quantile(0.95, sum by (le) (rate(hindsight_http_duration_seconds_bucket[5m])))
```
### Database pool utilization
```promql
hindsight_db_pool_size / hindsight_db_pool_max
```
### Active database connections
```promql
hindsight_db_pool_size - hindsight_db_pool_idle
```
### CPU usage rate
```promql
rate(hindsight_process_cpu_seconds{type="user"}[1m])
```
---
## Distributed Tracing
Hindsight supports OpenTelemetry distributed tracing for memory operations and LLM calls, following GenAI semantic conventions v1.37+.
### Configuration
See [Configuration - OpenTelemetry Tracing](./configuration#opentelemetry-tracing) for environment variables.
**Quick Start:**
```bash
# Enable tracing
export HINDSIGHT_API_OTEL_TRACES_ENABLED=true
export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# View traces with Grafana LGTM (local dev)
./scripts/dev/start-monitoring.sh
# Open http://localhost:3000 → Explore → Tempo
```
Supports any OTLP-compatible backend (Grafana LGTM, Langfuse, OpenLIT, DataDog, New Relic, Honeycomb, etc.).
### Span Hierarchy
**Parent Spans (Operations):**
- `hindsight.retain` - Memory ingestion
- `hindsight.recall` - Memory retrieval
- `hindsight.recall_embedding` - Query embedding
- `hindsight.recall_retrieval` - Parallel search (semantic, BM25, graph, temporal)
- `hindsight.recall_fusion` - Reciprocal Rank Fusion
- `hindsight.recall_rerank` - Cross-encoder reranking
- `hindsight.reflect` - Agentic reasoning
- `hindsight.reflect_tool_call` - Tool execution (recall, lookup, etc.)
- `hindsight.consolidation` - Observation synthesis
- `hindsight.mental_model_refresh` - Mental model updates
**Child Spans (LLM Calls):**
- Named by scope (e.g., `hindsight.memory`, `hindsight.reflect`)
- Contain full prompts/completions as events
- Follow GenAI semantic conventions for attributes
### Span Attributes
**Operation Spans:**
- `hindsight.operation` - Operation type
- `hindsight.bank_id` - Memory bank ID
- `hindsight.query` - Query text (truncated to 100 chars)
- `hindsight.fact_types` - Fact types for recall
- `hindsight.thinking_budget` - Budget allocation
- `hindsight.max_tokens` - Token limit
**LLM Spans (GenAI Semantic Conventions):**
- `gen_ai.operation.name` - Always `"chat"`
- `gen_ai.provider.name` - Provider (`openai`, `anthropic`, `google`, etc.)
- `gen_ai.request.model` - Model name
- `gen_ai.usage.input_tokens` - Input tokens
- `gen_ai.usage.output_tokens` - Output tokens
- `hindsight.scope` - LLM call purpose (`memory`, `reflect`, `consolidation`, etc.)
**Events:**
- `gen_ai.client.inference.operation.details` - Full prompts and completions
@@ -1,217 +0,0 @@
---
sidebar_position: 5
---
# Multilingual Support
Hindsight automatically detects the language of your input and responds in the same language. This means facts, entities, and reflect responses are preserved in their original language without translation to English.
## How It Works
```mermaid
graph LR
A[Chinese Input] --> B[Language Detection]
B --> C[Extract Facts in Chinese]
C --> D[Chinese Entities]
D --> E[Chinese Response]
```
When you retain content or reflect on a query, Hindsight:
1. **Detects the input language** automatically from the content
2. **Extracts facts in the original language** - preserving nuance and meaning
3. **Stores entities in their native script** - 张伟 stays 张伟, not "Zhang Wei"
4. **Responds in the same language** - queries in Chinese get Chinese answers
---
## Retain with Non-English Content
When you retain content in any language, Hindsight extracts and stores facts in that same language.
### Example: Chinese Content
```python
from hindsight import Hindsight
hindsight = Hindsight()
# Retain Chinese content
hindsight.retain(
bank_id="user-123",
content="""
张伟是一位资深软件工程师,在腾讯工作了五年。
他专门研究分布式系统,并领导了公司微服务架构的开发。
""",
context="团队概述"
)
# Query in Chinese - get Chinese results
results = hindsight.recall(
bank_id="user-123",
query="告诉我关于张伟的信息"
)
# Facts are returned in Chinese:
# - 张伟是一位资深软件工程师,在腾讯工作了五年
# - 张伟专门研究分布式系统,并领导了公司微服务架构的开发
```
### Example: Japanese Content
```python
hindsight.retain(
bank_id="user-123",
content="""
田中さんはソフトウェアエンジニアで、東京のスタートアップで働いています。
彼女はPythonとTypeScriptが得意で、毎日コードレビューをしています。
""",
context="チームプロフィール"
)
# Query in Japanese
results = hindsight.recall(
bank_id="user-123",
query="田中さんについて教えてください"
)
```
---
## Reflect with Non-English Queries
The `reflect` operation also respects the input language, generating thoughtful responses in the same language as the query.
### Example: Chinese Reflection
```python
# Store facts about team members (in Chinese)
hindsight.retain(
bank_id="team-eval",
content="张伟是一位优秀的软件工程师,完成了五个重大项目。他总是按时交付,代码整洁有良好的文档。",
context="绩效评估"
)
hindsight.retain(
bank_id="team-eval",
content="李明最近加入团队。他错过了第一个截止日期,代码有很多bug。",
context="绩效评估"
)
# Reflect in Chinese
result = hindsight.reflect(
bank_id="team-eval",
query="谁是更可靠的工程师?"
)
# Response is in Chinese:
# "我认为张伟更可靠。张伟完成了五个重大项目,按时交付,代码质量高..."
```
---
## Mixed Language Content
Hindsight handles mixed-language content gracefully, preserving both languages where appropriate.
### Example: Chinese Text with English Company Names
```python
hindsight.retain(
bank_id="user-123",
content="""
王芳在Google北京办公室工作,她是一名高级产品经理。
之前她在Microsoft和Amazon工作过。
她负责管理YouTube在中国市场的推广策略。
""",
context="员工资料"
)
# Facts preserve both languages:
# - 王芳在Google北京办公室工作,担任高级产品经理
# - 王芳曾在Microsoft和Amazon工作过
# - 王芳负责管理YouTube在中国市场的推广策略
```
---
## Supported Languages
**Hindsight's multilingual support depends entirely on your LLM's language capabilities.** Hindsight instructs the LLM to detect the input language and respond in that same language. If your LLM supports a language, Hindsight will work with it.
Most modern LLMs (GPT-4, Claude, Gemini, Llama 3, etc.) support dozens of languages including:
- **East Asian**: Chinese (Simplified/Traditional), Japanese, Korean
- **European**: Spanish, French, German, Italian, Portuguese, Dutch, Polish, Russian
- **Middle Eastern**: Arabic, Hebrew, Turkish
- **South Asian**: Hindi, Bengali, Tamil
- **Southeast Asian**: Thai, Vietnamese, Indonesian
**To verify support for your target language**, test your LLM directly with content in that language. If the LLM can understand and generate text in the language, Hindsight will preserve it correctly.
---
## Configuring for Multilingual Use
For optimal multilingual performance, you should configure all three components of the pipeline:
### 1. LLM (Required)
Your LLM must support the target languages. Most modern LLMs do, but verify with your specific model.
### 2. Embedding Model (Recommended)
The default embedding model (`BAAI/bge-small-en-v1.5`) is **English-only**. For multilingual content, use a multilingual embedding model:
```bash
# In your .env file
HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-m3
```
**Recommended multilingual embedding models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-m3` | 100+ | Best overall multilingual performance |
| `intfloat/multilingual-e5-large` | 100+ | Good alternative |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 50+ | Lighter weight |
### 3. Reranker Model (Recommended)
The default reranker (`cross-encoder/ms-marco-MiniLM-L-6-v2`) is **English-only**. For multilingual content, use a multilingual reranker:
```bash
# In your .env file
HINDSIGHT_API_RERANKER_LOCAL_MODEL=BAAI/bge-reranker-v2-m3
```
**Recommended multilingual reranker models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-reranker-v2-m3` | 100+ | Best multilingual reranking |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | 14 | Lighter alternative |
---
## Best Practices
### 1. Use Multilingual Models for Non-English Content
If you primarily work with non-English content, configure multilingual embedding and reranker models. English-only models will still store your content correctly, but semantic search quality will be degraded.
### 2. Keep Content in One Language Per Retain Call
While mixed content works, keeping each `retain` call in a single language produces more consistent results.
### 3. Query in the Same Language as Your Content
For best results, query using the same language as your stored content. Cross-language queries (e.g., English query for Chinese content) may work but results can vary depending on your embedding model.
---
## Technical Details
Multilingual support is implemented through LLM prompt instructions rather than external language detection libraries. This approach:
- **Requires no additional dependencies**
- **Works with any LLM** that supports multiple languages
- **Handles edge cases** like mixed-language content naturally
- **Preserves semantic meaning** better than rule-based translation
The LLM is instructed to:
1. Detect the input language
2. Extract all facts, entities, and descriptions in that same language
3. Never translate to English unless the input is in English
@@ -1,214 +0,0 @@
---
sidebar_position: 5
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
# Observations: Knowledge Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **observations** — synthesized knowledge representations that capture patterns and learnings.
```mermaid
graph LR
A[New Facts] --> B[Consolidation Engine]
B --> C{Existing Observation?}
C -->|Yes| D[Refine Observation]
C -->|No| E[Create Observation]
D --> F[Observations]
E --> F
```
---
## What Are Observations?
Observations are **consolidated knowledge** synthesized from multiple facts. Unlike raw facts which are individual pieces of information, observations represent patterns, preferences, and learnings that emerge from accumulated evidence.
| Raw Facts | Observation |
|-----------|--------------|
| "Alice prefers Python" | "Alice is a Python-focused developer who values readability and simplicity" |
| "Alice dislikes verbose code" | |
| "Alice recommends type hints" | |
Observations provide:
- **Synthesis**: Patterns that emerge from multiple facts
- **Context**: Richer understanding than individual facts
- **Efficiency**: Condensed knowledge for faster retrieval
---
## How Consolidation Works
### Automatic Background Processing
After `retain()` completes, the consolidation engine runs automatically:
1. **New facts analyzed** — Each new fact is compared against existing observations
2. **Pattern detection** — Related facts are grouped and synthesized
3. **Observation creation/update** — New observations are created or existing ones refined
4. **Evidence tracking** — Each observation maintains references to supporting facts
### Evidence-Based Evolution
Observations evolve as new evidence arrives:
| Event | What the bank learns | Observation state |
|-------|---------------------|----------------|
| **Day 1** | "Redis is open source under BSD license" | "Redis is excellent for caching — fast, reliable, and OSS-friendly" (2 supporting facts) |
| **Day 2** | "Redis has great community support" | Observation reinforced (3 supporting facts) |
| **Day 30** | "Redis changed license to SSPL" | Observation refined: "Redis is technically strong, but has license concerns for cloud" |
| **Day 45** | "Valkey forked Redis under BSD" | New observation: "Consider Valkey for new projects requiring true OSS" |
### Handling Contradictory Evidence
What happens when a new fact contradicts an existing observation?
The consolidation engine doesn't blindly overwrite — it **reconciles** the contradiction by capturing the evolution:
**Example: User preference changes**
| Time | Fact | Observation |
|------|------|--------------|
| Week 1 | "User says they love React" | "User prefers React for frontend development" |
| Week 2 | "User praises React's component model" | "User is enthusiastic about React, particularly its component model" |
| Week 3 | "User says they've switched to Vue and won't use React anymore" | "User was previously a React enthusiast who appreciated its component model, but has now switched to Vue and no longer uses React" |
Notice how the final observation captures the **full journey** — not just "User prefers Vue" but the complete evolution of their preference. This nuanced understanding means:
- Your agent won't recommend React tutorials to someone who explicitly moved away from it
- Your agent understands *why* this matters (they were enthusiastic before, so this is a deliberate choice)
- Your agent can reference this history when relevant ("I know you used to work with React...")
The system:
1. **Detects the conflict** — New fact contradicts existing observation
2. **Preserves history** — Incorporates the previous understanding into the new observation
3. **Creates nuanced observation** — Synthesizes a richer understanding that captures the change
4. **Updates freshness** — Marks the observation as recently updated
**Example: Correcting misinformation**
| Time | Fact | Observation |
|------|------|--------------|
| Day 1 | "Alice works at Google" | "Alice is a Google employee" |
| Day 10 | "Alice actually works at Meta, not Google" | "Alice works at Meta (previously thought to work at Google)" |
When a fact explicitly corrects previous information, the observation is updated to reflect the correction while noting the previous understanding. The raw facts are always preserved, so you can trace back to see what was originally stated and when it was corrected.
---
## Observations in Retrieval
Observations are automatically included in both `recall()` and `reflect()` operations:
### In Recall
Observations are returned alongside raw facts, filtered by the `types` parameter:
<CodeSnippet code={recallPy} section="recall-with-observations" language="python" />
### In Reflect
The reflect agent uses **hierarchical retrieval**:
1. **[Mental Models](/developer/api/mental-models)** — User-curated summaries (highest priority)
2. **Observations** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification
The agent automatically queries observations and uses them to inform its reasoning.
---
## Freshness Awareness
Observations track when they were last updated. During reflect, the agent considers freshness:
- **Fresh observations**: Used directly for reasoning
- **Stale observations**: Agent verifies against current facts before relying on them
This ensures responses stay accurate even as the underlying data changes.
---
## Observation Scopes
By default, observations are scoped to all of a memory's tags combined. The `observation_scopes` retain parameter lets you control this — building separate observations per tag, per combination, or with a custom list of scopes. This is key when a single memory carries multiple tags and you want each tag to accumulate its own observations independently.
See [`observation_scopes` in the Retain API](./api/retain#observation_scopes) for the full explanation and options.
---
## Observations Mission
You can define exactly what this bank should synthesise by setting an **observations mission** (`observations_mission`). This replaces the built-in durable-knowledge rules with your own instructions, letting you control what shape observations take.
```
e.g. Observations are stable facts about people and projects.
Always include preferences, skills, and recurring patterns.
Ignore one-off events and ephemeral state.
```
Leave it blank to use the server default — durable, specific facts that stay true over time (preferences, skills, relationships, recurring patterns), with ephemeral state filtered out.
**Examples:**
| `observations_mission` | What gets synthesised |
|------------------------|----------------------|
| *(unset — default)* | Durable facts: preferences, skills, relationships, recurring patterns |
| *"Observations are weekly summaries of sprint outcomes and blockers"* | Broad event summaries grouped by time period |
| *"Observations are stable facts about named individuals only"* | Person-centric knowledge, tied to specific people |
| *"Observations are recurring patterns in customer support interactions"* | Failure modes, common requests, pain points |
Set `observations_mission` via the [bank config API](/developer/api/memory-banks#observations-configuration) or the [`HINDSIGHT_API_OBSERVATIONS_MISSION`](/developer/configuration#observations) environment variable.
---
## Observation Lifecycle & Invalidation
### When Memories Are Deleted
Observations are derived from source memories. When source memories are removed, Hindsight automatically keeps observations consistent:
| Action | Effect on observations |
|--------|----------------------|
| Delete a document | All observations derived from the document's memories are deleted |
| Delete individual memories (by type) | Observations sourced from those memories are deleted |
| Delete an entire bank | All observations are deleted along with everything else |
After deletion, the **remaining source memories** that fed the affected observations have their consolidation state reset, so they will be re-consolidated on the next consolidation run and produce fresh observations.
### Clearing Observations for a Specific Memory
You can clear all observations derived from a single memory without deleting the memory itself. This is useful when you want to force re-synthesis of a memory's contribution to consolidated knowledge.
Use the `DELETE /v1/default/banks/{bank_id}/memories/{memory_id}/observations` endpoint. This will:
1. Delete all observations that list the memory as a source
2. Reset `consolidated_at` on the memory itself and any other source memories that contributed to those observations
3. Trigger a consolidation job so fresh observations are produced automatically
### Resetting All Observations
To wipe all consolidated knowledge and start over:
```python
# Clear all observations for a bank
client.clear_observations(bank_id="my-bank")
```
This resets the consolidation state for all source memories in the bank, so the next consolidation run will re-derive all observations from scratch.
---
## Configuration
Observation consolidation runs automatically. You can monitor consolidation via the [Operations API](./api/operations).
---
## Next Steps
- [**Retain**](./retain) — How facts are stored and trigger consolidation
- [**Recall**](./retrieval) — How observations are retrieved
- [**Reflect**](./reflect) — How the agentic loop uses observations
- [**Mental Models**](./api/mental-models) — User-curated summaries for common queries
@@ -1,151 +0,0 @@
# Performance
Hindsight is designed for high-performance semantic memory operations at scale. This page covers performance characteristics, optimization strategies, and best practices.
## Overview
Hindsight's performance is optimized across three key operations:
- **Retain (Ingestion)**: Batch processing with async operations for large-scale memory storage
- **Recall (Search)**: Sub-second semantic search with configurable thinking budgets
- **Reflect (Reasoning)**: Disposition-aware answer generation with controllable compute
## Design Philosophy: Optimized for Fast Reads
Hindsight is **architected from the ground up to prioritize read performance over write performance**. This design decision reflects the typical usage pattern of memory systems: memories are written once but read many times.
The system makes deliberate trade-offs to ensure **sub-second recall operations**:
- **Pre-computed embeddings**: All memory embeddings are generated and indexed during retention
- **Optimized vector search**: HNSW indexes enable fast approximate nearest neighbor search
- **Fact extraction at write time**: Complex LLM-based fact extraction happens during retention, not retrieval
- **Structured memory graphs**: Relationships and temporal information are resolved upfront
This means **Recall (search) operations are blazingly fast** because all the heavy lifting has already been done.
### Performance Comparison
| Operation | Typical Latency | Primary Bottleneck | Optimization Strategy |
|-----------|----------------|-------------------|----------------------------------|
| **Recall** | 100-600ms | Re-ranker (on CPU) | Use GPU for re-ranking, or reduce budget |
| **Reflect** | 800-3000ms | LLM generation | Use faster LLM |
| **Retain** | 500ms-2000ms per batch | **LLM fact extraction** | Use high-throughput LLM provider |
Hindsight is designed to ensure your **application's read path (recall/reflect) is always fast**, even if it means spending more time upfront during writes. This is the right trade-off for memory systems where:
- Memories are retained in background processes or during low-traffic periods
- Memories are queried frequently in user-facing, latency-sensitive contexts
- The ratio of reads to writes is high (typically 10:1 or higher)
---
## Retain Performance
**Retain (write) operations are inherently slower** because they involve LLM-based fact extraction, entity recognition, temporal reasoning, relationship mapping, and embedding generation. **The LLM is the primary bottleneck for write latency.**
### Hindsight Doesn't Need a Smart Model
The fact extraction process is structured and well-defined, so smaller, faster models work extremely well. Our recommended model is `gpt-oss-20b` (available via Groq and other providers).
To maximize retention throughput:
1. **Use high-throughput LLM providers**: Choose providers with high requests-per-minute (RPM) limits and low latency
- **Fast**: [Groq](https://groq.com) with `gpt-oss-20b` or other openai-oss models, self-hosted models on GPU clusters (vLLM, TGI)
- **Slow**: Standard cloud LLM providers with rate limits
2. **Batch your operations**: Group related content into batch requests. Send as much data as you want in a single request — the only limit is the HTTP payload size.
3. **Use async mode for large datasets**: Queue operations in the background
4. **Parallel processing**: For very large datasets, use multiple concurrent retention requests with different `document_id` values
### Automatic Batch Optimization
**When using async retain, Hindsight automatically handles batch sizing for you.** You don't need to manually tune batch sizes or worry about optimal chunking.
How it works:
- **Send large batches**: Submit hundreds or thousands of items in a single async retain request
- **Automatic splitting**: Hindsight automatically splits large batches (>10,000 tokens) into optimized sub-batches
- **Parallel processing**: Sub-batches are processed concurrently in the background
- **Status tracking**: Parent operation aggregates status from all sub-batches
- **Token-based**: Batching uses tiktoken for accurate token counting, not character counts
Benefits:
- Send entire documents or datasets in one API call
- Let Hindsight optimize the processing strategy
- Track overall progress via the parent operation status
- No need to manually split data into small batches
### Throughput
Factors affecting throughput:
- Document size and complexity
- LLM provider rate limits (for fact extraction)
- Database write performance
- Available CPU/memory resources
---
## Recall Performance
### Budget
The `budget` parameter controls the search depth and quality. Choose based on query complexity — comprehensive questions that need thorough analysis benefit from higher budgets:
| Budget | Use Case |
|--------|----------|
| `low` | Quick lookups, real-time chat |
| `mid` | Standard queries, balanced performance |
| `high` | Comprehensive questions, thorough analysis |
### Optimization
1. **Appropriate budgets**: Use lower budgets for simple queries, higher for comprehensive reasoning
2. **Limit result tokens**: Set `max_tokens` to control response size (default: 4096)
3. **Include chunks**: Use `include_chunks` to retrieve the raw text that generated memories when you need additional context
### Database Performance
Hindsight uses PostgreSQL with pgvector for efficient vector search:
- **Index type**: HNSW for approximate nearest neighbor search
- **Typical query time**: 10-50ms for vector search on 100K+ facts
- **Scalability**: Tested with millions of facts per bank
## Reflect Performance
### Performance Characteristics
| Component | Latency | Description |
|-----------|----------------|-------------|
| Memory search | 100-600ms | Based on budget (low/mid/high) |
| LLM generation | 500-2000ms | Depends on provider and response length |
| **Total** | **600-2600ms** | Typical end-to-end latency |
### Optimization Strategies
1. **Budget selection**: Use lower budgets when context is sufficient
2. **Context provision**: Provide relevant `context` to reduce recall requirements and steer towards more focused answers
## Best Practices
### Operations
- **Use appropriate budgets**: Don't over-provision for simple queries; use higher budgets for comprehensive reasoning
- **Batch retain operations**: Group related content together for better efficiency
- **Cache frequent queries**: Cache at the application level for repeated queries
- **Profile with trace**: Use the `trace` parameter to identify slow operations
### Scaling
- **Horizontal scaling**: Deploy multiple API instances behind a load balancer with shared PostgreSQL
- **Concurrency**: 100+ simultaneous requests supported; memory search scales with CPU cores
- **LLM rate limits**: Distribute load across multiple API keys/providers (typically 60-500 RPM per key)
### Cost Optimization
- **Use efficient models**: `gpt-oss-20b` via Groq for retain — Hindsight doesn't need frontier models
- **Enable provider Batch API**: Set `HINDSIGHT_API_RETAIN_BATCH_ENABLED=true` with async retain to cut LLM fact-extraction costs by 50% (supported on OpenAI and Groq; results delivered within 24 hours)
- **Control token budgets**: Limit `max_tokens` for recall, use lower budgets when possible
- **Optimize chunks**: Larger chunks (1000-2000 tokens) are more efficient than many small ones
### Monitoring
- **Prometheus metrics**: Available at `/metrics` — track latency percentiles, throughput, and error rates
- **Key metrics**: `hindsight_recall_duration_seconds`, `hindsight_reflect_duration_seconds`, `hindsight_retain_items_total`
@@ -1,110 +0,0 @@
---
sidebar_position: 2
---
# RAG vs Memory
Traditional RAG (Retrieval-Augmented Generation) retrieves documents similar to a query. Hindsight provides structured memory with temporal reasoning, entity understanding, and belief formation.
## Capability Comparison
| Capability | RAG | Hindsight |
|------------|-----|-----------|
| **Search strategy** | Semantic similarity only | Semantic + keyword + graph + temporal |
| **Multi-hop reasoning** | Limited to retrieved chunks | Graph traversal across entity relationships |
| **Temporal queries** | Keyword matching ("spring") | Date parsing and range filtering |
| **Entity understanding** | None | Entity resolution, co-occurrence tracking |
| **Knowledge consolidation** | Stateless | Mental models that synthesize and evolve |
| **Disposition** | None | 3 traits (skepticism, literalism, empathy) influence interpretation |
## Architecture Comparison
### RAG
| Step | Operation |
|------|-----------|
| 1 | Embed query |
| 2 | Vector similarity search |
| 3 | Return top-k chunks |
| 4 | Generate response |
Single retrieval strategy. No state between queries.
### Hindsight
| Step | Operation |
|------|-----------|
| 1 | Parse query (extract temporal expressions, entities) |
| 2 | Execute 4 parallel retrievals: semantic, BM25, graph, temporal |
| 3 | Fuse results with RRF |
| 4 | Rerank with cross-encoder |
| 5 | Apply disposition traits |
| 6 | Generate response |
Multiple retrieval strategies. Persistent state across sessions.
## Example Scenarios
### Multi-Hop Reasoning
**Stored facts:**
- "Alice is the tech lead on Project Atlas"
- "Project Atlas uses Kubernetes"
- "Kubernetes cluster had an outage Tuesday"
**Query:** "Was Alice affected by recent issues?"
| System | Result |
|--------|--------|
| RAG | Retrieves facts about Alice only (no semantic similarity to "issues") |
| Hindsight | Traverses Alice → Project Atlas → Kubernetes → outage via entity links |
### Temporal Queries
**Stored facts with timestamps:**
- March: "Alice started microservices migration"
- April: "Alice completed auth service"
- October: "Alice focusing on performance"
**Query:** "What did Alice do last spring?"
| System | Result |
|--------|--------|
| RAG | Returns all Alice facts regardless of date |
| Hindsight | Parses "last spring" → March-May, filters to that range |
### Entity Understanding
**Stored facts about a user across sessions:**
- "Pro subscription"
- "Mobile app crashes in settings"
- "Switched to annual billing"
- "Desktop app working fine"
**Query:** "What do you know about my account?"
| System | Result |
|--------|--------|
| RAG | Lists disconnected facts |
| Hindsight | Returns connected facts via entity graph: subscription status, billing, known issues |
### Knowledge Evolution
**Week 1:** User struggles with async Python, succeeds with threads
**Week 3:** User asks about asyncio, implements async database calls
| System | Behavior |
|--------|----------|
| RAG | No memory of progression |
| Hindsight | Consolidates mental model "user prefers sync" → refines to "user growing comfortable with async" |
## When to Use Each
| Use Case | Recommended |
|----------|-------------|
| Document Q&A over static corpus | RAG |
| Search with no temporal requirements | RAG |
| AI assistants with persistent memory | Hindsight |
| Applications requiring entity tracking | Hindsight |
| Systems needing consistent disposition | Hindsight |
| Temporal queries ("last month", "in 2023") | Hindsight |
@@ -1,244 +0,0 @@
---
sidebar_position: 4
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
# Reflect: Agentic Reasoning with Disposition
When you call `reflect()`, Hindsight runs an **agentic loop** that autonomously gathers evidence and reasons through the lens of the bank's disposition to generate contextual responses.
```mermaid
graph TB
subgraph agent["Reflect Agent Loop"]
A[Query] --> B{Need more info?}
B -->|Yes| C[Call Tools]
C --> D[search_mental_models]
C --> E[search_observations]
C --> F[recall]
C --> G[expand]
D --> B
E --> B
F --> B
G --> B
B -->|No| H[Generate Response]
end
H --> I[Response + Citations]
```
---
## How It Works
Unlike simple retrieval, reflect is an **agentic system** that:
1. **Autonomously gathers evidence** — The agent decides what information it needs and calls appropriate tools
2. **Uses hierarchical retrieval** — Checks mental models first, then observations, then raw facts
3. **Applies disposition** — Shapes reasoning based on the bank's personality traits
4. **Enforces directives** — Hard rules that must be followed in all responses
5. **Cites sources** — Returns which memories and observations were used
### The Agentic Loop
The reflect agent runs in a loop with access to these tools:
| Tool | Purpose | Priority |
|------|---------|----------|
| `search_mental_models` | User-curated summaries | Highest (check first) |
| `search_observations` | Consolidated knowledge | High |
| `recall` | Raw facts (ground truth) | Fallback |
| `expand` | Get more context for a memory | As needed |
| `done` | Complete with final answer | When ready |
The agent:
- **Must gather evidence** before answering (guardrail prevents empty responses)
- **Runs up to 10 iterations** to find relevant information
- **Validates citations** — only IDs that were actually retrieved can be cited
### Hierarchical Retrieval Strategy
The agent uses a smart retrieval hierarchy:
1. **[Mental Models](/developer/api/mental-models)** — User-curated summaries you've pre-computed for common queries
2. **[Observations](/developer/observations)** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification when observations are stale
**Mental models** are saved reflect responses that you create for frequently asked questions. They're checked first because they represent explicitly curated knowledge. See the [Mental Models API](/developer/api/mental-models) for how to create and manage them.
If an observation is marked as **stale**, the agent automatically verifies it against current facts.
---
## Why Reflect?
Most AI systems can retrieve facts, but they can't **reason** about them in a consistent way.
### The Problem
Without reflect:
- **No consistent character**: Same question gets different answers each time
- **No knowledge synthesis**: System never connects related facts
- **No reasoning context**: Responses don't reflect accumulated knowledge
- **Generic responses**: Every AI sounds the same
### The Value
With reflect:
- **Consistent character**: A "detail-oriented, cautious" bank emphasizes risks and thorough planning
- **Evolving knowledge**: Observations strengthen and adapt as evidence accumulates
- **Contextual reasoning**: "Based on what I know about your team's remote work success..."
- **Differentiated behavior**: Support bots sound diplomatic, code reviewers sound direct
### When to Use Reflect
| Use `recall()` when... | Use `reflect()` when... |
|------------------------|-------------------------|
| You need raw facts | You need reasoned interpretation |
| You're building your own reasoning | You want disposition-consistent responses |
| You need maximum control | You want the bank to "think" for itself |
| Simple fact lookup | Forming recommendations |
**Example:**
- `recall("Alice")` → Returns all Alice facts and relevant mental models
- `reflect("Should we hire Alice?")` → Agent gathers evidence about Alice, reasons about fit, returns answer with citations
---
## Disposition Traits
When you create a memory bank, you can configure its disposition using three traits. These traits influence how the bank interprets information and reasons during `reflect()`:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | 1-5 | Flexible interpretation, reads between the lines | Literal interpretation, takes things at face value |
| **Empathy** | 1-5 | Detached, focuses on facts | Empathetic, considers emotional context |
### Mission: Natural Language Identity
Beyond numeric traits, you can provide a natural language **mission** that describes the bank's identity and reasoning context:
<CodeSnippet code={memoryBanksPy} section="bank-with-disposition" language="python" />
The reflect mission frames how the agent reasons and responds:
- Provides identity context: who the agent is and what it cares about
- Shapes how disposition traits are applied in practice
- Keeps reasoning consistent across conversations
:::info Per-operation missions
The reflect mission only affects `reflect()`. To steer what gets extracted during `retain()`, use [`retain_mission`](/developer/api/memory-banks#retain-configuration). To control what gets synthesised into observations, use [`observations_mission`](/developer/api/memory-banks#observations-configuration).
:::
---
## Disposition Shapes Reasoning
Two banks with different dispositions, given identical facts about remote work:
**Bank A** (low skepticism, high empathy):
> "Remote work enables flexibility and work-life balance. The team seems happier and more productive when they can choose their environment."
**Bank B** (high skepticism, low empathy):
> "Remote work claims need verification. What are the actual productivity metrics? The anecdotal benefits may not translate to measurable outcomes."
**Same facts → Different conclusions** because disposition shapes interpretation.
---
## Disposition Presets by Use Case
Different use cases benefit from different disposition configurations:
| Use Case | Recommended Traits | Why |
|----------|-------------------|-----|
| **Customer Support** | skepticism: 2, literalism: 2, empathy: 5 | Trusting, flexible, understanding |
| **Code Review** | skepticism: 4, literalism: 5, empathy: 2 | Questions assumptions, precise, direct |
| **Legal Analysis** | skepticism: 5, literalism: 5, empathy: 2 | Highly skeptical, exact interpretation |
| **Therapist/Coach** | skepticism: 2, literalism: 2, empathy: 5 | Supportive, reads between lines |
| **Research Assistant** | skepticism: 4, literalism: 3, empathy: 3 | Questions claims, balanced interpretation |
---
## Directives: Hard Rules
While disposition traits *influence* reasoning style, **directives** are hard rules that the agent *must* follow. Directives are injected into the prompt and enforced in every response.
### When to Use Directives
Use directives for constraints that must never be violated:
- **Compliance rules**: "Never recommend specific stocks or financial products"
- **Privacy constraints**: "Never share personal data with third parties"
- **Style requirements**: "Always respond in formal English"
- **Domain guardrails**: "Always cite sources when making factual claims"
### Directives vs Disposition
| Aspect | Disposition | Directives |
|--------|-------------|------------|
| **Nature** | Soft influence | Hard rules |
| **Effect** | Shapes interpretation and tone | Must be followed exactly |
| **Violation** | Acceptable (it's a tendency) | Not acceptable |
| **Example** | High skepticism → questions claims | "Never make medical diagnoses" |
:::tip
Use disposition for personality and character. Use directives for compliance and guardrails.
:::
See [Memory Banks: Directives](/developer/api/memory-banks#directives) for how to create and manage directives.
---
## What You Get from Reflect
When you call `reflect()`:
**Returns:**
- **Response text** — Disposition-influenced answer from the agent
- **based_on** — Evidence used: memories, mental models, and directives that grounded the response
- **trace** — Tool calls, LLM calls, and observations accessed (when `include.tool_calls=True`)
- **structured_output** — Parsed response if `response_schema` was provided
- **usage** — Token usage metrics
**Example:**
```json
{
"text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
"based_on": {
"memories": [
{"id": "mem-123", "text": "Alice has 5 years of ML experience", "type": "world"},
{"id": "mem-456", "text": "Alice worked at Google on search ranking", "type": "experience"}
],
"mental_models": [],
"directives": [
{"id": "dir-001", "name": "Formal Language", "rules": ["Always respond in formal English"]}
]
},
"usage": {"input_tokens": 1500, "output_tokens": 500, "total_tokens": 2000}
}
```
The agent automatically gathers evidence, validates citations, and generates a grounded response.
---
## Why Disposition Matters
Without disposition, all AI assistants sound the same. With disposition:
- **Customer support bots** can be diplomatic and empathetic
- **Code review assistants** can be direct and thorough
- **Creative assistants** can be open to unconventional ideas
- **Risk analysts** can be appropriately cautious
Disposition creates **consistent character** across conversations while observations **evolve with evidence**.
---
## Next Steps
- [**Observations**](./observations) — How knowledge is consolidated
- [**Retain**](./retain) — How rich facts are stored
- [**Recall**](./retrieval) — How multi-strategy search works
- [**Reflect API**](./api/reflect) — Code examples and parameters
@@ -1,228 +0,0 @@
---
sidebar_position: 2
---
# Retain: How Hindsight Stores Memories
When you call `retain()`, Hindsight transforms conversations and documents into structured, searchable memories that preserve meaning and context.
## What Retain Does
```mermaid
graph LR
A[Your Content] --> B[Extract Facts]
B --> C[Identify Entities]
C --> D[Build Connections]
D --> E[Memory Bank]
```
---
## Rich Fact Extraction
Hindsight doesn't just store what was said — it captures **why**, **how**, and **what it means**.
### What Gets Captured
When you retain "Alice joined Google last spring and was thrilled about the research opportunities", Hindsight extracts:
**The core facts:**
- Alice joined Google
- This happened last spring
**The emotions and meaning:**
- She was thrilled
- It represented an important opportunity
**The reasoning:**
- She chose it for the research opportunities
This rich extraction means you can later ask "Why did Alice join Google?" and get a meaningful answer, not just "she joined Google."
### Preserving Context
Traditional systems fragment information:
- "Bob suggested Summer Vibes"
- "Alice wanted something unique"
- "They chose Beach Beats"
Hindsight preserves the full narrative:
- "Alice and Bob discussed naming their summer party playlist. Bob suggested 'Summer Vibes' because it's catchy, but Alice wanted something unique. They ultimately decided on 'Beach Beats' for its playful tone."
This means search results include the full context, not disconnected fragments.
---
## Two Types of Facts
Hindsight distinguishes between **world** facts (about others) and **experience** (conversations and events):
| Type | Description | Example |
|-----------------|-----------------------------------|---------|
| **world** | Facts about people, places, things | "Alice works at Google" |
| **experience** | Conversations and events | "I recommended Python to Alice" |
**Note:** Observations are consolidated automatically in the background after `retain()` operations complete. This consolidation process synthesizes patterns from new facts into the bank's knowledge base.
---
## Entity Recognition
Hindsight automatically identifies and tracks **entities** — the people, organizations, and concepts that matter.
### What Gets Recognized
- **People:** "Alice", "Dr. Smith", "Bob Chen"
- **Organizations:** "Google", "MIT", "OpenAI"
- **Places:** "Paris", "Central Park", "California"
- **Products & Concepts:** "Python", "TensorFlow", "machine learning"
### Entity Resolution
The same entity mentioned different ways gets unified:
- "Alice" + "Alice Chen" + "Alice C." → one person
- "Bob" + "Robert Chen" → one person (nickname resolution)
**Why it matters:** You can ask "What do I know about Alice?" and get everything, even if she was mentioned as "Alice Chen" in some conversations.
### Context-Aware Disambiguation
If "Alice" appears with "Google" and "Stanford" multiple times, a new "Alice" mentioning those is likely the same person. Hindsight uses co-occurrence patterns to disambiguate common names.
### Entity Labels
You can define a controlled vocabulary of `key:value` classification labels (e.g. `pedagogy:scaffolding`, `engagement:active`) that are extracted at retain time and stored as entities. Because labels become entities, they automatically link related memories in the knowledge graph and improve both semantic and keyword retrieval. Labels can optionally also write to the memory unit's tags, enabling standard tag-based filtering during recall and reflect.
See [entity_labels in the bank config](/developer/api/memory-banks#entity-labels) for full configuration details.
---
## Building Connections
Memories aren't isolated — Hindsight creates a **knowledge graph** with four types of connections:
### Entity Connections
All facts mentioning the same entity are linked together.
**Enables:** "Tell me everything about Alice" → retrieves all Alice-related facts
### Time-Based Connections
Facts close in time are connected, with stronger links for closer dates.
**Enables:** "What else happened around then?" → finds contextually related events
### Meaning-Based Connections
Semantically similar facts are linked, even if they use different words.
**Enables:** "Tell me about similar topics" → finds thematically related information
### Causal Connections
Cause-effect relationships are explicitly tracked.
**Enables:** "Why did this happen?" → trace reasoning chains
**Example:** "Alice felt burned out" ← caused by ← "She worked 80-hour weeks"
---
## Understanding Time
Hindsight tracks **two temporal dimensions**:
### When It Happened
For events (meetings, trips, milestones), Hindsight records when they occurred.
- "Alice got married in June 2024" → occurred in June 2024
For general facts (preferences, characteristics), there's no specific occurrence time.
- "Alice prefers Python" → ongoing preference
### When You Learned It
Hindsight also tracks when you told it each fact.
**Why both?**
Imagine in January 2025, someone tells you "Alice got married in June 2024":
- **Historical queries** work: "What did Alice do in 2024?" → finds the marriage
- **Recency ranking** works: Recent mentions get priority in search
- **Temporal reasoning** works: "What happened before her marriage?" → finds earlier events
Without this distinction, old information would either be unsearchable by date or treated as irrelevant.
---
## Tagging Memories
Tags enable visibility scoping—useful when one memory bank serves multiple users but each should only see relevant memories.
- **Item tags**: Tag individual memories with specific scopes
- **Document tags**: Apply tags to all items in a batch
- **Tag filtering**: Filter during recall/reflect by tags
See [Retain API](./api/retain) for code examples and [Recall API](./api/recall) for filtering options.
---
## What You Get
After `retain()` completes:
- **Structured facts** that preserve meaning, emotions, and reasoning
- **Unified entities** that resolve different name variations
- **Knowledge graph** with entity, temporal, semantic, and causal links
- **Temporal grounding** for both historical and recency-based queries
- **Optional tags** for filtering during recall
All stored in your isolated **memory bank**, ready for `recall()` and `reflect()`.
---
## Steering Extraction with a Mission
By default, `retain()` extracts all significant facts from the content. You can narrow this focus with a **retain mission** (`retain_mission`) — a plain-language description of what this bank should pay attention to.
```
e.g. Always include technical decisions, API design choices, and architectural trade-offs.
Ignore meeting logistics, greetings, and social exchanges.
```
The mission is injected into the extraction prompt alongside the built-in rules — it steers the LLM without replacing the extraction logic. It works with any extraction mode (`concise`, `verbose`, `custom`).
For finer control, you can also change the **extraction mode**:
| Mode | When to use |
|------|-------------|
| `concise` *(default)* | General-purpose — selective, fast |
| `verbose` | When you need richer facts with full context and relationships |
| `custom` | When you want to write your own extraction rules entirely |
Set `retain_mission` and `retain_extraction_mode` via the [bank config API](/developer/api/memory-banks#retain-configuration) or the [`HINDSIGHT_API_RETAIN_MISSION`](/developer/configuration#retain) environment variable.
---
## Observation Consolidation
After `retain()` completes, Hindsight automatically triggers **observation consolidation** in the background. This process:
1. Analyzes new facts against existing observations
2. Creates new observations when patterns emerge
3. Refines existing observations with new evidence
4. Tracks which facts support each observation
This happens asynchronously — your `retain()` call returns immediately while consolidation runs in the background.
See [Observations](./observations) for details on how consolidation works.
---
## Next Steps
- [**Observations**](./observations) — How knowledge is consolidated after retain
- [**Recall**](./retrieval) — How multi-strategy search retrieves relevant memories
- [**Reflect**](./reflect) — How the agentic loop uses observations
- [**Retain API**](./api/retain) — Code examples and parameters
@@ -1,226 +0,0 @@
---
sidebar_position: 3
---
# Recall: How Hindsight Retrieves Memories
When you call `recall()`, Hindsight uses multiple search strategies in parallel to find the most relevant memories, regardless of how you phrase your query.
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
---
## The Challenge of Memory Recall
Different queries need different search approaches:
- **"Alice works at Google"** → needs exact name matching
- **"Where does Alice work?"** → needs semantic understanding
- **"What did Alice do last spring?"** → needs temporal reasoning
- **"Why did Alice leave?"** → needs causal relationship tracing
No single search method handles all these well. Hindsight solves this with **TEMPR** — four complementary strategies that run in parallel.
---
## Four Search Strategies
### Semantic Search
**What it does:** Understands the *meaning* behind words, not just the words themselves.
**Best for:**
- Conceptual matches: "Alice's job" → "Alice works as a software engineer"
- Paraphrasing: "Bob's expertise" → "Bob specializes in machine learning"
- Synonyms: "meeting" matches "conference", "discussion", "gathering"
**Why it matters:** You can ask questions naturally without matching exact keywords.
---
### Keyword Search
**What it does:** Finds exact terms and names, even when they're spelled uniquely.
**Best for:**
- Proper nouns: "Google", "Alice Chen", "MIT"
- Technical terms: "PostgreSQL", "HNSW", "TensorFlow"
- Unique identifiers: URLs, product names, specific phrases
**Why it matters:** Ensures you never miss results that mention specific names or terms, even if they're semantically distant from your query.
---
### Graph Traversal
**What it does:** Follows connections between entities to find indirectly related information.
**Best for:**
- Indirect relationships: "What does Alice do?" → Alice → Google → Google's products
- Entity exploration: "Bob's colleagues" → Bob → co-workers → shared projects
- Multi-hop reasoning: "Alice's team's achievements"
**Why it matters:** Retrieves facts that aren't semantically or lexically similar but are **structurally connected** through the knowledge graph.
**Example:** Even if Alice and her manager are never mentioned together, graph traversal can find the manager through shared projects or team relationships.
---
### Temporal Search
**What it does:** Understands time expressions and filters by when events occurred.
**Best for:**
- Historical queries: "What did Alice do in 2023?"
- Time ranges: "What happened last spring?"
- Relative time: "What did Bob work on last year?"
- Before/after: "What happened before Alice joined Google?"
**How it works:** Combines semantic understanding with time filtering to find events within specific periods.
**Why it matters:** Enables precise historical queries without losing old information.
---
## Result Fusion
After the four strategies run, results are **fused together**:
- Memories appearing in **multiple strategies** rank higher (consensus)
- **Rank matters more than score** (robust across different scoring systems)
- Final results are **re-ranked** using a neural model that considers query-memory interaction
**Why fusion matters:** A fact that's both semantically similar AND mentions the right entity will rank higher than one that's only semantically similar.
---
## Why Multiple Strategies?
Consider the query: **"What did Alice say about Python last spring?"**
- **Semantic** finds facts about Alice's views on programming
- **Keyword** ensures "Python" is actually mentioned
- **Graph** connects Alice → programming languages → related entities
- **Temporal** filters to "last spring" timeframe
The **fusion** of all four gives you exactly what you're looking for, even though no single strategy would suffice.
---
## Token Budget Management
Hindsight is built for AI agents, not humans. Traditional search systems return "top-k" results, but agents don't think in terms of result counts—they think in tokens. An agent's context window is measured in tokens, and that's exactly how Hindsight measures results.
**How it works:**
- Top-ranked memories selected first
- Stops when token budget is exhausted
- You specify context budget, Hindsight fills it with the most relevant memories
**Parameters you control:**
- `max_tokens`: How much memory content to return (default: 4096 tokens)
- `budget`: Search depth level (low, mid, high)
- `types`: Filter by world, experience, observation, or all
- `tags`: Filter memories by visibility tags
- `tags_match`: How to match tags (see [Recall API](./api/recall) for all options)
### Expanding Context: Chunks
Memories are distilled facts—concise but sometimes missing nuance. When your agent needs deeper context, you can optionally retrieve the source material:
**Chunks** return the raw text that generated each memory—useful when the distilled fact loses important nuance:
```
Memory: "Alice prefers Python over JavaScript"
Chunk: "Alice mentioned she prefers Python over JavaScript, mainly because
of its data science ecosystem, though she admits JS is better for
frontend work and she's been learning TypeScript lately."
```
Use `include_chunks=True` with `max_chunk_tokens` to control the token budget for chunks. This is useful when generating responses that need verbatim quotes or when context matters (e.g., "What exactly did Alice say about the project?").
---
## Tuning Recall: Quality vs Latency
Different use cases require different trade-offs between **recall quality** and **response speed**. Two parameters control this:
### Budget: Search Depth
Controls how thoroughly Hindsight explores the memory bank—affecting graph traversal depth, candidate pool size, and cross-encoder re-ranking:
| Budget | Best For | Trade-off |
|--------|----------|-----------|
| **low** | Quick lookups, simple queries | Fast, may miss indirect connections |
| **mid** | Most queries, balanced | Good coverage, reasonable speed |
| **high** | Complex queries requiring deep exploration | Thorough, slower |
**Example:** "What did Alice's manager's team work on?" benefits from high budget to traverse multiple hops (Alice → manager → team → projects) and evaluate more candidates.
### Max Tokens: Context Window Size
Controls how much memory content to return:
| Max Tokens | ~Pages of Text | Best For | Trade-off |
|------------|----------------|----------|-----------|
| **2048** | ~2 pages | Focused answers, fast LLM | Fewer memories, faster |
| **4096** (default) | ~4 pages | Balanced context | Good coverage, standard |
| **8192** | ~8 pages | Comprehensive context | More memories, slower LLM |
**Example:** "Summarize everything about Alice" benefits from higher max_tokens to include more facts.
### Two Independent Dimensions
Budget and max_tokens control different aspects of recall:
| Parameter | What it controls | Latency impact | Example |
|-----------|------------------|----------------|---------|
| **Budget** | How thoroughly to explore memories | Search time | High budget finds Alice → manager → team → projects |
| **Max Tokens** | How much context to return | LLM processing time | High tokens returns more memories to the agent |
**They're independent.** Common combinations:
| Budget | Max Tokens | Use Case |
|--------|------------|----------|
| high | low | Deep search, return only the best results |
| low | high | Quick search, return everything found |
| high | high | Comprehensive research queries |
| low | low | Fast chatbot responses |
### Recommended Configurations
| Use Case | Budget | Max Tokens | Why |
|----------|--------|------------|-----|
| **Chatbot replies** | low | 2048 | Fast responses, focused context |
| **Document Q&A** | mid | 4096 | Balanced coverage and speed |
| **Research queries** | high | 8192 | Comprehensive, multi-hop reasoning |
| **Real-time search** | low | 2048 | Minimize latency |
---
## Graph Retrieval Algorithms
Hindsight supports multiple graph traversal algorithms. The default (`link_expansion`) is optimized for fast retrieval with target latency under 100ms.
See [Configuration → Retrieval](./configuration#retrieval) for available algorithms and how to configure them.
---
## Next Steps
- [**Retain**](./retain) — How memories are stored with rich context
- [**Reflect**](./reflect) — How disposition influences reasoning
- [**Recall API**](./api/recall) — Code examples, parameters, and tag filtering
@@ -1,66 +0,0 @@
# Services
Hindsight consists of three services that can run together or separately depending on your deployment needs.
## API Service
The core memory engine. Handles all memory operations:
- **Retain**: Ingests content, extracts facts, builds knowledge graph
- **Recall**: Semantic search across memories
- **Reflect**: Disposition-aware answer generation
```bash
hindsight-api # Default port: 8888
```
The API service is stateless and can be horizontally scaled behind a load balancer. All state is stored in PostgreSQL.
By default, the API also processes background tasks (mental model consolidation) internally. For high-throughput deployments, you can disable this and run dedicated workers instead.
## Worker Service
Dedicated task processor for background operations. Uses the **same package and Docker image** as the API service, just with a different entry point.
```bash
hindsight-worker # Default metrics port: 8889
```
Workers use PostgreSQL as a task broker, polling for pending tasks. Multiple workers can run simultaneously without conflicts.
| Deployment | Internal Worker | Dedicated Workers |
|------------|-----------------|-------------------|
| **Development** | ✅ Simple, all-in-one | ❌ Overkill |
| **Small production** | ✅ Less infrastructure | ❌ Overkill |
| **High throughput** | ❌ API bottleneck | ✅ Scale independently |
| **Long-running tasks** | ❌ Blocks API resources | ✅ Isolated processing |
To use dedicated workers, disable the internal worker in the API and start worker processes:
```bash
# Disable internal worker in API
HINDSIGHT_API_WORKER_ENABLED=false hindsight-api
# Start dedicated workers (run multiple instances)
hindsight-worker --worker-id worker-1
hindsight-worker --worker-id worker-2
```
Each worker exposes `/health` and `/metrics` endpoints for monitoring.
Before scaling down or removing workers, release their tasks with `hindsight-admin decommission-worker <worker-id>`.
See [Configuration - Distributed Workers](./configuration#distributed-workers) for all worker settings and [Installation - Helm](./installation#distributed-workers) for Kubernetes deployment.
## Control Plane
Web UI for managing and exploring your memory banks:
- Browse agents and memory banks
- Explore entities and relationships
- View ingestion history and operations
- Test recall queries interactively
The Control Plane connects to the API service and provides a visual interface for development and debugging.
For bare metal deployments, you can run the Control Plane standalone using npx. See [Installation - Bare Metal](./installation#control-plane) for details.
@@ -1,79 +0,0 @@
# Storage
Hindsight uses PostgreSQL as its sole storage backend.
## Why PostgreSQL?
PostgreSQL provides all capabilities required for a semantic memory system in a single database:
| Capability | Implementation |
|------------|----------------|
| Vector search | pgvector extension with HNSW indexes |
| Full-text search | Built-in tsvector with GIN indexes |
| Relational data | Native PostgreSQL |
| JSON documents | JSONB with indexing |
| Graph queries | Recursive CTEs |
### Reduced System Dependencies
Building exclusively for PostgreSQL simplifies deployment and operations:
- Single connection string to configure
- Single backup and restore strategy
- Single monitoring target
- ACID transactions across all data types
- Single upgrade path
### No Storage Abstraction
Hindsight does not abstract storage behind a generic interface. This is a deliberate trade-off.
We believe PostgreSQL is becoming the standard database API. Its popularity, extension ecosystem, and modularity mean that PostgreSQL-compatible interfaces are appearing everywhere—from serverless offerings to distributed databases. Building for PostgreSQL today means compatibility with a growing ecosystem tomorrow.
Supporting multiple databases would increase flexibility but conflict with our core goals: Hindsight is fully open source and designed to be as simple as possible to run and use. Adding database abstractions introduces complexity in code, testing, documentation, and operations—complexity that we pass on to users.
By committing to PostgreSQL, we keep the system simple:
- One set of deployment instructions
- One set of performance characteristics to understand
- One codebase optimized for one backend
- No configuration decisions about which database to use
## Development with pg0
For local development, Hindsight uses **[pg0](https://github.com/vectorize-io/pg0)**—an embedded PostgreSQL distribution.
### What is pg0?
pg0 is a single binary containing:
- PostgreSQL server
- pgvector extension (pre-installed)
- Automatic initialization
### Behavior
When no `DATABASE_URL` is configured, Hindsight:
1. Starts an embedded PostgreSQL instance on port 5555
2. Initializes the schema
3. Stores data in `~/.hindsight/pg0/`
### Environments
| Environment | Database | Configuration |
|-------------|----------|---------------|
| Development | pg0 (embedded) | Automatic |
| Production | PostgreSQL 15+ | `DATABASE_URL` environment variable |
## Requirements
- PostgreSQL 15 or later
- pgvector 0.5.0 or later
Any PostgreSQL instance that satisfies these requirements should work. If you encounter issues with a specific setup, [open a GitHub issue](https://github.com/hindsight-ai/hindsight/issues).
### Tested Managed Services
- AWS RDS (PostgreSQL 15+)
- Google Cloud SQL
- Azure Database for PostgreSQL
- Supabase
- Neon
@@ -1,244 +0,0 @@
---
sidebar_position: 4
---
# CLI Reference
The Hindsight CLI provides command-line access to memory operations and bank management. All commands follow the [OpenAPI specification](/api-reference), so you can use `--help` on any command to see all available options.
## Installation
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
## Configuration
Configure the API URL:
```bash
# Interactive configuration
hindsight configure
# Or set directly
hindsight configure --api-url http://localhost:8888
# With API key for authentication
hindsight configure --api-url http://localhost:8888 --api-key your-api-key
# Or use environment variables (highest priority)
export HINDSIGHT_API_URL=http://localhost:8888
export HINDSIGHT_API_KEY=your-api-key
```
## Core Commands
### Retain (Store Memory)
Store a single memory:
```bash
hindsight memory retain <bank_id> "Alice works at Google as a software engineer"
# With context
hindsight memory retain <bank_id> "Bob loves hiking" --context "hobby discussion"
# Queue for background processing
hindsight memory retain <bank_id> "Meeting notes" --async
```
### Retain Files
Bulk import from files:
```bash
# Single file
hindsight memory retain-files <bank_id> notes.txt
# Directory (recursive by default)
hindsight memory retain-files <bank_id> ./documents/
# With context
hindsight memory retain-files <bank_id> meeting-notes.txt --context "team meeting"
# Background processing
hindsight memory retain-files <bank_id> ./data/ --async
```
### Recall (Search)
Search memories using semantic similarity:
```bash
hindsight memory recall <bank_id> "What does Alice do?"
# With options
hindsight memory recall <bank_id> "hiking recommendations" \
--budget high \
--max-tokens 8192
# Filter by fact type
hindsight memory recall <bank_id> "query" --fact-type world,observation
# Show trace information
hindsight memory recall <bank_id> "query" --trace
```
### Reflect (Generate Response)
Generate a response using memories and bank disposition:
```bash
hindsight memory reflect <bank_id> "What do you know about Alice?"
# With additional context
hindsight memory reflect <bank_id> "Should I learn Python?" --context "career advice"
# Higher budget for complex questions
hindsight memory reflect <bank_id> "Summarize my week" --budget high
```
## Bank Management
### List Banks
```bash
hindsight bank list
```
### View Disposition
```bash
hindsight bank disposition <bank_id>
```
### View Statistics
```bash
hindsight bank stats <bank_id>
```
### Set Bank Name
```bash
hindsight bank name <bank_id> "My Assistant"
```
### Set Mission
```bash
hindsight bank mission <bank_id> "I am a helpful AI assistant interested in technology"
```
## Document Management
```bash
# List documents
hindsight document list <bank_id>
# Get document details
hindsight document get <bank_id> <document_id>
# Delete document and its memories
hindsight document delete <bank_id> <document_id>
```
## Entity Management
```bash
# List entities
hindsight entity list <bank_id>
# Get entity details
hindsight entity get <bank_id> <entity_id>
```
## Output Formats
```bash
# Pretty (default)
hindsight memory recall <bank_id> "query"
# JSON
hindsight memory recall <bank_id> "query" -o json
# YAML
hindsight memory recall <bank_id> "query" -o yaml
```
## Global Options
| Flag | Description |
|------|-------------|
| `-v, --verbose` | Show detailed output including request/response |
| `-o, --output <format>` | Output format: pretty, json, yaml |
| `--help` | Show help |
| `--version` | Show version |
## Control Plane UI
Launch the web-based Control Plane UI directly from the CLI:
```bash
hindsight ui
```
This runs the Control Plane locally on port 9999 using the API URL from your configuration. The UI provides:
- **Memory bank management** — Browse and manage all your banks
- **Entity explorer** — Visualize the knowledge graph
- **Query testing** — Interactive recall and reflect testing
- **Operation history** — View ingestion and processing logs
:::tip
The UI command requires Node.js to be installed. It automatically downloads and runs the `@vectorize-io/hindsight-control-plane` package via npx.
:::
## Interactive Explorer
Launch the TUI explorer for visual navigation of your memory banks:
```bash
hindsight explore
```
The explorer provides an interactive terminal interface to:
- **Browse memory banks** — View all banks and their statistics
- **Search memories** — Run recall queries with real-time results
- **Inspect entities** — Explore the knowledge graph and entity relationships
- **View facts** — Browse world facts, experiences, and observations
- **Navigate documents** — See source documents and their extracted memories
### Keyboard Shortcuts
| Key | Action |
|-----|--------|
| `↑/↓` | Navigate items |
| `Enter` | Select / Expand |
| `Tab` | Switch panels |
| `/` | Search |
| `q` | Quit |
<!-- Screenshot placeholder: explore command TUI -->
## Example Workflow
```bash
# Configure API URL
hindsight configure --api-url http://localhost:8888
# Store some memories
hindsight memory retain demo "Alice works at Google"
hindsight memory retain demo "Bob is a data scientist"
hindsight memory retain demo "Alice and Bob are colleagues"
# Search memories
hindsight memory recall demo "Who works with Alice?"
# Generate a response
hindsight memory reflect demo "What do you know about the team?"
# Check bank disposition
hindsight bank disposition demo
```
@@ -1,247 +0,0 @@
---
sidebar_position: 5
---
# Embedded SDK (hindsight-embed)
Zero-configuration local memory system with automatic daemon management. Perfect for development, prototyping, and single-user applications.
## Overview
`hindsight-embed` is a zero-configuration SDK that wraps the Hindsight API and PostgreSQL database into a single auto-managed local daemon. It's designed for development, prototyping, and single-user applications where you want memory capabilities without infrastructure overhead.
**How it works:**
1. **First command triggers startup**: When you run any `hindsight-embed` command, it checks if a local daemon is running
2. **Auto-daemon management**: If no daemon exists, it automatically spawns `hindsight-api --daemon` in the background
3. **Embedded database**: The daemon uses `pg0` (embedded PostgreSQL) — no separate database installation required
4. **Command forwarding**: Your command is forwarded to the local daemon via HTTP (localhost:8888)
5. **Auto-shutdown**: After 5 minutes of inactivity (configurable), the daemon gracefully shuts down to free resources
**Key features:**
- **Zero setup** — One `configure` command and you're ready
- **Automatic lifecycle** — Daemon starts on-demand, stops when idle
- **Isolated storage** — Each bank gets its own embedded PostgreSQL database
- **Local-only** — Binds to `127.0.0.1:8888`, not accessible from network
- **Production-grade engine** — Uses the same memory engine as the full API service
Think of it as SQLite for long-term memory — all the power of Hindsight without managing servers.
## Installation
Install via `uvx` (recommended - always latest version):
```bash
# Run directly without installation
uvx hindsight-embed@latest configure
# Or use pipx for persistent installation
pipx install hindsight-embed
```
## Quick Start
### 1. Configure
```bash
# Interactive configuration
hindsight-embed configure
# Or non-interactive via environment variables
export HINDSIGHT_EMBED_LLM_PROVIDER=openai
export HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=gpt-4o-mini
hindsight-embed configure
```
Configuration is saved to `~/.hindsight/embed`:
```bash
HINDSIGHT_EMBED_LLM_PROVIDER=openai
HINDSIGHT_EMBED_LLM_MODEL=gpt-4o-mini
HINDSIGHT_EMBED_BANK_ID=default
HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
# Daemon settings (macOS: force CPU to avoid MPS/XPC issues)
HINDSIGHT_API_EMBEDDINGS_LOCAL_FORCE_CPU=1
HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU=1
```
### 2. Use Memory Operations
```bash
# Store a memory
hindsight-embed memory retain default "User prefers dark mode"
# Query memories
hindsight-embed memory recall default "user preferences"
# Reasoning with memory
hindsight-embed memory reflect default "What color scheme should I use?"
```
The daemon starts automatically on first use!
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_EMBED_LLM_API_KEY` | **Required**. API key for LLM provider | - |
| `HINDSIGHT_EMBED_LLM_PROVIDER` | LLM provider: `openai`, `anthropic`, `gemini`, `groq`, `minimax`, `ollama` | `openai` |
| `HINDSIGHT_EMBED_LLM_MODEL` | Model name | `gpt-4o-mini` |
| `HINDSIGHT_EMBED_BANK_ID` | Default memory bank ID | `default` |
| `HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT` | Seconds before daemon auto-exits when idle (0 = never) | `0` |
**Provider Examples:**
```bash
# OpenAI
export HINDSIGHT_EMBED_LLM_PROVIDER=openai
export HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=gpt-4o
# Groq (fast inference)
export HINDSIGHT_EMBED_LLM_PROVIDER=groq
export HINDSIGHT_EMBED_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=llama-3.3-70b-versatile
# Anthropic
export HINDSIGHT_EMBED_LLM_PROVIDER=anthropic
export HINDSIGHT_EMBED_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=claude-sonnet-4-20250514
```
## Daemon Management
### Idle Timeout
Customize how long the daemon stays alive when idle:
```bash
# Never timeout (daemon runs until manually stopped)
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=0
# Shorter timeout: 1 minute
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=60
# Longer timeout: 30 minutes
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=1800
```
### Daemon Commands
```bash
# Check daemon status
hindsight-embed daemon status
# View daemon logs in real-time
hindsight-embed daemon logs -f
# Stop daemon manually
hindsight-embed daemon stop
```
## Commands
All memory operations follow the same interface as the CLI:
### Retain (Store Memory)
```bash
hindsight-embed memory retain <bank_id> "content"
# With context
hindsight-embed memory retain <bank_id> "content" --context "source information"
# Background processing
hindsight-embed memory retain <bank_id> "content" --async
```
### Recall (Search)
```bash
hindsight-embed memory recall <bank_id> "query"
# With budget control
hindsight-embed memory recall <bank_id> "query" --budget high
# Show trace
hindsight-embed memory recall <bank_id> "query" --trace
```
### Reflect (Generate Response)
```bash
hindsight-embed memory reflect <bank_id> "prompt"
# With additional context
hindsight-embed memory reflect <bank_id> "prompt" --context "additional info"
```
### Bank Management
```bash
# List all banks
hindsight-embed bank list
# View bank stats
hindsight-embed bank stats <bank_id>
# Set bank name
hindsight-embed bank name <bank_id> "My Assistant"
# Set bank mission
hindsight-embed bank mission <bank_id> "I am a helpful AI assistant"
```
## Troubleshooting
### Daemon Won't Start
Check the daemon logs:
```bash
hindsight-embed daemon logs
# Or watch in real-time
hindsight-embed daemon logs -f
```
Common issues:
- **Missing API key**: Set `HINDSIGHT_EMBED_LLM_API_KEY`
- **Port conflict**: Another service using port 8888
- **Permissions**: Check `~/.hindsight/` directory permissions
### Daemon Exits Immediately
Check if you have the idle timeout set too low:
```bash
# Disable idle timeout for debugging
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=0
hindsight-embed daemon status
```
### Reset Configuration
```bash
# Remove config file and reconfigure
rm ~/.hindsight/embed
hindsight-embed configure
```
## When to Use
**Perfect for:**
- Development and prototyping
- Single-user applications
- Local-first tools
- Quick experiments with Hindsight
**Not suitable for:**
- Production multi-user deployments
- Network-accessible services
- High-availability requirements
- Multi-tenant applications
For production deployments, use the [API Service](/developer/services) with external PostgreSQL instead.
@@ -1,51 +0,0 @@
---
sidebar_position: 3
---
# Go Client
Official Go client for the Hindsight API, generated from the OpenAPI 3.1 spec using [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator).
import CodeSnippet from '@site/src/components/CodeSnippet';
import quickstartGo from '!!raw-loader!@site/examples/api/quickstart.go';
## Installation
```bash
go get github.com/vectorize-io/hindsight/hindsight-clients/go
```
Requires Go 1.23+.
## Quick Start
<CodeSnippet code={quickstartGo} section="quickstart-full" language="go" />
## API Structure
The Go client provides access to all Hindsight API operations through structured namespaces:
- **`client.MemoryAPI`** - Retain, recall, reflect operations
- **`client.BanksAPI`** - Bank management
- **`client.DirectivesAPI`** - Directive management
- **`client.MentalModelsAPI`** - Mental model management
- **`client.DocumentsAPI`** - Document operations
- **`client.EntitiesAPI`** - Entity operations
- **`client.OperationsAPI`** - Async operation monitoring
## Working with Nullable Fields
The Go client uses `NullableString`, `NullableTime`, and similar types for optional fields:
<CodeSnippet code={quickstartGo} section="nullable-fields" language="go" />
## Error Handling
<CodeSnippet code={quickstartGo} section="error-handling" language="go" />
## More Examples
For detailed examples of all operations, see:
- [Python SDK documentation](./python.md) - API concepts are the same
- [Node.js SDK documentation](./nodejs.md) - API concepts are the same
- [OpenAPI specification](https://hindsight.dev/openapi.json) - Complete API reference
@@ -1,139 +0,0 @@
---
sidebar_position: 2
---
# TypeScript / JavaScript Client
Official TypeScript/JavaScript client for the Hindsight API. Supports **Node.js** and **Deno**.
## Installation
### Node.js
```bash
npm install @vectorize-io/hindsight-client
```
### Deno
No installation needed — import directly via the `npm:` specifier:
```typescript
import { HindsightClient } from "npm:@vectorize-io/hindsight-client";
```
## Quick Start
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Retain a memory
await client.retain('my-bank', 'Alice works at Google');
// Recall memories
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(r.text);
}
// Reflect - generate response with disposition
const answer = await client.reflect('my-bank', 'Tell me about Alice');
console.log(answer.text);
```
## Client Initialization
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({
baseUrl: 'http://localhost:8888',
});
```
## Core Operations
### Retain (Store Memory)
```typescript
// Simple
await client.retain('my-bank', 'Alice works at Google');
// With options
await client.retain('my-bank', 'Alice got promoted', {
timestamp: new Date('2024-01-15'),
context: 'career update',
metadata: { source: 'slack' },
async: false, // Set true for background processing
});
```
### Retain Batch
```typescript
await client.retainBatch('my-bank', [
{ content: 'Alice works at Google', context: 'career' },
{ content: 'Bob is a data scientist', context: 'career' },
], {
async: false,
});
```
### Recall (Search)
```typescript
// Simple - returns RecallResponse
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(`${r.text} (type: ${r.type})`);
}
// With options
const response = await client.recall('my-bank', 'What does Alice do?', {
types: ['world', 'observation'], // Filter by fact type
maxTokens: 4096,
budget: 'high', // 'low', 'mid', or 'high'
});
```
### Reflect (Generate Response)
```typescript
const answer = await client.reflect('my-bank', 'What should I know about Alice?', {
budget: 'low', // 'low', 'mid', or 'high'
context: 'preparing for a meeting',
});
console.log(answer.text); // Generated response
```
## Bank Management
### Create Bank
```typescript
await client.createBank('my-bank', {
name: 'Assistant',
mission: "You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition: {
skepticism: 3, // 1-5: trusting to skeptical
literalism: 3, // 1-5: flexible to literal
empathy: 3, // 1-5: detached to empathetic
},
});
```
### List Memories
```typescript
const response = await client.listMemories('my-bank', {
type: 'world', // Optional filter
q: 'Alice', // Optional text search
limit: 100,
offset: 0,
});
console.log(response)
```
@@ -1,361 +0,0 @@
---
sidebar_position: 1
---
# Python Client
Official Python client for the Hindsight API.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
## Installation
<Tabs>
<TabItem value="all-in-one" label="All-in-One (Recommended)">
The `hindsight-all` package includes embedded PostgreSQL, HTTP API server, and client:
```bash
pip install hindsight-all
```
</TabItem>
<TabItem value="client-only" label="Client Only">
If you already have a Hindsight server running:
```bash
pip install hindsight-client
```
</TabItem>
</Tabs>
## Quick Start
<Tabs>
<TabItem value="all-in-one" label="All-in-One">
```python
import os
from hindsight import HindsightServer, HindsightClient
with HindsightServer(
llm_provider="openai",
llm_model="gpt-4o-mini",
llm_api_key=os.environ["OPENAI_API_KEY"]
) as server:
client = HindsightClient(base_url=server.url)
# Retain a memory
client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r.text)
# Reflect - generate response with disposition
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
```
</TabItem>
<TabItem value="client-only" label="Client Only">
```python
from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888")
# Retain a memory
client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r.text)
# Reflect - generate response with disposition
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
```
</TabItem>
</Tabs>
## Embedded Client (Easiest Option)
`HindsightEmbedded` provides the simplest way to use Hindsight in Python. It automatically manages a background server for you - no manual setup required:
```python
from hindsight import HindsightEmbedded
import os
# Server starts automatically on first use
client = HindsightEmbedded(
profile="myapp", # Profile for data isolation
llm_provider="openai",
llm_model="gpt-4o-mini",
llm_api_key=os.environ["OPENAI_API_KEY"],
)
# Use immediately - no manual server management needed
client.retain(bank_id="my-bank", content="Alice works at Google")
results = client.recall(bank_id="my-bank", query="What does Alice do?")
# Server continues running (auto-stops after idle timeout)
# Or explicitly stop it:
client.close(stop_daemon=True)
```
**What's a Profile?**
A profile is an isolated Hindsight environment. Each profile gets its own PostgreSQL database (stored in `~/.pg0/instances/hindsight-embed-{profile}/`) and its own API server. Use different profiles to separate environments (dev/prod), applications, or users.
**When to Use HindsightEmbedded**
Use `HindsightEmbedded` when you want the server to start automatically and manage itself. Use `HindsightServer` when you need explicit control over server lifecycle (e.g., testing where you want immediate startup/shutdown).
**Advanced Operations**
`HindsightEmbedded` provides organized API namespaces for advanced operations. Each method call automatically ensures the daemon is running:
```python
from hindsight import HindsightEmbedded
import os
embedded = HindsightEmbedded(
profile="myapp",
llm_provider="openai",
llm_api_key=os.environ["OPENAI_API_KEY"],
)
# Core operations (automatically proxied)
embedded.retain(bank_id="test", content="Hello")
results = embedded.recall(bank_id="test", query="Hello")
# Bank management
embedded.banks.create(bank_id="test", name="Test Bank", mission="Help users")
embedded.banks.set_mission(bank_id="test", mission="Updated mission")
embedded.banks.delete(bank_id="test")
# Mental models
embedded.mental_models.create(
bank_id="test",
name="User Preferences",
content="User prefers dark mode"
)
models = embedded.mental_models.list(bank_id="test")
embedded.mental_models.update(bank_id="test", mental_model_id="...", content="New content")
# Directives
embedded.directives.create(
bank_id="test",
name="Response Style",
content="Be concise and friendly"
)
directives = embedded.directives.list(bank_id="test")
# List memories
memories = embedded.memories.list(bank_id="test", type="world", limit=50)
```
**Why Use API Namespaces?**
API namespaces (`banks`, `mental_models`, `directives`, `memories`) ensure the daemon is running before each call. This handles daemon crashes gracefully:
```python
# ✅ GOOD - Uses API namespace (daemon restarts handled)
embedded.banks.create(bank_id="test", name="Test")
# ❌ BAD - Direct client access (daemon crashes NOT handled)
client = embedded.client
client.create_bank(bank_id="test", name="Test") # Fails if daemon crashed
```
## Client Initialization
```python
from hindsight import HindsightClient
client = HindsightClient(
base_url="http://localhost:8888", # Hindsight API URL
timeout=30.0, # Request timeout in seconds
)
# Core operations
client.retain(bank_id="test", content="Hello world")
results = client.recall(bank_id="test", query="Hello")
# Organized API access (same as HindsightEmbedded)
client.banks.create(bank_id="test", name="Test Bank")
models = client.mental_models.list(bank_id="test")
directives = client.directives.list(bank_id="test")
memories = client.memories.list(bank_id="test")
```
Both `HindsightClient` and `HindsightEmbedded` provide the same organized API namespaces (`banks`, `mental_models`, `directives`, `memories`) for consistent developer experience.
## Core Operations
### Retain (Store Memory)
```python
# Simple
client.retain(
bank_id="my-bank",
content="Alice works at Google as a software engineer",
)
# With options
from datetime import datetime
client.retain(
bank_id="my-bank",
content="Alice got promoted",
context="career update",
timestamp=datetime(2024, 1, 15),
document_id="conversation_001",
metadata={"source": "slack"},
)
```
### Retain Batch
```python
client.retain_batch(
bank_id="my-bank",
items=[
{"content": "Alice works at Google", "context": "career"},
{"content": "Bob is a data scientist", "context": "career"},
],
document_id="conversation_001",
retain_async=False, # Set True for background processing
)
```
### Recall (Search)
```python
# Simple - returns list of RecallResult
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
)
for r in results.results:
print(f"{r.text} (type: {r.type})")
# With options
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "observation"], # Filter by fact type
max_tokens=4096,
budget="high", # low, mid, or high
)
```
### Recall with Chunks
```python
# Returns RecallResponse with source chunks
response = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "experience"],
budget="mid",
max_tokens=4096,
include_chunks=True,
max_chunk_tokens=500
)
print(f"Found {len(response.results)} memories")
for r in response.results:
print(f" - {r.text}")
if r.chunks:
print(f" Source: {r.chunks[0].text[:100]}...")
```
### Reflect (Generate Response)
```python
answer = client.reflect(
bank_id="my-bank",
query="What should I know about Alice?",
budget="low", # low, mid, or high
context="preparing for a meeting",
)
print(answer.text) # Generated response
```
## Bank Management
### Create Bank
```python
client.create_bank(
bank_id="my-bank",
name="Assistant",
mission="You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition={
"skepticism": 3, # 1-5: trusting to skeptical
"literalism": 3, # 1-5: flexible to literal
"empathy": 3, # 1-5: detached to empathetic
},
)
```
### List Memories
```python
client.list_memories(
bank_id="my-bank",
type="world", # Optional: filter by type
search_query="Alice", # Optional: text search
limit=100,
offset=0,
)
```
## Async Support
All methods have async versions prefixed with `a`:
```python
import asyncio
from hindsight_client import Hindsight
async def main():
client = Hindsight(base_url="http://localhost:8888")
# Async retain
await client.aretain(bank_id="my-bank", content="Hello world")
# Async recall
results = await client.arecall(bank_id="my-bank", query="Hello")
for r in results:
print(r.text)
# Async reflect
answer = await client.areflect(bank_id="my-bank", query="What did I say?")
print(answer.text)
client.close()
asyncio.run(main())
```
## Context Manager
```python
from hindsight_client import Hindsight
with Hindsight(base_url="http://localhost:8888") as client:
client.retain(bank_id="my-bank", content="Hello")
results = client.recall(bank_id="my-bank", query="Hello")
# Client automatically closed
```
@@ -1,262 +0,0 @@
# Admin CLI
The `hindsight-admin` CLI provides administrative commands for managing your Hindsight deployment, including database migrations, backup, and restore operations.
## Installation
The admin CLI is included with the `hindsight-api` package:
```bash
pip install hindsight-api
# or
uv add hindsight-api
```
## Commands
### run-db-migration
Run database migrations to the latest version. By default this migrates the base schema plus all tenant schemas discovered by the tenant extension. Use `--schema` for targeted migration of one schema. This is useful when you want to run migrations separately from API startup (e.g., in CI/CD pipelines or before deploying a new version).
```bash
hindsight-admin run-db-migration [OPTIONS]
```
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to run migrations on. If omitted, migrate the base schema plus all discovered tenant schemas. | All schemas |
**Examples:**
```bash
# Run migrations on the base schema plus all discovered tenant schemas
hindsight-admin run-db-migration
# Run migrations on a specific tenant schema
hindsight-admin run-db-migration --schema tenant_acme
```
:::tip Disabling Auto-Migrations
To disable automatic migrations on API startup, set `HINDSIGHT_API_RUN_MIGRATIONS_ON_STARTUP=false`. This is useful when you want to run migrations as a separate step in your deployment pipeline.
:::
---
### backup
Create a backup of all Hindsight data to a zip file.
```bash
hindsight-admin backup OUTPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `OUTPUT` | Output file path (will add `.zip` extension if not present) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to backup | `public` |
**Examples:**
```bash
# Backup to a file
hindsight-admin backup /backups/hindsight-2024-01-15.zip
# Backup a specific tenant schema
hindsight-admin backup /backups/tenant-acme.zip --schema tenant_acme
```
The backup includes:
- Memory banks and their configuration
- Documents and chunks
- Entities and their relationships
- Memory units (facts, experiences, observations)
- Entity cooccurrences and memory links
:::note Consistency
Backups are created within a database transaction with `REPEATABLE READ` isolation, ensuring a consistent snapshot across all tables.
:::
---
### restore
Restore data from a backup file. **Warning: This deletes all existing data in the target schema.**
```bash
hindsight-admin restore INPUT [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `INPUT` | Input backup file (.zip) |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema to restore to | `public` |
| `--yes`, `-y` | Skip confirmation prompt | `false` |
**Examples:**
```bash
# Restore with confirmation prompt
hindsight-admin restore /backups/hindsight-2024-01-15.zip
# Restore without confirmation (for scripts)
hindsight-admin restore /backups/hindsight-2024-01-15.zip --yes
# Restore to a specific tenant schema
hindsight-admin restore /backups/tenant-acme.zip --schema tenant_acme --yes
```
:::warning Data Loss
Restore will **delete all existing data** in the target schema before importing the backup. Always verify you have a recent backup before performing a restore.
:::
---
### decommission-worker
Release all tasks owned by a worker, resetting them from "processing" back to "pending" status so they can be picked up by other workers.
```bash
hindsight-admin decommission-worker WORKER_ID [OPTIONS]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| `WORKER_ID` | ID of the worker to decommission |
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema | `public` |
**Examples:**
```bash
# Before scaling down - release tasks from workers being removed
hindsight-admin decommission-worker hindsight-worker-4
hindsight-admin decommission-worker hindsight-worker-3
# Release tasks from a crashed worker
hindsight-admin decommission-worker worker-2
# For a specific tenant schema
hindsight-admin decommission-worker worker-1 --schema tenant_acme
```
**When to Use:**
- **Scaling down**: Before removing worker replicas in Kubernetes
- **Graceful removal**: When taking a worker offline for maintenance
- **Crash recovery**: If a worker crashed while processing tasks
- **Stuck worker**: When a worker is unresponsive
:::tip Finding Worker IDs
Worker IDs default to the hostname. In Kubernetes StatefulSets, this is the pod name (e.g., `hindsight-worker-0`). You can also set a custom ID with `HINDSIGHT_API_WORKER_ID` or `--worker-id`.
:::
### decommission-workers
Release all currently-processing tasks from every worker, resetting them from "processing" back to "pending" status. Use this when one or more workers have crashed or been removed without graceful shutdown and you don't know which worker IDs to target.
```bash
hindsight-admin decommission-workers [OPTIONS]
```
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema | `public` |
| `--yes`, `-y` | Skip confirmation prompt | `false` |
**Examples:**
```bash
# Release all processing tasks across all workers (with confirmation)
hindsight-admin decommission-workers
# Skip the confirmation prompt (useful in scripts)
hindsight-admin decommission-workers --yes
# Release tasks in a specific tenant schema
hindsight-admin decommission-workers --schema tenant_acme
```
**When to Use:**
- **Unknown dead workers**: Multiple workers crashed and you do not know their IDs
- **Fleet-wide recovery**: After an infrastructure event where many workers went down
- **"Just fix everything"**: A quick full-queue drain when per-worker cleanup is overkill
:::warning Disruptive
This releases **every** processing task regardless of worker, including tasks owned by healthy workers. Prefer `decommission-worker <WORKER_ID>` when you know which workers need cleanup.
:::
---
### worker-status
Show all currently-processing tasks grouped by worker, including operation type, bank, how long each task has been running, and when it was last updated. Useful for identifying orphaned tasks before decommissioning.
```bash
hindsight-admin worker-status [OPTIONS]
```
**Options:**
| Option | Description | Default |
|--------|-------------|---------|
| `--schema`, `-s` | Database schema | `public` |
**Examples:**
```bash
# Show all processing tasks across all workers
hindsight-admin worker-status
# Show processing tasks for a specific tenant schema
hindsight-admin worker-status --schema tenant_acme
```
**When to Use:**
- **Before decommissioning**: Inspect which workers have stale tasks and how long they have been stuck
- **Debugging throughput**: Diagnose why the queue is not draining (are tasks stuck in processing?)
- **Worker health check**: Spot workers whose `last_update_ago` keeps growing, indicating a dead or unresponsive worker
---
## Environment Variables
The admin CLI uses the same environment variables as the API service. The most important one is:
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | `pg0` (embedded) |
**Example:**
```bash
# Use a specific database
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
hindsight-admin backup /backups/mybackup.zip
```
@@ -1,251 +0,0 @@
---
sidebar_position: 9
---
# Bank Templates
Declarative JSON manifests for creating pre-configured memory banks with a single API call.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import templatesPy from '!!raw-loader!@site/examples/api/bank-templates.py';
import templatesMjs from '!!raw-loader!@site/examples/api/bank-templates.mjs';
import templatesSh from '!!raw-loader!@site/examples/api/bank-templates.sh';
import templatesGo from '!!raw-loader!@site/examples/api/bank-templates.go';
## Overview
A bank template is a JSON manifest that describes a bank's full setup: configuration overrides, mental models, directives, and more. Instead of making multiple API calls to configure a bank, you submit one manifest and the API provisions everything.
Templates are useful for:
- **Replication** — stamp out identically-configured banks for multiple users or agents
- **Onboarding** — new users start with a known-good configuration instead of configuring from scratch
- **Sharing** — distribute recommended setups as portable JSON files
- **Framework integrations** — ship a recommended template alongside your integration
Browse the [Bank Templates Hub](/templates) for ready-to-use templates.
## Manifest Schema
```json
{
"version": "1",
"bank": {
"reflect_mission": "...",
"retain_mission": "...",
"retain_extraction_mode": "concise | verbose | custom | chunks",
"retain_custom_instructions": "...",
"retain_chunk_size": 2048,
"disposition_skepticism": 3,
"disposition_literalism": 3,
"disposition_empathy": 3,
"enable_observations": true,
"observations_mission": "...",
"entity_labels": ["PERSON", "ORGANIZATION"],
"entities_allow_free_form": true
},
"mental_models": [
{
"id": "unique-lowercase-id",
"name": "Human-Readable Name",
"source_query": "The query that generates this mental model's content",
"tags": ["optional", "tags"],
"max_tokens": 2048,
"trigger": {
"refresh_after_consolidation": false,
"fact_types": ["world", "experience", "observation"],
"exclude_mental_models": false,
"exclude_mental_model_ids": []
}
}
],
"directives": [
{
"name": "directive-name",
"content": "The directive instruction text",
"priority": 0,
"is_active": true,
"tags": ["optional", "tags"]
}
]
}
```
### Fields
| Field | Required | Description |
|-------|----------|-------------|
| `version` | Yes | Schema version. Currently `"1"`. |
| `bank` | No | Bank configuration overrides. Omit to leave config unchanged. |
| `mental_models` | No | Mental models to create or update. Omit to leave unchanged. |
| `directives` | No | Directives to create or update. Omit to leave unchanged. |
All of `bank`, `mental_models`, and `directives` are optional. Omit any section to leave that part of the bank unchanged.
### Bank Config Fields
All fields in `bank` are optional. Only the fields you include will be set as per-bank overrides — everything else inherits from the server/tenant defaults.
| Field | Type | Description |
|-------|------|-------------|
| `reflect_mission` | string | Mission/context for reflect operations |
| `retain_mission` | string | Steers what gets extracted during retain |
| `retain_extraction_mode` | string | `concise`, `verbose`, `custom`, or `chunks` |
| `retain_custom_instructions` | string | Custom extraction prompt (requires `mode=custom`) |
| `retain_chunk_size` | integer | Max token size per content chunk |
| `disposition_skepticism` | integer (1-5) | How skeptical the disposition is |
| `disposition_literalism` | integer (1-5) | How literal the disposition is |
| `disposition_empathy` | integer (1-5) | How empathetic the disposition is |
| `enable_observations` | boolean | Toggle observation consolidation |
| `observations_mission` | string | Controls what gets synthesised into observations |
| `entity_labels` | string[] | Controlled vocabulary for entity labels |
| `entities_allow_free_form` | boolean | Allow entities outside the label vocabulary |
### Mental Model Fields
| Field | Required | Description |
|-------|----------|-------------|
| `id` | Yes | Unique ID (lowercase alphanumeric with hyphens). Used to match on re-import. |
| `name` | Yes | Human-readable name |
| `source_query` | Yes | The query that generates this model's content via reflect |
| `tags` | No | Tags for scoped visibility. Default: `[]` |
| `max_tokens` | No | Max tokens for generated content (256-8192). Default: `2048` |
| `trigger` | No | Trigger settings for auto-refresh |
### Directive Fields
| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Directive name. Used as the match key on re-import. |
| `content` | Yes | The directive instruction text. |
| `priority` | No | Priority value (higher = more important). Default: `0` |
| `is_active` | No | Whether the directive is active. Default: `true` |
| `tags` | No | Tags for categorization. Default: `[]` |
## Import
Import a manifest into a bank. If the bank doesn't exist, it's created automatically.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={templatesPy} section="import-template" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={templatesMjs} section="import-template" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={templatesSh} section="import-template" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={templatesGo} section="import-template" language="go" />
</TabItem>
</Tabs>
### Behavior
- **Config**: all `bank` fields are applied as per-bank config overrides
- **Mental models**: matched by `id` — existing models are updated, new ones are created
- **Directives**: matched by `name` — existing directives are updated, new ones are created
- **Async**: mental model content is generated asynchronously. The response includes `operation_ids` to track progress.
### Dry Run
Validate a manifest without applying changes:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={templatesPy} section="import-dry-run" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={templatesMjs} section="import-dry-run" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={templatesSh} section="import-dry-run" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={templatesGo} section="import-dry-run" language="go" />
</TabItem>
</Tabs>
Returns what *would* happen (which config would be applied, which mental models would be created) without making any changes. Returns HTTP 400 with a detailed error message if the manifest is invalid.
## Export
Export a bank's current config overrides, mental models, and directives as a manifest:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={templatesPy} section="export-template" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={templatesMjs} section="export-template" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={templatesSh} section="export-template" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={templatesGo} section="export-template" language="go" />
</TabItem>
</Tabs>
The exported manifest only includes config fields that were explicitly set as per-bank overrides — not the fully resolved config (which includes server/tenant defaults). This means the exported manifest is portable: importing it into a new bank only overrides the fields that were intentionally customized.
### Round-trip
Export from one bank and import into another to replicate the setup:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={templatesPy} section="export-reimport" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={templatesMjs} section="export-reimport" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={templatesSh} section="export-reimport" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={templatesGo} section="export-reimport" language="go" />
</TabItem>
</Tabs>
## JSON Schema
The manifest format is defined by a JSON Schema. Fetch the live schema from your server:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={templatesPy} section="get-schema" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={templatesMjs} section="get-schema" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={templatesSh} section="get-schema" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={templatesGo} section="get-schema" language="go" />
</TabItem>
</Tabs>
The static schema is also available at [bank-template-schema.json](/bank-template-schema.json).
## Control Plane
The control plane bank creation dialog includes an optional "Import from template" toggle. Enable it to paste a manifest JSON and pre-configure the bank on creation.
You can also export any bank's template from the bank Settings page via **Actions → Export Template**, which copies the manifest JSON to your clipboard.
## Versioning
The `version` field enables forward-compatible schema evolution. The current version is `"1"`.
When future versions are released:
- Older manifests are automatically upgraded to the current schema on import
- Export always produces the latest version
- The API rejects manifests with a version newer than what the server supports (with a clear error message suggesting an upgrade)
This means old templates keep working indefinitely — no need to manually update them.
@@ -1,248 +0,0 @@
---
sidebar_position: 8
---
# Documents
Track and manage document sources in your memory bank. Documents provide traceability — knowing where memories came from.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import documentsPy from '!!raw-loader!@site/examples/api/documents.py';
import documentsMjs from '!!raw-loader!@site/examples/api/documents.mjs';
import documentsGo from '!!raw-loader!@site/examples/api/documents.go';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## What Are Documents?
Documents are containers for retained content. They help you:
- **Track sources** — Know which PDF, conversation, or file a memory came from
- **Update content** — Re-retain a document to update its facts
- **Delete in bulk** — Remove all memories from a document at once
- **Organize memories** — Group related facts by source
## Chunks
When you retain content, Hindsight splits it into chunks before extracting facts. These chunks are stored alongside the extracted memories, preserving the original text segments.
**Why chunks matter:**
- **Context preservation** — Chunks contain the raw text that generated facts, useful when you need the exact wording
- **Richer recall** — Including chunks in recall provides surrounding context for matched facts
:::tip Include Chunks in Recall
Use `include_chunks=True` in your recall calls to get the original text chunks alongside fact results. See [Recall](./recall) for details.
:::
## Retain with Document ID
Associate retained content with a document:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Retain content with document ID
hindsight memory retain my-bank "Meeting notes content..." --doc-id notes-2024-03-15
# Batch retain from files
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-retain" language="go" />
</TabItem>
</Tabs>
## Update Documents
Re-retaining with the same document_id **replaces** the old content:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-update" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-update" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Original
hindsight memory retain my-bank "Project deadline: March 31" --doc-id project-plan
# Update
hindsight memory retain my-bank "Project deadline: April 15 (extended)" --doc-id project-plan
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
## Get Document
Retrieve a document's original text and metadata. This is useful for expanding document context after a recall operation returns memories with document references.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-get" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-get" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document get my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-get" language="go" />
</TabItem>
</Tabs>
## Update Document
Update mutable fields on an existing document without re-processing the content. Currently supports updating `tags`.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-update" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-update" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Replace tags with new values
hindsight document update-tags my-bank meeting-2024-03-15 --tags team-a --tags team-b
# Remove all tags
hindsight document update-tags my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
:::info Observations are re-consolidated
When tags change, any consolidated observations derived from the document's memories are invalidated and queued for re-consolidation under the new tags. Co-source memories from other documents that shared those observations are also reset.
:::
## Delete Document
Remove a document and all its associated memories:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-delete" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-delete" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight document delete my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-delete" language="go" />
</TabItem>
</Tabs>
:::warning
Deleting a document permanently removes all memories extracted from it. This action cannot be undone.
:::
## List Documents
List documents in a bank with optional filtering by ID and tags.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={documentsPy} section="document-list" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={documentsMjs} section="document-list" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# List all documents
hindsight document list my-bank
# Filter by ID substring
hindsight document list my-bank --q report
# Filter by tags
hindsight document list my-bank --tags team-a --tags team-b
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-list" language="go" />
</TabItem>
</Tabs>
### Filtering Options
| Parameter | Description |
|---|---|
| `q` | Case-insensitive substring match on document ID. `report` matches `report-2024`, `annual-report`, etc. |
| `tags` | Filter by document tags. Accepts multiple values. |
| `tags_match` | How to match tags (default: `any_strict`). See below. |
| `limit` / `offset` | Pagination. Default limit is 100. |
**`tags_match` modes:**
| Mode | Behaviour |
|---|---|
| `any_strict` *(default)* | Document must have **at least one** of the specified tags. Untagged docs excluded. |
| `any` | Same as `any_strict` but also includes untagged documents. |
| `all_strict` | Document must have **all** specified tags. Untagged docs excluded. |
| `all` | Same as `all_strict` but also includes untagged documents. |
## Document Response Format
```json
{
"id": "meeting-2024-03-15",
"bank_id": "my-bank",
"original_text": "Alice presented the Q4 roadmap...",
"content_hash": "abc123def456",
"memory_unit_count": 12,
"nodes_by_fact_type": {
"world": 5,
"experience": 4,
"observation": 3
},
"created_at": "2024-03-15T14:00:00Z",
"updated_at": "2024-03-15T14:00:00Z"
}
```
## Next Steps
- [**Operations**](./operations) — Monitor background tasks
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,148 +0,0 @@
---
sidebar_position: 2
---
# Main Methods
Hindsight provides three core operations: **retain**, **recall**, and **reflect**.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mainMethodsPy from '!!raw-loader!@site/examples/api/main-methods.py';
import mainMethodsMjs from '!!raw-loader!@site/examples/api/main-methods.mjs';
import mainMethodsGo from '!!raw-loader!@site/examples/api/main-methods.go';
:::tip Prerequisites
Make sure you've [installed Hindsight](../installation) and completed the [Quick Start](./quickstart).
:::
## Retain: Store Information
Store conversations, documents, and facts into a memory bank.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-retain" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-retain" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Store a single fact
hindsight memory retain my-bank "Alice joined Google in March 2024 as a Senior ML Engineer"
# Store from a file
hindsight memory retain-files my-bank conversation.txt --context "Daily standup"
# Store multiple files
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-retain" language="go" />
</TabItem>
</Tabs>
**What happens:** Content is processed by an LLM to extract rich facts, identify entities, and build connections in a knowledge graph.
**See:** [Retain Details](./retain) for advanced options and parameters.
---
## Recall: Search Memories
Search for relevant memories using multi-strategy retrieval.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-recall" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-recall" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic search
hindsight memory recall my-bank "What does Alice do at Google?"
# Search with options
hindsight memory recall my-bank "What happened last spring?" \
--budget high \
--max-tokens 8192 \
--fact-type world,experience
# Verbose output
hindsight memory recall my-bank "Tell me about Alice" -v
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-recall" language="go" />
</TabItem>
</Tabs>
**What happens:** Four search strategies (semantic, keyword, graph, temporal) run in parallel, results are fused and reranked.
**See:** [Recall Details](./recall) for tuning quality vs latency.
---
## Reflect: Reason with Disposition
Generate disposition-aware responses using memories and observations.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mainMethodsPy} section="main-reflect" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mainMethodsMjs} section="main-reflect" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Basic reflect
hindsight memory reflect my-bank "Should we adopt TypeScript for our backend?"
# With higher reasoning budget
hindsight memory reflect my-bank "Analyze our tech stack" --budget high
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-reflect" language="go" />
</TabItem>
</Tabs>
**What happens:** Memories and observations are recalled, bank disposition is applied, and the LLM reasons through the evidence to generate a response.
**See:** [Reflect Details](./reflect) for disposition configuration.
---
## Comparison
| Feature | Retain | Recall | Reflect |
|---------|--------|--------|---------|
| **Purpose** | Store information | Find information | Reason about information |
| **Input** | Raw text/documents | Search query | Question/prompt |
| **Output** | Memory IDs | Ranked facts + observations | Reasoned response |
| **Uses LLM** | Yes (extraction) | No | Yes (generation) |
| **Uses observations** | No | Yes | Yes |
| **Disposition** | No | No | Yes |
---
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Tuning search quality and performance
- [**Reflect**](./reflect) — Configuring disposition
- [**Memory Banks**](./memory-banks) — Managing memory bank disposition
@@ -1,467 +0,0 @@
---
sidebar_position: 6
---
# Memory Banks
Memory banks are isolated containers that store all memory-related data for a specific context or use case.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
import memoryBanksMjs from '!!raw-loader!@site/examples/api/memory-banks.mjs';
import memoryBanksSh from '!!raw-loader!@site/examples/api/memory-banks.sh';
import memoryBanksGo from '!!raw-loader!@site/examples/api/memory-banks.go';
import directivesPy from '!!raw-loader!@site/examples/api/directives.py';
import directivesMjs from '!!raw-loader!@site/examples/api/directives.mjs';
import directivesSh from '!!raw-loader!@site/examples/api/directives.sh';
import directivesGo from '!!raw-loader!@site/examples/api/directives.go';
## What is a Memory Bank?
A memory bank is a complete, isolated storage unit containing:
- **Memories** — Facts and information retained from conversations
- **Documents** — Files and content indexed for retrieval
- **Entities** — People, places, concepts extracted from memories
- **Relationships** — Connections between entities in the knowledge graph
- **Directives** — Hard rules the agent must follow during reflect operations
Banks are completely isolated from each other — memories stored in one bank are not visible to another.
You don't need to pre-create a bank. Hindsight will automatically create it with default settings when you first use it.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Creating a Memory Bank
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="create-bank" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="create-bank" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="create-bank" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="create-bank" language="go" />
</TabItem>
</Tabs>
## Bank Configuration
Each memory bank can be configured independently per operation. Configuration can be set via the [bank config API](#updating-configuration), the [Control Plane UI](/), or [server-wide environment variables](/developer/configuration).
### retain_mission {#retain-configuration}
A plain-language description of what this bank should pay attention to during extraction. The mission is injected into the extraction prompt alongside the built-in rules — it steers focus without replacing the extraction logic.
```
e.g. Always include technical decisions, API design choices, and architectural trade-offs.
Ignore meeting logistics, greetings, and social exchanges.
```
Works alongside any extraction mode. Leave blank for general-purpose extraction.
### retain_extraction_mode
Controls how aggressively facts are extracted:
| Mode | Description |
|------|-------------|
| `concise` *(default)* | Selective — only facts worth remembering long-term |
| `verbose` | Captures more detail per fact; slower and uses more tokens |
| `custom` | Write your own extraction rules via `retain_custom_instructions` |
### retain_custom_instructions
Only active when `retain_extraction_mode` is `custom`. Replaces the built-in extraction rules entirely with your own instructions.
### retain_chunk_size
Maximum number of characters per chunk when splitting content for fact extraction. Larger chunks mean fewer LLM calls but may reduce extraction quality on long inputs; smaller chunks improve granularity at the cost of more calls.
Default: `3000`
See [Retain configuration](/developer/configuration#retain) for environment variable names and defaults.
### entity_labels {#entity-labels}
Defines a controlled vocabulary of `key:value` classification labels extracted at retain time and stored as entities. Because labels become entities, they automatically link memories in the knowledge graph (two memories with `pedagogy:scaffolding` are linked), improve semantic and BM25 retrieval, and optionally filter memories via the standard `tags`/`tags_match` API when `tag: true` is set on a group.
Each entry in `entity_labels` is a **label group** — one classification dimension:
```json
{
"entity_labels": [
{
"key": "engagement",
"description": "Student engagement level during the session",
"type": "value",
"optional": true,
"values": [
{ "value": "active", "description": "Student is actively participating" },
{ "value": "passive", "description": "Student is listening but not participating" }
]
},
{
"key": "pedagogy",
"description": "Teaching strategies used",
"type": "multi-values",
"values": [
{ "value": "scaffolding", "description": "Breaking complex tasks into smaller steps" },
{ "value": "direct_instruction", "description": "Explicit explanation by the teacher" },
{ "value": "socratic_questioning", "description": "Guiding through questions rather than answers" }
]
}
]
}
```
| Field | Default | Description |
|-------|---------|-------------|
| `key` | — | Label group identifier. Becomes the prefix in `key:value` entities. |
| `description` | `""` | Shown to the LLM to guide label assignment. |
| `type` | `"value"` | `"value"` → pick one enum value; `"multi-values"` → pick multiple; `"text"` → free-form string. |
| `values` | `[]` | Allowed values for `"value"` and `"multi-values"` types. Ignored for `"text"`. |
| `optional` | `true` | When `true` the LLM may skip the label if not applicable. When `false` the LLM must always assign a value. Has no effect on `"multi-values"` groups (always optional). |
| `tag` | `false` | When `true`, extracted `key:value` labels are also written as tags on the memory unit, enabling filtering via `tags`/`tags_match` in recall/reflect. |
**Enum groups** (`type: "value"` or `type: "multi-values"`): the LLM picks from the predefined `values` list; anything outside the list is silently dropped. Vocabulary stays stable and graph links stay tight. Use `"multi-values"` when a fact can belong to several values at once.
**Free-text groups** (`type: "text"`): the LLM writes any string. Use the `description` field to provide examples and guidance. Graph clustering is less reliable than with enum groups because the model may phrase the same concept differently across sessions.
```json
{
"key": "topic",
"description": "Specific subject being discussed. Examples: algebra, quadratic equations, geometry.",
"type": "text",
"optional": true,
"values": []
}
```
### entities_allow_free_form
By default, entity labels are extracted **alongside** regular named entities (people, places, concepts). Set to `false` to disable free-form extraction so only label entities are stored:
```json
{
"entity_labels": [...],
"entities_allow_free_form": false
}
```
### enable_observations {#observations-configuration}
Toggles automatic observation consolidation on or off. Defaults to `true` when the observations feature is enabled on the server.
### observations_mission
Defines what this bank should synthesise into durable observations. Replaces the built-in consolidation rules entirely — leave blank to use the server default.
```
e.g. Observations are stable facts about people and projects.
Always include preferences, skills, and recurring patterns.
Ignore one-off events and ephemeral state.
```
### consolidation_llm_batch_size
Number of facts sent to the LLM in a single consolidation call. Higher values reduce LLM calls and improve throughput at the cost of larger prompts. Set to `1` to disable batching. Leave unset to use the server default (`8`).
### consolidation_source_facts_max_tokens
Total token budget for source facts included with observations in the consolidation prompt. Source facts give the LLM evidence to compare new facts against existing observations. `-1` = unlimited. Leave unset to use the server default (`-1`).
### consolidation_source_facts_max_tokens_per_observation
Per-observation token cap for source facts in the consolidation prompt. Each observation independently gets at most this many tokens of source facts, preventing a single observation with many source facts from consuming the entire budget. `-1` = unlimited. Leave unset to use the server default (`256`).
See [Observations configuration](/developer/configuration#observations) for environment variable names and defaults.
### reflect_mission
A first-person narrative that provides identity and framing context for `reflect`. The agent uses this to ground its reasoning and apply a consistent perspective.
```
e.g. You are a senior engineering assistant.
Always ground answers in documented decisions and rationale.
Ignore speculation. Be direct and precise.
```
### disposition_skepticism
How skeptical vs trusting the bank is when evaluating claims during `reflect`. Scale 15.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="bank-with-disposition" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="bank-with-disposition" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="bank-with-disposition" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="bank-with-disposition" language="go" />
</TabItem>
</Tabs>
| Value | Behaviour |
|-------|-----------|
| `1` | Trusting — accepts information at face value |
| `3` *(default)* | Balanced |
| `5` | Skeptical — questions and doubts claims |
### disposition_literalism
How literally to interpret information during `reflect`. Scale 15.
| Value | Behaviour |
|-------|-----------|
| `1` | Flexible — reads between the lines, considers context |
| `3` *(default)* | Balanced |
| `5` | Literal — takes things exactly as stated |
### disposition_empathy
How much to weight emotional context when reasoning during `reflect`. Scale 15.
| Value | Behaviour |
|-------|-----------|
| `1` | Detached — focuses on facts and logic |
| `3` *(default)* | Balanced |
| `5` | Empathetic — considers emotional context |
:::info
Disposition traits and `reflect_mission` only affect the `reflect` operation. `retain_mission` and `observations_mission` are separate per-operation settings.
:::
### mcp_enabled_tools
An allowlist of MCP tool names that are enabled for this bank. When set, only the listed tools can be invoked; any tool not in the list returns an error (tools still appear in the MCP tools list for protocol compatibility). Set to `null` (or omit) to allow all tools.
```json
["recall", "reflect"]
```
Available tool names: `retain`, `recall`, `reflect`, `list_banks`, `create_bank`, `list_mental_models`, `get_mental_model`, `create_mental_model`, `update_mental_model`, `delete_mental_model`, `refresh_mental_model`, `list_directives`, `create_directive`, `delete_directive`, `list_memories`, `get_memory`, `list_documents`, `get_document`, `delete_document`, `list_operations`, `get_operation`, `cancel_operation`, `list_tags`, `get_bank`, `get_bank_stats`, `update_bank`, `delete_bank`, `clear_memories`.
### llm_gemini_safety_settings
Controls content filtering thresholds for Gemini and VertexAI providers. Accepts a list of safety setting objects in the [Google AI safety settings format](https://ai.google.dev/api/generate-content#v1beta.SafetySetting). When `null` (default), Gemini's built-in safety defaults are used.
```json
[
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}
]
```
Only applies when `HINDSIGHT_API_LLM_PROVIDER` is `gemini` or `vertexai`.
### recall_budget_function {#recall-budget-configuration}
Selects how the [`recall` request's `budget` parameter](./recall) (`low` / `mid` / `high`) maps to the internal `thinking_budget` integer used by every retrieval method (semantic, BM25, graph, temporal). Two functions are supported:
| Function | Behaviour |
|----------|-----------|
| `fixed` *(default)* | `thinking_budget = recall_budget_fixed_<level>` — independent of `max_tokens`. Preserves legacy behavior. |
| `adaptive` | `thinking_budget = round(max_tokens * recall_budget_adaptive_<level>)`, clamped to `[recall_budget_min, recall_budget_max]`. Retrieval breadth scales with the requested output size. |
```json
{
"recall_budget_function": "adaptive",
"recall_budget_adaptive_low": 0.05,
"recall_budget_adaptive_mid": 0.1,
"recall_budget_adaptive_high": 0.3,
"recall_budget_min": 30,
"recall_budget_max": 1500
}
```
### recall_budget_fixed_low / recall_budget_fixed_mid / recall_budget_fixed_high
When `recall_budget_function` is `fixed` (the default), these positive integers are used directly as the per-method retrieval limit for each `budget` level. Defaults: `100` / `300` / `1000` — exactly matching the legacy hardcoded mapping.
### recall_budget_adaptive_low / recall_budget_adaptive_mid / recall_budget_adaptive_high
When `recall_budget_function` is `adaptive`, these positive ratios multiply the request's `max_tokens` to derive the per-method retrieval limit. Defaults: `0.025` / `0.075` / `0.25` — chosen to roughly match the fixed defaults at `max_tokens = 4096`.
### recall_budget_min / recall_budget_max
Floor and ceiling applied to the result of the adaptive function (after the ratio multiplication). Both must be positive integers and `min ≤ max`. Defaults: `20` / `2000`.
See [Recall budget mapping](/developer/configuration#recall-budget-mapping) for environment variable names and full defaults.
---
## Updating Configuration
Bank configuration fields (retain mission, extraction mode, observations mission, etc.) are managed via a **separate config API**, not the `create_bank` call. This lets you change operational settings independently from the bank's identity and disposition.
### Setting Configuration Overrides
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="update-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="update-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="update-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="update-bank-config" language="go" />
</TabItem>
</Tabs>
You can update any subset of fields — only the keys you provide are changed.
### Reading the Current Configuration
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="get-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="get-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="get-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="get-bank-config" language="go" />
</TabItem>
</Tabs>
The response distinguishes:
- **`config`** — the fully resolved configuration (server defaults merged with bank overrides)
- **`overrides`** — only the fields explicitly overridden for this bank
### Resetting to Defaults
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={memoryBanksPy} section="reset-bank-config" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="reset-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="reset-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="reset-bank-config" language="go" />
</TabItem>
</Tabs>
This removes all bank-level overrides. The bank reverts to server-wide defaults (set via environment variables).
You can also update configuration directly from the [Control Plane UI](/) — navigate to a bank and open the **Configuration** tab.
---
## Directives
Directives are hard rules that the agent must follow during [reflect](./reflect) operations. Unlike disposition traits which influence *how* the agent reasons, directives are explicit instructions that are *always* enforced.
:::info
Directives only affect the `reflect` operation. They are injected into prompts and the agent is required to comply with them in all responses.
:::
### When to Use Directives
Use directives for rules that must never be violated:
- **Language/style constraints**: "Always respond in formal English"
- **Privacy rules**: "Never share personal data with third parties"
- **Domain constraints**: "Prefer conservative investment recommendations"
- **Behavioral guardrails**: "Always cite sources when making claims"
### Creating Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="create-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="create-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="create-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="create-directive" language="go" />
</TabItem>
</Tabs>
### Listing Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="list-directives" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="list-directives" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="list-directives" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="list-directives" language="go" />
</TabItem>
</Tabs>
### Updating Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="update-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="update-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="update-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="update-directive" language="go" />
</TabItem>
</Tabs>
### Deleting Directives
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={directivesPy} section="delete-directive" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="delete-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="delete-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="delete-directive" language="go" />
</TabItem>
</Tabs>
### Directives vs Disposition
| Aspect | Directives | Disposition |
|--------|------------|-------------|
| **Nature** | Hard rules, must be followed | Soft influence on reasoning style |
| **Enforcement** | Strict — responses are rejected if violated | Flexible — shapes interpretation |
| **Use case** | Compliance, guardrails, constraints | Personality, character, tone |
| **Example** | "Never recommend specific stocks" | High skepticism: questions claims |
@@ -1,418 +0,0 @@
---
sidebar_position: 4
---
# Mental Models
User-curated summaries that provide high-quality, pre-computed answers for common queries.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mentalModelsPy from '!!raw-loader!@site/examples/api/mental-models.py';
import mentalModelsMjs from '!!raw-loader!@site/examples/api/mental-models.mjs';
import mentalModelsSh from '!!raw-loader!@site/examples/api/mental-models.sh';
import mentalModelsGo from '!!raw-loader!@site/examples/api/mental-models.go';
## What Are Mental Models?
Mental models are **saved reflect responses** that you curate for your memory bank. When you create a mental model, Hindsight runs a reflect operation with your source query and stores the result. During future reflect calls, these pre-computed summaries are checked first — providing faster, more consistent answers.
```mermaid
graph LR
A[Create Mental Model] --> B[Run Reflect]
B --> C[Store Result]
C --> D[Future Queries]
D --> E{Match Found?}
E -->|Yes| F[Return Mental Model]
E -->|No| G[Run Full Reflect]
```
### Why Use Mental Models?
| Benefit | Description |
|---------|-------------|
| **Consistency** | Same answer every time for common questions |
| **Speed** | Pre-computed responses are returned instantly |
| **Quality** | Manually curated summaries you've reviewed |
| **Control** | Define exactly how key topics should be answered |
### Hierarchical Retrieval
During reflect, the agent checks sources in priority order:
1. **Mental Models** — User-curated summaries (highest priority)
2. **Observations** — Consolidated knowledge
3. **Raw Facts** — Ground truth memories
Mental models are checked first because they represent your explicitly curated knowledge.
---
## Create a Mental Model
Creating a mental model runs a reflect operation in the background and saves the result:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model" language="go" />
</TabItem>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query to run to generate content |
| `id` | string | No | Custom ID for the mental model (alphanumeric lowercase with hyphens). Auto-generated if omitted. |
| `tags` | list | No | Tags that scope the model during reflect **and** filter source memories during refresh. Defaults to `all_strict` matching, so only memories carrying every listed tag are read. See [Tags and Visibility](#tags-and-visibility). |
| `max_tokens` | int | No | Maximum tokens for the mental model content |
| `trigger` | object | No | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
---
## Create with Custom ID
Assign a stable, human-readable ID to a mental model so you can retrieve or update it by name instead of relying on the auto-generated UUID:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-id" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-id" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-id" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-id" language="go" />
</TabItem>
</Tabs>
:::tip
Custom IDs must be lowercase alphanumeric and may contain hyphens (e.g. `team-policies`, `q4-status`). If a mental model with that ID already exists, the request is rejected.
:::
---
## Automatic Refresh
Mental models can be configured to **automatically refresh** when observations are updated. This keeps them in sync with the latest knowledge without manual intervention.
### Trigger Settings
| Setting | Type | Default | Description |
|---------|------|---------|-------------|
| `mode` | `"full"` \| `"delta"` | `"full"` | Refresh strategy. See [Refresh Mode](#refresh-mode) below. |
| `refresh_after_consolidation` | bool | false | Automatically refresh after observations consolidation |
When `refresh_after_consolidation` is enabled, the mental model will be re-generated every time the bank's observations are consolidated — ensuring it always reflects the latest synthesized knowledge.
### Refresh Mode
Two strategies are available for how a refresh produces the new content:
- **`full`** *(default)* — every refresh regenerates the entire content from scratch. Simple and predictable: the LLM synthesises a fresh document from the retrieved memories. Best when the document is short, when you want every refresh to potentially restructure the output, or when you're not yet sure what the final shape should be.
- **`delta`** — refresh emits a list of typed *operations* (add a section, append a bullet, replace a block, remove a stale paragraph) against the document's existing structure, then renders the result. Sections that aren't targeted by any operation are copied through **byte-identical** — no paraphrasing, no whitespace drift, no list-style normalisation. Best for long-lived "playbook"style mental models where you want stability across refreshes and only the genuinely changed parts to move.
Delta mode falls back to a full regeneration automatically in two cases:
1. The mental model has no existing content yet (nothing to anchor edits on).
2. The `source_query` has changed since the last refresh (the topic has shifted; the existing structure may no longer apply).
If the LLM call fails or returns an empty answer, the existing content is preserved — refreshes never overwrite a populated document with an empty one.
| Use Case | Recommended Mode | Why |
|----------|-----------------|-----|
| Skill / playbook docs | `delta` | Sections live for many refreshes; only specific rules change |
| Onboarding summaries | `delta` | Adding new team members shouldn't restructure the doc |
| Real-time dashboards | `full` | Each refresh is a fresh snapshot |
| Short FAQ summaries | `full` | Whole-document regeneration is cheap and unambiguous |
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-trigger" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-trigger" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-trigger" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-trigger" language="go" />
</TabItem>
</Tabs>
### When to Use Automatic Refresh
| Use Case | Automatic Refresh | Why |
|----------|-------------------|-----|
| **Real-time dashboards** | ✅ Enabled | Status should always be current |
| **Policy summaries** | ❌ Disabled | Policies change infrequently, manual refresh preferred |
| **User preferences** | ✅ Enabled | Preferences evolve with new interactions |
| **FAQ answers** | ❌ Disabled | Answers are curated, should be reviewed before updating |
:::tip
Enable automatic refresh for mental models that need to stay current. Disable it for curated content where you want to review changes before they go live.
:::
---
## List Mental Models
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="list-mental-models" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="list-mental-models" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="list-mental-models" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="list-mental-models" language="go" />
</TabItem>
</Tabs>
---
## Get a Mental Model
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="get-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model" language="go" />
</TabItem>
</Tabs>
### Detail Levels
Both **List** and **Get** endpoints accept an optional `detail` query parameter that controls how much data is returned. This is useful for reducing response size, especially in agent boot flows or MCP clients where context budget is limited.
| Level | Fields Returned | Use Case |
|-------|----------------|----------|
| `metadata` | `id`, `bank_id`, `name`, `tags`, `last_refreshed_at`, `created_at` | Inventory — "what models exist?" |
| `content` | All metadata fields + `source_query`, `content`, `max_tokens`, `trigger` | Agent boot — "what do the models say?" |
| `full` (default) | All fields including `reflect_response` | Deep inspection — "what evidence backs this model?" |
```bash
# List only names and tags (smallest response)
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models?detail=metadata"
# List with content but without provenance chains
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models?detail=content"
# Get full detail (default behavior)
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models/$MODEL_ID?detail=full"
```
The `detail` parameter is also available in the MCP tools:
```json
{"bank_id": "my-bank", "detail": "metadata"}
```
:::tip
Use `detail=content` for agent orientation flows. It includes everything the agent needs to understand the models without the heavyweight `reflect_response` provenance chains, which can exceed 200KB for banks with many models.
:::
### Response Fields
| Field | Type | Detail Level | Description |
|-------|------|-------------|-------------|
| `id` | string | metadata | Unique mental model ID |
| `bank_id` | string | metadata | Memory bank ID |
| `name` | string | metadata | Human-readable name |
| `tags` | list | metadata | Tags for filtering |
| `last_refreshed_at` | string | metadata | When the mental model was last updated |
| `created_at` | string | metadata | When the mental model was created |
| `source_query` | string | content | The query used to generate content |
| `content` | string | content | The generated mental model text |
| `max_tokens` | int | content | Maximum tokens for the mental model content |
| `trigger` | object | content | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
| `reflect_response` | object | full | Full reflect response including `based_on` provenance facts |
---
## Refresh a Mental Model
Re-run the source query to update the mental model with current knowledge:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="refresh-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="refresh-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="refresh-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="refresh-mental-model" language="go" />
</TabItem>
</Tabs>
Refreshing is useful when:
- New memories have been retained that affect the topic
- Observations have been updated
- You want to ensure the mental model reflects current knowledge
---
## Update a Mental Model
Update the mental model's name:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="update-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="update-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="update-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="update-mental-model" language="go" />
</TabItem>
</Tabs>
---
## Delete a Mental Model
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="delete-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="delete-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="delete-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="delete-mental-model" language="go" />
</TabItem>
</Tabs>
---
## Tags and Visibility
Mental models support the same tag system as memories. When you assign tags to a mental model, those tags control both **which memories it reads** during refresh and **when it is surfaced** during reflect.
### How tags affect mental model refresh
:::warning
Adding tags to a mental model narrows the pool of source memories its refresh can read from. If no memories carry those tags yet, refresh will return empty content (e.g. `"I cannot find any information…"`) even though direct `reflect` on the same query works. Backfill tags on the relevant memories first, or override the default via `trigger.tags_match` / `trigger.tag_groups`.
:::
When a mental model is refreshed (manually or automatically), it runs an internal reflect call to regenerate its content. If the mental model has tags, that reflect call uses `all_strict` tag matching — meaning it will only read memories that carry **all** of the mental model's tags. Untagged memories are excluded.
```
Mental model tags: ["user:alice"]
During refresh, it reads:
✅ "Alice prefers async communication" — has "user:alice"
✅ "Team uses Slack for announcements" — has "user:alice" (plus other tags)
❌ "Company policy: no meetings on Fridays" — untagged, excluded
❌ "Bob dislikes long meetings" — no "user:alice" tag
```
This means a mental model tagged `["user:alice"]` will also pick up memories tagged `["user:alice", "team"]` — extra tags on a memory don't disqualify it. Only the mental model's own tags are required to be present.
### How tags affect mental model lookup during reflect
When you call `reflect` with tags, those same tags are used to filter which mental models the agent can see. A mental model is visible only if its tags overlap with the tags on the reflect request.
For more details on tag matching modes (`any`, `any_strict`, `all`, `all_strict`) and worked examples, see the [Recall tags reference](./recall#tags).
### Listing mental model tags
`GET /v1/default/banks/{bank_id}/tags` accepts a `source` query parameter that selects which tag space to enumerate:
- `source=memories` *(default)* — tags attached to memory units.
- `source=mental_models` — tags attached to mental models in this bank.
Use the `mental_models` source to populate autocomplete or filter UIs over mental-model tags, distinct from the (typically larger) memory tag set.
---
## History
Every time a mental model's content changes (via refresh or manual update), the previous version is saved with a timestamp. You can retrieve the full change log with the history endpoint:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model-history" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model-history" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="get-mental-model-history" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model-history" language="go" />
</TabItem>
</Tabs>
### Response
The endpoint returns a list of history entries, most recent first:
| Field | Type | Description |
|-------|------|-------------|
| `previous_content` | string \| null | The content before this change (`null` if not available) |
| `changed_at` | string | ISO 8601 timestamp of when the change occurred |
Each entry captures the **content before the change** and when it happened. The current content is returned by the standard [Get a Mental Model](#get-a-mental-model) endpoint.
:::note
History tracking is enabled by default. Set `HINDSIGHT_API_ENABLE_MENTAL_MODEL_HISTORY=false` to disable it.
:::
---
## Use Cases
| Use Case | Example |
|----------|---------|
| **FAQ Answers** | Pre-compute answers to common customer questions |
| **Onboarding Summaries** | "What should new team members know?" |
| **Status Reports** | "What's the current project status?" refreshed weekly |
| **Policy Summaries** | "What are our security policies?" |
---
## Next Steps
- [**Reflect**](./reflect) — How the agentic loop uses mental models
- [**Observations**](/developer/observations) — How knowledge is consolidated
- [**Operations**](./operations) — Track async mental model creation
@@ -1,123 +0,0 @@
---
sidebar_position: 9
---
# Operations
Background tasks that Hindsight executes asynchronously.
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## How Operations Work
Hindsight processes several types of tasks in the background to maintain memory quality and consistency. These operations run automatically—you don't need to trigger them manually.
By default, all background operations are executed in-process within the API service.
:::note Kafka Integration
Support for external streaming platforms like Kafka for scale-out processing is planned but **not available out of the box** in the current release.
:::
## Operation Types
| Operation | Trigger | Description |
|-----------|---------|-------------|
| **batch_retain** | `retain_batch` with `async=True` | Processes large content batches in the background |
| **consolidate** | After `retain` | Consolidates new facts into observations |
## Async Retain Example
When retaining large batches of memories, use `async=true` to process in the background. The response includes an `operation_id` that you can use to poll for completion.
### 1. Submit async retain request
```bash
curl -X POST "http://localhost:8000/v1/default/banks/my-bank/memories" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"content": "Alice joined Google in 2023"},
{"content": "Bob prefers Python over JavaScript"}
],
"async": true
}'
```
Response:
```json
{
"success": true,
"bank_id": "my-bank",
"items_count": 2,
"async": true,
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
### 2. Poll for operation status
```bash
curl "http://localhost:8000/v1/default/banks/my-bank/operations"
```
Response:
```json
{
"bank_id": "my-bank",
"operations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"task_type": "retain",
"items_count": 2,
"document_id": null,
"created_at": "2024-01-15T10:30:00Z",
"status": "completed",
"error_message": null
}
]
}
```
### Operation Status Values
| Status | Description |
|--------|-------------|
| `pending` | Operation is queued and waiting to be processed |
| `processing` | Operation is actively being processed by a worker |
| `completed` | Operation finished successfully |
| `failed` | Operation failed (check `error_message` for details) |
| `cancelled` | Operation was cancelled via the DELETE endpoint before processing |
## Managing Operations
### Cancel a pending operation
```bash
curl -X DELETE "http://localhost:8000/v1/default/banks/my-bank/operations/550e8400-e29b-41d4-a716-446655440000"
```
### Retry a failed operation
If an operation fails, you can manually re-queue it for execution:
```bash
curl -X POST "http://localhost:8000/v1/default/banks/my-bank/operations/550e8400-e29b-41d4-a716-446655440000/retry"
```
Response:
```json
{
"success": true,
"message": "Operation 550e8400-e29b-41d4-a716-446655440000 queued for retry",
"operation_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
The operation status resets to `pending` and the worker picks it up again. Returns `409` if the operation is not in `failed` or `cancelled` state.
## Next Steps
- [**Documents**](./documents) — Track document sources
- [**Memory Banks**](./memory-banks) — Configure bank settings
@@ -1,128 +0,0 @@
---
sidebar_position: 0
---
# Quick Start
Get up and running with Hindsight in 60 seconds.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
import {ClientsGrid} from '@site/src/components/SupportedGrids';
{/* Import raw source files */}
import quickstartPy from '!!raw-loader!@site/examples/api/quickstart.py';
import quickstartMjs from '!!raw-loader!@site/examples/api/quickstart.mjs';
import quickstartSh from '!!raw-loader!@site/examples/api/quickstart.sh';
import quickstartGo from '!!raw-loader!@site/examples/api/quickstart.go';
## Clients
<ClientsGrid />
## Start the API Server
<Tabs>
<TabItem value="pip" label="pip (API only)">
```bash
pip install hindsight-api
export OPENAI_API_KEY=sk-xxx
export HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY
hindsight-api
```
API available at [http://localhost:8888](http://localhost:8888/docs)
</TabItem>
<TabItem value="docker" label="Docker (Full Experience)">
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
</TabItem>
</Tabs>
:::tip LLM Provider
Hindsight requires an LLM with structured output support. Recommended: **Groq** with `gpt-oss-20b` for fast, cost-effective inference.
See [LLM Providers](/developer/models#llm) for more details.
:::
---
## Use the Client
<Tabs>
<TabItem value="python" label="Python">
```bash
pip install hindsight-client
```
<CodeSnippet code={quickstartPy} section="quickstart-full" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
```bash
npm install @vectorize-io/hindsight-client
```
<CodeSnippet code={quickstartMjs} section="quickstart-full" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
<CodeSnippet code={quickstartSh} section="quickstart-full" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
```bash
go get github.com/vectorize-io/hindsight/hindsight-clients/go
```
<CodeSnippet code={quickstartGo} section="quickstart-full" language="go" />
</TabItem>
</Tabs>
---
## What's Happening
| Operation | What it does |
|-----------|--------------|
| **Retain** | Content is processed, facts are extracted, entities are identified and linked in a knowledge graph |
| **Recall** | Four search strategies (semantic, keyword, graph, temporal) run in parallel to find relevant memories |
| **Reflect** | Retrieved memories are used to generate a disposition-aware response |
---
## Integrations
Browse all supported integrations in the [Integrations Hub](/integrations).
## Next Steps
- [**Retain**](./retain) — Advanced options for storing memories
- [**Recall**](./recall) — Search and retrieval strategies
- [**Reflect**](./reflect) — Disposition-aware reasoning
- [**Memory Banks**](./memory-banks) — Configure disposition and mission
- [**Server Deployment**](/developer/installation) — Docker Compose, Helm, and production setup
@@ -1,414 +0,0 @@
---
sidebar_position: 2
---
# Recall Memories
Retrieve memories from a bank using multi-strategy recall.
When you **recall**, Hindsight runs four retrieval strategies in parallel — semantic similarity, keyword (BM25), graph traversal, and temporal — then fuses and reranks the results into a single ranked list. The response contains structured facts, not raw documents.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import recallMjs from '!!raw-loader!@site/examples/api/recall.mjs';
import recallSh from '!!raw-loader!@site/examples/api/recall.sh';
import recallGo from '!!raw-loader!@site/examples/api/recall.go';
:::info How Recall Works
Learn about the four retrieval strategies (semantic, keyword, graph, temporal) and RRF fusion in the [Recall Architecture](/developer/retrieval) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Recall
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-basic" language="go" />
</TabItem>
</Tabs>
---
## Parameters
### query
The natural language question or statement to search for. This is the only required field. The query drives all four retrieval strategies simultaneously: it is embedded for semantic search, tokenized for BM25 keyword search, used to seed graph traversal, and parsed for temporal expressions. After retrieval, the raw query text is also passed to the cross-encoder reranker to re-score every candidate. Queries exceeding 500 tokens are rejected.
### types
Controls which categories of memory facts are searched. Accepted values are `world` (objective facts), `experience` (events and conversations), and `observation` (deduplicated, evidence-grounded beliefs consolidated from multiple memories). When omitted, all three types are searched.
Each type runs the full four-strategy retrieval pipeline independently, so narrowing `types` reduces both the result set and query cost.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-world-only" language="python" />
<CodeSnippet code={recallPy} section="recall-experience-only" language="python" />
<CodeSnippet code={recallPy} section="recall-observations-only" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-world-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-experience-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-observations-only" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-fact-type" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-world-only" language="go" />
<CodeSnippet code={recallGo} section="recall-experience-only" language="go" />
<CodeSnippet code={recallGo} section="recall-observations-only" language="go" />
</TabItem>
</Tabs>
:::tip About Observations
Observations are deduplicated, evidence-grounded beliefs consolidated from multiple facts — preferences, recurring patterns, and durable learnings the memory bank has built up. Each observation references its supporting memories (with exact quotes) and carries a computed freshness trend, and is refined rather than overwritten when new evidence arrives. They are created and maintained automatically in the background after retain operations.
:::
### budget
Controls retrieval depth and breadth. Accepted values are `low`, `mid` (default), and `high`. Use `low` for fast simple lookups, `mid` for balanced everyday queries, and `high` when you need to find indirect connections or exhaustive coverage.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-budget-levels" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-budget-levels" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-budget-levels" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-budget-levels" language="go" />
</TabItem>
</Tabs>
### max_tokens
The maximum number of tokens the returned facts can collectively occupy. Defaults to `4096`. Only the `text` field of each fact is counted toward this budget — metadata, tags, entities, and other fields are not included. After reranking, facts are included in relevance order until this budget is exhausted — so you always get the most relevant memories that fit. Hindsight is designed for agents, which think in tokens rather than result counts: set `max_tokens` to however much of your context window you want to allocate to memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-token-budget" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-token-budget" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-token-budget" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-token-budget" language="go" />
</TabItem>
</Tabs>
### query_timestamp
An ISO 8601 datetime representing when the query is being asked, from the user's perspective. When provided, it is used as the anchor for resolving relative temporal expressions in the query — for example, if the query says "last month" and `query_timestamp` is `2023-05-30`, the temporal search window becomes approximately April 2023. Without it, the server's current time is used as the anchor. This field matters most for replaying historical conversations or building agents that need time-anchored recall.
### include
An optional object controlling supplementary data returned alongside the main facts.
#### chunks
When enabled, the response includes the raw source text chunks from which each fact was extracted. Chunks are fetched before the `max_tokens` filter, so setting `max_tokens=0` returns no facts but can still return chunks. The `max_tokens` sub-option (default `8192`) controls the total chunk token budget independently of the main fact budget. This is useful when agents need surrounding context beyond the extracted fact text.
:::note
When `include_chunks` is enabled, chunks are fetched based on the top-scored reranked results before token filtering. The last chunk is truncated (not dropped) to fit exactly within the budget, and each chunk carries a `truncated` flag indicating whether it was cut.
:::
#### source_facts
When enabled and `types` includes `observation`, each observation result is accompanied by the original contributing facts it was synthesized from. Source facts are returned in a top-level `source_facts` dict keyed by fact ID, and each observation result carries a `source_fact_ids` list for cross-referencing. Facts are deduplicated across observations. The `max_tokens` sub-option (default `4096`) limits the total token budget for source facts.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-source-facts" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-source-facts" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-source-facts" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-source-facts" language="go" />
</TabItem>
</Tabs>
#### entities
Enabled by default. When active, each returned fact includes the canonical names of entities associated with it. Set to `null` to skip the entity JOIN query and reduce response size. The `max_tokens` sub-option (default `500`) is a future-facing guard for entity data.
### tags
Filters recall to only memories that match the specified tags. When omitted, all memories regardless of tags are eligible. Tag filtering is applied at the database level across all four retrieval strategies, not as a post-processing step.
The `tags_match` parameter controls the filtering logic:
| Mode | Untagged memories | Match condition |
|------|-------------------|-----------------|
| `any` (default) | Included | Memory has **at least one** of the specified tags |
| `any_strict` | Excluded | Memory has **at least one** of the specified tags |
| `all` | Included | Memory has **all** of the specified tags |
| `all_strict` | Excluded | Memory has **all** of the specified tags |
#### Scenario setup
Consider a bank with these four memories:
| Memory | Tags |
|--------|------|
| "Alice prefers async communication" | `["user:alice"]` |
| "Bob dislikes long meetings" | `["user:bob"]` |
| "Team uses Slack for announcements" | `["user:alice", "team"]` |
| "Company policy: no meetings on Fridays" | *(untagged)* |
#### `any` — OR matching, includes untagged (default)
Returns memories that have **at least one** matching tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-with-tags" language="go" />
</TabItem>
</Tabs>
Use this for **shared global knowledge + user-specific** patterns, where untagged memories represent information everyone should see.
#### `any_strict` — OR matching, excludes untagged
Same as `any` but untagged memories are excluded.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-strict" language="go" />
</TabItem>
</Tabs>
Use this when memories are **fully partitioned by tags** and untagged memories should never be visible.
#### `all` — AND matching, includes untagged
Returns memories that have **every** specified tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-mode" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-mode" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-mode" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all-mode" language="go" />
</TabItem>
</Tabs>
Use this when memories must belong to a **specific intersection** of scopes (e.g., only memories relevant to both a user and a project), while still surfacing shared global knowledge.
#### `all_strict` — AND matching, excludes untagged
Returns memories that have **every** specified tag, and excludes untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all" language="go" />
</TabItem>
</Tabs>
Use this for strict scope enforcement where a memory must explicitly belong to **all** specified contexts.
:::tip Extra tags are fine
A memory with tags `["user:alice", "team", "project:x"]` will still match a filter of `["user:alice", "team"]` under `all_strict` — extra tags on the memory are not a problem. The filter only requires the memory to contain **at least** the specified tags.
:::
### tag_groups
`tag_groups` is a list of compound boolean tag filters. The groups in the list are AND-ed together at the top level. Each group is a recursive boolean expression: a **leaf** node `{tags, match}`, or a **compound** node `{and: [...]}`, `{or: [...]}`, or `{not: ...}`.
`tag_groups` and `tags` / `tags_match` can be used simultaneously — they are AND-ed together.
#### Leaf node
```json
{ "tags": ["step:5", "step:8"], "match": "any_strict" }
```
`match` accepts the same values as `tags_match`: `any`, `all`, `any_strict`, `all_strict`. Defaults to `any_strict`.
#### Compound nodes
```json
{ "and": [ <TagGroup>, <TagGroup>, ... ] }
{ "or": [ <TagGroup>, <TagGroup>, ... ] }
{ "not": <TagGroup> }
```
#### Examples
**Step filter AND user scope** — two top-level groups AND-ed:
```json
{
"tag_groups": [
{ "tags": ["step:5", "step:8", "step:12"], "match": "any_strict" },
{ "tags": ["user:ep_42"], "match": "all_strict" }
]
}
```
**Nested OR inside AND** — user must match, plus either step OR priority:
```json
{
"tag_groups": [
{ "tags": ["user:alice"], "match": "all_strict" },
{ "or": [
{ "tags": ["step:5"], "match": "any_strict" },
{ "tags": ["priority:high"], "match": "all_strict" }
]}
]
}
```
**Exclusion** — user must match, but archived memories are excluded:
```json
{
"tag_groups": [
{ "tags": ["user:alice"], "match": "all_strict" },
{ "not": { "tags": ["archived"], "match": "any_strict" } }
]
}
```
### trace
When set to `true`, the response includes a detailed debug trace covering the query embedding, entry points, per-strategy retrieval results, RRF fusion candidates, reranked results, temporal constraints detected, and per-phase timings. Has no effect on the retrieval logic itself. Useful for understanding why specific memories were or were not returned.
---
## Response
### results
The main list of recalled facts, ordered by relevance. Relevance is computed by running four retrieval strategies in parallel — semantic similarity, BM25 keyword, graph traversal, and temporal — fusing their rankings with Reciprocal Rank Fusion (RRF), then re-scoring the merged candidates with a cross-encoder reranker against the original query.
Results do not include a numeric score. Raw retrieval scores are not meaningful on an absolute scale — a score of 0.8 from one query tells you nothing useful compared to a score of 0.8 from another. What matters is the relative ordering, which is already reflected in the list order. Agents should consume memories in order and let `max_tokens` determine how many fit, rather than filtering by score.
Each item in `results` has the following fields:
#### id
The unique identifier of this fact. Use it to cross-reference with `source_facts` or for application-level deduplication.
#### text
The extracted fact text as stored in the memory bank.
#### type
The fact category: `world` for objective information, `experience` for events and conversations, or `observation` for consolidated knowledge synthesized over time.
#### context
The context label provided when the fact was retained (e.g., `"team meeting"`, `"slack"`). `null` if none was set.
#### metadata
The key-value string pairs attached when the fact was retained. `null` if none were set.
#### tags
The visibility-scoping tags attached to this fact.
#### entities
A list of canonical entity name strings linked to this fact. Only populated when `include.entities` is enabled (the default). `null` otherwise.
#### occurred_start / occurred_end
ISO 8601 datetimes representing when the described event started and ended. Extracted by the LLM from the content during retain. `null` if the content had no temporal information.
#### mentioned_at
ISO 8601 datetime of when this fact was retained into the bank.
#### document_id
The document ID this fact belongs to, as set during retain.
#### chunk_id
The ID of the source text chunk this fact was extracted from. Used to cross-reference with `chunks` in the response when `include.chunks` is enabled.
#### source_fact_ids
For `observation`-type results only: the IDs of the original facts this observation was synthesized from. Cross-references with `source_facts` in the response. `null` for other types or when `include.source_facts` is not enabled.
---
### source_facts
A dict keyed by fact ID containing full `RecallResult` objects for the source facts that contributed to observation results. Only present when `include.source_facts` is enabled. Facts are deduplicated — if two observations share a source fact, it appears once.
### chunks
A dict keyed by chunk ID containing the raw source text chunks associated with the returned facts. Only present when `include.chunks` is enabled. Each chunk has `id`, `text`, `chunk_index`, and `truncated` (whether the text was cut to fit the token budget).
### entities
A dict keyed by canonical entity name containing entity state objects. Only present when `include.entities` is enabled. Each entry has `entity_id`, `canonical_name`, and `observations`.
### trace
A debug object present only when `trace: true` was set in the request. Contains per-phase timings, retrieval breakdowns, and RRF fusion details.
@@ -1,171 +0,0 @@
---
sidebar_position: 3
---
# Reflect
Generate a grounded, disposition-aware response using an agentic reasoning loop.
When you call **reflect**, Hindsight runs an agentic loop that autonomously searches the memory bank using multiple retrieval tools, applies the bank's disposition traits to shape the reasoning style, and produces a final answer grounded in what it found. Unlike recall — which returns raw facts — reflect returns a synthesized response written by the LLM.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import reflectPy from '!!raw-loader!@site/examples/api/reflect.py';
import reflectMjs from '!!raw-loader!@site/examples/api/reflect.mjs';
import reflectSh from '!!raw-loader!@site/examples/api/reflect.sh';
import reflectGo from '!!raw-loader!@site/examples/api/reflect.go';
:::info How Reflect Works
Learn about disposition-driven reasoning in the [Reflect Architecture](/developer/reflect) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Usage
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-basic" language="go" />
</TabItem>
</Tabs>
---
## Parameters
### query
The question or prompt to reflect on. This is the only required field. If you have situational context that should influence the answer, include it directly in the query rather than as a separate field.
### budget
Controls how thoroughly the agent explores the memory bank before answering. Accepted values are `low` (default), `mid`, and `high`. At `low`, the agent does a shallow search optimized for speed. At `mid`, it checks multiple sources when the question warrants it. At `high`, it performs deep exploration across all knowledge levels and may use multiple query variations to find indirect connections. Use `high` for complex questions that require synthesizing information from many sources.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-params" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-params" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-params" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-params" language="go" />
</TabItem>
</Tabs>
### max_tokens
Limits the length of the final generated response. Defaults to `4096`. This does not affect how much the agent can retrieve during the agentic loop — only the final answer length.
### response_schema
An optional JSON Schema object. When provided, the LLM generates a response that conforms to the schema and the response includes a `structured_output` field with the result parsed accordingly. The `text` field will be empty since only a single structured LLM call is made. Use this when you need to process the response programmatically rather than display it as prose.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-structured-output" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-structured-output" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-structured-output" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-structured-output" language="go" />
</TabItem>
</Tabs>
### tags
Filters which memories the agent can access during reflection. Works identically to [recall tags](./recall#tags) — only memories matching the specified tags are considered. The `tags_match` parameter controls the matching logic (`any`, `all`, `any_strict`, `all_strict`) with the same semantics as recall.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-tags" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-tags" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-tags" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-tags" language="go" />
</TabItem>
</Tabs>
### include
Controls optional supplementary data returned alongside the main response.
#### include.facts
When enabled, the response includes a `based_on` object listing the memories, mental models, and directives the agent actually used to construct the answer. Only sources retrieved during the agent loop can appear here — citations are validated to prevent hallucinated references. Useful for transparency and verification.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-sources" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-sources" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-sources" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-sources" language="go" />
</TabItem>
</Tabs>
#### include.tool_calls
When enabled, the response includes a `trace` object with the full execution log of every tool call and LLM call made during the agentic loop, including inputs, outputs, and durations. Set `output: false` to include only tool inputs for a smaller payload. Useful for debugging why the agent reached a particular conclusion.
---
## Response
### text
The synthesized answer as a well-formatted markdown string. This is the primary output of reflect. Empty when `response_schema` is provided (use `structured_output` instead in that case).
### structured_output
The LLM's response parsed according to the `response_schema` provided in the request. Only present when `response_schema` was set. `null` otherwise.
### based_on
The sources the agent used to construct the answer. Only present when `include.facts` was enabled. Contains three fields:
- `memories` — a list of memory facts (world, experience, observation) that were retrieved and cited. Each item has `id`, `text`, `type`, `context`, `occurred_start`, and `occurred_end`.
- `mental_models` — a list of mental models that were used. Each item has `id`, `text`, and `context`.
- `directives` — a list of directives that were enforced during reasoning. Each item has `id`, `name`, and `content`.
### usage
Token usage for all LLM calls made during the agentic loop: `input_tokens`, `output_tokens`, and `total_tokens`. Useful for cost tracking.
### trace
The full execution log of the agentic loop. Only present when `include.tool_calls` was enabled. Contains:
- `tool_calls` — each tool invocation with `tool` name (`lookup`, `recall`, `learn`, `expand`), `input`, `output` (if `output: true`), `duration_ms`, and `iteration` number.
- `llm_calls` — each LLM call with `scope` (e.g., `"agent_1"`, `"final"`) and `duration_ms`.
@@ -1,334 +0,0 @@
---
sidebar_position: 2
---
# Ingest Data
Store documents, conversations, and raw content into Hindsight to automatically extract and create memories.
When you **retain** content, Hindsight doesn't just store the raw text—it intelligently analyzes the content to extract meaningful facts, identify entities, and build a connected knowledge graph. This process transforms unstructured information into structured, queryable memories.
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import retainPy from '!!raw-loader!@site/examples/api/retain.py';
import retainMjs from '!!raw-loader!@site/examples/api/retain.mjs';
import retainSh from '!!raw-loader!@site/examples/api/retain.sh';
import retainGo from '!!raw-loader!@site/examples/api/retain.go';
:::info How Retain Works
Learn about fact extraction, entity resolution, and graph construction in the [Retain Architecture](/developer/retain) guide.
:::
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Store a Document
A single retain call accepts one or more **items**. Each item is a piece of raw content — a conversation, a document, a note — that Hindsight will analyze and decompose into one or many memories. The content itself is never stored verbatim; what gets stored are the structured facts the LLM extracts from it.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-basic" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-basic" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-basic" language="go" />
</TabItem>
</Tabs>
### Retaining a Conversation
A full conversation should be retained as a single item. The LLM can parse any format — plain text, JSON, Markdown, or any structured representation — as long as it clearly conveys who said what and when. The example below uses a simple `Name (timestamp): text` format.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-conversation" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-conversation" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-conversation" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-conversation" language="go" />
</TabItem>
</Tabs>
When the conversation grows — a new message arrives — just retain again with the full updated content and the same `document_id`. Hindsight will delete the previous version and reprocess from scratch, so memories always reflect the latest state of the conversation.
---
## Parameters
### content
The raw text to store. This is the only required field. Hindsight chunks the content, sends each chunk to the LLM for fact extraction, and stores the resulting structured facts — not the original text. A single `content` value can produce many memories depending on how much information it contains.
### timestamp
When the event described in the content actually occurred. Three forms are accepted:
| Value | Behaviour |
|-------|-----------|
| Omitted / `null` | Defaults to the current time at ingestion. |
| ISO 8601 string (e.g. `"2024-01-15T10:30:00Z"`) | Uses the provided datetime. |
| `"unset"` | Stores the content **without any timestamp**. Use this for timeless material such as reference documents, books, or fictional content where no real event time exists. |
The timestamp is injected into the LLM fact-extraction prompt so the model can resolve relative temporal references in the content — for example, if the content says "last Monday", the model uses the provided timestamp as the anchor to pin down the actual date. When `"unset"` is passed the prompt shows `Event Date: Unknown`, allowing the model to correctly return `N/A` for the `when` field of every extracted fact. Providing a real timestamp also enables temporal recall queries like "What happened last spring?" to work correctly.
### context
A short label describing the source or situation — for example `"team meeting"`, `"slack"`, or `"support ticket"`. It is injected directly into the LLM prompt, so it actively shapes how facts are extracted. The same sentence can mean something very different depending on context: "the project was terminated" in a `"performance review"` context versus a `"product roadmap"` context produces different memories.
Providing context consistently is one of the highest-leverage things you can do to improve memory quality.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-with-context" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-with-context" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-with-context" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-with-context" language="go" />
</TabItem>
</Tabs>
### metadata
Arbitrary key-value string pairs that provide context about this item. For example: `{"source": "slack", "channel": "engineering", "thread_id": "T123"}`. Metadata is included in the fact extraction prompt, so the LLM can use it as additional context when extracting facts — for instance, knowing the document title or source can improve accuracy. It is also stored on each memory unit and returned with every recalled memory, letting you do client-side filtering or static enrichment without extra lookups — for example, linking a memory back to its source URL, thread ID, or any application-specific identifier.
### document_id
A caller-supplied string that groups one or more items under a logical document. This field is the key to making retain **idempotent**.
When you provide a `document_id`, Hindsight upserts the document: if a document with that ID already exists in the bank, it and all its associated memories are deleted before the new content is processed and inserted. This means you can safely re-run retain on updated content — for example, a chat thread that grew since last time — without accumulating duplicate memories.
If you omit `document_id`, Hindsight assigns a random UUID per request, so re-ingesting the same content will create duplicate memories.
### update_mode
Controls how Hindsight handles an existing document when you retain with a `document_id` that already exists.
| Value | Behaviour |
|-------|-----------|
| `"replace"` *(default)* | Deletes the old document and all its memories, then processes the new content from scratch. This is the standard upsert described above. |
| `"append"` | Concatenates the new content onto the existing document text and reprocesses the combined document. Delta retain automatically skips unchanged chunks, so only the new portion triggers LLM extraction. |
Append mode requires a `document_id` — without one there is no existing document to append to.
**When to use append**: Use `"append"` for content that grows incrementally — for example, a log file, a journal, or a chat transcript where you receive new messages one at a time. Instead of re-sending the entire history on each update, send only the new content with `update_mode: "append"` and Hindsight will efficiently merge it with what it already has.
```json
{
"items": [
{
"content": "New entry to add to the existing document.",
"document_id": "my-growing-doc",
"update_mode": "append"
}
]
}
```
### entities
A list of entities you want to guarantee are recognized, merged with any entities the LLM extracts automatically. Each entry has a `text` field (the entity name) and an optional `type` (e.g., `"PERSON"`, `"ORG"`, `"CONCEPT"` — defaults to `"CONCEPT"` if omitted).
Use this when you know certain entities are important but the LLM might miss them or refer to them inconsistently across different parts of the content. Providing entities explicitly ensures they are always linked in the knowledge graph.
### tags and document_tags
Tags control **visibility scoping** — which memories are visible during recall. A memory is only returned if its tags intersect with the tags filter provided in the recall request. This makes tags useful when a single memory bank serves multiple users or sessions and each should only see their own memories.
Use consistent naming patterns to keep tag filtering predictable. Common conventions: `user:<id>` for per-user scoping, `session:<id>` for session isolation, `room:<id>` for chat rooms, `topic:<name>` for category filtering. The bank also exposes a list-tags endpoint that returns all tags with their memory counts, useful for UI autocomplete or wildcard expansion.
See [Recall API](./recall#tags) for filtering by tags during retrieval.
### observation_scopes
Controls which [observations](../observations) this memory contributes to during consolidation. Each scope runs an independent pass, creating or updating observations tagged with only that scope's tags.
:::info Scope isolation
During consolidation, Hindsight uses `all_strict` matching to find existing observations to update — only observations whose tags exactly match the current scope are considered. This keeps scopes isolated: a memory consolidated under `["student:alice"]` will never bleed into an observation tagged `["student:alice", "teacher:bob"]`.
:::
The examples below use a lesson transcript retained with `tags: ["student:alice", "teacher:bob", "session-id:s1"]`.
#### combined *(default)*
One consolidation pass using all tags together. The resulting observation is tagged with the full set.
- Observations created: `["student:alice", "teacher:bob", "session-id:s1"]`
- ✗ *"What does Alice struggle with across all her sessions?"* — no match, because no observation was ever built for `student:alice` alone
- ✗ *"How does Bob teach?"* — no match for `teacher:bob` alone
- ✓ *"What happened in session s1 with Alice and Bob?"* — exact match
**Use when** the memory is meaningful only as a whole and you never need to query any single tag in isolation.
#### per_tag
One consolidation pass per individual tag. Each tag gets its own isolated observation that grows with every new memory sharing that tag.
- Observations created: `["student:alice"]` · `["teacher:bob"]` · `["session-id:s1"]`
- ✓ *"What does Alice struggle with across all her sessions?"*
- ✓ *"How does Bob teach?"*
- ✓ *"What happened in session s1?"*
- ✗ *"How does Alice perform specifically with Bob?"* — no observation for the `["student:alice", "teacher:bob"]` combination
- ✗ *"How does Bob teach in online sessions?"* — no observation for `["teacher:bob", "session-id:s1"]`
**Use when** content involves multiple tags that each represent an independent subject — the most common choice for multi-party content like conversations, lessons, or support sessions.
#### all_combinations
One pass per subset of tags — singles, pairs, triples, and so on. For 3 tags that is 7 passes.
- Observations created: all `"per_tag"` scopes above, plus `["student:alice", "teacher:bob"]` · `["student:alice", "session-id:s1"]` · `["teacher:bob", "session-id:s1"]` · `["student:alice", "teacher:bob", "session-id:s1"]`
- ✓ All questions from `"per_tag"` above
- ✓ *"How does Alice perform specifically with Bob?"* — matched by `["student:alice", "teacher:bob"]`
**Use when** you need observations at every granularity — per tag, per pair, per group.
#### custom
Pass an explicit list of tag sets. Each inner list is one scope.
```json
[["student:alice"], ["teacher:bob"], ["teacher:bob", "session-id:s1"]]
```
- Observations created: exactly those three scopes — nothing more
- ✓ *"What does Alice struggle with?"*
- ✓ *"How does Bob teach?"*
- ✓ *"How does Bob teach in session s1 specifically?"*
- ✗ *"What happened in session s1 regardless of teacher?"* — `["session-id:s1"]` alone was not included
**Use when** you know exactly which combinations are meaningful and want to avoid unnecessary passes.
### Response
The synchronous retain response includes:
- `success` — whether the operation completed without errors
- `bank_id` — the memory bank that received the content
- `items_count` — number of items processed
- `async` — whether processing ran asynchronously
- `usage` — token usage for the LLM calls (`input_tokens`, `output_tokens`, `total_tokens`), only present for synchronous operations
---
## Batch Ingestion
Multiple items can be submitted in a single request. Batch ingestion is the recommended approach — it reduces network overhead and lets Hindsight optimize extraction across related content.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-batch" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-batch" language="go" />
</TabItem>
</Tabs>
---
## Files
Upload files directly — Hindsight converts them to text and extracts memories automatically. File processing always runs asynchronously and returns operation IDs for tracking.
**Supported formats:** PDF, DOCX, DOC, PPTX, PPT, XLSX, XLS, images (JPG, PNG, GIF, etc. — OCR), audio (MP3, WAV, FLAC, etc. — transcription), HTML, and plain text formats (TXT, MD, CSV, JSON, YAML, etc.)
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
The file retain endpoint always returns asynchronously. The response contains `operation_ids` — one per uploaded file — which you can poll via `GET /v1/default/banks/{bank_id}/operations` to track progress.
Upload up to 10 files per request (max 100 MB total). Each file becomes a separate document with optional per-file metadata:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
:::info File Storage
Uploaded files are stored server-side (PostgreSQL by default, or S3/GCS/Azure for production). Configure storage via `HINDSIGHT_API_FILE_STORAGE_TYPE`. See [Configuration](../configuration#file-processing) for details.
:::
---
## Async Ingestion
For large batches, use async ingestion to avoid blocking your application:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-async" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-async" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-async" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-async" language="go" />
</TabItem>
</Tabs>
When `async: true`, the call returns immediately with an `operation_id`. Processing runs in the background via the worker service. No `usage` metrics are returned for async operations.
### Cut Costs 50% with Provider Batch APIs
When using async retain, enable the provider Batch API to reduce LLM fact-extraction costs by 50%. OpenAI and Groq both offer this discount in exchange for a processing window of up to 24 hours — a trade-off that's typically invisible when retain already runs in the background.
```bash
export HINDSIGHT_API_RETAIN_BATCH_ENABLED=true
```
Hindsight submits fact extraction calls as a batch job to the provider, polls for completion, and processes results automatically. No changes to your API calls are needed.
:::note
Batch API cost savings require `async=true` in your retain request and a compatible provider (OpenAI or Groq).
:::
@@ -1,96 +0,0 @@
---
sidebar_position: 10
---
# Webhooks
Hindsight can notify your application in real-time when memory events occur by sending HTTP POST requests to a URL you configure.
## Delivery and Retries
Webhooks are registered per memory bank and fire automatically when matching events occur. Each delivery attempt is tracked, and failed deliveries are retried with exponential backoff:
| Attempt | Delay after failure |
|---------|---------------------|
| 1 | 5 seconds |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 5 hours |
| 6 | Permanent failure |
A delivery is considered failed if your endpoint returns a non-2xx status code or does not respond within the configured timeout (default 30 seconds). After 6 failed attempts, the delivery is marked as permanently failed and no further retries are made.
:::info At-least-once delivery
Webhook delivery tasks are queued in the same database transaction as the primary operation (e.g. the retain or consolidation write). This means if the server crashes after committing but before sending, the delivery task survives and will be retried. As a result, **your endpoint may receive the same event more than once** — use the `operation_id` field to deduplicate if needed.
:::
## Event Types
### `consolidation.completed`
Fired after Hindsight finishes consolidating new memories into observations for a bank.
**Payload:**
```json
{
"event": "consolidation.completed",
"bank_id": "my-bank",
"operation_id": "a1b2c3d4e5f6",
"status": "completed",
"timestamp": "2026-03-04T12:00:00Z",
"data": {
"observations_created": 3,
"observations_updated": 1,
"observations_deleted": null,
"error_message": null
}
}
```
**`data` fields:**
| Field | Type | Description |
|-------|------|-------------|
| `observations_created` | `integer \| null` | Number of new observations created |
| `observations_updated` | `integer \| null` | Number of existing observations updated |
| `observations_deleted` | `integer \| null` | Number of observations deleted |
| `error_message` | `string \| null` | Set when `status` is `"failed"` |
**`status` values:** `"completed"` or `"failed"`
---
### `retain.completed`
Fired once per document after a retain operation completes (both synchronous and asynchronous). When retaining a batch of N documents, N separate events are fired.
**Payload:**
```json
{
"event": "retain.completed",
"bank_id": "my-bank",
"operation_id": "a1b2c3d4e5f6",
"status": "completed",
"timestamp": "2026-03-04T12:00:01Z",
"data": {
"document_id": "doc-abc123",
"tags": ["meeting", "q1-2026"]
}
}
```
**`data` fields:**
| Field | Type | Description |
|-------|------|-------------|
| `document_id` | `string \| null` | The document ID if one was provided in the retain request |
| `tags` | `string[] \| null` | Document-level tags applied during retain |
**Notes:**
- For async retain (`async: true`), `operation_id` matches the `operation_id` returned by the retain API.
- For sync retain, `operation_id` is a generated identifier for tracing purposes.
- One event is fired per content item in the retain request.
File diff suppressed because it is too large Load Diff
@@ -1,149 +0,0 @@
---
sidebar_position: 7
---
# Development Guide
Guide to setting up a local development environment for contributing to Hindsight.
## Prerequisites
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) - Fast Python package manager
- Docker and Docker Compose
- An LLM API key (OpenAI, Groq, or Ollama)
## Local Development Setup
### 1. Clone the Repository
```bash
git clone https://github.com/vectorize-io/hindsight.git
cd hindsight
```
### 2. Install Dependencies
```bash
uv sync
```
### 3. Start PostgreSQL
Start only the database via Docker:
```bash
cd docker && docker-compose up -d postgres
```
### 4. Configure Environment
```bash
cp .env.example .env
```
Edit `.env` with your LLM API key:
```bash
# Database (connects to Docker postgres)
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
# LLM Provider (choose one)
HINDSIGHT_API_LLM_PROVIDER=groq
HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile
```
### 5. Start the API Server
```bash
./scripts/start-server.sh --env local
```
The server will be available at http://localhost:8888.
## Running Tests
```bash
# Run all tests
uv run pytest
# Run specific test file
uv run pytest tests/test_retrieval.py
# Run with verbose output
uv run pytest -v
```
## Code Generation
### Regenerate API Clients
When you modify the OpenAPI spec, regenerate the clients:
```bash
./scripts/generate-clients.sh
```
This generates:
- Python client in `hindsight-clients/python/`
- TypeScript client in `hindsight-clients/typescript/`
### Export OpenAPI Schema
```bash
./scripts/export-openapi.sh
```
## Project Structure
```
hindsight/
├── hindsight-api/ # Main API server
│ ├── hindsight_api/
│ │ ├── api/ # HTTP endpoints
│ │ ├── engine/ # Memory engine, retrieval, reasoning
│ │ └── web/ # Server entry point
│ └── tests/
├── hindsight-clients/ # Generated SDK clients
│ ├── python/
│ └── typescript/
├── hindsight-control-plane/ # Admin UI (Next.js)
├── docker/ # Docker Compose setup
└── scripts/ # Development scripts
```
## Contributing
1. Create a feature branch from `main`
2. Make your changes
3. Run tests: `uv run pytest`
4. Submit a pull request
## Troubleshooting
### Database Connection Issues
Ensure PostgreSQL is running:
```bash
docker-compose ps
```
Check database connectivity:
```bash
psql postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
```
### ML Model Download
On first run, Hindsight downloads embedding and reranking models. This may take a few minutes. Models are cached in `~/.cache/huggingface/`.
### Port Conflicts
If port 8888 is in use:
```bash
HINDSIGHT_API_PORT=8889 ./scripts/start-server.sh --env local
```
@@ -1,329 +0,0 @@
# Extensions
Extensions allow you to customize and extend Hindsight behavior without modifying core code. They enable multi-tenancy, custom authentication, additional HTTP endpoints, and operation hooks.
---
## Available Extensions
### TenantExtension
Handles multi-tenancy and API key authentication. Validates incoming requests and determines which PostgreSQL schema to use for database operations, enabling tenant isolation at the database level.
**Built-in: ApiKeyTenantExtension**
A simple implementation that validates API keys against an environment variable and uses the `public` schema for all authenticated requests.
```bash
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
HINDSIGHT_API_TENANT_API_KEY=your-secret-key
```
**Built-in: SupabaseTenantExtension**
Validates [Supabase](https://supabase.com) JWTs and provides multi-tenant memory isolation. Each authenticated user gets their own PostgreSQL schema (`{prefix}_{user_id}`), ensuring complete data separation. Performs local JWT verification using JWKS for optimal performance (no network call per request).
```bash
HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.supabase_tenant:SupabaseTenantExtension
HINDSIGHT_API_TENANT_SUPABASE_URL=https://your-project.supabase.co
# Optional - only needed for legacy HS256 projects or health check
HINDSIGHT_API_TENANT_SUPABASE_SERVICE_KEY=your-service-role-key
```
See the [source code](https://github.com/vectorize-io/hindsight/blob/main/hindsight-api-slim/hindsight_api/extensions/builtin/supabase_tenant.py) for complete configuration options and implementation details.
For other multi-tenant setups with separate schemas per tenant (e.g., custom JWT-based auth), implement a custom `TenantExtension`.
---
### HttpExtension
Adds custom HTTP endpoints under the `/ext/` path prefix. Useful for adding domain-specific APIs that integrate with Hindsight's memory engine.
Provides two router methods:
- `get_router(memory)` — returns a FastAPI router mounted at `/ext/`
- `get_root_router(memory)` — returns a FastAPI router mounted at the application root (for well-known endpoints or other paths that must be at specific locations). Returns `None` by default.
**No built-in implementation** - implement your own to add custom endpoints.
```bash
HINDSIGHT_API_HTTP_EXTENSION=mypackage.ext:MyHttpExtension
```
---
### OperationValidatorExtension
Hooks into retain/recall/reflect operations for validation and monitoring. Use cases include:
- Rate limiting and quota enforcement
- Permission checks and content filtering
- Audit logging and usage tracking
- Custom metrics collection
**No built-in implementation** - implement your own based on your requirements.
```bash
HINDSIGHT_API_OPERATION_VALIDATOR_EXTENSION=mypackage.validators:MyValidator
```
---
### MCPExtension
Registers additional MCP (Model Context Protocol) tools on the Hindsight MCP server. Enables external packages to add custom tools without modifying core code.
**No built-in implementation** - implement your own to add custom MCP tools.
```bash
HINDSIGHT_API_MCP_EXTENSION=mypackage.mcp:MyMCPExtension
```
---
## Writing Custom Extensions
### Extension Basics
Extensions are Python classes loaded via environment variables:
```bash
HINDSIGHT_API_<TYPE>_EXTENSION=mypackage.module:MyExtensionClass
```
Configuration is passed via prefixed environment variables:
```bash
HINDSIGHT_API_<TYPE>_SOME_CONFIG=value
# Extension receives: {"some_config": "value"}
```
All extensions support lifecycle hooks:
- `on_startup()` - Called when the application starts
- `on_shutdown()` - Called when the application shuts down
Extensions have access to an `ExtensionContext` that provides:
- `run_migration(schema)` - Run database migrations for a schema
- `get_memory_engine()` - Get the MemoryEngine interface
### Example: Custom TenantExtension with JWT
```python
import jwt
from hindsight_api.extensions import TenantExtension, TenantContext, AuthenticationError
class JwtTenantExtension(TenantExtension):
def __init__(self, config: dict[str, str]):
super().__init__(config)
self.jwt_secret = config.get("jwt_secret")
if not self.jwt_secret:
raise ValueError("HINDSIGHT_API_TENANT_JWT_SECRET is required")
async def authenticate(self, context: RequestContext) -> TenantContext:
token = context.api_key
if not token:
# Optional headers dict is forwarded in HTTP/MCP error responses
raise AuthenticationError("Bearer token required")
try:
payload = jwt.decode(token, self.jwt_secret, algorithms=["HS256"])
tenant_id = payload.get("tenant_id")
if not tenant_id:
raise AuthenticationError("Missing tenant_id in token")
return TenantContext(schema_name=f"tenant_{tenant_id}")
except jwt.InvalidTokenError as e:
raise AuthenticationError(str(e))
```
`AuthenticationError` accepts an optional `headers` dict that is forwarded in both HTTP and MCP error responses. This is useful for returning custom headers like `WWW-Authenticate`:
```python
raise AuthenticationError(
"Authorization required",
headers={"WWW-Authenticate": 'Bearer realm="example"'},
)
```
### Example: Custom HttpExtension
```python
from fastapi import APIRouter
from hindsight_api.extensions import HttpExtension
class MyHttpExtension(HttpExtension):
def get_router(self, memory: MemoryEngine) -> APIRouter:
router = APIRouter()
@router.get("/hello")
async def hello():
return {"message": "Hello from extension!"}
@router.post("/custom/{bank_id}/action")
async def custom_action(bank_id: str):
# Access memory engine for database operations
pool = await memory._get_pool()
# ... custom logic
return {"status": "ok"}
return router
def get_root_router(self, memory: MemoryEngine) -> APIRouter | None:
"""Optional: mount routes at the application root (not under /ext/)."""
router = APIRouter()
@router.get("/.well-known/my-metadata")
async def metadata():
return {"version": "1.0"}
return router
```
Routes from `get_router` are available at `/ext/hello`, `/ext/custom/{bank_id}/action`, etc.
Routes from `get_root_router` are mounted at the app root (e.g., `/.well-known/my-metadata`).
### Example: Custom OperationValidatorExtension
```python
from hindsight_api.extensions import (
OperationValidatorExtension,
ValidationResult,
RetainContext,
RecallContext,
ReflectContext,
RetainResult,
)
class MyValidator(OperationValidatorExtension):
# Pre-operation validation (required)
async def validate_retain(self, ctx: RetainContext) -> ValidationResult:
# Implement your validation logic
return ValidationResult.accept()
# Or reject: return ValidationResult.reject("Reason")
async def validate_recall(self, ctx: RecallContext) -> ValidationResult:
return ValidationResult.accept()
async def validate_reflect(self, ctx: ReflectContext) -> ValidationResult:
return ValidationResult.accept()
# Post-operation hooks (optional)
async def on_retain_complete(self, result: RetainResult) -> None:
# Log usage, update metrics, send notifications, etc.
pass
```
#### Deferring an operation
In addition to `accept` and `reject`, a `validate_*` hook can ask the
worker to **requeue** the operation for a future time by raising
`DeferOperation`. Use this for backpressure (rate-limited upstream,
quota window not yet open, dependency warming up) — unlike a retry, it
does not increment `retry_count` or write `error_message`. The worker
sets `next_retry_at` to your `exec_date` and the task is invisible to
claim queries until that time.
```python
from datetime import datetime, timedelta, timezone
from hindsight_api.extensions import (
DeferOperation,
OperationValidatorExtension,
RetainContext,
ValidationResult,
)
class QuotaAwareValidator(OperationValidatorExtension):
async def validate_retain(self, ctx: RetainContext) -> ValidationResult:
if not await self._quota_available(ctx.bank_id):
raise DeferOperation(
exec_date=datetime.now(timezone.utc) + timedelta(minutes=5),
reason="bank quota window exhausted",
)
return ValidationResult.accept()
```
`DeferOperation` is **worker-only**: do not raise it from
`validate_recall` or `validate_reflect` in synchronous HTTP request
paths — there is no queue to defer to and it will surface as a 500.
### Example: Custom MCPExtension
```python
from mcp.server.fastmcp import FastMCP
from hindsight_api.extensions import MCPExtension
from hindsight_api.engine import MemoryEngine
class MyMCPExtension(MCPExtension):
async def register_tools(self, mcp: FastMCP, memory: MemoryEngine) -> None:
@mcp.tool()
async def custom_search(query: str) -> str:
"""Custom MCP tool for specialized search."""
# Access memory engine for operations
pool = await memory._get_pool()
# ... custom logic
return f"Results for: {query}"
```
---
## Deploying Custom Extensions
### With Docker
Mount your extension package as a volume and set the environment variable:
```yaml
# docker-compose.yml
services:
hindsight-api:
image: vectorize/hindsight-api:latest
volumes:
- ./my_extensions:/app/my_extensions
environment:
- HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
- HINDSIGHT_API_TENANT_JWT_SECRET=${JWT_SECRET}
- PYTHONPATH=/app
```
Or build a custom image with your extensions:
```dockerfile
FROM vectorize/hindsight-api:latest
COPY my_extensions /app/my_extensions
ENV PYTHONPATH=/app
```
### Bare Metal
Install your extension package in the same Python environment as Hindsight:
```bash
# Install Hindsight
pip install hindsight-api
# Install your extension package
pip install ./my-extensions
# or
pip install my-extensions-package
# Configure
export HINDSIGHT_API_TENANT_EXTENSION=my_extensions.auth:JwtTenantExtension
export HINDSIGHT_API_TENANT_JWT_SECRET=your-secret
# Run
hindsight-api
```
---
## Contributing Extensions
Custom extensions that solve common use cases are welcome contributions to the Hindsight project. If you've built an extension for:
- Authentication providers (OAuth, SAML, API gateways)
- Rate limiting or quota management
- Audit logging integrations
- Metrics exporters (Datadog, New Relic, etc.)
- Custom HTTP endpoints for specific platforms
Consider contributing it to the `hindsight_api.extensions.builtin` package. Open an issue or pull request on [GitHub](https://github.com/vectorize-io/hindsight) to discuss your extension.
@@ -1,149 +0,0 @@
---
sidebar_position: 1
slug: /
---
import {ClientsGrid} from '@site/src/components/SupportedGrids';
# Overview
## Why Hindsight?
AI agents forget everything between sessions. Every conversation starts from zero—no context about who you are, what you've discussed, or what the assistant has learned. This isn't just an implementation detail; it fundamentally limits what AI Agents can do.
**The problem is harder than it looks:**
- **Simple vector search isn't enough** — "What did Alice do last spring?" requires temporal reasoning, not just semantic similarity
- **Facts get disconnected** — Knowing "Alice works at Google" and "Google is in Mountain View" should let you answer "Where does Alice work?" even if you never stored that directly
- **AI Agents need to consolidate knowledge** — A coding assistant that remembers "the user prefers functional programming" should consolidate this into an observation and weigh it when making recommendations
- **Context matters** — The same information means different things to different memory banks with different personalities
Hindsight solves these problems with a memory system designed specifically for AI agents.
## What Hindsight Does
```mermaid
graph LR
subgraph app["<b>Your Application</b>"]
Agent[AI Agent]
end
subgraph hindsight["<b>Hindsight</b>"]
API[API Server]
subgraph bank["<b>Memory Bank</b>"]
direction TB
MentalModels[Mental Models]
Observations[Observations]
MemEnt[Memories & Entities]
Chunks[Chunks]
Documents[Documents]
MentalModels --> Observations --> MemEnt --> Chunks --> Documents
end
end
Agent -->|retain| API
Agent -->|recall| API
Agent -->|reflect| API
API --> bank
```
**Your AI agent** stores information via `retain()`, searches with `recall()`, and reasons with `reflect()` — all interactions with its dedicated **memory bank**
## Key Components
### Memory Types
Hindsight organizes knowledge into a hierarchy of facts and consolidated knowledge:
| Type | What it stores | Example |
|------|----------------|---------|
| **Mental Model** | User-curated summaries for common queries | "Team communication best practices" |
| **Observation** | Automatically consolidated knowledge from facts | "User was a React enthusiast but has now switched to Vue" (captures history) |
| **World Fact** | Objective facts received | "Alice works at Google" |
| **Experience Fact** | Bank's own actions and interactions | "I recommended Python to Bob" |
During reflect, the agent checks sources in priority order: **Mental Models → Observations → Raw Facts**.
### Multi-Strategy Retrieval (TEMPR)
Four search strategies run in parallel:
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
| Strategy | Best for |
|----------|----------|
| **Semantic** | Conceptual similarity, paraphrasing |
| **Keyword (BM25)** | Names, technical terms, exact matches |
| **Graph** | Related entities, indirect connections |
| **Temporal** | "last spring", "in June", time ranges |
### Observation Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **observations** — deduplicated, evidence-grounded beliefs that the bank has built up across many memories:
- **Deduplication**: Overlapping facts are merged into a single durable observation instead of piling up as repeats
- **Evidence tracking**: Each observation references the source memories (with exact quotes) that support it, plus a proof count
- **Continuous refinement**: Observations are updated — not overwritten — when new evidence supports, contradicts, or extends them; history is preserved
- **Freshness trend**: Each observation carries a computed trend (stable / strengthening / weakening / stale) based on when its evidence arrived
### Mission, Directives & Disposition
Memory banks can be configured to shape how the agent reasons during `reflect`:
| Configuration | Purpose | Example |
|---------------|---------|---------|
| **Mission** | Natural language identity for the bank | "I am a research assistant specializing in ML. I prefer simplicity over cutting-edge." |
| **Directives** | Hard rules the agent must follow | "Never recommend specific stocks", "Always cite sources" |
| **Disposition** | Soft traits that influence reasoning style | Skepticism, literalism, empathy (1-5 scale) |
The **mission** tells Hindsight what knowledge to prioritize and provides context for reasoning. **Directives** are guardrails and compliance rules that must never be violated. **Disposition traits** subtly influence interpretation style.
These settings only affect the `reflect` operation, not `recall`.
## Clients & Languages
<ClientsGrid />
## Integrations
Browse all supported integrations in the [Integrations Hub](/integrations).
## Next Steps
### Getting Started
- [**Quick Start**](/developer/api/quickstart) — Install and get up and running in 60 seconds
- [**RAG vs Hindsight**](/developer/rag-vs-hindsight) — See how Hindsight differs from traditional RAG with real examples
### Core Concepts
- [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts
- [**Recall**](/developer/retrieval) — How TEMPR's 4-way search retrieves memories
- [**Reflect**](/developer/reflect) — How mission, directives, and disposition shape reasoning
### API Methods
- [**Retain**](/developer/api/retain) — Store information in memory banks
- [**Recall**](/developer/api/recall) — Search and retrieve memories
- [**Reflect**](/developer/api/reflect) — Agentic reasoning with memory
- [**Mental Models**](/developer/api/mental-models) — User-curated summaries for common queries
- [**Memory Banks**](/developer/api/memory-banks) — Configure mission, directives, and disposition
- [**Documents**](/developer/api/documents) — Manage document sources
- [**Operations**](/developer/api/operations) — Monitor async tasks
### Deployment
- [**Server Setup**](/developer/installation) — Deploy with Docker Compose, Helm, or pip
@@ -1,341 +0,0 @@
# Installation
Hindsight can be deployed in several ways depending on your infrastructure and requirements.
:::tip Don't want to manage infrastructure?
**[Hindsight Cloud](https://ui.hindsight.vectorize.io/signup)** is a fully managed service that handles all infrastructure, scaling, and maintenance — [sign up here](https://ui.hindsight.vectorize.io/signup).
:::
## Supported Platforms
Hindsight runs on **Linux**, **macOS**, and **Windows**:
| Platform | Docker | Bare Metal (pip) | Embedded DB (pg0) | Notes |
|----------|--------|------------------|--------------------|-------|
| **Linux** (x86_64, ARM64) | ✅ | ✅ | ✅ | Fully supported, recommended for production |
| **macOS** (Apple Silicon, Intel) | ✅ | ✅ | ✅ | Fully supported |
| **Windows** (x86_64) | ✅ | ✅ | ✅ | Fully supported — see [Windows setup](#windows) for external PostgreSQL option |
All platforms support the embedded database (pg0) for development. On Windows, you can also use an external PostgreSQL installation — see the [Windows](#windows) section for a step-by-step guide.
---
## Prerequisites
### PostgreSQL
Hindsight requires PostgreSQL 14+ with a vector extension for similarity search. The supported extensions are:
- **pgvector** (default)
- **pgvectorscale**
- **vchord**
Configure which one to use with `HINDSIGHT_API_VECTOR_EXTENSION`. See [Configuration](./configuration) for details.
**By default**, Hindsight uses **pg0** — an embedded PostgreSQL that runs locally on your machine. This is convenient for development but **not recommended for production**.
**For production**, use an external PostgreSQL with one of the supported vector extensions:
- **Supabase** — Managed PostgreSQL with pgvector built-in
- **Neon** — Serverless PostgreSQL with pgvector
- **Azure Database for PostgreSQL** — With pgvector and pgvectorscale support
- **AWS RDS** / **Cloud SQL** — With pgvector extension enabled
- **Self-hosted** — PostgreSQL 14+ with your preferred vector extension
### LLM Provider
You need an LLM API key for fact extraction, entity resolution, and answer generation. See [Models](./models) for supported providers, model recommendations, and configuration.
### Hardware
Hindsight is designed to run on commodity hardware. The footprint depends mainly on whether the **full** image (which bundles local embedding and reranker models) or the **slim** image (which delegates those to external providers) is used.
| Component | Minimum RAM | Recommended RAM | Notes |
|-----------|-------------|-----------------|-------|
| **API — Full image** | 1.5 GB | 2 GB | Loads local BGE embedder (~130 MB) and MiniLM cross-encoder (~90 MB) into memory, plus PyTorch/ONNX runtime arenas. Idle RSS settles around 0.81.0 GB; expect 1.21.5 GB under load. |
| **API — Slim image** | 512 MB | 1 GB | No local models. Steady-state RSS is dominated by Python runtime and DB connections. Requires [external embedding and reranker providers](./configuration#embeddings) (e.g. TEI, OpenAI, Cohere). |
| **Control Plane (UI)** | 128 MB | 256 MB | Next.js process, lightweight. |
| **Worker** (if separated) | Same as API image variant | Same as API image variant | Workers load the same models as the API server. |
| **PostgreSQL** | 512 MB | 1 GB+ | Scales with the number of memories and indexes. |
:::tip Reducing the footprint
The bulk of the full image's memory comes from the bundled embedding and reranker models and their PyTorch/ONNX runtimes. To shrink the deployment to a few hundred MB of RAM, switch to the **slim** image and configure [external embedding and reranker providers](./configuration#embeddings).
:::
CPU vs GPU: 2 vCPUs on CPU-only is fine for development and basic workloads. For production traffic, the local reranker (cross-encoder) is the main bottleneck and typically benefits from a GPU to keep recall latency reasonable; alternatively, offload reranking to an [external reranker provider](./configuration#embeddings) (e.g. TEI, Cohere) on dedicated GPU hardware.
---
## Docker
**Best for**: Quick start, development, small deployments
Run everything in one container with embedded PostgreSQL:
```bash
export OPENAI_API_KEY=sk-xxx
docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
-v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest
```
- **API Server**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999
### Docker Image Variants
| Variant | Size (AMD64) | Size (ARM64) | When to use |
|---------|--------------|--------------|-------------|
| **Full** (`latest`) | ~9 GB | ~3.7 GB | Default. Works out of the box with no external services except the LLM. |
| **Slim** (`slim`) | ~500 MB | ~500 MB | Use when you already rely on external services for embeddings and reranking (OpenAI, Cohere, TEI). Significantly smaller image, faster deploys. Requires [external providers](./configuration#embeddings). |
The slim image corresponds to the [`hindsight-api-slim`](#bare-metal-pip) pip package. See [Configuration](./configuration#embeddings) for external provider options.
### Available Tags
```bash
# Standalone (API + Control Plane)
ghcr.io/vectorize-io/hindsight:latest # Full, latest release
ghcr.io/vectorize-io/hindsight:latest-slim # Slim, latest release
ghcr.io/vectorize-io/hindsight:0.4.9 # Full, specific version
ghcr.io/vectorize-io/hindsight:0.4.9-slim # Slim, specific version
# API only
ghcr.io/vectorize-io/hindsight-api:latest
ghcr.io/vectorize-io/hindsight-api:latest-slim
# Control Plane only
ghcr.io/vectorize-io/hindsight-control-plane:latest
```
---
## Helm / Kubernetes
**Best for**: Production deployments, auto-scaling, cloud environments
```bash
# Install with built-in PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=true
# Or use external PostgreSQL
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=false \
--set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight
# Install a specific version
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
# Upgrade to latest
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
```
**Requirements**:
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
- Helm 3.8+
### Distributed Workers
For high-throughput deployments, enable dedicated worker pods to scale task processing independently:
```bash
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set worker.enabled=true \
--set worker.replicaCount=3
```
See [Services - Worker Service](./services#worker-service) for configuration details and architecture.
See the [Helm chart values.yaml](https://github.com/vectorize-io/hindsight/tree/main/helm/hindsight/values.yaml) for all chart options.
---
## Bare Metal (pip)
**Best for**: Running Hindsight as a standalone service on a host machine.
### Install
```bash
pip install hindsight-api # Full — works out of the box
pip install hindsight-api-slim # Slim — requires external services for embeddings, reranking, and the database
```
When using `hindsight-api-slim`, you must configure external providers for all model operations. See [Configuration](./configuration#embeddings) for details.
### Run with Embedded Database
For development and testing, Hindsight can run with an embedded PostgreSQL (pg0):
```bash
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
This creates a database in `~/.hindsight/data/` and starts the API on http://localhost:8888.
### Run with External PostgreSQL
For production, connect to your own PostgreSQL instance:
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://user:pass@localhost:5432/hindsight
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
**Note**: The database must exist and have pgvector enabled (`CREATE EXTENSION vector;`).
### CLI Options
```bash
hindsight-api --port 9000 # Custom port (default: 8888)
hindsight-api --host 127.0.0.1 # Bind to localhost only
hindsight-api --workers 4 # Multiple worker processes
hindsight-api --log-level debug # Verbose logging
```
### Control Plane
The Control Plane (Web UI) can be run standalone using npx:
```bash
npx @vectorize-io/hindsight-control-plane --api-url http://localhost:8888
```
This connects to your running API server and provides a visual interface for managing memory banks, exploring entities, and testing queries.
#### Options
| Option | Environment Variable | Default | Description |
|--------|---------------------|---------|-------------|
| `-p, --port` | `PORT` | 9999 | Port to listen on |
| `-H, --hostname` | `HOSTNAME` | 0.0.0.0 | Hostname to bind to |
| `-a, --api-url` | `HINDSIGHT_CP_DATAPLANE_API_URL` | http://localhost:8888 | Hindsight API URL |
#### Examples
```bash
# Run on custom port
npx @vectorize-io/hindsight-control-plane --port 9999 --api-url http://localhost:8888
# Using environment variables
export HINDSIGHT_CP_DATAPLANE_API_URL=http://api.example.com
npx @vectorize-io/hindsight-control-plane
# Production deployment
PORT=80 HINDSIGHT_CP_DATAPLANE_API_URL=https://api.hindsight.io npx @vectorize-io/hindsight-control-plane
```
---
## Windows
**Best for**: Running Hindsight natively on Windows without Docker
Hindsight works on Windows with the embedded database (pg0) out of the box — just install and run:
```powershell
pip install hindsight-api
set HINDSIGHT_API_LLM_PROVIDER=openai
set HINDSIGHT_API_LLM_API_KEY=sk-xxx
set HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
hindsight-api
```
### Using External PostgreSQL (optional)
If you prefer to use your own PostgreSQL instance instead of the embedded database:
```powershell
# Install PostgreSQL
winget install PostgreSQL.PostgreSQL.17
# Build pgvector (requires Visual Studio Build Tools)
git clone https://github.com/pgvector/pgvector.git
cd pgvector
# Open "x64 Native Tools Command Prompt for VS" and run:
set PGROOT=C:\Program Files\PostgreSQL\17
nmake /F Makefile.win
nmake /F Makefile.win install
# Create the database and enable the vector extension
psql -U postgres -c "CREATE DATABASE hindsight;"
psql -U postgres -d hindsight -c "CREATE EXTENSION vector;"
```
Then run Hindsight pointing to your database:
```powershell
pip install hindsight-api
set HINDSIGHT_API_DATABASE_URL=postgresql://postgres@localhost:5432/hindsight
set HINDSIGHT_API_LLM_PROVIDER=openai
set HINDSIGHT_API_LLM_API_KEY=sk-xxx
set HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
hindsight-api
```
- **API Server**: http://localhost:8888
:::tip
You can also use the slim package (`pip install hindsight-api-slim`) if you configure external providers for embeddings and reranking. See [Configuration](./configuration#embeddings) for details.
:::
---
## Embedded in a Python Application
**Best for**: Using Hindsight programmatically from Python without running a separate server process.
```bash
pip install hindsight-all # Full — works out of the box
pip install hindsight-all-slim # Slim — requires external services for embeddings, reranking, and the database
```
`hindsight-all` supports two modes of embedding:
**In-process** (`HindsightServer`): the server runs in a background thread inside your application. Best when you want the tightest integration and are already managing your own process lifecycle.
```python
from hindsight import HindsightServer, HindsightClient
with HindsightServer(llm_provider="openai", llm_api_key="sk-xxx") as server:
client = HindsightClient(base_url=server.url)
client.retain(bank_id="alice", content="Alice prefers concise answers.")
results = client.recall(bank_id="alice", query="How should I respond to Alice?")
```
**Managed subprocess** (`HindsightEmbedded`): the server runs as a background daemon process, shared across multiple Python processes or sessions. The daemon starts on first use and shuts down automatically after an idle timeout.
```python
from hindsight import HindsightEmbedded
client = HindsightEmbedded(llm_provider="openai", llm_api_key="sk-xxx")
client.retain(bank_id="alice", content="Alice prefers concise answers.")
results = client.recall(bank_id="alice", query="How should I respond to Alice?")
```
See the [Python SDK](../sdks/python.md) for the full API reference.
---
## Next Steps
- [Configuration](./configuration.md) — Environment variables and settings
- [Models](./models.mdx) — ML models and providers
- [Monitoring](./monitoring.md) — Metrics and observability
@@ -1,512 +0,0 @@
---
sidebar_position: 5
---
# MCP Server
Hindsight includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that allows AI assistants to store and retrieve memories directly.
## Access
The MCP server is **enabled by default** and mounted at `/mcp` on the API server. Each memory bank has its own MCP endpoint:
```
http://localhost:8888/mcp/{bank_id}/
```
For example, to connect to the memory bank `alice`:
```
http://localhost:8888/mcp/alice/
```
To disable the MCP server, set the environment variable:
```bash
export HINDSIGHT_API_MCP_ENABLED=false
```
## Authentication
By default, the MCP endpoint is **open** (no authentication required).
To enable authentication, configure the API key tenant extension:
```bash
export HINDSIGHT_API_TENANT_EXTENSION=hindsight_api.extensions.builtin.tenant:ApiKeyTenantExtension
export HINDSIGHT_API_TENANT_API_KEY=your-secret-key
```
When authentication is enabled, include your API key in the `Authorization` header:
### Claude Code
```bash
claude mcp add --transport http hindsight http://localhost:8888/mcp \
--header "Authorization: Bearer your-secret-key" \
--header "X-Bank-Id: my-bank"
```
### Claude Desktop
Add to `~/.claude_desktop_config.json`:
```json
{
"mcpServers": {
"hindsight": {
"url": "http://localhost:8888/mcp",
"headers": {
"Authorization": "Bearer your-secret-key",
"X-Bank-Id": "my-bank"
}
}
}
}
```
### Direct HTTP Request
```bash
curl -X POST http://localhost:8888/mcp \
-H "Authorization: Bearer your-secret-key" \
-H "X-Bank-Id: my-bank" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
```
If the key is missing or invalid, requests will receive a `401 Unauthorized` response.
## Bank Selection
The memory bank is resolved in this priority order:
1. **URL path** (highest priority): `http://localhost:8888/mcp/my-bank/`
2. **X-Bank-Id header**: `--header "X-Bank-Id: my-bank"`
3. **Default**: Uses `HINDSIGHT_MCP_BANK_ID` env var (default: "default")
## Per-Bank Endpoints
Unlike traditional MCP servers where tools require explicit identifiers, Hindsight uses **per-bank endpoints**. The `bank_id` is part of the URL path, so tools don't need to specify which bank to use—it's implicit from the connection.
This design:
- **Simplifies tool usage** — no need to pass `bank_id` with every call
- **Enforces isolation** — each MCP connection is scoped to a single bank
- **Enables multi-tenant setups** — connect different users to different endpoints
## Two Modes
The MCP server operates in two modes depending on the URL:
| Mode | URL | Tools | bank_id |
|------|-----|-------|---------|
| **Single-bank** | `/mcp/{bank_id}/` | 26 tools (memory, mental models, directives, documents, operations, tags, bank management) | Implicit from URL |
| **Multi-bank** | `/mcp/` | All 29 tools including `list_banks`, `create_bank`, `get_bank_stats` | Explicit `bank_id` parameter on each tool |
**Single-bank mode** (recommended) scopes all operations to the bank in the URL. Tools don't expose a `bank_id` parameter.
**Multi-bank mode** exposes all tools with an optional `bank_id` parameter, plus bank management tools (`list_banks`, `create_bank`, `get_bank_stats`).
---
## Available Tools
### retain
Store information to long-term memory.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `content` | string | Yes | The fact or memory to store |
| `context` | string | No | Category for the memory (default: `general`) |
| `timestamp` | string | No | ISO 8601 timestamp for when the event occurred |
| `tags` | list[string] | No | Tags for organizing and filtering this memory |
| `metadata` | object | No | Key-value metadata to attach (e.g., `{"source": "slack"}`) |
| `document_id` | string | No | Associate this memory with an existing document |
**Example:**
```json
{
"name": "retain",
"arguments": {
"content": "User prefers Python over JavaScript for backend development",
"context": "programming_preferences",
"tags": ["user:alice", "preferences"]
}
}
```
**When to use:**
- User shares personal facts, preferences, or interests
- Important events or milestones are mentioned
- Decisions, opinions, or goals are stated
- Work context or project details are discussed
---
### recall
Search memories to provide personalized responses.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | Natural language search query |
| `max_tokens` | integer | No | Maximum tokens to return (default: 4096) |
| `budget` | string | No | Search thoroughness: `low`, `mid`, or `high` (default: `high`) |
| `types` | list[string] | No | Filter by fact type: `world`, `experience`, `opinion`. Defaults to all |
| `tags` | list[string] | No | Filter memories by tags |
| `tags_match` | string | No | Tag matching mode: `any` (default) or `all` |
| `query_timestamp` | string | No | ISO 8601 timestamp — recall as if asking at this point in time |
**Example:**
```json
{
"name": "recall",
"arguments": {
"query": "What are the user's programming language preferences?",
"tags": ["preferences"],
"budget": "high"
}
}
```
**When to use:**
- Start of conversation to recall relevant context
- Before making recommendations
- When user asks about something they may have mentioned before
- To provide continuity across conversations
---
### reflect
Generate thoughtful analysis by synthesizing stored memories with the bank's personality.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | The question or topic to reflect on |
| `context` | string | No | Optional context about why this reflection is needed |
| `budget` | string | No | Search budget: `low`, `mid`, or `high` (default: `low`) |
| `max_tokens` | integer | No | Maximum tokens in the response (default: 4096) |
| `response_schema` | object | No | JSON Schema for structured output. When provided, the response includes a `structured_output` field |
| `tags` | list[string] | No | Filter memories by tags before reflecting |
| `tags_match` | string | No | Tag matching mode: `any` (default) or `all` |
**Example:**
```json
{
"name": "reflect",
"arguments": {
"query": "Based on my past decisions, what architectural style do I prefer?",
"budget": "mid",
"tags": ["architecture"]
}
}
```
**When to use:**
- When reasoned analysis is needed, not just fact retrieval
- Questions like "What should I do?" rather than "What did I say?"
- Synthesizing patterns across multiple memories
---
### create_mental_model
Create a mental model — a living document that stays current with your memories. Mental models are pre-computed reflections that get automatically refreshed as new memories are stored.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query used to generate and refresh the model |
| `mental_model_id` | string | No | Custom ID (alphanumeric lowercase with hyphens). Auto-generated if not provided |
| `tags` | list[string] | No | Tags for organizing and filtering models |
| `max_tokens` | integer | No | Maximum tokens for model content (default: 2048) |
| `trigger_refresh_after_consolidation` | boolean | No | Auto-refresh this model after memory consolidation (default: `false`) |
**Example:**
```json
{
"name": "create_mental_model",
"arguments": {
"name": "Team Directory",
"source_query": "Who works here and what do they do?",
"tags": ["team", "people"]
}
}
```
Content generation runs asynchronously. The response includes an `operation_id` to track progress.
---
### list_mental_models
List all mental models in a bank, optionally filtered by tags.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | list[string] | No | Filter models by tags |
---
### get_mental_model
Retrieve a specific mental model by ID, including its full content.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to retrieve |
---
### update_mental_model
Update a mental model's metadata or settings.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to update |
| `name` | string | No | New name |
| `source_query` | string | No | New source query |
| `tags` | list[string] | No | New tags |
| `max_tokens` | integer | No | New max tokens |
| `trigger_refresh_after_consolidation` | boolean | No | Auto-refresh after consolidation. Only set when you want to change this setting |
---
### delete_mental_model
Permanently delete a mental model.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to delete |
---
### refresh_mental_model
Re-generate a mental model's content from the latest memories. Runs asynchronously.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mental_model_id` | string | Yes | The ID of the mental model to refresh |
---
### list_banks (multi-bank mode only)
List all available memory banks.
---
### create_bank (multi-bank mode only)
Create a new memory bank or retrieve an existing one.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bank_id` | string | Yes | The ID for the new bank |
| `name` | string | No | Human-friendly name for the bank |
| `mission` | string | No | Mission describing who the agent is and what they're trying to accomplish |
---
### list_directives
List all directives in a bank. Directives are instructions that guide how the memory system processes and responds to queries.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | list[string] | No | Filter directives by tags |
| `active_only` | boolean | No | Only return active directives (default: `true`) |
---
### create_directive
Create a new directive in a bank.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the directive |
| `content` | string | Yes | The directive content/instruction |
| `priority` | integer | No | Priority level (higher = more important) |
| `is_active` | boolean | No | Whether the directive is active (default: `true`) |
| `tags` | list[string] | No | Tags for organizing directives |
---
### delete_directive
Delete a directive by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `directive_id` | string | Yes | The ID of the directive to delete |
---
### list_memories
Browse stored memories with optional filtering and pagination.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Filter by fact type: `world`, `experience`, or `opinion` |
| `q` | string | No | Search query to filter memories |
| `limit` | integer | No | Maximum number of results (default: 100) |
| `offset` | integer | No | Number of results to skip for pagination (default: 0) |
---
### get_memory
Retrieve a specific memory by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `memory_id` | string | Yes | The ID of the memory to retrieve |
---
### list_documents
List documents that have been ingested into the memory bank.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | No | Search query to filter documents |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_document
Retrieve a specific document by ID, including its metadata.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `document_id` | string | Yes | The ID of the document to retrieve |
---
### delete_document
Delete a document and all memories linked to it.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `document_id` | string | Yes | The ID of the document to delete |
---
### list_operations
List async operations (retain processing, mental model refresh, etc.) with optional status filtering.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by status: `pending`, `running`, `completed`, `failed`, `cancelled` |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_operation
Get the status and details of an async operation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation_id` | string | Yes | The ID of the operation to check |
---
### cancel_operation
Cancel a pending or running async operation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `operation_id` | string | Yes | The ID of the operation to cancel |
---
### list_tags
List all unique tags used in a bank, optionally filtered by pattern.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | No | Glob pattern to filter tags (e.g., `project:*`) |
| `limit` | integer | No | Maximum number of results (default: 100) |
---
### get_bank
Get information about a memory bank, including its name, mission, and disposition.
---
### get_bank_stats (multi-bank mode only)
Get statistics for a memory bank (node/link counts).
---
### update_bank
Update a memory bank's configuration. Updates the bank's name and/or any bank-level configuration fields — only provided fields are updated; omitted fields remain unchanged.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | Human-friendly display name for the bank |
| `mission` | string | No | **Deprecated** — alias for `config_updates.reflect_mission` |
| `config_updates` | object | No | Dictionary of configuration fields to update. Supports all bank-configurable fields (see below). Non-configurable or credential fields are rejected |
The `config_updates` object accepts any bank-configurable field by its Python field name, including:
- `reflect_mission` — mission/context for Reflect operations
- `retain_mission` — steers what gets extracted during `retain()`
- `retain_extraction_mode``concise` (default), `verbose`, or `custom`
- `retain_custom_instructions` — custom extraction prompt (active when mode is `custom`)
- `retain_chunk_size` — maximum token size for each content chunk
- `retain_chunk_batch_size` — number of chunks to process in parallel
- `enable_observations` — toggle observation consolidation after `retain()`
- `observations_mission` — controls observation synthesis rules
- `disposition_skepticism` — critical evaluation level (15)
- `disposition_literalism` — literal vs. abstract interpretation (15)
- `disposition_empathy` — emotional context consideration (15)
- `entity_labels` — controlled vocabulary for entity classification
- `entities_allow_free_form` — allow labels outside `entity_labels`
- `recall_include_chunks` — include raw chunks in recall results
- `recall_max_tokens` — max tokens for recall results
- `mcp_enabled_tools` — tool allowlist for this bank
---
### delete_bank
Permanently delete a memory bank and all its data (memories, documents, entities, mental models).
---
### clear_memories
Clear all memories from a bank without deleting the bank itself. Optionally filter by fact type to only clear specific kinds of memories.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Fact type to clear: `world`, `experience`, or `opinion`. If not specified, clears all |
---
## Integration with AI Assistants
The MCP server can be used with any MCP-compatible AI assistant. See the [Authentication](#authentication) section above for Claude Code and Claude Desktop configuration examples.
Each user can have their own configuration pointing to their personal memory bank using either:
- A bank-specific URL path like `/mcp/alice/` (recommended)
- The `X-Bank-Id` header
@@ -1,568 +0,0 @@
import {LLMProvidersGrid} from '@site/src/components/SupportedGrids';
import {LLMProvidersTable} from '@site/src/components/LLMProvidersTable';
# Models
Hindsight uses several machine learning models for different tasks.
## Overview
- **LLM** — Fact extraction, reasoning, and generation. Provider-specific, fully configurable.
- **Embedding** — Vector representations for semantic search. Default: `BAAI/bge-small-en-v1.5`.
- **Cross-Encoder** — Reranking search results. Default: `cross-encoder/ms-marco-MiniLM-L-6-v2`.
Embedding and cross-encoder models are downloaded automatically from HuggingFace on first run.
---
## LLM
Used for fact extraction, entity resolution, mental model consolidation, and answer synthesis.
**Supported providers:**
<LLMProvidersGrid />
Also supports **any OpenAI-compatible API** (e.g., Azure OpenAI, Together AI, Fireworks) and **100+ providers via LiteLLM** (e.g., AWS Bedrock, Azure OpenAI, Together AI).
:::tip OpenAI-Compatible Providers
Hindsight works with any provider that exposes an OpenAI-compatible API (e.g., Azure OpenAI). Simply set `HINDSIGHT_API_LLM_PROVIDER=openai` and configure `HINDSIGHT_API_LLM_BASE_URL` to point to your provider's endpoint.
See [Configuration](./configuration#llm-provider) for setup examples.
:::
:::tip AWS Bedrock
Set `HINDSIGHT_API_LLM_PROVIDER=bedrock` to use AWS Bedrock models directly. Model names use Bedrock model IDs (e.g., `us.amazon.nova-2-lite-v1:0`). No API key is required — authentication uses AWS credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION_NAME`) or IAM roles.
See [Configuration](./configuration#llm-provider) for setup examples.
:::
:::tip Built-in llama.cpp (fully local, no API key)
Set `HINDSIGHT_API_LLM_PROVIDER=llamacpp` to run a built-in llama.cpp server with no external dependencies. A Gemma 4 E2B GGUF model (~3.5 GB) is auto-downloaded on first run. Requires the `local-llm` extra: `pip install 'hindsight-api-slim[local-llm]'`.
See [Configuration](./configuration#built-in-llamacpp) for all options.
:::
:::tip LiteLLM Provider (Azure, Together AI, and more)
Set `HINDSIGHT_API_LLM_PROVIDER=litellm` to use any model supported by [LiteLLM](https://docs.litellm.ai/docs/providers), including **Azure OpenAI**, **Together AI**, **Fireworks AI**, and many more. Model names use LiteLLM's provider prefix format (e.g., `azure/gpt-4o`).
See [Configuration](./configuration#llm-provider) for setup examples.
:::
### Benchmarks
Not sure which model to use? The **[Model Leaderboard](https://benchmarks.hindsight.vectorize.io/)** benchmarks models across accuracy, speed, cost, and reliability for retain, reflect, and observation consolidation so you can pick the right trade-off for your use case.
[![Model Leaderboard](/img/leaderboard.png)](https://benchmarks.hindsight.vectorize.io/)
### Tested Models
The following models have been tested and verified to work correctly with Hindsight:
| Provider | Model |
|----------|-------|
| **OpenAI** | `gpt-5.2` |
| **OpenAI** | `gpt-5` |
| **OpenAI** | `gpt-5-mini` |
| **OpenAI** | `gpt-5-nano` |
| **OpenAI** | `gpt-4.1-mini` |
| **OpenAI** | `gpt-4.1-nano` |
| **OpenAI** | `gpt-4o-mini` |
| **Anthropic** | `claude-sonnet-4-20250514` |
| **Anthropic** | `claude-3-5-sonnet-20241022` |
| **Gemini** | `gemini-3-pro-preview` |
| **Gemini** | `gemini-2.5-flash` |
| **Gemini** | `gemini-2.5-flash-lite` |
| **Groq** | `openai/gpt-oss-120b` |
| **Groq** | `openai/gpt-oss-20b` |
### Provider Default Models
Each provider has a recommended default model that's used when `HINDSIGHT_API_LLM_MODEL` is not explicitly set. This makes configuration simpler - just specify the provider and get a sensible default:
<LLMProvidersTable />
**Example:** Setting just the provider uses its default model:
```bash
# Uses claude-haiku-4-5-20251001 automatically
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
```
You can override the default by explicitly setting `HINDSIGHT_API_LLM_MODEL`:
```bash
# Override to use Sonnet instead
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-5-20250929
```
This also applies to per-operation overrides:
```bash
# Global: OpenAI gpt-4o-mini (default)
export HINDSIGHT_API_LLM_PROVIDER=openai
# Retain: Anthropic claude-haiku-4-5-20251001 (default)
export HINDSIGHT_API_RETAIN_LLM_PROVIDER=anthropic
```
### Using Other Models
Other LLM models not listed above may work with Hindsight, but they must support **at least 65,000 output tokens** to ensure reliable fact extraction. If you need support for a specific model that doesn't meet this requirement, please [open an issue](https://github.com/hindsight-ai/hindsight/issues) to request an exception.
:::tip Models with Limited Output Tokens
If your model only supports 32k or fewer output tokens (e.g., some older models), you can reduce the retain completion token limit:
```bash
# For models that support 32k output tokens
export HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS=32000
# For models that support 16k output tokens
export HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS=16000
```
**Important:** `HINDSIGHT_API_RETAIN_MAX_COMPLETION_TOKENS` must be greater than `HINDSIGHT_API_RETAIN_CHUNK_SIZE` (default: 3000). The system will validate this on startup and provide an error message if the configuration is invalid.
:::
### Configuration
```bash
# Groq (recommended)
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
# OpenAI
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o
# Gemini
export HINDSIGHT_API_LLM_PROVIDER=gemini
export HINDSIGHT_API_LLM_API_KEY=xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
# Anthropic
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Ollama (local)
export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
export HINDSIGHT_API_LLM_MODEL=llama3
# LM Studio (local)
export HINDSIGHT_API_LLM_PROVIDER=lmstudio
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
export HINDSIGHT_API_LLM_MODEL=your-local-model
# MiniMax (1M context window)
export HINDSIGHT_API_LLM_PROVIDER=minimax
export HINDSIGHT_API_LLM_API_KEY=your-minimax-api-key
export HINDSIGHT_API_LLM_MODEL=MiniMax-M2.7
# DeepSeek (https://api.deepseek.com)
export HINDSIGHT_API_LLM_PROVIDER=deepseek
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=deepseek-v4-flash # or deepseek-v4-pro / deepseek-chat / deepseek-reasoner
# Vertex AI (Google Cloud)
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-gcp-project-id
# Optional: region (default: us-central1)
# export HINDSIGHT_API_LLM_VERTEXAI_REGION=us-central1
# Optional: service account key (otherwise uses ADC)
# export HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY=/path/to/key.json
```
**Note:** The LLM is the primary bottleneck for retain operations. See [Performance](./performance) for optimization strategies.
---
### OpenAI Codex Setup (ChatGPT Plus/Pro)
Use your ChatGPT Plus or Pro subscription for Hindsight without separate OpenAI Platform API costs.
**Prerequisites:**
- Active ChatGPT Plus or Pro subscription
- Node.js/npm installed (for Codex CLI)
**Setup Steps:**
1. **Install Codex CLI:**
```bash
npm install -g @openai/codex
```
2. **Login with ChatGPT credentials:**
```bash
codex auth login
```
This opens a browser window to authenticate with your ChatGPT account and saves OAuth tokens to `~/.codex/auth.json`.
3. **Verify authentication:**
```bash
ls ~/.codex/auth.json # Should show the auth file exists
```
4. **Configure Hindsight:**
```bash
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
# export HINDSIGHT_API_LLM_MODEL=gpt-5.1-codex # defaults to gpt-5.2-codex
# No API key needed - reads from ~/.codex/auth.json automatically
```
5. **Start Hindsight:**
```bash
hindsight-api
```
You can use any model supported by OpenAI Codex CLI
**Important Notes:**
- OAuth tokens are stored in `~/.codex/auth.json`
- Tokens refresh automatically when needed
- Usage is billed to your ChatGPT subscription (not separate API costs)
- For personal development use only (see ChatGPT Terms of Service)
---
### Claude Code Setup (Claude Pro/Max)
Use your Claude Pro or Max subscription for Hindsight without separate Anthropic API costs.
:::warning Terms of Service Notice
This integration uses the Claude Agent SDK with your personal Claude Pro/Max subscription
credentials. You must be logged into Claude Code on your own machine before using this provider.
**Please be aware:**
- Anthropic's [Agent SDK documentation](https://docs.claude.com/en/api/agent-sdk/overview)
states that third-party developers should not offer claude.ai login or rate limits for
their products. Hindsight does **not** perform any login on your behalf — it uses
credentials you've already authenticated via `claude auth login`.
- In January 2026, Anthropic [enforced restrictions](https://paddo.dev/blog/anthropic-walled-garden-crackdown/)
against third-party tools using Claude subscription OAuth tokens. Those restrictions
targeted tools that **spoofed the Claude Code client identity** — Hindsight uses the
official Claude Agent SDK instead.
- This provider is intended for **local, personal development use only**. Do not use it
in production deployments or shared environments.
- Anthropic's terms may change. If you want guaranteed compliance, use the `anthropic`
provider with an API key instead.
- Usage counts against your Claude Pro/Max subscription limits.
For production or team use, we recommend using `HINDSIGHT_API_LLM_PROVIDER=anthropic` with
an API key from the [Anthropic Console](https://console.anthropic.com/).
:::
**Prerequisites:**
- Active Claude Pro or Max subscription
- Claude Code CLI installed
**Setup Steps:**
1. **Install Claude Code CLI:**
```bash
npm install -g @anthropics/claude-code
# Or via Homebrew
brew install anthropics/claude-code/claude-code
```
2. **Login with Claude credentials:**
```bash
claude auth login
```
This opens a browser window to authenticate with your Claude account. Authentication is automatically managed by the Claude Agent SDK.
3. **Verify authentication:**
```bash
claude --version
# Should show version without errors
```
4. **Configure Hindsight:**
```bash
export HINDSIGHT_API_LLM_PROVIDER=claude-code
# No API key needed - uses claude auth login credentials
```
5. **Start Hindsight:**
```bash
hindsight-api
```
You can use any model supported by Claude Code CLI.
**Important Notes:**
- Authentication handled by Claude Agent SDK (uses bundled CLI)
- Credentials managed securely by Claude Code
- Usage billed to your Claude subscription (not separate API costs)
- For personal development use only (see Claude Terms of Service)
---
### Vertex AI Setup (Google Cloud)
Google Cloud's Vertex AI provides access to Gemini models via the native Google GenAI SDK.
**Prerequisites:**
- GCP project with Vertex AI API enabled
- IAM role `roles/aiplatform.user` for your credentials
**Environment Variables:**
| Variable | Description | Required |
|----------|-------------|----------|
| `HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID` | Your GCP project ID | Yes |
| `HINDSIGHT_API_LLM_VERTEXAI_REGION` | GCP region (e.g., `us-central1`) | No (default: `us-central1`) |
| `HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY` | Path to service account JSON key file | No (uses ADC if not set) |
**Authentication Methods:**
1. **Application Default Credentials (ADC)** - Recommended for development
```bash
# Setup ADC
gcloud auth application-default login
# Configure Hindsight
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-project-id
```
2. **Service Account Key** - Recommended for production
```bash
# Create service account and download key
gcloud iam service-accounts create hindsight-api
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:[email protected]" \
--role="roles/aiplatform.user"
gcloud iam service-accounts keys create key.json \
--iam-account=hindsight-api@your-project-id.iam.gserviceaccount.com
# Configure Hindsight
export HINDSIGHT_API_LLM_PROVIDER=vertexai
export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash-001
export HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID=your-project-id
export HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY=/path/to/key.json
```
**Notes:**
- Model names can optionally include the `google/` prefix (e.g., `google/gemini-2.0-flash-001`) — it will be stripped automatically
- The native SDK handles token refresh automatically
- Uses service account credentials if provided, otherwise falls back to ADC
---
## Embedding Model
Converts text into dense vector representations for semantic similarity search.
**Default:** `BAAI/bge-small-en-v1.5` (384 dimensions, ~130MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers (default) | Development, low latency |
| `openai` | OpenAI embeddings API | Production, high quality |
| `cohere` | Cohere embeddings API | Production, multilingual |
| `google` | Google embeddings (Gemini API or Vertex AI) | Production, multilingual, high quality |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
### Local Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `BAAI/bge-small-en-v1.5` | 384 | Default, fast, good quality |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
### OpenAI Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `text-embedding-3-small` | 1536 | Default OpenAI, cost-effective |
| `text-embedding-3-large` | 3072 | Higher quality, more expensive |
| `text-embedding-ada-002` | 1536 | Legacy model |
### Google Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `gemini-embedding-001` | 768 (configurable) | Default Google, general purpose |
Google's `gemini-embedding-001` supports configurable output dimensionality via truncation, google recommend using: 768, 1536, 3072, via `HINDSIGHT_API_EMBEDDINGS_GEMINI_OUTPUT_DIMENSIONALITY`. Default is 768.
### Cohere Models
| Model | Dimensions | Use Case |
|-------|------------|----------|
| `embed-english-v3.0` | 1024 | English text |
| `embed-multilingual-v3.0` | 1024 | 100+ languages |
:::warning Embedding Dimensions
Hindsight automatically detects the embedding dimension at startup and adjusts the database schema. Once memories are stored, you cannot change dimensions without losing data.
:::
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=local
export HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5
# OpenAI
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai
export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_EMBEDDINGS_OPENAI_MODEL=text-embedding-3-small
# Cohere
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_EMBEDDINGS_COHERE_MODEL=embed-english-v3.0
# Google (API key auth)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=google
export HINDSIGHT_API_EMBEDDINGS_GEMINI_API_KEY=xxxxxxxxxxxx
export HINDSIGHT_API_EMBEDDINGS_GEMINI_MODEL=gemini-embedding-001
# Google (Vertex AI auth - auto-detected when project ID is set)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=google
export HINDSIGHT_API_EMBEDDINGS_GEMINI_MODEL=gemini-embedding-001
export HINDSIGHT_API_EMBEDDINGS_VERTEXAI_PROJECT_ID=your-gcp-project-id
# TEI (self-hosted)
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=tei
export HINDSIGHT_API_EMBEDDINGS_TEI_URL=http://localhost:8080
# LiteLLM proxy
export HINDSIGHT_API_EMBEDDINGS_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_EMBEDDINGS_LITELLM_MODEL=text-embedding-3-small
```
See [Configuration](./configuration#embeddings) for all options including Azure OpenAI and custom endpoints.
---
## Cross-Encoder (Reranker)
Reranks initial search results to improve precision.
**Default:** `cross-encoder/ms-marco-MiniLM-L-6-v2` (~85MB)
### Supported Providers
| Provider | Description | Best For |
|----------|-------------|----------|
| `local` | SentenceTransformers CrossEncoder (default) | Development, low latency |
| `cohere` | Cohere rerank API | Production, high quality |
| `zeroentropy` | ZeroEntropy rerank API (zerank-2) | Production, state-of-the-art accuracy |
| `siliconflow` | SiliconFlow rerank API (Cohere-compatible `/rerank` endpoint) | Users in China or anyone on SiliconFlow's platform |
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
| `flashrank` | FlashRank (lightweight, fast) | Resource-constrained environments |
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
| `litellm-sdk` | LiteLLM SDK (direct API, no proxy) | Multi-provider, simpler setup |
| `rrf` | RRF-only (no neural reranking) | Testing, minimal resources |
### Local Models
| Model | Use Case |
|-------|----------|
| `cross-encoder/ms-marco-MiniLM-L-6-v2` | Default, fast |
| `cross-encoder/ms-marco-MiniLM-L-12-v2` | Higher accuracy |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | Multilingual |
### Cohere Models
| Model | Use Case |
|-------|----------|
| `rerank-english-v3.0` | English text |
| `rerank-multilingual-v3.0` | 100+ languages |
### ZeroEntropy Models
| Model | Use Case |
|-------|----------|
| `zerank-2` | Flagship multilingual reranker (default) |
| `zerank-2-small` | Faster, lighter variant |
### SiliconFlow Models
SiliconFlow hosts a range of open-weight rerankers behind a Cohere-compatible `/rerank` endpoint:
| Model | Use Case |
|-------|----------|
| `BAAI/bge-reranker-v2-m3` | Multilingual, strong default |
| `Qwen/Qwen3-Reranker-8B` | Larger, higher accuracy |
### LiteLLM Supported Providers
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
| Provider | Model Example |
|----------|---------------|
| Cohere | `cohere/rerank-english-v3.0` |
| Together AI | `together_ai/...` |
| Voyage AI | `voyage/rerank-2` |
| Jina AI | `jina_ai/...` |
| AWS Bedrock | `bedrock/...` |
**Configuration Examples:**
```bash
# Local provider (default)
export HINDSIGHT_API_RERANKER_PROVIDER=local
export HINDSIGHT_API_RERANKER_LOCAL_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2
# Cohere
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
# Cohere-compatible endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, ...)
# Setting COHERE_BASE_URL switches the provider off the Cohere SDK and onto a
# plain HTTP client that speaks the standard rerank wire format:
# POST {base_url} Authorization: Bearer <key>
# {"model","query","documents","return_documents":false}
# -> {"results":[{"index","relevance_score"}, ...]}
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-v3.5 # whatever model the endpoint serves
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-endpoint.example/rerank
# ZeroEntropy (state-of-the-art accuracy)
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # default, can omit
# SiliconFlow (Cohere-compatible /rerank endpoint)
export HINDSIGHT_API_RERANKER_PROVIDER=siliconflow
export HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY=your-api-key
export HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL=BAAI/bge-reranker-v2-m3 # default, can omit
# TEI (self-hosted)
export HINDSIGHT_API_RERANKER_PROVIDER=tei
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
# FlashRank (lightweight)
export HINDSIGHT_API_RERANKER_PROVIDER=flashrank
# LiteLLM proxy
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
export HINDSIGHT_API_LITELLM_API_BASE=http://localhost:4000
export HINDSIGHT_API_RERANKER_LITELLM_MODEL=cohere/rerank-english-v3.0
# RRF-only (no neural reranking)
export HINDSIGHT_API_RERANKER_PROVIDER=rrf
```
See [Configuration](./configuration#reranker) for all options including Azure-hosted endpoints and batch settings.
@@ -1,270 +0,0 @@
# Monitoring
Hindsight provides comprehensive observability through Prometheus metrics, OpenTelemetry distributed tracing, and pre-built Grafana dashboards.
## Local Development
For local observability, use the Grafana LGTM (Loki, Grafana, Tempo, Mimir) all-in-one stack:
```bash
./scripts/dev/start-monitoring.sh
```
This starts a single Docker container providing:
- **Grafana UI**: http://localhost:3000 (anonymous admin access)
- **Traces (Tempo)**: OTLP endpoint at http://localhost:4318 (HTTP) and http://localhost:4317 (gRPC)
- **Metrics (Prometheus/Mimir)**: Scrapes http://localhost:8888/metrics automatically
- **Logs (Loki)**: Available for log aggregation
- **Pre-built Dashboards**: Hindsight Operations, LLM Metrics, API Service
**Enable tracing in your API:**
```bash
export HINDSIGHT_API_OTEL_TRACES_ENABLED=true
export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
```
:::note Production Deployment
The local monitoring stack is for development only. In production, deploy Grafana LGTM separately or use commercial platforms (Grafana Cloud, DataDog, New Relic, etc.).
:::
## Grafana Dashboards
Pre-built dashboards are available in [`monitoring/grafana/dashboards/`](https://github.com/anthropics/hindsight/tree/main/monitoring/grafana/dashboards). Import these JSON files into your Grafana instance:
| Dashboard | Description |
|-----------|-------------|
| **Hindsight Operations** | Operation rates, latency percentiles, per-bank metrics |
| **Hindsight LLM Metrics** | LLM calls, token usage, latency by scope/provider |
| **Hindsight API Service** | HTTP requests, error rates, DB pool, process metrics |
The dashboards are automatically provisioned when using the monitoring stack script.
## Metrics Endpoint
Hindsight exposes Prometheus metrics at `/metrics`:
```bash
curl http://localhost:8888/metrics
```
## Available Metrics
### Operation Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.operation.duration` | Histogram | operation, bank_id, source, budget, max_tokens, success | Duration of operations in seconds |
| `hindsight.operation.total` | Counter | operation, bank_id, source, budget, max_tokens, success | Total number of operations executed |
**Labels:**
- `operation`: Operation type (`retain`, `recall`, `reflect`)
- `bank_id`: Memory bank identifier
- `source`: Where the operation was triggered from (`api`, `reflect`, `internal`)
- `budget`: Budget level if specified (`low`, `mid`, `high`)
- `max_tokens`: Max tokens if specified
- `success`: Whether the operation succeeded (`true`, `false`)
The `source` label allows distinguishing between:
- `api`: Direct API calls from clients
- `reflect`: Internal recall calls made during reflect operations
- `internal`: Other internal operations
### LLM Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.llm.duration` | Histogram | provider, model, scope, success | Duration of LLM API calls in seconds |
| `hindsight.llm.calls.total` | Counter | provider, model, scope, success | Total number of LLM API calls |
| `hindsight.llm.tokens.input` | Counter | provider, model, scope, success, token_bucket | Input tokens for LLM calls |
| `hindsight.llm.tokens.output` | Counter | provider, model, scope, success, token_bucket | Output tokens from LLM calls |
**Labels:**
- `provider`: LLM provider (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio`, `bedrock`, `litellm`)
- `model`: Model name (e.g., `gpt-4`, `claude-3-sonnet`)
- `scope`: What the LLM call is for (`memory`, `reflect`, `consolidation`, `answer`)
- `success`: Whether the call succeeded (`true`, `false`)
- `token_bucket`: Token count bucket for cardinality control (`0-100`, `100-500`, `500-1k`, `1k-5k`, `5k-10k`, `10k-50k`, `50k+`)
### HTTP Request Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.http.duration` | Histogram | method, endpoint, status_code, status_class | Duration of HTTP requests in seconds |
| `hindsight.http.requests.total` | Counter | method, endpoint, status_code, status_class | Total number of HTTP requests |
| `hindsight.http.requests.in_progress` | UpDownCounter | method, endpoint | Number of HTTP requests currently being processed |
**Labels:**
- `method`: HTTP method (`GET`, `POST`, `PUT`, `DELETE`)
- `endpoint`: Request path (normalized to reduce cardinality - UUIDs replaced with `{id}`)
- `status_code`: HTTP status code (`200`, `400`, `500`, etc.)
- `status_class`: Status code class (`2xx`, `4xx`, `5xx`)
### Database Pool Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.db.pool.size` | Gauge | - | Current number of connections in the pool |
| `hindsight.db.pool.idle` | Gauge | - | Number of idle connections in the pool |
| `hindsight.db.pool.min` | Gauge | - | Minimum pool size |
| `hindsight.db.pool.max` | Gauge | - | Maximum pool size |
### Process Metrics
| Metric | Type | Labels | Description |
|--------|------|--------|-------------|
| `hindsight.process.cpu.seconds` | Gauge | type | Process CPU time in seconds |
| `hindsight.process.memory.bytes` | Gauge | type | Process memory usage in bytes |
| `hindsight.process.open_fds` | Gauge | - | Number of open file descriptors |
| `hindsight.process.threads` | Gauge | - | Number of active threads |
**Labels:**
- `type` (CPU): `user` or `system`
- `type` (Memory): `rss_max` (maximum resident set size)
### Histogram Buckets
Custom bucket boundaries are configured for better percentile accuracy:
**Operation Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 60.0, 120.0
```
**LLM Duration Buckets (seconds):**
```
0.1, 0.25, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0, 15.0, 30.0, 60.0, 120.0
```
**HTTP Duration Buckets (seconds):**
```
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0
```
## Prometheus Configuration
```yaml
scrape_configs:
- job_name: 'hindsight'
static_configs:
- targets: ['localhost:8888']
```
## Example Queries
### Average operation latency by type
```promql
rate(hindsight_operation_duration_sum[5m]) / rate(hindsight_operation_duration_count[5m])
```
### LLM calls per minute by provider
```promql
rate(hindsight_llm_calls_total[1m]) * 60
```
### P95 LLM latency
```promql
histogram_quantile(0.95, rate(hindsight_llm_duration_bucket[5m]))
```
### Total tokens consumed by model
```promql
sum by (model) (hindsight_llm_tokens_input_total + hindsight_llm_tokens_output_total)
```
### Internal vs API recall operations
```promql
sum by (source) (rate(hindsight_operation_total{operation="recall"}[5m]))
```
### HTTP requests per second by endpoint
```promql
sum by (endpoint) (rate(hindsight_http_requests_total[1m]))
```
### HTTP error rate (5xx)
```promql
sum(rate(hindsight_http_requests_total{status_class="5xx"}[5m])) / sum(rate(hindsight_http_requests_total[5m]))
```
### P95 HTTP latency
```promql
histogram_quantile(0.95, sum by (le) (rate(hindsight_http_duration_seconds_bucket[5m])))
```
### Database pool utilization
```promql
hindsight_db_pool_size / hindsight_db_pool_max
```
### Active database connections
```promql
hindsight_db_pool_size - hindsight_db_pool_idle
```
### CPU usage rate
```promql
rate(hindsight_process_cpu_seconds{type="user"}[1m])
```
---
## Distributed Tracing
Hindsight supports OpenTelemetry distributed tracing for memory operations and LLM calls, following GenAI semantic conventions v1.37+.
### Configuration
See [Configuration - OpenTelemetry Tracing](./configuration#opentelemetry-tracing) for environment variables.
**Quick Start:**
```bash
# Enable tracing
export HINDSIGHT_API_OTEL_TRACES_ENABLED=true
export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# View traces with Grafana LGTM (local dev)
./scripts/dev/start-monitoring.sh
# Open http://localhost:3000 → Explore → Tempo
```
Supports any OTLP-compatible backend (Grafana LGTM, Langfuse, OpenLIT, DataDog, New Relic, Honeycomb, etc.).
### Span Hierarchy
**Parent Spans (Operations):**
- `hindsight.retain` - Memory ingestion
- `hindsight.recall` - Memory retrieval
- `hindsight.recall_embedding` - Query embedding
- `hindsight.recall_retrieval` - Parallel search (semantic, BM25, graph, temporal)
- `hindsight.recall_fusion` - Reciprocal Rank Fusion
- `hindsight.recall_rerank` - Cross-encoder reranking
- `hindsight.reflect` - Agentic reasoning
- `hindsight.reflect_tool_call` - Tool execution (recall, lookup, etc.)
- `hindsight.consolidation` - Observation synthesis
- `hindsight.mental_model_refresh` - Mental model updates
**Child Spans (LLM Calls):**
- Named by scope (e.g., `hindsight.memory`, `hindsight.reflect`)
- Contain full prompts/completions as events
- Follow GenAI semantic conventions for attributes
### Span Attributes
**Operation Spans:**
- `hindsight.operation` - Operation type
- `hindsight.bank_id` - Memory bank ID
- `hindsight.query` - Query text (truncated to 100 chars)
- `hindsight.fact_types` - Fact types for recall
- `hindsight.thinking_budget` - Budget allocation
- `hindsight.max_tokens` - Token limit
**LLM Spans (GenAI Semantic Conventions):**
- `gen_ai.operation.name` - Always `"chat"`
- `gen_ai.provider.name` - Provider (`openai`, `anthropic`, `google`, etc.)
- `gen_ai.request.model` - Model name
- `gen_ai.usage.input_tokens` - Input tokens
- `gen_ai.usage.output_tokens` - Output tokens
- `hindsight.scope` - LLM call purpose (`memory`, `reflect`, `consolidation`, etc.)
**Events:**
- `gen_ai.client.inference.operation.details` - Full prompts and completions
@@ -1,217 +0,0 @@
---
sidebar_position: 5
---
# Multilingual Support
Hindsight automatically detects the language of your input and responds in the same language. This means facts, entities, and reflect responses are preserved in their original language without translation to English.
## How It Works
```mermaid
graph LR
A[Chinese Input] --> B[Language Detection]
B --> C[Extract Facts in Chinese]
C --> D[Chinese Entities]
D --> E[Chinese Response]
```
When you retain content or reflect on a query, Hindsight:
1. **Detects the input language** automatically from the content
2. **Extracts facts in the original language** - preserving nuance and meaning
3. **Stores entities in their native script** - 张伟 stays 张伟, not "Zhang Wei"
4. **Responds in the same language** - queries in Chinese get Chinese answers
---
## Retain with Non-English Content
When you retain content in any language, Hindsight extracts and stores facts in that same language.
### Example: Chinese Content
```python
from hindsight import Hindsight
hindsight = Hindsight()
# Retain Chinese content
hindsight.retain(
bank_id="user-123",
content="""
张伟是一位资深软件工程师,在腾讯工作了五年。
他专门研究分布式系统,并领导了公司微服务架构的开发。
""",
context="团队概述"
)
# Query in Chinese - get Chinese results
results = hindsight.recall(
bank_id="user-123",
query="告诉我关于张伟的信息"
)
# Facts are returned in Chinese:
# - 张伟是一位资深软件工程师,在腾讯工作了五年
# - 张伟专门研究分布式系统,并领导了公司微服务架构的开发
```
### Example: Japanese Content
```python
hindsight.retain(
bank_id="user-123",
content="""
田中さんはソフトウェアエンジニアで、東京のスタートアップで働いています。
彼女はPythonとTypeScriptが得意で、毎日コードレビューをしています。
""",
context="チームプロフィール"
)
# Query in Japanese
results = hindsight.recall(
bank_id="user-123",
query="田中さんについて教えてください"
)
```
---
## Reflect with Non-English Queries
The `reflect` operation also respects the input language, generating thoughtful responses in the same language as the query.
### Example: Chinese Reflection
```python
# Store facts about team members (in Chinese)
hindsight.retain(
bank_id="team-eval",
content="张伟是一位优秀的软件工程师,完成了五个重大项目。他总是按时交付,代码整洁有良好的文档。",
context="绩效评估"
)
hindsight.retain(
bank_id="team-eval",
content="李明最近加入团队。他错过了第一个截止日期,代码有很多bug。",
context="绩效评估"
)
# Reflect in Chinese
result = hindsight.reflect(
bank_id="team-eval",
query="谁是更可靠的工程师?"
)
# Response is in Chinese:
# "我认为张伟更可靠。张伟完成了五个重大项目,按时交付,代码质量高..."
```
---
## Mixed Language Content
Hindsight handles mixed-language content gracefully, preserving both languages where appropriate.
### Example: Chinese Text with English Company Names
```python
hindsight.retain(
bank_id="user-123",
content="""
王芳在Google北京办公室工作,她是一名高级产品经理。
之前她在Microsoft和Amazon工作过。
她负责管理YouTube在中国市场的推广策略。
""",
context="员工资料"
)
# Facts preserve both languages:
# - 王芳在Google北京办公室工作,担任高级产品经理
# - 王芳曾在Microsoft和Amazon工作过
# - 王芳负责管理YouTube在中国市场的推广策略
```
---
## Supported Languages
**Hindsight's multilingual support depends entirely on your LLM's language capabilities.** Hindsight instructs the LLM to detect the input language and respond in that same language. If your LLM supports a language, Hindsight will work with it.
Most modern LLMs (GPT-4, Claude, Gemini, Llama 3, etc.) support dozens of languages including:
- **East Asian**: Chinese (Simplified/Traditional), Japanese, Korean
- **European**: Spanish, French, German, Italian, Portuguese, Dutch, Polish, Russian
- **Middle Eastern**: Arabic, Hebrew, Turkish
- **South Asian**: Hindi, Bengali, Tamil
- **Southeast Asian**: Thai, Vietnamese, Indonesian
**To verify support for your target language**, test your LLM directly with content in that language. If the LLM can understand and generate text in the language, Hindsight will preserve it correctly.
---
## Configuring for Multilingual Use
For optimal multilingual performance, you should configure all three components of the pipeline:
### 1. LLM (Required)
Your LLM must support the target languages. Most modern LLMs do, but verify with your specific model.
### 2. Embedding Model (Recommended)
The default embedding model (`BAAI/bge-small-en-v1.5`) is **English-only**. For multilingual content, use a multilingual embedding model:
```bash
# In your .env file
HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-m3
```
**Recommended multilingual embedding models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-m3` | 100+ | Best overall multilingual performance |
| `intfloat/multilingual-e5-large` | 100+ | Good alternative |
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 50+ | Lighter weight |
### 3. Reranker Model (Recommended)
The default reranker (`cross-encoder/ms-marco-MiniLM-L-6-v2`) is **English-only**. For multilingual content, use a multilingual reranker:
```bash
# In your .env file
HINDSIGHT_API_RERANKER_LOCAL_MODEL=BAAI/bge-reranker-v2-m3
```
**Recommended multilingual reranker models:**
| Model | Languages | Notes |
|-------|-----------|-------|
| `BAAI/bge-reranker-v2-m3` | 100+ | Best multilingual reranking |
| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | 14 | Lighter alternative |
---
## Best Practices
### 1. Use Multilingual Models for Non-English Content
If you primarily work with non-English content, configure multilingual embedding and reranker models. English-only models will still store your content correctly, but semantic search quality will be degraded.
### 2. Keep Content in One Language Per Retain Call
While mixed content works, keeping each `retain` call in a single language produces more consistent results.
### 3. Query in the Same Language as Your Content
For best results, query using the same language as your stored content. Cross-language queries (e.g., English query for Chinese content) may work but results can vary depending on your embedding model.
---
## Technical Details
Multilingual support is implemented through LLM prompt instructions rather than external language detection libraries. This approach:
- **Requires no additional dependencies**
- **Works with any LLM** that supports multiple languages
- **Handles edge cases** like mixed-language content naturally
- **Preserves semantic meaning** better than rule-based translation
The LLM is instructed to:
1. Detect the input language
2. Extract all facts, entities, and descriptions in that same language
3. Never translate to English unless the input is in English
@@ -1,216 +0,0 @@
---
sidebar_position: 5
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
# Observations: Knowledge Consolidation
After memories are retained, Hindsight automatically consolidates related facts into **observations** — deduplicated, evidence-grounded beliefs the bank has built up from multiple memories. Each observation tracks its supporting evidence (with exact quotes), a proof count, and a computed freshness trend, and is refined rather than overwritten when new evidence arrives.
```mermaid
graph LR
A[New Facts] --> B[Consolidation Engine]
B --> C{Existing Observation?}
C -->|Yes| D[Refine Observation]
C -->|No| E[Create Observation]
D --> F[Observations]
E --> F
```
---
## What Are Observations?
Observations are **consolidated knowledge** built from multiple facts. Unlike raw facts — which are individual pieces of information — observations represent deduplicated beliefs, preferences, and learnings grounded in accumulated evidence. They are not summaries the LLM invents on the fly: each observation is backed by specific source memories, carries a proof count, and evolves as new evidence supports, contradicts, or extends it.
| Raw Facts | Observation |
|-----------|--------------|
| "Alice prefers Python" | "Alice is a Python-focused developer who values readability and simplicity" |
| "Alice dislikes verbose code" | |
| "Alice recommends type hints" | |
Observations provide:
- **Deduplication**: One durable belief instead of many overlapping facts
- **Grounding**: Every observation references the specific memories (with quotes) that support it
- **Evolution**: Refined as evidence strengthens, weakens, or contradicts it — history is preserved
- **Freshness signal**: A computed trend (stable / strengthening / weakening / new / stale) tells you whether the belief still holds
- **Efficiency**: Condensed knowledge for faster retrieval
---
## How Consolidation Works
### Automatic Background Processing
After `retain()` completes, the consolidation engine runs automatically:
1. **New facts analyzed** — Each new fact is compared against existing observations
2. **Pattern detection** — Related facts are grouped and synthesized
3. **Observation creation/update** — New observations are created or existing ones refined
4. **Evidence tracking** — Each observation maintains references to supporting facts
### Evidence-Based Evolution
Observations evolve as new evidence arrives:
| Event | What the bank learns | Observation state |
|-------|---------------------|----------------|
| **Day 1** | "Redis is open source under BSD license" | "Redis is excellent for caching — fast, reliable, and OSS-friendly" (2 supporting facts) |
| **Day 2** | "Redis has great community support" | Observation reinforced (3 supporting facts) |
| **Day 30** | "Redis changed license to SSPL" | Observation refined: "Redis is technically strong, but has license concerns for cloud" |
| **Day 45** | "Valkey forked Redis under BSD" | New observation: "Consider Valkey for new projects requiring true OSS" |
### Handling Contradictory Evidence
What happens when a new fact contradicts an existing observation?
The consolidation engine doesn't blindly overwrite — it **reconciles** the contradiction by capturing the evolution:
**Example: User preference changes**
| Time | Fact | Observation |
|------|------|--------------|
| Week 1 | "User says they love React" | "User prefers React for frontend development" |
| Week 2 | "User praises React's component model" | "User is enthusiastic about React, particularly its component model" |
| Week 3 | "User says they've switched to Vue and won't use React anymore" | "User was previously a React enthusiast who appreciated its component model, but has now switched to Vue and no longer uses React" |
Notice how the final observation captures the **full journey** — not just "User prefers Vue" but the complete evolution of their preference. This nuanced understanding means:
- Your agent won't recommend React tutorials to someone who explicitly moved away from it
- Your agent understands *why* this matters (they were enthusiastic before, so this is a deliberate choice)
- Your agent can reference this history when relevant ("I know you used to work with React...")
The system:
1. **Detects the conflict** — New fact contradicts existing observation
2. **Preserves history** — Incorporates the previous understanding into the new observation
3. **Creates nuanced observation** — Synthesizes a richer understanding that captures the change
4. **Updates freshness** — Marks the observation as recently updated
**Example: Correcting misinformation**
| Time | Fact | Observation |
|------|------|--------------|
| Day 1 | "Alice works at Google" | "Alice is a Google employee" |
| Day 10 | "Alice actually works at Meta, not Google" | "Alice works at Meta (previously thought to work at Google)" |
When a fact explicitly corrects previous information, the observation is updated to reflect the correction while noting the previous understanding. The raw facts are always preserved, so you can trace back to see what was originally stated and when it was corrected.
---
## Observations in Retrieval
Observations are automatically included in both `recall()` and `reflect()` operations:
### In Recall
Observations are returned alongside raw facts, filtered by the `types` parameter:
<CodeSnippet code={recallPy} section="recall-with-observations" language="python" />
### In Reflect
The reflect agent uses **hierarchical retrieval**:
1. **[Mental Models](/developer/api/mental-models)** — User-curated summaries (highest priority)
2. **Observations** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification
The agent automatically queries observations and uses them to inform its reasoning.
---
## Freshness Awareness
Observations track when they were last updated. During reflect, the agent considers freshness:
- **Fresh observations**: Used directly for reasoning
- **Stale observations**: Agent verifies against current facts before relying on them
This ensures responses stay accurate even as the underlying data changes.
---
## Observation Scopes
By default, observations are scoped to all of a memory's tags combined. The `observation_scopes` retain parameter lets you control this — building separate observations per tag, per combination, or with a custom list of scopes. This is key when a single memory carries multiple tags and you want each tag to accumulate its own observations independently.
See [`observation_scopes` in the Retain API](./api/retain#observation_scopes) for the full explanation and options.
---
## Observations Mission
You can define exactly what this bank should synthesise by setting an **observations mission** (`observations_mission`). This replaces the built-in durable-knowledge rules with your own instructions, letting you control what shape observations take.
```
e.g. Observations are stable facts about people and projects.
Always include preferences, skills, and recurring patterns.
Ignore one-off events and ephemeral state.
```
Leave it blank to use the server default — durable, specific facts that stay true over time (preferences, skills, relationships, recurring patterns), with ephemeral state filtered out.
**Examples:**
| `observations_mission` | What gets synthesised |
|------------------------|----------------------|
| *(unset — default)* | Durable facts: preferences, skills, relationships, recurring patterns |
| *"Observations are weekly summaries of sprint outcomes and blockers"* | Broad event summaries grouped by time period |
| *"Observations are stable facts about named individuals only"* | Person-centric knowledge, tied to specific people |
| *"Observations are recurring patterns in customer support interactions"* | Failure modes, common requests, pain points |
Set `observations_mission` via the [bank config API](/developer/api/memory-banks#observations-configuration) or the [`HINDSIGHT_API_OBSERVATIONS_MISSION`](/developer/configuration#observations) environment variable.
---
## Observation Lifecycle & Invalidation
### When Memories Are Deleted
Observations are derived from source memories. When source memories are removed, Hindsight automatically keeps observations consistent:
| Action | Effect on observations |
|--------|----------------------|
| Delete a document | All observations derived from the document's memories are deleted |
| Delete individual memories (by type) | Observations sourced from those memories are deleted |
| Delete an entire bank | All observations are deleted along with everything else |
After deletion, the **remaining source memories** that fed the affected observations have their consolidation state reset, so they will be re-consolidated on the next consolidation run and produce fresh observations.
### Clearing Observations for a Specific Memory
You can clear all observations derived from a single memory without deleting the memory itself. This is useful when you want to force re-synthesis of a memory's contribution to consolidated knowledge.
Use the `DELETE /v1/default/banks/{bank_id}/memories/{memory_id}/observations` endpoint. This will:
1. Delete all observations that list the memory as a source
2. Reset `consolidated_at` on the memory itself and any other source memories that contributed to those observations
3. Trigger a consolidation job so fresh observations are produced automatically
### Resetting All Observations
To wipe all consolidated knowledge and start over:
```python
# Clear all observations for a bank
client.clear_observations(bank_id="my-bank")
```
This resets the consolidation state for all source memories in the bank, so the next consolidation run will re-derive all observations from scratch.
---
## Configuration
Observation consolidation runs automatically. You can monitor consolidation via the [Operations API](./api/operations).
---
## Next Steps
- [**Retain**](./retain) — How facts are stored and trigger consolidation
- [**Recall**](./retrieval) — How observations are retrieved
- [**Reflect**](./reflect) — How the agentic loop uses observations
- [**Mental Models**](./api/mental-models) — User-curated summaries for common queries
@@ -1,151 +0,0 @@
# Performance
Hindsight is designed for high-performance semantic memory operations at scale. This page covers performance characteristics, optimization strategies, and best practices.
## Overview
Hindsight's performance is optimized across three key operations:
- **Retain (Ingestion)**: Batch processing with async operations for large-scale memory storage
- **Recall (Search)**: Sub-second semantic search with configurable thinking budgets
- **Reflect (Reasoning)**: Disposition-aware answer generation with controllable compute
## Design Philosophy: Optimized for Fast Reads
Hindsight is **architected from the ground up to prioritize read performance over write performance**. This design decision reflects the typical usage pattern of memory systems: memories are written once but read many times.
The system makes deliberate trade-offs to ensure **sub-second recall operations**:
- **Pre-computed embeddings**: All memory embeddings are generated and indexed during retention
- **Optimized vector search**: HNSW indexes enable fast approximate nearest neighbor search
- **Fact extraction at write time**: Complex LLM-based fact extraction happens during retention, not retrieval
- **Structured memory graphs**: Relationships and temporal information are resolved upfront
This means **Recall (search) operations are blazingly fast** because all the heavy lifting has already been done.
### Performance Comparison
| Operation | Typical Latency | Primary Bottleneck | Optimization Strategy |
|-----------|----------------|-------------------|----------------------------------|
| **Recall** | 100-600ms | Re-ranker (on CPU) | Use GPU for re-ranking, or reduce budget |
| **Reflect** | 800-3000ms | LLM generation | Use faster LLM |
| **Retain** | 500ms-2000ms per batch | **LLM fact extraction** | Use high-throughput LLM provider |
Hindsight is designed to ensure your **application's read path (recall/reflect) is always fast**, even if it means spending more time upfront during writes. This is the right trade-off for memory systems where:
- Memories are retained in background processes or during low-traffic periods
- Memories are queried frequently in user-facing, latency-sensitive contexts
- The ratio of reads to writes is high (typically 10:1 or higher)
---
## Retain Performance
**Retain (write) operations are inherently slower** because they involve LLM-based fact extraction, entity recognition, temporal reasoning, relationship mapping, and embedding generation. **The LLM is the primary bottleneck for write latency.**
### Hindsight Doesn't Need a Smart Model
The fact extraction process is structured and well-defined, so smaller, faster models work extremely well. Our recommended model is `gpt-oss-20b` (available via Groq and other providers).
To maximize retention throughput:
1. **Use high-throughput LLM providers**: Choose providers with high requests-per-minute (RPM) limits and low latency
- **Fast**: [Groq](https://groq.com) with `gpt-oss-20b` or other openai-oss models, self-hosted models on GPU clusters (vLLM, TGI)
- **Slow**: Standard cloud LLM providers with rate limits
2. **Batch your operations**: Group related content into batch requests. Send as much data as you want in a single request — the only limit is the HTTP payload size.
3. **Use async mode for large datasets**: Queue operations in the background
4. **Parallel processing**: For very large datasets, use multiple concurrent retention requests with different `document_id` values
### Automatic Batch Optimization
**When using async retain, Hindsight automatically handles batch sizing for you.** You don't need to manually tune batch sizes or worry about optimal chunking.
How it works:
- **Send large batches**: Submit hundreds or thousands of items in a single async retain request
- **Automatic splitting**: Hindsight automatically splits large batches (>10,000 tokens) into optimized sub-batches
- **Parallel processing**: Sub-batches are processed concurrently in the background
- **Status tracking**: Parent operation aggregates status from all sub-batches
- **Token-based**: Batching uses tiktoken for accurate token counting, not character counts
Benefits:
- Send entire documents or datasets in one API call
- Let Hindsight optimize the processing strategy
- Track overall progress via the parent operation status
- No need to manually split data into small batches
### Throughput
Factors affecting throughput:
- Document size and complexity
- LLM provider rate limits (for fact extraction)
- Database write performance
- Available CPU/memory resources
---
## Recall Performance
### Budget
The `budget` parameter controls the search depth and quality. Choose based on query complexity — comprehensive questions that need thorough analysis benefit from higher budgets:
| Budget | Use Case |
|--------|----------|
| `low` | Quick lookups, real-time chat |
| `mid` | Standard queries, balanced performance |
| `high` | Comprehensive questions, thorough analysis |
### Optimization
1. **Appropriate budgets**: Use lower budgets for simple queries, higher for comprehensive reasoning
2. **Limit result tokens**: Set `max_tokens` to control response size (default: 4096)
3. **Include chunks**: Use `include_chunks` to retrieve the raw text that generated memories when you need additional context
### Database Performance
Hindsight uses PostgreSQL with pgvector for efficient vector search:
- **Index type**: HNSW for approximate nearest neighbor search
- **Typical query time**: 10-50ms for vector search on 100K+ facts
- **Scalability**: Tested with millions of facts per bank
## Reflect Performance
### Performance Characteristics
| Component | Latency | Description |
|-----------|----------------|-------------|
| Memory search | 100-600ms | Based on budget (low/mid/high) |
| LLM generation | 500-2000ms | Depends on provider and response length |
| **Total** | **600-2600ms** | Typical end-to-end latency |
### Optimization Strategies
1. **Budget selection**: Use lower budgets when context is sufficient
2. **Context provision**: Provide relevant `context` to reduce recall requirements and steer towards more focused answers
## Best Practices
### Operations
- **Use appropriate budgets**: Don't over-provision for simple queries; use higher budgets for comprehensive reasoning
- **Batch retain operations**: Group related content together for better efficiency
- **Cache frequent queries**: Cache at the application level for repeated queries
- **Profile with trace**: Use the `trace` parameter to identify slow operations
### Scaling
- **Horizontal scaling**: Deploy multiple API instances behind a load balancer with shared PostgreSQL
- **Concurrency**: 100+ simultaneous requests supported; memory search scales with CPU cores
- **LLM rate limits**: Distribute load across multiple API keys/providers (typically 60-500 RPM per key)
### Cost Optimization
- **Use efficient models**: `gpt-oss-20b` via Groq for retain — Hindsight doesn't need frontier models
- **Enable provider Batch API**: Set `HINDSIGHT_API_RETAIN_BATCH_ENABLED=true` with async retain to cut LLM fact-extraction costs by 50% (supported on OpenAI and Groq; results delivered within 24 hours)
- **Control token budgets**: Limit `max_tokens` for recall, use lower budgets when possible
- **Optimize chunks**: Larger chunks (1000-2000 tokens) are more efficient than many small ones
### Monitoring
- **Prometheus metrics**: Available at `/metrics` — track latency percentiles, throughput, and error rates
- **Key metrics**: `hindsight_recall_duration_seconds`, `hindsight_reflect_duration_seconds`, `hindsight_retain_items_total`
@@ -1,110 +0,0 @@
---
sidebar_position: 2
---
# RAG vs Memory
Traditional RAG (Retrieval-Augmented Generation) retrieves documents similar to a query. Hindsight provides structured memory with temporal reasoning, entity understanding, and belief formation.
## Capability Comparison
| Capability | RAG | Hindsight |
|------------|-----|-----------|
| **Search strategy** | Semantic similarity only | Semantic + keyword + graph + temporal |
| **Multi-hop reasoning** | Limited to retrieved chunks | Graph traversal across entity relationships |
| **Temporal queries** | Keyword matching ("spring") | Date parsing and range filtering |
| **Entity understanding** | None | Entity resolution, co-occurrence tracking |
| **Knowledge consolidation** | Stateless | Mental models that synthesize and evolve |
| **Disposition** | None | 3 traits (skepticism, literalism, empathy) influence interpretation |
## Architecture Comparison
### RAG
| Step | Operation |
|------|-----------|
| 1 | Embed query |
| 2 | Vector similarity search |
| 3 | Return top-k chunks |
| 4 | Generate response |
Single retrieval strategy. No state between queries.
### Hindsight
| Step | Operation |
|------|-----------|
| 1 | Parse query (extract temporal expressions, entities) |
| 2 | Execute 4 parallel retrievals: semantic, BM25, graph, temporal |
| 3 | Fuse results with RRF |
| 4 | Rerank with cross-encoder |
| 5 | Apply disposition traits |
| 6 | Generate response |
Multiple retrieval strategies. Persistent state across sessions.
## Example Scenarios
### Multi-Hop Reasoning
**Stored facts:**
- "Alice is the tech lead on Project Atlas"
- "Project Atlas uses Kubernetes"
- "Kubernetes cluster had an outage Tuesday"
**Query:** "Was Alice affected by recent issues?"
| System | Result |
|--------|--------|
| RAG | Retrieves facts about Alice only (no semantic similarity to "issues") |
| Hindsight | Traverses Alice → Project Atlas → Kubernetes → outage via entity links |
### Temporal Queries
**Stored facts with timestamps:**
- March: "Alice started microservices migration"
- April: "Alice completed auth service"
- October: "Alice focusing on performance"
**Query:** "What did Alice do last spring?"
| System | Result |
|--------|--------|
| RAG | Returns all Alice facts regardless of date |
| Hindsight | Parses "last spring" → March-May, filters to that range |
### Entity Understanding
**Stored facts about a user across sessions:**
- "Pro subscription"
- "Mobile app crashes in settings"
- "Switched to annual billing"
- "Desktop app working fine"
**Query:** "What do you know about my account?"
| System | Result |
|--------|--------|
| RAG | Lists disconnected facts |
| Hindsight | Returns connected facts via entity graph: subscription status, billing, known issues |
### Knowledge Evolution
**Week 1:** User struggles with async Python, succeeds with threads
**Week 3:** User asks about asyncio, implements async database calls
| System | Behavior |
|--------|----------|
| RAG | No memory of progression |
| Hindsight | Consolidates mental model "user prefers sync" → refines to "user growing comfortable with async" |
## When to Use Each
| Use Case | Recommended |
|----------|-------------|
| Document Q&A over static corpus | RAG |
| Search with no temporal requirements | RAG |
| AI assistants with persistent memory | Hindsight |
| Applications requiring entity tracking | Hindsight |
| Systems needing consistent disposition | Hindsight |
| Temporal queries ("last month", "in 2023") | Hindsight |
@@ -1,244 +0,0 @@
---
sidebar_position: 4
---
import CodeSnippet from '@site/src/components/CodeSnippet';
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
# Reflect: Agentic Reasoning with Disposition
When you call `reflect()`, Hindsight runs an **agentic loop** that autonomously gathers evidence and reasons through the lens of the bank's disposition to generate contextual responses.
```mermaid
graph TB
subgraph agent["Reflect Agent Loop"]
A[Query] --> B{Need more info?}
B -->|Yes| C[Call Tools]
C --> D[search_mental_models]
C --> E[search_observations]
C --> F[recall]
C --> G[expand]
D --> B
E --> B
F --> B
G --> B
B -->|No| H[Generate Response]
end
H --> I[Response + Citations]
```
---
## How It Works
Unlike simple retrieval, reflect is an **agentic system** that:
1. **Autonomously gathers evidence** — The agent decides what information it needs and calls appropriate tools
2. **Uses hierarchical retrieval** — Checks mental models first, then observations, then raw facts
3. **Applies disposition** — Shapes reasoning based on the bank's personality traits
4. **Enforces directives** — Hard rules that must be followed in all responses
5. **Cites sources** — Returns which memories and observations were used
### The Agentic Loop
The reflect agent runs in a loop with access to these tools:
| Tool | Purpose | Priority |
|------|---------|----------|
| `search_mental_models` | User-curated summaries | Highest (check first) |
| `search_observations` | Consolidated knowledge | High |
| `recall` | Raw facts (ground truth) | Fallback |
| `expand` | Get more context for a memory | As needed |
| `done` | Complete with final answer | When ready |
The agent:
- **Must gather evidence** before answering (guardrail prevents empty responses)
- **Runs up to 10 iterations** to find relevant information
- **Validates citations** — only IDs that were actually retrieved can be cited
### Hierarchical Retrieval Strategy
The agent uses a smart retrieval hierarchy:
1. **[Mental Models](/developer/api/mental-models)** — User-curated summaries you've pre-computed for common queries
2. **[Observations](/developer/observations)** — Consolidated knowledge with freshness awareness
3. **Raw Facts** — Ground truth for verification when observations are stale
**Mental models** are saved reflect responses that you create for frequently asked questions. They're checked first because they represent explicitly curated knowledge. See the [Mental Models API](/developer/api/mental-models) for how to create and manage them.
If an observation is marked as **stale**, the agent automatically verifies it against current facts.
---
## Why Reflect?
Most AI systems can retrieve facts, but they can't **reason** about them in a consistent way.
### The Problem
Without reflect:
- **No consistent character**: Same question gets different answers each time
- **No knowledge synthesis**: System never connects related facts
- **No reasoning context**: Responses don't reflect accumulated knowledge
- **Generic responses**: Every AI sounds the same
### The Value
With reflect:
- **Consistent character**: A "detail-oriented, cautious" bank emphasizes risks and thorough planning
- **Evolving knowledge**: Observations strengthen and adapt as evidence accumulates
- **Contextual reasoning**: "Based on what I know about your team's remote work success..."
- **Differentiated behavior**: Support bots sound diplomatic, code reviewers sound direct
### When to Use Reflect
| Use `recall()` when... | Use `reflect()` when... |
|------------------------|-------------------------|
| You need raw facts | You need reasoned interpretation |
| You're building your own reasoning | You want disposition-consistent responses |
| You need maximum control | You want the bank to "think" for itself |
| Simple fact lookup | Forming recommendations |
**Example:**
- `recall("Alice")` → Returns all Alice facts and relevant mental models
- `reflect("Should we hire Alice?")` → Agent gathers evidence about Alice, reasons about fit, returns answer with citations
---
## Disposition Traits
When you create a memory bank, you can configure its disposition using three traits. These traits influence how the bank interprets information and reasons during `reflect()`:
| Trait | Scale | Low (1) | High (5) |
|-------|-------|---------|----------|
| **Skepticism** | 1-5 | Trusting, accepts information at face value | Skeptical, questions and doubts claims |
| **Literalism** | 1-5 | Flexible interpretation, reads between the lines | Literal interpretation, takes things at face value |
| **Empathy** | 1-5 | Detached, focuses on facts | Empathetic, considers emotional context |
### Mission: Natural Language Identity
Beyond numeric traits, you can provide a natural language **mission** that describes the bank's identity and reasoning context:
<CodeSnippet code={memoryBanksPy} section="bank-with-disposition" language="python" />
The reflect mission frames how the agent reasons and responds:
- Provides identity context: who the agent is and what it cares about
- Shapes how disposition traits are applied in practice
- Keeps reasoning consistent across conversations
:::info Per-operation missions
The reflect mission only affects `reflect()`. To steer what gets extracted during `retain()`, use [`retain_mission`](/developer/api/memory-banks#retain-configuration). To control what gets synthesised into observations, use [`observations_mission`](/developer/api/memory-banks#observations-configuration).
:::
---
## Disposition Shapes Reasoning
Two banks with different dispositions, given identical facts about remote work:
**Bank A** (low skepticism, high empathy):
> "Remote work enables flexibility and work-life balance. The team seems happier and more productive when they can choose their environment."
**Bank B** (high skepticism, low empathy):
> "Remote work claims need verification. What are the actual productivity metrics? The anecdotal benefits may not translate to measurable outcomes."
**Same facts → Different conclusions** because disposition shapes interpretation.
---
## Disposition Presets by Use Case
Different use cases benefit from different disposition configurations:
| Use Case | Recommended Traits | Why |
|----------|-------------------|-----|
| **Customer Support** | skepticism: 2, literalism: 2, empathy: 5 | Trusting, flexible, understanding |
| **Code Review** | skepticism: 4, literalism: 5, empathy: 2 | Questions assumptions, precise, direct |
| **Legal Analysis** | skepticism: 5, literalism: 5, empathy: 2 | Highly skeptical, exact interpretation |
| **Therapist/Coach** | skepticism: 2, literalism: 2, empathy: 5 | Supportive, reads between lines |
| **Research Assistant** | skepticism: 4, literalism: 3, empathy: 3 | Questions claims, balanced interpretation |
---
## Directives: Hard Rules
While disposition traits *influence* reasoning style, **directives** are hard rules that the agent *must* follow. Directives are injected into the prompt and enforced in every response.
### When to Use Directives
Use directives for constraints that must never be violated:
- **Compliance rules**: "Never recommend specific stocks or financial products"
- **Privacy constraints**: "Never share personal data with third parties"
- **Style requirements**: "Always respond in formal English"
- **Domain guardrails**: "Always cite sources when making factual claims"
### Directives vs Disposition
| Aspect | Disposition | Directives |
|--------|-------------|------------|
| **Nature** | Soft influence | Hard rules |
| **Effect** | Shapes interpretation and tone | Must be followed exactly |
| **Violation** | Acceptable (it's a tendency) | Not acceptable |
| **Example** | High skepticism → questions claims | "Never make medical diagnoses" |
:::tip
Use disposition for personality and character. Use directives for compliance and guardrails.
:::
See [Memory Banks: Directives](/developer/api/memory-banks#directives) for how to create and manage directives.
---
## What You Get from Reflect
When you call `reflect()`:
**Returns:**
- **Response text** — Disposition-influenced answer from the agent
- **based_on** — Evidence used: memories, mental models, and directives that grounded the response
- **trace** — Tool calls, LLM calls, and observations accessed (when `include.tool_calls=True`)
- **structured_output** — Parsed response if `response_schema` was provided
- **usage** — Token usage metrics
**Example:**
```json
{
"text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
"based_on": {
"memories": [
{"id": "mem-123", "text": "Alice has 5 years of ML experience", "type": "world"},
{"id": "mem-456", "text": "Alice worked at Google on search ranking", "type": "experience"}
],
"mental_models": [],
"directives": [
{"id": "dir-001", "name": "Formal Language", "rules": ["Always respond in formal English"]}
]
},
"usage": {"input_tokens": 1500, "output_tokens": 500, "total_tokens": 2000}
}
```
The agent automatically gathers evidence, validates citations, and generates a grounded response.
---
## Why Disposition Matters
Without disposition, all AI assistants sound the same. With disposition:
- **Customer support bots** can be diplomatic and empathetic
- **Code review assistants** can be direct and thorough
- **Creative assistants** can be open to unconventional ideas
- **Risk analysts** can be appropriately cautious
Disposition creates **consistent character** across conversations while observations **evolve with evidence**.
---
## Next Steps
- [**Observations**](./observations) — How knowledge is consolidated
- [**Retain**](./retain) — How rich facts are stored
- [**Recall**](./retrieval) — How multi-strategy search works
- [**Reflect API**](./api/reflect) — Code examples and parameters
@@ -1,228 +0,0 @@
---
sidebar_position: 2
---
# Retain: How Hindsight Stores Memories
When you call `retain()`, Hindsight transforms conversations and documents into structured, searchable memories that preserve meaning and context.
## What Retain Does
```mermaid
graph LR
A[Your Content] --> B[Extract Facts]
B --> C[Identify Entities]
C --> D[Build Connections]
D --> E[Memory Bank]
```
---
## Rich Fact Extraction
Hindsight doesn't just store what was said — it captures **why**, **how**, and **what it means**.
### What Gets Captured
When you retain "Alice joined Google last spring and was thrilled about the research opportunities", Hindsight extracts:
**The core facts:**
- Alice joined Google
- This happened last spring
**The emotions and meaning:**
- She was thrilled
- It represented an important opportunity
**The reasoning:**
- She chose it for the research opportunities
This rich extraction means you can later ask "Why did Alice join Google?" and get a meaningful answer, not just "she joined Google."
### Preserving Context
Traditional systems fragment information:
- "Bob suggested Summer Vibes"
- "Alice wanted something unique"
- "They chose Beach Beats"
Hindsight preserves the full narrative:
- "Alice and Bob discussed naming their summer party playlist. Bob suggested 'Summer Vibes' because it's catchy, but Alice wanted something unique. They ultimately decided on 'Beach Beats' for its playful tone."
This means search results include the full context, not disconnected fragments.
---
## Two Types of Facts
Hindsight distinguishes between **world** facts (about others) and **experience** (conversations and events):
| Type | Description | Example |
|-----------------|-----------------------------------|---------|
| **world** | Facts about people, places, things | "Alice works at Google" |
| **experience** | Conversations and events | "I recommended Python to Alice" |
**Note:** Observations are consolidated automatically in the background after `retain()` operations complete. This consolidation process synthesizes patterns from new facts into the bank's knowledge base.
---
## Entity Recognition
Hindsight automatically identifies and tracks **entities** — the people, organizations, and concepts that matter.
### What Gets Recognized
- **People:** "Alice", "Dr. Smith", "Bob Chen"
- **Organizations:** "Google", "MIT", "OpenAI"
- **Places:** "Paris", "Central Park", "California"
- **Products & Concepts:** "Python", "TensorFlow", "machine learning"
### Entity Resolution
The same entity mentioned different ways gets unified:
- "Alice" + "Alice Chen" + "Alice C." → one person
- "Bob" + "Robert Chen" → one person (nickname resolution)
**Why it matters:** You can ask "What do I know about Alice?" and get everything, even if she was mentioned as "Alice Chen" in some conversations.
### Context-Aware Disambiguation
If "Alice" appears with "Google" and "Stanford" multiple times, a new "Alice" mentioning those is likely the same person. Hindsight uses co-occurrence patterns to disambiguate common names.
### Entity Labels
You can define a controlled vocabulary of `key:value` classification labels (e.g. `pedagogy:scaffolding`, `engagement:active`) that are extracted at retain time and stored as entities. Because labels become entities, they automatically link related memories in the knowledge graph and improve both semantic and keyword retrieval. Labels can optionally also write to the memory unit's tags, enabling standard tag-based filtering during recall and reflect.
See [entity_labels in the bank config](/developer/api/memory-banks#entity-labels) for full configuration details.
---
## Building Connections
Memories aren't isolated — Hindsight creates a **knowledge graph** with four types of connections:
### Entity Connections
All facts mentioning the same entity are linked together.
**Enables:** "Tell me everything about Alice" → retrieves all Alice-related facts
### Time-Based Connections
Facts close in time are connected, with stronger links for closer dates.
**Enables:** "What else happened around then?" → finds contextually related events
### Meaning-Based Connections
Semantically similar facts are linked, even if they use different words.
**Enables:** "Tell me about similar topics" → finds thematically related information
### Causal Connections
Cause-effect relationships are explicitly tracked.
**Enables:** "Why did this happen?" → trace reasoning chains
**Example:** "Alice felt burned out" ← caused by ← "She worked 80-hour weeks"
---
## Understanding Time
Hindsight tracks **two temporal dimensions**:
### When It Happened
For events (meetings, trips, milestones), Hindsight records when they occurred.
- "Alice got married in June 2024" → occurred in June 2024
For general facts (preferences, characteristics), there's no specific occurrence time.
- "Alice prefers Python" → ongoing preference
### When You Learned It
Hindsight also tracks when you told it each fact.
**Why both?**
Imagine in January 2025, someone tells you "Alice got married in June 2024":
- **Historical queries** work: "What did Alice do in 2024?" → finds the marriage
- **Recency ranking** works: Recent mentions get priority in search
- **Temporal reasoning** works: "What happened before her marriage?" → finds earlier events
Without this distinction, old information would either be unsearchable by date or treated as irrelevant.
---
## Tagging Memories
Tags enable visibility scoping—useful when one memory bank serves multiple users but each should only see relevant memories.
- **Item tags**: Tag individual memories with specific scopes
- **Document tags**: Apply tags to all items in a batch
- **Tag filtering**: Filter during recall/reflect by tags
See [Retain API](./api/retain) for code examples and [Recall API](./api/recall) for filtering options.
---
## What You Get
After `retain()` completes:
- **Structured facts** that preserve meaning, emotions, and reasoning
- **Unified entities** that resolve different name variations
- **Knowledge graph** with entity, temporal, semantic, and causal links
- **Temporal grounding** for both historical and recency-based queries
- **Optional tags** for filtering during recall
All stored in your isolated **memory bank**, ready for `recall()` and `reflect()`.
---
## Steering Extraction with a Mission
By default, `retain()` extracts all significant facts from the content. You can narrow this focus with a **retain mission** (`retain_mission`) — a plain-language description of what this bank should pay attention to.
```
e.g. Always include technical decisions, API design choices, and architectural trade-offs.
Ignore meeting logistics, greetings, and social exchanges.
```
The mission is injected into the extraction prompt alongside the built-in rules — it steers the LLM without replacing the extraction logic. It works with any extraction mode (`concise`, `verbose`, `custom`).
For finer control, you can also change the **extraction mode**:
| Mode | When to use |
|------|-------------|
| `concise` *(default)* | General-purpose — selective, fast |
| `verbose` | When you need richer facts with full context and relationships |
| `custom` | When you want to write your own extraction rules entirely |
Set `retain_mission` and `retain_extraction_mode` via the [bank config API](/developer/api/memory-banks#retain-configuration) or the [`HINDSIGHT_API_RETAIN_MISSION`](/developer/configuration#retain) environment variable.
---
## Observation Consolidation
After `retain()` completes, Hindsight automatically triggers **observation consolidation** in the background. This process:
1. Analyzes new facts against existing observations
2. Creates new observations when patterns emerge
3. Refines existing observations with new evidence
4. Tracks which facts support each observation
This happens asynchronously — your `retain()` call returns immediately while consolidation runs in the background.
See [Observations](./observations) for details on how consolidation works.
---
## Next Steps
- [**Observations**](./observations) — How knowledge is consolidated after retain
- [**Recall**](./retrieval) — How multi-strategy search retrieves relevant memories
- [**Reflect**](./reflect) — How the agentic loop uses observations
- [**Retain API**](./api/retain) — Code examples and parameters
@@ -1,419 +0,0 @@
---
sidebar_position: 3
---
# Recall: How Hindsight Retrieves Memories
When you call `recall()`, Hindsight uses multiple search strategies in parallel to find the most relevant memories, regardless of how you phrase your query.
```mermaid
graph LR
Q[Query] --> S[Semantic]
Q --> K[Keyword]
Q --> G[Graph]
Q --> T[Temporal]
S --> RRF[RRF Fusion]
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder]
CE --> R[Results]
```
---
## The Challenge of Memory Recall
Different queries need different search approaches:
- **"Alice works at Google"** → needs exact name matching
- **"Where does Alice work?"** → needs semantic understanding
- **"What did Alice do last spring?"** → needs temporal reasoning
- **"Why did Alice leave?"** → needs causal relationship tracing
No single search method handles all these well. Hindsight solves this with **TEMPR** — four complementary strategies that run in parallel.
---
## Four Search Strategies
### Semantic Search
**What it does:** Understands the *meaning* behind words, not just the words themselves.
**Best for:**
- Conceptual matches: "Alice's job" → "Alice works as a software engineer"
- Paraphrasing: "Bob's expertise" → "Bob specializes in machine learning"
- Synonyms: "meeting" matches "conference", "discussion", "gathering"
**Why it matters:** You can ask questions naturally without matching exact keywords.
---
### Keyword Search
**What it does:** Finds exact terms and names, even when they're spelled uniquely.
**Best for:**
- Proper nouns: "Google", "Alice Chen", "MIT"
- Technical terms: "PostgreSQL", "HNSW", "TensorFlow"
- Unique identifiers: URLs, product names, specific phrases
**Why it matters:** Ensures you never miss results that mention specific names or terms, even if they're semantically distant from your query.
---
### Graph Traversal
**What it does:** Follows connections between entities to find indirectly related information.
**Best for:**
- Indirect relationships: "What does Alice do?" → Alice → Google → Google's products
- Entity exploration: "Bob's colleagues" → Bob → co-workers → shared projects
- Multi-hop reasoning: "Alice's team's achievements"
**Why it matters:** Retrieves facts that aren't semantically or lexically similar but are **structurally connected** through the knowledge graph.
**Example:** Even if Alice and her manager are never mentioned together, graph traversal can find the manager through shared projects or team relationships.
---
### Temporal Search
**What it does:** Understands time expressions and filters by when events occurred.
**Best for:**
- Historical queries: "What did Alice do in 2023?"
- Time ranges: "What happened last spring?"
- Relative time: "What did Bob work on last year?"
- Before/after: "What happened before Alice joined Google?"
**How it works:** Combines semantic understanding with time filtering to find events within specific periods.
**Why it matters:** Enables precise historical queries without losing old information.
---
## Result Fusion
After the four strategies run, results are **fused together**:
- Memories appearing in **multiple strategies** rank higher (consensus)
- **Rank matters more than score** (robust across different scoring systems)
- Final results are **re-ranked** using a neural model that considers query-memory interaction
**Why fusion matters:** A fact that's both semantically similar AND mentions the right entity will rank higher than one that's only semantically similar.
---
## Why Multiple Strategies?
Consider the query: **"What did Alice say about Python last spring?"**
- **Semantic** finds facts about Alice's views on programming
- **Keyword** ensures "Python" is actually mentioned
- **Graph** connects Alice → programming languages → related entities
- **Temporal** filters to "last spring" timeframe
The **fusion** of all four gives you exactly what you're looking for, even though no single strategy would suffice.
---
## Token Budget Management
Hindsight is built for AI agents, not humans. Traditional search systems return "top-k" results, but agents don't think in terms of result counts—they think in tokens. An agent's context window is measured in tokens, and that's exactly how Hindsight measures results.
**How it works:**
- Top-ranked memories selected first
- Stops when token budget is exhausted
- You specify context budget, Hindsight fills it with the most relevant memories
**Parameters you control:**
- `max_tokens`: How much memory content to return (default: 4096 tokens)
- `budget`: Search depth level (low, mid, high)
- `types`: Filter by world, experience, observation, or all
- `tags`: Filter memories by visibility tags
- `tags_match`: How to match tags (see [Recall API](./api/recall) for all options)
### Expanding Context: Chunks
Memories are distilled facts—concise but sometimes missing nuance. When your agent needs deeper context, you can optionally retrieve the source material:
**Chunks** return the raw text that generated each memory—useful when the distilled fact loses important nuance:
```
Memory: "Alice prefers Python over JavaScript"
Chunk: "Alice mentioned she prefers Python over JavaScript, mainly because
of its data science ecosystem, though she admits JS is better for
frontend work and she's been learning TypeScript lately."
```
Use `include_chunks=True` with `max_chunk_tokens` to control the token budget for chunks. This is useful when generating responses that need verbatim quotes or when context matters (e.g., "What exactly did Alice say about the project?").
---
## Tuning Recall: Quality vs Latency
Different use cases require different trade-offs between **recall quality** and **response speed**. Two parameters control this:
### Budget: Search Depth
Controls how thoroughly Hindsight explores the memory bank—affecting graph traversal depth, candidate pool size, and cross-encoder re-ranking:
| Budget | Best For | Trade-off |
|--------|----------|-----------|
| **low** | Quick lookups, simple queries | Fast, may miss indirect connections |
| **mid** | Most queries, balanced | Good coverage, reasonable speed |
| **high** | Complex queries requiring deep exploration | Thorough, slower |
**Example:** "What did Alice's manager's team work on?" benefits from high budget to traverse multiple hops (Alice → manager → team → projects) and evaluate more candidates.
### Max Tokens: Context Window Size
Controls how much memory content to return:
| Max Tokens | ~Pages of Text | Best For | Trade-off |
|------------|----------------|----------|-----------|
| **2048** | ~2 pages | Focused answers, fast LLM | Fewer memories, faster |
| **4096** (default) | ~4 pages | Balanced context | Good coverage, standard |
| **8192** | ~8 pages | Comprehensive context | More memories, slower LLM |
**Example:** "Summarize everything about Alice" benefits from higher max_tokens to include more facts.
### Two Independent Dimensions
Budget and max_tokens control different aspects of recall:
| Parameter | What it controls | Latency impact | Example |
|-----------|------------------|----------------|---------|
| **Budget** | How thoroughly to explore memories | Search time | High budget finds Alice → manager → team → projects |
| **Max Tokens** | How much context to return | LLM processing time | High tokens returns more memories to the agent |
**They're independent.** Common combinations:
| Budget | Max Tokens | Use Case |
|--------|------------|----------|
| high | low | Deep search, return only the best results |
| low | high | Quick search, return everything found |
| high | high | Comprehensive research queries |
| low | low | Fast chatbot responses |
### Recommended Configurations
| Use Case | Budget | Max Tokens | Why |
|----------|--------|------------|-----|
| **Chatbot replies** | low | 2048 | Fast responses, focused context |
| **Document Q&A** | mid | 4096 | Balanced coverage and speed |
| **Research queries** | high | 8192 | Comprehensive, multi-hop reasoning |
| **Real-time search** | low | 2048 | Minimize latency |
---
## Scoring & Ranking Deep Dive
This section explains exactly how Hindsight turns raw retrieval results into a final ranked list. The pipeline has three stages: **RRF fusion**, **cross-encoder reranking**, and **combined scoring**.
### Stage 1: Reciprocal Rank Fusion (RRF)
After all strategies run in parallel, their results are merged using [Reciprocal Rank Fusion](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf). RRF combines ranked lists by rewarding items that appear highly ranked across multiple strategies, without relying on raw scores (which aren't comparable across different retrieval methods).
**Formula:**
```
score(d) = Σ 1 / (k + rank_i(d))
i
```
Where:
- **k = 60** (smoothing constant — prevents top-ranked items from dominating)
- **rank_i(d)** = position of document *d* in strategy *i* (1-indexed)
- The sum runs over all strategies where *d* appears
**All four strategies are weighted equally.** There are no per-strategy weight multipliers — importance comes from rank position, not the source.
**Why RRF over raw score merging?** Each retrieval strategy produces scores on a different scale (cosine similarity, BM25 tf-idf, graph activation). These scores aren't comparable — a BM25 score of 12.5 and a cosine similarity of 0.85 don't mean the same thing. RRF sidesteps this by using only rank positions, making it robust across any scoring system without requiring calibration.
**Example:** A memory ranked #1 in semantic and #5 in BM25:
```
RRF score = 1/(60+1) + 1/(60+5) = 0.0164 + 0.0154 = 0.0318
```
A memory ranked #1 in semantic only:
```
RRF score = 1/(60+1) = 0.0164
```
The first memory ranks higher because it has **consensus** across strategies.
---
### Stage 2: Cross-Encoder Reranking
RRF gives a good initial ranking, but it's based on positions, not on deep query-document understanding. The cross-encoder evaluates each candidate against the query as a pair, producing a relevance score.
**Pre-filtering:** Before reranking, candidates are trimmed to the top **300** (by RRF score) to limit computational cost. This is configurable via `HINDSIGHT_API_RERANKER_MAX_CANDIDATES`.
**Why rerank after RRF?** RRF is position-based — it knows a memory ranked well across strategies, but it never actually reads the query and the memory together. The cross-encoder does: it takes the query and each candidate as a pair and produces a relevance score based on their full interaction. This catches nuances that position-based fusion misses, like a memory that ranked #1 in keyword search because it matched a common term but is actually irrelevant to the query's intent.
**Score normalization:** Cross-encoders output raw logits (which can be negative). These are normalized to [0, 1] using the sigmoid function:
```
CE_normalized = 1 / (1 + e^(-raw_logit))
```
**Batch processing:** Candidates are scored in batches — **32 pairs** for the local reranker, **128 pairs** for TEI.
:::tip No cross-encoder?
When running without a cross-encoder (e.g., slim image with no external reranker), the system falls back to RRF-derived scores: candidates are assigned synthetic scores spread across [0.1, 1.0] based on their RRF rank, so the combined scoring boosts below still work meaningfully.
:::
---
### Stage 3: Combined Scoring (Boosts)
The normalized cross-encoder score is adjusted by three **multiplicative boosts** that incorporate signals the cross-encoder can't see: recency, temporal proximity, and evidence strength.
**Why multiplicative instead of additive?** Additive boosts (e.g., `CE + 0.1 × recency`) would give the same absolute bonus to every candidate regardless of relevance. A barely-relevant memory could leapfrog a highly-relevant one just by being recent. Multiplicative boosts keep adjustments proportional to the base relevance score — a +10% nudge on a high-relevance memory is a bigger absolute change than +10% on a low-relevance one. This ensures secondary signals never overpower the primary relevance judgment.
**Formula:**
```
final_score = CE_normalized × recency_boost × temporal_boost × proof_count_boost
```
Each boost is centered at 1.0 (neutral) and controlled by an alpha that caps how much it can swing:
```
boost = 1 + α × (signal - 0.5)
```
| Boost | α | Max adjustment | What it rewards |
|-------|---|----------------|-----------------|
| **Recency** | 0.2 | ±10% | Recent memories over older ones |
| **Temporal proximity** | 0.2 | ±10% | Memories close to a queried time window |
| **Proof count** | 0.1 | ±5% | Observations backed by more evidence |
#### Recency signal
Linear decay over 365 days from the memory's occurrence date:
```
recency = clamp(1.0 - days_ago / 365, 0.1, 1.0)
```
A memory from today has recency 1.0 (+10% boost). A memory from 6 months ago has recency ~0.5 (neutral). A memory older than a year has recency 0.1 (-8% penalty). Memories without dates get 0.5 (neutral — no boost or penalty).
#### Temporal proximity signal
Only active when the query contains a time reference (e.g., "last spring", "in 2023"). Measures how close a memory's date is to the center of the queried time window:
```
temporal_proximity = 1.0 - min(days_from_center / (window_days / 2), 1.0)
```
A memory at the center of the window gets 1.0 (+10% boost). A memory at the edge gets 0.0 (-10% penalty). For non-temporal queries, all memories get 0.5 (neutral).
#### Proof count signal
For observation-type memories, rewards those backed by more evidence using a logarithmic curve:
```
proof_norm = clamp(0.5 + ln(proof_count) / 10, 0.0, 1.0)
```
| Proof count | proof_norm | Boost |
|-------------|-----------|-------|
| 1 | 0.5 | Neutral |
| 3 | 0.61 | +1.1% |
| 10 | 0.73 | +2.3% |
| 150+ | 1.0 | +5% (max) |
#### Maximum combined range
With all boosts at their extremes:
- **Best case:** ×1.10 × 1.10 × 1.05 ≈ **+27%**
- **Worst case:** ×0.90 × 0.90 × 0.95 ≈ **-23%**
The boosts are intentionally conservative — they nudge the ranking without overriding cross-encoder relevance.
---
### Stage 4: Token Truncation
After scoring, results are sorted by `final_score` and selected top-down until the `max_tokens` budget is exhausted. Only the memory text counts toward the budget — metadata is free.
---
### How Budget Maps to Pipeline Parameters
The `budget` parameter (low/mid/high) controls **search depth** — how many candidates each strategy considers. Each level maps to a **recall budget** number that flows through every pipeline stage:
| Budget | Recall budget (fixed mode) | Env var override |
|--------|---------------------------|-----------------|
| **low** | 100 | `HINDSIGHT_API_RECALL_BUDGET_FIXED_LOW` |
| **mid** | 300 (default) | `HINDSIGHT_API_RECALL_BUDGET_FIXED_MID` |
| **high** | 1000 | `HINDSIGHT_API_RECALL_BUDGET_FIXED_HIGH` |
This recall budget flows through the pipeline as follows:
| Pipeline stage | How the recall budget is used |
|----------------|-------------------------------|
| **Semantic search** | Over-fetches max(recall_budget × 5, 100) from HNSW, trims to recall_budget |
| **BM25 search** | `LIMIT recall_budget` in SQL |
| **Graph traversal** | Explores up to recall_budget nodes |
| **Temporal spreading** | Activates up to recall_budget nodes via links |
| **Result consideration** | Top recall_budget × 2 results considered for token filtering |
Reranking pre-filter (300 candidates) is **independent** of budget — it's a separate knob (`HINDSIGHT_API_RERANKER_MAX_CANDIDATES`).
:::info Adaptive budgeting
An alternative budget mode scales the recall budget with `max_tokens` instead of using fixed values:
```
recall_budget = clamp(max_tokens × ratio, min, max)
```
| Budget | Ratio | Env var override |
|--------|-------|-----------------|
| low | 2.5% of max_tokens | `HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_LOW` |
| mid | 7.5% of max_tokens | `HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_MID` |
| high | 25% of max_tokens | `HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_HIGH` |
The result is clamped to a floor of **20** (`HINDSIGHT_API_RECALL_BUDGET_MIN`) and a ceiling of **2000** (`HINDSIGHT_API_RECALL_BUDGET_MAX`).
Enable with `HINDSIGHT_API_RECALL_BUDGET_FUNCTION=adaptive`.
:::
---
### Graph Scoring Detail
The graph traversal (link expansion) combines three independent signals additively for each candidate:
| Signal | Score formula | Range |
|--------|--------------|-------|
| **Entity overlap** | tanh(shared_entity_count × 0.5) | [0, ~1.0] |
| **Semantic link** | Precomputed kNN link weight | [0.7, 1.0] |
| **Causal link** | Causal link weight | [0, 1.0] |
```
graph_score = entity_score + semantic_score + causal_score ∈ [0, 3]
```
The additive combination rewards **convergent evidence** — a memory connected to the query through multiple signal types ranks higher than one connected through a single strong signal.
**Why tanh for entity scores?** Raw shared-entity count is unbounded — a high-fanout entity like "user" could produce counts of 50+, drowning out the other two signals. `tanh(count × 0.5)` saturates naturally: the first few shared entities matter a lot (1→0.46, 2→0.76, 3→0.91), but additional ones contribute diminishing returns, keeping the entity signal in [0, 1] alongside semantic and causal scores.
**Why additive instead of multiplicative here?** Unlike the combined scoring boosts, graph signals are independent evidence channels, not adjustments to a base score. A memory might be connected only through causal links (no shared entities, no semantic similarity) — multiplicative combination would zero it out. Additive scoring lets each signal contribute independently, and the outer RRF fusion handles ranking across strategies.
**Entity signal example:** A memory sharing 1 entity with the query scores tanh(0.5) ≈ 0.46. Two shared entities score tanh(1.0) ≈ 0.76. Three or more saturate near 0.91+.
---
## Next Steps
- [**Retain**](./retain) — How memories are stored with rich context
- [**Reflect**](./reflect) — How disposition influences reasoning
- [**Recall API**](./api/recall) — Code examples, parameters, and tag filtering
@@ -1,66 +0,0 @@
# Services
Hindsight consists of three services that can run together or separately depending on your deployment needs.
## API Service
The core memory engine. Handles all memory operations:
- **Retain**: Ingests content, extracts facts, builds knowledge graph
- **Recall**: Semantic search across memories
- **Reflect**: Disposition-aware answer generation
```bash
hindsight-api # Default port: 8888
```
The API service is stateless and can be horizontally scaled behind a load balancer. All state is stored in PostgreSQL.
By default, the API also processes background tasks (mental model consolidation) internally. For high-throughput deployments, you can disable this and run dedicated workers instead.
## Worker Service
Dedicated task processor for background operations. Uses the **same package and Docker image** as the API service, just with a different entry point.
```bash
hindsight-worker # Default metrics port: 8889
```
Workers use PostgreSQL as a task broker, polling for pending tasks. Multiple workers can run simultaneously without conflicts.
| Deployment | Internal Worker | Dedicated Workers |
|------------|-----------------|-------------------|
| **Development** | ✅ Simple, all-in-one | ❌ Overkill |
| **Small production** | ✅ Less infrastructure | ❌ Overkill |
| **High throughput** | ❌ API bottleneck | ✅ Scale independently |
| **Long-running tasks** | ❌ Blocks API resources | ✅ Isolated processing |
To use dedicated workers, disable the internal worker in the API and start worker processes:
```bash
# Disable internal worker in API
HINDSIGHT_API_WORKER_ENABLED=false hindsight-api
# Start dedicated workers (run multiple instances)
hindsight-worker --worker-id worker-1
hindsight-worker --worker-id worker-2
```
Each worker exposes `/health` and `/metrics` endpoints for monitoring.
Before scaling down or removing workers, release their tasks with `hindsight-admin decommission-worker <worker-id>`.
See [Configuration - Distributed Workers](./configuration#distributed-workers) for all worker settings and [Installation - Helm](./installation#distributed-workers) for Kubernetes deployment.
## Control Plane
Web UI for managing and exploring your memory banks:
- Browse agents and memory banks
- Explore entities and relationships
- View ingestion history and operations
- Test recall queries interactively
The Control Plane connects to the API service and provides a visual interface for development and debugging.
For bare metal deployments, you can run the Control Plane standalone using npx. See [Installation - Bare Metal](./installation#control-plane) for details.
@@ -1,83 +0,0 @@
# Storage
Hindsight uses PostgreSQL as its primary storage backend, with Oracle AI Database available as an alternative for enterprise deployments.
## Why PostgreSQL?
PostgreSQL provides all capabilities required for a semantic memory system in a single database:
| Capability | Implementation |
|------------|----------------|
| Vector search | pgvector extension with HNSW indexes |
| Full-text search | Built-in tsvector with GIN indexes |
| Relational data | Native PostgreSQL |
| JSON documents | JSONB with indexing |
| Graph queries | Recursive CTEs |
### Reduced System Dependencies
Building exclusively for PostgreSQL simplifies deployment and operations:
- Single connection string to configure
- Single backup and restore strategy
- Single monitoring target
- ACID transactions across all data types
- Single upgrade path
### No Storage Abstraction
Hindsight does not abstract storage behind a generic interface. This is a deliberate trade-off.
We believe PostgreSQL is becoming the standard database API. Its popularity, extension ecosystem, and modularity mean that PostgreSQL-compatible interfaces are appearing everywhere—from serverless offerings to distributed databases. Building for PostgreSQL today means compatibility with a growing ecosystem tomorrow.
Supporting multiple databases would increase flexibility but conflict with our core goals: Hindsight is fully open source and designed to be as simple as possible to run and use. Adding database abstractions introduces complexity in code, testing, documentation, and operations—complexity that we pass on to users.
By building on PostgreSQL, we keep the system simple:
- One set of deployment instructions
- One set of performance characteristics to understand
- One codebase optimized for one backend
- No configuration decisions about which database to use
### Oracle AI Database Support
For enterprise deployments, Hindsight also supports Oracle AI Database with full feature parity. All memory operations—retain, recall, and reflect—work identically on Oracle, making it a drop-in option for organizations that standardize on Oracle infrastructure.
## Development with pg0
For local development, Hindsight uses **[pg0](https://github.com/vectorize-io/pg0)**—an embedded PostgreSQL distribution.
### What is pg0?
pg0 is a single binary containing:
- PostgreSQL server
- pgvector extension (pre-installed)
- Automatic initialization
### Behavior
When no `DATABASE_URL` is configured, Hindsight:
1. Starts an embedded PostgreSQL instance on port 5555
2. Initializes the schema
3. Stores data in `~/.hindsight/pg0/`
### Environments
| Environment | Database | Configuration |
|-------------|----------|---------------|
| Development | pg0 (embedded) | Automatic |
| Production | PostgreSQL 15+ | `DATABASE_URL` environment variable |
## Requirements
- PostgreSQL 15 or later
- pgvector 0.5.0 or later
Any PostgreSQL instance that satisfies these requirements should work. If you encounter issues with a specific setup, [open a GitHub issue](https://github.com/hindsight-ai/hindsight/issues).
### Tested Managed Services
- AWS RDS (PostgreSQL 15+)
- Google Cloud SQL
- Azure Database for PostgreSQL
- Supabase
- Neon
@@ -1,410 +0,0 @@
---
sidebar_position: 4
---
# CLI Reference
The Hindsight CLI provides command-line access to memory operations and bank management. All commands follow the [OpenAPI specification](/api-reference), so you can use `--help` on any command to see all available options.
## Installation
```bash
curl -fsSL https://hindsight.vectorize.io/get-cli | bash
```
## Configuration
Configure the API URL:
```bash
# Interactive configuration
hindsight configure
# Or set directly
hindsight configure --api-url http://localhost:8888
# With API key for authentication
hindsight configure --api-url http://localhost:8888 --api-key your-api-key
# Or use environment variables (highest priority)
export HINDSIGHT_API_URL=http://localhost:8888
export HINDSIGHT_API_KEY=your-api-key
```
### Named Profiles
When you need to switch between multiple Hindsight deployments (e.g. local,
staging, production) without constantly rewriting `~/.hindsight/config`, use
named profiles. Each profile is a TOML file at
`~/.hindsight/cli-profiles/<name>.toml` and is selected per-invocation with
`-p/--profile` (or by setting `$HINDSIGHT_PROFILE`).
```bash
# Create (or overwrite) a profile
hindsight profile create prod \
--api-url https://api.hindsight.vectorize.io \
--api-key hsk_...
# List and inspect profiles
hindsight profile list
hindsight profile show prod
# Use a profile for a single command
hindsight -p prod bank list
# Or make it sticky for the current shell
export HINDSIGHT_PROFILE=prod
hindsight bank list
# Remove a profile
hindsight profile delete prod -y
```
Profile files are written with `0600` permissions on Unix so the API key is
only readable by the owner.
**Configuration precedence** (highest first):
1. Environment variables (`HINDSIGHT_API_URL`, `HINDSIGHT_API_KEY`)
2. Named profile — explicit `-p <name>`, otherwise `$HINDSIGHT_PROFILE`
3. Shared config file (`~/.hindsight/config`, written by `hindsight configure`)
4. Default (`http://localhost:8888`)
`HINDSIGHT_API_URL` / `HINDSIGHT_API_KEY` always override profile values, which
makes it safe to use `-p` in scripts while letting CI inject credentials via
environment.
## Core Commands
### Retain (Store Memory)
Store a single memory:
```bash
hindsight memory retain <bank_id> "Alice works at Google as a software engineer"
# With context
hindsight memory retain <bank_id> "Bob loves hiking" --context "hobby discussion"
# Queue for background processing
hindsight memory retain <bank_id> "Meeting notes" --async
```
### Retain Files
Bulk import from files:
```bash
# Single file
hindsight memory retain-files <bank_id> notes.txt
# Directory (recursive by default)
hindsight memory retain-files <bank_id> ./documents/
# With context
hindsight memory retain-files <bank_id> meeting-notes.txt --context "team meeting"
# Background processing
hindsight memory retain-files <bank_id> ./data/ --async
```
### Recall (Search)
Search memories using semantic similarity:
```bash
hindsight memory recall <bank_id> "What does Alice do?"
# With options
hindsight memory recall <bank_id> "hiking recommendations" \
--budget high \
--max-tokens 8192
# Filter by fact type
hindsight memory recall <bank_id> "query" --fact-type world,observation
# Filter by tags
hindsight memory recall <bank_id> "query" --tags work,project \
--tags-match all
# Pin results to a specific time
hindsight memory recall <bank_id> "query" --query-timestamp "2026-01-15T00:00:00Z"
# Show trace information
hindsight memory recall <bank_id> "query" --trace
```
### Reflect (Generate Response)
Generate a response using memories and bank disposition:
```bash
hindsight memory reflect <bank_id> "What do you know about Alice?"
# With additional context
hindsight memory reflect <bank_id> "Should I learn Python?" --context "career advice"
# Higher budget for complex questions
hindsight memory reflect <bank_id> "Summarize my week" --budget high
# Filter by fact type
hindsight memory reflect <bank_id> "query" \
--fact-types world,experience \
--exclude-mental-models
```
### Memory History
View the observation history for a specific memory unit:
```bash
hindsight memory history <bank_id> <memory_id>
```
### Clear Observations
Remove all observations for a memory unit, keeping the core fact:
```bash
hindsight memory clear-observations <bank_id> <memory_id>
# Skip confirmation prompt
hindsight memory clear-observations <bank_id> <memory_id> -y
```
## Bank Management
### List Banks
```bash
hindsight bank list
```
### View Disposition
```bash
hindsight bank disposition <bank_id>
```
### Set Disposition
```bash
hindsight bank set-disposition <bank_id> --mission "..." --name "..."
```
### View Statistics
```bash
hindsight bank stats <bank_id>
```
### Set Bank Name
```bash
hindsight bank name <bank_id> "My Assistant"
```
### Set Mission
```bash
hindsight bank mission <bank_id> "I am a helpful AI assistant interested in technology"
```
### Clear Observations (Bank-wide)
Remove all observations across the entire bank:
```bash
hindsight bank clear-observations <bank_id>
# Skip confirmation prompt
hindsight bank clear-observations <bank_id> -y
```
### Recover Consolidation
Recover from a failed or stuck consolidation:
```bash
hindsight bank consolidation-recover <bank_id>
```
## Document Management
```bash
# List documents
hindsight document list <bank_id>
# Get document details
hindsight document get <bank_id> <document_id>
# Update document metadata
hindsight document update <bank_id> <document_id> --context "updated context"
# Delete document and its memories
hindsight document delete <bank_id> <document_id>
```
## Entity Management
```bash
# List entities
hindsight entity list <bank_id>
# Get entity details
hindsight entity get <bank_id> <entity_id>
```
## Operation Management
Track and manage async operations (retain-files, consolidation, etc.):
```bash
# List operations
hindsight operation list <bank_id>
# Get operation status
hindsight operation get <bank_id> <operation_id>
# Cancel a pending operation
hindsight operation cancel <bank_id> <operation_id>
# Retry a failed operation
hindsight operation retry <bank_id> <operation_id>
```
## Webhook Management
Configure event delivery hooks for bank activity:
```bash
# List webhooks
hindsight webhook list <bank_id>
# Create a webhook (defaults to consolidation.completed events)
hindsight webhook create <bank_id> https://example.com/hook
# Create with specific events and signing secret
hindsight webhook create <bank_id> https://example.com/hook \
--event-types retain.completed,consolidation.completed \
--secret my-hmac-secret
# Update a webhook
hindsight webhook update <bank_id> <webhook_id> --url https://new-url.com
# Delete a webhook
hindsight webhook delete <bank_id> <webhook_id>
# View delivery history
hindsight webhook deliveries <bank_id> <webhook_id>
```
## Audit Logs
Inspect the audit trail for a bank:
```bash
# List audit entries
hindsight audit list <bank_id>
# Filter by action and transport
hindsight audit list <bank_id> --action recall --transport mcp
# Filter by date range
hindsight audit list <bank_id> \
--start-date "2026-04-01T00:00:00Z" \
--end-date "2026-04-10T00:00:00Z"
# Pagination
hindsight audit list <bank_id> --limit 50 --offset 100
```
## Output Formats
```bash
# Pretty (default)
hindsight memory recall <bank_id> "query"
# JSON
hindsight memory recall <bank_id> "query" -o json
# YAML
hindsight memory recall <bank_id> "query" -o yaml
```
## Global Options
| Flag | Description |
|------|-------------|
| `-v, --verbose` | Show detailed output including request/response |
| `-o, --output <format>` | Output format: pretty, json, yaml |
| `--help` | Show help |
| `--version` | Show version |
## Control Plane UI
Launch the web-based Control Plane UI directly from the CLI:
```bash
hindsight ui
```
This runs the Control Plane locally on port 9999 using the API URL from your configuration. The UI provides:
- **Memory bank management** — Browse and manage all your banks
- **Entity explorer** — Visualize the knowledge graph
- **Query testing** — Interactive recall and reflect testing
- **Operation history** — View ingestion and processing logs
:::tip
The UI command requires Node.js to be installed. It automatically downloads and runs the `@vectorize-io/hindsight-control-plane` package via npx.
:::
## Interactive Explorer
Launch the TUI explorer for visual navigation of your memory banks:
```bash
hindsight explore
```
The explorer provides an interactive terminal interface to:
- **Browse memory banks** — View all banks and their statistics
- **Search memories** — Run recall queries with real-time results
- **Inspect entities** — Explore the knowledge graph and entity relationships
- **View facts** — Browse world facts, experiences, and observations
- **Navigate documents** — See source documents and their extracted memories
### Keyboard Shortcuts
| Key | Action |
|-----|--------|
| `↑/↓` | Navigate items |
| `Enter` | Select / Expand |
| `Tab` | Switch panels |
| `/` | Search |
| `q` | Quit |
<!-- Screenshot placeholder: explore command TUI -->
## Example Workflow
```bash
# Configure API URL
hindsight configure --api-url http://localhost:8888
# Store some memories
hindsight memory retain demo "Alice works at Google"
hindsight memory retain demo "Bob is a data scientist"
hindsight memory retain demo "Alice and Bob are colleagues"
# Search memories
hindsight memory recall demo "Who works with Alice?"
# Generate a response
hindsight memory reflect demo "What do you know about the team?"
# Check bank disposition
hindsight bank disposition demo
```
@@ -1,247 +0,0 @@
---
sidebar_position: 5
---
# Daemon CLI (hindsight-embed)
Zero-configuration local memory system with automatic daemon management. Perfect for development, prototyping, and single-user applications.
## Overview
`hindsight-embed` is a zero-configuration SDK that wraps the Hindsight API and PostgreSQL database into a single auto-managed local daemon. It's designed for development, prototyping, and single-user applications where you want memory capabilities without infrastructure overhead.
**How it works:**
1. **First command triggers startup**: When you run any `hindsight-embed` command, it checks if a local daemon is running
2. **Auto-daemon management**: If no daemon exists, it automatically spawns `hindsight-api --daemon` in the background
3. **Embedded database**: The daemon uses `pg0` (embedded PostgreSQL) — no separate database installation required
4. **Command forwarding**: Your command is forwarded to the local daemon via HTTP (localhost:8888)
5. **Auto-shutdown**: After 5 minutes of inactivity (configurable), the daemon gracefully shuts down to free resources
**Key features:**
- **Zero setup** — One `configure` command and you're ready
- **Automatic lifecycle** — Daemon starts on-demand, stops when idle
- **Isolated storage** — Each bank gets its own embedded PostgreSQL database
- **Local-only** — Binds to `127.0.0.1:8888`, not accessible from network
- **Production-grade engine** — Uses the same memory engine as the full API service
Think of it as SQLite for long-term memory — all the power of Hindsight without managing servers.
## Installation
Install via `uvx` (recommended - always latest version):
```bash
# Run directly without installation
uvx hindsight-embed@latest configure
# Or use pipx for persistent installation
pipx install hindsight-embed
```
## Quick Start
### 1. Configure
```bash
# Interactive configuration
hindsight-embed configure
# Or non-interactive via environment variables
export HINDSIGHT_EMBED_LLM_PROVIDER=openai
export HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=gpt-4o-mini
hindsight-embed configure
```
Configuration is saved to `~/.hindsight/embed`:
```bash
HINDSIGHT_EMBED_LLM_PROVIDER=openai
HINDSIGHT_EMBED_LLM_MODEL=gpt-4o-mini
HINDSIGHT_EMBED_BANK_ID=default
HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
# Daemon settings (macOS: force CPU to avoid MPS/XPC issues)
HINDSIGHT_API_EMBEDDINGS_LOCAL_FORCE_CPU=1
HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU=1
```
### 2. Use Memory Operations
```bash
# Store a memory
hindsight-embed memory retain default "User prefers dark mode"
# Query memories
hindsight-embed memory recall default "user preferences"
# Reasoning with memory
hindsight-embed memory reflect default "What color scheme should I use?"
```
The daemon starts automatically on first use!
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_EMBED_LLM_API_KEY` | **Required**. API key for LLM provider | - |
| `HINDSIGHT_EMBED_LLM_PROVIDER` | LLM provider: `openai`, `anthropic`, `gemini`, `groq`, `minimax`, `ollama` | `openai` |
| `HINDSIGHT_EMBED_LLM_MODEL` | Model name | `gpt-4o-mini` |
| `HINDSIGHT_EMBED_BANK_ID` | Default memory bank ID | `default` |
| `HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT` | Seconds before daemon auto-exits when idle (0 = never) | `0` |
**Provider Examples:**
```bash
# OpenAI
export HINDSIGHT_EMBED_LLM_PROVIDER=openai
export HINDSIGHT_EMBED_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=gpt-4o
# Groq (fast inference)
export HINDSIGHT_EMBED_LLM_PROVIDER=groq
export HINDSIGHT_EMBED_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=llama-3.3-70b-versatile
# Anthropic
export HINDSIGHT_EMBED_LLM_PROVIDER=anthropic
export HINDSIGHT_EMBED_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_EMBED_LLM_MODEL=claude-sonnet-4-20250514
```
## Daemon Management
### Idle Timeout
Customize how long the daemon stays alive when idle:
```bash
# Never timeout (daemon runs until manually stopped)
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=0
# Shorter timeout: 1 minute
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=60
# Longer timeout: 30 minutes
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=1800
```
### Daemon Commands
```bash
# Check daemon status
hindsight-embed daemon status
# View daemon logs in real-time
hindsight-embed daemon logs -f
# Stop daemon manually
hindsight-embed daemon stop
```
## Commands
All memory operations follow the same interface as the CLI:
### Retain (Store Memory)
```bash
hindsight-embed memory retain <bank_id> "content"
# With context
hindsight-embed memory retain <bank_id> "content" --context "source information"
# Background processing
hindsight-embed memory retain <bank_id> "content" --async
```
### Recall (Search)
```bash
hindsight-embed memory recall <bank_id> "query"
# With budget control
hindsight-embed memory recall <bank_id> "query" --budget high
# Show trace
hindsight-embed memory recall <bank_id> "query" --trace
```
### Reflect (Generate Response)
```bash
hindsight-embed memory reflect <bank_id> "prompt"
# With additional context
hindsight-embed memory reflect <bank_id> "prompt" --context "additional info"
```
### Bank Management
```bash
# List all banks
hindsight-embed bank list
# View bank stats
hindsight-embed bank stats <bank_id>
# Set bank name
hindsight-embed bank name <bank_id> "My Assistant"
# Set bank mission
hindsight-embed bank mission <bank_id> "I am a helpful AI assistant"
```
## Troubleshooting
### Daemon Won't Start
Check the daemon logs:
```bash
hindsight-embed daemon logs
# Or watch in real-time
hindsight-embed daemon logs -f
```
Common issues:
- **Missing API key**: Set `HINDSIGHT_EMBED_LLM_API_KEY`
- **Port conflict**: Another service using port 8888
- **Permissions**: Check `~/.hindsight/` directory permissions
### Daemon Exits Immediately
Check if you have the idle timeout set too low:
```bash
# Disable idle timeout for debugging
export HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT=0
hindsight-embed daemon status
```
### Reset Configuration
```bash
# Remove config file and reconfigure
rm ~/.hindsight/embed
hindsight-embed configure
```
## When to Use
**Perfect for:**
- Development and prototyping
- Single-user applications
- Local-first tools
- Quick experiments with Hindsight
**Not suitable for:**
- Production multi-user deployments
- Network-accessible services
- High-availability requirements
- Multi-tenant applications
For production deployments, use the [API Service](/developer/services) with external PostgreSQL instead.
@@ -1,51 +0,0 @@
---
sidebar_position: 3
---
# Go Client
Official Go client for the Hindsight API, generated from the OpenAPI 3.1 spec using [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator).
import CodeSnippet from '@site/src/components/CodeSnippet';
import quickstartGo from '!!raw-loader!@site/examples/api/quickstart.go';
## Installation
```bash
go get github.com/vectorize-io/hindsight/hindsight-clients/go
```
Requires Go 1.23+.
## Quick Start
<CodeSnippet code={quickstartGo} section="quickstart-full" language="go" />
## API Structure
The Go client provides access to all Hindsight API operations through structured namespaces:
- **`client.MemoryAPI`** - Retain, recall, reflect operations
- **`client.BanksAPI`** - Bank management
- **`client.DirectivesAPI`** - Directive management
- **`client.MentalModelsAPI`** - Mental model management
- **`client.DocumentsAPI`** - Document operations
- **`client.EntitiesAPI`** - Entity operations
- **`client.OperationsAPI`** - Async operation monitoring
## Working with Nullable Fields
The Go client uses `NullableString`, `NullableTime`, and similar types for optional fields:
<CodeSnippet code={quickstartGo} section="nullable-fields" language="go" />
## Error Handling
<CodeSnippet code={quickstartGo} section="error-handling" language="go" />
## More Examples
For detailed examples of all operations, see:
- [Python SDK documentation](./python.md) - API concepts are the same
- [Node.js SDK documentation](./nodejs.md) - API concepts are the same
- [OpenAPI specification](https://hindsight.dev/openapi.json) - Complete API reference
@@ -1,90 +0,0 @@
---
sidebar_position: 6
---
# Programmatic API (Node.js)
The `@vectorize-io/hindsight-all` npm package is the Node.js equivalent of the Python [`hindsight-all`](./hindsight-all.md) package. It lets your Node code spawn and supervise a local Hindsight daemon without deploying any server infrastructure — pair it with [`@vectorize-io/hindsight-client`](./nodejs.md) for memory operations.
The daemon runs as a **separate OS process** on `127.0.0.1` (not in your Node process). Your code talks to it over HTTP via `HindsightClient`.
This package **does not ship an HTTP client** — it only owns the server process. Once the daemon is running, talk to it with [`@vectorize-io/hindsight-client`](./nodejs.md) against `server.getBaseUrl()`. The two packages compose: one owns the process, the other owns the API surface.
## How it works
1. `server.start()` resolves the underlying `hindsight-embed` command (via `uvx` from PyPI, or `uv run --directory <path>` for a local checkout).
2. Runs `profile create <name> --merge --port <port> [--env KEY=VALUE ...]` with every entry from `options.env` forwarded as `--env`.
3. Runs `daemon --profile <name> start`.
4. Polls `http://host:port/health` until it returns `200` or the `readyTimeoutMs` budget is exhausted.
5. `server.stop()` runs `daemon --profile <name> stop`.
The server is intentionally transparent: new daemon env vars or CLI flags never require a wrapper release — pass them through `env`, `extraProfileCreateArgs`, or `extraDaemonStartArgs`.
## Requirements
- **Node.js ≥ 22** — uses global `fetch` and `AbortSignal.timeout`.
- **`uv` / `uvx`** on `PATH` — used to download and run the Hindsight daemon. Install via [docs.astral.sh/uv](https://docs.astral.sh/uv/).
## Install
```bash
npm install @vectorize-io/hindsight-all @vectorize-io/hindsight-client
```
## Example
```ts
import { HindsightServer, consoleLogger } from '@vectorize-io/hindsight-all';
import { HindsightClient } from '@vectorize-io/hindsight-client';
const server = new HindsightServer({
profile: 'my-app',
port: 9077,
env: {
HINDSIGHT_API_LLM_PROVIDER: 'anthropic',
HINDSIGHT_API_LLM_API_KEY: process.env.ANTHROPIC_API_KEY,
HINDSIGHT_API_LLM_MODEL: 'claude-sonnet-4-20250514',
HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT: '0',
},
logger: consoleLogger,
});
await server.start();
const client = new HindsightClient({ baseUrl: server.getBaseUrl() });
await client.retain('user-123', 'User prefers dark mode.');
const recall = await client.recall('user-123', 'what are the user preferences?');
await server.stop();
```
For a remote Hindsight API, skip the server entirely and point `HindsightClient` directly at the remote URL.
## `HindsightServerOptions`
| Option | Type | Default | Description |
|---|---|---|---|
| `profile` | `string` | `"default"` | Profile name passed to `--profile` on every sub-command. |
| `port` | `number` | `8888` | TCP port the daemon listens on. |
| `host` | `string` | `"127.0.0.1"` | Hostname the daemon binds to (used for health checks). |
| `embedVersion` | `string` | `"latest"` | Version of the underlying `hindsight-embed` package to run via `uvx`. |
| `embedPackagePath` | `string` | — | Local checkout path — takes precedence over `embedVersion`. Uses `uv run --directory` instead of `uvx`. |
| `env` | `Record<string, string \| undefined>` | `{}` | Environment variables passed to the daemon process **and** written into the profile config via `--env KEY=VALUE`. The preferred way to surface any `HINDSIGHT_API_*` / `HINDSIGHT_EMBED_*` setting. |
| `extraProfileCreateArgs` | `string[]` | `[]` | Extra args appended verbatim to `profile create`. |
| `extraDaemonStartArgs` | `string[]` | `[]` | Extra args appended verbatim to `daemon start`. |
| `platformCpuWorkaround` | `boolean` | `true` on macOS | Auto-set `HINDSIGHT_API_EMBEDDINGS_LOCAL_FORCE_CPU=1` and `HINDSIGHT_API_RERANKER_LOCAL_FORCE_CPU=1` to avoid Metal/MPS crashes. Caller-supplied `env` values win over the auto-applied ones. |
| `readyTimeoutMs` | `number` | `30000` | Max time to wait for `/health` to return 200. |
| `readyPollIntervalMs` | `number` | `1000` | Polling interval while waiting for `/health`. |
| `logger` | `Logger` | silent | Pluggable logger (`debug`/`info`/`warn`/`error`). `consoleLogger` and `silentLogger` helpers are exported. |
## Server methods
| Method | Returns | Description |
|---|---|---|
| `start()` | `Promise<void>` | Configure profile, spawn the daemon, wait for `/health`. Idempotent — safe to re-run. |
| `stop()` | `Promise<void>` | Stop the daemon. Never throws; logs and resolves even on failure. |
| `checkHealth()` | `Promise<boolean>` | One-shot `/health` probe with a 2 s timeout. |
| `getBaseUrl()` | `string` | `http://host:port` — pass this straight to `HindsightClient`. |
| `getProfile()` | `string` | The profile name this server operates on. |
For memory operations (retain, recall, reflect, bank management) use [`@vectorize-io/hindsight-client`](./nodejs.md).
@@ -1,146 +0,0 @@
---
sidebar_position: 2
---
# Programmatic API (Python)
The `hindsight-all` Python package lets your code spawn and manage a local Hindsight daemon without deploying any server infrastructure. It bundles the Hindsight API server, embedded PostgreSQL, and the Python client into one install — `pip install hindsight-all` and you can start a fully-functional Hindsight instance from a few lines of Python.
The daemon runs as a **separate OS process** on `127.0.0.1` (not in your Python process memory). Your code talks to it over HTTP via the bundled `HindsightClient`.
If you already have a Hindsight server running and just need a client, use [Python Client (hindsight-client)](./python.md) instead.
## How it works
`hindsight-all` exposes two primary APIs:
- **`HindsightServer`** — explicit lifecycle. Use it as a context manager when you want deterministic startup/shutdown (e.g. in tests).
- **`HindsightEmbedded`** — auto-managed. Starts a daemon on first use, reuses it across calls, shuts it down after an idle timeout. Easiest for application code that doesn't want to think about lifecycle.
Both end up talking to the same underlying daemon via the same `HindsightClient` HTTP interface — the difference is only how the server process is managed.
## Installation
```bash
pip install hindsight-all
```
The `hindsight-all` wheel bundles `hindsight-api-slim`, `hindsight-client`, and `hindsight-embed` as dependencies, so one `pip install` gets you everything.
## `HindsightServer` — explicit lifecycle
Use `HindsightServer` as a context manager when you want the server to start immediately, run for the duration of a block, and shut down cleanly afterwards. Ideal for tests and short-lived scripts.
```python
import os
from hindsight import HindsightServer, HindsightClient
with HindsightServer(
llm_provider="openai",
llm_model="gpt-4o-mini",
llm_api_key=os.environ["OPENAI_API_KEY"],
) as server:
client = HindsightClient(base_url=server.url)
client.retain(bank_id="my-bank", content="Alice works at Google")
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r.text)
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
# Server is stopped here
```
## `HindsightEmbedded` — auto-managed
`HindsightEmbedded` is the simplest way to use Hindsight in Python. It automatically manages a background daemon for you — starts on first use, stays alive across calls, shuts down after an idle timeout.
```python
from hindsight import HindsightEmbedded
import os
# Server starts automatically on first call
client = HindsightEmbedded(
profile="myapp", # Profile for data isolation
llm_provider="openai",
llm_model="gpt-4o-mini",
llm_api_key=os.environ["OPENAI_API_KEY"],
)
# Use immediately - no manual server management needed
client.retain(bank_id="my-bank", content="Alice works at Google")
results = client.recall(bank_id="my-bank", query="What does Alice do?")
# Server continues running (auto-stops after idle timeout)
# Or explicitly stop it:
client.close(stop_daemon=True)
```
### What's a Profile?
A profile is an isolated Hindsight environment. Each profile gets its own embedded PostgreSQL database (stored in `~/.pg0/instances/hindsight-embed-{profile}/`) and its own API server. Use different profiles to separate environments (dev/prod), applications, or users.
### When to use which
| Use case | Pick |
|---|---|
| Tests, short-lived scripts, deterministic startup/shutdown | `HindsightServer` (context manager) |
| Long-running application, auto-start on first use, don't want to manage lifecycle | `HindsightEmbedded` |
| Existing Hindsight server running elsewhere | [`hindsight-client`](./python.md) directly |
## API namespaces
Both `HindsightEmbedded` and `HindsightClient` expose organized API namespaces for bank management, mental models, directives, and memories:
```python
from hindsight import HindsightEmbedded
import os
embedded = HindsightEmbedded(
profile="myapp",
llm_provider="openai",
llm_api_key=os.environ["OPENAI_API_KEY"],
)
# Core operations
embedded.retain(bank_id="test", content="Hello")
results = embedded.recall(bank_id="test", query="Hello")
# Bank management
embedded.banks.create(bank_id="test", name="Test Bank", mission="Help users")
embedded.banks.set_mission(bank_id="test", mission="Updated mission")
embedded.banks.delete(bank_id="test")
# Mental models
embedded.mental_models.create(
bank_id="test",
name="User Preferences",
content="User prefers dark mode"
)
models = embedded.mental_models.list(bank_id="test")
# Directives
embedded.directives.create(
bank_id="test",
name="Response Style",
content="Be concise and friendly"
)
directives = embedded.directives.list(bank_id="test")
# List memories
memories = embedded.memories.list(bank_id="test", type="world", limit=50)
```
API namespaces ensure the daemon is running before each call, so daemon crashes are handled gracefully:
```python
# ✅ GOOD - Uses API namespace (daemon restarts handled)
embedded.banks.create(bank_id="test", name="Test")
# ❌ BAD - Direct client access (daemon crashes NOT handled)
client = embedded.client
client.create_bank(bank_id="test", name="Test") # Fails if daemon crashed
```
For the full reference of retain/recall/reflect methods and their options (which work the same regardless of how you obtain the client) see the [Python Client page](./python.md).
@@ -1,173 +0,0 @@
---
sidebar_position: 2
---
# TypeScript / JavaScript Client
Official TypeScript/JavaScript client for the Hindsight API. Supports **Node.js** and **Deno**.
## Installation
### Node.js
```bash
npm install @vectorize-io/hindsight-client
```
### Deno
No installation needed — import directly via the `npm:` specifier:
```typescript
import { HindsightClient } from "npm:@vectorize-io/hindsight-client";
```
## Quick Start
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Retain a memory
await client.retain('my-bank', 'Alice works at Google');
// Recall memories
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(r.text);
}
// Reflect - generate response with disposition
const answer = await client.reflect('my-bank', 'Tell me about Alice');
console.log(answer.text);
```
## Client Initialization
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({
baseUrl: 'http://localhost:8888',
});
```
## Core Operations
### Retain (Store Memory)
```typescript
// Simple
await client.retain('my-bank', 'Alice works at Google');
// With options
await client.retain('my-bank', 'Alice got promoted', {
timestamp: new Date('2024-01-15'),
context: 'career update',
metadata: { source: 'slack' },
async: false, // Set true for background processing
});
```
### Retain Batch
```typescript
await client.retainBatch('my-bank', [
{ content: 'Alice works at Google', context: 'career' },
{ content: 'Bob is a data scientist', context: 'career' },
], {
async: false,
});
```
### Recall (Search)
```typescript
// Simple - returns RecallResponse
const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) {
console.log(`${r.text} (type: ${r.type})`);
}
// With options
const response = await client.recall('my-bank', 'What does Alice do?', {
types: ['world', 'observation'], // Filter by fact type
maxTokens: 4096,
budget: 'high', // 'low', 'mid', or 'high'
});
```
### Reflect (Generate Response)
```typescript
const answer = await client.reflect('my-bank', 'What should I know about Alice?', {
budget: 'low', // 'low', 'mid', or 'high'
context: 'preparing for a meeting',
});
console.log(answer.text); // Generated response
```
## Bank Management
### Create Bank
```typescript
await client.createBank('my-bank', {
name: 'Assistant',
mission: "You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition: {
skepticism: 3, // 1-5: trusting to skeptical
literalism: 3, // 1-5: flexible to literal
empathy: 3, // 1-5: detached to empathetic
},
});
```
### List Memories
```typescript
const response = await client.listMemories('my-bank', {
type: 'world', // Optional filter
q: 'Alice', // Optional text search
limit: 100,
offset: 0,
});
console.log(response)
```
## Document Management
### Get Document
```typescript
const doc = await client.getDocument('my-bank', 'conversation_001');
if (doc) {
console.log(doc); // null when document not found
}
```
### List Documents
```typescript
const response = await client.listDocuments('my-bank', {
limit: 50,
offset: 0,
});
console.log(response);
```
### Update Document
```typescript
await client.updateDocument('my-bank', 'conversation_001', {
tags: ['important', 'meeting-notes'],
});
```
### Delete Document
```typescript
await client.deleteDocument('my-bank', 'conversation_001');
```
@@ -1,219 +0,0 @@
---
sidebar_position: 1
---
# Python Client
Official HTTP client for the Hindsight API. Use this when you have a Hindsight server already running — locally, in Docker, or as a managed service — and you want a typed Python client to talk to it.
If you want to **embed and run a Hindsight server in your Python process** (no external server required), see [Embedded Python (hindsight-all)](./hindsight-all.md) instead.
## Installation
```bash
pip install hindsight-client
```
## Quick Start
```python
from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888")
# Retain a memory
client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results.results:
print(r.text)
# Reflect - generate a contextual answer
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text)
```
## Client Initialization
```python
from hindsight_client import Hindsight
client = Hindsight(
base_url="http://localhost:8888", # Hindsight API URL
timeout=30.0, # Request timeout in seconds
# api_key="your-api-key", # Optional bearer token
)
# Core operations
client.retain(bank_id="test", content="Hello world")
results = client.recall(bank_id="test", query="Hello")
# Organized API namespaces
client.banks.create(bank_id="test", name="Test Bank")
models = client.mental_models.list(bank_id="test")
directives = client.directives.list(bank_id="test")
memories = client.memories.list(bank_id="test")
```
## Core Operations
### Retain (Store Memory)
```python
# Simple
client.retain(
bank_id="my-bank",
content="Alice works at Google as a software engineer",
)
# With options
from datetime import datetime
client.retain(
bank_id="my-bank",
content="Alice got promoted",
context="career update",
timestamp=datetime(2024, 1, 15),
document_id="conversation_001",
metadata={"source": "slack"},
)
```
### Retain Batch
```python
client.retain_batch(
bank_id="my-bank",
items=[
{"content": "Alice works at Google", "context": "career"},
{"content": "Bob is a data scientist", "context": "career"},
],
document_id="conversation_001",
retain_async=False, # Set True for background processing
)
```
### Recall (Search)
```python
# Simple - returns list of RecallResult
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
)
for r in results.results:
print(f"{r.text} (type: {r.type})")
# With options
results = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "observation"], # Filter by fact type
max_tokens=4096,
budget="high", # low, mid, or high
)
```
### Recall with Chunks
```python
# Returns RecallResponse with source chunks
response = client.recall(
bank_id="my-bank",
query="What does Alice do?",
types=["world", "experience"],
budget="mid",
max_tokens=4096,
include_chunks=True,
max_chunk_tokens=500
)
print(f"Found {len(response.results)} memories")
for r in response.results:
print(f" - {r.text}")
if r.chunks:
print(f" Source: {r.chunks[0].text[:100]}...")
```
### Reflect (Generate Response)
```python
answer = client.reflect(
bank_id="my-bank",
query="What should I know about Alice?",
budget="low", # low, mid, or high
context="preparing for a meeting",
)
print(answer.text) # Generated response
```
## Bank Management
### Create Bank
```python
client.create_bank(
bank_id="my-bank",
name="Assistant",
mission="You're a helpful AI assistant - keep track of user preferences and conversation history.",
disposition={
"skepticism": 3, # 1-5: trusting to skeptical
"literalism": 3, # 1-5: flexible to literal
"empathy": 3, # 1-5: detached to empathetic
},
)
```
### List Memories
```python
client.list_memories(
bank_id="my-bank",
type="world", # Optional: filter by type
search_query="Alice", # Optional: text search
limit=100,
offset=0,
)
```
## Async Support
All methods have async versions prefixed with `a`:
```python
import asyncio
from hindsight_client import Hindsight
async def main():
client = Hindsight(base_url="http://localhost:8888")
# Async retain
await client.aretain(bank_id="my-bank", content="Hello world")
# Async recall
results = await client.arecall(bank_id="my-bank", query="Hello")
for r in results:
print(r.text)
# Async reflect
answer = await client.areflect(bank_id="my-bank", query="What did I say?")
print(answer.text)
client.close()
asyncio.run(main())
```
## Context Manager
```python
from hindsight_client import Hindsight
with Hindsight(base_url="http://localhost:8888") as client:
client.retain(bank_id="my-bank", content="Hello")
results = client.recall(bank_id="my-bank", query="Hello")
# Client automatically closed
```
@@ -1,260 +0,0 @@
{
"developerSidebar": [
{
"type": "category",
"label": "Architecture",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "developer/index",
"label": "Overview",
"customProps": {
"icon": "lu-book"
}
},
{
"type": "doc",
"id": "developer/retain",
"label": "Retain",
"customProps": {
"icon": "lu-brain"
}
},
{
"type": "doc",
"id": "developer/retrieval",
"label": "Recall",
"customProps": {
"icon": "lu-search"
}
},
{
"type": "doc",
"id": "developer/reflect",
"label": "Reflect",
"customProps": {
"icon": "lu-message"
}
},
{
"type": "doc",
"id": "developer/multilingual",
"label": "Multilingual",
"customProps": {
"icon": "lu-languages"
}
},
{
"type": "doc",
"id": "developer/performance",
"label": "Performance",
"customProps": {
"icon": "lu-zap"
}
},
{
"type": "doc",
"id": "developer/storage",
"label": "Storage",
"customProps": {
"icon": "lu-database"
}
},
{
"type": "doc",
"id": "developer/rag-vs-hindsight",
"label": "RAG vs Memory",
"customProps": {
"icon": "lu-compare"
}
}
]
},
{
"type": "category",
"label": "API",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "developer/api/quickstart",
"label": "Quick Start",
"customProps": {
"icon": "lu-rocket"
}
},
{
"type": "doc",
"id": "developer/api/retain",
"label": "Retain",
"customProps": {
"icon": "lu-brain"
}
},
{
"type": "doc",
"id": "developer/api/recall",
"label": "Recall",
"customProps": {
"icon": "lu-search"
}
},
{
"type": "doc",
"id": "developer/api/reflect",
"label": "Reflect",
"customProps": {
"icon": "lu-message"
}
},
{
"type": "doc",
"id": "developer/api/memory-banks",
"label": "Memory Banks",
"customProps": {
"icon": "lu-memory"
}
},
{
"type": "doc",
"id": "developer/api/entities",
"label": "Entities",
"customProps": {
"icon": "lu-network"
}
},
{
"type": "doc",
"id": "developer/api/documents",
"label": "Documents",
"customProps": {
"icon": "lu-file"
}
},
{
"type": "doc",
"id": "developer/api/operations",
"label": "Operations",
"customProps": {
"icon": "lu-cpu"
}
}
]
},
{
"type": "category",
"label": "Clients",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "sdks/python",
"label": "Python",
"customProps": {
"icon": "si-python"
}
},
{
"type": "doc",
"id": "sdks/nodejs",
"label": "TypeScript",
"customProps": {
"icon": "/img/icons/typescript.png"
}
},
{
"type": "doc",
"id": "sdks/cli",
"label": "CLI",
"customProps": {
"icon": "lu-terminal"
}
}
]
},
{
"type": "category",
"label": "Integrations",
"collapsible": false,
"items": [
{
"type": "link",
"href": "/integrations",
"label": "Browse all integrations"
}
]
},
{
"type": "category",
"label": "Hosting",
"collapsible": false,
"items": [
{
"type": "doc",
"id": "developer/installation",
"label": "Installation",
"customProps": {
"icon": "lu-package"
}
},
{
"type": "doc",
"id": "developer/services",
"label": "Services",
"customProps": {
"icon": "lu-server"
}
},
{
"type": "doc",
"id": "developer/configuration",
"label": "Configuration",
"customProps": {
"icon": "lu-settings"
}
},
{
"type": "doc",
"id": "developer/admin-cli",
"label": "Admin CLI",
"customProps": {
"icon": "lu-terminal"
}
},
{
"type": "doc",
"id": "developer/extensions",
"label": "Extensions",
"customProps": {
"icon": "lu-plug"
}
},
{
"type": "doc",
"id": "developer/models",
"label": "Models",
"customProps": {
"icon": "lu-cpu"
}
},
{
"type": "doc",
"id": "developer/monitoring",
"label": "Monitoring",
"customProps": {
"icon": "lu-activity"
}
},
{
"type": "doc",
"id": "developer/mcp-server",
"label": "MCP Server",
"customProps": {
"icon": "lu-network"
}
}
]
}
]
}

Some files were not shown because too many files have changed in this diff Show More