{
- const client = createClient({...});
- const entries = await client.getEntries({ content_type: 'blogPost' });
- return entries.items.map(transformPost);
-}
-```
-
-### 2. Content Transformation
-
-Ensure your content is formatted for semantic search:
-
-```typescript
-function formatPostContent(post: BlogPost): string {
- return `# ${post.title}
-
-**Published:** ${post.date}
-...
-${post.content}`;
-}
-```
-
-### 3. Document ID Strategy
-
-Use a consistent document ID for upsert behavior:
-
-```typescript
-await retainBlogPost(content, {
- documentId: `post:${post.slug}`, // Unique, stable identifier
- timestamp: post.date,
-});
-```
-
-## Troubleshooting
-
-### "Connection refused" error
-
-Make sure Hindsight is running:
-```bash
-docker compose up -d
-curl http://localhost:8888/health
-```
-
-### "No posts found" during sync
-
-Check your Sanity configuration:
-```bash
-# Verify project ID
-echo $SANITY_PROJECT_ID
-
-# Test GROQ query
-npx sanity query '*[_type == "post"][0..2]{title}'
-```
-
-### Slow recall/reflect responses
-
-This is normal for the first query as Hindsight builds embeddings. Subsequent queries are faster. Use `budget: 'low'` for faster responses at the cost of recall quality.
-
-## Resources
-
-- [Hindsight Documentation](https://hindsight.vectorize.io/)
-- [Hindsight GitHub](https://github.com/vectorize-io/hindsight)
-- [Sanity CMS Documentation](https://www.sanity.io/docs)
-- [Hindsight Cookbook](https://github.com/vectorize-io/hindsight-cookbook)
-
-## License
-
-MIT
diff --git a/skills/hindsight-docs/references/cookbook/applications/stancetracker.md b/skills/hindsight-docs/references/cookbook/applications/stancetracker.md
deleted file mode 100644
index 5d464017..00000000
--- a/skills/hindsight-docs/references/cookbook/applications/stancetracker.md
+++ /dev/null
@@ -1,276 +0,0 @@
----
-sidebar_position: 8
----
-
-# Stance Tracker
-
-
-:::info Complete Application
-This is a complete, runnable application demonstrating Hindsight integration.
-[**View source on GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/stancetracker)
-:::
-
-
-An AI-powered application that tracks political candidates' stances on issues over time using Hindsight memory system and web scraping.
-
-## Features
-
-- **Geographic Targeting**: Track stances by country, state/province, and city
-- **Multi-Candidate Tracking**: Monitor multiple candidates simultaneously
-- **Temporal Analysis**: Historical stance tracking with configurable time ranges
-- **Automated Scraping**: Periodic content collection with configurable frequencies (hourly/daily/weekly)
-- **Stance Change Detection**: Automatic detection and highlighting of position changes
-- **Interactive Timeline**: Visual graph showing stance evolution with reference callouts
-- **Source Attribution**: All stances linked to verified sources with excerpts
-
-## Architecture
-
-### Memory System (Hindsight Integration)
-
-This app uses the Hindsight memory system from `github.com/vectorize-io/hindsight`:
-
-1. **Banks**: Each scraper agent has its own memory bank
-2. **Retain**: Stores candidate statements and web scraping results
-3. **Recall**: Semantic search to retrieve relevant memories
-4. **Reflect**: Generates contextual analysis using stored memories
-5. **Temporal Search**: Queries memories within specific time periods
-
-### Tech Stack
-
-- **Frontend**: Next.js 16, React, TypeScript, TailwindCSS
-- **Visualization**: Recharts for timeline graphs
-- **Backend**: Next.js API routes
-- **Memory**: Hindsight (from github.com/vectorize-io/hindsight)
-- **Database**: JSON file storage (no database required)
-- **Web Search**: Tavily API
-- **LLM**: OpenAI/Anthropic/Groq (configurable)
-- **Scheduling**: node-cron
-
-## Prerequisites
-
-1. **Hindsight API** running (from github.com/vectorize-io/hindsight)
-2. **API Keys**:
- - Tavily API key (for web search)
- - LLM provider API key (OpenAI, Anthropic, or Groq)
-
-## Setup
-
-### 1. Install Dependencies
-
-```bash
-npm install
-```
-
-### 2. Configure Environment
-
-Copy `.env.example` to `.env` and fill in your credentials:
-
-```bash
-cp .env.example .env
-```
-
-Edit `.env`:
-
-```env
-# Hindsight API (from github.com/vectorize-io/hindsight)
-HINDSIGHT_API_URL=http://localhost:8888
-
-# Tavily API (for web search)
-TAVILY_API_KEY=your_tavily_api_key_here
-
-# LLM Provider
-LLM_PROVIDER=openai # or anthropic, groq
-LLM_API_KEY=your_llm_api_key_here
-LLM_MODEL=gpt-4-turbo-preview
-```
-
-### 3. Start Hindsight
-
-Clone and run Hindsight from github.com/vectorize-io/hindsight:
-
-```bash
-# Clone and run github.com/vectorize-io/hindsight
-cd /path/to/hindsight
-cargo run --bin hindsight-server
-```
-
-Verify Hindsight is running at `http://localhost:8888`
-
-### 4. Run the Application
-
-```bash
-npm run dev
-```
-
-Visit `http://localhost:3000`
-
-## Usage
-
-### Creating a Tracking Session
-
-1. **Set Location**: Enter country (required), state/province, and city (optional)
-2. **Choose Topic**: Specify the issue to track (e.g., "Climate Change Policy")
-3. **Add Candidates**: Enter names of candidates/politicians to track
-4. **Configure Time Range**: Set historical start/end dates for initial analysis
-5. **Set Frequency**: Choose how often to check for updates (hourly/daily/weekly)
-6. **Start Tracking**: Click "Start Tracking" to begin
-
-### Viewing Results
-
-- **Timeline Graph**: Shows confidence levels of each candidate's stance over time
-- **Stance Changes**: Red circles on the graph indicate detected position changes
-- **Click Points**: Click any point to see detailed stance information and sources
-- **Source Links**: Each stance includes links to original references
-
-### Managing Sessions
-
-- **Pause/Resume**: Temporarily stop or restart tracking
-- **Run Now**: Trigger an immediate update outside the schedule
-- **Status**: View current session status and frequency
-
-## API Endpoints
-
-### Sessions
-
-- `POST /api/sessions` - Create new tracking session
-- `GET /api/sessions?id={id}` - Get session details
-- `GET /api/sessions` - List all sessions
-- `PATCH /api/sessions` - Update session status
-
-### Stances
-
-- `POST /api/stances` - Process candidate stance
-- `GET /api/stances?sessionId={id}&candidate={name}` - Get stances
-
-### Scheduler
-
-- `POST /api/scheduler` - Control session scheduling
- - Actions: `start`, `stop`, `run`
-
-## Hindsight Integration Examples
-
-### 1. Storing Memories
-
-```typescript
-// Store web scraping results
-await hindsightClient.retain(bankId, articleContent, {
- context: 'web_search_result',
- timestamp: articleDate,
- metadata: { url: articleUrl }
-});
-```
-
-### 2. Semantic Search
-
-```typescript
-// Search for relevant memories
-const results = await hindsightClient.recall(bankId, query, {
- budget: 'high',
- maxTokens: 8192
-});
-```
-
-### 3. Temporal Filtering
-
-```typescript
-// Query memories up to a specific point in time
-const results = await hindsightClient.recall(bankId, query, {
- queryTimestamp: '2024-12-01T00:00:00Z'
-});
-```
-
-### 4. Contextual Analysis
-
-```typescript
-// Generate analysis using stored memories
-const response = await hindsightClient.reflect(bankId,
- 'What is the candidate\'s stance on this issue?',
- { budget: 'high' }
-);
-```
-
-## Production Deployment
-
-### Vercel Deployment
-
-```bash
-# Install Vercel CLI
-npm i -g vercel
-
-# Deploy
-vercel
-
-# Set environment variables in Vercel dashboard:
-# - HINDSIGHT_API_URL
-# - TAVILY_API_KEY
-# - LLM_PROVIDER
-# - LLM_API_KEY
-# - LLM_MODEL
-```
-
-**Note**: The `data/` directory for JSON storage will be ephemeral on Vercel. For production, consider using a persistent database or object storage.
-
-## Development
-
-### Project Structure
-
-```
-stancetracker/
-├── app/
-│ ├── api/ # API routes
-│ ├── globals.css # Global styles
-│ ├── layout.tsx # Root layout
-│ └── page.tsx # Main page
-├── components/ # React components
-├── lib/
-│ ├── db/ # JSON database utilities
-│ ├── hindsight-client.ts # Hindsight API client
-│ ├── llm-client.ts # LLM provider client
-│ ├── web-scraper.ts # Tavily web scraper
-│ ├── scraper-agent.ts # Content scraper
-│ ├── rag-system.ts # Memory retrieval
-│ ├── stance-extractor.ts # Stance analysis
-│ ├── stance-pipeline.ts # Main pipeline
-│ └── scheduler.ts # Job scheduling
-└── types/ # TypeScript types
-```
-
-### Adding New LLM Providers
-
-Edit `lib/llm-client.ts` and add a new method:
-
-```typescript
-private async newProviderComplete(messages, options) {
- // Implementation
-}
-```
-
-## Limitations
-
-- **Web Search**: Uses Tavily API which has rate limits
-- **Source Verification**: Manual verification recommended for critical applications
-- **Stance Extraction**: LLM-based, subject to model limitations
-- **Storage**: JSON file storage is not suitable for high-scale production use
-- **Rate Limits**: Respect API rate limits for Tavily, Hindsight, and LLM providers
-
-## Future Enhancements
-
-- [ ] Real-time social media monitoring
-- [ ] Speech/video transcription analysis
-- [ ] Multi-language support
-- [ ] Sentiment analysis integration
-- [ ] Comparative analysis dashboard
-- [ ] Export to CSV/PDF
-- [ ] Email notifications for stance changes
-- [ ] Public API for third-party integrations
-
-## License
-
-MIT
-
-## Support
-
-For issues or questions, please check:
-- Hindsight documentation: `github.com/vectorize-io/hindsight/README.md`
-- Tavily API docs: https://tavily.com/
-- Project issues: Create an issue in the repository
diff --git a/skills/hindsight-docs/references/cookbook/applications/taste-ai.md b/skills/hindsight-docs/references/cookbook/applications/taste-ai.md
deleted file mode 100644
index b6883c16..00000000
--- a/skills/hindsight-docs/references/cookbook/applications/taste-ai.md
+++ /dev/null
@@ -1,122 +0,0 @@
----
-sidebar_position: 9
----
-
-# Hindsight AI SDK - Personal Chef
-
-
-:::info Complete Application
-This is a complete, runnable application demonstrating Hindsight integration.
-[**View source on GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/taste-ai)
-:::
-
-
-A personal food assistant demonstrating three key Hindsight integrations using the [Vercel AI SDK v6](https://sdk.vercel.ai/docs).
-
-## Architecture: Single Bank with User Tags
-
-This demo uses a **single Hindsight bank** (`taste-ai`) for all users, with each user's data tagged using `user:${username}`.
-
-```typescript
-// All users share the same bank
-const BANK_ID = 'taste-ai';
-
-// Each memory is tagged with the user
-await hindsightTools.retain.execute({
- bankId: BANK_ID,
- content: userData,
- tags: [`user:${username}`],
-});
-```
-
-This architecture enables:
-- **Per-user queries**: Filter by `user:alice` to get personalized results
-- **Aggregated insights**: Query across all users to find popular recipes or common dietary patterns
-- **Simplified management**: One bank to maintain instead of per-user banks
-
-## Three Hindsight Integrations
-
-### 1. Meal Suggestions with Memory Recall & Reflection
-
-Uses `recall` and `reflect` tools with AI SDK's agent-based approach to gather personalized context.
-
-```typescript
-const contextResult = await generateText({
- model: llmModel,
- tools: {
- recall: hindsightTools.recall,
- reflect: hindsightTools.reflect,
- },
- toolChoice: 'auto',
- prompt: `You are gathering context for personalized ${mealType} recipe suggestions.
-
-Use the recall tool to search for the user's food preferences, dislikes, and recent meals.
-Then use the reflect tool to analyze their dietary patterns and restrictions.
-
-After gathering context, summarize their preferences and recent eating patterns.`,
-});
-```
-
-The AI agent autonomously:
-- Searches memory for cuisine preferences and dietary restrictions
-- Analyzes recent protein consumption for variety
-- Identifies foods to avoid
-
-### 2. Goal Progress Tracking with Mental Models
-
-Uses mental models to automatically maintain updated insights about user progress.
-
-```typescript
-// Create a mental model that auto-refreshes after new meals
-await hindsightTools.createMentalModel.execute({
- bankId: BANK_ID,
- mentalModelId: getMentalModelId(username, 'goals'),
- name: `${username}'s Goal Progress`,
- sourceQuery: `Analyze ${username}'s dietary goals and eating patterns.
- Describe their progress towards their stated goals (weight loss, muscle gain, etc.).`,
- tags: [`user:${username}`],
- autoRefresh: true, // Refreshes automatically after consolidation
-});
-
-// Query the mental model for current insights
-const result = await hindsightTools.queryMentalModel.execute({
- bankId: BANK_ID,
- mentalModelId: mentalModelId,
-});
-```
-
-Mental models automatically:
-- Track progress towards dietary goals
-- Update after each new meal is logged
-- Provide fresh insights without manual refresh
-
-### 3. Language Enforcement with Directives
-
-Uses directives to ensure all responses match user's language preference.
-
-```typescript
-await hindsightClient.createDirective(BANK_ID, {
- name: `${username}'s Language Preference`,
- content: `Always respond in ${language}. All suggestions must be in ${language}.`,
- priority: 100,
- tags: [`user:${username}`, 'directive:language'],
-});
-```
-
-Directives are automatically injected when mental models generate insights, ensuring consistent language across all interactions.
-
-## Running the Demo
-
-```bash
-npm install
-npm run dev
-```
-
-**Requirements:**
-- Hindsight server running at `http://localhost:8888` (or set `HINDSIGHT_URL`)
-- Node.js 18+
-
-## Learn More
-
-- [Hindsight AI SDK on npm](https://www.npmjs.com/package/@vectorize-io/hindsight-ai-sdk)
-- [AI SDK Documentation](https://sdk.vercel.ai/docs)
diff --git a/skills/hindsight-docs/references/cookbook/index.md b/skills/hindsight-docs/references/cookbook/index.md
deleted file mode 100644
index ba631b27..00000000
--- a/skills/hindsight-docs/references/cookbook/index.md
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
-
-# Cookbook
-
-Learn how to build with Hindsight through practical examples:
-
-- **Recipes** - Step-by-step guides and patterns for common use cases
-- **Applications** - Complete, runnable applications demonstrating Hindsight integration
-
-
-
-
-
-
diff --git a/skills/hindsight-docs/references/cookbook/recipes/fitness_tracker.md b/skills/hindsight-docs/references/cookbook/recipes/fitness_tracker.md
deleted file mode 100644
index a411eede..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/fitness_tracker.md
+++ /dev/null
@@ -1,306 +0,0 @@
----
-sidebar_position: 6
----
-
-# Fitness Coach with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/fitness_tracker.ipynb)
-:::
-
-
-A personalized fitness assistant that tracks your workouts, diet, recovery, and progress over time to give contextual advice.
-
-## Features
-- Logs workout sessions with exercises and weights
-- Tracks meals and dietary preferences
-- Monitors recovery and sleep patterns
-- Provides personalized training advice
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-!pip install -q hindsight-client openai nest-asyncio
-```
-
-## 2. Configure OpenAI API Key
-
-Enter your OpenAI API key when prompted (used by both Hindsight and the demo).
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-print("API key configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from datetime import datetime
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-USER_ID = "fitness-user-demo"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-
-```python
-def log_workout(workout_details: str) -> str:
- """Log a workout session with timestamp."""
- today = datetime.now().strftime("%B %d, %Y")
- hindsight.retain(
- bank_id=USER_ID,
- content=f"{today} - WORKOUT LOG: {workout_details}",
- metadata={"category": "workout", "date": today},
- )
- return f"Logged workout for {today}: {workout_details}"
-
-
-def log_meal(meal_details: str) -> str:
- """Log a meal with timestamp."""
- today = datetime.now().strftime("%B %d, %Y")
- hindsight.retain(
- bank_id=USER_ID,
- content=f"{today} - MEAL LOG: {meal_details}",
- metadata={"category": "nutrition", "date": today},
- )
- return f"Logged meal for {today}: {meal_details}"
-
-
-def log_recovery(recovery_details: str) -> str:
- """Log recovery information (sleep, soreness, etc.)."""
- today = datetime.now().strftime("%B %d, %Y")
- hindsight.retain(
- bank_id=USER_ID,
- content=f"{today} - RECOVERY LOG: {recovery_details}",
- metadata={"category": "recovery", "date": today},
- )
- return f"Logged recovery for {today}: {recovery_details}"
-
-
-def store_user_profile(profile_info: str) -> str:
- """Store user profile information."""
- hindsight.retain(
- bank_id=USER_ID,
- content=f"USER PROFILE: {profile_info}",
- metadata={"category": "profile"},
- )
- return f"Stored profile info: {profile_info}"
-
-
-def fitness_coach(user_query: str) -> str:
- """Get personalized fitness advice based on query and user history."""
- memories = hindsight.recall(
- bank_id=USER_ID,
- query=f"fitness workout diet recovery goals {user_query}",
- budget="high",
- )
-
- memory_context = ""
- if memories and memories.results:
- memory_context = "\n".join(f"- {m.text}" for m in memories.results[:10])
-
- system_prompt = f"""You are a knowledgeable and supportive fitness coach.
-You have access to the user's workout history, diet logs, recovery notes, and personal profile.
-
-What you know about this user:
-{memory_context if memory_context else "No history recorded yet."}
-
-Provide personalized, actionable advice based on their:
-- Training history and progress
-- Dietary preferences and restrictions
-- Recovery patterns
-- Personal goals
-
-Be encouraging but realistic. Reference their specific history when relevant."""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_query},
- ],
- temperature=0.7,
- max_tokens=600,
- )
-
- advice = response.choices[0].message.content
-
- hindsight.retain(
- bank_id=USER_ID,
- content=f"User asked: {user_query}\nCoach advised: {advice[:200]}...",
- metadata={"category": "coaching"},
- )
-
- return advice
-
-
-def get_progress_report() -> str:
- """Generate a progress report based on workout history."""
- report = hindsight.reflect(
- bank_id=USER_ID,
- query="""Analyze this user's fitness journey:
- 1. How consistent have they been with workouts?
- 2. What progress have they made (weight lifted, exercises)?
- 3. How is their recovery and sleep?
- 4. What dietary patterns do you notice?
- 5. What should they focus on next?""",
- budget="high",
- )
- return report.text if hasattr(report, 'text') else str(report)
-
-print("Helper functions defined!")
-```
-
-## 5. Set Up User Profile
-
-
-```python
-print("Setting up user profile...")
-
-profile_data = [
- "Name: Anish, Age: 26, Height: 5'10\", Weight: 72kg",
- "Goal: Building lean muscle, started gym 6 months ago",
- "Routine: Push-pull-legs split, 5x per week",
- "Rest days: Wednesday and Sunday",
- "Dietary restriction: Mild lactose intolerance, uses almond milk",
- "Health note: Occasional knee pain, avoids deep squats",
- "Supplements: Whey protein (lactose-free), magnesium",
- "Sleep: Aims for 7+ hours, performance drops under 6 hours",
-]
-
-for info in profile_data:
- store_user_profile(info)
- print(f" Stored: {info[:50]}...")
-```
-
-## 6. Log Workout History
-
-
-```python
-print("Logging workout history...")
-
-workouts = [
- "Push day: Bench press 3x8 @ 60kg, overhead press 4x12, tricep dips 3x10. Felt strong.",
- "Pull day: Deadlift 3x5 @ 80kg, barbell rows 4x10, bicep curls 3x12. Good session.",
- "Leg day: Leg press 4x12, hamstring curls 3x12, glute bridges 3x15. Knee felt okay.",
-]
-
-for workout in workouts:
- print(f" {log_workout(workout)[:60]}...")
-
-print("\nLogging recent meals...")
-meals = [
- "Post-workout: Whey shake with almond milk, banana, oats",
- "Dinner: Grilled chicken, brown rice, steamed vegetables",
- "Snack: Greek yogurt (lactose-free) with berries",
-]
-
-for meal in meals:
- print(f" {log_meal(meal)[:60]}...")
-
-print("\nLogging recovery notes...")
-recovery = [
- "Slept 7.5 hours, feeling well rested",
- "Some DOMS in legs from yesterday, using turmeric milk",
-]
-
-for note in recovery:
- print(f" {log_recovery(note)[:60]}...")
-```
-
-## 7. Talk to Your Fitness Coach
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Talking to your fitness coach...")
-print("=" * 60)
-
-queries = [
- "How much was I lifting for bench press recently?",
- "I slept poorly last night (only 5 hours). What should I do for today's workout?",
- "Suggest a post-workout meal that works with my dietary restrictions.",
- "My knee has been bothering me more. Any exercise modifications?",
-]
-
-for query in queries:
- print(f"\nUser: {query}")
- print("-" * 40)
- response = fitness_coach(query)
- print(f"Coach: {response}")
- time.sleep(1)
-```
-
-## 8. Generate Progress Report
-
-
-```python
-print("=" * 60)
-print(" Progress Report")
-print("=" * 60)
-print(get_progress_report())
-```
-
-## 9. Try Your Own Query
-
-
-```python
-your_query = "What exercises should I do today?" # Change this!
-
-print(f"You: {your_query}")
-print("-" * 40)
-print(f"Coach: {fitness_coach(your_query)}")
-```
-
-## 10. Cleanup
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/healthcare_assistant.md b/skills/hindsight-docs/references/cookbook/recipes/healthcare_assistant.md
deleted file mode 100644
index 995f621b..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/healthcare_assistant.md
+++ /dev/null
@@ -1,299 +0,0 @@
----
-sidebar_position: 7
----
-
-# Healthcare Assistant with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/healthcare_assistant.ipynb)
-:::
-
-
-A supportive healthcare chatbot that remembers patient history, symptoms, medications, and preferences to provide personalized guidance.
-
-## Disclaimer
-
-**This is a demo application and should NOT be used for actual medical advice. Always consult qualified healthcare professionals.**
-
-## Features
-- Tracks symptoms, medications, and allergies
-- Maintains patient history across conversations
-- Provides health information and wellness tips
-- Schedules appointments
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-!pip install -q hindsight-client openai nest-asyncio
-```
-
-## 2. Configure OpenAI API Key
-
-Enter your OpenAI API key when prompted (used by both Hindsight and the demo).
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-print("API key configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from datetime import datetime
-import random
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-PATIENT_ID = "patient-demo"
-
-def get_patient_bank_id(patient_id: str) -> str:
- return f"patient-{patient_id}"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-
-```python
-def store_patient_info(patient_id: str, info: str, category: str = "general") -> str:
- """Store patient information."""
- bank_id = get_patient_bank_id(patient_id)
- today = datetime.now().strftime("%B %d, %Y")
-
- hindsight.retain(
- bank_id=bank_id,
- content=f"{today} - {category.upper()}: {info}",
- metadata={"category": category, "date": today},
- )
-
- return f"Recorded {category}: {info}"
-
-
-def get_patient_history(patient_id: str, query: str) -> str:
- """Retrieve relevant patient history."""
- bank_id = get_patient_bank_id(patient_id)
-
- memories = hindsight.recall(
- bank_id=bank_id,
- query=query,
- budget="high",
- )
-
- if memories and memories.results:
- return "\n".join(f"- {m.text}" for m in memories.results[:10])
- return "No relevant history found."
-
-
-def healthcare_chat(patient_id: str, user_message: str) -> str:
- """Chat with the healthcare assistant."""
- bank_id = get_patient_bank_id(patient_id)
-
- history = get_patient_history(
- patient_id,
- f"symptoms medications allergies conditions {user_message}"
- )
-
- system_prompt = f"""You are a supportive healthcare assistant chatbot.
-
-IMPORTANT DISCLAIMERS:
-- You are NOT a doctor and cannot provide medical diagnoses
-- Always recommend consulting healthcare professionals for serious concerns
-- Never prescribe medications or suggest stopping prescribed treatments
-
-Your role:
-- Listen empathetically to patient concerns
-- Remember and reference their medical history
-- Provide general health information and wellness tips
-- Help track symptoms over time
-- Remind about medications and appointments
-- Suggest when to seek professional care
-
-Patient History:
-{history}
-
-Guidelines:
-- Be warm and supportive
-- Ask clarifying questions when needed
-- Reference their history when relevant
-- Flag any concerning symptoms for professional review"""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_message},
- ],
- temperature=0.7,
- max_tokens=600,
- )
-
- answer = response.choices[0].message.content
-
- hindsight.retain(
- bank_id=bank_id,
- content=f"Patient concern: {user_message}\nGuidance provided: {answer[:200]}...",
- metadata={"category": "consultation"},
- )
-
- return answer
-
-
-def get_health_summary(patient_id: str) -> str:
- """Generate a health summary for the patient."""
- bank_id = get_patient_bank_id(patient_id)
-
- summary = hindsight.reflect(
- bank_id=bank_id,
- query="""Summarize this patient's health profile:
- 1. Known conditions and diagnoses
- 2. Current medications
- 3. Allergies and sensitivities
- 4. Recent symptoms reported
- 5. Lifestyle factors mentioned
- 6. Any patterns or trends in their health""",
- budget="high",
- )
- return summary.text if hasattr(summary, 'text') else str(summary)
-
-
-def schedule_appointment(patient_id: str, appointment_type: str, preferred_time: str) -> str:
- """Schedule an appointment (demo)."""
- confirmation_id = f"APT-{random.randint(10000, 99999)}"
-
- store_patient_info(
- patient_id,
- f"Appointment scheduled: {appointment_type} - Preferred time: {preferred_time} - Confirmation: {confirmation_id}",
- category="appointment"
- )
-
- return f"Appointment requested: {appointment_type}\nPreferred time: {preferred_time}\nConfirmation ID: {confirmation_id}\n\nA staff member will confirm the exact time within 24 hours."
-
-print("Helper functions defined!")
-```
-
-## 5. Set Up Patient Profile
-
-
-```python
-print("Setting up patient profile...")
-
-patient_info = [
- ("Age: 45, Male, Height: 5'11\", Weight: 185 lbs", "demographics"),
- ("Allergy: Penicillin - causes hives", "allergies"),
- ("Allergy: Shellfish - causes throat swelling", "allergies"),
- ("Current medication: Lisinopril 10mg daily for blood pressure", "medications"),
- ("Current medication: Metformin 500mg twice daily for Type 2 diabetes", "medications"),
- ("Condition: Diagnosed with Type 2 diabetes in 2020", "conditions"),
- ("Condition: Mild hypertension, well-controlled", "conditions"),
- ("Family history: Father had heart disease", "family_history"),
- ("Lifestyle: Sedentary job, trying to exercise more", "lifestyle"),
-]
-
-for info, category in patient_info:
- result = store_patient_info(PATIENT_ID, info, category)
- print(f" {result}")
-```
-
-## 6. Healthcare Chat
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Healthcare Chat")
-print("=" * 60)
-
-conversations = [
- "Hi, I've been having headaches for the past few days. Should I be worried?",
- "The headaches are mostly in the afternoon. I've also been feeling more tired than usual.",
- "I've been checking my blood sugar and it's been a bit higher lately, around 140-150 fasting.",
- "Can you remind me what allergies I have? I'm going to a new restaurant.",
-]
-
-for message in conversations:
- print(f"\nPatient: {message}")
- print("-" * 40)
- response = healthcare_chat(PATIENT_ID, message)
- print(f"Assistant: {response}")
- time.sleep(1)
-```
-
-## 7. Schedule Appointment
-
-
-```python
-print("=" * 60)
-print(" Scheduling Appointment")
-print("=" * 60)
-print(schedule_appointment(PATIENT_ID, "General checkup", "Next Tuesday afternoon"))
-```
-
-## 8. Health Summary
-
-
-```python
-print("=" * 60)
-print(" Patient Health Summary")
-print("=" * 60)
-print(get_health_summary(PATIENT_ID))
-```
-
-## 9. Try Your Own Question
-
-
-```python
-your_question = "Should I adjust my Metformin dose?" # Change this!
-
-print(f"You: {your_question}")
-print("-" * 40)
-print(f"Assistant: {healthcare_chat(PATIENT_ID, your_question)}")
-```
-
-## 10. Cleanup
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/litellm-memory-demo.md b/skills/hindsight-docs/references/cookbook/recipes/litellm-memory-demo.md
deleted file mode 100644
index d633f706..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/litellm-memory-demo.md
+++ /dev/null
@@ -1,187 +0,0 @@
----
-sidebar_position: 4
----
-
-# Memory with LiteLLM
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/04-litellm-memory-demo.ipynb)
-:::
-
-
-This notebook demonstrates how to add persistent memory to any LLM app using the `hindsight-litellm` package. Memory storage and injection happen automatically via LiteLLM callbacks - no manual memory management needed!
-
-**Key features demonstrated:**
-1. `configure()` + `enable()` - Set up automatic memory integration
-2. Automatic storage - Conversations are stored after each LLM call
-3. Automatic injection - Relevant memories are injected into prompts
-
-The `hindsight-litellm` package hooks into LiteLLM's callback system to:
-- Store each conversation after successful LLM responses
-- Inject relevant memories into the system prompt before LLM calls
-
-## Prerequisites
-
-Make sure you have Hindsight running:
-
-```bash
-export OPENAI_API_KEY=your-key
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-- API: http://localhost:8888
-- UI: http://localhost:9999
-
-## Installation
-
-
-```python
-!pip install hindsight-litellm litellm nest_asyncio python-dotenv -U -q
-```
-
-## Setup
-
-
-```python
-import os
-import uuid
-import time
-import logging
-import nest_asyncio
-from dotenv import load_dotenv
-
-# Apply nest_asyncio for Jupyter compatibility
-nest_asyncio.apply()
-
-# Load environment variables
-load_dotenv()
-
-# Configure logging
-logging.basicConfig(level=logging.INFO)
-logging.getLogger("LiteLLM").setLevel(logging.WARNING)
-logging.getLogger("LiteLLM Router").setLevel(logging.WARNING)
-logging.getLogger("LiteLLM Proxy").setLevel(logging.WARNING)
-
-# Import hindsight_litellm
-import hindsight_litellm
-
-# Configuration
-HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
-
-# Check for API key
-if not os.getenv("OPENAI_API_KEY"):
- print("Warning: OPENAI_API_KEY not set")
-```
-
-## Configure and Enable Automatic Memory
-
-This is all you need! After this, all LiteLLM calls will automatically:
-- Have relevant memories injected into the prompt
-- Store conversations to Hindsight after the response
-
-
-```python
-# Generate a unique bank_id for this demo session
-bank_id = f"demo-{uuid.uuid4().hex[:8]}"
-print(f"Using bank_id: {bank_id}")
-
-# Configure and enable hindsight
-hindsight_litellm.configure(
- hindsight_api_url=HINDSIGHT_API_URL,
- bank_id=bank_id,
- store_conversations=True, # Automatically store conversations
- inject_memories=True, # Automatically inject relevant memories
- verbose=True, # Enable logging to debug memory operations
-)
-hindsight_litellm.enable()
-
-print("Hindsight memory integration enabled!")
-```
-
-## Conversation 1: User Introduces Themselves
-
-In this first conversation, the user shares some information about themselves. This will be automatically stored to Hindsight memory.
-
-
-```python
-user_message_1 = "Hi! I'm Alex and I work at Google as a software engineer. I love Python and machine learning."
-print(f"User: {user_message_1}\n")
-
-# Use hindsight_litellm.completion() directly
-response_1 = hindsight_litellm.completion(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": user_message_1}
- ],
-)
-
-assistant_response_1 = response_1.choices[0].message.content
-print(f"Assistant: {assistant_response_1}")
-print("\n(Conversation automatically stored to Hindsight)")
-```
-
-## Wait for Memory Processing
-
-Hindsight needs a few seconds to process and extract facts from the conversation.
-
-
-```python
-print("Waiting 12 seconds for memory processing...")
-time.sleep(12)
-print("Done!")
-```
-
-## Conversation 2: Test Memory-Augmented Response
-
-Now we start a fresh conversation and ask what the assistant remembers. The memories from the previous conversation will be automatically injected into the prompt!
-
-
-```python
-user_message_2 = "What do you know about me? What programming language should I use for my next project?"
-print(f"User: {user_message_2}\n")
-
-# Memories are automatically injected before this call!
-response_2 = hindsight_litellm.completion(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": user_message_2}
- ],
-)
-
-print(f"Assistant: {response_2.choices[0].message.content}")
-```
-
-## Summary
-
-The assistant should have remembered that Alex:
-- Works at Google as a software engineer
-- Loves Python and machine learning
-
-And it should have recommended Python based on that knowledge!
-
-
-```python
-print(f"Memories stored in bank: {bank_id}")
-print(f"View in UI: http://localhost:9999/banks/{bank_id}")
-```
-
-## Cleanup
-
-
-```python
-hindsight_litellm.cleanup()
-
-# Optional: delete the bank
-import requests
-response = requests.delete(f"{HINDSIGHT_API_URL}/v1/default/banks/{bank_id}")
-print(f"Deleted bank: {response.json()}")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/movie_recommendation.md b/skills/hindsight-docs/references/cookbook/recipes/movie_recommendation.md
deleted file mode 100644
index 1dc3bf4a..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/movie_recommendation.md
+++ /dev/null
@@ -1,246 +0,0 @@
----
-sidebar_position: 8
----
-
-# Movie Recommendation Assistant with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/movie_recommendation.ipynb)
-:::
-
-
-A personalized movie recommender that remembers your preferences, watch history, and tastes to give better suggestions over time.
-
-## Features
-- Remembers favorite genres, directors, and actors
-- Tracks movies you've watched and enjoyed
-- Provides contextual recommendations based on mood
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-!pip install -q hindsight-client openai nest-asyncio
-```
-
-## 2. Configure OpenAI API Key
-
-Enter your OpenAI API key when prompted (used by both Hindsight and the demo).
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-print("API key configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-# Initialize OpenAI client
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-# Unique identifier for this user's memory bank
-USER_ID = "movie-fan-demo"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-These functions demonstrate the three core Hindsight operations:
-- **retain()**: Store memories
-- **recall()**: Retrieve relevant memories
-- **reflect()**: Synthesize insights from memories
-
-
-```python
-def get_recommendation(user_query: str) -> str:
- """
- Get a movie recommendation based on user query and remembered preferences.
- """
- # Recall relevant memories about this user's movie preferences
- memories = hindsight.recall(
- bank_id=USER_ID,
- query=f"movie preferences tastes genres {user_query}",
- budget="mid",
- )
-
- # Build context from memories
- memory_context = ""
- if memories and memories.results:
- memory_context = "\n".join(
- f"- {m.text}" for m in memories.results[:5]
- )
-
- # Generate recommendation with context
- system_prompt = f"""You are a helpful movie recommendation assistant.
-You remember the user's preferences and past conversations to give personalized suggestions.
-
-What you know about this user:
-{memory_context if memory_context else "No previous preferences recorded yet."}
-
-Give thoughtful, personalized recommendations based on their tastes.
-If they mention new preferences, acknowledge them."""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_query},
- ],
- temperature=0.7,
- max_tokens=500,
- )
-
- recommendation = response.choices[0].message.content
-
- # Store this interaction for future context
- hindsight.retain(
- bank_id=USER_ID,
- content=f"User asked: {user_query}\nRecommendation given: {recommendation}",
- metadata={"category": "movie_recommendation"},
- )
-
- return recommendation
-
-
-def store_preference(preference: str) -> None:
- """Store an explicit user preference."""
- hindsight.retain(
- bank_id=USER_ID,
- content=f"User preference: {preference}",
- metadata={"category": "preference"},
- )
- print(f"Stored preference: {preference}")
-
-
-def get_preference_summary() -> str:
- """Get a summary of what we know about the user's movie tastes."""
- summary = hindsight.reflect(
- bank_id=USER_ID,
- query="Summarize this user's movie preferences, favorite genres, actors they like, and movies they've mentioned enjoying or disliking.",
- budget="high",
- )
- return summary.text if hasattr(summary, 'text') else str(summary)
-
-print("Helper functions defined!")
-```
-
-## 5. Run the Demo
-
-Watch how the assistant learns and remembers preferences across conversations.
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Movie Recommendation Assistant with Memory")
-print("=" * 60)
-print()
-
-# Simulate a conversation over time
-conversations = [
- "I'm looking for a movie to watch tonight. Any suggestions?",
- "I really loved Inception and Interstellar. Christopher Nolan is amazing!",
- "Can you suggest something similar to those? I like mind-bending plots.",
- "Actually, I'm not in the mood for something heavy. Something lighter?",
- "I watched The Grand Budapest Hotel last week and loved it!",
- "What should I watch tonight? Remember what I like!",
-]
-
-for i, query in enumerate(conversations, 1):
- print(f"\n[Conversation {i}]")
- print(f"User: {query}")
- print("-" * 40)
-
- response = get_recommendation(query)
- print(f"Assistant: {response}")
- print()
-
- time.sleep(1)
-```
-
-## 6. View Learned Preferences
-
-Use `reflect()` to synthesize what Hindsight has learned about your movie tastes.
-
-
-```python
-print("=" * 60)
-print(" What I've learned about your movie tastes:")
-print("=" * 60)
-print(get_preference_summary())
-```
-
-## 7. Try Your Own Queries
-
-Experiment with your own movie preferences!
-
-
-```python
-# Try your own query!
-your_query = "I'm in the mood for a sci-fi thriller" # Change this!
-
-print(f"You: {your_query}")
-print("-" * 40)
-print(f"Assistant: {get_recommendation(your_query)}")
-```
-
-## 8. Cleanup
-
-Close the Hindsight client connection.
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
-
-
-```python
-
-```
-
-
-```python
-
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/per-user-memory.md b/skills/hindsight-docs/references/cookbook/recipes/per-user-memory.md
deleted file mode 100644
index 9a7e79b2..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/per-user-memory.md
+++ /dev/null
@@ -1,247 +0,0 @@
----
-sidebar_position: 2
----
-
-# Per-User Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/02-per-user-memory.ipynb)
-:::
-
-
-The simplest pattern: give your agent persistent memory for each user. The agent remembers past conversations, user preferences, and context across sessions.
-
-## The Problem
-
-Without memory, every conversation starts from scratch:
-
-```
-Session 1: "I prefer dark mode and use Python"
-Session 2: "What's my preferred language?" → Agent doesn't know
-```
-
-## The Solution: One Bank Per User
-
-```
-┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
-│ User A Bank │ │ User B Bank │ │ User C Bank │
-│ │ │ │ │ │
-│ - Conversations│ │ - Conversations│ │ - Conversations│
-│ - Preferences │ │ - Preferences │ │ - Preferences │
-│ - Context │ │ - Context │ │ - Context │
-└─────────────────┘ └─────────────────┘ └─────────────────┘
- │ │ │
- 100% isolated 100% isolated 100% isolated
-```
-
-Each user gets their own memory bank. Complete isolation, simple mental model.
-
-
-```python
-!pip install hindsight-client nest_asyncio openai python-dotenv -U
-```
-
-## 1. Create a Bank When User Signs Up
-
-
-```python
-# Jupyter notebooks already run an asyncio event loop. The hindsight client
-# uses loop.run_until_complete() internally, but Python doesn't allow nested
-# event loops by default. nest_asyncio patches this to allow nesting.
-import nest_asyncio
-nest_asyncio.apply()
-
-import os
-from dotenv import load_dotenv
-from openai import OpenAI as OpenAIClient
-
-# Load environment variables from .env file
-# Copy .env.example to .env and fill in your values
-load_dotenv()
-
-# Configuration (override with env vars if set)
-HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
-HINDSIGHT_UI_URL = os.getenv("HINDSIGHT_UI_URL", "http://localhost:9999")
-
-from hindsight_client import Hindsight
-
-client = Hindsight(base_url=HINDSIGHT_API_URL)
-llm = OpenAIClient() # Uses OPENAI_API_KEY from .env
-
-def on_user_signup(user_id: str):
- client.create_bank(
- bank_id=f"user-{user_id}",
- name=f"Memory for {user_id}"
- )
- print(f"View bank: {HINDSIGHT_UI_URL}/banks/user-{user_id}?view=documents")
-```
-
-## 2. Manage Conversation Sessions
-
-Use `document_id` to group messages belonging to the same conversation. When you retain with the same `document_id`, Hindsight replaces the previous version (upsert behavior), keeping the memory up-to-date as the conversation evolves.
-
-
-```python
-import uuid
-import json
-
-class ConversationSession:
- def __init__(self, user_id: str):
- self.user_id = user_id
- self.session_id = str(uuid.uuid4()) # Unique ID for this conversation
- self.messages = []
-
- def add_message(self, role: str, content: str):
- self.messages.append({"role": role, "content": content})
-
- def save(self, client: Hindsight):
- """Save the entire conversation. Replaces previous version if session_id exists."""
- # Convert messages to string format for retain
- content = "\n".join([f"{m['role']}: {m['content']}" for m in self.messages])
- client.retain(
- bank_id=f"user-{self.user_id}",
- content=content,
- document_id=self.session_id # Same ID = upsert (replace old version)
- )
-```
-
-## 3. Recall Context Before Responding
-
-
-```python
-def get_context(user_id: str, query: str):
- result = client.recall(
- bank_id=f"user-{user_id}",
- query=query
- )
- return result.results
-```
-
-## 4. Complete Agent Loop
-
-
-```python
-def format_results(results):
- """Format recall results for the prompt."""
- if not results:
- return "No relevant memories found."
- return "\n".join([f"- {r.text}" for r in results])
-
-def format_messages(messages):
- """Format conversation messages for the prompt."""
- return "\n".join([f"{m['role']}: {m['content']}" for m in messages])
-
-def handle_message(session: ConversationSession, user_message: str):
- # 1. Add user message to session
- session.add_message("user", user_message)
-
- # 2. Recall relevant context from past conversations
- context = client.recall(
- bank_id=f"user-{session.user_id}",
- query=user_message
- )
-
- # 3. Build system prompt with memory
- system_prompt = f"""You are a helpful assistant with memory of past conversations.
-
-## What you remember about this user
-{format_results(context.results)}
-
-Respond helpfully and reference relevant memories when appropriate."""
-
- # 4. Generate response using OpenAI
- response = llm.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- *[{"role": m["role"], "content": m["content"]} for m in session.messages]
- ]
- )
- assistant_response = response.choices[0].message.content
-
- # 5. Add assistant response to session
- session.add_message("assistant", assistant_response)
-
- # 6. Save the updated conversation (upserts based on session_id)
- session.save(client)
-
- print(f"User: {user_message}")
- print(f"Assistant: {assistant_response}\n")
-
- return assistant_response
-```
-
-## 5. Starting a New Conversation
-
-
-```python
-# Create the user's bank
-on_user_signup("alice")
-
-# Each new conversation gets a new session with a unique ID
-session = ConversationSession(user_id="alice")
-
-# Multiple exchanges in the same conversation
-handle_message(session, "Hi! I'm working on a Python project")
-handle_message(session, "Can you help me with async/await?")
-
-# View the stored conversation in the UI.
-# Each message updates the same document (via document_id), so you'll see
-# the full conversation history in a single document rather than separate entries.
-print(f"\nView documents: {HINDSIGHT_UI_URL}/banks/user-alice?view=documents")
-```
-
-## How Document ID Works
-
-The `document_id` parameter is key to managing evolving conversations:
-
-| Scenario | Behavior |
-|----------|----------|
-| First retain with `document_id="session_123"` | Creates new document |
-| Retain again with same `document_id="session_123"` | **Replaces** previous version (upsert) |
-| Retain with different `document_id="session_456"` | Creates separate document |
-| Retain without `document_id` | Creates new document each time |
-
-This upsert behavior means:
-- You always retain the **full conversation** state
-- Facts are re-extracted from the complete conversation
-- No duplicate or stale facts from old versions
-- Memory stays consistent as conversations evolve
-
-## What Gets Remembered
-
-Hindsight automatically extracts and connects:
-
-- **Facts**: "User prefers Python", "User is building a CLI tool"
-- **Entities**: People, projects, technologies mentioned
-- **Relationships**: How entities relate to each other
-- **Temporal context**: When things happened
-
-You don't need to manually extract or structure this - just retain the conversations.
-
-## When to Use This Pattern
-
-**Good fit:**
-- Chatbots and assistants
-- Personal AI companions
-- Any 1:1 user-to-agent interaction
-
-**Consider adding shared knowledge if:**
-- You have product docs or FAQs to reference
-- Multiple users need access to the same information
-- See the Support Agent with Shared Knowledge notebook
-
-## Cleanup
-
-Delete the banks created during this notebook:
-
-
-```python
-import requests
-
-# Delete the user-alice bank
-response = requests.delete(f"{HINDSIGHT_API_URL}/v1/default/banks/user-alice")
-print(f"Deleted user-alice: {response.json()}")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/personal_assistant.md b/skills/hindsight-docs/references/cookbook/recipes/personal_assistant.md
deleted file mode 100644
index ecc520d6..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/personal_assistant.md
+++ /dev/null
@@ -1,266 +0,0 @@
----
-sidebar_position: 9
----
-
-# Personal AI Assistant with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/personal_assistant.ipynb)
-:::
-
-
-A general-purpose personal assistant that remembers your preferences, schedule, family, work context, and past conversations.
-
-## Features
-- Remembers family, work, and personal details
-- Tracks preferences and habits
-- Helps with scheduling and reminders
-- Maintains context across conversations
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-!pip install -q hindsight-client openai nest-asyncio
-```
-
-## 2. Configure OpenAI API Key
-
-Enter your OpenAI API key when prompted (used by both Hindsight and the demo).
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-print("API key configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from datetime import datetime
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-USER_ID = "assistant-user-demo"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-
-```python
-def remember(info: str, category: str = "general") -> str:
- """Store information to remember."""
- today = datetime.now().strftime("%B %d, %Y")
-
- hindsight.retain(
- bank_id=USER_ID,
- content=f"{today}: {info}",
- metadata={"category": category, "date": today},
- )
-
- return f"I'll remember: {info}"
-
-
-def recall_context(query: str) -> str:
- """Recall relevant memories for context."""
- memories = hindsight.recall(
- bank_id=USER_ID,
- query=query,
- budget="high",
- )
-
- if memories and memories.results:
- return "\n".join(f"- {m.text}" for m in memories.results[:8])
- return ""
-
-
-def chat(user_message: str) -> str:
- """Chat with the personal assistant."""
- context = recall_context(user_message)
-
- system_prompt = f"""You are a helpful personal AI assistant with long-term memory.
-You remember the user's preferences, schedule, family, work context, and past conversations.
-
-What you remember about this user:
-{context if context else "No memories recorded yet."}
-
-Your capabilities:
-- Remember things when asked ("Remember that...", "Don't forget...")
-- Recall past information ("What did I tell you about...", "When is...")
-- Provide personalized suggestions based on known preferences
-- Help with scheduling and reminders
-- Have natural conversations while maintaining context
-
-Be helpful, proactive, and reference relevant memories naturally."""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_message},
- ],
- temperature=0.7,
- max_tokens=500,
- )
-
- answer = response.choices[0].message.content
-
- # Check if user is asking to remember something
- lower_msg = user_message.lower()
- if any(phrase in lower_msg for phrase in ["remember that", "don't forget", "remind me", "note that"]):
- hindsight.retain(
- bank_id=USER_ID,
- content=f"User asked to remember: {user_message}",
- metadata={"category": "reminder"},
- )
-
- # Store the interaction
- hindsight.retain(
- bank_id=USER_ID,
- content=f"Conversation - User: {user_message[:100]} | Assistant: {answer[:100]}",
- metadata={"category": "conversation"},
- )
-
- return answer
-
-
-def get_summary(topic: str = None) -> str:
- """Get a summary of memories."""
- query = f"Summarize what you know about {topic}" if topic else \
- "Summarize everything you know about this user"
-
- summary = hindsight.reflect(
- bank_id=USER_ID,
- query=query,
- budget="high",
- )
- return summary.text if hasattr(summary, 'text') else str(summary)
-
-print("Helper functions defined!")
-```
-
-## 5. Build Context
-
-
-```python
-print("Building context...")
-
-initial_context = [
- ("My name is Alex and I work as a product manager at TechCorp", "personal"),
- ("My wife's name is Sarah and we have two kids: Emma (7) and Jack (4)", "family"),
- ("I prefer morning meetings and try to keep afternoons for deep work", "preference"),
- ("My mom's birthday is March 15th", "event"),
- ("I'm trying to read more - currently reading 'Atomic Habits'", "hobby"),
- ("I have a weekly team standup every Monday at 10am", "schedule"),
- ("I'm allergic to cats", "health"),
- ("My favorite coffee is a flat white with oat milk", "preference"),
- ("I'm training for a half marathon in April", "goal"),
-]
-
-for info, category in initial_context:
- result = remember(info, category)
- print(f" {result}")
-```
-
-## 6. Have a Conversation
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Conversation")
-print("=" * 60)
-
-conversations = [
- "Hey, what's my wife's name again?",
- "Remember that my Q1 review is next Thursday at 2pm",
- "I need a gift idea for my mom's birthday",
- "What time is my Monday standup?",
- "Can you recommend a coffee order for me?",
- "What books am I reading?",
-]
-
-for message in conversations:
- print(f"\nAlex: {message}")
- print("-" * 40)
- response = chat(message)
- print(f"Assistant: {response}")
- time.sleep(1)
-```
-
-## 7. View Summary
-
-
-```python
-print("=" * 60)
-print(" What I Know About You")
-print("=" * 60)
-print(get_summary())
-```
-
-
-```python
-print("=" * 60)
-print(" Your Family")
-print("=" * 60)
-print(get_summary("family"))
-```
-
-## 8. Try Your Own Message
-
-
-```python
-your_message = "What should I focus on this month with my training?" # Change this!
-
-print(f"You: {your_message}")
-print("-" * 40)
-print(f"Assistant: {chat(your_message)}")
-```
-
-## 9. Cleanup
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/personalized_search.md b/skills/hindsight-docs/references/cookbook/recipes/personalized_search.md
deleted file mode 100644
index 61ac26f5..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/personalized_search.md
+++ /dev/null
@@ -1,299 +0,0 @@
----
-sidebar_position: 10
----
-
-# Personalized Search Agent with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/personalized_search.ipynb)
-:::
-
-
-A search assistant that learns your preferences, location, dietary needs, and lifestyle to provide contextually relevant search results.
-
-## Features
-- Learns location, dietary restrictions, and lifestyle
-- Personalizes search queries based on context
-- Remembers past searches and preferences
-- Integrates with Tavily for real web search (optional)
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-- Tavily API key (optional, for real web search)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-# Tavily is optional - demo works with simulated results if not installed
-!pip install -q hindsight-client openai tavily-python nest-asyncio
-```
-
-## 2. Configure API Keys
-
-Enter your API keys when prompted. Tavily is optional - press Enter to skip for simulated search results.
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-# Tavily is optional - for real web search
-if not os.getenv("TAVILY_API_KEY"):
- tavily_key = getpass.getpass("Enter your Tavily API key (or press Enter to skip): ")
- if tavily_key:
- os.environ["TAVILY_API_KEY"] = tavily_key
-
-print("API keys configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-# Optional: Tavily for real web search
-try:
- from tavily import TavilyClient
- tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
- HAS_TAVILY = True
- print("Tavily configured - using real web search!")
-except (ImportError, Exception) as e:
- HAS_TAVILY = False
- print("Note: Using simulated search results (Tavily not configured)")
-
-USER_ID = "search-user-demo"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-
-```python
-def store_preference(preference: str) -> str:
- """Store a user preference."""
- hindsight.retain(
- bank_id=USER_ID,
- content=f"User preference: {preference}",
- metadata={"category": "preference"},
- )
- return f"Learned: {preference}"
-
-
-def store_interaction(query: str, response: str) -> None:
- """Store a search interaction."""
- hindsight.retain(
- bank_id=USER_ID,
- content=f"Search query: {query}\nResult highlights: {response[:200]}",
- metadata={"category": "search_history"},
- )
-
-
-def get_user_context(query: str) -> str:
- """Retrieve relevant user context."""
- memories = hindsight.recall(
- bank_id=USER_ID,
- query=f"preferences location dietary lifestyle {query}",
- budget="mid",
- )
-
- if memories and memories.results:
- return "\n".join(f"- {m.text}" for m in memories.results[:6])
- return ""
-
-
-def personalized_search(query: str) -> str:
- """Perform a personalized search."""
- user_context = get_user_context(query)
-
- enhancement_prompt = f"""Given this user's preferences and the search query, suggest how to enhance the search.
-
-User preferences:
-{user_context if user_context else "No preferences recorded yet."}
-
-Search query: {query}
-
-Return a JSON object with:
-- "enhanced_query": The improved search query incorporating relevant preferences
-- "filters": Any specific filters to apply (e.g., "vegetarian", "within 5 miles")
-- "reasoning": Brief explanation of personalizations applied"""
-
- enhancement = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[{"role": "user", "content": enhancement_prompt}],
- temperature=0.3,
- max_tokens=300,
- )
-
- enhanced_info = enhancement.choices[0].message.content
-
- # Perform the search
- if HAS_TAVILY:
- search_results = tavily.search(
- query=query,
- search_depth="advanced",
- max_results=5,
- )
- results_text = "\n".join(
- f"- {r['title']}: {r['content'][:150]}..."
- for r in search_results.get('results', [])
- )
- else:
- results_text = f"[Simulated search results for: {query}]"
-
- response_prompt = f"""Based on the search results and user preferences, provide a personalized summary.
-
-User preferences:
-{user_context if user_context else "No preferences recorded yet."}
-
-Query: {query}
-
-Search enhancement applied:
-{enhanced_info}
-
-Search results:
-{results_text}
-
-Provide a helpful, personalized response that takes into account their preferences."""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[{"role": "user", "content": response_prompt}],
- temperature=0.7,
- max_tokens=500,
- )
-
- answer = response.choices[0].message.content
- store_interaction(query, answer)
-
- return answer
-
-
-def get_preference_profile() -> str:
- """Get a summary of the user's preference profile."""
- profile = hindsight.reflect(
- bank_id=USER_ID,
- query="""Summarize what we know about this user:
- - Location and neighborhood
- - Dietary preferences and restrictions
- - Work style and schedule
- - Hobbies and interests
- - Family situation
- - Shopping preferences""",
- budget="high",
- )
- return profile.text if hasattr(profile, 'text') else str(profile)
-
-print("Helper functions defined!")
-```
-
-## 5. Build User Profile
-
-
-```python
-print("Learning user preferences...")
-
-preferences = [
- "Lives in San Francisco, Mission District",
- "Works remotely as a software engineer",
- "Vegetarian, prefers organic food when possible",
- "Has a 5-year-old daughter named Emma",
- "Enjoys hiking and outdoor activities on weekends",
- "Prefers quiet coffee shops for remote work",
- "Lactose intolerant, uses oat milk",
- "Interested in sustainable and eco-friendly products",
- "Usually free on Tuesday and Thursday afternoons",
- "Husband is allergic to nuts",
-]
-
-for pref in preferences:
- result = store_preference(pref)
- print(f" {result}")
-```
-
-## 6. Personalized Search Results
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Personalized Search Results")
-print("=" * 60)
-
-searches = [
- "Find a good coffee shop for working remotely",
- "Restaurant recommendations for a family dinner",
- "Birthday gift ideas for a 5-year-old",
-]
-
-for query in searches:
- print(f"\nSearch: {query}")
- print("-" * 40)
- result = personalized_search(query)
- print(result)
- time.sleep(1)
-```
-
-## 7. View Preference Profile
-
-
-```python
-print("=" * 60)
-print(" User Preference Profile")
-print("=" * 60)
-print(get_preference_profile())
-```
-
-## 8. Try Your Own Search
-
-
-```python
-your_search = "Best hiking trails near me" # Change this!
-
-print(f"Search: {your_search}")
-print("-" * 40)
-print(personalized_search(your_search))
-```
-
-## 9. Cleanup
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/quickstart.md b/skills/hindsight-docs/references/cookbook/recipes/quickstart.md
deleted file mode 100644
index cea1cd77..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/quickstart.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-sidebar_position: 1
----
-
-# Hindsight Quickstart
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/01-quickstart.ipynb)
-:::
-
-
-This notebook covers the basics of using Hindsight:
-- **Retain**: Store information in memory
-- **Recall**: Retrieve memories matching a query
-- **Reflect**: Generate insights from memories
-
-## Prerequisites
-
-Make sure you have Hindsight running. The easiest way is via Docker:
-
-```bash
-export OPENAI_API_KEY=your-key
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-- API: http://localhost:8888
-- UI: http://localhost:9999
-
-## Installation
-
-Install the Hindsight Python client:
-
-
-```python
-!pip install hindsight-client nest_asyncio python-dotenv -U
-```
-
-## Connect to Hindsight
-
-
-```python
-# Jupyter notebooks already run an asyncio event loop. The hindsight client
-# uses loop.run_until_complete() internally, but Python doesn't allow nested
-# event loops by default. nest_asyncio patches this to allow nesting.
-import nest_asyncio
-nest_asyncio.apply()
-
-import os
-from dotenv import load_dotenv
-
-# Load environment variables from .env file
-# Copy .env.example to .env and fill in your values
-load_dotenv()
-
-# Configuration (override with env vars if set)
-HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
-HINDSIGHT_UI_URL = os.getenv("HINDSIGHT_UI_URL", "http://localhost:9999")
-
-from hindsight_client import Hindsight
-
-client = Hindsight(base_url=HINDSIGHT_API_URL)
-```
-
-## Retain: Store Information
-
-The `retain` operation is used to push new memories into Hindsight. It tells Hindsight to _retain_ the information you pass in.
-
-Behind the scenes, the retain operation uses an LLM to extract key facts, temporal data, entities, and relationships.
-
-
-```python
-# Simple retain
-client.retain(
- bank_id="my-bank",
- content="Alice works at Google as a software engineer"
-)
-
-# View the stored document in the UI:
-print(f"View documents: {HINDSIGHT_UI_URL}/banks/my-bank?view=documents")
-```
-
-
-```python
-# Retain with context and timestamp
-client.retain(
- bank_id="my-bank",
- content="Alice got promoted to senior engineer",
- context="career update",
- timestamp="2025-06-15T10:00:00Z"
-)
-```
-
-## Recall: Retrieve Memories
-
-The `recall` operation retrieves memories matching a query. It performs 4 retrieval strategies in parallel:
-- **Semantic**: Vector similarity
-- **Keyword**: BM25 exact matching
-- **Graph**: Entity/temporal/causal links
-- **Temporal**: Time range filtering
-
-
-```python
-# Simple recall
-results = client.recall(bank_id="my-bank", query="What does Alice do?")
-
-print("Memories:")
-for r in results.results:
- print(f" - {r.text}")
-```
-
-
-```python
-# Temporal recall
-results = client.recall(bank_id="my-bank", query="What happened in June?")
-
-print("Memories:")
-for r in results.results:
- print(f" - {r.text}")
-```
-
-## Reflect: Generate Insights
-
-The `reflect` operation performs a more thorough analysis of existing memories. This allows the agent to form new connections between memories which are then persisted as opinions and/or observations.
-
-Example use cases:
-- An AI Project Manager reflecting on what risks need to be mitigated
-- A Sales Agent reflecting on why certain outreach messages have gotten responses
-- A Support Agent reflecting on opportunities where customers have unanswered questions
-
-
-```python
-response = client.reflect(bank_id="my-bank", query="What should I know about Alice?")
-print(response)
-```
-
-## Memory Types
-
-Hindsight organizes memory into four networks to mimic human memory:
-
-- **World**: Facts about the world ("The stove gets hot")
-- **Experiences**: Agent's own experiences ("I touched the stove and it really hurt")
-- **Opinion**: Beliefs with confidence scores ("I shouldn't touch the stove again" - .99 confidence)
-- **Observation**: Complex mental models derived by reflecting on facts and experiences
-
-## Cleanup
-
-Delete the bank created during this notebook:
-
-
-```python
-import requests
-
-response = requests.delete(f"{HINDSIGHT_API_URL}/v1/default/banks/my-bank")
-print(f"Deleted my-bank: {response.json()}")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/study_buddy.md b/skills/hindsight-docs/references/cookbook/recipes/study_buddy.md
deleted file mode 100644
index 3174c210..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/study_buddy.md
+++ /dev/null
@@ -1,335 +0,0 @@
----
-sidebar_position: 11
----
-
-# Study Buddy with Hindsight Memory
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/study_buddy.ipynb)
-:::
-
-
-A personalized study assistant that tracks what you've learned, identifies knowledge gaps, and helps with spaced repetition.
-
-## Features
-- Tracks study sessions and topics covered
-- Monitors confidence levels per topic
-- Identifies knowledge gaps
-- Suggests topics for spaced repetition review
-
-## Prerequisites
-- OpenAI API key
-- Hindsight running locally via Docker (see setup below)
-
-## Start Hindsight Locally
-
-Before running this notebook, start Hindsight in a terminal:
-
-```bash
-export OPENAI_API_KEY="your-openai-api-key"
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## 1. Install Dependencies
-
-
-```python
-!pip install -q hindsight-client openai nest-asyncio
-```
-
-## 2. Configure OpenAI API Key
-
-Enter your OpenAI API key when prompted (used by both Hindsight and the demo).
-
-
-```python
-import getpass
-import os
-
-# Set OpenAI API key (used by both Hindsight and the demo)
-if not os.getenv("OPENAI_API_KEY"):
- os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
-
-print("API key configured!")
-```
-
-## 3. Initialize Clients
-
-
-```python
-import nest_asyncio
-nest_asyncio.apply()
-
-from datetime import datetime
-from openai import OpenAI
-from hindsight_client import Hindsight
-
-# Initialize Hindsight client (connects to local Docker instance)
-hindsight = Hindsight(
- base_url=os.getenv("HINDSIGHT_BASE_URL", "http://localhost:8888"),
-)
-
-openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
-
-USER_ID = "student-demo"
-
-print("Clients initialized!")
-```
-
-## 4. Define Helper Functions
-
-
-```python
-def record_study_session(topic: str, notes: str, confidence: str = "medium") -> str:
- """Record a study session with topic, notes, and self-assessed confidence."""
- today = datetime.now().strftime("%B %d, %Y")
-
- content = f"""{today} - STUDY SESSION
-Topic: {topic}
-Confidence Level: {confidence}
-Notes: {notes}"""
-
- hindsight.retain(
- bank_id=USER_ID,
- content=content,
- metadata={
- "category": "study_session",
- "topic": topic,
- "confidence": confidence,
- "date": today,
- },
- )
-
- return f"Recorded study session on '{topic}' (confidence: {confidence})"
-
-
-def record_question(topic: str, question: str, understood: bool) -> str:
- """Record a question asked during study."""
- today = datetime.now().strftime("%B %d, %Y")
-
- content = f"""{today} - QUESTION
-Topic: {topic}
-Question: {question}
-Understood: {"Yes" if understood else "No - needs review"}"""
-
- hindsight.retain(
- bank_id=USER_ID,
- content=content,
- metadata={
- "category": "question",
- "topic": topic,
- "understood": str(understood),
- },
- )
-
- return f"Recorded question on '{topic}'"
-
-
-def study_buddy(user_query: str) -> str:
- """Interact with the study buddy."""
- memories = hindsight.recall(
- bank_id=USER_ID,
- query=f"study session topic notes questions {user_query}",
- budget="high",
- )
-
- memory_context = ""
- if memories and memories.results:
- memory_context = "\n".join(f"- {m.text}" for m in memories.results[:8])
-
- system_prompt = f"""You are a helpful study buddy and tutor.
-You have access to the student's study history, including:
-- Topics they've studied and their notes
-- Their self-assessed confidence levels
-- Questions they've asked and whether they understood the answers
-
-Study History:
-{memory_context if memory_context else "No study history recorded yet."}
-
-Your role:
-1. Answer questions about topics they're studying
-2. Identify knowledge gaps based on their history
-3. Suggest topics to review (spaced repetition)
-4. Provide encouragement and study tips
-5. Connect new concepts to things they've already learned
-
-Be supportive and pedagogical. Reference their previous learning when relevant."""
-
- response = openai_client.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_query},
- ],
- temperature=0.7,
- max_tokens=800,
- )
-
- answer = response.choices[0].message.content
-
- hindsight.retain(
- bank_id=USER_ID,
- content=f"Student asked: {user_query}\nExplanation given: {answer[:300]}...",
- metadata={"category": "tutoring"},
- )
-
- return answer
-
-
-def get_review_suggestions() -> str:
- """Get suggestions for topics to review."""
- suggestions = hindsight.reflect(
- bank_id=USER_ID,
- query="""Analyze this student's study history and suggest:
- 1. Topics with low confidence that need more review
- 2. Topics studied a while ago that should be revisited
- 3. Questions that weren't fully understood
- 4. Connections between topics they might have missed
-
- Prioritize by what would most improve their understanding.""",
- budget="high",
- )
- return suggestions.text if hasattr(suggestions, 'text') else str(suggestions)
-
-
-def get_knowledge_summary(topic: str = None) -> str:
- """Get a summary of what the student knows."""
- query = f"Summarize what this student knows about {topic}" if topic else \
- "Summarize this student's overall knowledge and progress"
-
- summary = hindsight.reflect(
- bank_id=USER_ID,
- query=query,
- budget="high",
- )
- return summary.text if hasattr(summary, 'text') else str(summary)
-
-print("Helper functions defined!")
-```
-
-## 5. Record Study Sessions
-
-
-```python
-print("Recording study sessions...")
-
-sessions = [
- {
- "topic": "Classical Mechanics - Newton's Laws",
- "notes": "Covered F=ma, action-reaction pairs, inertia. Solved problems on inclined planes.",
- "confidence": "high",
- },
- {
- "topic": "Classical Mechanics - Conservation of Momentum",
- "notes": "Elastic vs inelastic collisions. Struggled with 2D collision problems.",
- "confidence": "low",
- },
- {
- "topic": "Classical Mechanics - Generalized Coordinates",
- "notes": "Introduction to Lagrangian mechanics. Degrees of freedom concept.",
- "confidence": "medium",
- },
- {
- "topic": "Waves - Simple Harmonic Motion",
- "notes": "SHM equations, period, frequency. Connected to springs and pendulums.",
- "confidence": "high",
- },
- {
- "topic": "Waves - Frequency Domain",
- "notes": "Started Fourier transforms. Math is confusing, need more practice.",
- "confidence": "low",
- },
-]
-
-for session in sessions:
- result = record_study_session(**session)
- print(f" {result}")
-```
-
-## 6. Record Questions
-
-
-```python
-print("Recording questions...")
-
-questions = [
- ("Conservation of Momentum", "Why is momentum conserved in collisions?", True),
- ("Conservation of Momentum", "How do I solve 2D collision problems?", False),
- ("Generalized Coordinates", "What's the advantage of Lagrangian over Newtonian?", True),
- ("Frequency Domain", "When do I use Fourier transforms vs Laplace?", False),
-]
-
-for topic, question, understood in questions:
- result = record_question(topic, question, understood)
- print(f" {result}")
-```
-
-## 7. Interactive Study Session
-
-
-```python
-import time
-
-print("=" * 60)
-print(" Study Session")
-print("=" * 60)
-
-queries = [
- "Can you explain generalized coordinates again? I remember we covered it but I'm fuzzy on the details.",
- "What topics should I review before my exam next week?",
- "I'm still confused about 2D collision problems. Can you walk me through an example?",
-]
-
-for query in queries:
- print(f"\nStudent: {query}")
- print("-" * 40)
- response = study_buddy(query)
- print(f"Study Buddy: {response}")
- time.sleep(1)
-```
-
-## 8. Get Review Suggestions
-
-
-```python
-print("=" * 60)
-print(" Recommended Review Topics")
-print("=" * 60)
-print(get_review_suggestions())
-```
-
-## 9. Knowledge Summary
-
-
-```python
-print("=" * 60)
-print(" Knowledge Summary")
-print("=" * 60)
-print(get_knowledge_summary())
-```
-
-## 10. Try Your Own Question
-
-
-```python
-your_question = "What are my biggest knowledge gaps right now?" # Change this!
-
-print(f"You: {your_question}")
-print("-" * 40)
-print(f"Study Buddy: {study_buddy(your_question)}")
-```
-
-## 11. Cleanup
-
-
-```python
-hindsight.close()
-print("Client connection closed.")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/support-agent-shared-knowledge.md b/skills/hindsight-docs/references/cookbook/recipes/support-agent-shared-knowledge.md
deleted file mode 100644
index 3bf5ba11..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/support-agent-shared-knowledge.md
+++ /dev/null
@@ -1,315 +0,0 @@
----
-sidebar_position: 3
----
-
-# Support Agent with Shared Knowledge
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/03-support-agent-shared-knowledge.ipynb)
-:::
-
-
-This pattern shows how to build a support agent that combines **per-user memory** with **shared product knowledge** (RAG), giving users personalized support while leveraging a single source of truth for documentation.
-
-## The Problem
-
-You're building a support agent that needs to:
-- Remember each user's history, preferences, and past issues
-- Access shared product documentation
-- Keep user data completely isolated from other users
-
-A naive approach would index product docs into each user's memory bank, but this is expensive and wasteful (N copies for N users).
-
-## The Solution: Multi-Bank Architecture
-
-```
-┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
-│ User A Bank │ │ User B Bank │ │ Shared Docs │
-│ │ │ │ │ Bank │
-│ - Conversations│ │ - Conversations│ │ │
-│ - Preferences │ │ - Preferences │ │ - Product docs │
-│ - Past issues │ │ - Past issues │ │ - FAQs │
-│ - Solutions │ │ - Solutions │ │ - Guides │
-└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
- │ │ │
- └───────────────────────┴───────────────────────┘
- │
- Agent queries
- multiple banks
-```
-
-**Key benefits:**
-- Product docs indexed once, shared by all users
-- User memory is 100% isolated
-- Simple mental model, no complex filtering
-
-
-```python
-!pip install hindsight-client nest_asyncio openai python-dotenv -U
-```
-
-## 1. Set Up Memory Banks
-
-Create three types of banks:
-
-
-```python
-# Jupyter notebooks already run an asyncio event loop. The hindsight client
-# uses loop.run_until_complete() internally, but Python doesn't allow nested
-# event loops by default. nest_asyncio patches this to allow nesting.
-import nest_asyncio
-nest_asyncio.apply()
-
-import os
-from dotenv import load_dotenv
-from openai import OpenAI as OpenAIClient
-
-# Load environment variables from .env file
-# Copy .env.example to .env and fill in your values
-load_dotenv()
-
-# Configuration (override with env vars if set)
-HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
-HINDSIGHT_UI_URL = os.getenv("HINDSIGHT_UI_URL", "http://localhost:9999")
-
-from hindsight_client import Hindsight
-
-client = Hindsight(base_url=HINDSIGHT_API_URL)
-llm = OpenAIClient() # Uses OPENAI_API_KEY from .env
-
-# Shared knowledge bank (created once)
-shared_bank = client.create_bank(
- bank_id="product-docs",
- name="Product Documentation"
-)
-
-# Per-user banks (created when user signs up)
-def create_user_bank(user_id: str):
- return client.create_bank(
- bank_id=f"user-{user_id}",
- name=f"Memory for {user_id}"
- )
-```
-
-## 2. Index Product Documentation
-
-Index your product docs into the shared bank (do this once, or on doc updates):
-
-
-```python
-# Index product documentation - retain each doc separately
-client.retain(
- bank_id="product-docs",
- content="# Pricing Tiers\n\nBasic: $10/mo, Pro: $25/mo, Enterprise: Contact us"
-)
-
-client.retain(
- bank_id="product-docs",
- content="# Getting Started\n\nTo set up your account, visit the dashboard and click 'New Project'"
-)
-
-# View the stored documents in the UI:
-print(f"View documents: {HINDSIGHT_UI_URL}/banks/product-docs?view=documents")
-```
-
-## 3. Store User Conversations
-
-After each support interaction, retain it in the user's bank:
-
-
-```python
-def save_conversation(user_id: str, messages: list):
- # Convert messages to string format
- content = "\n".join([f"{m['role']}: {m['content']}" for m in messages])
- client.retain(
- bank_id=f"user-{user_id}",
- content=content
- )
-```
-
-## 4. Query Multiple Banks at Support Time
-
-When handling a user query, retrieve context from both banks:
-
-
-```python
-def get_support_context(user_id: str, query: str):
- # Get user's personal context
- user_context = client.recall(
- bank_id=f"user-{user_id}",
- query=query
- )
-
- # Get relevant product documentation
- docs_context = client.recall(
- bank_id="product-docs",
- query=query
- )
-
- return {
- "user_history": user_context.results,
- "documentation": docs_context.results
- }
-```
-
-## 5. Build the Agent Prompt
-
-Combine both contexts in your agent's prompt:
-
-
-```python
-def format_results(results):
- """Format recall results for the prompt."""
- if not results:
- return "No relevant information found."
- return "\n".join([f"- {r.text}" for r in results])
-
-def build_prompt(query: str, context: dict) -> str:
- return f"""You are a helpful support agent.
-
-## User's History
-{format_results(context["user_history"])}
-
-## Product Documentation
-{format_results(context["documentation"])}
-
-## Current Question
-{query}
-
-Use the user's history to personalize your response and the documentation
-for accurate product information. If you find a solution, remember it for
-future reference.
-"""
-```
-
-## Promoting Learnings to Shared Knowledge
-
-When the agent discovers a solution that's not in the docs, you can optionally promote it to a "learnings" bank:
-
-```
-┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
-│ User A Bank │ │ Shared Docs │ │ Learnings │
-│ │ │ Bank │ │ Bank │
-│ - Conversations│ │ │ │ │
-│ - Preferences │ │ - Product docs │ │ - Verified │
-│ - Past issues │ │ - FAQs │ │ solutions │
-│ - Solutions │ │ - Guides │ │ - Workarounds │
-└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
- │ │ │
- └───────────────────────┴───────────────────────┘
- │
- Agent queries
- all three banks
-```
-
-
-```python
-# Optional: Create a curated learnings bank
-learnings_bank = client.create_bank(
- bank_id="support-learnings",
- name="Curated Support Learnings"
-)
-
-# After a successful resolution
-def promote_learning(insight: str):
- client.retain(
- bank_id="support-learnings",
- content=insight
- )
-```
-
-## Complete Example
-
-
-```python
-def format_results(results):
- if not results:
- return "No relevant information found."
- return "\n".join([f"- {r.text}" for r in results])
-
-def handle_support_request(user_id: str, query: str):
- # 1. Recall from user's memory
- user_recall = client.recall(
- bank_id=f"user-{user_id}",
- query=query
- )
-
- # 2. Recall from shared docs
- docs_recall = client.recall(
- bank_id="product-docs",
- query=query
- )
-
- # 3. Recall from learnings (optional)
- learnings_recall = client.recall(
- bank_id="support-learnings",
- query=query
- )
-
- # 4. Build system prompt with context
- system_prompt = f"""You are a helpful support agent. Use the context below to answer the user's question.
-
-## User's History
-{format_results(user_recall.results)}
-
-## Product Documentation
-{format_results(docs_recall.results)}
-
-## Known Solutions
-{format_results(learnings_recall.results)}
-
-Provide helpful, accurate responses based on the documentation. Reference the user's history when relevant."""
-
- # 5. Generate response using OpenAI
- response = llm.chat.completions.create(
- model="gpt-4o-mini",
- messages=[
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": query}
- ]
- )
- assistant_response = response.choices[0].message.content
-
- # 6. Save the conversation to user's memory
- conversation = f"user: {query}\nassistant: {assistant_response}"
- client.retain(
- bank_id=f"user-{user_id}",
- content=conversation
- )
-
- return assistant_response
-
-# Test the function
-create_user_bank("bob")
-print("User: How do I get started?")
-result = handle_support_request("bob", "How do I get started?")
-print(f"Assistant: {result}")
-print(f"\nView user memory: {HINDSIGHT_UI_URL}/banks/user-bob?view=documents")
-```
-
-## When to Use This Pattern
-
-**Good fit:**
-- Support agents with shared documentation
-- Multi-tenant applications with shared reference data
-- Any scenario needing user isolation + shared knowledge
-
-**Consider alternatives if:**
-- You need cross-user learning (users benefiting from other users' solutions)
-- Entity relationships must span across users and docs
-
-## Cleanup
-
-Delete the banks created during this notebook:
-
-
-```python
-import requests
-
-# Delete all banks created in this notebook
-for bank_id in ["product-docs", "support-learnings", "user-bob"]:
- response = requests.delete(f"{HINDSIGHT_API_URL}/v1/default/banks/{bank_id}")
- print(f"Deleted {bank_id}: {response.json()}")
-```
diff --git a/skills/hindsight-docs/references/cookbook/recipes/tool-learning-demo.md b/skills/hindsight-docs/references/cookbook/recipes/tool-learning-demo.md
deleted file mode 100644
index c2e3b870..00000000
--- a/skills/hindsight-docs/references/cookbook/recipes/tool-learning-demo.md
+++ /dev/null
@@ -1,372 +0,0 @@
----
-sidebar_position: 5
----
-
-# Routing Tool Learning
-
-
-:::tip Run this notebook
-This recipe is available as an interactive Jupyter notebook.
-[**Open in GitHub →**](https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/05-tool-learning-demo.ipynb)
-:::
-
-
-This notebook demonstrates how Hindsight helps an LLM learn which tool to use when tool names are ambiguous. Without memory, the LLM might randomly select between similarly-named tools. With Hindsight, it learns from past interactions and consistently makes the correct choice.
-
-## The Scenario
-
-We have a task routing system with two tools:
-- `route_to_channel_alpha` - Routes to processing channel Alpha
-- `route_to_channel_omega` - Routes to processing channel Omega
-
-The tool names and descriptions are **intentionally vague**. In reality:
-- Channel Alpha handles **FINANCIAL/PAYMENT** tasks (refunds, billing, etc.)
-- Channel Omega handles **TECHNICAL/SUPPORT** tasks (bugs, features, etc.)
-
-**Without Hindsight:** The LLM guesses randomly based on vague descriptions
-**With Hindsight:** The LLM learns from feedback which channel handles what
-
-## Prerequisites
-
-Make sure you have Hindsight running:
-
-```bash
-export OPENAI_API_KEY=your-key
-
-docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
- -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \
- -e HINDSIGHT_API_LLM_MODEL=gpt-4o-mini \
- -v $HOME/.hindsight-docker:/home/hindsight/.pg0 \
- ghcr.io/vectorize-io/hindsight:latest
-```
-
-## Installation
-
-
-```python
-!pip install hindsight-litellm hindsight-client litellm nest_asyncio python-dotenv -U -q
-```
-
-## Setup
-
-
-```python
-import os
-import json
-import uuid
-import time
-import logging
-import nest_asyncio
-from typing import Optional
-from dotenv import load_dotenv
-
-nest_asyncio.apply()
-load_dotenv()
-
-logging.basicConfig(level=logging.INFO)
-logging.getLogger("LiteLLM").setLevel(logging.WARNING)
-logging.getLogger("LiteLLM Router").setLevel(logging.WARNING)
-logging.getLogger("httpx").setLevel(logging.WARNING)
-
-import litellm
-import hindsight_litellm
-from hindsight_client import Hindsight
-
-HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888")
-
-if not os.getenv("OPENAI_API_KEY"):
- print("Warning: OPENAI_API_KEY not set")
-```
-
-## Define Tools
-
-These tool definitions are **intentionally ambiguous** - the descriptions don't reveal which channel handles what type of request.
-
-
-```python
-TOOLS = [
- {
- "type": "function",
- "function": {
- "name": "route_to_channel_alpha",
- "description": "Routes the customer request to processing channel Alpha. Use this channel for appropriate request types.",
- "parameters": {
- "type": "object",
- "properties": {
- "request_summary": {
- "type": "string",
- "description": "A brief summary of the customer's request"
- },
- "priority": {
- "type": "string",
- "enum": ["low", "medium", "high"],
- "description": "Priority level of the request"
- }
- },
- "required": ["request_summary"]
- }
- }
- },
- {
- "type": "function",
- "function": {
- "name": "route_to_channel_omega",
- "description": "Routes the customer request to processing channel Omega. Use this channel for appropriate request types.",
- "parameters": {
- "type": "object",
- "properties": {
- "request_summary": {
- "type": "string",
- "description": "A brief summary of the customer's request"
- },
- "priority": {
- "type": "string",
- "enum": ["low", "medium", "high"],
- "description": "Priority level of the request"
- }
- },
- "required": ["request_summary"]
- }
- }
- }
-]
-```
-
-## Test Scenarios
-
-A mix of financial and technical requests to test routing accuracy.
-
-
-```python
-TEST_SCENARIOS = [
- {
- "type": "financial",
- "request": "I was charged twice for my subscription last month. I need a refund for the duplicate charge.",
- "correct_tool": "route_to_channel_alpha"
- },
- {
- "type": "technical",
- "request": "The app keeps crashing when I try to upload a file larger than 10MB. This bug is blocking my work.",
- "correct_tool": "route_to_channel_omega"
- },
- {
- "type": "financial",
- "request": "My invoice shows an incorrect amount. The billing department needs to fix this.",
- "correct_tool": "route_to_channel_alpha"
- },
- {
- "type": "technical",
- "request": "I'd like to request a new feature: the ability to export reports as PDF.",
- "correct_tool": "route_to_channel_omega"
- },
- {
- "type": "financial",
- "request": "I need to update my payment method and understand why my last payment failed.",
- "correct_tool": "route_to_channel_alpha"
- },
-]
-```
-
-## Helper Functions
-
-
-```python
-SYSTEM_PROMPT = """You are a customer service routing agent. Your job is to route customer requests to the appropriate processing channel.
-
-You have access to two routing channels:
-- route_to_channel_alpha: Routes to channel Alpha
-- route_to_channel_omega: Routes to channel Omega
-
-Analyze the customer's request and route it to the most appropriate channel. You must call one of the routing functions to process the request.
-
-Important: Base your routing decision on what you know about each channel's purpose. If you have learned from previous interactions which channel handles specific types of requests, use that knowledge."""
-
-
-def make_routing_request(user_request: str, use_hindsight: bool, bank_id: Optional[str] = None):
- """Make a routing request and return the tool called."""
- messages = [
- {"role": "system", "content": SYSTEM_PROMPT},
- {"role": "user", "content": f"Customer Request: {user_request}"}
- ]
-
- if use_hindsight and bank_id:
- response = hindsight_litellm.completion(
- model="gpt-4o-mini",
- messages=messages,
- tools=TOOLS,
- tool_choice="required",
- temperature=0.0,
- )
- else:
- response = litellm.completion(
- model="gpt-4o-mini",
- messages=messages,
- tools=TOOLS,
- tool_choice="required",
- temperature=0.7,
- )
-
- if response.choices[0].message.tool_calls:
- tool_call = response.choices[0].message.tool_calls[0]
- return tool_call.function.name
- return None
-
-
-def store_feedback(bank_id: str, request: str, correct_tool: str, request_type: str):
- """Store feedback about which tool was correct for a request type."""
- client = Hindsight(base_url=HINDSIGHT_API_URL, timeout=60.0)
-
- feedback_content = f"""ROUTING FEEDBACK:
-Request type: {request_type}
-Customer request: "{request}"
-Correct routing: {correct_tool}
-
-LEARNED RULE: {request_type.upper()} requests (like refunds, billing, payments, charges, invoices) should ALWAYS be routed to {correct_tool}.
-This is important institutional knowledge for routing decisions."""
-
- client.retain(
- bank_id=bank_id,
- content=feedback_content,
- context=f"routing:feedback:{request_type}",
- metadata={"request_type": request_type, "correct_tool": correct_tool}
- )
-```
-
-## Phase 1: Without Hindsight (No Memory)
-
-The LLM has no prior knowledge about which channel handles what. With ambiguous tool descriptions, it may route incorrectly.
-
-
-```python
-print("=" * 60)
-print("PHASE 1: WITHOUT HINDSIGHT (No Memory)")
-print("=" * 60)
-
-phase1_results = []
-for i, scenario in enumerate(TEST_SCENARIOS[:3], 1):
- print(f"\n--- Test {i}: {scenario['type'].upper()} Request ---")
- print(f"Request: \"{scenario['request'][:60]}...\"")
-
- tool_name = make_routing_request(scenario['request'], use_hindsight=False)
-
- is_correct = tool_name == scenario['correct_tool']
- phase1_results.append(is_correct)
-
- print(f"LLM chose: {tool_name}")
- print(f"Correct tool: {scenario['correct_tool']}")
- print(f"Result: {'✓ CORRECT' if is_correct else '✗ INCORRECT'}")
-
-phase1_accuracy = sum(phase1_results) / len(phase1_results) * 100
-print(f"\n>>> Phase 1 Accuracy: {phase1_accuracy:.0f}% ({sum(phase1_results)}/{len(phase1_results)})")
-```
-
-## Phase 2: Teaching Phase
-
-Now we provide feedback about correct routing to build memory. This simulates a human supervisor correcting the AI's routing decisions.
-
-
-```python
-bank_id = f"tool-learning-{uuid.uuid4().hex[:8]}"
-print(f"Using bank_id: {bank_id}")
-
-# Configure and enable Hindsight
-hindsight_litellm.configure(
- hindsight_api_url=HINDSIGHT_API_URL,
- bank_id=bank_id,
- store_conversations=True,
- inject_memories=True,
- max_memories=10,
- recall_budget="high",
- verbose=False,
-)
-hindsight_litellm.enable()
-
-print("\nStoring routing feedback...")
-
-feedback_examples = [
- ("I need a refund for an incorrect charge on my account.", "route_to_channel_alpha", "financial"),
- ("There's a bug in the system causing data loss.", "route_to_channel_omega", "technical"),
- ("My billing statement has errors that need correction.", "route_to_channel_alpha", "financial"),
- ("I want to request a new feature for the dashboard.", "route_to_channel_omega", "technical"),
-]
-
-for request, correct_tool, req_type in feedback_examples:
- print(f" Storing: {req_type.upper()} → {correct_tool}")
- store_feedback(bank_id, request, correct_tool, req_type)
-
-print("\nWaiting 15 seconds for Hindsight to process memories...")
-time.sleep(15)
-print("Done!")
-```
-
-## Phase 3: With Hindsight (Memory-Augmented)
-
-The LLM now has access to learned routing knowledge via Hindsight. It should route requests correctly based on past feedback.
-
-
-```python
-print("=" * 60)
-print("PHASE 3: WITH HINDSIGHT (Memory-Augmented)")
-print("=" * 60)
-
-phase3_results = []
-for i, scenario in enumerate(TEST_SCENARIOS, 1):
- print(f"\n--- Test {i}: {scenario['type'].upper()} Request ---")
- print(f"Request: \"{scenario['request'][:60]}...\"")
-
- tool_name = make_routing_request(
- scenario['request'],
- use_hindsight=True,
- bank_id=bank_id
- )
-
- is_correct = tool_name == scenario['correct_tool']
- phase3_results.append(is_correct)
-
- print(f"LLM chose: {tool_name}")
- print(f"Correct tool: {scenario['correct_tool']}")
- print(f"Result: {'✓ CORRECT' if is_correct else '✗ INCORRECT'}")
-
-phase3_accuracy = sum(phase3_results) / len(phase3_results) * 100
-print(f"\n>>> Phase 3 Accuracy: {phase3_accuracy:.0f}% ({sum(phase3_results)}/{len(phase3_results)})")
-```
-
-## Summary
-
-
-```python
-print("=" * 60)
-print("SUMMARY")
-print("=" * 60)
-print(f"\nPhase 1 (No Memory): {phase1_accuracy:.0f}% accuracy")
-print(f"Phase 3 (With Hindsight): {phase3_accuracy:.0f}% accuracy")
-
-improvement = phase3_accuracy - phase1_accuracy
-if improvement > 0:
- print(f"\n🎉 Improvement: +{improvement:.0f}% accuracy with Hindsight!")
-elif improvement == 0:
- print(f"\nNote: Results may vary. Run again to see learning effect.")
-else:
- print(f"\nNote: Phase 1 got lucky! Run again to see typical behavior.")
-
-print(f"\nMemories stored in bank: {bank_id}")
-print(f"View in UI: http://localhost:9999/banks/{bank_id}")
-
-print("\n" + "=" * 60)
-print("KEY INSIGHT")
-print("=" * 60)
-print("Hindsight allows the LLM to learn from experience which tool")
-print("to use, even when tool names/descriptions are ambiguous.")
-```
-
-## Cleanup
-
-
-```python
-hindsight_litellm.cleanup()
-
-# Optional: delete the bank
-import requests
-response = requests.delete(f"{HINDSIGHT_API_URL}/v1/default/banks/{bank_id}")
-print(f"Deleted bank: {response.json()}")
-```
diff --git a/skills/hindsight-docs/references/developer/api/documents.md b/skills/hindsight-docs/references/developer/api/documents.md
index b9d28177..c6d16d03 100644
--- a/skills/hindsight-docs/references/developer/api/documents.md
+++ b/skills/hindsight-docs/references/developer/api/documents.md
@@ -132,7 +132,6 @@ Retrieve a document's original text and metadata. This is useful for expanding d
### Python
```python
-import asyncio
from hindsight_client_api import ApiClient, Configuration
from hindsight_client_api.api import DocumentsApi
@@ -158,8 +157,6 @@ asyncio.run(get_document_example())
### Node.js
```javascript
-const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));
-
// Get document to expand context from recall results
const { data: doc, error } = await sdk.getDocument({
client: apiClient,
@@ -228,6 +225,124 @@ hindsight document delete my-bank meeting-2024-03-15
:::warning
Deleting a document permanently removes all memories extracted from it. This action cannot be undone.
+## List Documents
+
+List documents in a bank with optional filtering by ID and tags.
+
+### Python
+
+```python
+from hindsight_client_api import ApiClient, Configuration
+from hindsight_client_api.api import DocumentsApi
+
+async def list_documents_example():
+ config = Configuration(host="http://localhost:8888")
+ api_client = ApiClient(config)
+ api = DocumentsApi(api_client)
+
+ # List all documents
+ result = await api.list_documents(bank_id="my-bank")
+ print(f"Total documents: {result.total}")
+
+ # Filter by document ID substring
+ result = await api.list_documents(bank_id="my-bank", q="report")
+
+ # Filter by tags — only docs tagged with "team-a" (untagged excluded)
+ result = await api.list_documents(
+ bank_id="my-bank",
+ tags=["team-a"],
+ tags_match="any_strict",
+ )
+
+ # Combine ID search and tags
+ result = await api.list_documents(
+ bank_id="my-bank",
+ q="meeting",
+ tags=["team-a", "team-b"],
+ tags_match="all_strict", # must have both tags
+ )
+
+ # Paginate
+ result = await api.list_documents(bank_id="my-bank", limit=20, offset=40)
+ print(f"Page items: {len(result.items)}")
+
+import asyncio
+asyncio.run(list_documents_example())
+```
+
+### Node.js
+
+```javascript
+const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' }));
+
+// List all documents
+const { data: allDocs } = await sdk.listDocuments({
+ client: apiClient,
+ path: { bank_id: 'my-bank' }
+});
+console.log(`Total documents: ${allDocs.total}`);
+
+// Filter by document ID substring
+const { data: reportDocs } = await sdk.listDocuments({
+ client: apiClient,
+ path: { bank_id: 'my-bank' },
+ query: { q: 'report' }
+});
+
+// Filter by tags — only docs tagged with "team-a" (untagged excluded)
+const { data: taggedDocs } = await sdk.listDocuments({
+ client: apiClient,
+ path: { bank_id: 'my-bank' },
+ query: { tags: ['team-a'], tags_match: 'any_strict' }
+});
+
+// Combine ID search and tags
+const { data: filtered } = await sdk.listDocuments({
+ client: apiClient,
+ path: { bank_id: 'my-bank' },
+ query: { q: 'meeting', tags: ['team-a', 'team-b'], tags_match: 'all_strict' }
+});
+
+// Paginate
+const { data: page } = await sdk.listDocuments({
+ client: apiClient,
+ path: { bank_id: 'my-bank' },
+ query: { limit: 20, offset: 40 }
+});
+console.log(`Page items: ${page.items.length}`);
+```
+
+### CLI
+
+```bash
+# List all documents
+hindsight document list my-bank
+
+# Filter by ID substring
+hindsight document list my-bank --q report
+
+# Filter by tags
+hindsight document list my-bank --tags team-a --tags team-b
+```
+
+### Filtering Options
+
+| Parameter | Description |
+|---|---|
+| `q` | Case-insensitive substring match on document ID. `report` matches `report-2024`, `annual-report`, etc. |
+| `tags` | Filter by document tags. Accepts multiple values. |
+| `tags_match` | How to match tags (default: `any_strict`). See below. |
+| `limit` / `offset` | Pagination. Default limit is 100. |
+
+**`tags_match` modes:**
+
+| Mode | Behaviour |
+|---|---|
+| `any_strict` *(default)* | Document must have **at least one** of the specified tags. Untagged docs excluded. |
+| `any` | Same as `any_strict` but also includes untagged documents. |
+| `all_strict` | Document must have **all** specified tags. Untagged docs excluded. |
+| `all` | Same as `all_strict` but also includes untagged documents. |
+
## Document Response Format
```json
diff --git a/skills/hindsight-docs/references/developer/api/memory-banks.md b/skills/hindsight-docs/references/developer/api/memory-banks.md
index 1b86011e..01060055 100644
--- a/skills/hindsight-docs/references/developer/api/memory-banks.md
+++ b/skills/hindsight-docs/references/developer/api/memory-banks.md
@@ -73,6 +73,73 @@ Only active when `retain_extraction_mode` is `custom`. Replaces the built-in ext
See [Retain configuration](/developer/configuration#retain) for environment variable names and defaults.
+### entity_labels {#entity-labels}
+
+Defines a controlled vocabulary of `key:value` classification labels extracted at retain time and stored as entities. Because labels become entities, they automatically link memories in the knowledge graph (two memories with `pedagogy:scaffolding` are linked), improve semantic and BM25 retrieval, and optionally filter memories via the standard `tags`/`tags_match` API when `tag: true` is set on a group.
+
+Each entry in `entity_labels` is a **label group** — one classification dimension:
+
+```json
+{
+ "entity_labels": [
+ {
+ "key": "engagement",
+ "description": "Student engagement level during the session",
+ "type": "value",
+ "optional": true,
+ "values": [
+ { "value": "active", "description": "Student is actively participating" },
+ { "value": "passive", "description": "Student is listening but not participating" }
+ ]
+ },
+ {
+ "key": "pedagogy",
+ "description": "Teaching strategies used",
+ "type": "multi-values",
+ "values": [
+ { "value": "scaffolding", "description": "Breaking complex tasks into smaller steps" },
+ { "value": "direct_instruction", "description": "Explicit explanation by the teacher" },
+ { "value": "socratic_questioning", "description": "Guiding through questions rather than answers" }
+ ]
+ }
+ ]
+}
+```
+
+| Field | Default | Description |
+|-------|---------|-------------|
+| `key` | — | Label group identifier. Becomes the prefix in `key:value` entities. |
+| `description` | `""` | Shown to the LLM to guide label assignment. |
+| `type` | `"value"` | `"value"` → pick one enum value; `"multi-values"` → pick multiple; `"text"` → free-form string. |
+| `values` | `[]` | Allowed values for `"value"` and `"multi-values"` types. Ignored for `"text"`. |
+| `optional` | `true` | When `true` the LLM may skip the label if not applicable. When `false` the LLM must always assign a value. Has no effect on `"multi-values"` groups (always optional). |
+| `tag` | `false` | When `true`, extracted `key:value` labels are also written as tags on the memory unit, enabling filtering via `tags`/`tags_match` in recall/reflect. |
+
+**Enum groups** (`type: "value"` or `type: "multi-values"`): the LLM picks from the predefined `values` list; anything outside the list is silently dropped. Vocabulary stays stable and graph links stay tight. Use `"multi-values"` when a fact can belong to several values at once.
+
+**Free-text groups** (`type: "text"`): the LLM writes any string. Use the `description` field to provide examples and guidance. Graph clustering is less reliable than with enum groups because the model may phrase the same concept differently across sessions.
+
+```json
+{
+ "key": "topic",
+ "description": "Specific subject being discussed. Examples: algebra, quadratic equations, geometry.",
+ "type": "text",
+ "optional": true,
+ "values": []
+}
+```
+
+### entities_allow_free_form
+
+By default, entity labels are extracted **alongside** regular named entities (people, places, concepts). Set to `false` to disable free-form extraction so only label entities are stored:
+
+```json
+{
+ "entity_labels": [...],
+ "entities_allow_free_form": false
+}
+```
+
### enable_observations {#observations-configuration}
Toggles automatic observation consolidation on or off. Defaults to `true` when the observations feature is enabled on the server.
diff --git a/skills/hindsight-docs/references/developer/api/retain.md b/skills/hindsight-docs/references/developer/api/retain.md
index d27fc738..84d095fa 100644
--- a/skills/hindsight-docs/references/developer/api/retain.md
+++ b/skills/hindsight-docs/references/developer/api/retain.md
@@ -97,9 +97,15 @@ The raw text to store. This is the only required field. Hindsight chunks the con
### timestamp
-When the event described in the content actually occurred. Accepts any ISO 8601 string (e.g., `"2024-01-15T10:30:00Z"`). If omitted, defaults to the current time at ingestion.
+When the event described in the content actually occurred. Three forms are accepted:
-The timestamp is injected verbatim into the LLM fact-extraction prompt so the model can resolve relative temporal references in the content — for example, if the content says "last Monday", the model uses the provided timestamp as the anchor to pin down the actual date. It also enables temporal recall queries like "What happened last spring?" to work correctly.
+| Value | Behaviour |
+|-------|-----------|
+| Omitted / `null` | Defaults to the current time at ingestion. |
+| ISO 8601 string (e.g. `"2024-01-15T10:30:00Z"`) | Uses the provided datetime. |
+| `"unset"` | Stores the content **without any timestamp**. Use this for timeless material such as reference documents, books, or fictional content where no real event time exists. |
+
+The timestamp is injected into the LLM fact-extraction prompt so the model can resolve relative temporal references in the content — for example, if the content says "last Monday", the model uses the provided timestamp as the anchor to pin down the actual date. When `"unset"` is passed the prompt shows `Event Date: Unknown`, allowing the model to correctly return `N/A` for the `when` field of every extracted fact. Providing a real timestamp also enables temporal recall queries like "What happened last spring?" to work correctly.
### context
@@ -160,6 +166,64 @@ Use consistent naming patterns to keep tag filtering predictable. Common convent
See [Recall API](./recall#tags) for filtering by tags during retrieval.
+### observation_scopes
+
+Controls which [observations](../observations) this memory contributes to during consolidation. Each scope runs an independent pass, creating or updating observations tagged with only that scope's tags.
+
+:::info Scope isolation
+During consolidation, Hindsight uses `all_strict` matching to find existing observations to update — only observations whose tags exactly match the current scope are considered. This keeps scopes isolated: a memory consolidated under `["student:alice"]` will never bleed into an observation tagged `["student:alice", "teacher:bob"]`.
+The examples below use a lesson transcript retained with `tags: ["student:alice", "teacher:bob", "session-id:s1"]`.
+
+#### combined *(default)*
+
+One consolidation pass using all tags together. The resulting observation is tagged with the full set.
+
+- Observations created: `["student:alice", "teacher:bob", "session-id:s1"]`
+- ✗ *"What does Alice struggle with across all her sessions?"* — no match, because no observation was ever built for `student:alice` alone
+- ✗ *"How does Bob teach?"* — no match for `teacher:bob` alone
+- ✓ *"What happened in session s1 with Alice and Bob?"* — exact match
+
+**Use when** the memory is meaningful only as a whole and you never need to query any single tag in isolation.
+
+#### per_tag
+
+One consolidation pass per individual tag. Each tag gets its own isolated observation that grows with every new memory sharing that tag.
+
+- Observations created: `["student:alice"]` · `["teacher:bob"]` · `["session-id:s1"]`
+- ✓ *"What does Alice struggle with across all her sessions?"*
+- ✓ *"How does Bob teach?"*
+- ✓ *"What happened in session s1?"*
+- ✗ *"How does Alice perform specifically with Bob?"* — no observation for the `["student:alice", "teacher:bob"]` combination
+- ✗ *"How does Bob teach in online sessions?"* — no observation for `["teacher:bob", "session-id:s1"]`
+
+**Use when** content involves multiple tags that each represent an independent subject — the most common choice for multi-party content like conversations, lessons, or support sessions.
+
+#### all_combinations
+
+One pass per subset of tags — singles, pairs, triples, and so on. For 3 tags that is 7 passes.
+
+- Observations created: all `"per_tag"` scopes above, plus `["student:alice", "teacher:bob"]` · `["student:alice", "session-id:s1"]` · `["teacher:bob", "session-id:s1"]` · `["student:alice", "teacher:bob", "session-id:s1"]`
+- ✓ All questions from `"per_tag"` above
+- ✓ *"How does Alice perform specifically with Bob?"* — matched by `["student:alice", "teacher:bob"]`
+
+**Use when** you need observations at every granularity — per tag, per pair, per group.
+
+#### custom
+
+Pass an explicit list of tag sets. Each inner list is one scope.
+
+```json
+[["student:alice"], ["teacher:bob"], ["teacher:bob", "session-id:s1"]]
+```
+
+- Observations created: exactly those three scopes — nothing more
+- ✓ *"What does Alice struggle with?"*
+- ✓ *"How does Bob teach?"*
+- ✓ *"How does Bob teach in session s1 specifically?"*
+- ✗ *"What happened in session s1 regardless of teacher?"* — `["session-id:s1"]` alone was not included
+
+**Use when** you know exactly which combinations are meaningful and want to avoid unnecessary passes.
+
### Response
The synchronous retain response includes:
diff --git a/skills/hindsight-docs/references/developer/configuration.md b/skills/hindsight-docs/references/developer/configuration.md
index cb5aea1b..4e642092 100644
--- a/skills/hindsight-docs/references/developer/configuration.md
+++ b/skills/hindsight-docs/references/developer/configuration.md
@@ -553,6 +553,8 @@ Controls the retain (memory ingestion) pipeline.
| `HINDSIGHT_API_RETAIN_BATCH_ENABLED` | Use LLM Batch API for fact extraction (50% cost savings, only with async operations) | `false` |
| `HINDSIGHT_API_RETAIN_BATCH_POLL_INTERVAL_SECONDS` | Batch API polling interval in seconds | `60` |
+> **Entity labels** (`entity_labels`) and **free-form entity extraction** (`entities_allow_free_form`) are configured per bank via the [bank config API](/developer/api/memory-banks#retain-configuration), not as global environment variables — each bank can have its own controlled vocabulary. See [Entity Labels](/developer/retain#entity-labels) for details.
+
#### Customizing retain: when to use what
There are three levels of customization for the retain pipeline. Start with the simplest that covers your needs:
@@ -780,6 +782,7 @@ export HINDSIGHT_API_OBSERVATIONS_MISSION="Observations are recurring patterns i
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_REFLECT_MAX_ITERATIONS` | Max tool call iterations before forcing a response | `10` |
+| `HINDSIGHT_API_REFLECT_MAX_CONTEXT_TOKENS` | Max accumulated context tokens in the reflect loop before forcing final synthesis. Prevents `context_length_exceeded` errors on large banks. Lower this if your LLM has a context window smaller than 128K. | `100000` |
| `HINDSIGHT_API_REFLECT_MISSION` | Global reflect mission (identity and reasoning framing). Overridden per bank via config API. | - |
#### Disposition
diff --git a/skills/hindsight-docs/references/developer/extensions.md b/skills/hindsight-docs/references/developer/extensions.md
index 3264fe2e..ba8ef011 100644
--- a/skills/hindsight-docs/references/developer/extensions.md
+++ b/skills/hindsight-docs/references/developer/extensions.md
@@ -40,6 +40,10 @@ For other multi-tenant setups with separate schemas per tenant (e.g., custom JWT
Adds custom HTTP endpoints under the `/ext/` path prefix. Useful for adding domain-specific APIs that integrate with Hindsight's memory engine.
+Provides two router methods:
+- `get_router(memory)` — returns a FastAPI router mounted at `/ext/`
+- `get_root_router(memory)` — returns a FastAPI router mounted at the application root (for well-known endpoints or other paths that must be at specific locations). Returns `None` by default.
+
**No built-in implementation** - implement your own to add custom endpoints.
```bash
@@ -117,6 +121,7 @@ class JwtTenantExtension(TenantExtension):
async def authenticate(self, context: RequestContext) -> TenantContext:
token = context.api_key
if not token:
+ # Optional headers dict is forwarded in HTTP/MCP error responses
raise AuthenticationError("Bearer token required")
try:
@@ -129,6 +134,15 @@ class JwtTenantExtension(TenantExtension):
raise AuthenticationError(str(e))
```
+`AuthenticationError` accepts an optional `headers` dict that is forwarded in both HTTP and MCP error responses. This is useful for returning custom headers like `WWW-Authenticate`:
+
+```python
+raise AuthenticationError(
+ "Authorization required",
+ headers={"WWW-Authenticate": 'Bearer realm="example"'},
+)
+```
+
### Example: Custom HttpExtension
```python
@@ -151,9 +165,20 @@ class MyHttpExtension(HttpExtension):
return {"status": "ok"}
return router
+
+ def get_root_router(self, memory: MemoryEngine) -> APIRouter | None:
+ """Optional: mount routes at the application root (not under /ext/)."""
+ router = APIRouter()
+
+ @router.get("/.well-known/my-metadata")
+ async def metadata():
+ return {"version": "1.0"}
+
+ return router
```
-Routes are available at `/ext/hello`, `/ext/custom/{bank_id}/action`, etc.
+Routes from `get_router` are available at `/ext/hello`, `/ext/custom/{bank_id}/action`, etc.
+Routes from `get_root_router` are mounted at the app root (e.g., `/.well-known/my-metadata`).
### Example: Custom OperationValidatorExtension
diff --git a/skills/hindsight-docs/references/developer/models.md b/skills/hindsight-docs/references/developer/models.md
index 339bf6c5..59667ada 100644
--- a/skills/hindsight-docs/references/developer/models.md
+++ b/skills/hindsight-docs/references/developer/models.md
@@ -26,6 +26,12 @@ Hindsight works with any provider that exposes an OpenAI-compatible API (e.g., A
See [Configuration](./configuration#llm-provider) for setup examples.
:::
+### Benchmarks
+
+Not sure which model to use? The **[Model Leaderboard](https://benchmarks.hindsight.vectorize.io/)** benchmarks models across accuracy, speed, cost, and reliability for retain, reflect, and observation consolidation so you can pick the right trade-off for your use case.
+
+[](https://benchmarks.hindsight.vectorize.io/)
+
### Tested Models
The following models have been tested and verified to work correctly with Hindsight:
diff --git a/skills/hindsight-docs/references/developer/observations.md b/skills/hindsight-docs/references/developer/observations.md
index 281a0d85..5a02d3f4 100644
--- a/skills/hindsight-docs/references/developer/observations.md
+++ b/skills/hindsight-docs/references/developer/observations.md
@@ -139,6 +139,14 @@ This ensures responses stay accurate even as the underlying data changes.
---
+## Observation Scopes
+
+By default, observations are scoped to all of a memory's tags combined. The `observation_scopes` retain parameter lets you control this — building separate observations per tag, per combination, or with a custom list of scopes. This is key when a single memory carries multiple tags and you want each tag to accumulate its own observations independently.
+
+See [`observation_scopes` in the Retain API](./api/retain#observation_scopes) for the full explanation and options.
+
+---
+
## Observations Mission
You can define exactly what this bank should synthesise by setting an **observations mission** (`observations_mission`). This replaces the built-in durable-knowledge rules with your own instructions, letting you control what shape observations take.
diff --git a/skills/hindsight-docs/references/developer/retain.md b/skills/hindsight-docs/references/developer/retain.md
index fc85d5e0..4966ef6f 100644
--- a/skills/hindsight-docs/references/developer/retain.md
+++ b/skills/hindsight-docs/references/developer/retain.md
@@ -90,6 +90,12 @@ The same entity mentioned different ways gets unified:
If "Alice" appears with "Google" and "Stanford" multiple times, a new "Alice" mentioning those is likely the same person. Hindsight uses co-occurrence patterns to disambiguate common names.
+### Entity Labels
+
+You can define a controlled vocabulary of `key:value` classification labels (e.g. `pedagogy:scaffolding`, `engagement:active`) that are extracted at retain time and stored as entities. Because labels become entities, they automatically link related memories in the knowledge graph and improve both semantic and keyword retrieval. Labels can optionally also write to the memory unit's tags, enabling standard tag-based filtering during recall and reflect.
+
+See [entity_labels in the bank config](/developer/api/memory-banks#entity-labels) for full configuration details.
+
---
## Building Connections
diff --git a/skills/hindsight-docs/references/sdks/integrations/chat.md b/skills/hindsight-docs/references/sdks/integrations/chat.md
index 8b024f52..a35b3b18 100644
--- a/skills/hindsight-docs/references/sdks/integrations/chat.md
+++ b/skills/hindsight-docs/references/sdks/integrations/chat.md
@@ -4,7 +4,7 @@ sidebar_position: 5
# Vercel Chat SDK
-The `@vectorize-io/hindsight-chat` package gives your [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. Works with Slack, Discord, Teams, Google Chat, GitHub, and Linear.
+We built `@vectorize-io/hindsight-chat` to give [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear — no custom plumbing required.
## Installation
@@ -60,7 +60,7 @@ chat.onNewMention(
### `withHindsightChat(options, handler)`
-Returns a standard Chat SDK handler `(thread, message) => Promise`.
+`withHindsightChat` wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler `(thread, message) => Promise` so it drops in without changing your handler signature.
#### Options
@@ -80,7 +80,7 @@ Returns a standard Chat SDK handler `(thread, message) => Promise`.
### Context (`ctx`)
-The third argument passed to your handler:
+We inject a third `ctx` argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank:
| Property/Method | Description |
|----------------|-------------|
@@ -160,4 +160,4 @@ chat.onNewMention(
## Error Handling
-Memory failures never break your bot. Auto-recall and auto-retain errors are logged as warnings and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed.
+We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed.
diff --git a/skills/hindsight-docs/references/sdks/integrations/pydantic-ai.md b/skills/hindsight-docs/references/sdks/integrations/pydantic-ai.md
new file mode 100644
index 00000000..8f6e6baa
--- /dev/null
+++ b/skills/hindsight-docs/references/sdks/integrations/pydantic-ai.md
@@ -0,0 +1,184 @@
+---
+sidebar_position: 6
+---
+
+# Pydantic AI
+
+Persistent memory tools for [Pydantic AI](https://ai.pydantic.dev/) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — all async-native with no thread-pool hacks.
+
+## Features
+
+- **Async-Native Tools** — Uses Pydantic AI's async tool interface directly (`aretain`, `arecall`, `areflect`)
+- **Memory Instructions** — Auto-inject relevant memories into every agent run via `instructions=[...]`
+- **Three Memory Tools** — Retain (store), Recall (search), Reflect (synthesize) — include any combination
+- **Simple Configuration** — Configure once globally, or pass a client directly
+- **Lightweight** — Depends on `pydantic-ai-slim` to avoid pulling in all model providers
+
+## Installation
+
+```bash
+pip install hindsight-pydantic-ai
+```
+
+## Quick Start
+
+```python
+from hindsight_client import Hindsight
+from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions
+from pydantic_ai import Agent
+
+client = Hindsight(base_url="http://localhost:8888")
+
+agent = Agent(
+ "openai:gpt-4o",
+ tools=create_hindsight_tools(client=client, bank_id="user-123"),
+ instructions=[memory_instructions(client=client, bank_id="user-123")],
+)
+
+result = await agent.run("What do you remember about my preferences?")
+print(result.output)
+```
+
+The agent now has three tools it can call:
+
+- **`hindsight_retain`** — Store information to long-term memory
+- **`hindsight_recall`** — Search long-term memory for relevant facts
+- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
+
+The `memory_instructions` callable automatically recalls relevant memories and injects them into the system prompt on every run.
+
+## Tools Only (No Auto-Injection)
+
+If you want the agent to decide when to use memory rather than always injecting context:
+
+```python
+agent = Agent(
+ "openai:gpt-4o",
+ tools=create_hindsight_tools(client=client, bank_id="user-123"),
+)
+```
+
+## Instructions Only (No Tools)
+
+If you just want memories auto-injected without giving the agent explicit memory tools:
+
+```python
+agent = Agent(
+ "openai:gpt-4o",
+ instructions=[memory_instructions(client=client, bank_id="user-123")],
+)
+```
+
+## Selecting Tools
+
+Include only the tools you need:
+
+```python
+tools = create_hindsight_tools(
+ client=client,
+ bank_id="user-123",
+ include_retain=True,
+ include_recall=True,
+ include_reflect=False, # Omit reflect
+)
+```
+
+## Global Configuration
+
+Instead of passing a client to every call, configure once:
+
+```python
+from hindsight_pydantic_ai import configure, create_hindsight_tools
+
+configure(
+ hindsight_api_url="http://localhost:8888",
+ api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
+ budget="mid", # Recall budget: low/mid/high
+ max_tokens=4096, # Max tokens for recall results
+ tags=["env:prod"], # Tags for stored memories
+ recall_tags=["scope:global"], # Tags to filter recall
+ recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
+)
+
+# Now create tools without passing client — uses global config
+tools = create_hindsight_tools(bank_id="user-123")
+```
+
+## Per-Tool Overrides
+
+Constructor arguments override global configuration:
+
+```python
+tools = create_hindsight_tools(
+ bank_id="user-123",
+ budget="high", # Override global budget
+ max_tokens=8192, # Override global max_tokens
+ tags=["session:abc"], # Override global tags
+)
+```
+
+## Memory Instructions Options
+
+Customize what memories get injected and how:
+
+```python
+instructions_fn = memory_instructions(
+ client=client,
+ bank_id="user-123",
+ query="relevant context about the user", # What to search for
+ budget="low", # Keep it fast
+ max_results=5, # Limit injected memories
+ max_tokens=4096, # Max recall tokens
+ prefix="Relevant memories:\n", # Text before the memory list
+ tags=["scope:global"], # Filter by tags
+ tags_match="any", # Tag match mode
+)
+```
+
+## API Reference
+
+### `create_hindsight_tools()`
+
+| Parameter | Default | Description |
+|---|---|---|
+| `bank_id` | *required* | Hindsight memory bank ID |
+| `client` | `None` | Pre-configured Hindsight client |
+| `hindsight_api_url` | `None` | API URL (used if no client provided) |
+| `api_key` | `None` | API key (used if no client provided) |
+| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
+| `max_tokens` | `4096` | Maximum tokens for recall results |
+| `tags` | `None` | Tags applied when storing memories |
+| `recall_tags` | `None` | Tags to filter when searching |
+| `recall_tags_match` | `"any"` | Tag matching mode |
+| `include_retain` | `True` | Include the retain (store) tool |
+| `include_recall` | `True` | Include the recall (search) tool |
+| `include_reflect` | `True` | Include the reflect (synthesize) tool |
+
+### `memory_instructions()`
+
+| Parameter | Default | Description |
+|---|---|---|
+| `bank_id` | *required* | Hindsight memory bank ID |
+| `client` | `None` | Pre-configured Hindsight client |
+| `hindsight_api_url` | `None` | API URL (used if no client provided) |
+| `api_key` | `None` | API key (used if no client provided) |
+| `query` | `"relevant context about the user"` | Recall query for memory injection |
+| `budget` | `"low"` | Recall budget level |
+| `max_results` | `5` | Maximum memories to inject |
+| `max_tokens` | `4096` | Maximum tokens for recall results |
+| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
+| `tags` | `None` | Tags to filter recall results |
+| `tags_match` | `"any"` | Tag matching mode |
+
+### `configure()`
+
+| Parameter | Default | Description |
+|---|---|---|
+| `hindsight_api_url` | Production API | Hindsight API URL |
+| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
+| `budget` | `"mid"` | Default recall budget level |
+| `max_tokens` | `4096` | Default max tokens for recall |
+| `tags` | `None` | Default tags for retain operations |
+| `recall_tags` | `None` | Default tags to filter recall |
+| `recall_tags_match` | `"any"` | Default tag matching mode |
+| `verbose` | `False` | Enable verbose logging |
diff --git a/uv.lock b/uv.lock
index e317cf03..234f4e1b 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1422,7 +1422,7 @@ wheels = [
[[package]]
name = "hindsight-all"
-version = "0.4.14"
+version = "0.4.15"
source = { editable = "hindsight" }
dependencies = [
{ name = "hindsight-api" },
@@ -1448,7 +1448,7 @@ provides-extras = ["test"]
[[package]]
name = "hindsight-api"
-version = "0.4.14"
+version = "0.4.15"
source = { editable = "hindsight-api" }
dependencies = [
{ name = "aiohttp" },
@@ -1596,7 +1596,7 @@ dev = [
[[package]]
name = "hindsight-client"
-version = "0.4.14"
+version = "0.4.15"
source = { editable = "hindsight-clients/python" }
dependencies = [
{ name = "aiohttp" },
@@ -1630,7 +1630,7 @@ provides-extras = ["test"]
[[package]]
name = "hindsight-dev"
-version = "0.4.14"
+version = "0.4.15"
source = { editable = "hindsight-dev" }
dependencies = [
{ name = "hindsight-api" },
@@ -1678,7 +1678,7 @@ dev = [
[[package]]
name = "hindsight-embed"
-version = "0.4.14"
+version = "0.4.15"
source = { editable = "hindsight-embed" }
dependencies = [
{ name = "httpx" },