docs, packages and quick start

This commit is contained in:
Nicolò Boschi 2025-12-04 12:49:01 +01:00
parent 9b69202525
commit 6073ac4ffd
38 changed files with 692 additions and 2831 deletions

View file

@ -313,6 +313,10 @@ jobs:
## Quick Start ## Quick Start
```bash ```bash
# Install the CLI
curl -fsSL https://raw.githubusercontent.com/vectorize-io/hindsight/refs/heads/main/hindsight-cli/install.sh | bash
# Start the server
docker run -p 8888:8888 -p 9999:9999 \ docker run -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_PROVIDER=openai \ -e HINDSIGHT_API_LLM_PROVIDER=openai \
-e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \ -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
@ -325,6 +329,11 @@ jobs:
- `ghcr.io/${{ github.repository_owner }}/hindsight-api:${{ steps.get_version.outputs.VERSION }}` - API only - `ghcr.io/${{ github.repository_owner }}/hindsight-api:${{ steps.get_version.outputs.VERSION }}` - API only
- `ghcr.io/${{ github.repository_owner }}/hindsight-control-plane:${{ steps.get_version.outputs.VERSION }}` - Web UI only - `ghcr.io/${{ github.repository_owner }}/hindsight-control-plane:${{ steps.get_version.outputs.VERSION }}` - Web UI only
## CLI
```bash
curl -fsSL https://raw.githubusercontent.com/vectorize-io/hindsight/refs/heads/main/hindsight-cli/install.sh | bash
```
## Python ## Python
```bash ```bash
pip install hindsight-all # or hindsight-api, hindsight-client pip install hindsight-all # or hindsight-api, hindsight-client
@ -335,9 +344,6 @@ jobs:
npm install @vectorize-io/hindsight-client npm install @vectorize-io/hindsight-client
``` ```
## CLI
Download the appropriate binary from the release assets below.
## Helm ## Helm
```bash ```bash
helm install hindsight oci://ghcr.io/${{ github.repository_owner }}/charts/hindsight --version ${{ steps.get_version.outputs.VERSION }} helm install hindsight oci://ghcr.io/${{ github.repository_owner }}/charts/hindsight --version ${{ steps.get_version.outputs.VERSION }}

View file

@ -1,6 +1,9 @@
# Hindsight # Hindsight
[![CI](https://github.com/vectorize-io/hindsight/actions/workflows/test.yml/badge.svg)](https://github.com/vectorize-io/hindsight/actions/workflows/test.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![PyPI - hindsight-client](https://img.shields.io/pypi/v/hindsight-client?label=hindsight-client)](https://pypi.org/project/hindsight-client/) [![PyPI - hindsight-client](https://img.shields.io/pypi/v/hindsight-client?label=hindsight-client)](https://pypi.org/project/hindsight-client/)
[![PyPI - hindsight-api](https://img.shields.io/pypi/v/hindsight-api?label=hindsight-api)](https://pypi.org/project/hindsight-api/)
[![PyPI - hindsight-all](https://img.shields.io/pypi/v/hindsight-all?label=hindsight-all)](https://pypi.org/project/hindsight-all/) [![PyPI - hindsight-all](https://img.shields.io/pypi/v/hindsight-all?label=hindsight-all)](https://pypi.org/project/hindsight-all/)
[![npm](https://img.shields.io/npm/v/@vectorize-io/hindsight-client)](https://www.npmjs.com/package/@vectorize-io/hindsight-client) [![npm](https://img.shields.io/npm/v/@vectorize-io/hindsight-client)](https://www.npmjs.com/package/@vectorize-io/hindsight-client)

View file

@ -16,8 +16,8 @@ from typing import Optional
import uvicorn import uvicorn
from hindsight_api import MemoryEngine from . import MemoryEngine
from hindsight_api.api import create_app from .api import create_app
# Disable tokenizers parallelism to avoid warnings # Disable tokenizers parallelism to avoid warnings
@ -53,7 +53,7 @@ def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog="hindsight-api", prog="hindsight-api",
description="Hindsight - Semantic memory system for AI agents. Press Ctrl+C to stop.", description="Hindsight API Server",
) )
parser.add_argument( parser.add_argument(
"--host", default="0.0.0.0", "--host", default="0.0.0.0",
@ -63,18 +63,6 @@ def main():
"--port", type=int, default=8888, "--port", type=int, default=8888,
help="Port to bind to (default: 8888)" help="Port to bind to (default: 8888)"
) )
parser.add_argument(
"--mcp", action="store_true",
help="Enable MCP server at /mcp"
)
parser.add_argument(
"--reload", action="store_true",
help="Enable auto-reload on code changes"
)
parser.add_argument(
"--workers", type=int, default=1,
help="Number of worker processes (default: 1)"
)
parser.add_argument( parser.add_argument(
"--log-level", default="info", "--log-level", default="info",
choices=["critical", "error", "warning", "info", "debug", "trace"], choices=["critical", "error", "warning", "info", "debug", "trace"],
@ -112,7 +100,7 @@ def main():
app = create_app( app = create_app(
memory=_memory, memory=_memory,
http_api_enabled=True, http_api_enabled=True,
mcp_api_enabled=args.mcp, mcp_api_enabled=True,
mcp_mount_path="/mcp", mcp_mount_path="/mcp",
run_migrations=True, run_migrations=True,
initialize_memory=True, initialize_memory=True,
@ -127,16 +115,10 @@ def main():
"access_log": args.access_log, "access_log": args.access_log,
} }
if args.reload:
uvicorn_config["reload"] = True
if args.workers > 1:
uvicorn_config["workers"] = args.workers
print(f"\nStarting Hindsight API...") print(f"\nStarting Hindsight API...")
print(f" URL: http://{args.host}:{args.port}") print(f" URL: http://{args.host}:{args.port}")
print(f" Database: {db_url}") print(f" Database: {db_url}")
print(f" LLM Provider: {llm_provider}") print(f" LLM Provider: {llm_provider}")
print(f" MCP: {'enabled' if args.mcp else 'disabled'}")
print() print()
uvicorn.run(**uvicorn_config) uvicorn.run(**uvicorn_config)

View file

@ -85,7 +85,7 @@ if __name__ == "__main__":
env_log_level = "info" env_log_level = "info"
# Parse CLI arguments # Parse CLI arguments
parser = argparse.ArgumentParser(description="Memory Graph API Server") parser = argparse.ArgumentParser(description="Hindsight API Server")
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to (default: 0.0.0.0)") parser.add_argument("--host", default="0.0.0.0", help="Host to bind to (default: 0.0.0.0)")
parser.add_argument("--port", type=int, default=8888, help="Port to bind to (default: 8888)") parser.add_argument("--port", type=int, default=8888, help="Port to bind to (default: 8888)")
parser.add_argument("--reload", action="store_true", help="Enable auto-reload on code changes") parser.add_argument("--reload", action="store_true", help="Enable auto-reload on code changes")

View file

@ -47,6 +47,9 @@ test = [
"testcontainers[postgres]>=4.0.0", "testcontainers[postgres]>=4.0.0",
] ]
[project.scripts]
hindsight-api = "hindsight_api.cli:main"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["hindsight_api"] packages = ["hindsight_api"]

View file

@ -116,7 +116,10 @@ impl ApiClient {
}) })
} }
pub fn recall(&self, agent_id: &str, request: &types::RecallRequest, _verbose: bool) -> Result<types::RecallResponse> { pub fn recall(&self, agent_id: &str, request: &types::RecallRequest, verbose: bool) -> Result<types::RecallResponse> {
if verbose {
eprintln!("Request body: {}", serde_json::to_string_pretty(request).unwrap_or_default());
}
self.runtime.block_on(async { self.runtime.block_on(async {
let response = self.client.recall_memories(agent_id, request).await?; let response = self.client.recall_memories(agent_id, request).await?;
Ok(response.into_inner()) Ok(response.into_inner())

View file

@ -145,8 +145,8 @@ enum MemoryCommands {
/// Search query /// Search query
query: String, query: String,
/// Fact types to search (world, agent, opinion) /// Fact types to search (world, bank, opinion)
#[arg(short = 't', long, value_delimiter = ',', default_values = &["world", "agent", "opinion"])] #[arg(short = 't', long, value_delimiter = ',', default_values = &["world", "bank", "opinion"])]
fact_type: Vec<String>, fact_type: Vec<String>,
/// Thinking budget (low, mid, high) /// Thinking budget (low, mid, high)

View file

@ -11,16 +11,41 @@ Example:
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
# Store a memory # Store a memory
client.put(agent_id="alice", content="Alice loves AI") result = client.retain(bank_id="alice", content="Alice loves AI")
print(result.success)
# Search memories # Search memories
results = client.search(agent_id="alice", query="What does Alice like?") results = client.recall(bank_id="alice", query="What does Alice like?")
for r in results:
print(r.text)
# Generate contextual answer # Generate contextual answer
answer = client.think(agent_id="alice", query="What are my interests?") answer = client.reflect(bank_id="alice", query="What are my interests?")
print(answer.text)
``` ```
""" """
from .hindsight_client import Hindsight from .hindsight_client import Hindsight
__all__ = ["Hindsight"] # Re-export response types for convenient access
from hindsight_client_api.models.retain_response import RetainResponse
from hindsight_client_api.models.recall_response import RecallResponse
from hindsight_client_api.models.recall_result import RecallResult
from hindsight_client_api.models.reflect_response import ReflectResponse
from hindsight_client_api.models.reflect_fact import ReflectFact
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.models.personality_traits import PersonalityTraits
__all__ = [
"Hindsight",
# Response types
"RetainResponse",
"RecallResponse",
"RecallResult",
"ReflectResponse",
"ReflectFact",
"ListMemoryUnitsResponse",
"BankProfileResponse",
"PersonalityTraits",
]

View file

@ -17,6 +17,12 @@ from hindsight_client_api.models import (
memory_item, memory_item,
reflect_request, reflect_request,
) )
from hindsight_client_api.models.retain_response import RetainResponse
from hindsight_client_api.models.recall_response import RecallResponse
from hindsight_client_api.models.recall_result import RecallResult
from hindsight_client_api.models.reflect_response import ReflectResponse
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
def _run_async(coro): def _run_async(coro):
@ -86,7 +92,7 @@ class Hindsight:
context: Optional[str] = None, context: Optional[str] = None,
document_id: Optional[str] = None, document_id: Optional[str] = None,
metadata: Optional[Dict[str, str]] = None, metadata: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]: ) -> RetainResponse:
""" """
Store a single memory (simplified interface). Store a single memory (simplified interface).
@ -99,7 +105,7 @@ class Hindsight:
metadata: Optional user-defined metadata metadata: Optional user-defined metadata
Returns: Returns:
Response with success status RetainResponse with success status
""" """
return self.retain_batch( return self.retain_batch(
bank_id=bank_id, bank_id=bank_id,
@ -112,8 +118,8 @@ class Hindsight:
bank_id: str, bank_id: str,
items: List[Dict[str, Any]], items: List[Dict[str, Any]],
document_id: Optional[str] = None, document_id: Optional[str] = None,
async_: bool = False, retain_async: bool = False,
) -> Dict[str, Any]: ) -> RetainResponse:
""" """
Store multiple memories in batch. Store multiple memories in batch.
@ -121,10 +127,10 @@ class Hindsight:
bank_id: The memory bank ID bank_id: The memory bank ID
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata' items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata'
document_id: Optional document ID for grouping memories document_id: Optional document ID for grouping memories
async_: If True, process asynchronously in background (default: False) retain_async: If True, process asynchronously in background (default: False)
Returns: Returns:
Response with success status and item count RetainResponse with success status and item count
""" """
memory_items = [ memory_items = [
memory_item.MemoryItem( memory_item.MemoryItem(
@ -139,11 +145,10 @@ class Hindsight:
request_obj = retain_request.RetainRequest( request_obj = retain_request.RetainRequest(
items=memory_items, items=memory_items,
document_id=document_id, document_id=document_id,
async_=async_, async_=retain_async,
) )
response = _run_async(self._api.retain_memories(bank_id, request_obj)) return _run_async(self._api.retain_memories(bank_id, request_obj))
return response.to_dict() if hasattr(response, 'to_dict') else response
def recall( def recall(
self, self,
@ -152,7 +157,7 @@ class Hindsight:
types: Optional[List[str]] = None, types: Optional[List[str]] = None,
max_tokens: int = 4096, max_tokens: int = 4096,
budget: str = "mid", budget: str = "mid",
) -> List[Dict[str, Any]]: ) -> List[RecallResult]:
""" """
Recall memories using semantic similarity. Recall memories using semantic similarity.
@ -164,7 +169,7 @@ class Hindsight:
budget: Budget level for recall - "low", "mid", or "high" (default: "mid") budget: Budget level for recall - "low", "mid", or "high" (default: "mid")
Returns: Returns:
List of recall results List of RecallResult objects
""" """
request_obj = recall_request.RecallRequest( request_obj = recall_request.RecallRequest(
query=query, query=query,
@ -175,10 +180,7 @@ class Hindsight:
) )
response = _run_async(self._api.recall_memories(bank_id, request_obj)) response = _run_async(self._api.recall_memories(bank_id, request_obj))
return response.results if hasattr(response, 'results') else []
if hasattr(response, 'results'):
return [r.to_dict() if hasattr(r, 'to_dict') else r for r in response.results]
return []
def reflect( def reflect(
self, self,
@ -186,7 +188,7 @@ class Hindsight:
query: str, query: str,
budget: str = "low", budget: str = "low",
context: Optional[str] = None, context: Optional[str] = None,
) -> Dict[str, Any]: ) -> ReflectResponse:
""" """
Generate a contextual answer based on bank identity and memories. Generate a contextual answer based on bank identity and memories.
@ -197,7 +199,7 @@ class Hindsight:
context: Optional additional context context: Optional additional context
Returns: Returns:
Response with answer text and optionally facts used ReflectResponse with answer text and optionally facts used
""" """
request_obj = reflect_request.ReflectRequest( request_obj = reflect_request.ReflectRequest(
query=query, query=query,
@ -205,8 +207,7 @@ class Hindsight:
context=context, context=context,
) )
response = _run_async(self._api.reflect(bank_id, request_obj)) return _run_async(self._api.reflect(bank_id, request_obj))
return response.to_dict() if hasattr(response, 'to_dict') else response
# Full-featured methods (expose more options) # Full-featured methods (expose more options)
@ -221,7 +222,7 @@ class Hindsight:
query_timestamp: Optional[str] = None, query_timestamp: Optional[str] = None,
include_entities: bool = True, include_entities: bool = True,
max_entity_tokens: int = 500, max_entity_tokens: int = 500,
) -> Dict[str, Any]: ) -> RecallResponse:
""" """
Recall memories with all options (full-featured). Recall memories with all options (full-featured).
@ -237,7 +238,7 @@ class Hindsight:
max_entity_tokens: Maximum tokens for entity observations (default: 500) max_entity_tokens: Maximum tokens for entity observations (default: 500)
Returns: Returns:
Full recall response with results, optional entities, and optional trace RecallResponse with results, optional entities, and optional trace
""" """
from hindsight_client_api.models import include_options, entity_include_options from hindsight_client_api.models import include_options, entity_include_options
@ -255,8 +256,7 @@ class Hindsight:
include=include_opts, include=include_opts,
) )
response = _run_async(self._api.recall_memories(bank_id, request_obj)) return _run_async(self._api.recall_memories(bank_id, request_obj))
return response.to_dict() if hasattr(response, 'to_dict') else response
def list_memories( def list_memories(
self, self,
@ -265,16 +265,15 @@ class Hindsight:
search_query: Optional[str] = None, search_query: Optional[str] = None,
limit: int = 100, limit: int = 100,
offset: int = 0, offset: int = 0,
) -> Dict[str, Any]: ) -> ListMemoryUnitsResponse:
"""List memory units with pagination.""" """List memory units with pagination."""
response = _run_async(self._api.list_memories( return _run_async(self._api.list_memories(
bank_id=bank_id, bank_id=bank_id,
type=type, type=type,
q=search_query, q=search_query,
limit=limit, limit=limit,
offset=offset, offset=offset,
)) ))
return response.to_dict() if hasattr(response, 'to_dict') else response
def create_bank( def create_bank(
self, self,
@ -282,7 +281,7 @@ class Hindsight:
name: Optional[str] = None, name: Optional[str] = None,
background: Optional[str] = None, background: Optional[str] = None,
personality: Optional[Dict[str, float]] = None, personality: Optional[Dict[str, float]] = None,
) -> Dict[str, Any]: ) -> BankProfileResponse:
"""Create or update a memory bank.""" """Create or update a memory bank."""
from hindsight_client_api.models import create_bank_request, personality_traits from hindsight_client_api.models import create_bank_request, personality_traits
@ -296,8 +295,7 @@ class Hindsight:
personality=personality_obj, personality=personality_obj,
) )
response = _run_async(self._api.create_or_update_bank(bank_id, request_obj)) return _run_async(self._api.create_or_update_bank(bank_id, request_obj))
return response.to_dict() if hasattr(response, 'to_dict') else response
# Async methods (native async, no _run_async wrapper) # Async methods (native async, no _run_async wrapper)
@ -306,8 +304,8 @@ class Hindsight:
bank_id: str, bank_id: str,
items: List[Dict[str, Any]], items: List[Dict[str, Any]],
document_id: Optional[str] = None, document_id: Optional[str] = None,
async_: bool = False, retain_async: bool = False,
) -> Dict[str, Any]: ) -> RetainResponse:
""" """
Store multiple memories in batch (async). Store multiple memories in batch (async).
@ -315,10 +313,10 @@ class Hindsight:
bank_id: The memory bank ID bank_id: The memory bank ID
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata' items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata'
document_id: Optional document ID for grouping memories document_id: Optional document ID for grouping memories
async_: If True, process asynchronously in background (default: False) retain_async: If True, process asynchronously in background (default: False)
Returns: Returns:
Response with success status and item count RetainResponse with success status and item count
""" """
memory_items = [ memory_items = [
memory_item.MemoryItem( memory_item.MemoryItem(
@ -333,11 +331,10 @@ class Hindsight:
request_obj = retain_request.RetainRequest( request_obj = retain_request.RetainRequest(
items=memory_items, items=memory_items,
document_id=document_id, document_id=document_id,
async_=async_, async_=retain_async,
) )
response = await self._api.retain_memories(bank_id, request_obj) return await self._api.retain_memories(bank_id, request_obj)
return response.to_dict() if hasattr(response, 'to_dict') else response
async def aretain( async def aretain(
self, self,
@ -347,7 +344,7 @@ class Hindsight:
context: Optional[str] = None, context: Optional[str] = None,
document_id: Optional[str] = None, document_id: Optional[str] = None,
metadata: Optional[Dict[str, str]] = None, metadata: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]: ) -> RetainResponse:
""" """
Store a single memory (async). Store a single memory (async).
@ -360,7 +357,7 @@ class Hindsight:
metadata: Optional user-defined metadata metadata: Optional user-defined metadata
Returns: Returns:
Response with success status RetainResponse with success status
""" """
return await self.aretain_batch( return await self.aretain_batch(
bank_id=bank_id, bank_id=bank_id,
@ -375,7 +372,7 @@ class Hindsight:
types: Optional[List[str]] = None, types: Optional[List[str]] = None,
max_tokens: int = 4096, max_tokens: int = 4096,
budget: str = "mid", budget: str = "mid",
) -> List[Dict[str, Any]]: ) -> List[RecallResult]:
""" """
Recall memories using semantic similarity (async). Recall memories using semantic similarity (async).
@ -387,7 +384,7 @@ class Hindsight:
budget: Budget level for recall - "low", "mid", or "high" (default: "mid") budget: Budget level for recall - "low", "mid", or "high" (default: "mid")
Returns: Returns:
List of recall results List of RecallResult objects
""" """
request_obj = recall_request.RecallRequest( request_obj = recall_request.RecallRequest(
query=query, query=query,
@ -398,10 +395,7 @@ class Hindsight:
) )
response = await self._api.recall_memories(bank_id, request_obj) response = await self._api.recall_memories(bank_id, request_obj)
return response.results if hasattr(response, 'results') else []
if hasattr(response, 'results'):
return [r.to_dict() if hasattr(r, 'to_dict') else r for r in response.results]
return []
async def areflect( async def areflect(
self, self,
@ -409,7 +403,7 @@ class Hindsight:
query: str, query: str,
budget: str = "low", budget: str = "low",
context: Optional[str] = None, context: Optional[str] = None,
) -> Dict[str, Any]: ) -> ReflectResponse:
""" """
Generate a contextual answer based on bank identity and memories (async). Generate a contextual answer based on bank identity and memories (async).
@ -420,7 +414,7 @@ class Hindsight:
context: Optional additional context context: Optional additional context
Returns: Returns:
Response with answer text and optionally facts used ReflectResponse with answer text and optionally facts used
""" """
request_obj = reflect_request.ReflectRequest( request_obj = reflect_request.ReflectRequest(
query=query, query=query,
@ -428,5 +422,4 @@ class Hindsight:
context=context, context=context,
) )
response = await self._api.reflect(bank_id, request_obj) return await self._api.reflect(bank_id, request_obj)
return response.to_dict() if hasattr(response, 'to_dict') else response

View file

@ -1,11 +1,11 @@
{ {
"name": "@hindsight/client", "name": "@vectorize-io/hindsight-client",
"version": "0.0.7", "version": "0.0.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@hindsight/client", "name": "@vectorize-io/hindsight-client",
"version": "0.0.7", "version": "0.0.7",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View file

@ -3,7 +3,7 @@
* *
* Example: * Example:
* ```typescript * ```typescript
* import { HindsightClient } from '@hindsight/client'; * import { HindsightClient } from '@vectorize-io/hindsight-client';
* *
* const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); * const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
* *
@ -64,7 +64,7 @@ export class HindsightClient {
async retain( async retain(
bankId: string, bankId: string,
content: string, content: string,
options?: { timestamp?: Date | string; context?: string; metadata?: Record<string, string> } options?: { timestamp?: Date | string; context?: string; metadata?: Record<string, string>; async?: boolean }
): Promise<RetainResponse> { ): Promise<RetainResponse> {
const item: { content: string; timestamp?: string; context?: string; metadata?: Record<string, string> } = { content }; const item: { content: string; timestamp?: string; context?: string; metadata?: Record<string, string> } = { content };
if (options?.timestamp) { if (options?.timestamp) {
@ -83,7 +83,7 @@ export class HindsightClient {
const response = await sdk.retainMemories({ const response = await sdk.retainMemories({
client: this.client, client: this.client,
path: { bank_id: bankId }, path: { bank_id: bankId },
body: { items: [item] }, body: { items: [item], async: options?.async },
}); });
return response.data!; return response.data!;
@ -124,53 +124,25 @@ export class HindsightClient {
/** /**
* Recall memories with a natural language query. * Recall memories with a natural language query.
* Returns a simplified list of recall results.
*/ */
async recall( async recall(
bankId: string, bankId: string,
query: string, query: string,
options?: { maxTokens?: number; budget?: Budget } options?: { types?: string[]; maxTokens?: number; budget?: Budget; trace?: boolean }
): Promise<RecallResult[]> {
const response = await sdk.recallMemories({
client: this.client,
path: { bank_id: bankId },
body: {
query,
max_tokens: options?.maxTokens,
budget: options?.budget || 'mid',
},
});
return response.data?.results ?? [];
}
/**
* Recall memories with full options and response.
*/
async recallMemories(
bankId: string,
options: {
query: string;
types?: string[];
maxTokens?: number;
trace?: boolean;
budget?: Budget;
}
): Promise<RecallResponse> { ): Promise<RecallResponse> {
const response = await sdk.recallMemories({ const response = await sdk.recallMemories({
client: this.client, client: this.client,
path: { bank_id: bankId }, path: { bank_id: bankId },
body: { body: {
query: options.query, query,
types: options.types, types: options?.types,
max_tokens: options.maxTokens, max_tokens: options?.maxTokens,
trace: options.trace, budget: options?.budget || 'mid',
budget: options.budget || 'mid', trace: options?.trace,
}, },
}); });
if (!response.data) { if (!response.data) {
console.error('recallMemories: No data in response', { response, error: response.error });
throw new Error(`API returned no data: ${JSON.stringify(response.error || 'Unknown error')}`); throw new Error(`API returned no data: ${JSON.stringify(response.error || 'Unknown error')}`);
} }

View file

@ -175,17 +175,18 @@ class LongMemEvalAnswerGenerator(LLMAnswerGenerator):
**How to Answer:** **How to Answer:**
1. Start by scanning retrieved context to understand the facts and events that happened and the timeline. 1. Start by scanning retrieved context to understand the facts and events that happened and the timeline.
2. Reason about all the memories and find the right answer, considering the most recent memory as an update of the current facts. 2. Reason about all the memories and find the right answer, considering the most recent memory as an update of the current facts.
3. If you have 2 possible answers, just say both.
In general the answer must be comprehensive and plenty of details from the retrieved context. In general the answer must be comprehensive and plenty of details from the retrieved context.
For quantitative questions, use numbers and units. Example: 'How many..', just answer the number and which ones. Consider EACH item even if it's not the most recent one. Reason and do calculation for complex questions. For quantitative questions, use numbers and units. Example: 'How many..', just answer the number and which ones. Consider EACH item even if it's not the most recent one. Reason and do calculation for complex questions.
If questions asks a location (where...?) make sure to include the location name. If questions asks a location (where...?) make sure to include the location name.
For recommendations/suggestions, use the retrieved context to understand the user's preferences and provide a possible answer based on those. Include the reasoning and explicitly say what the user prefers, before making suggestions (user previous experiences or specific requests FROM the user). Consider as much user preferences as possible in your answer. For recommendations/suggestions, use the retrieved context to understand the user's preferences and user's personal experiences, and provide a possible answer based on those. Include the reasoning and explicitly say what the user prefers, before making suggestions (user previous experiences or specific requests FROM the user). Consider as much user preferences as possible in your answer.
For questions asking for help or instructions, consider the users' latest purchases and previous interactions with the assistant to understand which details to focus your answer on (include these references in your answer). For questions asking for help or instructions, consider the users' recent memories and previous interactions with the assistant to understand their current situation better (recent purchases, specific product models used..)
For specific number/value questions, use the context to understand what is the most up-to-date number based on recency, but also include the reasoning (in the answer) on previous possible values and why you think are less relevant. For specific number/value questions, use the context to understand what is the most up-to-date number based on recency, but also include the reasoning (in the answer) on previous possible values and why you think are less relevant.
For open questions, include as much details as possible from different sources that are relevant. For open questions, include as much details as possible from different sources that are relevant.
For questions where a specific entity/role is mentioned and it's different from your memory, just say the truth, don't make up anything just to fulfill the question. For example, if the question is about a specific sport, you should consider if the memories and the question are about the same sport. (e.g. american football vs soccer) For questions where a specific entity/role is mentioned and it's different from your memory, just say the truth, don't make up anything just to fulfill the question. For example, if the question is about a specific sport, you should consider if the memories and the question are about the same sport. (e.g. american football vs soccer, shows vs podcasts)
For comparative questions , say you don't know the answer if you don't have information about both sides. (or more sides) For comparative questions, say you don't know the answer if you don't have information about both sides. (or more sides)
For questions related to time/date, carefully review the question date and the memories date to correctly answer the question. For questions related to time/date, carefully review the question date and the memories date to correctly answer the question.
For questions related to time/date calculation (e.g. How many days passed between X and Y?), carefully review the memories date to correctly answer the question and only provide an answer if you have information about both X and Y, otherwise say it's not possible to calculate and why. For questions related to time/date calculation (e.g. How many days passed between X and Y?), carefully review the memories date to correctly answer the question and only provide an answer if you have information about both X and Y, otherwise say it's not possible to calculate and why.

View file

@ -10,7 +10,7 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites :::tip Prerequisites
Make sure you've [installed Hindsight](./installation) and understand [how retain works](./retain). Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
::: :::
## What Are Documents? ## What Are Documents?
@ -22,6 +22,18 @@ Documents are containers for retained content. They help you:
- **Delete in bulk** — Remove all memories from a document at once - **Delete in bulk** — Remove all memories from a document at once
- **Organize memories** — Group related facts by source - **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 ## Retain with Document ID
Associate retained content with a document: Associate retained content with a document:
@ -65,7 +77,7 @@ with open("notes.txt") as f:
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
@ -183,7 +195,7 @@ response = api.list_documents(
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { sdk, createClient, createConfig } from '@hindsight/client'; import { sdk, createClient, createConfig } from '@vectorize-io/hindsight-client';
const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' })); const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));

View file

@ -10,7 +10,19 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites :::tip Prerequisites
Make sure you've [installed Hindsight](./installation) and understand [how retain works](./retain). Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
:::
## 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.
:::tip Include Entities in Recall
Use `include_entities=True` in your recall calls to get entity observations alongside fact results. See [Recall](./recall) for details.
::: :::
## What Are Entities? ## What Are Entities?
@ -35,7 +47,7 @@ client.retain(
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
@ -93,7 +105,7 @@ response = api.list_entities(
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { sdk, createClient, createConfig } from '@hindsight/client'; import { sdk, createClient, createConfig } from '@vectorize-io/hindsight-client';
const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' })); const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));

View file

@ -1,123 +0,0 @@
---
sidebar_position: 0
---
# Installation
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
## Choose Your Setup
<Tabs>
<TabItem value="python" label="Python">
### All-in-One (Recommended)
The `hindsight-all` package includes everything: embedded PostgreSQL, HTTP API server, and Python client.
```bash
pip install hindsight-all
```
**Use when:** You want the simplest setup for development or small deployments.
### Client Only
If you already have a Hindsight server running:
```bash
pip install hindsight-client
```
**Use when:** You're connecting to an existing Hindsight server (development, staging, or production).
</TabItem>
<TabItem value="node" label="Node.js">
```bash
npm install @hindsight/client
```
**Requires:** A running Hindsight server (see [Server Deployment](/developer/installation) for setup).
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Coming soon
```
The CLI is included with the Rust distribution. See [CLI documentation](/sdks/cli) for installation.
</TabItem>
</Tabs>
## LLM Provider Setup
Hindsight requires an LLM that supports **structured output** for fact extraction and reasoning. Configure your provider:
<Tabs>
<TabItem value="openai" label="OpenAI">
```bash
export OPENAI_API_KEY=sk-...
```
**Requirement:** Model must support structured output (JSON mode)
</TabItem>
<TabItem value="groq" label="Groq">
```bash
export GROQ_API_KEY=gsk_...
```
**Requirement:** Model must support structured output (JSON mode)
</TabItem>
<TabItem value="ollama" label="Ollama (Local)">
```bash
# No API key needed - runs locally
```
**Requirement:** Model must support structured output (JSON mode)
See [Ollama documentation](https://ollama.ai) for setup.
</TabItem>
</Tabs>
## Verify Installation
<Tabs>
<TabItem value="python" label="Python">
```python
import hindsight
print(hindsight.__version__)
```
</TabItem>
<TabItem value="node" label="Node.js">
```javascript
const { HindsightClient } = require('@hindsight/client');
console.log('Hindsight client loaded');
```
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight --version
```
</TabItem>
</Tabs>
## Next Steps
- [**Quick Start**](./quickstart) — Get running in 60 seconds
- [**Server Deployment**](/developer/installation) — Production setup options

View file

@ -2,13 +2,21 @@
sidebar_position: 6 sidebar_position: 6
--- ---
# Memory Bank Identity # Memory Bank
Configure memory bank personality, background, and behavior. Configure memory bank personality, background, and behavior.
Memory banks have charateristics:
- Banks are completely isolated from each other.
- You don't need to pre-create it, Hindsight will create it for you with default settings.
- Banks have a profile that influences how they form opinions from memories. (optional)
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Creating a Memory Bank ## Creating a Memory Bank
<Tabs> <Tabs>
@ -38,7 +46,7 @@ client.create_bank(
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });

View file

@ -10,18 +10,18 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites :::tip Prerequisites
Make sure you've [installed Hindsight](./installation) and understand [how retain works](./retain). Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
::: :::
## What Are Operations? ## What Are Operations?
Some Hindsight tasks run asynchronously in the background: When you call `retain_batch` with `async=True`, Hindsight processes the content in the background and returns immediately with an operation ID. Operations let you track and manage these async retain tasks.
- **Batch retain** — Processing large document sets By default, async operations are executed in-process within the API service. This is managed automatically — you don't need to configure anything.
- **Entity observations** — Synthesizing entity summaries
- **Graph updates** — Building connections between memories
Operations provide a way to track these background tasks. :::tip Scaling with Streaming
For high-throughput workloads, you can extend the task backend to use a streaming platform like Kafka. This enables scale-out processing across multiple workers and handles backpressure on the API.
:::
## Async Batch Retain ## Async Batch Retain
@ -35,46 +35,35 @@ from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
# Start async batch retain client.retain_batch(
result = client.retain_batch(
bank_id="my-bank", bank_id="my-bank",
items=[ items=[
{"content": doc1_text}, {"content": doc1_text},
{"content": doc2_text}, {"content": doc2_text},
# ... hundreds or thousands of documents
], ],
async_=True # Enable async mode retain_async=True
) )
print(f"Operation ID: {result.get('operation_id')}")
``` ```
</TabItem> </TabItem>
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Start async batch retain await client.retainBatch('my-bank', [
const result = await client.retainBatch('my-bank', [
{ content: doc1Text }, { content: doc1Text },
{ content: doc2Text }, { content: doc2Text },
// ... hundreds or thousands of documents
], { async: true }); ], { async: true });
console.log(`Operation ID: ${result.operation_id}`);
``` ```
</TabItem> </TabItem>
<TabItem value="cli" label="CLI"> <TabItem value="cli" label="CLI">
```bash ```bash
# Start async batch retain
hindsight retain my-bank --files docs/*.md --async hindsight retain my-bank --files docs/*.md --async
# Returns operation ID: op-abc123...
``` ```
</TabItem> </TabItem>
@ -110,7 +99,7 @@ for op in response.items:
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { sdk, createClient, createConfig } from '@hindsight/client'; import { sdk, createClient, createConfig } from '@vectorize-io/hindsight-client';
const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' })); const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));
@ -217,29 +206,6 @@ hindsight operations cancel my-bank --all-pending
| **failed** | Encountered an error | | **failed** | Encountered an error |
| **cancelled** | Stopped by user | | **cancelled** | Stopped by user |
## Operation Types
| Type | Description |
|------|-------------|
| **batch_retain** | Async batch content ingestion |
| **regenerate_observations** | Entity observation synthesis |
| **graph_update** | Link and connection building |
## Operation Response Format
```json
{
"id": "op-abc123",
"bank_id": "my-bank",
"task_type": "batch_retain",
"status": "completed",
"items_count": 1000,
"document_id": "batch-001",
"created_at": "2024-03-15T10:00:00Z",
"error_message": null
}
```
## Monitoring Strategies ## Monitoring Strategies
### Polling ### Polling

View file

@ -9,6 +9,10 @@ How memory banks form, store, and evolve beliefs.
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## What Are Opinions? ## What Are Opinions?
Opinions are beliefs formed by the memory bank based on evidence and personality. Unlike world facts (objective information received) or agent facts (actions taken), opinions are **judgments** with confidence scores. Opinions are beliefs formed by the memory bank based on evidence and personality. Unlike world facts (objective information received) or agent facts (actions taken), opinions are **judgments** with confidence scores.

View file

@ -1,5 +1,5 @@
--- ---
sidebar_position: 1 sidebar_position: 0
--- ---
# Quick Start # Quick Start
@ -9,45 +9,52 @@ Get up and running with Hindsight in 60 seconds.
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites ## Start the Server
Make sure you've [installed Hindsight](./installation) and configured your LLM provider.
<Tabs>
<TabItem value="pip" label="pip (API only)">
```bash
pip install hindsight-all
export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
hindsight-api
```
API available at http://localhost:8888
</TabItem>
<TabItem value="docker" label="Docker (Full Experience)">
```bash
docker run -p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_PROVIDER=groq \
-e HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx \
ghcr.io/vectorize-io/hindsight
```
- **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. Also supports OpenAI and Ollama.
::: :::
## Basic Usage ---
## Use the Client
<Tabs> <Tabs>
<TabItem value="python" label="Python"> <TabItem value="python" label="Python">
### With All-in-One Package ```bash
pip install hindsight-client
```python
import os
from hindsight import HindsightServer, HindsightClient
# Start embedded server (PostgreSQL + HTTP API)
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: Store information
client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer")
client.retain(bank_id="my-bank", content="Bob prefers Python over JavaScript")
# Recall: Search memories
results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results:
print(r["text"])
# Reflect: Generate personality-aware response
response = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(response["text"])
``` ```
### With Client Only
```python ```python
from hindsight_client import Hindsight from hindsight_client import Hindsight
@ -57,65 +64,66 @@ client = Hindsight(base_url="http://localhost:8888")
client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer") client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer")
# Recall: Search memories # Recall: Search memories
results = client.recall(bank_id="my-bank", query="What does Alice do?") client.recall(bank_id="my-bank", query="What does Alice do?")
# Reflect: Generate response # Reflect: Generate personality-aware response
response = client.reflect(bank_id="my-bank", query="Tell me about Alice") client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(response["text"])
``` ```
</TabItem> </TabItem>
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```bash
npm install @vectorize-io/hindsight-client
```
```javascript ```javascript
const { HindsightClient } = require('@hindsight/client'); const { HindsightClient } = require('@vectorize-io/hindsight-client');
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Retain: Store information // Retain: Store information
await client.retain({ await client.retain('my-bank', 'Alice works at Google as a software engineer');
bankId: 'my-bank',
content: 'Alice works at Google as a software engineer'
});
// Recall: Search memories // Recall: Search memories
const results = await client.recall({ await client.recall('my-bank', 'What does Alice do?');
bankId: 'my-bank',
query: 'What does Alice do?'
});
// Reflect: Generate response // Reflect: Generate response
const response = await client.reflect({ await client.reflect('my-bank', 'Tell me about Alice');
bankId: 'my-bank',
query: 'Tell me about Alice'
});
console.log(response.text);
``` ```
</TabItem> </TabItem>
<TabItem value="cli" label="CLI"> <TabItem value="cli" label="CLI">
```bash
curl -fsSL https://raw.githubusercontent.com/vectorize-io/hindsight/refs/heads/main/hindsight-cli/install.sh | bash
```
```bash ```bash
# Retain: Store information # Retain: Store information
hindsight retain my-bank "Alice works at Google as a software engineer" hindsight memory retain my-bank "Alice works at Google as a software engineer"
# Recall: Search memories # Recall: Search memories
hindsight recall my-bank "What does Alice do?" hindsight memory recall my-bank "What does Alice do?"
# Reflect: Generate response # Reflect: Generate response
hindsight reflect my-bank "Tell me about Alice" hindsight memory reflect my-bank "Tell me about Alice"
``` ```
</TabItem> </TabItem>
</Tabs> </Tabs>
---
## What's Happening ## What's Happening
**Retain** → Content is processed, facts are extracted, entities are identified and linked in a knowledge graph | 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 personality-aware response |
**Recall** → Four search strategies (semantic, keyword, graph, temporal) run in parallel to find relevant memories ---
**Reflect** → Retrieved memories are used to generate a personality-aware response with formed opinions
## Next Steps ## Next Steps
@ -123,4 +131,4 @@ hindsight reflect my-bank "Tell me about Alice"
- [**Recall**](./recall) — Search and retrieval strategies - [**Recall**](./recall) — Search and retrieval strategies
- [**Reflect**](./reflect) — Personality-aware reasoning - [**Reflect**](./reflect) — Personality-aware reasoning
- [**Memory Banks**](./memory-banks) — Configure personality and background - [**Memory Banks**](./memory-banks) — Configure personality and background
- [**Server Options**](/developer/installation) — Production deployment - [**Server Deployment**](/developer/installation) — Docker Compose, Helm, and production setup

View file

@ -9,6 +9,10 @@ Retrieve memories using multi-strategy search.
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Search ## Basic Search
<Tabs> <Tabs>
@ -19,28 +23,18 @@ from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
results = client.recall( client.recall(bank_id="my-bank", query="What does Alice do?")
bank_id="my-bank",
query="What does Alice do?"
)
for r in results:
print(f"{r['text']} (score: {r['weight']:.2f})")
``` ```
</TabItem> </TabItem>
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
const results = await client.recall('my-bank', 'What does Alice do?'); await client.recall('my-bank', 'What does Alice do?');
for (const r of results) {
console.log(`${r.text} (score: ${r.weight})`);
}
``` ```
</TabItem> </TabItem>
@ -223,66 +217,60 @@ hindsight memory search my-bank "Alice" --fact-type world,agent
</TabItem> </TabItem>
</Tabs> </Tabs>
## How Search Works :::info How Recall Works
Learn about the four search strategies (semantic, keyword, graph, temporal) and RRF fusion in the [Recall Architecture](/developer/retrieval) guide.
:::
Search runs four strategies in parallel: ## Token Budget Management
```mermaid 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.
graph LR
Q[Query] --> S[Semantic<br/>Vector similarity]
Q --> K[Keyword<br/>BM25 exact match]
Q --> G[Graph<br/>Entity traversal]
Q --> T[Temporal<br/>Time-filtered]
S --> RRF[RRF Fusion] The `max_tokens` parameter lets you control how much of your agent's context budget to spend on memories:
K --> RRF
G --> RRF
T --> RRF
RRF --> CE[Cross-Encoder<br/>Rerank]
CE --> R[Results]
```
| Strategy | When it helps |
|----------|---------------|
| **Semantic** | Conceptual matches, paraphrasing |
| **Keyword** | Names, technical terms, exact phrases |
| **Graph** | Related entities, indirect connections |
| **Temporal** | "last spring", "in June", time ranges |
## Response Format
```python ```python
{ # Fill up to 4K tokens of context with relevant memories
"results": [ results = client.recall(bank_id="my-bank", query="What do I know about Alice?", max_tokens=4096)
{
"id": "550e8400-e29b-41d4-a716-446655440000", # Smaller budget for quick lookups
"text": "Alice works at Google as a software engineer", results = client.recall(bank_id="my-bank", query="Alice's email", max_tokens=500)
"context": "career discussion",
"event_date": "2024-01-15T10:00:00Z",
"weight": 0.95,
"fact_type": "world"
}
]
}
``` ```
| Field | Description | 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.
|-------|-------------|
| `id` | Unique memory ID | ### Additional Context: Chunks and Entity Observations
| `text` | Memory content |
| `context` | Original context (if provided) | For the most relevant memories, you can optionally retrieve additional context—each with its own token budget:
| `event_date` | When the event occurred |
| `weight` | Relevance score (0-1) | | Option | Parameter | Description |
| `fact_type` | `world`, `agent`, or `opinion` | |--------|-----------|-------------|
| **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 |
```python
response = client.recall_memories(
bank_id="my-bank",
query="What does Alice do?",
max_tokens=4096, # Budget for memories
include_chunks=True,
max_chunk_tokens=2000, # Budget for raw chunks
include_entities=True,
max_entity_tokens=1000 # Budget for entity observations
)
# Access the additional context
chunks = response.get("chunks", {})
entities = response.get("entities", [])
```
This gives your agent richer context while maintaining precise control over total token consumption.
## Budget Levels ## Budget Levels
The `budget` parameter controls graph traversal depth: The `budget` parameter controls graph traversal depth:
- **"low" (100 nodes)**: Fast, shallow search — good for simple lookups - **"low"**: Fast, shallow search — good for simple lookups
- **"mid" (300 nodes)**: Balanced — default for most queries - **"mid"**: Balanced — default for most queries
- **"high" (600 nodes)**: Deep exploration — finds indirect connections - **"high"**: Deep exploration — finds indirect connections
<Tabs> <Tabs>
<TabItem value="python" label="Python"> <TabItem value="python" label="Python">

View file

@ -9,6 +9,10 @@ Generate personality-aware responses using retrieved memories.
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
:::
## Basic Usage ## Basic Usage
<Tabs> <Tabs>
@ -19,25 +23,18 @@ from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
response = client.reflect( client.reflect(bank_id="my-bank", query="What should I know about Alice?")
bank_id="my-bank",
query="What should I know about Alice?"
)
print(response["answer"])
``` ```
</TabItem> </TabItem>
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
const response = await client.reflect('my-bank', 'What should I know about Alice?'); await client.reflect('my-bank', 'What should I know about Alice?');
console.log(response.answer);
``` ```
</TabItem> </TabItem>
@ -45,35 +42,11 @@ console.log(response.answer);
```bash ```bash
hindsight memory think my-bank "What should I know about Alice?" hindsight memory think my-bank "What should I know about Alice?"
# Verbose output shows reasoning and sources
hindsight memory think my-bank "What should I know about Alice?" -v
``` ```
</TabItem> </TabItem>
</Tabs> </Tabs>
## Response Format
```python
{
"answer": "Alice is a software engineer at Google who joined last year...",
"facts_used": [
{"text": "Alice works at Google", "weight": 0.95, "id": "..."},
{"text": "Alice is very competent", "weight": 0.82, "id": "..."}
],
"new_opinions": [
{"text": "Alice would be good for the ML project", "id": "..."}
]
}
```
| Field | Description |
|-------|-------------|
| `answer` | Generated response |
| `facts_used` | Memories used in generation |
| `new_opinions` | New opinions formed during reasoning |
## Parameters ## Parameters
| Parameter | Type | Default | Description | | Parameter | Type | Default | Description |
@ -107,30 +80,9 @@ const response = await client.reflect('my-bank', 'What do you think about remote
</TabItem> </TabItem>
</Tabs> </Tabs>
## What Reflect Does :::info How Reflect Works
Learn about personality-driven reasoning and opinion formation in the [Reflect Architecture](/developer/personality) guide.
```mermaid :::
sequenceDiagram
participant C as Client
participant A as Hindsight API
participant M as Memory Store
participant L as LLM
C->>A: reflect("What about Alice?")
A->>M: Search all networks
M-->>A: World + Bank + Opinion facts
A->>A: Load bank personality
A->>L: Generate with personality context
L-->>A: Response + new opinions
A->>M: Store new opinions
A-->>C: Response + sources + new opinions
```
1. **Retrieves** relevant memories from all three networks
2. **Loads** bank personality (Big Five traits + background)
3. **Generates** response influenced by personality
4. **Forms opinions** if the query warrants it
5. **Returns** response with sources and any new opinions
## Opinion Formation ## Opinion Formation

View file

@ -9,31 +9,9 @@ Store memories, conversations, and documents into Hindsight.
import Tabs from '@theme/Tabs'; import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem'; import TabItem from '@theme/TabItem';
## Installation :::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) to install the client and start the server.
<Tabs> :::
<TabItem value="python" label="Python">
```bash
pip install hindsight-client
```
</TabItem>
<TabItem value="node" label="Node.js">
```bash
npm install @hindsight/client
```
</TabItem>
<TabItem value="cli" label="CLI">
```bash
cd hindsight-cli && cargo build --release
```
</TabItem>
</Tabs>
## Store a Single Memory ## Store a Single Memory
@ -55,7 +33,7 @@ client.retain(
<TabItem value="node" label="Node.js"> <TabItem value="node" label="Node.js">
```typescript ```typescript
import { HindsightClient } from '@hindsight/client'; import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
@ -166,25 +144,9 @@ hindsight memory put-files my-bank report.pdf --document-id "q4-report"
</TabItem> </TabItem>
</Tabs> </Tabs>
## What Happens During Ingestion :::info How Retain Works
Learn about fact extraction, entity resolution, and graph construction in the [Retain Architecture](/developer/retain) guide.
When you store content, Hindsight: :::
1. **Extracts facts** using an LLM — converts raw text into structured narrative facts
2. **Identifies entities** — people, places, organizations, concepts
3. **Resolves entities** — "Alice" and "Alice Chen" become the same entity
4. **Builds graph links** — connects memories through shared entities
5. **Generates embeddings** — 384-dim vectors for semantic search
6. **Stores to PostgreSQL** — with vector and full-text indexes
```mermaid
graph LR
A[Raw Content] --> B[LLM Extraction]
B --> C[Entity Resolution]
C --> D[Graph Construction]
D --> E[Embedding]
E --> F[(PostgreSQL)]
```
## Async Ingestion ## Async Ingestion

View file

@ -14,12 +14,10 @@ Configure the LLM provider used for fact extraction, entity resolution, and reas
| Variable | Description | Default | Required | | Variable | Description | Default | Required |
|----------|-------------|---------|----------| |----------|-------------|---------|----------|
| `HINDSIGHT_API_LLM_PROVIDER` | LLM provider: `openai`, `groq`, `ollama`, `anthropic` | `groq` | Yes | | `HINDSIGHT_API_LLM_PROVIDER` | LLM provider: `groq`, `openai`, `ollama` | `groq` | Yes |
| `HINDSIGHT_API_LLM_API_KEY` | API key for LLM provider | - | Yes (except ollama) | | `HINDSIGHT_API_LLM_API_KEY` | API key for LLM provider | - | Yes (except ollama) |
| `HINDSIGHT_API_LLM_MODEL` | Model name | Provider-specific | No | | `HINDSIGHT_API_LLM_MODEL` | Model name | Provider-specific | No |
| `HINDSIGHT_API_LLM_BASE_URL` | Custom LLM endpoint | Provider default | No | | `HINDSIGHT_API_LLM_BASE_URL` | Custom LLM endpoint | Provider default | No |
| `HINDSIGHT_API_LLM_MAX_RETRIES` | Maximum retry attempts for LLM calls | `3` | No |
| `HINDSIGHT_API_LLM_TIMEOUT` | Request timeout in seconds | `30` | No |
#### Provider-Specific Examples #### Provider-Specific Examples
@ -28,7 +26,7 @@ Configure the LLM provider used for fact extraction, entity resolution, and reas
```bash ```bash
export HINDSIGHT_API_LLM_PROVIDER=groq export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
``` ```
**OpenAI** **OpenAI**
@ -39,14 +37,6 @@ export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=gpt-4o export HINDSIGHT_API_LLM_MODEL=gpt-4o
``` ```
**Anthropic Claude**
```bash
export HINDSIGHT_API_LLM_PROVIDER=anthropic
export HINDSIGHT_API_LLM_API_KEY=sk-ant-xxxxxxxxxxxx
export HINDSIGHT_API_LLM_MODEL=claude-3-5-sonnet-20241022
```
**Ollama (Local, No API Key)** **Ollama (Local, No API Key)**
```bash ```bash
@ -71,86 +61,8 @@ Configure the PostgreSQL database connection and behavior.
| Variable | Description | Default | Required | | Variable | Description | Default | Required |
|----------|-------------|---------|----------| |----------|-------------|---------|----------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | - | Yes* | | `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | - | Yes* |
| `HINDSIGHT_API_DB_POOL_SIZE` | Connection pool size | `20` | No |
| `HINDSIGHT_API_DB_MAX_OVERFLOW` | Max overflow connections | `10` | No |
| `HINDSIGHT_API_DB_POOL_TIMEOUT` | Pool checkout timeout (seconds) | `30` | No |
| `HINDSIGHT_API_DB_POOL_RECYCLE` | Connection recycle time (seconds) | `3600` | No |
**\*Note**: If `DATABASE_URL` is not provided and running via `pip install hindsight-all`, the server will use embedded `pg0` (PostgreSQL in a single file). **\*Note**: If `DATABASE_URL` is not provided, the server will use embedded `pg0` (embedded PostGRE).
#### Connection String Format
```bash
# Standard PostgreSQL URL format
postgresql://username:password@hostname:port/database
# With SSL
postgresql://user:pass@host:5432/db?sslmode=require
# With connection pool settings
postgresql://user:pass@host:5432/db?pool_size=20&max_overflow=10
```
#### Examples
**Docker Compose Default**
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@postgres:5432/hindsight
```
**AWS RDS**
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://admin:password@hindsight.xxxx.us-east-1.rds.amazonaws.com:5432/hindsight?sslmode=require
```
**Supabase**
```bash
export HINDSIGHT_API_DATABASE_URL=postgresql://postgres:password@db.xxxxxxxxxxxx.supabase.co:5432/postgres
```
**Embedded pg0 (Default for pip install)**
```bash
# No DATABASE_URL needed - automatically uses pg0
# Data stored in: ~/.hindsight/data/
```
### Server Configuration
Configure the HTTP server behavior.
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `HINDSIGHT_API_HOST` | Server bind address | `0.0.0.0` | No |
| `HINDSIGHT_API_PORT` | Server port | `8888` | No |
| `HINDSIGHT_API_WORKERS` | Number of worker processes | `1` | No |
| `HINDSIGHT_API_RELOAD` | Enable auto-reload (dev mode) | `false` | No |
| `HINDSIGHT_API_LOG_LEVEL` | Logging level: `debug`, `info`, `warning`, `error` | `info` | No |
| `HINDSIGHT_API_CORS_ORIGINS` | Allowed CORS origins (comma-separated) | `*` | No |
#### Examples
**Production Server**
```bash
export HINDSIGHT_API_HOST=0.0.0.0
export HINDSIGHT_API_PORT=8888
export HINDSIGHT_API_WORKERS=4
export HINDSIGHT_API_LOG_LEVEL=warning
export HINDSIGHT_API_CORS_ORIGINS="https://app.example.com,https://admin.example.com"
```
**Development Server**
```bash
export HINDSIGHT_API_HOST=127.0.0.1
export HINDSIGHT_API_PORT=8888
export HINDSIGHT_API_RELOAD=true
export HINDSIGHT_API_LOG_LEVEL=debug
```
### MCP Server Configuration ### MCP Server Configuration
@ -159,7 +71,6 @@ Configure the Model Context Protocol (MCP) server for AI assistant integrations.
| Variable | Description | Default | Required | | Variable | Description | Default | Required |
|----------|-------------|---------|----------| |----------|-------------|---------|----------|
| `HINDSIGHT_API_MCP_ENABLED` | Enable MCP server | `true` | No | | `HINDSIGHT_API_MCP_ENABLED` | Enable MCP server | `true` | No |
| `HINDSIGHT_API_MCP_TRANSPORT` | Transport: `stdio`, `sse` | `stdio` | No |
```bash ```bash
# Enable MCP server (default) # Enable MCP server (default)
@ -169,273 +80,19 @@ export HINDSIGHT_API_MCP_ENABLED=true
export HINDSIGHT_API_MCP_ENABLED=false export HINDSIGHT_API_MCP_ENABLED=false
``` ```
### Search and Retrieval Configuration
Configure search behavior and performance.
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `HINDSIGHT_API_DEFAULT_THINKING_BUDGET` | Default thinking budget (tokens) | `500` | No |
| `HINDSIGHT_API_MAX_SEARCH_RESULTS` | Maximum search results to return | `100` | No |
| `HINDSIGHT_API_RERANK_ENABLED` | Enable cross-encoder reranking | `true` | No |
| `HINDSIGHT_API_RERANK_TOP_K` | Number of results to rerank | `50` | No |
```bash
# High-performance search
export HINDSIGHT_API_DEFAULT_THINKING_BUDGET=1000
export HINDSIGHT_API_MAX_SEARCH_RESULTS=200
export HINDSIGHT_API_RERANK_ENABLED=true
export HINDSIGHT_API_RERANK_TOP_K=100
# Fast, resource-efficient search
export HINDSIGHT_API_DEFAULT_THINKING_BUDGET=200
export HINDSIGHT_API_MAX_SEARCH_RESULTS=50
export HINDSIGHT_API_RERANK_ENABLED=false
```
### Embedding Model Configuration
Configure the embedding model for vector search.
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `HINDSIGHT_API_EMBEDDING_MODEL` | HuggingFace model name | `all-MiniLM-L6-v2` | No |
| `HINDSIGHT_API_EMBEDDING_DEVICE` | Device: `cpu`, `cuda`, `mps` | `cpu` | No |
| `HINDSIGHT_API_EMBEDDING_BATCH_SIZE` | Batch size for embedding generation | `32` | No |
```bash
# Use GPU for embeddings (if available)
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
# Use Apple Silicon GPU
export HINDSIGHT_API_EMBEDDING_DEVICE=mps
# Larger batch size for better throughput
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=64
```
### Control Plane Configuration
Configure the optional web UI control plane.
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `HINDSIGHT_CP_API_URL` | API server URL | `http://localhost:8888` | No |
| `HINDSIGHT_CP_HOSTNAME` | Server bind address | `0.0.0.0` | No |
| `HINDSIGHT_CP_PORT` | Server port | `3000` | No |
```bash
export HINDSIGHT_CP_API_URL=http://api.example.com:8888
export HINDSIGHT_CP_HOSTNAME=0.0.0.0
export HINDSIGHT_CP_PORT=3000
```
## Configuration Files ## Configuration Files
### .env File ### .env File
For local development and Docker Compose deployments, use a `.env` file: The Hindisight API will look for a `.env` file:
```bash ```bash
# .env # .env
# Database
HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight HINDSIGHT_API_DATABASE_URL=postgresql://hindsight:hindsight_dev@localhost:5432/hindsight
# LLM Provider
HINDSIGHT_API_LLM_PROVIDER=groq HINDSIGHT_API_LLM_PROVIDER=groq
HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile
# Server
HINDSIGHT_API_HOST=0.0.0.0
HINDSIGHT_API_PORT=8888
HINDSIGHT_API_LOG_LEVEL=info
# Search
HINDSIGHT_API_DEFAULT_THINKING_BUDGET=500
HINDSIGHT_API_MAX_SEARCH_RESULTS=100
# Control Plane
HINDSIGHT_CP_API_URL=http://localhost:8888
HINDSIGHT_CP_PORT=3000
```
### Docker Compose
Example `docker-compose.yml` configuration:
```yaml
version: '3.8'
services:
postgres:
image: pgvector/pgvector:pg16
environment:
POSTGRES_USER: hindsight
POSTGRES_PASSWORD: hindsight_dev
POSTGRES_DB: hindsight
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
api:
image: hindsight/api:latest
environment:
HINDSIGHT_API_DATABASE_URL: postgresql://hindsight:hindsight_dev@postgres:5432/hindsight
HINDSIGHT_API_LLM_PROVIDER: groq
HINDSIGHT_API_LLM_API_KEY: ${GROQ_API_KEY}
HINDSIGHT_API_LLM_MODEL: llama-3.1-70b-versatile
HINDSIGHT_API_PORT: 8888
HINDSIGHT_API_LOG_LEVEL: info
ports:
- "8888:8888"
depends_on:
- postgres
control-plane:
image: hindsight/control-plane:latest
environment:
HINDSIGHT_CP_API_URL: http://api:8888
HINDSIGHT_CP_PORT: 3000
ports:
- "3000:3000"
depends_on:
- api
volumes:
postgres_data:
```
### Kubernetes ConfigMap
Example Kubernetes configuration:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hindsight-config
data:
HINDSIGHT_API_LLM_PROVIDER: "groq"
HINDSIGHT_API_LLM_MODEL: "llama-3.1-70b-versatile"
HINDSIGHT_API_PORT: "8888"
HINDSIGHT_API_LOG_LEVEL: "info"
HINDSIGHT_API_DEFAULT_THINKING_BUDGET: "500"
HINDSIGHT_API_MAX_SEARCH_RESULTS: "100"
---
apiVersion: v1
kind: Secret
metadata:
name: hindsight-secrets
type: Opaque
stringData:
HINDSIGHT_API_DATABASE_URL: "postgresql://user:pass@postgres:5432/hindsight"
HINDSIGHT_API_LLM_API_KEY: "gsk_xxxxxxxxxxxx"
```
## Configuration Precedence
Configuration values are resolved in this order (highest to lowest priority):
1. **Environment variables** - Direct environment variables
2. **`.env` file** - Local `.env` file in current directory
3. **Default values** - Built-in defaults
## Configuration Validation
Hindsight validates configuration on startup and will fail fast with clear error messages:
```bash
# Missing required configuration
ERROR: HINDSIGHT_API_LLM_API_KEY is required when using provider 'openai'
# Invalid value
ERROR: HINDSIGHT_API_LOG_LEVEL must be one of: debug, info, warning, error
# Invalid connection
ERROR: Failed to connect to database at postgresql://localhost:5432/hindsight
```
## Best Practices
1. **Use secrets management** for production deployments (AWS Secrets Manager, Vault, etc.)
2. **Never commit** `.env` files with real credentials to version control
3. **Use different configs** for dev, staging, and production environments
4. **Set appropriate log levels**: `debug` for dev, `info` for staging, `warning` for production
5. **Configure connection pooling** based on expected load
6. **Use managed databases** in production with proper backups
7. **Enable SSL/TLS** for database connections in production
8. **Set CORS origins** explicitly in production (don't use `*`)
## Troubleshooting
### Database Connection Issues
```bash
# Test database connection
psql "$HINDSIGHT_API_DATABASE_URL"
# Check PostgreSQL is running
docker-compose ps postgres
# View database logs
docker-compose logs postgres
```
### LLM Provider Issues
```bash
# Test API key
curl https://api.groq.com/openai/v1/models \
-H "Authorization: Bearer $HINDSIGHT_API_LLM_API_KEY"
# Enable debug logging
export HINDSIGHT_API_LOG_LEVEL=debug
hindsight-api
```
### Port Already in Use
```bash
# Find process using port 8888
lsof -i :8888
# Kill process
kill -9 <PID>
# Or use a different port
export HINDSIGHT_API_PORT=9000
```
## Advanced Configuration
### Custom Embedding Models
Use custom embedding models from HuggingFace:
```bash
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-mpnet-base-v2
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
```
### Custom Temporal Parser
Use custom T5 model for temporal parsing:
```bash
export HINDSIGHT_API_TEMPORAL_MODEL=google/t5-v1_1-base
```
### Multi-GPU Configuration
For distributed embedding generation:
```bash
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda:0,cuda:1
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=128
``` ```
--- ---

View file

@ -7,29 +7,29 @@ slug: /
## Why Hindsight? ## Why Hindsight?
AI assistants forget everything between sessions. Every conversation starts from zero—no context about who you are, what you've discussed, or what the memory bank has learned. This isn't just inconvenient; it fundamentally limits what AI memory banks can do. 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:** **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 - **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 - **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
- **Memory banks need opinions** — A coding assistant that remembers "the user prefers functional programming" should weigh that when making recommendations - **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 - **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 memory banks. Hindsight solves these problems with a memory system designed specifically for AI agents.
## What Hindsight Does ## What Hindsight Does
```mermaid ```mermaid
graph TB graph TB
subgraph Your Application subgraph app[Your Application]
Agent[AI Agent] Agent[AI Agent]
end end
subgraph Hindsight subgraph hindsight[Hindsight]
API[Hindsight API] API[API Server]
subgraph Memory Bank subgraph bank[Memory Bank]
Documents[Documents] Documents[Documents]
Memories[Memories] Memories[Memories]
Entities[Entities] Entities[Entities]
@ -103,8 +103,7 @@ The `bias_strength` parameter (0-1) controls how much personality influences opi
## Next Steps ## Next Steps
### Getting Started ### Getting Started
- [**Installation**](/developer/api/installation) — Install Hindsight for Python, Node.js, or CLI - [**Quick Start**](/developer/api/quickstart) — Install and get up and running in 60 seconds
- [**Quick Start**](/developer/api/quickstart) — Get up and running in 60 seconds
### Core Concepts ### Core Concepts
- [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts - [**Retain**](/developer/retain) — How memories are stored with multi-dimensional facts

View file

@ -6,21 +6,26 @@ Hindsight can be deployed in three ways depending on your infrastructure and req
### PostgreSQL with pgvector ### PostgreSQL with pgvector
Hindsight requires PostgreSQL with the **pgvector** extension for vector similarity search: Hindsight requires PostgreSQL with the **pgvector** extension for vector similarity search.
- PostgreSQL 14+ (recommended: 16+) **By default**, Hindsight uses **pg0** — an embedded PostgreSQL that runs locally on your machine. This is convenient for development but **not recommended for production**.
- pgvector extension installed
- ~2GB+ RAM for small deployments **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 ### LLM Provider
You need an LLM API key for fact extraction, entity resolution, and answer generation: You need an LLM API key for fact extraction, entity resolution, and answer generation:
- **Groq** (recommended): Fast inference, high throughput - **Groq** (recommended): Fast inference with `gpt-oss-20b`
- **OpenAI**: GPT-4, GPT-4o, GPT-4 Mini - **OpenAI**: GPT-4o, GPT-4o-mini
- **Anthropic**: Claude 3.5 Sonnet, Haiku
- **Ollama**: Run models locally - **Ollama**: Run models locally
See [Models](./models) for detailed comparison and configuration.
--- ---
## Docker ## Docker
@ -42,30 +47,6 @@ docker run -p 8888:8888 -p 9999:9999 \
- **API Server**: http://localhost:8888 - **API Server**: http://localhost:8888
- **Control Plane** (Web UI): http://localhost:9999 - **Control Plane** (Web UI): http://localhost:9999
### Docker Compose
For more control, use Docker Compose which bundles all dependencies separately:
```bash
# Clone the repository
git clone https://github.com/vectorize-io/hindsight.git
cd hindsight
# Create environment file
cp .env.example .env
# Edit .env with your LLM API key
# Start all services
cd docker
./start.sh
```
**Management**:
```bash
./stop.sh # Stop services
./clean.sh # Delete all data
```
--- ---
## Helm / Kubernetes ## Helm / Kubernetes

View file

@ -0,0 +1,115 @@
---
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:
```
http://localhost:8888/mcp
```
To disable it, set the environment variable:
```bash
export HINDSIGHT_API_MCP_ENABLED=false
```
## Available Tools
### hindsight_put
Store information to a user's memory bank.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bank_id` | string | Yes | Unique identifier for the user (e.g., `user_12345`, `alice@example.com`) |
| `content` | string | Yes | The fact or memory to store |
| `context` | string | Yes | Category for the memory (e.g., `personal_preferences`, `work_history`) |
| `explanation` | string | No | Why this memory is being stored |
**Example:**
```json
{
"name": "hindsight_put",
"arguments": {
"bank_id": "user_12345",
"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
---
### hindsight_search
Search a user's memory bank to provide personalized responses.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `bank_id` | string | Yes | Unique identifier for the user |
| `query` | string | Yes | Natural language search query |
| `max_tokens` | integer | No | Maximum tokens for results (default: 4096) |
| `explanation` | string | No | Why this search is being performed |
**Example:**
```json
{
"name": "hindsight_search",
"arguments": {
"bank_id": "user_12345",
"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,
"document_id": 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
---
## Per-User Isolation
Both tools require a `bank_id` that uniquely identifies the user. Memories are strictly isolated per bank — one user cannot access another user's memories.
**Best practices:**
- Use consistent identifiers (user ID, email, session ID)
- Don't share `bank_id` between different users
- Only call these tools when you can identify the specific user
---
## Integration with AI Assistants
The MCP server can be used with any MCP-compatible AI assistant. For Claude Desktop integration using the CLI, see [MCP Server (CLI)](/sdks/mcp).

View file

@ -1,611 +1,58 @@
# Metrics # Metrics
Hindsight exposes comprehensive metrics for monitoring performance, usage, and system health in production deployments. Hindsight exposes Prometheus metrics at `/metrics` for monitoring.
## Prometheus Metrics
Hindsight exposes metrics in Prometheus format at the `/metrics` endpoint.
### Accessing Metrics
```bash ```bash
# View all metrics
curl http://localhost:8888/metrics curl http://localhost:8888/metrics
```
# Scrape with Prometheus ## Available Metrics
# Add to prometheus.yml:
### Request Metrics
| Metric | Type | Description |
|--------|------|-------------|
| `hindsight_http_requests_total` | Counter | Total HTTP requests (labels: method, endpoint, status_code) |
| `hindsight_http_request_duration_seconds` | Histogram | Request latency (labels: method, endpoint) |
### Memory Operations
| Metric | Type | Description |
|--------|------|-------------|
| `hindsight_retain_duration_seconds` | Histogram | Retain operation latency |
| `hindsight_retain_items_total` | Counter | Total items retained |
| `hindsight_recall_duration_seconds` | Histogram | Recall operation latency |
| `hindsight_recall_results_count` | Histogram | Number of results per recall |
| `hindsight_reflect_duration_seconds` | Histogram | Reflect operation latency |
### LLM Metrics
| Metric | Type | Description |
|--------|------|-------------|
| `hindsight_llm_requests_total` | Counter | LLM API requests (labels: provider, model, status) |
| `hindsight_llm_request_duration_seconds` | Histogram | LLM request latency |
| `hindsight_llm_tokens_total` | Counter | Tokens consumed (labels: provider, token_type) |
### Database Metrics
| Metric | Type | Description |
|--------|------|-------------|
| `hindsight_db_connections_active` | Gauge | Active database connections |
| `hindsight_db_connections_idle` | Gauge | Idle connections in pool |
| `hindsight_db_query_duration_seconds` | Histogram | Query latency (labels: query_type) |
### Memory Bank Metrics
| Metric | Type | Description |
|--------|------|-------------|
| `hindsight_bank_memory_units_total` | Gauge | Total memories per bank |
| `hindsight_bank_entities_total` | Gauge | Total entities per bank |
## Prometheus Configuration
```yaml
scrape_configs: scrape_configs:
- job_name: 'hindsight' - job_name: 'hindsight'
static_configs: static_configs:
- targets: ['localhost:8888'] - targets: ['localhost:8888']
``` ```
## Core Metrics
### Request Metrics
Track API request performance and throughput.
#### `hindsight_http_requests_total`
Total number of HTTP requests by endpoint and status code.
**Type**: Counter
**Labels**:
- `method`: HTTP method (GET, POST, etc.)
- `endpoint`: API endpoint path
- `status_code`: HTTP status code (200, 400, 500, etc.)
```promql
# Requests per second by endpoint
rate(hindsight_http_requests_total[5m])
# Error rate (4xx and 5xx)
rate(hindsight_http_requests_total{status_code=~"4..|5.."}[5m])
```
#### `hindsight_http_request_duration_seconds`
HTTP request latency distribution.
**Type**: Histogram
**Labels**:
- `method`: HTTP method
- `endpoint`: API endpoint path
```promql
# P95 latency by endpoint
histogram_quantile(0.95,
rate(hindsight_http_request_duration_seconds_bucket[5m])
)
# Average request duration
rate(hindsight_http_request_duration_seconds_sum[5m]) /
rate(hindsight_http_request_duration_seconds_count[5m])
```
### Memory Operations
Track retain, recall, and reflect operations.
#### `hindsight_retain_duration_seconds`
Time spent in retain (ingestion) operations.
**Type**: Histogram
**Labels**:
- `bank_id`: Memory bank identifier
- `async`: Whether operation was async (`true`/`false`)
```promql
# P99 retain latency
histogram_quantile(0.99,
rate(hindsight_retain_duration_seconds_bucket[5m])
)
# Sync vs async retain performance
histogram_quantile(0.50,
rate(hindsight_retain_duration_seconds_bucket{async="false"}[5m])
)
vs
histogram_quantile(0.50,
rate(hindsight_retain_duration_seconds_bucket{async="true"}[5m])
)
```
#### `hindsight_retain_items_total`
Total number of memory items retained.
**Type**: Counter
**Labels**:
- `bank_id`: Memory bank identifier
```promql
# Items retained per second
rate(hindsight_retain_items_total[5m])
# Total items retained per bank
sum by(bank_id) (hindsight_retain_items_total)
```
#### `hindsight_recall_duration_seconds`
Time spent in recall (search) operations.
**Type**: Histogram
**Labels**:
- `bank_id`: Memory bank identifier
- `budget`: Thinking budget level (low, mid, high)
```promql
# P95 recall latency by budget
histogram_quantile(0.95,
rate(hindsight_recall_duration_seconds_bucket[5m])
) by (budget)
# Recall operations per second
rate(hindsight_recall_duration_seconds_count[5m])
```
#### `hindsight_recall_results_count`
Number of results returned by recall operations.
**Type**: Histogram
**Labels**:
- `bank_id`: Memory bank identifier
```promql
# Average number of results per recall
rate(hindsight_recall_results_count_sum[5m]) /
rate(hindsight_recall_results_count_count[5m])
```
#### `hindsight_reflect_duration_seconds`
Time spent in reflect (reasoning) operations.
**Type**: Histogram
**Labels**:
- `bank_id`: Memory bank identifier
- `budget`: Thinking budget level
```promql
# P50, P95, P99 reflect latency
histogram_quantile(0.50, rate(hindsight_reflect_duration_seconds_bucket[5m]))
histogram_quantile(0.95, rate(hindsight_reflect_duration_seconds_bucket[5m]))
histogram_quantile(0.99, rate(hindsight_reflect_duration_seconds_bucket[5m]))
```
### LLM Metrics
Track LLM provider usage and performance.
#### `hindsight_llm_requests_total`
Total number of LLM API requests.
**Type**: Counter
**Labels**:
- `provider`: LLM provider (openai, groq, ollama, etc.)
- `model`: Model name
- `operation`: Operation type (fact_extraction, entity_resolution, reasoning, etc.)
- `status`: Request status (success, error, timeout)
```promql
# LLM requests per second by provider
rate(hindsight_llm_requests_total[5m]) by (provider)
# LLM error rate
rate(hindsight_llm_requests_total{status="error"}[5m]) /
rate(hindsight_llm_requests_total[5m])
```
#### `hindsight_llm_request_duration_seconds`
LLM API request latency.
**Type**: Histogram
**Labels**:
- `provider`: LLM provider
- `model`: Model name
- `operation`: Operation type
```promql
# P95 LLM latency by provider
histogram_quantile(0.95,
rate(hindsight_llm_request_duration_seconds_bucket[5m])
) by (provider)
```
#### `hindsight_llm_tokens_total`
Total tokens consumed (prompt + completion).
**Type**: Counter
**Labels**:
- `provider`: LLM provider
- `model`: Model name
- `token_type`: Token type (prompt, completion)
```promql
# Tokens per second
rate(hindsight_llm_tokens_total[5m])
# Cost estimation (OpenAI GPT-4)
rate(hindsight_llm_tokens_total{provider="openai",model="gpt-4",token_type="prompt"}[5m]) * 0.00003 +
rate(hindsight_llm_tokens_total{provider="openai",model="gpt-4",token_type="completion"}[5m]) * 0.00006
```
### Database Metrics
Track database connection pool and query performance.
#### `hindsight_db_connections_active`
Number of active database connections.
**Type**: Gauge
```promql
# Active connections
hindsight_db_connections_active
# Connection pool utilization %
(hindsight_db_connections_active / 20) * 100
```
#### `hindsight_db_connections_idle`
Number of idle database connections in the pool.
**Type**: Gauge
```promql
# Idle connections
hindsight_db_connections_idle
# Pool efficiency (lower is better)
hindsight_db_connections_idle /
(hindsight_db_connections_active + hindsight_db_connections_idle)
```
#### `hindsight_db_query_duration_seconds`
Database query latency distribution.
**Type**: Histogram
**Labels**:
- `query_type`: Type of query (select, insert, update, vector_search)
```promql
# P95 vector search latency
histogram_quantile(0.95,
rate(hindsight_db_query_duration_seconds_bucket{query_type="vector_search"}[5m])
)
# Slow queries (> 1s)
histogram_quantile(0.99,
rate(hindsight_db_query_duration_seconds_bucket[5m])
)
```
### Memory Bank Metrics
Track memory bank usage and statistics.
#### `hindsight_bank_memory_units_total`
Total number of memory units per bank.
**Type**: Gauge
**Labels**:
- `bank_id`: Memory bank identifier
- `fact_type`: Fact type (world, agent, opinion)
```promql
# Total memories per bank
sum by(bank_id) (hindsight_bank_memory_units_total)
# Memory distribution by type
sum by(fact_type) (hindsight_bank_memory_units_total)
```
#### `hindsight_bank_entities_total`
Total number of entities per bank.
**Type**: Gauge
**Labels**:
- `bank_id`: Memory bank identifier
```promql
# Entities per bank
hindsight_bank_entities_total
# Total entities across all banks
sum(hindsight_bank_entities_total)
```
### System Metrics
Track system resource usage.
#### `hindsight_embedding_model_memory_bytes`
Memory used by embedding models.
**Type**: Gauge
```promql
# Model memory in GB
hindsight_embedding_model_memory_bytes / 1024 / 1024 / 1024
```
#### `hindsight_process_cpu_seconds_total`
Total CPU time used by the process.
**Type**: Counter
```promql
# CPU utilization %
rate(hindsight_process_cpu_seconds_total[5m]) * 100
```
#### `hindsight_process_memory_bytes`
Process memory usage.
**Type**: Gauge
```promql
# Memory usage in GB
hindsight_process_memory_bytes / 1024 / 1024 / 1024
```
## Sample Queries
### Performance Monitoring
```promql
# Request latency by endpoint (P95)
histogram_quantile(0.95,
sum by(endpoint, le) (
rate(hindsight_http_request_duration_seconds_bucket[5m])
)
)
# Requests per second
sum(rate(hindsight_http_requests_total[5m]))
# Error rate %
sum(rate(hindsight_http_requests_total{status_code=~"5.."}[5m])) /
sum(rate(hindsight_http_requests_total[5m])) * 100
```
### Capacity Planning
```promql
# Database connection pool saturation
hindsight_db_connections_active / 20 * 100
# LLM request rate trend
rate(hindsight_llm_requests_total[1h])
# Average items retained per operation
rate(hindsight_retain_items_total[5m]) /
rate(hindsight_retain_duration_seconds_count[5m])
```
### Cost Analysis
```promql
# Estimated LLM cost per hour (OpenAI GPT-4)
(
rate(hindsight_llm_tokens_total{provider="openai",model="gpt-4",token_type="prompt"}[1h]) * 0.00003 +
rate(hindsight_llm_tokens_total{provider="openai",model="gpt-4",token_type="completion"}[1h]) * 0.00006
) * 3600
# Tokens per operation type
sum by(operation) (
rate(hindsight_llm_tokens_total[5m])
)
```
### Troubleshooting
```promql
# Slow recalls (> 1 second)
count(
hindsight_recall_duration_seconds_bucket{le="1.0"} == 0
)
# LLM timeout rate
rate(hindsight_llm_requests_total{status="timeout"}[5m])
# Database connection exhaustion events
changes(hindsight_db_connections_active[5m])
```
## Grafana Dashboard
Example Grafana dashboard configuration:
```json
{
"dashboard": {
"title": "Hindsight Monitoring",
"panels": [
{
"title": "Request Rate",
"targets": [{
"expr": "sum(rate(hindsight_http_requests_total[5m]))"
}]
},
{
"title": "P95 Latency by Endpoint",
"targets": [{
"expr": "histogram_quantile(0.95, rate(hindsight_http_request_duration_seconds_bucket[5m])) by (endpoint)"
}]
},
{
"title": "Error Rate",
"targets": [{
"expr": "sum(rate(hindsight_http_requests_total{status_code=~\"5..\"}[5m])) / sum(rate(hindsight_http_requests_total[5m])) * 100"
}]
},
{
"title": "LLM Requests by Provider",
"targets": [{
"expr": "sum by(provider) (rate(hindsight_llm_requests_total[5m]))"
}]
},
{
"title": "Database Connections",
"targets": [
{
"expr": "hindsight_db_connections_active",
"legendFormat": "Active"
},
{
"expr": "hindsight_db_connections_idle",
"legendFormat": "Idle"
}
]
}
]
}
}
```
## Alerting Rules
Example Prometheus alerting rules:
```yaml
groups:
- name: hindsight
rules:
# High error rate
- alert: HighErrorRate
expr: |
sum(rate(hindsight_http_requests_total{status_code=~"5.."}[5m])) /
sum(rate(hindsight_http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"
# Slow recalls
- alert: SlowRecalls
expr: |
histogram_quantile(0.95,
rate(hindsight_recall_duration_seconds_bucket[5m])
) > 2.0
for: 10m
labels:
severity: warning
annotations:
summary: "Recall operations are slow"
description: "P95 recall latency is {{ $value }}s"
# Database connection pool exhaustion
- alert: DatabasePoolExhaustion
expr: hindsight_db_connections_active / 20 > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: "Database connection pool nearly exhausted"
description: "Pool utilization is {{ $value | humanizePercentage }}"
# LLM API failures
- alert: LLMAPIFailures
expr: |
rate(hindsight_llm_requests_total{status="error"}[5m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "High LLM API error rate"
description: "LLM error rate: {{ $value }} errors/s"
# High memory usage
- alert: HighMemoryUsage
expr: hindsight_process_memory_bytes / 1024 / 1024 / 1024 > 8
for: 10m
labels:
severity: warning
annotations:
summary: "High memory usage"
description: "Process using {{ $value }}GB of memory"
```
## Custom Metrics
### Adding Custom Metrics
Extend Hindsight with custom metrics in your application code:
```python
from prometheus_client import Counter, Histogram
# Define custom metric
custom_operations = Counter(
'hindsight_custom_operations_total',
'Total custom operations',
['operation_type']
)
# Use in code
custom_operations.labels(operation_type='batch_import').inc()
```
## Trace Information
Enable detailed trace information in API responses:
```python
result = client.recall_memories(
bank_id="my-bank",
query="test query",
trace=True # Enable trace
)
# Access trace data
if result.trace:
print(f"Total time: {result.trace['total_time']}ms")
print(f"Activations: {result.trace['activation_count']}")
print(f"Vector search: {result.trace['vector_search_time']}ms")
print(f"Reranking: {result.trace['rerank_time']}ms")
```
## Logging Integration
Hindsight logs are structured and can be easily integrated with log aggregation systems:
### JSON Structured Logs
```bash
export HINDSIGHT_API_LOG_FORMAT=json
export HINDSIGHT_API_LOG_LEVEL=info
```
```json
{
"timestamp": "2025-01-15T10:30:45.123Z",
"level": "INFO",
"logger": "hindsight.api",
"message": "Recall completed",
"bank_id": "my-bank",
"query": "test query",
"results_count": 15,
"duration_ms": 423
}
```
### Log Levels
- `DEBUG`: Detailed diagnostic information
- `INFO`: General informational messages
- `WARNING`: Warning messages for potentially harmful situations
- `ERROR`: Error messages for failures
## Best Practices
1. **Set up alerting** for critical metrics (error rate, latency, connection pool)
2. **Monitor costs** by tracking LLM token usage
3. **Track trends** over time to identify capacity needs
4. **Use dashboards** to visualize key metrics
5. **Set appropriate retention** for metrics data (30-90 days)
6. **Correlate metrics** with logs for troubleshooting
7. **Establish baselines** for normal operation
8. **Review metrics regularly** to identify optimization opportunities
---
For metrics-related questions or issues, please [open an issue](https://github.com/your-repo/hindsight/issues) on GitHub.

View file

@ -1,529 +1,134 @@
# Models # Models
Hindsight uses several machine learning models for different tasks. This page explains what models are used, why they're chosen, and how to optimize their performance. Hindsight uses several machine learning models for different tasks.
## Model Overview ## Overview
Hindsight's processing pipeline uses four types of models: | Model Type | Purpose | Default | Configurable |
|------------|---------|---------|--------------|
| Model Type | Purpose | Default Model | Configurable |
|------------|---------|---------------|--------------|
| **Embedding** | Vector representations for semantic search | `all-MiniLM-L6-v2` | Yes | | **Embedding** | Vector representations for semantic search | `all-MiniLM-L6-v2` | Yes |
| **Cross-Encoder** | Reranking search results | `ms-marco-MiniLM-L-6-v2` | Yes | | **Cross-Encoder** | Reranking search results | `ms-marco-MiniLM-L-6-v2` | Yes |
| **Temporal Parser** | Understanding time expressions | `t5-small` | Yes | | **Temporal Parser** | Understanding time expressions | `t5-small` | Yes |
| **LLM** | Fact extraction, reasoning, generation | Provider-specific | Yes | | **LLM** | Fact extraction, reasoning, generation | Provider-specific | Yes |
All local models (embedding, cross-encoder, temporal) are automatically downloaded from HuggingFace on first run and cached in `~/.cache/huggingface/`. All local models (embedding, cross-encoder, temporal) are automatically downloaded from HuggingFace on first run.
---
## Embedding Model ## Embedding Model
### Purpose Converts text into dense vector representations for semantic similarity search.
The embedding model converts text into dense vector representations (embeddings) for semantic similarity search. **Default:** `sentence-transformers/all-MiniLM-L6-v2` (384 dimensions, ~90MB)
**Used for**: **Alternatives:**
- Encoding memory units during retention
- Encoding search queries during recall
- Vector similarity calculations
### Default: all-MiniLM-L6-v2 | Model | Dimensions | Use Case |
|-------|------------|----------|
| `all-MiniLM-L6-v2` | 384 | Default, fast, good quality |
| `all-mpnet-base-v2` | 768 | Higher accuracy, slower |
| `paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
``` **Configuration:**
Model: sentence-transformers/all-MiniLM-L6-v2
Dimensions: 384
Size: ~90MB
Performance: ~2000 texts/second on CPU
```
**Why this model?**
- **Fast**: Optimized for CPU inference
- **Small**: Only 384 dimensions, efficient storage
- **Accurate**: Strong performance on semantic similarity tasks
- **Well-balanced**: Good trade-off between speed and quality
### Performance Optimization
#### 1. Use GPU Acceleration
```bash ```bash
# Enable CUDA (NVIDIA GPUs)
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
# Enable MPS (Apple Silicon)
export HINDSIGHT_API_EMBEDDING_DEVICE=mps
# Verify GPU usage in logs
hindsight-api --log-level debug
# Should see: "Loading embedding model on device: cuda"
```
**Expected speedup**:
- CPU: ~2000 texts/second
- GPU (CUDA): ~10,000-20,000 texts/second
- Apple Silicon (MPS): ~5,000-10,000 texts/second
#### 2. Increase Batch Size
```bash
# Default batch size
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=32
# Larger batch size for better throughput (requires more memory)
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=128
# Smaller batch size for limited memory
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=16
```
**Guidelines**:
- **CPU**: 32-64 (diminishing returns beyond 64)
- **GPU**: 128-256 (can go higher with more VRAM)
- **Memory-constrained**: 8-16
#### 3. Alternative Embedding Models
For different use cases, you can use other embedding models:
**Higher Quality (Slower)**
```bash
# 768 dimensions, better accuracy, slower
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-mpnet-base-v2 export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-mpnet-base-v2
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda # or mps for Apple Silicon
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=64
``` ```
**Multilingual Support** ---
```bash
# Supports 50+ languages
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
```
**Larger Context Window**
```bash
# 512 token context (vs 256 for MiniLM)
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-roberta-large-v1
```
### Memory Requirements
| Model | Dimensions | Model Size | Runtime RAM (CPU) | Runtime RAM (GPU) |
|-------|------------|------------|-------------------|-------------------|
| all-MiniLM-L6-v2 | 384 | 90MB | ~500MB | ~1GB |
| all-mpnet-base-v2 | 768 | 420MB | ~1GB | ~2GB |
| all-roberta-large-v1 | 1024 | 1.3GB | ~2GB | ~4GB |
## Cross-Encoder (Reranker) ## Cross-Encoder (Reranker)
### Purpose Reranks initial search results to improve precision.
The cross-encoder reranks initial search results to improve precision. **Default:** `cross-encoder/ms-marco-MiniLM-L-6-v2` (~85MB)
**How it works**: **Alternatives:**
1. Vector search returns top 50-100 candidates (fast but approximate)
2. Cross-encoder scores each candidate with the query (slower but accurate)
3. Results are reranked by cross-encoder score
### Default: ms-marco-MiniLM-L-6-v2 | Model | Use Case |
|-------|----------|
| `ms-marco-MiniLM-L-6-v2` | Default, fast |
| `ms-marco-MiniLM-L-12-v2` | Higher accuracy |
| `mmarco-mMiniLMv2-L12-H384-v1` | Multilingual |
``` **Configuration:**
Model: cross-encoder/ms-marco-MiniLM-L-6-v2
Size: ~85MB
Performance: ~500 pairs/second on CPU
```
**Why this model?**
- **Accurate**: Trained on Microsoft MARCO dataset for passage ranking
- **Fast enough**: Can rerank 50 results in ~100ms on CPU
- **Small**: Efficient memory footprint
### Performance Optimization
#### 1. Control Reranking Scope
```bash
# Rerank top 50 results (default)
export HINDSIGHT_API_RERANK_TOP_K=50
# More thorough reranking (slower)
export HINDSIGHT_API_RERANK_TOP_K=100
# Faster reranking (less accurate)
export HINDSIGHT_API_RERANK_TOP_K=20
# Disable reranking entirely (fastest, less accurate)
export HINDSIGHT_API_RERANK_ENABLED=false
```
**Trade-offs**:
- More reranking = Better precision, higher latency
- Less reranking = Faster queries, lower precision
- No reranking = Fastest, relies only on vector similarity
#### 2. GPU Acceleration
Cross-encoders also benefit from GPU:
```bash
# Uses same device as embedding model
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
```
**Speedup**: ~5-10x faster on GPU vs CPU
### Alternative Reranker Models
**Higher Accuracy**
```bash ```bash
export HINDSIGHT_API_RERANK_MODEL=cross-encoder/ms-marco-MiniLM-L-12-v2 export HINDSIGHT_API_RERANK_MODEL=cross-encoder/ms-marco-MiniLM-L-12-v2
# Larger model, ~200MB, better accuracy export HINDSIGHT_API_RERANK_TOP_K=50 # How many results to rerank
export HINDSIGHT_API_RERANK_ENABLED=true # Set to false to disable
``` ```
**Multilingual** ---
```bash
export HINDSIGHT_API_RERANK_MODEL=cross-encoder/mmarco-mMiniLMv2-L12-H384-v1
# Supports multiple languages
```
## Temporal Parser ## Temporal Parser
### Purpose
Parses natural language time expressions into structured dates. Parses natural language time expressions into structured dates.
**Examples**: **Examples:**
- "last spring" → 2024-03-20 to 2024-06-20 - "last spring" → 2024-03-20 to 2024-06-20
- "in June 2024" → 2024-06-01 to 2024-06-30 - "two weeks ago" → calculated date range
- "two weeks ago" → 2024-05-15 to 2024-05-15
### Default: t5-small **Default:** `google/t5-small` (~240MB)
``` **Alternatives:**
Model: google/t5-small
Size: ~240MB
Performance: ~100 expressions/second on CPU
```
**Why this model?** | Model | Use Case |
- **Accurate**: Good performance on temporal expression parsing |-------|----------|
- **Compact**: Small enough for CPU inference | `t5-small` | Default, compact |
- **Standard**: Well-established model for sequence-to-sequence tasks | `t5-base` | Better accuracy for complex expressions |
### Performance Optimization **Configuration:**
Temporal parsing is typically not a bottleneck, but you can:
1. **Use a larger model for better accuracy**:
```bash
export HINDSIGHT_API_TEMPORAL_MODEL=google/t5-base
# ~850MB, better at complex temporal expressions
```
2. **Use GPU** (shared with other models):
```bash
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
```
## LLM (Large Language Model)
### Purpose
The LLM is used for high-level reasoning tasks that require language understanding and generation.
**Used for**:
- **Fact extraction**: Converting text into structured facts (retention)
- **Entity resolution**: Identifying and linking entities (retention)
- **Opinion generation**: Creating personality-based opinions (reflection)
- **Answer synthesis**: Generating responses from memories (reflect)
### Default: Provider-Specific
Hindsight supports multiple LLM providers. The default depends on your configuration:
| Provider | Default Model | Best For |
|----------|---------------|----------|
| **Groq** | `llama-3.1-70b-versatile` | High throughput, fast inference |
| **OpenAI** | `gpt-4o` | Best quality, general-purpose |
| **Anthropic** | `claude-3-5-sonnet-20241022` | Long context, complex reasoning |
| **Ollama** | User-specified | Local deployment, privacy |
### Performance Optimization
**The LLM is the primary bottleneck for write operations (retention).** See [Performance](./performance.md) for detailed optimization strategies.
#### 1. Choose the Right Provider
For **high-throughput retention** (many memories/second):
```bash ```bash
# Groq - fastest inference export HINDSIGHT_API_TEMPORAL_MODEL=google/t5-base
```
---
## LLM
Used for fact extraction, entity resolution, opinion generation, and answer synthesis.
**Supported providers:** Groq, OpenAI, Ollama
| Provider | Recommended Model | Best For |
|----------|-------------------|----------|
| **Groq** | `gpt-oss-20b` | Fast inference, high throughput (recommended) |
| **OpenAI** | `gpt-4o-mini` | Good quality, cost-effective |
| **OpenAI** | `gpt-4o` | Best quality |
| **Ollama** | `llama3.1` | Local deployment, privacy |
**Configuration:**
```bash
# Groq (recommended)
export HINDSIGHT_API_LLM_PROVIDER=groq export HINDSIGHT_API_LLM_PROVIDER=groq
export HINDSIGHT_API_LLM_MODEL=llama-3.1-70b-versatile
export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx export HINDSIGHT_API_LLM_API_KEY=gsk_xxxxxxxxxxxx
``` export HINDSIGHT_API_LLM_MODEL=openai/gpt-oss-20b
**Groq advantages**: # OpenAI
- 10-30x faster than OpenAI for similar models
- High rate limits (30+ RPM for free tier)
- Low latency (~500ms for retention)
For **best quality** (reasoning, complex fact extraction):
```bash
# OpenAI GPT-4
export HINDSIGHT_API_LLM_PROVIDER=openai export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_MODEL=gpt-4o
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
```
For **cost optimization**:
```bash
# OpenAI GPT-4 Mini - 60x cheaper than GPT-4
export HINDSIGHT_API_LLM_PROVIDER=openai
export HINDSIGHT_API_LLM_MODEL=gpt-4o-mini export HINDSIGHT_API_LLM_MODEL=gpt-4o-mini
export HINDSIGHT_API_LLM_API_KEY=sk-xxxxxxxxxxxx
```
For **local/private deployment**: # Ollama (local)
```bash
# Ollama with local Llama 3.1
export HINDSIGHT_API_LLM_PROVIDER=ollama export HINDSIGHT_API_LLM_PROVIDER=ollama
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1 export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
export HINDSIGHT_API_LLM_MODEL=llama3.1 export HINDSIGHT_API_LLM_MODEL=llama3.1
``` ```
#### 2. Optimize LLM Configuration **Note:** The LLM is the primary bottleneck for write operations. See [Performance](./performance) for optimization strategies.
```bash
# Increase timeout for slower providers
export HINDSIGHT_API_LLM_TIMEOUT=60 # seconds
# Increase retries for reliability
export HINDSIGHT_API_LLM_MAX_RETRIES=5
# Enable request caching (if supported by provider)
export HINDSIGHT_API_LLM_CACHE_ENABLED=true
```
#### 3. Rate Limit Management
For providers with strict rate limits:
1. **Use async retention** to queue operations:
```python
client.retain_memories(bank_id="...", items=batch, async_=True)
```
2. **Distribute across multiple API keys**:
```bash
# Rotate between keys in application logic
export HINDSIGHT_API_LLM_API_KEY_1=sk-key1
export HINDSIGHT_API_LLM_API_KEY_2=sk-key2
```
3. **Use multiple providers** for different operations:
```bash
# Groq for retention (fast)
# OpenAI for reflection (quality)
```
### Model Comparison
| Provider | Model | Speed | Quality | Cost/1M tokens | Rate Limit |
|----------|-------|-------|---------|----------------|------------|
| Groq | llama-3.1-70b | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Free tier | 30 RPM |
| OpenAI | gpt-4o-mini | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | $0.15 / $0.60 | 500 RPM |
| OpenAI | gpt-4o | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | $2.50 / $10.00 | 500 RPM |
| Anthropic | claude-3-5-sonnet | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | $3.00 / $15.00 | 50 RPM |
| Ollama | llama3.1 (local) | ⭐⭐ | ⭐⭐⭐ | Free | Unlimited |
## Resource Requirements
### Minimal Configuration (Development)
```
CPU: 2 cores
RAM: 4GB
Storage: 5GB (models + data)
```
Models loaded:
- Embedding model (~500MB RAM)
- Cross-encoder (~300MB RAM)
- Temporal parser (~500MB RAM)
- **Total**: ~1.5GB for models + 2GB for application
### Recommended Configuration (Production)
```
CPU: 4-8 cores
RAM: 8-16GB
GPU: Optional (NVIDIA with 4GB+ VRAM for 10x speedup)
Storage: 20GB+ (models + database)
```
Models loaded:
- Same models as minimal
- Additional RAM for connection pooling
- PostgreSQL in separate container/server
### High-Performance Configuration
```
CPU: 8-16 cores
RAM: 16-32GB
GPU: NVIDIA T4, V100, or A100 (8-40GB VRAM)
Storage: 50GB+ SSD
```
Benefits:
- GPU acceleration for embeddings: 10x faster
- More RAM for larger batch sizes
- More CPU cores for parallel processing
## Model Caching and Storage
### Cache Locations
```bash
# HuggingFace models
~/.cache/huggingface/
# Model-specific caches
~/.cache/torch/
# Clear caches
rm -rf ~/.cache/huggingface/
rm -rf ~/.cache/torch/
```
### Preloading Models
To avoid download delays in production:
```bash
# Pre-download all models
python -c "
from sentence_transformers import SentenceTransformer, CrossEncoder
from transformers import T5ForConditionalGeneration, T5Tokenizer
# Download embedding model
SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
# Download cross-encoder
CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# Download temporal parser
T5ForConditionalGeneration.from_pretrained('google/t5-small')
T5Tokenizer.from_pretrained('google/t5-small')
"
```
Or build into Docker image:
```dockerfile
FROM python:3.11-slim
# Install dependencies
RUN pip install hindsight-all
# Pre-download models
RUN python -c "from sentence_transformers import SentenceTransformer; \
SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"
# Rest of Dockerfile...
```
## Monitoring Model Performance
### Check Model Loading
```bash
# Enable debug logging
export HINDSIGHT_API_LOG_LEVEL=debug
hindsight-api
# Look for logs like:
# INFO: Loading embedding model: all-MiniLM-L6-v2 on device: cpu
# INFO: Loading cross-encoder: ms-marco-MiniLM-L-6-v2
# INFO: Loading temporal parser: t5-small
```
### Monitor Resource Usage
```python
# In your application logs
import psutil
# Memory usage
print(f"RAM: {psutil.virtual_memory().percent}%")
# CPU usage
print(f"CPU: {psutil.cpu_percent()}%")
# GPU usage (if available)
import torch
if torch.cuda.is_available():
print(f"GPU Memory: {torch.cuda.memory_allocated() / 1e9:.2f} GB")
```
## Troubleshooting
### Models Not Downloaded
```bash
# Check cache directory
ls -lh ~/.cache/huggingface/
# Manually download
python -c "from sentence_transformers import SentenceTransformer; \
SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"
# Check network connectivity
curl https://huggingface.co/
```
### Out of Memory
```bash
# Reduce batch size
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=8
# Use smaller models
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2 # smallest
# Disable reranking
export HINDSIGHT_API_RERANK_ENABLED=false
```
### Slow Inference
```bash
# Enable GPU if available
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda
# Check GPU availability
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
# Increase batch size (if you have RAM)
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=128
```
### LLM Rate Limits
```bash
# Use Groq for higher limits
export HINDSIGHT_API_LLM_PROVIDER=groq
# Use async retention to queue operations
# (in your application code)
client.retain_memories(..., async_=True)
```
--- ---
For model-related questions or issues, please [open an issue](https://github.com/your-repo/hindsight/issues) on GitHub. ## Model Comparison
| Provider | Model | Speed | Quality | Cost |
|----------|-------|-------|---------|------|
| Groq | gpt-oss-20b | Fast | Good | Free tier |
| OpenAI | gpt-4o-mini | Medium | Good | $0.15 / $0.60 per 1M tokens |
| OpenAI | gpt-4o | Slower | Best | $2.50 / $10.00 per 1M tokens |
| Ollama | llama3.1 | Varies | Good | Free (local) |

View file

@ -14,8 +14,6 @@ Hindsight's performance is optimized across three key operations:
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. 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.
### Read-Optimized Architecture
The system makes deliberate trade-offs to ensure **sub-second recall operations**: The system makes deliberate trade-offs to ensure **sub-second recall operations**:
- **Pre-computed embeddings**: All memory embeddings are generated and indexed during retention - **Pre-computed embeddings**: All memory embeddings are generated and indexed during retention
@ -25,43 +23,6 @@ The system makes deliberate trade-offs to ensure **sub-second recall operations*
This means **Recall (search) operations are blazingly fast** because all the heavy lifting has already been done. This means **Recall (search) operations are blazingly fast** because all the heavy lifting has already been done.
### Write Performance: LLM-Bound Operations
The trade-off is that **Retain (write) operations are inherently slower** because they involve:
1. **LLM-based fact extraction**: Converting raw text into structured semantic facts
2. **Entity recognition and resolution**: Identifying and linking entities across memories
3. **Temporal reasoning**: Extracting and normalizing time references
4. **Relationship mapping**: Building the semantic graph structure
5. **Embedding generation**: Creating vector representations for search
**The LLM is the primary bottleneck for write latency.** Each piece of content requires one or more LLM calls for fact extraction, which typically takes 500ms-2000ms per batch depending on content complexity.
### Achieving Fast Writes
To maximize retention throughput, we recommend:
1. **Use high-throughput LLM providers**: Choose providers with high requests-per-minute (RPM) limits
- ✅ **Recommended**: Groq (up to 30 RPM for Llama models), OpenAI GPT-4 Turbo/Mini
- ⚠️ **Slower**: Claude with lower rate limits, local models
2. **Batch your operations**: Group related content into batch requests to amortize overhead
```python
# Good: Batch retention
client.retain_memories(bank_id="...", items=batch_of_100_items)
# Less efficient: Individual retention
for item in items:
client.retain_memories(bank_id="...", items=[item])
```
3. **Use async mode for large datasets**: Queue operations in the background
```python
client.retain_memories(bank_id="...", items=large_batch, async_=True)
```
4. **Parallel processing**: For very large datasets, use multiple concurrent retention requests with different `document_id` values
### Performance Comparison ### Performance Comparison
| Operation | Typical Latency | Primary Bottleneck | Optimization Strategy | | Operation | Typical Latency | Primary Bottleneck | Optimization Strategy |
@ -70,64 +31,37 @@ To maximize retention throughput, we recommend:
| **Reflect** | 800-3000ms | LLM generation + search | Reduce search budget, use faster LLM | | **Reflect** | 800-3000ms | LLM generation + search | Reduce search budget, use faster LLM |
| **Retain** | 500ms-2000ms per batch | **LLM fact extraction** | Use high-throughput LLM provider | | **Retain** | 500ms-2000ms per batch | **LLM fact extraction** | Use high-throughput LLM provider |
### The Bottom Line
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: 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 retained in background processes or during low-traffic periods
- Memories are queried frequently in user-facing, latency-sensitive contexts - Memories are queried frequently in user-facing, latency-sensitive contexts
- The ratio of reads to writes is high (typically 10:1 or higher) - The ratio of reads to writes is high (typically 10:1 or higher)
If your use case requires extremely fast writes, focus on **LLM provider selection** and **batching strategies** rather than database or infrastructure optimization. ---
## Retain Performance ## Retain Performance
### Batch Ingestion **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 supports high-throughput batch ingestion for efficient memory storage: ### Hindsight Doesn't Need a Smart Model
```python 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).
from hindsight_client import HindsightClient
client = HindsightClient(base_url="http://localhost:8888") To maximize retention throughput:
# Batch retain for better performance 1. **Use high-throughput LLM providers**: Choose providers with high requests-per-minute (RPM) limits and low latency
items = [ - ✅ **Fast**: [Groq](https://groq.com) with `gpt-oss-20b` or other openai-oss models, self-hosted models on GPU clusters (vLLM, TGI)
{"content": "Memory 1", "context": "Context 1"}, - ⚠️ **Slower**: Standard cloud LLM providers with rate limits
{"content": "Memory 2", "context": "Context 2"},
# ... up to thousands of items
]
result = client.retain_memories( 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.
bank_id="my-bank",
items=items,
document_id="batch-doc-001"
)
```
### Async Operations 3. **Use async mode for large datasets**: Queue operations in the background
For very large datasets, use async operations to avoid blocking: 4. **Parallel processing**: For very large datasets, use multiple concurrent retention requests with different `document_id` values
```python ### Throughput
# Queue for background processing
result = client.retain_memories(
bank_id="my-bank",
items=large_dataset,
async_=True # Process in background
)
print(f"Queued {result.items_count} items for processing") Typical ingestion performance:
# Check operation status
operations = client.list_operations(bank_id="my-bank")
for op in operations:
print(f"Operation {op.id}: {op.status}")
```
### Ingestion Throughput
Typical ingestion performance on standard hardware:
| Mode | Items/second | Use Case | | Mode | Items/second | Use Case |
|------|--------------|----------| |------|--------------|----------|
@ -141,58 +75,25 @@ Typical ingestion performance on standard hardware:
- Database write performance - Database write performance
- Available CPU/memory resources - Available CPU/memory resources
### Optimization Tips ---
1. **Batch related memories**: Group related content into the same document for better context
2. **Use async for large batches**: Set `async_=True` for batches > 100 items
3. **Optimize chunk sizes**: Larger chunks (1000-2000 tokens) are more efficient than many small chunks
4. **Parallel processing**: Use multiple concurrent requests with different `document_id` values
## Recall Performance ## Recall Performance
### Search Latency ### Budget
Hindsight provides sub-second semantic search with configurable performance/quality tradeoffs: The `budget` parameter controls the search depth and quality. Choose based on query complexity — comprehensive questions that need thorough analysis benefit from higher budgets:
```python
# Fast search (low budget)
result = client.recall_memories(
bank_id="my-bank",
query="What did we discuss about the project?",
budget="low" # ~100-200ms
)
# Balanced search (mid budget)
result = client.recall_memories(
bank_id="my-bank",
query="What did we discuss about the project?",
budget="mid" # ~300-500ms
)
# Thorough search (high budget)
result = client.recall_memories(
bank_id="my-bank",
query="What did we discuss about the project?",
budget="high" # ~500-1000ms
)
```
### Thinking Budget
The `budget` parameter controls the search depth and quality:
| Budget | Latency | Memory Activation | Use Case | | Budget | Latency | Memory Activation | Use Case |
|--------|---------|-------------------|----------| |--------|---------|-------------------|----------|
| `low` | 100-300ms | ~10-50 facts | Quick lookups, real-time chat | | `low` | 100-300ms | ~10-50 facts | Quick lookups, real-time chat |
| `mid` | 300-600ms | ~50-200 facts | Standard queries, balanced performance | | `mid` | 300-600ms | ~50-200 facts | Standard queries, balanced performance |
| `high` | 500-1500ms | ~200-500 facts | Complex questions, thorough analysis | | `high` | 500-1500ms | ~200-500 facts | Comprehensive questions, thorough analysis |
### Search Optimization ### Search Optimization
1. **Appropriate budgets**: Use lower budgets for simple queries, higher for complex reasoning 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) 2. **Limit result tokens**: Set `max_tokens` to control response size (default: 4096)
3. **Filter by fact type**: Specify `types` to search only relevant fact categories 3. **Include entities/chunks**: Use `include_entities` and `include_chunks` to retrieve additional context when needed — each has its own token budget
4. **Temporal filtering**: Use `query_timestamp` for time-aware search
### Database Performance ### Database Performance
@ -204,21 +105,6 @@ Hindsight uses PostgreSQL with pgvector for efficient vector search:
## Reflect Performance ## Reflect Performance
### Answer Generation
Reflect combines semantic search with personality-aware reasoning:
```python
result = client.reflect(
bank_id="my-bank",
query="What should we prioritize next quarter?",
budget="mid", # Controls memory search depth
context="We have limited resources"
)
print(result.text) # Personality-aware answer
```
### Performance Characteristics ### Performance Characteristics
| Component | Latency | Description | | Component | Latency | Description |
@ -234,187 +120,24 @@ print(result.text) # Personality-aware answer
3. **Streaming responses**: Use streaming APIs (when available) for faster time-to-first-token 3. **Streaming responses**: Use streaming APIs (when available) for faster time-to-first-token
4. **Caching**: Cache frequent queries at the application level 4. **Caching**: Cache frequent queries at the application level
## Concurrent Operations
### Parallelism
Hindsight supports high levels of concurrent operations:
```python
import asyncio
from hindsight_client import AsyncHindsightClient
async def parallel_recall():
client = AsyncHindsightClient(base_url="http://localhost:8888")
# Execute multiple recalls in parallel
tasks = [
client.recall_memories("bank-1", query="query 1"),
client.recall_memories("bank-2", query="query 2"),
client.recall_memories("bank-3", query="query 3"),
]
results = await asyncio.gather(*tasks)
return results
```
### Concurrency Limits
Default limits (configurable in server settings):
- **Database connections**: Pool of 20 connections
- **LLM rate limits**: Depends on provider (typically 60-500 RPM)
- **Memory search**: No hard limit, scales with CPU cores
- **Concurrent requests**: 100+ simultaneous requests supported
## Scaling Strategies
### Horizontal Scaling
Hindsight can be scaled horizontally for high-throughput scenarios:
1. **Multiple API instances**: Deploy multiple Hindsight servers behind a load balancer
2. **Shared database**: All instances connect to the same PostgreSQL database
3. **LLM provider limits**: Distribute load across multiple API keys/providers
4. **Bank isolation**: Distribute banks across different instances for better isolation
### Database Scaling
For very large deployments:
1. **Connection pooling**: Use pgBouncer for connection management
2. **Read replicas**: Use PostgreSQL read replicas for read-heavy workloads
3. **Partitioning**: Partition large banks by time or topic
4. **Vacuum and analyze**: Regular maintenance for optimal query performance
### Resource Requirements
Recommended specifications per 1M facts:
| Resource | Minimum | Recommended |
|----------|---------|-------------|
| CPU | 2 cores | 4-8 cores |
| RAM | 4GB | 8-16GB |
| Database storage | 10GB | 20GB+ (with indexes) |
| Vector index RAM | 2GB | 4GB+ |
## Benchmarks
### LoComo Benchmark Results
Hindsight has been evaluated on the LoComo (Long Context Memory) benchmark:
- **Dataset**: 10 conversations with multi-hop, temporal, and reasoning questions
- **Overall accuracy**: ~65-75% (varies by category)
- **Average recall latency**: 400-600ms (mid budget)
- **Average reflect latency**: 1500-2500ms (end-to-end)
See the [GitHub repository](https://github.com/vectorize-io/hindsight/tree/main/hindsight-dev/benchmarks) for detailed benchmark results.
### Performance Metrics
Key performance indicators to monitor:
1. **Latency percentiles**: Track p50, p95, p99 for recall/reflect operations
2. **Throughput**: Requests per second for each operation type
3. **Error rates**: Failed requests, timeouts, LLM errors
4. **Resource utilization**: CPU, memory, database connection pool usage
5. **LLM costs**: Token usage and API costs per operation
## Monitoring and Optimization
### Enable Trace Information
Use the `trace` parameter to analyze performance:
```python
result = client.recall_memories(
bank_id="my-bank",
query="test query",
trace=True
)
if result.trace:
print(f"Total time: {result.trace.get('total_time')}ms")
print(f"Activations: {result.trace.get('activation_count')}")
```
### Metrics Collection
Hindsight exposes Prometheus metrics for monitoring:
```bash
curl http://localhost:8888/metrics
```
Key metrics:
- `hindsight_recall_duration_seconds`: Recall operation latency
- `hindsight_reflect_duration_seconds`: Reflect operation latency
- `hindsight_retain_items_total`: Number of items retained
- `hindsight_database_connections`: Active database connections
### Performance Tuning
Server configuration options (environment variables):
```bash
# Database connection pool
export DB_POOL_SIZE=20
export DB_MAX_OVERFLOW=10
# LLM configuration
export LLM_PROVIDER=openai
export LLM_MAX_RETRIES=3
export LLM_TIMEOUT=30
# Search configuration
export DEFAULT_THINKING_BUDGET=500
export MAX_SEARCH_RESULTS=100
```
## Best Practices ## Best Practices
1. **Use appropriate budgets**: Don't over-provision thinking budget for simple queries ### Operations
2. **Batch operations**: Group related retains together for better efficiency - **Use appropriate budgets**: Don't over-provision for simple queries; use higher budgets for comprehensive reasoning
3. **Monitor costs**: Track LLM token usage and optimize prompts - **Batch retain operations**: Group related content together for better efficiency
4. **Cache when possible**: Cache frequently accessed queries at the application level - **Cache frequent queries**: Cache at the application level for repeated queries
5. **Clean old data**: Regularly archive or delete unused memory banks - **Profile with trace**: Use the `trace` parameter to identify slow operations
6. **Profile queries**: Use trace information to identify slow operations
7. **Load test**: Test your specific workload before production deployment
## Cost Optimization ### 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)
### LLM Token Usage ### 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
Optimize costs by controlling token usage: ### Monitoring
- **Prometheus metrics**: Available at `/metrics` — track latency percentiles, throughput, and error rates
1. **Chunk size**: Larger chunks reduce overhead but increase individual LLM calls - **Key metrics**: `hindsight_recall_duration_seconds`, `hindsight_reflect_duration_seconds`, `hindsight_retain_items_total`
2. **Max tokens**: Limit `max_tokens` to reduce response size
3. **Fact extraction**: Use efficient models (e.g., GPT-4 Mini) for retain operations
4. **Budget management**: Lower budgets reduce the number of facts processed
### Typical Costs
Example costs using OpenAI GPT-4:
| Operation | Tokens | Cost per request | Notes |
|-----------|--------|------------------|-------|
| Retain (1 item) | ~1000-2000 | $0.01-0.02 | Fact extraction |
| Recall | ~2000-8000 | $0.02-0.08 | Depends on budget |
| Reflect | ~4000-12000 | $0.04-0.12 | Search + generation |
**Note**: Costs vary significantly by model provider and configuration. Use cheaper models (GPT-4 Mini, Claude Haiku) for non-critical operations.
## Future Improvements
Planned optimizations:
- **Adaptive budgeting**: Automatically adjust thinking budget based on query complexity
- **Incremental updates**: Update facts without full re-extraction
- **Query caching**: Built-in cache for frequently accessed memories
- **Multi-modal support**: Efficient processing of images and documents
- **Distributed search**: Shard large banks across multiple databases
---
For specific performance issues or questions, please [open an issue](https://github.com/your-repo/hindsight/issues) on GitHub.

View file

@ -43,19 +43,11 @@ With reflect:
## The Reflect Process ## The Reflect Process
``` 1. **Recall** relevant memories based on the query
Query 2. **Load** the bank's personality traits and background
3. **Reason** about the memories through the personality lens
Recall relevant memories 4. **Form** new opinions with confidence scores
5. **Return** response, sources, and any new beliefs
Load bank personality
Reason with personality context
Form new opinions
Response + Sources + New Beliefs
```
--- ---
@ -125,25 +117,24 @@ Two banks with different personalities, given identical facts about remote work:
## Opinion Evolution ## Opinion Evolution
Opinions aren't static — they evolve as new evidence arrives: 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: retain("Python is widely used in ML") |-------|---------------------|----------------|
→ Opinion formed: "Python is best for data science" (confidence: 0.70) | **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) |
Day 2: retain("98% of ML engineers use Python") **Before the license change:**
→ Opinion reinforced: "Python is best for data science" (confidence: 0.85) > "Should we use Redis for our caching layer?"
> → "Yes, Redis is the industry standard — fast, battle-tested, and fully open source."
Day 3: retain("Julia is 10x faster for numerical computing") **After the license change:**
→ Opinion revised: "Python is best for data science due to ecosystem, > "Should we use Redis for our caching layer?"
though Julia excels in performance" (confidence: 0.75) > → "It depends. For cloud deployments, consider Valkey (the BSD-licensed fork). For on-premise, Redis remains excellent technically."
Day 4: retain("Rust ML libraries growing rapidly") This **continuous learning** ensures recommendations stay current with real-world changes.
→ Opinion updated: "Python remains dominant but Rust is gaining ground
for production systems" (confidence: 0.60)
```
This **continuous learning** ensures opinions stay current.
--- ---
@ -168,25 +159,22 @@ When you call `reflect()`:
**Returns:** **Returns:**
- **Response text** — Personality-influenced answer - **Response text** — Personality-influenced answer
- **Based on** — Which memories were used (with relevance scores) - **Based on** — Which memories were used (with relevance scores)
- **New opinions** — Any beliefs formed during reasoning (with confidence)
**Example:** **Example:**
```json ```json
{ {
"text": "Based on Alice's ML expertise and her work at Google, "text": "Based on Alice's ML expertise and her work at Google, she'd be an excellent fit for the research team lead position...",
she'd be an excellent fit for the research team lead position...",
"based_on": { "based_on": {
"world": [ "world": [
{"text": "Alice works at Google...", "weight": 0.95}, {"text": "Alice works at Google...", "weight": 0.95},
{"text": "Alice specializes in ML...", "weight": 0.88} {"text": "Alice specializes in ML...", "weight": 0.88}
] ]
}, }
"new_opinions": [
{"text": "Alice would excel as research team lead", "confidence": 0.82}
]
} }
``` ```
**Note:** New opinions are formed asynchronously in the background. They'll influence future `reflect()` calls but aren't returned directly.
--- ---
## Why Personality Matters ## Why Personality Matters

View file

@ -59,12 +59,12 @@ This means search results include the full context, not disconnected fragments.
## Two Types of Facts ## Two Types of Facts
Hindsight distinguishes between **world** facts (about others) and **agent** facts (about the bank itself): Hindsight distinguishes between **world** facts (about others) and **memory bank** facts (about the bank itself):
| Type | Description | Example | | Type | Description | Example |
|------|-------------|---------| |-----------------|-----------------------------------|---------|
| **world** | Facts about people, places, things | "Alice works at Google" | | **world** | Facts about people, places, things | "Alice works at Google" |
| **agent** | What the bank did or said | "I recommended Python to Alice" | | **memory bank** | What the bank did or said | "I recommended Python to Alice" |
This separation is important for `reflect()` — the bank can reason about what it knows versus what it did. This separation is important for `reflect()` — the bank can reason about what it knows versus what it did.
@ -169,8 +169,6 @@ As facts accumulate about an entity, Hindsight synthesizes **observations** —
**Why it helps:** You can quickly understand an entity without reading through dozens of individual facts. **Why it helps:** You can quickly understand an entity without reading through dozens of individual facts.
**Note:** Observations are created in the background after retain completes, so they don't slow down your writes.
--- ---
## What You Get ## What You Get

View file

@ -92,17 +92,29 @@ After the four strategies run, results are **fused together**:
## Token Budget Management ## Token Budget Management
Results are automatically filtered to fit within your context window: 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 - Top-ranked memories selected first
- Stops when token budget is exhausted - Stops when token budget is exhausted
- Ensures you get the most relevant information within constraints - You specify context budget, Hindsight fills it with the most relevant memories
**Parameters you control:** **Parameters you control:**
- `budget`: Budget level for graph traversal (low=100, mid=300, high=600 nodes)
- `max_tokens`: How much memory content to return (default: 4096 tokens) - `max_tokens`: How much memory content to return (default: 4096 tokens)
- `budget`: Budget level for graph traversal (low, mid, high)
- `fact_type`: Filter by world, agent, opinion, or all - `fact_type`: Filter by world, agent, opinion, or all
### Additional Context: Chunks and Entity Observations
For the most relevant memories, you can optionally retrieve additional context—each with its own token budget:
| Option | Parameters | 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 |
This gives your agent richer context while maintaining precise control over total token consumption.
--- ---
## How Recall Works ## How Recall Works
@ -146,24 +158,23 @@ Controls how much memory content to return:
**Example:** "Summarize everything about Alice" benefits from higher max_tokens to include more facts. **Example:** "Summarize everything about Alice" benefits from higher max_tokens to include more facts.
### Trade-off Diagram ### Two Independent Dimensions
``` Budget and max_tokens control different aspects of recall:
Quality (Recall Coverage)
| Parameter | What it controls | Latency impact | Example |
| |-----------|------------------|----------------|---------|
high budget | **Budget** | How deep to explore the graph | Search time | High budget finds Alice → manager → team → projects |
8192 tokens | **Max Tokens** | How much context to return | LLM processing time | High tokens returns more memories to the agent |
|
mid budget | **They're independent.** Common combinations:
4096 tokens |
| | Budget | Max Tokens | Use Case |
low budget | |--------|------------|----------|
2048 tokens | | high | low | Deep search, return only the best results |
| | low | high | Quick search, return everything found |
────────────────┼────────────────→ Latency (Speed) | high | high | Comprehensive research queries |
Faster Slower | low | low | Fast chatbot responses |
```
### Recommended Configurations ### Recommended Configurations

View file

@ -1,446 +0,0 @@
---
sidebar_position: 4
---
# Temporal Reasoning
Hindsight provides sophisticated temporal reasoning capabilities, allowing memory banks to understand and query memories based on when events occurred. This enables natural queries like "What did Alice do last spring?" or "What happened between March and May?"
## Overview
Temporal reasoning in Hindsight operates at two levels:
1. **Fact Storage**: Every memory can have an `event_date` timestamp indicating when the event occurred
2. **Query Analysis**: Natural language temporal expressions are automatically detected and parsed
3. **Temporal-Graph Retrieval**: A specialized retrieval strategy that filters memories by time range while maintaining entity relationships
## Storing Facts with Temporal Information
### Event Date Assignment
When ingesting facts, Hindsight extracts temporal information in the following order:
1. **Explicit `event_date` parameter**: If provided, this is used directly
2. **LLM-extracted date**: The fact extraction LLM identifies temporal markers in the content
3. **Default to storage time**: If no date is found, uses the current timestamp
```python
from hindsight_client import Hindsight
from datetime import datetime
client = Hindsight(base_url="http://localhost:8888")
# Explicit event date
client.put(
agent_id="my-agent",
content="Alice started working at Google",
event_date=datetime(2023, 3, 15)
)
# LLM will extract the date from content
client.put(
agent_id="my-agent",
content="In June 2023, Alice moved to San Francisco"
)
# Falls back to current time
client.put(
agent_id="my-agent",
content="Alice likes Python"
)
```
### LLM Temporal Extraction
During fact extraction, the LLM identifies temporal markers in natural language:
| Input Content | Extracted Event Date |
|--------------|---------------------|
| "In March 2024, Alice started a new job" | March 1, 2024 00:00:00 |
| "Last spring, Bob learned Python" | March 1 (previous year) |
| "Yesterday I met with the team" | Yesterday's date |
| "Alice works at Google" | Current timestamp (no temporal marker) |
The extracted `event_date` is stored as a timestamp with the fact:
```sql
CREATE TABLE memory_units (
id UUID PRIMARY KEY,
text TEXT NOT NULL,
event_date TIMESTAMP DEFAULT NOW(),
-- other fields...
);
```
## Query-Time Temporal Analysis
### Automatic Detection
When a search query is issued, Hindsight automatically analyzes it for temporal expressions:
```python
# These queries activate temporal-graph retrieval
results = client.search(agent_id="my-agent", query="What did Alice do last spring?")
results = client.search(agent_id="my-agent", query="What happened in June?")
results = client.search(agent_id="my-agent", query="Events from last year")
# These do NOT activate temporal retrieval
results = client.search(agent_id="my-agent", query="What does Alice do?")
results = client.search(agent_id="my-agent", query="Tell me about Python")
```
### T5-Based Parsing
Hindsight uses a T5 transformer model (`google/flan-t5-small`) to convert natural language temporal expressions into structured date ranges:
```python
# Query: "What did Alice do last spring?"
#
# T5 Input Prompt:
# "Today is 2024-11-25. Convert temporal expressions to date ranges.
# June 2024 = 2024-06-01 to 2024-06-30
# last year = 2023-01-01 to 2023-12-31
# What did Alice do last spring? ="
#
# T5 Generated Output:
# "2024-03-01 to 2024-05-31"
#
# Parsed Constraint:
# TemporalConstraint(
# start_date=datetime(2024, 3, 1, 0, 0, 0),
# end_date=datetime(2024, 5, 31, 23, 59, 59, 999999)
# )
```
### Supported Temporal Expressions
| Expression | Parsed Date Range | Notes |
|------------|------------------|-------|
| "last spring" | March 1 - May 31 (previous year) | Seasonal ranges |
| "in June" | June 1-30 (current/nearest year) | Specific month |
| "in June 2023" | June 1-30, 2023 | Month with year |
| "last year" | Jan 1 - Dec 31 (previous year) | Relative year |
| "last week" | 7 days ago - today | Relative week |
| "yesterday" | Yesterday's date (00:00 - 23:59) | Specific day |
| "between March and May" | March 1 - May 31 (current year) | Date range |
| "March 2023" | March 1-31, 2023 | Month and year |
| "2023" | Jan 1 - Dec 31, 2023 | Year only |
### No Temporal Expression
If no temporal expression is detected, the query analysis returns `None` and standard retrieval (without temporal filtering) is used:
```python
# No temporal expression detected
query = "What does Alice do?"
temporal_constraint = extract_temporal_constraint(query)
# Returns: None
```
## Temporal-Graph Retrieval Strategy
When a temporal constraint is detected, Hindsight activates the **Temporal-Graph** retrieval strategy as a 4th parallel search path.
### How It Works
```mermaid
graph TD
Q[Query: What did Alice do last spring?]
Q --> T5[T5 Temporal Parser]
T5 --> TC[TemporalConstraint<br/>2024-03-01 to 2024-05-31]
TC --> VS[Vector Similarity<br/>Find semantic entry points]
VS --> F[Filter by event_date]
F --> GT[Graph Traversal<br/>Through time-filtered entities]
GT --> R[Ranked Results]
```
### Algorithm Steps
```python
def retrieve_temporal(
query_embedding,
agent_id,
start_date,
end_date,
budget,
semantic_threshold=0.4
):
"""
Temporal-Graph retrieval combines time filtering with entity relationships.
Steps:
1. Find semantic entry points (similarity >= threshold)
WHERE event_date BETWEEN start_date AND end_date
2. Initialize activation scores from similarity scores
3. Graph traversal (spreading activation):
- Follow entity links to related facts
- ONLY traverse to facts within the time range
- Propagate activation with decay (0.8x per hop)
- Boost causal links (2x activation)
4. Return facts sorted by activation score
"""
```
### SQL Query Example
```sql
-- Step 1: Find temporal entry points
WITH temporal_candidates AS (
SELECT
mu.id,
mu.text,
mu.event_date,
1 - (mu.embedding <=> $query_embedding) AS similarity
FROM memory_units mu
WHERE mu.agent_id = $agent_id
AND mu.fact_type = ANY($fact_types)
AND mu.event_date >= $start_date
AND mu.event_date <= $end_date
AND (1 - (mu.embedding <=> $query_embedding)) >= $semantic_threshold
ORDER BY similarity DESC
LIMIT 10
)
-- Step 2 & 3: Graph traversal happens in Python code
-- walking through memory_links while enforcing temporal bounds
SELECT * FROM temporal_candidates;
```
### Key Differences from Standard Graph Traversal
| Aspect | Standard Graph | Temporal-Graph |
|--------|---------------|----------------|
| **Entry Points** | Top-K by similarity (any time) | Top-K by similarity **within time range** |
| **Traversal** | Follow all entity links | Only follow links to **facts within time range** |
| **Use Case** | "What does Alice do?" | "What did Alice do last spring?" |
| **Activation Source** | Pure semantic similarity | Time-filtered semantic similarity |
### Example Walkthrough
**Query**: "What did Alice do last spring?"
**Step 1 - Temporal Parsing:**
```python
temporal_constraint = extract_temporal_constraint(query)
# Result: (2024-03-01 00:00:00, 2024-05-31 23:59:59)
```
**Step 2 - Find Entry Points:**
```
Semantic search for "What did Alice do"
WHERE event_date BETWEEN '2024-03-01' AND '2024-05-31'
Found:
- "Alice started learning Rust" (April 15, 2024, similarity: 0.78)
- "Alice attended ML conference" (May 3, 2024, similarity: 0.72)
```
**Step 3 - Graph Traversal (time-filtered):**
```
From "Alice started learning Rust":
→ Alice (entity)
→ "Alice joined Rust meetup" (April 20, 2024) ✓ within range
→ "Alice published Rust blog post" (April 28, 2024) ✓ within range
→ "Alice works at Google" (Jan 2023) ✗ outside range, skip
From "Alice attended ML conference":
→ ML Conference (entity)
→ "Conference keynote on LLMs" (May 3, 2024) ✓ within range
→ Alice (entity)
→ Same as above
```
**Step 4 - Activation Scores:**
```
1. "Alice started learning Rust" (0.78) - direct match
2. "Alice attended ML conference" (0.72) - direct match
3. "Alice published Rust blog post" (0.78 × 0.8 = 0.62) - 1 hop
4. "Alice joined Rust meetup" (0.78 × 0.8 = 0.62) - 1 hop
5. "Conference keynote on LLMs" (0.72 × 0.8 = 0.58) - 1 hop
```
## Integration with Multi-Strategy Retrieval
Temporal-Graph runs **in parallel** with the other three strategies:
```python
# When temporal constraint detected:
results = await asyncio.gather(
retrieve_semantic(query_embedding, agent_id, fact_types),
retrieve_bm25(query, agent_id, fact_types),
retrieve_graph(query_embedding, agent_id, fact_types, budget),
retrieve_temporal(query_embedding, agent_id, fact_types,
start_date, end_date, budget) # 4th strategy
)
# Fusion via RRF
fused = reciprocal_rank_fusion(results)
# Rerank with temporal awareness
final = cross_encoder_rerank(query, fused, include_dates=True)
```
The 4th strategy (Temporal-Graph) contributes additional ranked results to the fusion process, ensuring that time-relevant facts are strongly represented in the final results.
## Cross-Encoder Temporal Awareness
The cross-encoder reranker receives temporal context for more accurate relevance scoring:
```python
# Standard input:
cross_encoder.predict([
(query, memory_text)
])
# With temporal awareness:
cross_encoder.predict([
(query, f"[Date: {date_readable}] {memory_text}")
])
# Example:
# Query: "What did Alice do last spring?"
# Input to cross-encoder:
# ("What did Alice do last spring?",
# "[Date: April 15, 2024] Alice started learning Rust")
```
This allows the reranker to boost facts that align with the query's temporal intent.
## Performance Considerations
### Latency
| Component | Typical Latency | Notes |
|-----------|----------------|-------|
| T5 temporal parsing | ~30-80ms (CPU) | Cached for repeated queries |
| Temporal entry point search | ~25-40ms | PostgreSQL query with time+vector filter |
| Time-filtered graph traversal | ~35-60ms | Similar to standard graph, fewer candidates |
| **Total temporal overhead** | **~50-120ms** | Only when temporal expressions detected |
### Optimization Tips
1. **Event Date Indexing**: Ensure `event_date` column is indexed:
```sql
CREATE INDEX idx_memory_units_event_date
ON memory_units (event_date)
WHERE event_date IS NOT NULL;
```
2. **Composite Index**: For frequent temporal queries:
```sql
CREATE INDEX idx_memory_units_agent_temporal
ON memory_units (agent_id, event_date, fact_type);
```
3. **T5 Model Caching**: The model is loaded once and reused across queries
## API Reference
### Python Client
```python
from hindsight_client import Hindsight
from datetime import datetime
client = Hindsight(base_url="http://localhost:8888")
# Store with explicit event date
client.put(
agent_id="my-agent",
content="Event content",
event_date=datetime(2024, 3, 15)
)
# Search with natural language temporal expression
results = client.search(
agent_id="my-agent",
query="What happened last spring?"
)
# Results include event_date
for r in results:
print(f"{r['text']} (date: {r['event_date']})")
```
### REST API
```bash
# Store with event date
curl -X POST http://localhost:8888/api/v1/put \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"content": "Alice started new job",
"event_date": "2024-03-15T10:00:00Z"
}'
# Search (temporal parsing automatic)
curl -X POST http://localhost:8888/api/v1/search \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"query": "What did Alice do last spring?"
}'
```
## Advanced: Temporal Ranges in Memory bank Profiles
Memory banks can have temporal ranges that represent their active periods or episodic boundaries:
```python
# Create agent with temporal range
client.create_agent(
agent_id="my-agent",
name="Alice",
temporal_ranges=[
{
"start_date": "2023-01-01T00:00:00Z",
"end_date": "2023-12-31T23:59:59Z",
"description": "2023 academic year"
},
{
"start_date": "2024-01-01T00:00:00Z",
"end_date": "2024-12-31T23:59:59Z",
"description": "2024 academic year"
}
]
)
```
These ranges can be used to:
- Segment memories into distinct time periods
- Filter retrieval to specific life phases
- Support episodic memory queries
## Limitations and Future Work
### Current Limitations
1. **Relative Dates**: "yesterday", "last week" are resolved relative to query time, not event time
2. **Duration Expressions**: "for 3 months" or "during the conference" not fully supported
3. **Fuzzy Temporal**: "around June" or "early spring" treated as exact boundaries
4. **Recurring Events**: "every Monday" or "annual conference" not handled specially
### Planned Enhancements
- Duration-based queries ("events lasting more than a week")
- Temporal relationship extraction ("after Alice joined Google", "before the conference")
- Recurrence pattern recognition
- Relative temporal reasoning ("what happened next")
## Summary
Hindsight's temporal reasoning capabilities enable natural, time-aware memory queries:
**Automatic fact timestamping** - LLM extracts dates from content
**Natural language parsing** - T5 converts "last spring" to date ranges
**Temporal-Graph retrieval** - 4th parallel strategy for time-filtered search
**Cross-encoder awareness** - Reranker considers temporal alignment
**Low overhead** - Only activates when temporal expressions detected
This allows memory banks to answer complex temporal queries like:
- "What did I learn last quarter?"
- "What was Alice working on in March?"
- "Events between the conference and the project launch"

View file

@ -8,23 +8,8 @@ The Hindsight CLI provides command-line access to memory operations and agent ma
## Installation ## Installation
### Pre-built Binaries
Download from the releases page:
```bash ```bash
# macOS (Apple Silicon) curl -fsSL https://raw.githubusercontent.com/vectorize-io/hindsight/refs/heads/main/hindsight-cli/install.sh | bash
curl -L https://github.com/hindsight/hindsight/releases/latest/download/hindsight-macos-arm64 -o hindsight
chmod +x hindsight
sudo mv hindsight /usr/local/bin/
```
### Build from Source
```bash
cd hindsight-cli-rust
cargo build --release
cp target/release/hindsight /usr/local/bin/
``` ```
## Configuration ## Configuration

View file

@ -9,13 +9,13 @@ Official TypeScript/JavaScript client for the Hindsight API.
## Installation ## Installation
```bash ```bash
npm install @hindsight/client npm install @vectorize-io/hindsight-client
``` ```
## Quick Start ## Quick Start
```typescript ```typescript
import { OpenAPI, MemoryStorageService, SearchService } from '@hindsight/client'; import { OpenAPI, MemoryStorageService, SearchService } from '@vectorize-io/hindsight-client';
// Configure base URL // Configure base URL
OpenAPI.BASE = 'http://localhost:8888'; OpenAPI.BASE = 'http://localhost:8888';
@ -38,7 +38,7 @@ console.log(results);
## Configuration ## Configuration
```typescript ```typescript
import { OpenAPI } from '@hindsight/client'; import { OpenAPI } from '@vectorize-io/hindsight-client';
OpenAPI.BASE = 'http://localhost:8888'; OpenAPI.BASE = 'http://localhost:8888';
OpenAPI.TOKEN = 'your-api-token'; // If authentication is enabled OpenAPI.TOKEN = 'your-api-token'; // If authentication is enabled
@ -49,7 +49,7 @@ OpenAPI.TOKEN = 'your-api-token'; // If authentication is enabled
### Store Memory ### Store Memory
```typescript ```typescript
import { MemoryStorageService } from '@hindsight/client'; import { MemoryStorageService } from '@vectorize-io/hindsight-client';
await MemoryStorageService.putApiPutPost({ await MemoryStorageService.putApiPutPost({
agent_id: 'my-agent', agent_id: 'my-agent',
@ -77,7 +77,7 @@ await MemoryStorageService.batchApiMemoriesBatchPost({
### Basic Search ### Basic Search
```typescript ```typescript
import { SearchService } from '@hindsight/client'; import { SearchService } from '@vectorize-io/hindsight-client';
const results = await SearchService.searchApiSearchPost({ const results = await SearchService.searchApiSearchPost({
agent_id: 'my-agent', agent_id: 'my-agent',
@ -121,7 +121,7 @@ const opinions = await SearchService.opinionSearchApiOpinionSearchPost({
## Reflect (Generate Response) ## Reflect (Generate Response)
```typescript ```typescript
import { ReasoningService } from '@hindsight/client'; import { ReasoningService } from '@vectorize-io/hindsight-client';
const response = await ReasoningService.reflectApiReflectPost({ const response = await ReasoningService.reflectApiReflectPost({
bank_id: 'my-agent', bank_id: 'my-agent',
@ -139,7 +139,7 @@ console.log(response.new_opinions); // New opinions formed
### Create Memory bank ### Create Memory bank
```typescript ```typescript
import { ManagementService } from '@hindsight/client'; import { ManagementService } from '@vectorize-io/hindsight-client';
await ManagementService.createAgentApiAgentsAgentIdPut('my-agent', { await ManagementService.createAgentApiAgentsAgentIdPut('my-agent', {
name: 'Assistant', name: 'Assistant',
@ -192,7 +192,7 @@ await ManagementService.mergeBackgroundApiAgentsAgentIdBackgroundPost('my-agent'
## Error Handling ## Error Handling
```typescript ```typescript
import { ApiError } from '@hindsight/client'; import { ApiError } from '@vectorize-io/hindsight-client';
try { try {
await SearchService.searchApiSearchPost({ await SearchService.searchApiSearchPost({
@ -218,7 +218,7 @@ import type {
ThinkResponse, ThinkResponse,
MemoryItem, MemoryItem,
PersonalityTraits, PersonalityTraits,
} from '@hindsight/client'; } from '@vectorize-io/hindsight-client';
const personality: PersonalityTraits = { const personality: PersonalityTraits = {
openness: 0.7, openness: 0.7,

View file

@ -186,6 +186,30 @@ const config: Config = {
darkTheme: prismThemes.dracula, darkTheme: prismThemes.dracula,
additionalLanguages: ['bash', 'json', 'python', 'rust'], additionalLanguages: ['bash', 'json', 'python', 'rust'],
}, },
mermaid: {
theme: {
light: 'base',
dark: 'dark',
},
options: {
themeVariables: {
primaryColor: '#6366f1',
primaryTextColor: '#ffffff',
primaryBorderColor: '#4f46e5',
secondaryColor: '#f1f5f9',
secondaryTextColor: '#1e293b',
secondaryBorderColor: '#cbd5e1',
tertiaryColor: '#e0e7ff',
lineColor: '#94a3b8',
textColor: '#1e293b',
mainBkg: '#ffffff',
nodeBorder: '#4f46e5',
clusterBkg: '#f8fafc',
clusterBorder: '#e2e8f0',
fontFamily: 'system-ui, -apple-system, sans-serif',
},
},
},
} satisfies Preset.ThemeConfig, } satisfies Preset.ThemeConfig,
}; };

View file

@ -40,11 +40,6 @@ const sidebars: SidebarsConfig = {
label: 'Getting Started', label: 'Getting Started',
collapsible: false, collapsible: false,
items: [ items: [
{
type: 'doc',
id: 'developer/api/installation',
label: 'Installation',
},
{ {
type: 'doc', type: 'doc',
id: 'developer/api/quickstart', id: 'developer/api/quickstart',
@ -119,6 +114,11 @@ const sidebars: SidebarsConfig = {
id: 'developer/metrics', id: 'developer/metrics',
label: 'Metrics', label: 'Metrics',
}, },
{
type: 'doc',
id: 'developer/mcp-server',
label: 'MCP Server',
},
], ],
}, },
], ],

View file

@ -23,9 +23,6 @@ test = [
"pytest-asyncio>=0.21.0", "pytest-asyncio>=0.21.0",
] ]
[project.scripts]
hindsight-api = "hindsight.cli:main"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["hindsight"] packages = ["hindsight"]