improve docker and mcp
This commit is contained in:
parent
e0cfec1666
commit
b83fd3a5c3
10 changed files with 635 additions and 644 deletions
|
|
@ -159,6 +159,15 @@ RUN pg0 start --help && \
|
|||
|
||||
ENV PG0_HOME=/home/hindsight/.pg0
|
||||
|
||||
# Pre-download ML models to avoid runtime download
|
||||
RUN /app/api/.venv/bin/python -c "\
|
||||
from sentence_transformers import SentenceTransformer, CrossEncoder; \
|
||||
print('Downloading embedding model...'); \
|
||||
SentenceTransformer('BAAI/bge-small-en-v1.5'); \
|
||||
print('Downloading cross-encoder model...'); \
|
||||
CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2'); \
|
||||
print('Models cached successfully')"
|
||||
|
||||
EXPOSE 8888
|
||||
|
||||
ENV HINDSIGHT_API_HOST=0.0.0.0
|
||||
|
|
@ -282,6 +291,15 @@ RUN pg0 start --help && \
|
|||
|
||||
ENV PG0_HOME=/home/hindsight/.pg0
|
||||
|
||||
# Pre-download ML models to avoid runtime download
|
||||
RUN /app/api/.venv/bin/python -c "\
|
||||
from sentence_transformers import SentenceTransformer, CrossEncoder; \
|
||||
print('Downloading embedding model...'); \
|
||||
SentenceTransformer('BAAI/bge-small-en-v1.5'); \
|
||||
print('Downloading cross-encoder model...'); \
|
||||
CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2'); \
|
||||
print('Models cached successfully')"
|
||||
|
||||
EXPOSE 8888 9999
|
||||
|
||||
ENV HINDSIGHT_API_HOST=0.0.0.0
|
||||
|
|
|
|||
|
|
@ -62,14 +62,13 @@ def create_app(
|
|||
# Mount MCP server if enabled
|
||||
if mcp_api_enabled:
|
||||
try:
|
||||
from .mcp import create_mcp_server
|
||||
from .mcp import create_mcp_app
|
||||
|
||||
# Create MCP server with shared memory instance
|
||||
mcp_server = create_mcp_server(memory=memory)
|
||||
|
||||
# Mount at specified path using sse_app for compatibility with mcp-remote
|
||||
app.mount(mcp_mount_path, mcp_server.sse_app())
|
||||
logger.info(f"MCP server enabled at {mcp_mount_path}")
|
||||
# Create MCP app with dynamic bank_id support
|
||||
# Supports: /mcp/{bank_id}/sse (bank-specific SSE endpoint)
|
||||
mcp_app = create_mcp_app(memory=memory)
|
||||
app.mount(mcp_mount_path, mcp_app)
|
||||
logger.info(f"MCP server enabled at {mcp_mount_path}/{{bank_id}}/sse")
|
||||
except ImportError as e:
|
||||
logger.error(f"MCP server requested but dependencies not available: {e}")
|
||||
logger.error("Install with: pip install hindsight-api[mcp]")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
from contextvars import ContextVar
|
||||
from typing import Optional
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from hindsight_api import MemoryEngine
|
||||
|
|
@ -17,6 +19,14 @@ logging.basicConfig(
|
|||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Context variable to hold the current bank_id from the URL path
|
||||
_current_bank_id: ContextVar[Optional[str]] = ContextVar("current_bank_id", default=None)
|
||||
|
||||
|
||||
def get_current_bank_id() -> Optional[str]:
|
||||
"""Get the current bank_id from context (set from URL path)."""
|
||||
return _current_bank_id.get()
|
||||
|
||||
|
||||
def create_mcp_server(memory: MemoryEngine) -> FastMCP:
|
||||
"""
|
||||
|
|
@ -28,125 +38,71 @@ def create_mcp_server(memory: MemoryEngine) -> FastMCP:
|
|||
Returns:
|
||||
Configured FastMCP server instance
|
||||
"""
|
||||
# Create FastMCP server
|
||||
mcp = FastMCP("hindsight-mcp-server")
|
||||
|
||||
@mcp.tool()
|
||||
async def hindsight_put(bank_id: str, content: str, context: str, explanation: str = "") -> str:
|
||||
async def retain(content: str, context: str = "general") -> str:
|
||||
"""
|
||||
**CRITICAL: Store important user information to long-term memory.**
|
||||
|
||||
**⚠️ PER-USER TOOL - REQUIRES USER IDENTIFICATION:**
|
||||
- This tool is STRICTLY per-user. Each user MUST have a unique `bank_id`.
|
||||
- ONLY use this tool if you have a valid user identifier (user ID, email, session ID, etc.) to map to `bank_id`.
|
||||
- DO NOT use this tool if you cannot identify the specific user.
|
||||
- DO NOT share memories between different users - each user's memories are isolated by their `bank_id`.
|
||||
- If you don't have a user identifier, DO NOT use this tool at all.
|
||||
Store important information to long-term memory.
|
||||
|
||||
Use this tool PROACTIVELY whenever the user shares:
|
||||
- Personal facts, preferences, or interests (e.g., "I love hiking", "I'm a vegetarian")
|
||||
- Important events or milestones (e.g., "I got promoted", "My birthday is June 15")
|
||||
- User history, experiences, or background (e.g., "I used to work at Google", "I studied CS at MIT")
|
||||
- Decisions, opinions, or stated preferences (e.g., "I prefer Python over JavaScript")
|
||||
- Goals, plans, or future intentions (e.g., "I'm planning to visit Japan next year")
|
||||
- Relationships or people mentioned (e.g., "My manager Sarah", "My wife Alice")
|
||||
- Personal facts, preferences, or interests
|
||||
- Important events or milestones
|
||||
- User history, experiences, or background
|
||||
- Decisions, opinions, or stated preferences
|
||||
- Goals, plans, or future intentions
|
||||
- Relationships or people mentioned
|
||||
- Work context, projects, or responsibilities
|
||||
- Any other information the user would want remembered for future conversations
|
||||
|
||||
**When to use**: Immediately after user shares personal information. Don't ask permission - just store it naturally.
|
||||
|
||||
**Context guidelines**: Use descriptive contexts like "personal_preferences", "work_history", "family", "hobbies",
|
||||
"career_goals", "project_details", etc. This helps organize and retrieve related memories later.
|
||||
|
||||
Args:
|
||||
bank_id: **REQUIRED** - The unique, persistent identifier for this specific user (e.g., user_id, email, session_id).
|
||||
This MUST be consistent across all interactions with the same user.
|
||||
Example: "user_12345", "alice@example.com", "session_abc123"
|
||||
content: The fact/memory to store (be specific and include relevant details)
|
||||
context: Categorize the memory (e.g., 'personal_preferences', 'work_history', 'hobbies', 'family')
|
||||
explanation: Optional explanation for why this memory is being stored
|
||||
context: Category for the memory (e.g., 'preferences', 'work', 'hobbies', 'family'). Default: 'general'
|
||||
"""
|
||||
try:
|
||||
# Log explanation if provided
|
||||
if explanation:
|
||||
pass # Explanation provided
|
||||
|
||||
# Store memory using put_batch_async
|
||||
bank_id = get_current_bank_id()
|
||||
await memory.put_batch_async(
|
||||
bank_id=bank_id,
|
||||
contents=[{"content": content, "context": context}]
|
||||
)
|
||||
return f"Fact stored successfully"
|
||||
return "Memory stored successfully"
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing fact: {e}", exc_info=True)
|
||||
logger.error(f"Error storing memory: {e}", exc_info=True)
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
@mcp.tool()
|
||||
async def hindsight_search(bank_id: str, query: str, max_tokens: int = 4096, explanation: str = "") -> str:
|
||||
async def recall(query: str, max_results: int = 10) -> str:
|
||||
"""
|
||||
**CRITICAL: Search user's memory to provide personalized, context-aware responses.**
|
||||
Search memories to provide personalized, context-aware responses.
|
||||
|
||||
**⚠️ PER-USER TOOL - REQUIRES USER IDENTIFICATION:**
|
||||
- This tool is STRICTLY per-user. Each user MUST have a unique `bank_id`.
|
||||
- ONLY use this tool if you have a valid user identifier (user ID, email, session ID, etc.) to map to `bank_id`.
|
||||
- DO NOT use this tool if you cannot identify the specific user.
|
||||
- DO NOT search across multiple users - each user's memories are isolated by their `bank_id`.
|
||||
- If you don't have a user identifier, DO NOT use this tool at all.
|
||||
|
||||
Use this tool PROACTIVELY at the start of conversations or when making recommendations to:
|
||||
- Check user's preferences before making suggestions (e.g., "what foods does the user like?")
|
||||
- Recall user's history to provide continuity (e.g., "what projects has the user worked on?")
|
||||
- Remember user's goals and context (e.g., "what is the user trying to accomplish?")
|
||||
- Avoid repeating information or asking questions you should already know
|
||||
- Personalize responses based on user's background, interests, and past interactions
|
||||
- Reference past conversations or events the user mentioned
|
||||
|
||||
**When to use**:
|
||||
- Start of conversation: Search for relevant context about the user
|
||||
- Before recommendations: Check user preferences and past experiences
|
||||
- When user asks about something they may have mentioned before
|
||||
- To provide continuity across conversations
|
||||
|
||||
**Search tips**: Use natural language queries like "user's programming language preferences",
|
||||
"user's work experience", "user's dietary restrictions", "what does the user know about X?"
|
||||
Use this tool PROACTIVELY to:
|
||||
- Check user's preferences before making suggestions
|
||||
- Recall user's history to provide continuity
|
||||
- Remember user's goals and context
|
||||
- Personalize responses based on past interactions
|
||||
|
||||
Args:
|
||||
bank_id: **REQUIRED** - The unique, persistent identifier for this specific user (e.g., user_id, email, session_id).
|
||||
This MUST be consistent across all interactions with the same user.
|
||||
Example: "user_12345", "alice@example.com", "session_abc123"
|
||||
query: Natural language search query to find relevant memories
|
||||
max_tokens: Maximum tokens for search context (default: 4096)
|
||||
explanation: Optional explanation for why this search is being performed
|
||||
query: Natural language search query (e.g., "user's food preferences", "what projects is user working on")
|
||||
max_results: Maximum number of results to return (default: 10)
|
||||
"""
|
||||
try:
|
||||
# Log all parameters for debugging
|
||||
logger.info(f"hindsight_search called with: query={query!r}, max_tokens={max_tokens}, explanation={explanation!r}")
|
||||
|
||||
# Log explanation if provided
|
||||
if explanation:
|
||||
pass # Explanation provided
|
||||
|
||||
# Search using recall_async
|
||||
bank_id = get_current_bank_id()
|
||||
from hindsight_api.engine.memory_engine import Budget
|
||||
search_result = await memory.recall_async(
|
||||
bank_id=bank_id,
|
||||
query=query,
|
||||
fact_type=["world", "bank", "opinion"], # Search all fact types
|
||||
max_tokens=max_tokens,
|
||||
fact_type=["world", "bank", "opinion"],
|
||||
budget=Budget.LOW
|
||||
)
|
||||
|
||||
# Convert results to dict format
|
||||
results = [
|
||||
{
|
||||
"id": fact.id,
|
||||
"text": fact.text,
|
||||
"type": fact.fact_type,
|
||||
"context": fact.context,
|
||||
"event_date": fact.event_date, # Already a string from the database
|
||||
"document_id": fact.document_id
|
||||
"event_date": fact.event_date,
|
||||
}
|
||||
for fact in search_result.results
|
||||
for fact in search_result.results[:max_results]
|
||||
]
|
||||
|
||||
return json.dumps({"results": results}, indent=2)
|
||||
|
|
@ -155,3 +111,104 @@ def create_mcp_server(memory: MemoryEngine) -> FastMCP:
|
|||
return json.dumps({"error": str(e), "results": []})
|
||||
|
||||
return mcp
|
||||
|
||||
|
||||
class MCPMiddleware:
|
||||
"""ASGI middleware that extracts bank_id from path and sets context."""
|
||||
|
||||
def __init__(self, app, memory: MemoryEngine):
|
||||
self.app = app
|
||||
self.memory = memory
|
||||
self.mcp_server = create_mcp_server(memory)
|
||||
# Use sse_app - http_app requires lifespan management that's complex with middleware
|
||||
import warnings
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
self.mcp_app = self.mcp_server.sse_app()
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
if scope["type"] != "http":
|
||||
await self.mcp_app(scope, receive, send)
|
||||
return
|
||||
|
||||
path = scope.get("path", "")
|
||||
|
||||
# Strip any mount prefix (e.g., /mcp) that FastAPI might not have stripped
|
||||
root_path = scope.get("root_path", "")
|
||||
if root_path and path.startswith(root_path):
|
||||
path = path[len(root_path):] or "/"
|
||||
|
||||
# Also handle case where mount path wasn't stripped (e.g., /mcp/...)
|
||||
if path.startswith("/mcp/"):
|
||||
path = path[4:] # Remove /mcp prefix
|
||||
|
||||
# Extract bank_id from path: /{bank_id}/ or /{bank_id}
|
||||
# http_app expects requests at /
|
||||
if not path.startswith("/") or len(path) <= 1:
|
||||
# No bank_id in path - return error
|
||||
await self._send_error(send, 400, "bank_id required in path: /mcp/{bank_id}/")
|
||||
return
|
||||
|
||||
# Extract bank_id from first path segment
|
||||
parts = path[1:].split("/", 1)
|
||||
if not parts[0]:
|
||||
await self._send_error(send, 400, "bank_id required in path: /mcp/{bank_id}/")
|
||||
return
|
||||
|
||||
bank_id = parts[0]
|
||||
new_path = "/" + parts[1] if len(parts) > 1 else "/"
|
||||
|
||||
# Set bank_id context
|
||||
token = _current_bank_id.set(bank_id)
|
||||
try:
|
||||
new_scope = scope.copy()
|
||||
new_scope["path"] = new_path
|
||||
|
||||
# Wrap send to rewrite the SSE endpoint URL to include bank_id
|
||||
# The SSE app sends "event: endpoint\ndata: /messages\n" but we need
|
||||
# the client to POST to /{bank_id}/messages instead
|
||||
async def send_wrapper(message):
|
||||
if message["type"] == "http.response.body":
|
||||
body = message.get("body", b"")
|
||||
if body and b"/messages" in body:
|
||||
# Rewrite /messages to /{bank_id}/messages in SSE endpoint event
|
||||
body = body.replace(
|
||||
b"data: /messages",
|
||||
f"data: /{bank_id}/messages".encode()
|
||||
)
|
||||
message = {**message, "body": body}
|
||||
await send(message)
|
||||
|
||||
await self.mcp_app(new_scope, receive, send_wrapper)
|
||||
finally:
|
||||
_current_bank_id.reset(token)
|
||||
|
||||
async def _send_error(self, send, status: int, message: str):
|
||||
"""Send an error response."""
|
||||
body = json.dumps({"error": message}).encode()
|
||||
await send({
|
||||
"type": "http.response.start",
|
||||
"status": status,
|
||||
"headers": [(b"content-type", b"application/json")],
|
||||
})
|
||||
await send({
|
||||
"type": "http.response.body",
|
||||
"body": body,
|
||||
})
|
||||
|
||||
|
||||
def create_mcp_app(memory: MemoryEngine):
|
||||
"""
|
||||
Create an ASGI app that handles MCP requests.
|
||||
|
||||
URL pattern: /mcp/{bank_id}/
|
||||
|
||||
The bank_id is extracted from the URL path and made available to tools.
|
||||
|
||||
Args:
|
||||
memory: MemoryEngine instance
|
||||
|
||||
Returns:
|
||||
ASGI application
|
||||
"""
|
||||
return MCPMiddleware(None, memory)
|
||||
|
|
|
|||
|
|
@ -10,13 +10,9 @@ import warnings
|
|||
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated")
|
||||
warnings.filterwarnings("ignore", message="websockets.server.WebSocketServerProtocol is deprecated")
|
||||
|
||||
import asyncio
|
||||
import atexit
|
||||
import logging
|
||||
import os
|
||||
import argparse
|
||||
import signal
|
||||
import sys
|
||||
|
||||
from hindsight_api import MemoryEngine
|
||||
from hindsight_api.api import create_app
|
||||
|
|
@ -25,36 +21,6 @@ from hindsight_api.api import create_app
|
|||
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
||||
|
||||
|
||||
def _cleanup_pg0():
|
||||
"""Synchronous cleanup function to stop pg0 on exit."""
|
||||
global _memory
|
||||
if _memory is not None and _memory._pg0 is not None:
|
||||
try:
|
||||
# Run async stop in a new event loop
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(_memory._pg0.stop())
|
||||
loop.close()
|
||||
print("\npg0 stopped.")
|
||||
except Exception as e:
|
||||
print(f"\nError stopping pg0: {e}")
|
||||
|
||||
|
||||
# Register cleanup on normal exit
|
||||
atexit.register(_cleanup_pg0)
|
||||
|
||||
|
||||
def _signal_handler(signum, frame):
|
||||
"""Handle SIGINT/SIGTERM to ensure pg0 cleanup."""
|
||||
print(f"\nReceived signal {signum}, shutting down...")
|
||||
_cleanup_pg0()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# Register signal handlers for graceful shutdown
|
||||
signal.signal(signal.SIGINT, _signal_handler)
|
||||
signal.signal(signal.SIGTERM, _signal_handler)
|
||||
|
||||
|
||||
# Create app at module level (required for uvicorn import string)
|
||||
_memory = MemoryEngine(
|
||||
db_url=os.getenv("HINDSIGHT_API_DATABASE_URL", "pg0"),
|
||||
|
|
|
|||
|
|
@ -6,30 +6,35 @@ sidebar_position: 3
|
|||
|
||||
Model Context Protocol (MCP) tools exposed by the Hindsight MCP server.
|
||||
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
/mcp/{bank_id}/sse
|
||||
```
|
||||
|
||||
The `bank_id` is extracted from the URL path and used for all tool operations. The MCP server uses Server-Sent Events (SSE) transport.
|
||||
|
||||
## Available Tools
|
||||
|
||||
### hindsight_put
|
||||
### retain
|
||||
|
||||
Store a new memory for a user.
|
||||
Store a new memory.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `bank_id` | string | yes | Unique identifier for the user (e.g., user_id, email) |
|
||||
| `content` | string | yes | Memory content to store |
|
||||
| `context` | string | yes | Category for the memory (e.g., 'personal_preferences', 'work_history') |
|
||||
| `explanation` | string | no | Optional explanation for why this memory is being stored |
|
||||
| `context` | string | no | Category for the memory (default: 'general') |
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hindsight_put",
|
||||
"name": "retain",
|
||||
"arguments": {
|
||||
"bank_id": "user_12345",
|
||||
"content": "User prefers Python for data analysis",
|
||||
"context": "programming_preferences"
|
||||
"context": "preferences"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -37,31 +42,28 @@ Store a new memory for a user.
|
|||
**Response:**
|
||||
|
||||
```
|
||||
Fact stored successfully
|
||||
Memory stored successfully
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### hindsight_search
|
||||
### recall
|
||||
|
||||
Search memories for a user.
|
||||
Search memories.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `bank_id` | string | yes | Unique identifier for the user (e.g., user_id, email) |
|
||||
| `query` | string | yes | Natural language search query |
|
||||
| `max_tokens` | integer | no | Maximum tokens for results (default: 4096) |
|
||||
| `explanation` | string | no | Optional explanation for why this search is being performed |
|
||||
| `max_results` | integer | no | Maximum results to return (default: 10) |
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hindsight_search",
|
||||
"name": "recall",
|
||||
"arguments": {
|
||||
"bank_id": "user_12345",
|
||||
"query": "What does the user do for work?"
|
||||
}
|
||||
}
|
||||
|
|
@ -76,9 +78,8 @@ Search memories for a user.
|
|||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"text": "User works at Google as a software engineer",
|
||||
"type": "world",
|
||||
"context": "work_history",
|
||||
"event_date": null,
|
||||
"document_id": null
|
||||
"context": "work",
|
||||
"event_date": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -88,30 +89,12 @@ Search memories for a user.
|
|||
|
||||
## Usage Guidelines
|
||||
|
||||
The MCP tools are designed for **per-user memory**:
|
||||
|
||||
- Each user MUST have a unique `bank_id` (user ID, email, session ID, etc.)
|
||||
- Memories are isolated by `bank_id` — users cannot access each other's memories
|
||||
- Use consistent `bank_id` values across all interactions with the same user
|
||||
|
||||
**When to use `hindsight_put`:**
|
||||
**When to use `retain`:**
|
||||
- User shares personal facts, preferences, or interests
|
||||
- Important events or milestones are mentioned
|
||||
- Decisions, opinions, or goals are stated
|
||||
- Any information the user would want remembered
|
||||
|
||||
**When to use `hindsight_search`:**
|
||||
**When to use `recall`:**
|
||||
- Start of conversation to get user context
|
||||
- Before making recommendations
|
||||
- To provide continuity across conversations
|
||||
- When user asks about something they may have mentioned before
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
MCP tools return errors as strings:
|
||||
|
||||
```
|
||||
Error: Memory bank 'unknown-bank' not found
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ Hindsight uses several machine learning models for different tasks.
|
|||
|
||||
| Model Type | Purpose | Default | Configurable |
|
||||
|------------|---------|---------|--------------|
|
||||
| **Embedding** | Vector representations for semantic search | `all-MiniLM-L6-v2` | Yes |
|
||||
| **Embedding** | Vector representations for semantic search | `BAAI/bge-small-en-v1.5` | Yes |
|
||||
| **Cross-Encoder** | Reranking search results | `ms-marco-MiniLM-L-6-v2` | Yes |
|
||||
| **Temporal Parser** | Understanding time expressions | `t5-small` | Yes |
|
||||
| **LLM** | Fact extraction, reasoning, generation | Provider-specific | Yes |
|
||||
|
||||
All local models (embedding, cross-encoder, temporal) are automatically downloaded from HuggingFace on first run.
|
||||
All local models (embedding, cross-encoder) are automatically downloaded from HuggingFace on first run.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -19,20 +18,20 @@ All local models (embedding, cross-encoder, temporal) are automatically download
|
|||
|
||||
Converts text into dense vector representations for semantic similarity search.
|
||||
|
||||
**Default:** `sentence-transformers/all-MiniLM-L6-v2` (384 dimensions, ~90MB)
|
||||
**Default:** `BAAI/bge-small-en-v1.5` (384 dimensions, ~130MB)
|
||||
|
||||
**Alternatives:**
|
||||
|
||||
| Model | Dimensions | Use Case |
|
||||
|-------|------------|----------|
|
||||
| `all-MiniLM-L6-v2` | 384 | Default, fast, good quality |
|
||||
| `all-mpnet-base-v2` | 768 | Higher accuracy, slower |
|
||||
| `paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
|
||||
| `BAAI/bge-small-en-v1.5` | 384 | Default, fast, good quality |
|
||||
| `BAAI/bge-base-en-v1.5` | 768 | Higher accuracy, slower |
|
||||
| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 384 | Multilingual (50+ languages) |
|
||||
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
export HINDSIGHT_API_EMBEDDING_MODEL=sentence-transformers/all-mpnet-base-v2
|
||||
export HINDSIGHT_API_EMBEDDING_MODEL=BAAI/bge-base-en-v1.5
|
||||
export HINDSIGHT_API_EMBEDDING_DEVICE=cuda # or mps for Apple Silicon
|
||||
export HINDSIGHT_API_EMBEDDING_BATCH_SIZE=64
|
||||
```
|
||||
|
|
@ -63,31 +62,6 @@ export HINDSIGHT_API_RERANK_ENABLED=true # Set to false to disable
|
|||
|
||||
---
|
||||
|
||||
## Temporal Parser
|
||||
|
||||
Parses natural language time expressions into structured dates.
|
||||
|
||||
**Examples:**
|
||||
- "last spring" → 2024-03-20 to 2024-06-20
|
||||
- "two weeks ago" → calculated date range
|
||||
|
||||
**Default:** `google/t5-small` (~240MB)
|
||||
|
||||
**Alternatives:**
|
||||
|
||||
| Model | Use Case |
|
||||
|-------|----------|
|
||||
| `t5-small` | Default, compact |
|
||||
| `t5-base` | Better accuracy for complex expressions |
|
||||
|
||||
**Configuration:**
|
||||
|
||||
```bash
|
||||
export HINDSIGHT_API_TEMPORAL_MODEL=google/t5-base
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM
|
||||
|
||||
Used for fact extraction, entity resolution, opinion generation, and answer synthesis.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ sidebar_position: 3
|
|||
|
||||
# CLI Reference
|
||||
|
||||
The Hindsight CLI provides command-line access to memory operations and agent management.
|
||||
The Hindsight CLI provides command-line access to memory operations and bank management.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -14,228 +14,194 @@ curl -fsSL https://raw.githubusercontent.com/vectorize-io/hindsight/refs/heads/m
|
|||
|
||||
## Configuration
|
||||
|
||||
Set environment variables or use command flags:
|
||||
Configure the API URL:
|
||||
|
||||
```bash
|
||||
# Interactive configuration
|
||||
hindsight configure
|
||||
|
||||
# Or set directly
|
||||
hindsight configure --api-url http://localhost:8888
|
||||
|
||||
# Or use environment variable (highest priority)
|
||||
export HINDSIGHT_API_URL=http://localhost:8888
|
||||
export HINDSIGHT_AGENT_ID=my-agent
|
||||
```
|
||||
|
||||
## Commands
|
||||
## Core Commands
|
||||
|
||||
### Memory Operations
|
||||
### Retain (Store Memory)
|
||||
|
||||
#### put
|
||||
|
||||
Store a memory:
|
||||
Store a single memory:
|
||||
|
||||
```bash
|
||||
hindsight put <agent_id> "Alice works at Google as a software engineer"
|
||||
hindsight memory retain <bank_id> "Alice works at Google as a software engineer"
|
||||
|
||||
# With context
|
||||
hindsight put <agent_id> "Bob loves hiking" --context "hobby discussion"
|
||||
hindsight memory retain <bank_id> "Bob loves hiking" --context "hobby discussion"
|
||||
|
||||
# With event date
|
||||
hindsight put <agent_id> "Meeting with Carol" --date "2024-01-15"
|
||||
# Queue for background processing
|
||||
hindsight memory retain <bank_id> "Meeting notes" --async
|
||||
```
|
||||
|
||||
#### put-files
|
||||
### Retain Files
|
||||
|
||||
Store file contents as memories:
|
||||
Bulk import from files:
|
||||
|
||||
```bash
|
||||
# Store a single file
|
||||
hindsight put-files <agent_id> notes.txt
|
||||
# Single file
|
||||
hindsight memory retain-files <bank_id> notes.txt
|
||||
|
||||
# Store multiple files
|
||||
hindsight put-files <agent_id> file1.txt file2.md file3.json
|
||||
# Directory (recursive by default)
|
||||
hindsight memory retain-files <bank_id> ./documents/
|
||||
|
||||
# With context
|
||||
hindsight put-files <agent_id> meeting-notes.txt --context "team meeting"
|
||||
hindsight memory retain-files <bank_id> meeting-notes.txt --context "team meeting"
|
||||
|
||||
# Background processing
|
||||
hindsight memory retain-files <bank_id> ./data/ --async
|
||||
```
|
||||
|
||||
#### search
|
||||
### Recall (Search)
|
||||
|
||||
Search memories:
|
||||
Search memories using semantic similarity:
|
||||
|
||||
```bash
|
||||
hindsight search <agent_id> "What does Alice do?"
|
||||
hindsight memory recall <bank_id> "What does Alice do?"
|
||||
|
||||
# With options
|
||||
hindsight search <agent_id> "hiking recommendations" --budget 100 --top-k 5
|
||||
hindsight memory recall <bank_id> "hiking recommendations" \
|
||||
--budget high \
|
||||
--max-tokens 8192
|
||||
|
||||
# Verbose output
|
||||
hindsight search <agent_id> "query" -v
|
||||
# Filter by fact type
|
||||
hindsight memory recall <bank_id> "query" --fact-type world,opinion
|
||||
|
||||
# Show trace information
|
||||
hindsight memory recall <bank_id> "query" --trace
|
||||
```
|
||||
|
||||
#### think
|
||||
### Reflect (Generate Response)
|
||||
|
||||
Generate a response using memories and opinions:
|
||||
Generate a response using memories and bank personality:
|
||||
|
||||
```bash
|
||||
hindsight think <agent_id> "What do you know about Alice?"
|
||||
hindsight memory reflect <bank_id> "What do you know about Alice?"
|
||||
|
||||
# Verbose mode shows reasoning
|
||||
hindsight think <agent_id> "Should I recommend Python or Java?" -v
|
||||
# With additional context
|
||||
hindsight memory reflect <bank_id> "Should I learn Python?" --context "career advice"
|
||||
|
||||
# Higher budget for complex questions
|
||||
hindsight memory reflect <bank_id> "Summarize my week" --budget high
|
||||
```
|
||||
|
||||
### Memory bank Management
|
||||
## Bank Management
|
||||
|
||||
#### memory banks
|
||||
|
||||
List all memory banks:
|
||||
### List Banks
|
||||
|
||||
```bash
|
||||
hindsight memory banks
|
||||
hindsight bank list
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Available memory banks:
|
||||
- alice-agent
|
||||
- bob-agent
|
||||
- tech-advisor
|
||||
```
|
||||
|
||||
#### profile
|
||||
|
||||
View memory bank profile:
|
||||
### View Profile
|
||||
|
||||
```bash
|
||||
hindsight profile <agent_id>
|
||||
hindsight bank profile <bank_id>
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
Memory bank: my-agent
|
||||
|
||||
Personality:
|
||||
Openness: 0.80
|
||||
Conscientiousness: 0.60
|
||||
Extraversion: 0.50
|
||||
Agreeableness: 0.70
|
||||
Neuroticism: 0.30
|
||||
Bias Strength: 0.70
|
||||
|
||||
Background:
|
||||
I am a helpful AI assistant interested in technology.
|
||||
```
|
||||
|
||||
#### set-personality
|
||||
|
||||
Update personality traits:
|
||||
### View Statistics
|
||||
|
||||
```bash
|
||||
hindsight set-personality <agent_id> \
|
||||
--openness 0.8 \
|
||||
--conscientiousness 0.6 \
|
||||
--extraversion 0.5 \
|
||||
--agreeableness 0.7 \
|
||||
--neuroticism 0.3 \
|
||||
--bias-strength 0.7
|
||||
hindsight bank stats <bank_id>
|
||||
```
|
||||
|
||||
#### background
|
||||
|
||||
Add or merge background:
|
||||
### Set Bank Name
|
||||
|
||||
```bash
|
||||
# Set/merge background
|
||||
hindsight background <agent_id> "I have expertise in distributed systems"
|
||||
hindsight bank name <bank_id> "My Assistant"
|
||||
```
|
||||
|
||||
### MCP Server
|
||||
|
||||
Start the MCP server:
|
||||
### Set Background
|
||||
|
||||
```bash
|
||||
hindsight mcp-server
|
||||
hindsight bank background <bank_id> "I am a helpful AI assistant interested in technology"
|
||||
|
||||
# With custom configuration
|
||||
HINDSIGHT_API_URL=http://api.example.com hindsight mcp-server
|
||||
# Skip automatic personality inference
|
||||
hindsight bank background <bank_id> "Background text" --no-update-personality
|
||||
```
|
||||
|
||||
## Document Management
|
||||
|
||||
```bash
|
||||
# List documents
|
||||
hindsight document list <bank_id>
|
||||
|
||||
# Get document details
|
||||
hindsight document get <bank_id> <document_id>
|
||||
|
||||
# Delete document and its memories
|
||||
hindsight document delete <bank_id> <document_id>
|
||||
```
|
||||
|
||||
## Entity Management
|
||||
|
||||
```bash
|
||||
# List entities
|
||||
hindsight entity list <bank_id>
|
||||
|
||||
# Get entity details
|
||||
hindsight entity get <bank_id> <entity_id>
|
||||
|
||||
# Regenerate entity observations
|
||||
hindsight entity regenerate <bank_id> <entity_id>
|
||||
```
|
||||
|
||||
## Output Formats
|
||||
|
||||
### Pretty (Default)
|
||||
|
||||
Human-readable formatted output:
|
||||
|
||||
```bash
|
||||
hindsight search <agent_id> "query"
|
||||
# Pretty (default)
|
||||
hindsight memory recall <bank_id> "query"
|
||||
|
||||
# JSON
|
||||
hindsight memory recall <bank_id> "query" -o json
|
||||
|
||||
# YAML
|
||||
hindsight memory recall <bank_id> "query" -o yaml
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
Machine-readable JSON output:
|
||||
|
||||
```bash
|
||||
hindsight search <agent_id> "query" -o json
|
||||
```
|
||||
|
||||
### YAML
|
||||
|
||||
YAML formatted output:
|
||||
|
||||
```bash
|
||||
hindsight search <agent_id> "query" -o yaml
|
||||
```
|
||||
|
||||
## Verbose Mode
|
||||
|
||||
Add `-v` or `--verbose` for detailed output:
|
||||
|
||||
```bash
|
||||
hindsight search <agent_id> "query" -v
|
||||
```
|
||||
|
||||
Shows:
|
||||
- Request payload
|
||||
- Response details
|
||||
- Timing information
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `-v, --verbose` | Verbose output |
|
||||
| `-v, --verbose` | Show detailed output including request/response |
|
||||
| `-o, --output <format>` | Output format: pretty, json, yaml |
|
||||
| `--api-url <url>` | Override API URL |
|
||||
| `--help` | Show help |
|
||||
| `--version` | Show version |
|
||||
|
||||
## Examples
|
||||
## Interactive Explorer
|
||||
|
||||
### Full Workflow
|
||||
Launch the TUI explorer for visual navigation:
|
||||
|
||||
```bash
|
||||
# Create a memory bank
|
||||
curl -X PUT http://localhost:8888/api/memory banks/demo-agent \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"background": "Demo agent"}'
|
||||
|
||||
# Store memories
|
||||
hindsight put demo-agent "Alice works at Google"
|
||||
hindsight put demo-agent "Bob is a data scientist"
|
||||
hindsight put demo-agent "Alice and Bob are colleagues"
|
||||
|
||||
# Search
|
||||
hindsight search demo-agent "Who works with Alice?"
|
||||
|
||||
# Think (with opinions)
|
||||
hindsight think demo-agent "What do you know about the team?"
|
||||
|
||||
# Update personality
|
||||
hindsight set-personality demo-agent \
|
||||
--openness 0.9 \
|
||||
--conscientiousness 0.7 \
|
||||
--extraversion 0.6 \
|
||||
--agreeableness 0.8 \
|
||||
--neuroticism 0.2 \
|
||||
--bias-strength 0.6
|
||||
|
||||
# Check profile
|
||||
hindsight profile demo-agent
|
||||
hindsight explore
|
||||
```
|
||||
|
||||
## Example Workflow
|
||||
|
||||
```bash
|
||||
# Configure API URL
|
||||
hindsight configure --api-url http://localhost:8888
|
||||
|
||||
# Store some memories
|
||||
hindsight memory retain demo "Alice works at Google"
|
||||
hindsight memory retain demo "Bob is a data scientist"
|
||||
hindsight memory retain demo "Alice and Bob are colleagues"
|
||||
|
||||
# Search memories
|
||||
hindsight memory recall demo "Who works with Alice?"
|
||||
|
||||
# Generate a response
|
||||
hindsight memory reflect demo "What do you know about the team?"
|
||||
|
||||
# Check bank profile
|
||||
hindsight bank profile demo
|
||||
```
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Model Context Protocol server for AI assistants like Claude Desktop.
|
|||
|
||||
## Setup
|
||||
|
||||
The MCP server is included in the Hindsight API. When running the API with MCP enabled (default), it exposes MCP tools via SSE at `/mcp/sse`.
|
||||
The MCP server is included in the Hindsight API. When running the API with MCP enabled, it exposes MCP tools at `/mcp/{bank_id}/sse`.
|
||||
|
||||
### Claude Desktop Configuration
|
||||
|
||||
|
|
@ -19,50 +19,64 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|||
"mcpServers": {
|
||||
"hindsight": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-remote", "http://localhost:8888/mcp/sse"]
|
||||
"args": ["-y", "mcp-remote", "http://localhost:8888/mcp/my-bank-id/sse"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `my-bank-id` with your memory bank ID.
|
||||
|
||||
## Available Tools
|
||||
|
||||
### hindsight_put
|
||||
### retain
|
||||
|
||||
Store a memory for a user:
|
||||
Store a memory:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hindsight_put",
|
||||
"name": "retain",
|
||||
"arguments": {
|
||||
"bank_id": "user_12345",
|
||||
"content": "User prefers Python for data analysis",
|
||||
"context": "programming_preferences"
|
||||
"context": "preferences"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### hindsight_search
|
||||
**Parameters:**
|
||||
|
||||
Search memories for a user:
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `content` | string | yes | Memory content to store |
|
||||
| `context` | string | no | Category (default: 'general') |
|
||||
|
||||
### recall
|
||||
|
||||
Search memories:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "hindsight_search",
|
||||
"name": "recall",
|
||||
"arguments": {
|
||||
"bank_id": "user_12345",
|
||||
"query": "What does the user do for work?"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `query` | string | yes | Natural language search query |
|
||||
| `max_results` | integer | no | Max results (default: 10) |
|
||||
|
||||
## Usage Example
|
||||
|
||||
Once configured, Claude can use Hindsight naturally:
|
||||
|
||||
**User**: "Remember that I prefer morning meetings"
|
||||
|
||||
**Claude**: *Uses hindsight_put*
|
||||
**Claude**: *Uses retain*
|
||||
|
||||
> "I've noted that you prefer morning meetings."
|
||||
|
||||
|
|
@ -70,16 +84,6 @@ Once configured, Claude can use Hindsight naturally:
|
|||
|
||||
**User**: "What do you know about my preferences?"
|
||||
|
||||
**Claude**: *Uses hindsight_search*
|
||||
**Claude**: *Uses recall*
|
||||
|
||||
> "Based on our conversations, you prefer morning meetings and like Python for data analysis."
|
||||
|
||||
## Per-User Memory
|
||||
|
||||
The MCP tools require a `bank_id` for each user:
|
||||
|
||||
- Each user must have a unique `bank_id` (user ID, email, session ID)
|
||||
- Memories are isolated by `bank_id`
|
||||
- Use consistent `bank_id` values across interactions
|
||||
|
||||
See [MCP API Reference](/api-reference/mcp) for full parameter details.
|
||||
|
|
|
|||
|
|
@ -15,133 +15,100 @@ npm install @vectorize-io/hindsight-client
|
|||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { OpenAPI, MemoryStorageService, SearchService } from '@vectorize-io/hindsight-client';
|
||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
||||
|
||||
// Configure base URL
|
||||
OpenAPI.BASE = 'http://localhost:8888';
|
||||
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
|
||||
|
||||
// Store a memory
|
||||
await MemoryStorageService.putApiPutPost({
|
||||
agent_id: 'my-agent',
|
||||
content: 'Alice works at Google as a software engineer',
|
||||
});
|
||||
// Retain a memory
|
||||
await client.retain('my-agent', 'Alice works at Google');
|
||||
|
||||
// Search memories
|
||||
const results = await SearchService.searchApiSearchPost({
|
||||
agent_id: 'my-agent',
|
||||
query: 'What does Alice do?',
|
||||
});
|
||||
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```typescript
|
||||
import { OpenAPI } from '@vectorize-io/hindsight-client';
|
||||
|
||||
OpenAPI.BASE = 'http://localhost:8888';
|
||||
OpenAPI.TOKEN = 'your-api-token'; // If authentication is enabled
|
||||
```
|
||||
|
||||
## Memory Operations
|
||||
|
||||
### Store Memory
|
||||
|
||||
```typescript
|
||||
import { MemoryStorageService } from '@vectorize-io/hindsight-client';
|
||||
|
||||
await MemoryStorageService.putApiPutPost({
|
||||
agent_id: 'my-agent',
|
||||
content: 'Alice works at Google as a software engineer',
|
||||
context: 'career discussion',
|
||||
event_date: '2024-01-15T10:00:00Z',
|
||||
});
|
||||
```
|
||||
|
||||
### Store Batch
|
||||
|
||||
```typescript
|
||||
await MemoryStorageService.batchApiMemoriesBatchPost({
|
||||
agent_id: 'my-agent',
|
||||
items: [
|
||||
{ content: 'Alice works at Google', context: 'career' },
|
||||
{ content: 'Bob is a data scientist', context: 'career' },
|
||||
],
|
||||
document_id: 'conversation_001',
|
||||
});
|
||||
```
|
||||
|
||||
## Search Operations
|
||||
|
||||
### Basic Search
|
||||
|
||||
```typescript
|
||||
import { SearchService } from '@vectorize-io/hindsight-client';
|
||||
|
||||
const results = await SearchService.searchApiSearchPost({
|
||||
agent_id: 'my-agent',
|
||||
query: 'What does Alice do?',
|
||||
});
|
||||
|
||||
for (const r of results.results) {
|
||||
console.log(`${r.text} (weight: ${r.weight})`);
|
||||
// Recall memories
|
||||
const response = await client.recall('my-agent', 'What does Alice do?');
|
||||
for (const r of response.results) {
|
||||
console.log(r.text);
|
||||
}
|
||||
|
||||
// Reflect - generate response with personality
|
||||
const answer = await client.reflect('my-agent', 'Tell me about Alice');
|
||||
console.log(answer.text);
|
||||
```
|
||||
|
||||
### Advanced Search
|
||||
## Client Initialization
|
||||
|
||||
```typescript
|
||||
const results = await SearchService.recallApiRecallPost({
|
||||
bank_id: 'my-agent',
|
||||
query: 'What does Alice do?',
|
||||
budget: 'low', // 'low', 'mid', or 'high'
|
||||
top_k: 10,
|
||||
});
|
||||
```
|
||||
|
||||
### Search World Facts
|
||||
|
||||
```typescript
|
||||
const worldFacts = await SearchService.worldSearchApiWorldSearchPost({
|
||||
agent_id: 'my-agent',
|
||||
query: 'Who works at Google?',
|
||||
});
|
||||
```
|
||||
|
||||
### Search Opinions
|
||||
|
||||
```typescript
|
||||
const opinions = await SearchService.opinionSearchApiOpinionSearchPost({
|
||||
agent_id: 'my-agent',
|
||||
query: 'What do I think about Python?',
|
||||
});
|
||||
```
|
||||
|
||||
## Reflect (Generate Response)
|
||||
|
||||
```typescript
|
||||
import { ReasoningService } from '@vectorize-io/hindsight-client';
|
||||
|
||||
const response = await ReasoningService.reflectApiReflectPost({
|
||||
bank_id: 'my-agent',
|
||||
query: 'What should I know about Alice?',
|
||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
||||
|
||||
const client = new HindsightClient({
|
||||
baseUrl: 'http://localhost:8888',
|
||||
});
|
||||
```
|
||||
|
||||
## Core Operations
|
||||
|
||||
### Retain (Store Memory)
|
||||
|
||||
```typescript
|
||||
// Simple
|
||||
await client.retain('my-agent', 'Alice works at Google');
|
||||
|
||||
// With options
|
||||
await client.retain('my-agent', 'Alice got promoted', {
|
||||
timestamp: new Date('2024-01-15'),
|
||||
context: 'career update',
|
||||
metadata: { source: 'slack' },
|
||||
async: false, // Set true for background processing
|
||||
});
|
||||
```
|
||||
|
||||
### Retain Batch
|
||||
|
||||
```typescript
|
||||
await client.retainBatch('my-agent', [
|
||||
{ content: 'Alice works at Google', context: 'career' },
|
||||
{ content: 'Bob is a data scientist', context: 'career' },
|
||||
], {
|
||||
documentId: 'conversation_001',
|
||||
async: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Recall (Search)
|
||||
|
||||
```typescript
|
||||
// Simple - returns RecallResponse
|
||||
const response = await client.recall('my-agent', '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?', {
|
||||
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?', {
|
||||
budget: 'low', // 'low', 'mid', or 'high'
|
||||
context: 'preparing for a meeting',
|
||||
});
|
||||
|
||||
console.log(response.text); // Generated response
|
||||
console.log(response.based_on); // Memories used
|
||||
console.log(response.new_opinions); // New opinions formed
|
||||
console.log(answer.text); // Generated response
|
||||
console.log(answer.based_on); // Memories used
|
||||
```
|
||||
|
||||
## Memory bank Management
|
||||
## Bank Management
|
||||
|
||||
### Create Memory bank
|
||||
### Create Bank
|
||||
|
||||
```typescript
|
||||
import { ManagementService } from '@vectorize-io/hindsight-client';
|
||||
|
||||
await ManagementService.createAgentApiAgentsAgentIdPut('my-agent', {
|
||||
await client.createBank('my-agent', {
|
||||
name: 'Assistant',
|
||||
background: 'I am a helpful AI assistant',
|
||||
personality: {
|
||||
|
|
@ -155,55 +122,26 @@ await ManagementService.createAgentApiAgentsAgentIdPut('my-agent', {
|
|||
});
|
||||
```
|
||||
|
||||
### Get Profile
|
||||
### Get Bank Profile
|
||||
|
||||
```typescript
|
||||
const profile = await ManagementService.getProfileApiAgentsAgentIdProfileGet('my-agent');
|
||||
const profile = await client.getBankProfile('my-agent');
|
||||
console.log(profile.personality);
|
||||
console.log(profile.background);
|
||||
```
|
||||
|
||||
### List Memory banks
|
||||
### List Memories
|
||||
|
||||
```typescript
|
||||
const memory banks = await ManagementService.listAgentsApiAgentsGet();
|
||||
for (const agent of memory banks.memory banks) {
|
||||
console.log(agent.agent_id);
|
||||
}
|
||||
```
|
||||
|
||||
### Update Personality
|
||||
|
||||
```typescript
|
||||
await ManagementService.updatePersonalityApiAgentsAgentIdProfilePut('my-agent', {
|
||||
openness: 0.9,
|
||||
conscientiousness: 0.7,
|
||||
const response = await client.listMemories('my-agent', {
|
||||
type: 'world', // Optional filter
|
||||
q: 'Alice', // Optional text search
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
});
|
||||
```
|
||||
|
||||
### Merge Background
|
||||
|
||||
```typescript
|
||||
await ManagementService.mergeBackgroundApiAgentsAgentIdBackgroundPost('my-agent', {
|
||||
background: 'Additional context to merge',
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
import { ApiError } from '@vectorize-io/hindsight-client';
|
||||
|
||||
try {
|
||||
await SearchService.searchApiSearchPost({
|
||||
agent_id: 'unknown-agent',
|
||||
query: 'test',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) {
|
||||
console.log(`Error: ${error.message}`);
|
||||
console.log(`Status: ${error.status}`);
|
||||
}
|
||||
for (const memory of response.memories) {
|
||||
console.log(`${memory.id}: ${memory.text}`);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -213,19 +151,91 @@ The client exports all types for full TypeScript support:
|
|||
|
||||
```typescript
|
||||
import type {
|
||||
AgentProfile,
|
||||
SearchResult,
|
||||
ThinkResponse,
|
||||
MemoryItem,
|
||||
PersonalityTraits,
|
||||
RetainResponse,
|
||||
RecallResponse,
|
||||
RecallResult,
|
||||
ReflectResponse,
|
||||
BankProfileResponse,
|
||||
Budget,
|
||||
} from '@vectorize-io/hindsight-client';
|
||||
|
||||
const personality: PersonalityTraits = {
|
||||
openness: 0.7,
|
||||
conscientiousness: 0.8,
|
||||
extraversion: 0.5,
|
||||
agreeableness: 0.6,
|
||||
neuroticism: 0.3,
|
||||
bias_strength: 0.5,
|
||||
};
|
||||
// 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 personality
|
||||
await client.createBank('demo', {
|
||||
name: 'Demo Agent',
|
||||
background: 'A helpful assistant for demos',
|
||||
personality: {
|
||||
openness: 0.8,
|
||||
conscientiousness: 0.7,
|
||||
extraversion: 0.6,
|
||||
agreeableness: 0.8,
|
||||
neuroticism: 0.2,
|
||||
bias_strength: 0.5,
|
||||
},
|
||||
});
|
||||
|
||||
// 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);
|
||||
```
|
||||
|
|
|
|||
|
|
@ -48,17 +48,17 @@ with HindsightServer(
|
|||
) as server:
|
||||
client = HindsightClient(base_url=server.url)
|
||||
|
||||
# Store a memory
|
||||
client.put(agent_id="my-agent", content="Alice works at Google")
|
||||
# Retain a memory
|
||||
client.retain(bank_id="my-agent", content="Alice works at Google")
|
||||
|
||||
# Search memories
|
||||
results = client.search(agent_id="my-agent", query="What does Alice do?")
|
||||
# Recall memories
|
||||
results = client.recall(bank_id="my-agent", query="What does Alice do?")
|
||||
for r in results:
|
||||
print(r["text"], r["weight"])
|
||||
print(r.text)
|
||||
|
||||
# Generate response with personality
|
||||
answer = client.think(agent_id="my-agent", query="Tell me about Alice")
|
||||
print(answer["text"])
|
||||
# Reflect - generate response with personality
|
||||
answer = client.reflect(bank_id="my-agent", query="Tell me about Alice")
|
||||
print(answer.text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -69,17 +69,17 @@ from hindsight_client import Hindsight
|
|||
|
||||
client = Hindsight(base_url="http://localhost:8888")
|
||||
|
||||
# Store a memory
|
||||
client.put(agent_id="my-agent", content="Alice works at Google")
|
||||
# Retain a memory
|
||||
client.retain(bank_id="my-agent", content="Alice works at Google")
|
||||
|
||||
# Search memories
|
||||
results = client.search(agent_id="my-agent", query="What does Alice do?")
|
||||
# Recall memories
|
||||
results = client.recall(bank_id="my-agent", query="What does Alice do?")
|
||||
for r in results:
|
||||
print(r["text"], r["weight"])
|
||||
print(r.text)
|
||||
|
||||
# Generate response with personality
|
||||
answer = client.think(agent_id="my-agent", query="Tell me about Alice")
|
||||
print(answer["text"])
|
||||
# Reflect - generate response with personality
|
||||
answer = client.reflect(bank_id="my-agent", query="Tell me about Alice")
|
||||
print(answer.text)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -96,101 +96,112 @@ client = Hindsight(
|
|||
)
|
||||
```
|
||||
|
||||
## Memory Operations
|
||||
## Core Operations
|
||||
|
||||
### Store Single Memory
|
||||
### Retain (Store Memory)
|
||||
|
||||
```python
|
||||
client.store(
|
||||
agent_id="my-agent",
|
||||
# Simple
|
||||
client.retain(
|
||||
bank_id="my-agent",
|
||||
content="Alice works at Google as a software engineer",
|
||||
context="career discussion", # Optional context
|
||||
event_date="2024-01-15T10:00:00Z", # Optional event date
|
||||
)
|
||||
|
||||
# With options
|
||||
from datetime import datetime
|
||||
|
||||
client.retain(
|
||||
bank_id="my-agent",
|
||||
content="Alice got promoted",
|
||||
context="career update",
|
||||
timestamp=datetime(2024, 1, 15),
|
||||
document_id="conversation_001",
|
||||
metadata={"source": "slack"},
|
||||
)
|
||||
```
|
||||
|
||||
### Store Batch
|
||||
### Retain Batch
|
||||
|
||||
```python
|
||||
client.store_batch(
|
||||
agent_id="my-agent",
|
||||
client.retain_batch(
|
||||
bank_id="my-agent",
|
||||
items=[
|
||||
{"content": "Alice works at Google", "context": "career"},
|
||||
{"content": "Bob is a data scientist", "context": "career"},
|
||||
],
|
||||
document_id="conversation_001", # Optional grouping
|
||||
document_id="conversation_001",
|
||||
retain_async=False, # Set True for background processing
|
||||
)
|
||||
```
|
||||
|
||||
## Search Operations
|
||||
|
||||
### Basic Search
|
||||
### Recall (Search)
|
||||
|
||||
```python
|
||||
results = client.search(
|
||||
agent_id="my-agent",
|
||||
# Simple - returns list of RecallResult
|
||||
results = client.recall(
|
||||
bank_id="my-agent",
|
||||
query="What does Alice do?",
|
||||
)
|
||||
|
||||
for r in results:
|
||||
print(f"{r['text']} (weight: {r['weight']})")
|
||||
```
|
||||
print(f"{r.text} (type: {r.type})")
|
||||
|
||||
### Advanced Search
|
||||
|
||||
```python
|
||||
results = client.search_memories(
|
||||
agent_id="my-agent",
|
||||
# With options
|
||||
results = client.recall(
|
||||
bank_id="my-agent",
|
||||
query="What does Alice do?",
|
||||
fact_type=["world", "agent"], # Filter by type
|
||||
max_tokens=4096, # Token budget for results
|
||||
top_k=10, # Max results
|
||||
types=["world", "opinion"], # Filter by fact type
|
||||
max_tokens=4096,
|
||||
budget="high", # low, mid, or high
|
||||
)
|
||||
```
|
||||
|
||||
### Search by Fact Type
|
||||
### Recall with Full Response
|
||||
|
||||
```python
|
||||
# Search only world facts
|
||||
world_facts = client.search_memories(
|
||||
agent_id="my-agent",
|
||||
query="Who works at Google?",
|
||||
fact_type=["world"],
|
||||
# Returns RecallResponse with entities and trace info
|
||||
response = client.recall_memories(
|
||||
bank_id="my-agent",
|
||||
query="What does Alice do?",
|
||||
types=["world", "bank"],
|
||||
budget="mid",
|
||||
max_tokens=4096,
|
||||
trace=True,
|
||||
include_entities=True,
|
||||
max_entity_tokens=500,
|
||||
)
|
||||
|
||||
# Search only opinions
|
||||
opinions = client.search_memories(
|
||||
agent_id="my-agent",
|
||||
query="What do I think about Python?",
|
||||
fact_type=["opinion"],
|
||||
)
|
||||
print(f"Found {len(response.results)} memories")
|
||||
for r in response.results:
|
||||
print(f" - {r.text}")
|
||||
|
||||
# Access entities
|
||||
if response.entities:
|
||||
for entity in response.entities:
|
||||
print(f"Entity: {entity.name}")
|
||||
```
|
||||
|
||||
## Think (Generate Response)
|
||||
|
||||
Generate personality-aware responses using retrieved memories:
|
||||
### Reflect (Generate Response)
|
||||
|
||||
```python
|
||||
from hindsight_api.engine.memory_engine import Budget
|
||||
|
||||
answer = client.reflect(
|
||||
bank_id="my-agent",
|
||||
query="What should I know about Alice?",
|
||||
budget=Budget.LOW, # Budget level for retrieval
|
||||
budget="low", # low, mid, or high
|
||||
context="preparing for a meeting",
|
||||
)
|
||||
|
||||
print(answer["text"]) # Generated response
|
||||
print(answer["based_on"]) # Memories used
|
||||
print(answer["new_opinions"]) # New opinions formed
|
||||
print(answer.text) # Generated response
|
||||
print(answer.based_on) # Memories used
|
||||
```
|
||||
|
||||
## Memory bank Management
|
||||
## Bank Management
|
||||
|
||||
### Create Memory bank
|
||||
### Create Bank
|
||||
|
||||
```python
|
||||
client.create_agent(
|
||||
agent_id="my-agent",
|
||||
client.create_bank(
|
||||
bank_id="my-agent",
|
||||
name="Assistant",
|
||||
background="I am a helpful AI assistant",
|
||||
personality={
|
||||
|
|
@ -204,69 +215,72 @@ client.create_agent(
|
|||
)
|
||||
```
|
||||
|
||||
### Get Profile
|
||||
### List Memories
|
||||
|
||||
```python
|
||||
profile = client.get_profile(agent_id="my-agent")
|
||||
print(profile["personality"])
|
||||
print(profile["background"])
|
||||
```
|
||||
|
||||
### List Memory banks
|
||||
|
||||
```python
|
||||
memory banks = client.list_agents()
|
||||
for agent in memory banks:
|
||||
print(agent["agent_id"])
|
||||
```
|
||||
|
||||
### Update Personality
|
||||
|
||||
```python
|
||||
client.update_personality(
|
||||
agent_id="my-agent",
|
||||
openness=0.9,
|
||||
conscientiousness=0.7,
|
||||
response = client.list_memories(
|
||||
bank_id="my-agent",
|
||||
type="world", # Optional: filter by type
|
||||
search_query="Alice", # Optional: text search
|
||||
limit=100,
|
||||
offset=0,
|
||||
)
|
||||
```
|
||||
|
||||
### Update Background
|
||||
|
||||
```python
|
||||
client.update_background(
|
||||
agent_id="my-agent",
|
||||
background="Additional context to merge with existing background",
|
||||
)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from hindsight_client import Hindsight, HindsightError
|
||||
|
||||
client = Hindsight(base_url="http://localhost:8888")
|
||||
|
||||
try:
|
||||
results = client.search(agent_id="unknown", query="test")
|
||||
except HindsightError as e:
|
||||
print(f"Error: {e.message}")
|
||||
print(f"Status: {e.status}")
|
||||
for memory in response.memories:
|
||||
print(f"{memory.id}: {memory.text}")
|
||||
```
|
||||
|
||||
## Async Support
|
||||
|
||||
All methods have async versions prefixed with `a`:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hindsight_client import AsyncHindsight
|
||||
from hindsight_client import Hindsight
|
||||
|
||||
async def main():
|
||||
client = AsyncHindsight(base_url="http://localhost:8888")
|
||||
client = Hindsight(base_url="http://localhost:8888")
|
||||
|
||||
# All methods have async versions
|
||||
await client.store(agent_id="my-agent", content="Hello world")
|
||||
results = await client.search(agent_id="my-agent", query="Hello")
|
||||
# Async retain
|
||||
await client.aretain(bank_id="my-agent", content="Hello world")
|
||||
|
||||
print(results)
|
||||
# Async recall
|
||||
results = await client.arecall(bank_id="my-agent", query="Hello")
|
||||
for r in results:
|
||||
print(r.text)
|
||||
|
||||
# Async reflect
|
||||
answer = await client.areflect(bank_id="my-agent", query="What did I say?")
|
||||
print(answer.text)
|
||||
|
||||
client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Response Types
|
||||
|
||||
The client exports response types for type hints:
|
||||
|
||||
```python
|
||||
from hindsight_client import (
|
||||
Hindsight,
|
||||
RetainResponse,
|
||||
RecallResponse,
|
||||
RecallResult,
|
||||
ReflectResponse,
|
||||
BankProfileResponse,
|
||||
PersonalityTraits,
|
||||
)
|
||||
```
|
||||
|
||||
## 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 automatically closed
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue