fix docs on clients

This commit is contained in:
Nicolò Boschi 2025-12-11 14:23:56 +01:00
parent ebc85a5c3d
commit 99db7b26c3
22 changed files with 372 additions and 476 deletions

View file

@ -219,6 +219,9 @@ jobs:
release-helm-chart: release-helm-chart:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -228,12 +231,18 @@ jobs:
with: with:
version: 'latest' version: 'latest'
- name: Log in to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Lint Helm chart - name: Lint Helm chart
run: helm lint helm/hindsight run: helm lint helm/hindsight
- name: Package Helm chart - name: Package Helm chart
run: helm package helm/hindsight --destination ./helm-packages run: helm package helm/hindsight --destination ./helm-packages
- name: Push to GHCR OCI
run: helm push helm-packages/*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:

View file

@ -66,7 +66,7 @@ UI: http://localhost:9999
Install client: Install client:
```bash ```bash
pip install hindsight-client pip install hindsight-client -U
# or # or
npm install @vectorize-io/hindsight-client npm install @vectorize-io/hindsight-client
``` ```
@ -91,7 +91,7 @@ client.reflect(bank_id="my-bank", query="Tell me about Alice")
### Python (embedded, no Docker) ### Python (embedded, no Docker)
```bash ```bash
pip install hindsight-all pip install hindsight-all -U
``` ```
```python ```python
@ -100,27 +100,27 @@ from hindsight import HindsightServer, HindsightClient
with HindsightServer( with HindsightServer(
llm_provider="openai", llm_provider="openai",
llm_model="gpt-4o-mini", llm_model="gpt-5-mini",
llm_api_key=os.environ["OPENAI_API_KEY"] llm_api_key=os.environ["OPENAI_API_KEY"]
) as server: ) as server:
client = HindsightClient(base_url=server.url) client = HindsightClient(base_url=server.url)
client.retain(bank_id="my-agent", content="Alice works at Google") client.retain(bank_id="my-bank", content="Alice works at Google")
results = client.recall(bank_id="my-agent", query="Where does Alice work?") results = client.recall(bank_id="my-bank", query="Where does Alice work?")
``` ```
### TypeScript ### Node.js / TypeScript
```bash ```bash
npm install @vectorize-io/hindsight-client npm install @vectorize-io/hindsight-client
``` ```
```typescript ```javascript
import { HindsightClient } from '@vectorize-io/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' });
await client.retain('my-agent', 'Alice loves hiking in Yosemite'); await client.retain('my-bank', 'Alice loves hiking in Yosemite');
const response = await client.recall('my-agent', 'What does Alice like?'); await client.recall('my-bank', 'What does Alice like?');
``` ```
--- ---
@ -168,9 +168,7 @@ client = Hindsight(base_url="http://localhost:8888")
client.recall(bank_id="my-bank", query="What does Alice do?") client.recall(bank_id="my-bank", query="What does Alice do?")
# Temporal # Temporal
results = client.recall(bank_id="my-bank", query="What happened in June?") client.recall(bank_id="my-bank", query="What happened in June?")
``` ```
Recall performs 4 retrieval strategies in parallel: Recall performs 4 retrieval strategies in parallel:

View file

@ -177,6 +177,7 @@ ENV HINDSIGHT_API_PORT=8888
ENV HINDSIGHT_API_LOG_LEVEL=info ENV HINDSIGHT_API_LOG_LEVEL=info
ENV HINDSIGHT_ENABLE_API=true ENV HINDSIGHT_ENABLE_API=true
ENV HINDSIGHT_ENABLE_CP=false ENV HINDSIGHT_ENABLE_CP=false
ENV PYTHONUNBUFFERED=1
CMD ["/app/start-all.sh"] CMD ["/app/start-all.sh"]
@ -311,6 +312,7 @@ ENV NODE_ENV=production
ENV HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888 ENV HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888
ENV HINDSIGHT_ENABLE_API=true ENV HINDSIGHT_ENABLE_API=true
ENV HINDSIGHT_ENABLE_CP=true ENV HINDSIGHT_ENABLE_CP=true
ENV PYTHONUNBUFFERED=1
CMD ["/app/start-all.sh"] CMD ["/app/start-all.sh"]

View file

@ -23,7 +23,7 @@ PIDS=()
# Start API if enabled # Start API if enabled
if [ "$ENABLE_API" = "true" ]; then if [ "$ENABLE_API" = "true" ]; then
cd /app/api cd /app/api
hindsight-api 2>&1 | sed 's/^/[api] /' & hindsight-api 2>&1 | sed -u 's/^/[api] /' &
API_PID=$! API_PID=$!
PIDS+=($API_PID) PIDS+=($API_PID)
@ -42,7 +42,7 @@ fi
if [ "$ENABLE_CP" = "true" ]; then if [ "$ENABLE_CP" = "true" ]; then
echo "🎛️ Starting Control Plane..." echo "🎛️ Starting Control Plane..."
cd /app/control-plane cd /app/control-plane
PORT=9999 node server.js 2>&1 | grep -v -E "^[[:space:]]*(▲|✓|-|$)" | sed 's/^/[control-plane] /' & PORT=9999 node server.js 2>&1 | grep -v -E "^[[:space:]]*(▲|✓|-|$)" | sed -u 's/^/[control-plane] /' &
CP_PID=$! CP_PID=$!
PIDS+=($CP_PID) PIDS+=($CP_PID)
else else

View file

@ -170,24 +170,38 @@ class LLMProvider:
"messages": messages, "messages": messages,
} }
if max_completion_tokens is not None:
call_params["max_completion_tokens"] = max_completion_tokens
# Check if model supports reasoning parameter (o1, o3, gpt-5 families) # Check if model supports reasoning parameter (o1, o3, gpt-5 families)
model_lower = self.model.lower() model_lower = self.model.lower()
is_reasoning_model = any(x in model_lower for x in ["gpt-5", "o1", "o3"]) is_reasoning_model = any(x in model_lower for x in ["gpt-5", "o1", "o3"])
# For GPT-4 and GPT-4.1 models, cap max_completion_tokens to 32000
is_gpt4_model = any(x in model_lower for x in ["gpt-4.1", "gpt-4-"])
if max_completion_tokens is not None:
if is_gpt4_model and max_completion_tokens > 32000:
max_completion_tokens = 32000
# For reasoning models, max_completion_tokens includes reasoning + output tokens
# Enforce minimum of 16000 to ensure enough space for both
if is_reasoning_model and max_completion_tokens < 16000:
max_completion_tokens = 16000
call_params["max_completion_tokens"] = max_completion_tokens
# GPT-5/o1/o3 family doesn't support custom temperature (only default 1) # GPT-5/o1/o3 family doesn't support custom temperature (only default 1)
if temperature is not None and not is_reasoning_model: if temperature is not None and not is_reasoning_model:
call_params["temperature"] = temperature call_params["temperature"] = temperature
# Set reasoning_effort for reasoning models (OpenAI gpt-5, o1, o3)
if is_reasoning_model and self.provider == "openai":
call_params["reasoning_effort"] = self.reasoning_effort
# Provider-specific parameters # Provider-specific parameters
if self.provider == "groq": if self.provider == "groq":
call_params["seed"] = DEFAULT_LLM_SEED call_params["seed"] = DEFAULT_LLM_SEED
call_params["extra_body"] = { extra_body = {"service_tier": "auto"}
"service_tier": "auto", # Only add reasoning parameters for reasoning models
"reasoning_effort": self.reasoning_effort, if is_reasoning_model:
"include_reasoning": False, extra_body["reasoning_effort"] = self.reasoning_effort
} extra_body["include_reasoning"] = False
call_params["extra_body"] = extra_body
last_exception = None last_exception = None

View file

@ -10,36 +10,30 @@ from hindsight_api.engine.llm_wrapper import LLMProvider
MODEL_MATRIX = [ MODEL_MATRIX = [
# OpenAI models # OpenAI models
("openai", "gpt-4o-mini"), ("openai", "gpt-4o-mini"),
("openai", "gpt-4.1-mini"),
("openai", "gpt-4.1-nano"),
("openai", "gpt-5-mini"), ("openai", "gpt-5-mini"),
("openai", "gpt-5-nano"),
("openai", "gpt-5"),
# Groq models # Groq models
("groq", "llama-3.3-70b-versatile"), ("groq", "llama-3.3-70b-versatile"),
("groq", "openai/gpt-oss-120b"), ("groq", "openai/gpt-oss-120b"),
("groq", "openai/gpt-oss-20b"),
# Gemini models # Gemini models
("gemini", "gemini-2.0-flash"), ("gemini", "gemini-2.5-flash"),
("gemini", "gemini-2.5-flash-preview-05-20"), ("gemini", "gemini-2.5-flash-lite"),
] ]
def get_api_key_for_provider(provider: str) -> str | None: def get_api_key_for_provider(provider: str) -> str | None:
"""Get API key for provider from environment variables.""" """Get API key for provider from environment variables."""
# Try provider-specific env vars first
provider_key_map = { provider_key_map = {
"openai": ["OPENAI_API_KEY", "HINDSIGHT_API_LLM_API_KEY"], "openai": "OPENAI_API_KEY",
"groq": ["GROQ_API_KEY", "HINDSIGHT_API_LLM_API_KEY"], "groq": "GROQ_API_KEY",
"gemini": ["GEMINI_API_KEY", "GOOGLE_API_KEY", "HINDSIGHT_API_LLM_API_KEY"], "gemini": "GEMINI_API_KEY",
} }
env_var = provider_key_map.get(provider)
for env_var in provider_key_map.get(provider, []): return os.getenv(env_var) if env_var else None
key = os.getenv(env_var)
if key:
# For HINDSIGHT_API_LLM_API_KEY, only use if provider matches
if env_var == "HINDSIGHT_API_LLM_API_KEY":
configured_provider = os.getenv("HINDSIGHT_API_LLM_PROVIDER", "").lower()
if configured_provider == provider:
return key
else:
return key
return None
@pytest.mark.parametrize("provider,model", MODEL_MATRIX) @pytest.mark.parametrize("provider,model", MODEL_MATRIX)
@ -97,8 +91,10 @@ async def test_llm_provider_verify_connection(provider: str, model: str):
# Models that support large output (65000+ tokens) # Models that support large output (65000+ tokens)
LARGE_OUTPUT_MODELS = [ LARGE_OUTPUT_MODELS = [
("openai", "gpt-5-mini"), ("openai", "gpt-5-mini"),
("gemini", "gemini-2.0-flash"), ("openai", "gpt-5-nano"),
("gemini", "gemini-2.5-flash-preview-05-20"), ("openai", "gpt-5"),
("gemini", "gemini-2.5-flash"),
("gemini", "gemini-2.5-flash-lite"),
] ]

View file

@ -15,8 +15,8 @@ Example:
print(result.success) print(result.success)
# Search memories # Search memories
results = client.recall(bank_id="alice", query="What does Alice like?") response = client.recall(bank_id="alice", query="What does Alice like?")
for r in results: for r in response.results:
print(r.text) print(r.text)
# Generate contextual answer # Generate contextual answer
@ -29,14 +29,59 @@ from .hindsight_client import Hindsight
# Re-export response types for convenient access # Re-export response types for convenient access
from hindsight_client_api.models.retain_response import RetainResponse from hindsight_client_api.models.retain_response import RetainResponse
from hindsight_client_api.models.recall_response import RecallResponse from hindsight_client_api.models.recall_response import RecallResponse as _RecallResponse
from hindsight_client_api.models.recall_result import RecallResult from hindsight_client_api.models.recall_result import RecallResult as _RecallResult
from hindsight_client_api.models.reflect_response import ReflectResponse from hindsight_client_api.models.reflect_response import ReflectResponse
from hindsight_client_api.models.reflect_fact import ReflectFact 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.list_memory_units_response import ListMemoryUnitsResponse
from hindsight_client_api.models.bank_profile_response import BankProfileResponse from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.models.disposition_traits import DispositionTraits from hindsight_client_api.models.disposition_traits import DispositionTraits
# Add cleaner __repr__ and __iter__ for REPL usability
def _recall_result_repr(self):
text_preview = self.text[:80] + "..." if len(self.text) > 80 else self.text
return f"RecallResult(id='{self.id[:8]}...', type='{self.type}', text='{text_preview}')"
def _recall_response_repr(self):
count = len(self.results) if self.results else 0
extras = []
if self.trace:
extras.append("trace=True")
if self.entities:
extras.append(f"entities={len(self.entities)}")
if self.chunks:
extras.append(f"chunks={len(self.chunks)}")
extras_str = ", " + ", ".join(extras) if extras else ""
return f"RecallResponse({count} results{extras_str})"
def _recall_response_iter(self):
"""Iterate directly over results for convenience."""
return iter(self.results or [])
def _recall_response_len(self):
"""Return number of results."""
return len(self.results) if self.results else 0
def _recall_response_getitem(self, index):
"""Access results by index."""
return self.results[index]
_RecallResult.__repr__ = _recall_result_repr
_RecallResponse.__repr__ = _recall_response_repr
_RecallResponse.__iter__ = _recall_response_iter
_RecallResponse.__len__ = _recall_response_len
_RecallResponse.__getitem__ = _recall_response_getitem
# Re-export with patched repr
RecallResult = _RecallResult
RecallResponse = _RecallResponse
__all__ = [ __all__ = [
"Hindsight", "Hindsight",
# Response types # Response types

View file

@ -50,7 +50,9 @@ class Hindsight:
client.retain(bank_id="alice", content="Alice loves AI") client.retain(bank_id="alice", content="Alice loves AI")
# Recall memories # Recall memories
results = client.recall(bank_id="alice", query="What does Alice like?") response = client.recall(bank_id="alice", query="What does Alice like?")
for r in response.results:
print(r.text)
# Generate contextual answer # Generate contextual answer
answer = client.reflect(bank_id="alice", query="What are my interests?") answer = client.reflect(bank_id="alice", query="What are my interests?")
@ -125,8 +127,8 @@ class Hindsight:
Args: Args:
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'
document_id: Optional document ID for grouping memories document_id: Optional document ID for grouping memories (applied to items that don't have their own)
retain_async: If True, process asynchronously in background (default: False) retain_async: If True, process asynchronously in background (default: False)
Returns: Returns:
@ -138,13 +140,14 @@ class Hindsight:
timestamp=item.get("timestamp"), timestamp=item.get("timestamp"),
context=item.get("context"), context=item.get("context"),
metadata=item.get("metadata"), metadata=item.get("metadata"),
# Use item's document_id if provided, otherwise fall back to batch-level document_id
document_id=item.get("document_id") or document_id,
) )
for item in items for item in items
] ]
request_obj = retain_request.RetainRequest( request_obj = retain_request.RetainRequest(
items=memory_items, items=memory_items,
document_id=document_id,
async_=retain_async, async_=retain_async,
) )
@ -161,6 +164,8 @@ class Hindsight:
query_timestamp: Optional[str] = None, query_timestamp: Optional[str] = None,
include_entities: bool = False, include_entities: bool = False,
max_entity_tokens: int = 500, max_entity_tokens: int = 500,
include_chunks: bool = False,
max_chunk_tokens: int = 8192,
) -> RecallResponse: ) -> RecallResponse:
""" """
Recall memories using semantic similarity. Recall memories using semantic similarity.
@ -175,14 +180,17 @@ class Hindsight:
query_timestamp: Optional ISO format date string (e.g., '2023-05-30T23:40:00') query_timestamp: Optional ISO format date string (e.g., '2023-05-30T23:40:00')
include_entities: Include entity observations in results (default: False) include_entities: Include entity observations in results (default: False)
max_entity_tokens: Maximum tokens for entity observations (default: 500) max_entity_tokens: Maximum tokens for entity observations (default: 500)
include_chunks: Include raw text chunks in results (default: False)
max_chunk_tokens: Maximum tokens for chunks (default: 8192)
Returns: Returns:
RecallResponse with results, optional entities, and optional trace RecallResponse with results, optional entities, optional chunks, 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, chunk_include_options
include_opts = include_options.IncludeOptions( include_opts = include_options.IncludeOptions(
entities=entity_include_options.EntityIncludeOptions(max_tokens=max_entity_tokens) if include_entities else None entities=entity_include_options.EntityIncludeOptions(max_tokens=max_entity_tokens) if include_entities else None,
chunks=chunk_include_options.ChunkIncludeOptions(max_tokens=max_chunk_tokens) if include_chunks else None,
) )
request_obj = recall_request.RecallRequest( request_obj = recall_request.RecallRequest(
@ -277,8 +285,8 @@ class Hindsight:
Args: Args:
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'
document_id: Optional document ID for grouping memories document_id: Optional document ID for grouping memories (applied to items that don't have their own)
retain_async: If True, process asynchronously in background (default: False) retain_async: If True, process asynchronously in background (default: False)
Returns: Returns:
@ -290,13 +298,14 @@ class Hindsight:
timestamp=item.get("timestamp"), timestamp=item.get("timestamp"),
context=item.get("context"), context=item.get("context"),
metadata=item.get("metadata"), metadata=item.get("metadata"),
# Use item's document_id if provided, otherwise fall back to batch-level document_id
document_id=item.get("document_id") or document_id,
) )
for item in items for item in items
] ]
request_obj = retain_request.RetainRequest( request_obj = retain_request.RetainRequest(
items=memory_items, items=memory_items,
document_id=document_id,
async_=retain_async, async_=retain_async,
) )

View file

@ -128,7 +128,17 @@ export class HindsightClient {
async recall( async recall(
bankId: string, bankId: string,
query: string, query: string,
options?: { types?: string[]; maxTokens?: number; budget?: Budget; trace?: boolean } options?: {
types?: string[];
maxTokens?: number;
budget?: Budget;
trace?: boolean;
queryTimestamp?: string;
includeEntities?: boolean;
maxEntityTokens?: number;
includeChunks?: boolean;
maxChunkTokens?: number;
}
): Promise<RecallResponse> { ): Promise<RecallResponse> {
const response = await sdk.recallMemories({ const response = await sdk.recallMemories({
client: this.client, client: this.client,
@ -139,6 +149,11 @@ export class HindsightClient {
max_tokens: options?.maxTokens, max_tokens: options?.maxTokens,
budget: options?.budget || 'mid', budget: options?.budget || 'mid',
trace: options?.trace, trace: options?.trace,
query_timestamp: options?.queryTimestamp,
include: {
entities: options?.includeEntities ? { max_tokens: options?.maxEntityTokens ?? 500 } : undefined,
chunks: options?.includeChunks ? { max_tokens: options?.maxChunkTokens ?? 8192 } : undefined,
},
}, },
}); });

View file

@ -1,153 +0,0 @@
---
sidebar_position: 4
---
# Think vs Search
When to use `search` vs `think`.
## Quick Comparison
| | Search | Think |
|---|--------|-------|
| **Returns** | Raw memory results | Generated response |
| **Use case** | Retrieval, lookup | Q&A, reasoning |
| **LLM calls** | 0 (retrieval only) | 1+ (generation) |
| **Speed** | Fast (~100-200ms) | Slower (~500-2000ms) |
| **Opinions** | Returns existing | Can form new ones |
| **Disposition** | Not applied | Applied to response |
## When to Use Search
**Use Search when you need:**
- Raw facts for your own processing
- Fast retrieval without generation
- To populate context for another LLM
- To check what's in memory
- Debugging retrieval quality
```python
# Get raw facts to inject into your own prompt
results = client.search(agent_id="my-agent", query="Alice's preferences")
context = "\n".join([r["text"] for r in results])
# Use context in your own LLM call
```
**Examples:**
```python
# Lookup — just get the facts
results = client.search(agent_id="my-agent", query="Alice's email address")
# Context building — feed into another system
results = client.search(agent_id="my-agent", query="Recent project discussions")
context = format_for_prompt(results)
# Verification — check what's stored
results = client.search(agent_id="my-agent", query="What do I know about Bob?")
```
## When to Use Think
**Use Think when you need:**
- A natural language response
- Disposition-aware answers
- Opinion formation
- Reasoning over multiple facts
- Source attribution
```python
# Get a complete answer with disposition
answer = client.think(agent_id="my-agent", query="What should I recommend to Alice?")
print(answer["text"]) # Natural language response
print(answer["based_on"]) # Sources used
```
**Examples:**
```python
# Q&A — need a response, not just facts
answer = client.think(agent_id="my-agent", query="What does Alice do for work?")
# Reasoning — synthesize multiple facts
answer = client.think(agent_id="my-agent", query="How are Alice and Bob connected?")
# Opinion — agent forms a view
answer = client.think(agent_id="my-agent", query="What do you think about Python?")
# Recommendation — disposition-influenced
answer = client.think(agent_id="my-agent", query="What book should I read next?")
```
## Performance Comparison
```mermaid
graph LR
subgraph Search
S1[Query] --> S2[4-way Retrieval]
S2 --> S3[RRF + Rerank]
S3 --> S4[Results]
end
subgraph Think
T1[Query] --> T2[4-way Retrieval]
T2 --> T3[RRF + Rerank]
T3 --> T4[Load Disposition]
T4 --> T5[LLM Generation]
T5 --> T6[Store Opinions]
T6 --> T7[Response]
end
```
| Operation | Search | Think |
|-----------|--------|-------|
| Retrieval | ~100ms | ~100ms |
| Reranking | ~35ms | ~35ms |
| LLM Generation | — | ~500-1500ms |
| Opinion Storage | — | ~50ms |
| **Total** | **~135ms** | **~700-1700ms** |
## Hybrid Pattern
Use Search for context, Think for final response:
```python
# First: fast search to check relevance
results = client.search(agent_id="my-agent", query="Alice project status")
if len(results) > 0:
# Only call Think if we have relevant memories
answer = client.think(agent_id="my-agent", query="Summarize Alice's project status")
else:
answer = {"text": "I don't have information about Alice's projects."}
```
## Decision Flowchart
```mermaid
graph TD
A[Need memory access] --> B{Need natural language response?}
B -->|No| C[Use Search]
B -->|Yes| D{Need disposition/opinions?}
D -->|No| E{Building context for another LLM?}
E -->|Yes| C
E -->|No| F[Use Think]
D -->|Yes| F
```
## Cost Considerations
| Factor | Search | Think |
|--------|--------|-------|
| API calls | 1 | 1 |
| LLM tokens | 0 | 500-2000 |
| Latency | Low | Medium |
| Cost | Low | Higher (LLM usage) |
If you're making many requests or building a high-throughput system, consider:
- Use Search for bulk operations
- Use Think for user-facing responses
- Cache Think responses when appropriate

View file

@ -55,27 +55,29 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
**Best for**: Production deployments, auto-scaling, cloud environments **Best for**: Production deployments, auto-scaling, cloud environments
```bash ```bash
# Add Hindsight Helm repository
helm repo add hindsight https://vectorize-io.github.io/hindsight
helm repo update
# Install with built-in PostgreSQL # Install with built-in PostgreSQL
helm install hindsight hindsight/hindsight \ helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \ --set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \ --set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=true --set postgresql.enabled=true
# Or use external PostgreSQL # Or use external PostgreSQL
helm install hindsight hindsight/hindsight \ helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \ --set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \ --set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=false \ --set postgresql.enabled=false \
--set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight --set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight
# Install a specific version
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
# Upgrade to latest
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
``` ```
**Requirements**: **Requirements**:
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted) - Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
- Helm 3+ - Helm 3.8+
See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration. See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration.

View file

@ -15,21 +15,21 @@ npm install @vectorize-io/hindsight-client
## Quick Start ## Quick Start
```typescript ```typescript
import { HindsightClient } from '@vectorize-io/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 a memory // Retain a memory
await client.retain('my-agent', 'Alice works at Google'); await client.retain('my-bank', 'Alice works at Google');
// Recall memories // Recall memories
const response = await client.recall('my-agent', 'What does Alice do?'); const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) { for (const r of response.results) {
console.log(r.text); console.log(r.text);
} }
// Reflect - generate response with disposition // Reflect - generate response with disposition
const answer = await client.reflect('my-agent', 'Tell me about Alice'); const answer = await client.reflect('my-bank', 'Tell me about Alice');
console.log(answer.text); console.log(answer.text);
``` ```
@ -49,10 +49,10 @@ const client = new HindsightClient({
```typescript ```typescript
// Simple // Simple
await client.retain('my-agent', 'Alice works at Google'); await client.retain('my-bank', 'Alice works at Google');
// With options // With options
await client.retain('my-agent', 'Alice got promoted', { await client.retain('my-bank', 'Alice got promoted', {
timestamp: new Date('2024-01-15'), timestamp: new Date('2024-01-15'),
context: 'career update', context: 'career update',
metadata: { source: 'slack' }, metadata: { source: 'slack' },
@ -63,11 +63,10 @@ await client.retain('my-agent', 'Alice got promoted', {
### Retain Batch ### Retain Batch
```typescript ```typescript
await client.retainBatch('my-agent', [ await client.retainBatch('my-bank', [
{ content: 'Alice works at Google', context: 'career' }, { content: 'Alice works at Google', context: 'career' },
{ content: 'Bob is a data scientist', context: 'career' }, { content: 'Bob is a data scientist', context: 'career' },
], { ], {
documentId: 'conversation_001',
async: false, async: false,
}); });
``` ```
@ -76,31 +75,29 @@ await client.retainBatch('my-agent', [
```typescript ```typescript
// Simple - returns RecallResponse // Simple - returns RecallResponse
const response = await client.recall('my-agent', 'What does Alice do?'); const response = await client.recall('my-bank', 'What does Alice do?');
for (const r of response.results) { for (const r of response.results) {
console.log(`${r.text} (type: ${r.type})`); console.log(`${r.text} (type: ${r.type})`);
} }
// With options // With options
const response = await client.recall('my-agent', 'What does Alice do?', { const response = await client.recall('my-bank', 'What does Alice do?', {
types: ['world', 'opinion'], // Filter by fact type types: ['world', 'opinion'], // Filter by fact type
maxTokens: 4096, maxTokens: 4096,
budget: 'high', // 'low', 'mid', or 'high' budget: 'high', // 'low', 'mid', or 'high'
trace: true,
}); });
``` ```
### Reflect (Generate Response) ### Reflect (Generate Response)
```typescript ```typescript
const answer = await client.reflect('my-agent', 'What should I know about Alice?', { const answer = await client.reflect('my-bank', 'What should I know about Alice?', {
budget: 'low', // 'low', 'mid', or 'high' budget: 'low', // 'low', 'mid', or 'high'
context: 'preparing for a meeting', context: 'preparing for a meeting',
}); });
console.log(answer.text); // Generated response console.log(answer.text); // Generated response
console.log(answer.based_on); // Memories used
``` ```
## Bank Management ## Bank Management
@ -108,7 +105,7 @@ console.log(answer.based_on); // Memories used
### Create Bank ### Create Bank
```typescript ```typescript
await client.createBank('my-agent', { await client.createBank('my-bank', {
name: 'Assistant', name: 'Assistant',
background: 'I am a helpful AI assistant', background: 'I am a helpful AI assistant',
disposition: { disposition: {
@ -119,117 +116,14 @@ await client.createBank('my-agent', {
}); });
``` ```
### Get Bank Profile
```typescript
const profile = await client.getBankProfile('my-agent');
console.log(profile.disposition);
console.log(profile.background);
```
### List Memories ### List Memories
```typescript ```typescript
const response = await client.listMemories('my-agent', { const response = await client.listMemories('my-bank', {
type: 'world', // Optional filter type: 'world', // Optional filter
q: 'Alice', // Optional text search q: 'Alice', // Optional text search
limit: 100, limit: 100,
offset: 0, offset: 0,
}); });
console.log(response)
for (const memory of response.memories) {
console.log(`${memory.id}: ${memory.text}`);
}
```
## TypeScript Types
The client exports all types for full TypeScript support:
```typescript
import type {
RetainResponse,
RecallResponse,
RecallResult,
ReflectResponse,
BankProfileResponse,
Budget,
} from '@vectorize-io/hindsight-client';
// Budget is a union type: 'low' | 'mid' | 'high'
const budget: Budget = 'mid';
```
## Advanced: Low-Level SDK
For advanced use cases, access the auto-generated SDK directly:
```typescript
import { sdk, createClient, createConfig } from '@vectorize-io/hindsight-client';
const client = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));
// Use sdk functions directly
const response = await sdk.recallMemories({
client,
path: { bank_id: 'my-agent' },
body: {
query: 'What does Alice do?',
budget: 'mid',
max_tokens: 4096,
},
});
```
## Error Handling
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
try {
const response = await client.recall('unknown-agent', 'test');
} catch (error) {
console.error('Error:', error.message);
}
```
## Example: Full Workflow
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
async function main() {
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
// Create a bank with disposition
await client.createBank('demo', {
name: 'Demo Agent',
background: 'A helpful assistant for demos',
disposition: {
skepticism: 2, // Trusting
literalism: 3, // Balanced
empathy: 4, // Empathetic
},
});
// Store some memories
await client.retain('demo', 'Alice works at Google');
await client.retain('demo', 'Bob is a data scientist at Google');
await client.retain('demo', 'Alice and Bob collaborate on ML projects');
// Search for memories
const searchResults = await client.recall('demo', 'Who works at Google?');
console.log('Search results:');
for (const r of searchResults.results) {
console.log(` - ${r.text}`);
}
// Generate a response
const answer = await client.reflect('demo', 'What do you know about the team?');
console.log('\nReflection:', answer.text);
}
main().catch(console.error);
``` ```

View file

@ -49,15 +49,15 @@ with HindsightServer(
client = HindsightClient(base_url=server.url) client = HindsightClient(base_url=server.url)
# Retain a memory # Retain a memory
client.retain(bank_id="my-agent", content="Alice works at Google") client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories # Recall memories
results = client.recall(bank_id="my-agent", query="What does Alice do?") results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results: for r in results:
print(r.text) print(r.text)
# Reflect - generate response with disposition # Reflect - generate response with disposition
answer = client.reflect(bank_id="my-agent", query="Tell me about Alice") answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text) print(answer.text)
``` ```
@ -70,15 +70,15 @@ from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
# Retain a memory # Retain a memory
client.retain(bank_id="my-agent", content="Alice works at Google") client.retain(bank_id="my-bank", content="Alice works at Google")
# Recall memories # Recall memories
results = client.recall(bank_id="my-agent", query="What does Alice do?") results = client.recall(bank_id="my-bank", query="What does Alice do?")
for r in results: for r in results:
print(r.text) print(r.text)
# Reflect - generate response with disposition # Reflect - generate response with disposition
answer = client.reflect(bank_id="my-agent", query="Tell me about Alice") answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
print(answer.text) print(answer.text)
``` ```
@ -103,7 +103,7 @@ client = Hindsight(
```python ```python
# Simple # Simple
client.retain( client.retain(
bank_id="my-agent", bank_id="my-bank",
content="Alice works at Google as a software engineer", content="Alice works at Google as a software engineer",
) )
@ -111,7 +111,7 @@ client.retain(
from datetime import datetime from datetime import datetime
client.retain( client.retain(
bank_id="my-agent", bank_id="my-bank",
content="Alice got promoted", content="Alice got promoted",
context="career update", context="career update",
timestamp=datetime(2024, 1, 15), timestamp=datetime(2024, 1, 15),
@ -124,7 +124,7 @@ client.retain(
```python ```python
client.retain_batch( client.retain_batch(
bank_id="my-agent", bank_id="my-bank",
items=[ items=[
{"content": "Alice works at Google", "context": "career"}, {"content": "Alice works at Google", "context": "career"},
{"content": "Bob is a data scientist", "context": "career"}, {"content": "Bob is a data scientist", "context": "career"},
@ -139,16 +139,16 @@ client.retain_batch(
```python ```python
# Simple - returns list of RecallResult # Simple - returns list of RecallResult
results = client.recall( results = client.recall(
bank_id="my-agent", bank_id="my-bank",
query="What does Alice do?", query="What does Alice do?",
) )
for r in results: for r in results.results:
print(f"{r.text} (type: {r.type})") print(f"{r.text} (type: {r.type})")
# With options # With options
results = client.recall( results = client.recall(
bank_id="my-agent", bank_id="my-bank",
query="What does Alice do?", query="What does Alice do?",
types=["world", "opinion"], # Filter by fact type types=["world", "opinion"], # Filter by fact type
max_tokens=4096, max_tokens=4096,
@ -159,16 +159,15 @@ results = client.recall(
### Recall with Full Response ### Recall with Full Response
```python ```python
# Returns RecallResponse with entities and trace info # Returns RecallResponse with entities and chunks
response = client.recall_memories( response = client.recall(
bank_id="my-agent", bank_id="my-bank",
query="What does Alice do?", query="What does Alice do?",
types=["world", "experience"], types=["world", "experience"],
budget="mid", budget="mid",
max_tokens=4096, max_tokens=4096,
trace=True,
include_entities=True, include_entities=True,
max_entity_tokens=500, max_entity_tokens=500
) )
print(f"Found {len(response.results)} memories") print(f"Found {len(response.results)} memories")
@ -185,14 +184,13 @@ if response.entities:
```python ```python
answer = client.reflect( answer = client.reflect(
bank_id="my-agent", bank_id="my-bank",
query="What should I know about Alice?", query="What should I know about Alice?",
budget="low", # low, mid, or high budget="low", # low, mid, or high
context="preparing for a meeting", context="preparing for a meeting",
) )
print(answer.text) # Generated response print(answer.text) # Generated response
print(answer.based_on) # Memories used
``` ```
## Bank Management ## Bank Management
@ -201,7 +199,7 @@ print(answer.based_on) # Memories used
```python ```python
client.create_bank( client.create_bank(
bank_id="my-agent", bank_id="my-bank",
name="Assistant", name="Assistant",
background="I am a helpful AI assistant", background="I am a helpful AI assistant",
disposition={ disposition={
@ -215,16 +213,13 @@ client.create_bank(
### List Memories ### List Memories
```python ```python
response = client.list_memories( client.list_memories(
bank_id="my-agent", bank_id="my-bank",
type="world", # Optional: filter by type type="world", # Optional: filter by type
search_query="Alice", # Optional: text search search_query="Alice", # Optional: text search
limit=100, limit=100,
offset=0, offset=0,
) )
for memory in response.memories:
print(f"{memory.id}: {memory.text}")
``` ```
## Async Support ## Async Support
@ -239,15 +234,15 @@ async def main():
client = Hindsight(base_url="http://localhost:8888") client = Hindsight(base_url="http://localhost:8888")
# Async retain # Async retain
await client.aretain(bank_id="my-agent", content="Hello world") await client.aretain(bank_id="my-bank", content="Hello world")
# Async recall # Async recall
results = await client.arecall(bank_id="my-agent", query="Hello") results = await client.arecall(bank_id="my-bank", query="Hello")
for r in results: for r in results:
print(r.text) print(r.text)
# Async reflect # Async reflect
answer = await client.areflect(bank_id="my-agent", query="What did I say?") answer = await client.areflect(bank_id="my-bank", query="What did I say?")
print(answer.text) print(answer.text)
client.close() client.close()
@ -255,29 +250,13 @@ async def main():
asyncio.run(main()) asyncio.run(main())
``` ```
## Response Types
The client exports response types for type hints:
```python
from hindsight_client import (
Hindsight,
RetainResponse,
RecallResponse,
RecallResult,
ReflectResponse,
BankProfileResponse,
DispositionTraits,
)
```
## Context Manager ## Context Manager
```python ```python
from hindsight_client import Hindsight from hindsight_client import Hindsight
with Hindsight(base_url="http://localhost:8888") as client: with Hindsight(base_url="http://localhost:8888") as client:
client.retain(bank_id="my-agent", content="Hello") client.retain(bank_id="my-bank", content="Hello")
results = client.recall(bank_id="my-agent", query="Hello") results = client.recall(bank_id="my-bank", query="Hello")
# Client automatically closed # Client automatically closed
``` ```

View file

@ -128,39 +128,38 @@ const config: Config = {
}, },
items: [ items: [
{ {
type: 'custom-iconLink', type: 'doc',
docId: 'developer/index',
position: 'left', position: 'left',
icon: 'code',
label: 'Developer', label: 'Developer',
to: '/', className: 'navbar-item-developer',
}, },
{ {
type: 'custom-iconLink', type: 'doc',
docId: 'sdks/python',
position: 'left', position: 'left',
icon: 'package',
label: 'SDKs', label: 'SDKs',
to: '/sdks/python', className: 'navbar-item-sdks',
}, },
{ {
type: 'custom-iconLink',
position: 'left',
icon: 'file-code',
label: 'API Reference',
to: '/api-reference', to: '/api-reference',
position: 'left',
label: 'API Reference',
className: 'navbar-item-api',
}, },
{ {
type: 'custom-iconLink', type: 'doc',
docId: 'cookbook/index',
position: 'left', position: 'left',
icon: 'book-open',
label: 'Cookbook', label: 'Cookbook',
to: '/cookbook', className: 'navbar-item-cookbook',
}, },
{ {
type: 'custom-iconLink', type: 'doc',
docId: 'changelog/index',
position: 'left', position: 'left',
icon: 'clock',
label: 'Changelog', label: 'Changelog',
to: '/changelog', className: 'navbar-item-changelog',
}, },
{ {
href: 'https://github.com/vectorize-io/hindsight', href: 'https://github.com/vectorize-io/hindsight',
@ -200,7 +199,7 @@ const config: Config = {
], ],
}, },
], ],
copyright: `Copyright © ${new Date().getFullYear()} Hindsight. Built with Docusaurus.`, copyright: `Copyright © ${new Date().getFullYear()} Hindsight.`,
}, },
prism: { prism: {
theme: prismThemes.github, theme: prismThemes.github,

View file

@ -13,7 +13,6 @@
"@docusaurus/theme-common": "^3.9.2", "@docusaurus/theme-common": "^3.9.2",
"@docusaurus/theme-mermaid": "^3.9.2", "@docusaurus/theme-mermaid": "^3.9.2",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
"@phosphor-icons/react": "^2.1.10",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"react": "^19.0.0", "react": "^19.0.0",
@ -4534,19 +4533,6 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/@phosphor-icons/react": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz",
"integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": ">= 16.8",
"react-dom": ">= 16.8"
}
},
"node_modules/@pnpm/config.env-replace": { "node_modules/@pnpm/config.env-replace": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",

View file

@ -21,7 +21,6 @@
"@docusaurus/theme-common": "^3.9.2", "@docusaurus/theme-common": "^3.9.2",
"@docusaurus/theme-mermaid": "^3.9.2", "@docusaurus/theme-mermaid": "^3.9.2",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
"@phosphor-icons/react": "^2.1.10",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"react": "^19.0.0", "react": "^19.0.0",

View file

@ -1,42 +0,0 @@
import React from 'react';
import Link from '@docusaurus/Link';
import {
House,
Code,
Package,
FileCode,
BookOpen,
ClockCounterClockwise,
} from '@phosphor-icons/react';
const iconMap = {
house: House,
code: Code,
package: Package,
'file-code': FileCode,
'book-open': BookOpen,
clock: ClockCounterClockwise,
};
export default function NavbarIconLink({
icon,
label,
to,
className,
}: {
icon: keyof typeof iconMap;
label: string;
to: string;
className?: string;
}) {
const IconComponent = iconMap[icon];
return (
<Link to={to} className={`navbar__link ${className || ''}`}>
{IconComponent && (
<IconComponent size={16} weight="bold" style={{ marginRight: '6px', verticalAlign: 'middle' }} />
)}
<span style={{ verticalAlign: 'middle' }}>{label}</span>
</Link>
);
}

View file

@ -94,6 +94,66 @@
transition: background-color 0.15s ease; transition: background-color 0.15s ease;
} }
/* Navbar icons (desktop only) */
@media (min-width: 997px) {
.navbar-item-developer::before,
.navbar-item-sdks::before,
.navbar-item-api::before,
.navbar-item-cookbook::before,
.navbar-item-changelog::before {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 6px;
vertical-align: middle;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
content: '';
}
.navbar-item-developer::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M71.68 97.22 34.74 128l36.94 30.78a12 12 0 1 1-15.36 18.44l-48-40a12 12 0 0 1 0-18.44l48-40a12 12 0 0 1 15.36 18.44Zm176 21.56-48-40a12 12 0 1 0-15.36 18.44L221.26 128l-36.94 30.78a12 12 0 1 0 15.36 18.44l48-40a12 12 0 0 0 0-18.44ZM164.1 28.72a12 12 0 0 0-15.38 7.18l-64 176a12 12 0 0 0 7.18 15.37 11.79 11.79 0 0 0 4.1.73 12 12 0 0 0 11.28-7.9l64-176a12 12 0 0 0-7.18-15.38Z'/%3E%3C/svg%3E");
}
.navbar-item-sdks::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='m225.6 62.64-88-48.17a19.91 19.91 0 0 0-19.2 0l-88 48.17A20 20 0 0 0 20 80.19v95.62a20 20 0 0 0 10.4 17.55l88 48.17a19.89 19.89 0 0 0 19.2 0l88-48.17a20 20 0 0 0 10.4-17.55V80.19a20 20 0 0 0-10.4-17.55ZM128 36.57 200 76l-72 39.42L56 76ZM44 96.82l72 39.43v76.89l-72-39.42Zm96 116.32v-76.89l72-39.43v76.89Z'/%3E%3C/svg%3E");
}
.navbar-item-api::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M180.49 143.51a12 12 0 0 1 0 17l-24 24a12 12 0 0 1-17-17L155 152l-15.52-15.51a12 12 0 0 1 17-17ZM112.49 120.49a12 12 0 0 0-17 0l-24 24a12 12 0 0 0 0 17l24 24a12 12 0 0 0 17-17L97 153l15.52-15.51a12 12 0 0 0-.03-17ZM220 88v24a12 12 0 0 1-24 0v-16h-44a12 12 0 0 1-12-12V40H60v68a12 12 0 0 1-24 0V40a20 20 0 0 1 20-20h96a12 12 0 0 1 8.49 3.52l56 56A12 12 0 0 1 220 88Zm-60-8h23L160 57Zm-4 132H60v-12a12 12 0 0 0-24 0v12a20 20 0 0 0 20 20h100a12 12 0 0 0 0-24Zm64-44a12 12 0 0 0-12 12v36h-44a12 12 0 0 0 0 24h44a20 20 0 0 0 20-20v-40a12 12 0 0 0-8-12Z'/%3E%3C/svg%3E");
}
.navbar-item-cookbook::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M224 44H160a43.86 43.86 0 0 0-32 13.85A43.86 43.86 0 0 0 96 44H32a20 20 0 0 0-20 20v128a20 20 0 0 0 20 20h64a20 20 0 0 1 20 20 12 12 0 0 0 24 0 20 20 0 0 1 20-20h64a20 20 0 0 0 20-20V64a20 20 0 0 0-20-20ZM96 188H36V68h60a20 20 0 0 1 20 20v108.69A43.74 43.74 0 0 0 96 188Zm124 0h-60a43.74 43.74 0 0 0-20 8.69V88a20 20 0 0 1 20-20h60Z'/%3E%3C/svg%3E");
}
.navbar-item-changelog::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M140 80v41.21l34.17 20.5a12 12 0 1 1-12.34 20.58l-40-24A12 12 0 0 1 116 128V80a12 12 0 0 1 24 0Zm-12-52a99.38 99.38 0 0 0-70.76 29.34c-4.69 4.74-9 9.37-13.24 14V64a12 12 0 0 0-24 0v40a12 12 0 0 0 12 12h40a12 12 0 0 0 0-24H53.41c4.24-5.95 8.53-11.93 13.49-16.95A76 76 0 1 1 52 128a12 12 0 0 0-24 0 100 100 0 1 0 100-100Z'/%3E%3C/svg%3E");
}
/* Dark mode icons */
[data-theme='dark'] .navbar-item-developer::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='M71.68 97.22 34.74 128l36.94 30.78a12 12 0 1 1-15.36 18.44l-48-40a12 12 0 0 1 0-18.44l48-40a12 12 0 0 1 15.36 18.44Zm176 21.56-48-40a12 12 0 1 0-15.36 18.44L221.26 128l-36.94 30.78a12 12 0 1 0 15.36 18.44l48-40a12 12 0 0 0 0-18.44ZM164.1 28.72a12 12 0 0 0-15.38 7.18l-64 176a12 12 0 0 0 7.18 15.37 11.79 11.79 0 0 0 4.1.73 12 12 0 0 0 11.28-7.9l64-176a12 12 0 0 0-7.18-15.38Z'/%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-item-sdks::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='m225.6 62.64-88-48.17a19.91 19.91 0 0 0-19.2 0l-88 48.17A20 20 0 0 0 20 80.19v95.62a20 20 0 0 0 10.4 17.55l88 48.17a19.89 19.89 0 0 0 19.2 0l88-48.17a20 20 0 0 0 10.4-17.55V80.19a20 20 0 0 0-10.4-17.55ZM128 36.57 200 76l-72 39.42L56 76ZM44 96.82l72 39.43v76.89l-72-39.42Zm96 116.32v-76.89l72-39.43v76.89Z'/%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-item-api::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='M180.49 143.51a12 12 0 0 1 0 17l-24 24a12 12 0 0 1-17-17L155 152l-15.52-15.51a12 12 0 0 1 17-17ZM112.49 120.49a12 12 0 0 0-17 0l-24 24a12 12 0 0 0 0 17l24 24a12 12 0 0 0 17-17L97 153l15.52-15.51a12 12 0 0 0-.03-17ZM220 88v24a12 12 0 0 1-24 0v-16h-44a12 12 0 0 1-12-12V40H60v68a12 12 0 0 1-24 0V40a20 20 0 0 1 20-20h96a12 12 0 0 1 8.49 3.52l56 56A12 12 0 0 1 220 88Zm-60-8h23L160 57Zm-4 132H60v-12a12 12 0 0 0-24 0v12a20 20 0 0 0 20 20h100a12 12 0 0 0 0-24Zm64-44a12 12 0 0 0-12 12v36h-44a12 12 0 0 0 0 24h44a20 20 0 0 0 20-20v-40a12 12 0 0 0-8-12Z'/%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-item-cookbook::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='M224 44H160a43.86 43.86 0 0 0-32 13.85A43.86 43.86 0 0 0 96 44H32a20 20 0 0 0-20 20v128a20 20 0 0 0 20 20h64a20 20 0 0 1 20 20 12 12 0 0 0 24 0 20 20 0 0 1 20-20h64a20 20 0 0 0 20-20V64a20 20 0 0 0-20-20ZM96 188H36V68h60a20 20 0 0 1 20 20v108.69A43.74 43.74 0 0 0 96 188Zm124 0h-60a43.74 43.74 0 0 0-20 8.69V88a20 20 0 0 1 20-20h60Z'/%3E%3C/svg%3E");
}
[data-theme='dark'] .navbar-item-changelog::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath fill='%23ccc' d='M140 80v41.21l34.17 20.5a12 12 0 1 1-12.34 20.58l-40-24A12 12 0 0 1 116 128V80a12 12 0 0 1 24 0Zm-12-52a99.38 99.38 0 0 0-70.76 29.34c-4.69 4.74-9 9.37-13.24 14V64a12 12 0 0 0-24 0v40a12 12 0 0 0 12 12h40a12 12 0 0 0 0-24H53.41c4.24-5.95 8.53-11.93 13.49-16.95A76 76 0 1 1 52 128a12 12 0 0 0-24 0 100 100 0 1 0 100-100Z'/%3E%3C/svg%3E");
}
}
/* GitHub icon link */ /* GitHub icon link */
.header-github-link::before { .header-github-link::before {
content: ''; content: '';
@ -126,6 +186,95 @@
background-color: #27272a; background-color: #27272a;
} }
/* Mobile navbar */
@media (max-width: 996px) {
:root {
--ifm-navbar-height: 3.5rem;
}
.navbar {
padding: 0 0.75rem;
}
.navbar__logo {
margin-bottom: 0;
height: 24px !important;
}
.navbar__logo img {
height: 24px !important;
}
/* Hamburger menu toggle */
.navbar__toggle {
color: var(--ifm-color-primary);
}
/* Mobile sidebar */
.navbar-sidebar {
background: #ffffff !important;
}
.navbar-sidebar__brand {
padding: 1rem;
border-bottom: 1px solid var(--ifm-toc-border-color);
background: #ffffff !important;
}
.navbar-sidebar__items {
padding: 1rem 0;
background: #ffffff !important;
}
.navbar-sidebar .menu__link {
font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 600;
font-size: 1rem;
padding: 0.75rem 1.25rem;
color: #1e293b !important;
}
.navbar-sidebar .menu__link--active {
background: var(--hindsight-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Close button */
.navbar-sidebar__close {
color: #1e293b !important;
}
/* Backdrop overlay */
.navbar-sidebar__backdrop {
background: rgba(0, 0, 0, 0.5) !important;
}
}
/* Dark mode mobile sidebar */
@media (max-width: 996px) {
[data-theme='dark'] .navbar-sidebar {
background: #09090b !important;
}
[data-theme='dark'] .navbar-sidebar__brand {
background: #09090b !important;
}
[data-theme='dark'] .navbar-sidebar__items {
background: #09090b !important;
}
[data-theme='dark'] .navbar-sidebar .menu__link {
color: #e2e8f0 !important;
}
[data-theme='dark'] .navbar-sidebar__close {
color: #e2e8f0 !important;
}
}
/* Hero section */ /* Hero section */
.hero { .hero {
padding: 4rem 0; padding: 4rem 0;

View file

@ -1,7 +0,0 @@
import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes';
import NavbarIconLink from '@site/src/components/NavbarIconLink';
export default {
...ComponentTypes,
'custom-iconLink': NavbarIconLink,
};

View file

@ -3,7 +3,7 @@
> Agent Memory that Works Like Human Memory > Agent Memory that Works Like Human Memory
This file contains the complete Hindsight documentation for LLM consumption. This file contains the complete Hindsight documentation for LLM consumption.
Generated: 2025-12-11T11:54:08.183Z Generated: 2025-12-11T12:49:49.534Z
--- ---
@ -2454,27 +2454,29 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
**Best for**: Production deployments, auto-scaling, cloud environments **Best for**: Production deployments, auto-scaling, cloud environments
```bash ```bash
# Add Hindsight Helm repository
helm repo add hindsight https://vectorize-io.github.io/hindsight
helm repo update
# Install with built-in PostgreSQL # Install with built-in PostgreSQL
helm install hindsight hindsight/hindsight \ helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \ --set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \ --set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=true --set postgresql.enabled=true
# Or use external PostgreSQL # Or use external PostgreSQL
helm install hindsight hindsight/hindsight \ helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight \
--set api.llm.provider=groq \ --set api.llm.provider=groq \
--set api.llm.apiKey=gsk_xxxxxxxxxxxx \ --set api.llm.apiKey=gsk_xxxxxxxxxxxx \
--set postgresql.enabled=false \ --set postgresql.enabled=false \
--set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight --set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight
# Install a specific version
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
# Upgrade to latest
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
``` ```
**Requirements**: **Requirements**:
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted) - Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
- Helm 3+ - Helm 3.8+
See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration. See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration.

View file

@ -27,7 +27,7 @@ def llm_config():
model = os.getenv("HINDSIGHT_LLM_MODEL", "openai/gpt-oss-120b") model = os.getenv("HINDSIGHT_LLM_MODEL", "openai/gpt-oss-120b")
if not api_key: if not api_key:
pytest.skip("LLM API key not configured. Set HINDSIGHT_LLM_API_KEY environment variable.") raise Exception("LLM API key not configured. Set HINDSIGHT_LLM_API_KEY environment variable.")
return { return {
"llm_provider": provider, "llm_provider": provider,

View file

@ -1141,7 +1141,7 @@ wheels = [
[[package]] [[package]]
name = "hindsight-all" name = "hindsight-all"
version = "0.1.2" version = "0.1.3"
source = { editable = "hindsight" } source = { editable = "hindsight" }
dependencies = [ dependencies = [
{ name = "hindsight-api" }, { name = "hindsight-api" },
@ -1165,7 +1165,7 @@ provides-extras = ["test"]
[[package]] [[package]]
name = "hindsight-api" name = "hindsight-api"
version = "0.1.2" version = "0.1.3"
source = { editable = "hindsight-api" } source = { editable = "hindsight-api" }
dependencies = [ dependencies = [
{ name = "alembic" }, { name = "alembic" },
@ -1265,7 +1265,7 @@ dev = [
[[package]] [[package]]
name = "hindsight-client" name = "hindsight-client"
version = "0.1.2" version = "0.1.3"
source = { editable = "hindsight-clients/python" } source = { editable = "hindsight-clients/python" }
dependencies = [ dependencies = [
{ name = "aiohttp" }, { name = "aiohttp" },
@ -1297,7 +1297,7 @@ provides-extras = ["test"]
[[package]] [[package]]
name = "hindsight-dev" name = "hindsight-dev"
version = "0.1.2" version = "0.1.3"
source = { editable = "hindsight-dev" } source = { editable = "hindsight-dev" }
dependencies = [ dependencies = [
{ name = "hindsight-api" }, { name = "hindsight-api" },