docs, packages and quick start
This commit is contained in:
parent
9b69202525
commit
6073ac4ffd
38 changed files with 692 additions and 2831 deletions
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
|
|
@ -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 }}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
# Hindsight
|
# Hindsight
|
||||||
|
|
||||||
|
[](https://github.com/vectorize-io/hindsight/actions/workflows/test.yml)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://pypi.org/project/hindsight-client/)
|
[](https://pypi.org/project/hindsight-client/)
|
||||||
|
[](https://pypi.org/project/hindsight-api/)
|
||||||
[](https://pypi.org/project/hindsight-all/)
|
[](https://pypi.org/project/hindsight-all/)
|
||||||
[](https://www.npmjs.com/package/@vectorize-io/hindsight-client)
|
[](https://www.npmjs.com/package/@vectorize-io/hindsight-client)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
4
hindsight-clients/typescript/package-lock.json
generated
4
hindsight-clients/typescript/package-lock.json
generated
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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' }));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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' }));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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' });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
115
hindsight-docs/docs/developer/mcp-server.md
Normal file
115
hindsight-docs/docs/developer/mcp-server.md
Normal 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).
|
||||||
|
|
@ -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.
|
|
||||||
|
|
|
||||||
|
|
@ -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) |
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue