fix docs on clients
This commit is contained in:
parent
ebc85a5c3d
commit
99db7b26c3
22 changed files with 372 additions and 476 deletions
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
|
|
@ -219,6 +219,9 @@ jobs:
|
|||
|
||||
release-helm-chart:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -228,12 +231,18 @@ jobs:
|
|||
with:
|
||||
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
|
||||
run: helm lint helm/hindsight
|
||||
|
||||
- name: Package Helm chart
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -66,7 +66,7 @@ UI: http://localhost:9999
|
|||
Install client:
|
||||
|
||||
```bash
|
||||
pip install hindsight-client
|
||||
pip install hindsight-client -U
|
||||
# or
|
||||
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)
|
||||
|
||||
```bash
|
||||
pip install hindsight-all
|
||||
pip install hindsight-all -U
|
||||
```
|
||||
|
||||
```python
|
||||
|
|
@ -100,27 +100,27 @@ from hindsight import HindsightServer, HindsightClient
|
|||
|
||||
with HindsightServer(
|
||||
llm_provider="openai",
|
||||
llm_model="gpt-4o-mini",
|
||||
llm_model="gpt-5-mini",
|
||||
llm_api_key=os.environ["OPENAI_API_KEY"]
|
||||
) as server:
|
||||
client = HindsightClient(base_url=server.url)
|
||||
client.retain(bank_id="my-agent", content="Alice works at Google")
|
||||
results = client.recall(bank_id="my-agent", query="Where does Alice work?")
|
||||
client.retain(bank_id="my-bank", content="Alice works at Google")
|
||||
results = client.recall(bank_id="my-bank", query="Where does Alice work?")
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
### Node.js / TypeScript
|
||||
|
||||
```bash
|
||||
npm install @vectorize-io/hindsight-client
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
||||
```javascript
|
||||
const { HindsightClient } = require('@vectorize-io/hindsight-client');
|
||||
|
||||
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
|
||||
|
||||
await client.retain('my-agent', 'Alice loves hiking in Yosemite');
|
||||
const response = await client.recall('my-agent', 'What does Alice like?');
|
||||
await client.retain('my-bank', 'Alice loves hiking in Yosemite');
|
||||
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?")
|
||||
|
||||
# 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:
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ ENV HINDSIGHT_API_PORT=8888
|
|||
ENV HINDSIGHT_API_LOG_LEVEL=info
|
||||
ENV HINDSIGHT_ENABLE_API=true
|
||||
ENV HINDSIGHT_ENABLE_CP=false
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
CMD ["/app/start-all.sh"]
|
||||
|
||||
|
|
@ -311,6 +312,7 @@ ENV NODE_ENV=production
|
|||
ENV HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888
|
||||
ENV HINDSIGHT_ENABLE_API=true
|
||||
ENV HINDSIGHT_ENABLE_CP=true
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
CMD ["/app/start-all.sh"]
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ PIDS=()
|
|||
# Start API if enabled
|
||||
if [ "$ENABLE_API" = "true" ]; then
|
||||
cd /app/api
|
||||
hindsight-api 2>&1 | sed 's/^/[api] /' &
|
||||
hindsight-api 2>&1 | sed -u 's/^/[api] /' &
|
||||
API_PID=$!
|
||||
PIDS+=($API_PID)
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ fi
|
|||
if [ "$ENABLE_CP" = "true" ]; then
|
||||
echo "🎛️ Starting 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=$!
|
||||
PIDS+=($CP_PID)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -170,24 +170,38 @@ class LLMProvider:
|
|||
"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)
|
||||
model_lower = self.model.lower()
|
||||
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)
|
||||
if temperature is not None and not is_reasoning_model:
|
||||
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
|
||||
if self.provider == "groq":
|
||||
call_params["seed"] = DEFAULT_LLM_SEED
|
||||
call_params["extra_body"] = {
|
||||
"service_tier": "auto",
|
||||
"reasoning_effort": self.reasoning_effort,
|
||||
"include_reasoning": False,
|
||||
}
|
||||
extra_body = {"service_tier": "auto"}
|
||||
# Only add reasoning parameters for reasoning models
|
||||
if is_reasoning_model:
|
||||
extra_body["reasoning_effort"] = self.reasoning_effort
|
||||
extra_body["include_reasoning"] = False
|
||||
call_params["extra_body"] = extra_body
|
||||
|
||||
last_exception = None
|
||||
|
||||
|
|
|
|||
|
|
@ -10,36 +10,30 @@ from hindsight_api.engine.llm_wrapper import LLMProvider
|
|||
MODEL_MATRIX = [
|
||||
# OpenAI models
|
||||
("openai", "gpt-4o-mini"),
|
||||
("openai", "gpt-4.1-mini"),
|
||||
("openai", "gpt-4.1-nano"),
|
||||
("openai", "gpt-5-mini"),
|
||||
("openai", "gpt-5-nano"),
|
||||
("openai", "gpt-5"),
|
||||
# Groq models
|
||||
("groq", "llama-3.3-70b-versatile"),
|
||||
("groq", "openai/gpt-oss-120b"),
|
||||
("groq", "openai/gpt-oss-20b"),
|
||||
# Gemini models
|
||||
("gemini", "gemini-2.0-flash"),
|
||||
("gemini", "gemini-2.5-flash-preview-05-20"),
|
||||
("gemini", "gemini-2.5-flash"),
|
||||
("gemini", "gemini-2.5-flash-lite"),
|
||||
]
|
||||
|
||||
|
||||
def get_api_key_for_provider(provider: str) -> str | None:
|
||||
"""Get API key for provider from environment variables."""
|
||||
# Try provider-specific env vars first
|
||||
provider_key_map = {
|
||||
"openai": ["OPENAI_API_KEY", "HINDSIGHT_API_LLM_API_KEY"],
|
||||
"groq": ["GROQ_API_KEY", "HINDSIGHT_API_LLM_API_KEY"],
|
||||
"gemini": ["GEMINI_API_KEY", "GOOGLE_API_KEY", "HINDSIGHT_API_LLM_API_KEY"],
|
||||
"openai": "OPENAI_API_KEY",
|
||||
"groq": "GROQ_API_KEY",
|
||||
"gemini": "GEMINI_API_KEY",
|
||||
}
|
||||
|
||||
for env_var in provider_key_map.get(provider, []):
|
||||
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
|
||||
env_var = provider_key_map.get(provider)
|
||||
return os.getenv(env_var) if env_var else None
|
||||
|
||||
|
||||
@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)
|
||||
LARGE_OUTPUT_MODELS = [
|
||||
("openai", "gpt-5-mini"),
|
||||
("gemini", "gemini-2.0-flash"),
|
||||
("gemini", "gemini-2.5-flash-preview-05-20"),
|
||||
("openai", "gpt-5-nano"),
|
||||
("openai", "gpt-5"),
|
||||
("gemini", "gemini-2.5-flash"),
|
||||
("gemini", "gemini-2.5-flash-lite"),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ Example:
|
|||
print(result.success)
|
||||
|
||||
# Search memories
|
||||
results = client.recall(bank_id="alice", query="What does Alice like?")
|
||||
for r in results:
|
||||
response = client.recall(bank_id="alice", query="What does Alice like?")
|
||||
for r in response.results:
|
||||
print(r.text)
|
||||
|
||||
# Generate contextual answer
|
||||
|
|
@ -29,14 +29,59 @@ from .hindsight_client import 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.recall_response import RecallResponse as _RecallResponse
|
||||
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_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.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__ = [
|
||||
"Hindsight",
|
||||
# Response types
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ class Hindsight:
|
|||
client.retain(bank_id="alice", content="Alice loves AI")
|
||||
|
||||
# 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
|
||||
answer = client.reflect(bank_id="alice", query="What are my interests?")
|
||||
|
|
@ -125,8 +127,8 @@ class Hindsight:
|
|||
|
||||
Args:
|
||||
bank_id: The memory bank ID
|
||||
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata'
|
||||
document_id: Optional document ID for grouping memories
|
||||
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata', 'document_id'
|
||||
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)
|
||||
|
||||
Returns:
|
||||
|
|
@ -138,13 +140,14 @@ class Hindsight:
|
|||
timestamp=item.get("timestamp"),
|
||||
context=item.get("context"),
|
||||
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
|
||||
]
|
||||
|
||||
request_obj = retain_request.RetainRequest(
|
||||
items=memory_items,
|
||||
document_id=document_id,
|
||||
async_=retain_async,
|
||||
)
|
||||
|
||||
|
|
@ -161,6 +164,8 @@ class Hindsight:
|
|||
query_timestamp: Optional[str] = None,
|
||||
include_entities: bool = False,
|
||||
max_entity_tokens: int = 500,
|
||||
include_chunks: bool = False,
|
||||
max_chunk_tokens: int = 8192,
|
||||
) -> RecallResponse:
|
||||
"""
|
||||
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')
|
||||
include_entities: Include entity observations in results (default: False)
|
||||
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:
|
||||
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(
|
||||
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(
|
||||
|
|
@ -277,8 +285,8 @@ class Hindsight:
|
|||
|
||||
Args:
|
||||
bank_id: The memory bank ID
|
||||
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata'
|
||||
document_id: Optional document ID for grouping memories
|
||||
items: List of memory items with 'content' and optional 'timestamp', 'context', 'metadata', 'document_id'
|
||||
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)
|
||||
|
||||
Returns:
|
||||
|
|
@ -290,13 +298,14 @@ class Hindsight:
|
|||
timestamp=item.get("timestamp"),
|
||||
context=item.get("context"),
|
||||
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
|
||||
]
|
||||
|
||||
request_obj = retain_request.RetainRequest(
|
||||
items=memory_items,
|
||||
document_id=document_id,
|
||||
async_=retain_async,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,17 @@ export class HindsightClient {
|
|||
async recall(
|
||||
bankId: 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> {
|
||||
const response = await sdk.recallMemories({
|
||||
client: this.client,
|
||||
|
|
@ -139,6 +149,11 @@ export class HindsightClient {
|
|||
max_tokens: options?.maxTokens,
|
||||
budget: options?.budget || 'mid',
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -55,27 +55,29 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
|
|||
**Best for**: Production deployments, auto-scaling, cloud environments
|
||||
|
||||
```bash
|
||||
# Add Hindsight Helm repository
|
||||
helm repo add hindsight https://vectorize-io.github.io/hindsight
|
||||
helm repo update
|
||||
|
||||
# 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.apiKey=gsk_xxxxxxxxxxxx \
|
||||
--set postgresql.enabled=true
|
||||
|
||||
# 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.apiKey=gsk_xxxxxxxxxxxx \
|
||||
--set postgresql.enabled=false \
|
||||
--set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight
|
||||
|
||||
# Install a specific version
|
||||
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
|
||||
|
||||
# Upgrade to latest
|
||||
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
|
||||
- Helm 3+
|
||||
- Helm 3.8+
|
||||
|
||||
See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration.
|
||||
|
||||
|
|
|
|||
|
|
@ -15,21 +15,21 @@ npm install @vectorize-io/hindsight-client
|
|||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
||||
const { HindsightClient } = require('@vectorize-io/hindsight-client');
|
||||
|
||||
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
|
||||
|
||||
// Retain a memory
|
||||
await client.retain('my-agent', 'Alice works at Google');
|
||||
await client.retain('my-bank', 'Alice works at Google');
|
||||
|
||||
// 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) {
|
||||
console.log(r.text);
|
||||
}
|
||||
|
||||
// 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);
|
||||
```
|
||||
|
||||
|
|
@ -49,10 +49,10 @@ const client = new HindsightClient({
|
|||
|
||||
```typescript
|
||||
// Simple
|
||||
await client.retain('my-agent', 'Alice works at Google');
|
||||
await client.retain('my-bank', 'Alice works at Google');
|
||||
|
||||
// With options
|
||||
await client.retain('my-agent', 'Alice got promoted', {
|
||||
await client.retain('my-bank', 'Alice got promoted', {
|
||||
timestamp: new Date('2024-01-15'),
|
||||
context: 'career update',
|
||||
metadata: { source: 'slack' },
|
||||
|
|
@ -63,11 +63,10 @@ await client.retain('my-agent', 'Alice got promoted', {
|
|||
### Retain Batch
|
||||
|
||||
```typescript
|
||||
await client.retainBatch('my-agent', [
|
||||
await client.retainBatch('my-bank', [
|
||||
{ content: 'Alice works at Google', context: 'career' },
|
||||
{ content: 'Bob is a data scientist', context: 'career' },
|
||||
], {
|
||||
documentId: 'conversation_001',
|
||||
async: false,
|
||||
});
|
||||
```
|
||||
|
|
@ -76,31 +75,29 @@ await client.retainBatch('my-agent', [
|
|||
|
||||
```typescript
|
||||
// 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) {
|
||||
console.log(`${r.text} (type: ${r.type})`);
|
||||
}
|
||||
|
||||
// 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
|
||||
maxTokens: 4096,
|
||||
budget: 'high', // 'low', 'mid', or 'high'
|
||||
trace: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Reflect (Generate Response)
|
||||
|
||||
```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'
|
||||
context: 'preparing for a meeting',
|
||||
});
|
||||
|
||||
console.log(answer.text); // Generated response
|
||||
console.log(answer.based_on); // Memories used
|
||||
```
|
||||
|
||||
## Bank Management
|
||||
|
|
@ -108,7 +105,7 @@ console.log(answer.based_on); // Memories used
|
|||
### Create Bank
|
||||
|
||||
```typescript
|
||||
await client.createBank('my-agent', {
|
||||
await client.createBank('my-bank', {
|
||||
name: 'Assistant',
|
||||
background: 'I am a helpful AI assistant',
|
||||
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
|
||||
|
||||
```typescript
|
||||
const response = await client.listMemories('my-agent', {
|
||||
const response = await client.listMemories('my-bank', {
|
||||
type: 'world', // Optional filter
|
||||
q: 'Alice', // Optional text search
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
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);
|
||||
console.log(response)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -49,15 +49,15 @@ with HindsightServer(
|
|||
client = HindsightClient(base_url=server.url)
|
||||
|
||||
# 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
|
||||
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:
|
||||
print(r.text)
|
||||
|
||||
# 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)
|
||||
```
|
||||
|
||||
|
|
@ -70,15 +70,15 @@ from hindsight_client import Hindsight
|
|||
client = Hindsight(base_url="http://localhost:8888")
|
||||
|
||||
# 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
|
||||
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:
|
||||
print(r.text)
|
||||
|
||||
# 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)
|
||||
```
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ client = Hindsight(
|
|||
```python
|
||||
# Simple
|
||||
client.retain(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
content="Alice works at Google as a software engineer",
|
||||
)
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ client.retain(
|
|||
from datetime import datetime
|
||||
|
||||
client.retain(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
content="Alice got promoted",
|
||||
context="career update",
|
||||
timestamp=datetime(2024, 1, 15),
|
||||
|
|
@ -124,7 +124,7 @@ client.retain(
|
|||
|
||||
```python
|
||||
client.retain_batch(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
items=[
|
||||
{"content": "Alice works at Google", "context": "career"},
|
||||
{"content": "Bob is a data scientist", "context": "career"},
|
||||
|
|
@ -139,16 +139,16 @@ client.retain_batch(
|
|||
```python
|
||||
# Simple - returns list of RecallResult
|
||||
results = client.recall(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
query="What does Alice do?",
|
||||
)
|
||||
|
||||
for r in results:
|
||||
for r in results.results:
|
||||
print(f"{r.text} (type: {r.type})")
|
||||
|
||||
# With options
|
||||
results = client.recall(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
query="What does Alice do?",
|
||||
types=["world", "opinion"], # Filter by fact type
|
||||
max_tokens=4096,
|
||||
|
|
@ -159,16 +159,15 @@ results = client.recall(
|
|||
### Recall with Full Response
|
||||
|
||||
```python
|
||||
# Returns RecallResponse with entities and trace info
|
||||
response = client.recall_memories(
|
||||
bank_id="my-agent",
|
||||
# Returns RecallResponse with entities and chunks
|
||||
response = client.recall(
|
||||
bank_id="my-bank",
|
||||
query="What does Alice do?",
|
||||
types=["world", "experience"],
|
||||
budget="mid",
|
||||
max_tokens=4096,
|
||||
trace=True,
|
||||
include_entities=True,
|
||||
max_entity_tokens=500,
|
||||
max_entity_tokens=500
|
||||
)
|
||||
|
||||
print(f"Found {len(response.results)} memories")
|
||||
|
|
@ -185,14 +184,13 @@ if response.entities:
|
|||
|
||||
```python
|
||||
answer = client.reflect(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
query="What should I know about Alice?",
|
||||
budget="low", # low, mid, or high
|
||||
context="preparing for a meeting",
|
||||
)
|
||||
|
||||
print(answer.text) # Generated response
|
||||
print(answer.based_on) # Memories used
|
||||
```
|
||||
|
||||
## Bank Management
|
||||
|
|
@ -201,7 +199,7 @@ print(answer.based_on) # Memories used
|
|||
|
||||
```python
|
||||
client.create_bank(
|
||||
bank_id="my-agent",
|
||||
bank_id="my-bank",
|
||||
name="Assistant",
|
||||
background="I am a helpful AI assistant",
|
||||
disposition={
|
||||
|
|
@ -215,16 +213,13 @@ client.create_bank(
|
|||
### List Memories
|
||||
|
||||
```python
|
||||
response = client.list_memories(
|
||||
bank_id="my-agent",
|
||||
client.list_memories(
|
||||
bank_id="my-bank",
|
||||
type="world", # Optional: filter by type
|
||||
search_query="Alice", # Optional: text search
|
||||
limit=100,
|
||||
offset=0,
|
||||
)
|
||||
|
||||
for memory in response.memories:
|
||||
print(f"{memory.id}: {memory.text}")
|
||||
```
|
||||
|
||||
## Async Support
|
||||
|
|
@ -239,15 +234,15 @@ async def main():
|
|||
client = Hindsight(base_url="http://localhost:8888")
|
||||
|
||||
# Async retain
|
||||
await client.aretain(bank_id="my-agent", content="Hello world")
|
||||
await client.aretain(bank_id="my-bank", content="Hello world")
|
||||
|
||||
# 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:
|
||||
print(r.text)
|
||||
|
||||
# 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)
|
||||
|
||||
client.close()
|
||||
|
|
@ -255,29 +250,13 @@ async def 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
|
||||
|
||||
```python
|
||||
from hindsight_client import Hindsight
|
||||
|
||||
with Hindsight(base_url="http://localhost:8888") as client:
|
||||
client.retain(bank_id="my-agent", content="Hello")
|
||||
results = client.recall(bank_id="my-agent", query="Hello")
|
||||
client.retain(bank_id="my-bank", content="Hello")
|
||||
results = client.recall(bank_id="my-bank", query="Hello")
|
||||
# Client automatically closed
|
||||
```
|
||||
|
|
|
|||
|
|
@ -128,39 +128,38 @@ const config: Config = {
|
|||
},
|
||||
items: [
|
||||
{
|
||||
type: 'custom-iconLink',
|
||||
type: 'doc',
|
||||
docId: 'developer/index',
|
||||
position: 'left',
|
||||
icon: 'code',
|
||||
label: 'Developer',
|
||||
to: '/',
|
||||
className: 'navbar-item-developer',
|
||||
},
|
||||
{
|
||||
type: 'custom-iconLink',
|
||||
type: 'doc',
|
||||
docId: 'sdks/python',
|
||||
position: 'left',
|
||||
icon: 'package',
|
||||
label: 'SDKs',
|
||||
to: '/sdks/python',
|
||||
className: 'navbar-item-sdks',
|
||||
},
|
||||
{
|
||||
type: 'custom-iconLink',
|
||||
position: 'left',
|
||||
icon: 'file-code',
|
||||
label: 'API Reference',
|
||||
to: '/api-reference',
|
||||
position: 'left',
|
||||
label: 'API Reference',
|
||||
className: 'navbar-item-api',
|
||||
},
|
||||
{
|
||||
type: 'custom-iconLink',
|
||||
type: 'doc',
|
||||
docId: 'cookbook/index',
|
||||
position: 'left',
|
||||
icon: 'book-open',
|
||||
label: 'Cookbook',
|
||||
to: '/cookbook',
|
||||
className: 'navbar-item-cookbook',
|
||||
},
|
||||
{
|
||||
type: 'custom-iconLink',
|
||||
type: 'doc',
|
||||
docId: 'changelog/index',
|
||||
position: 'left',
|
||||
icon: 'clock',
|
||||
label: 'Changelog',
|
||||
to: '/changelog',
|
||||
className: 'navbar-item-changelog',
|
||||
},
|
||||
{
|
||||
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: {
|
||||
theme: prismThemes.github,
|
||||
|
|
|
|||
14
hindsight-docs/package-lock.json
generated
14
hindsight-docs/package-lock.json
generated
|
|
@ -13,7 +13,6 @@
|
|||
"@docusaurus/theme-common": "^3.9.2",
|
||||
"@docusaurus/theme-mermaid": "^3.9.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^19.0.0",
|
||||
|
|
@ -4534,19 +4533,6 @@
|
|||
"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": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
"@docusaurus/theme-common": "^3.9.2",
|
||||
"@docusaurus/theme-mermaid": "^3.9.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^19.0.0",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -94,6 +94,66 @@
|
|||
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 */
|
||||
.header-github-link::before {
|
||||
content: '';
|
||||
|
|
@ -126,6 +186,95 @@
|
|||
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 {
|
||||
padding: 4rem 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes';
|
||||
import NavbarIconLink from '@site/src/components/NavbarIconLink';
|
||||
|
||||
export default {
|
||||
...ComponentTypes,
|
||||
'custom-iconLink': NavbarIconLink,
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
> Agent Memory that Works Like Human Memory
|
||||
|
||||
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
|
||||
|
||||
```bash
|
||||
# Add Hindsight Helm repository
|
||||
helm repo add hindsight https://vectorize-io.github.io/hindsight
|
||||
helm repo update
|
||||
|
||||
# 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.apiKey=gsk_xxxxxxxxxxxx \
|
||||
--set postgresql.enabled=true
|
||||
|
||||
# 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.apiKey=gsk_xxxxxxxxxxxx \
|
||||
--set postgresql.enabled=false \
|
||||
--set api.database.url=postgresql://user:pass@postgres.example.com:5432/hindsight
|
||||
|
||||
# Install a specific version
|
||||
helm install hindsight oci://ghcr.io/vectorize-io/charts/hindsight --version 0.1.3
|
||||
|
||||
# Upgrade to latest
|
||||
helm upgrade hindsight oci://ghcr.io/vectorize-io/charts/hindsight
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Kubernetes cluster (GKE, EKS, AKS, or self-hosted)
|
||||
- Helm 3+
|
||||
- Helm 3.8+
|
||||
|
||||
See the [Helm chart documentation](https://github.com/vectorize-io/hindsight/tree/main/helm) for advanced configuration.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ def llm_config():
|
|||
model = os.getenv("HINDSIGHT_LLM_MODEL", "openai/gpt-oss-120b")
|
||||
|
||||
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 {
|
||||
"llm_provider": provider,
|
||||
|
|
|
|||
8
uv.lock
8
uv.lock
|
|
@ -1141,7 +1141,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "hindsight-all"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = { editable = "hindsight" }
|
||||
dependencies = [
|
||||
{ name = "hindsight-api" },
|
||||
|
|
@ -1165,7 +1165,7 @@ provides-extras = ["test"]
|
|||
|
||||
[[package]]
|
||||
name = "hindsight-api"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = { editable = "hindsight-api" }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
|
|
@ -1265,7 +1265,7 @@ dev = [
|
|||
|
||||
[[package]]
|
||||
name = "hindsight-client"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = { editable = "hindsight-clients/python" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
|
@ -1297,7 +1297,7 @@ provides-extras = ["test"]
|
|||
|
||||
[[package]]
|
||||
name = "hindsight-dev"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = { editable = "hindsight-dev" }
|
||||
dependencies = [
|
||||
{ name = "hindsight-api" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue