diff --git a/hindsight-dev/hindsight_dev/sync_cookbook.py b/hindsight-dev/hindsight_dev/sync_cookbook.py
index 362bd4c3..52f13a27 100644
--- a/hindsight-dev/hindsight_dev/sync_cookbook.py
+++ b/hindsight-dev/hindsight_dev/sync_cookbook.py
@@ -70,37 +70,137 @@ def extract_title_from_notebook(notebook_path: Path) -> str:
def extract_description_from_notebook(notebook_path: Path) -> str | None:
- """Extract first paragraph after title from notebook."""
+ """Extract description from notebook metadata."""
try:
content = json.loads(notebook_path.read_text())
- for cell in content.get("cells", []):
- if cell.get("cell_type") == "markdown":
- source = cell.get("source", [])
- if isinstance(source, list):
- source = "".join(source)
-
- lines = source.split("\n")
- found_title = False
- description = []
-
- for line in lines:
- if line.startswith("#"):
- found_title = True
- continue
- if found_title and line.strip():
- if line.startswith("#"):
- break
- description.append(line.strip())
- if line.strip().endswith("."):
- break
-
- if description:
- return " ".join(description)[:200]
+ metadata = content.get("metadata", {})
+ description = metadata.get("description", "")
+ if description:
+ return description[:200]
except Exception:
pass
return None
+def extract_tags_from_notebook(notebook_path: Path) -> list[str]:
+ """Extract tags from notebook metadata.
+
+ Supports both array format and structured object format.
+ """
+ try:
+ content = json.loads(notebook_path.read_text())
+ metadata = content.get("metadata", {})
+ tags = metadata.get("tags", [])
+
+ # Array format: ["Python", "Client"]
+ if isinstance(tags, list):
+ return tags
+
+ # Object format: { "language": "Python", "sdk": "Client", "topic": "Learning" }
+ if isinstance(tags, dict):
+ result = []
+ for key in ["language", "sdk", "topic"]:
+ if key in tags and tags[key]:
+ result.append(tags[key])
+ return result
+ except Exception:
+ pass
+ return []
+
+
+def extract_description_from_readme(readme_path: Path) -> str | None:
+ """Extract description from frontmatter in README."""
+ try:
+ content = readme_path.read_text()
+ # Check for frontmatter
+ if content.startswith("---"):
+ end_idx = content.find("---", 3)
+ if end_idx > 0:
+ frontmatter = content[3:end_idx]
+ # Look for description: line
+ for line in frontmatter.split("\n"):
+ if line.strip().startswith("description:"):
+ desc = line.split("description:", 1)[1].strip()
+ # Remove quotes if present
+ desc = desc.strip('"').strip("'")
+ return desc[:200]
+ except Exception:
+ pass
+ return None
+
+
+def extract_tags_from_readme(readme_path: Path) -> list[str]:
+ """Extract tags from frontmatter in README if present.
+
+ Supports multiple formats:
+ - Array: tags: ["Python", "Client"]
+ - Structured YAML: tags:\n language: "Python"\n sdk: "Client"
+ - Object literal: tags: { language: "Python", sdk: "Client" }
+ """
+ try:
+ content = readme_path.read_text()
+ # Check for frontmatter
+ if content.startswith("---"):
+ end_idx = content.find("---", 3)
+ if end_idx > 0:
+ frontmatter = content[3:end_idx]
+ lines = frontmatter.split("\n")
+
+ # Look for tags: line
+ for i, line in enumerate(lines):
+ if line.strip().startswith("tags:"):
+ tags_str = line.split("tags:", 1)[1].strip()
+
+ # Inline array format: tags: ["Python", "Client"]
+ if tags_str.startswith("["):
+ tags_str = tags_str.strip("[]")
+ return [t.strip().strip('"').strip("'") for t in tags_str.split(",")]
+
+ # JavaScript object literal format: tags: { language: "Python", sdk: "Client", topic: "Learning" }
+ if tags_str.startswith("{"):
+ tags = []
+ # Extract the entire object literal (might span multiple lines)
+ obj_str = tags_str
+ if "}" not in obj_str:
+ # Multi-line object - collect remaining lines
+ for j in range(i + 1, len(lines)):
+ obj_str += " " + lines[j].strip()
+ if "}" in lines[j]:
+ break
+
+ # Parse the object literal
+ obj_str = obj_str.strip("{}")
+ # Split by comma and extract key-value pairs
+ for pair in obj_str.split(","):
+ if ":" in pair:
+ key, value = pair.split(":", 1)
+ value = value.strip().strip('"').strip("'")
+ if value:
+ tags.append(value)
+ return tags
+
+ # Structured YAML format:
+ # tags:
+ # language: "Python"
+ # sdk: "Client"
+ if not tags_str or tags_str == "":
+ # Parse structured tags from following lines
+ tags = []
+ for j in range(i + 1, len(lines)):
+ next_line = lines[j].strip()
+ if not next_line or not next_line.startswith(("language:", "sdk:", "topic:")):
+ break
+ # Extract value
+ if ":" in next_line:
+ value = next_line.split(":", 1)[1].strip().strip('"').strip("'")
+ if value:
+ tags.append(value)
+ return tags
+ except Exception:
+ pass
+ return []
+
+
def extract_title_from_readme(readme_path: Path) -> str | None:
"""Extract title from README's first # heading."""
try:
@@ -187,12 +287,16 @@ def process_notebooks(cookbook_dir: Path, recipes_dir: Path) -> list[dict]:
slug = slugify(notebook_path.name)
title = extract_title_from_notebook(notebook_path)
description = extract_description_from_notebook(notebook_path)
+ tags = extract_tags_from_notebook(notebook_path)
print(f" Processing: {notebook_path.name} → {slug}.md")
# Convert notebook to markdown
md_content = convert_notebook_to_markdown(notebook_path)
+ # Strip any existing frontmatter from converted notebook
+ md_content = strip_frontmatter(md_content)
+
# Create recipe page with frontmatter
notebook_url = f"https://github.com/vectorize-io/hindsight-cookbook/blob/main/notebooks/{notebook_path.name}"
@@ -225,6 +329,7 @@ This recipe is available as an interactive Jupyter notebook.
"slug": slug,
"title": title,
"description": description,
+ "tags": tags,
"id": f"cookbook/recipes/{slug}",
}
)
@@ -232,6 +337,15 @@ This recipe is available as an interactive Jupyter notebook.
return recipes
+def strip_frontmatter(content: str) -> str:
+ """Remove frontmatter from markdown content."""
+ if content.startswith("---"):
+ end_idx = content.find("---", 3)
+ if end_idx > 0:
+ return content[end_idx + 3 :].lstrip()
+ return content
+
+
def process_applications(cookbook_dir: Path, apps_dir: Path) -> list[dict]:
"""Process application directories with README.md."""
apps = []
@@ -252,11 +366,14 @@ def process_applications(cookbook_dir: Path, apps_dir: Path) -> list[dict]:
slug = entry.name
title = extract_title_from_readme(readme_path) or " ".join(word.capitalize() for word in slug.split("-"))
+ description = extract_description_from_readme(readme_path)
+ tags = extract_tags_from_readme(readme_path)
print(f" Processing app: {entry.name} → {slug}.md")
- # Read README content
+ # Read README content and strip existing frontmatter
readme_content = readme_path.read_text()
+ readme_content = strip_frontmatter(readme_content)
# Create application page with frontmatter
app_url = f"https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/{entry.name}"
@@ -289,6 +406,8 @@ This is a complete, runnable application demonstrating Hindsight integration.
{
"slug": slug,
"title": title,
+ "description": description,
+ "tags": tags,
"id": f"cookbook/applications/{slug}",
}
)
@@ -297,61 +416,19 @@ This is a complete, runnable application demonstrating Hindsight integration.
def update_sidebars(recipes: list[dict], apps: list[dict], sidebars_file: Path):
- """Update sidebars.ts with new recipe and app entries."""
+ """Update sidebars.ts - keep it simple with just the index."""
content = sidebars_file.read_text()
- # Build recipe items
- recipe_item_list = []
- for r in recipes:
- label = r["title"].replace("'", "\\'")
- recipe_item_list.append(
- f""" {{
- type: 'doc',
- id: '{r["id"]}',
- label: '{label}',
- }}"""
- )
- recipe_items = ",\n".join(recipe_item_list)
-
- # Build app items
- app_item_list = []
- for a in apps:
- label = a["title"].replace("'", "\\'")
- app_item_list.append(
- f""" {{
- type: 'doc',
- id: '{a["id"]}',
- label: '{label}',
- }}"""
- )
- app_items = ",\n".join(app_item_list)
-
- new_cookbook_sidebar = f"""cookbookSidebar: [
- {{
+ # Simple sidebar with just the cookbook index
+ new_cookbook_sidebar = """cookbookSidebar: [
+ {
type: 'doc',
id: 'cookbook/index',
- label: 'Overview',
- }},
- {{
- type: 'category',
- label: 'Recipes',
- collapsible: false,
- items: [
-{recipe_items}
- ],
- }},
- {{
- type: 'category',
- label: 'Applications',
- collapsible: false,
- items: [
-{app_items}
- ],
- }},
+ label: 'Cookbook',
+ },
]"""
- # Replace existing cookbookSidebar - match the full sidebar array including nested structures
- # We need to match balanced brackets
+ # Replace existing cookbookSidebar
start = content.find("cookbookSidebar:")
if start == -1:
raise ValueError("cookbookSidebar not found in sidebars.ts")
@@ -405,31 +482,113 @@ def clean_description(desc: str) -> str:
return desc
+def convert_tags_to_structured(tags: list[str]) -> dict[str, str]:
+ """Convert list of tags to structured format.
+
+ New format has 2 tags:
+ - sdk: Package name (detected from tag values)
+ - topic: anything else (Learning, Quick Start, etc.)
+
+ If sdk tag starts with '@vectorize-io', it's Node.js.
+ Otherwise assumes Python.
+ """
+ structured = {}
+ topic_tags = {"Learning", "Quick Start", "Recommendation", "Chat"}
+
+ for tag in tags:
+ # Check if it's a topic tag
+ if tag in topic_tags:
+ structured["topic"] = tag
+ # Check if it's already a package name (contains @ or -)
+ elif "@" in tag or (tag and not tag[0].isupper()):
+ structured["sdk"] = tag
+ else:
+ # Legacy tag values - map to new format
+ # For now, treat everything else as SDK/package identifier
+ structured["sdk"] = tag
+
+ return structured
+
+
def update_cookbook_index(recipes: list[dict], apps: list[dict], docs_dir: Path):
"""Update cookbook/index.mdx with recipe and app carousels."""
- # Build recipe items for the carousel
+ # Build recipe items for the carousel with descriptions and tags
recipe_items = []
for r in recipes:
title = r["title"].replace('"', '\\"')
- recipe_items.append(f' {{ title: "{title}", href: "/cookbook/recipes/{r["slug"]}" }}')
+ description = r.get("description", "")
+ if description:
+ description = clean_description(description).replace('"', '\\"')
+ tags = r.get("tags", [])
+
+ item = f' {{\n title: "{title}",\n href: "/cookbook/recipes/{r["slug"]}"'
+ if description:
+ item += f',\n description: "{description}"'
+ if tags:
+ # Convert tags list to structured format
+ structured_tags = convert_tags_to_structured(tags)
+ tags_parts = []
+ if "language" in structured_tags:
+ tags_parts.append(f'language: "{structured_tags["language"]}"')
+ if "sdk" in structured_tags:
+ tags_parts.append(f'sdk: "{structured_tags["sdk"]}"')
+ if "topic" in structured_tags:
+ tags_parts.append(f'topic: "{structured_tags["topic"]}"')
+ if tags_parts:
+ item += f",\n tags: {{ {', '.join(tags_parts)} }}"
+ item += "\n }"
+ recipe_items.append(item)
+
recipes_json = ",\n".join(recipe_items)
# Build app items for the carousel
app_items = []
for a in apps:
title = a["title"].replace('"', '\\"')
- app_items.append(f' {{ title: "{title}", href: "/cookbook/applications/{a["slug"]}" }}')
+ description = a.get("description", "")
+ if description:
+ description = clean_description(description).replace('"', '\\"')
+ tags = a.get("tags", [])
+
+ item = f' {{\n title: "{title}",\n href: "/cookbook/applications/{a["slug"]}"'
+ if description:
+ item += f',\n description: "{description}"'
+ if tags:
+ # Convert tags list to structured format
+ structured_tags = convert_tags_to_structured(tags)
+ tags_parts = []
+ if "language" in structured_tags:
+ tags_parts.append(f'language: "{structured_tags["language"]}"')
+ if "sdk" in structured_tags:
+ tags_parts.append(f'sdk: "{structured_tags["sdk"]}"')
+ if "topic" in structured_tags:
+ tags_parts.append(f'topic: "{structured_tags["topic"]}"')
+ if tags_parts:
+ item += f",\n tags: {{ {', '.join(tags_parts)} }}"
+ item += "\n }"
+ app_items.append(item)
+
apps_json = ",\n".join(app_items)
content = f"""---
sidebar_position: 1
+hide_table_of_contents: true
+pagination_next: null
+pagination_prev: null
+custom_edit_url: null
+sidebar_class_name: hidden-sidebar
---
import RecipeCarousel from '@site/src/components/RecipeCarousel';
+
+
# Cookbook
-Practical patterns, recipes, and complete applications for building with Hindsight.
+Learn how to build with Hindsight through practical examples:
+
+- **[Recipes](#recipes)** - Step-by-step guides and patterns for common use cases
+- **[Applications](#applications)** - Complete, runnable applications demonstrating Hindsight integration
+
+
"""
index_path = docs_dir / "index.mdx"
@@ -457,6 +618,63 @@ Practical patterns, recipes, and complete applications for building with Hindsig
print("Updated cookbook/index.mdx")
+def extract_existing_entries(docs_dir: Path) -> tuple[list[dict], list[dict]]:
+ """Extract existing recipe and app entries before syncing.
+
+ This allows us to preserve manually added entries that aren't in the cookbook repo.
+ Returns entries with their content stored in memory.
+ """
+ existing_recipes = []
+ existing_apps = []
+
+ recipes_dir = docs_dir / "recipes"
+ apps_dir = docs_dir / "applications"
+
+ # Scan existing recipes
+ if recipes_dir.exists():
+ for md_file in recipes_dir.glob("*.md"):
+ slug = md_file.stem
+ # Read file content
+ content = md_file.read_text()
+ # Try to extract title from first heading
+ title_match = re.search(r"^#\s+(.+)$", content, re.MULTILINE)
+ title = (
+ title_match.group(1).strip() if title_match else " ".join(word.capitalize() for word in slug.split("-"))
+ )
+
+ existing_recipes.append(
+ {
+ "slug": slug,
+ "title": title,
+ "id": f"cookbook/recipes/{slug}",
+ "content": content, # Store content in memory
+ }
+ )
+
+ # Scan existing apps
+ if apps_dir.exists():
+ for md_file in apps_dir.glob("*.md"):
+ slug = md_file.stem
+ # Read file content
+ content = md_file.read_text()
+ # Try to extract title from first heading
+ title_match = re.search(r"^#\s+(.+)$", content, re.MULTILINE)
+ title = (
+ title_match.group(1).strip() if title_match else " ".join(word.capitalize() for word in slug.split("-"))
+ )
+
+ existing_apps.append(
+ {
+ "slug": slug,
+ "title": title,
+ "id": f"cookbook/applications/{slug}",
+ "content": content, # Store content in memory
+ }
+ )
+
+ return existing_recipes, existing_apps
+
+
def main():
"""Main entry point."""
print("Syncing hindsight-cookbook...\n")
@@ -466,11 +684,16 @@ def main():
recipes_dir = docs_dir / "recipes"
apps_dir = docs_dir / "applications"
+ # Extract existing entries before we delete anything
+ print("Scanning for existing manual entries...")
+ existing_recipes, existing_apps = extract_existing_entries(docs_dir)
+ print(f" Found {len(existing_recipes)} existing recipes, {len(existing_apps)} existing apps")
+
# Create temp directory and clone
with tempfile.TemporaryDirectory() as tmpdir:
cookbook_dir = Path(tmpdir) / "cookbook"
- print(f"Cloning {COOKBOOK_REPO}...")
+ print(f"\nCloning {COOKBOOK_REPO}...")
subprocess.run(
["git", "clone", "--depth", "1", COOKBOOK_REPO, str(cookbook_dir)],
capture_output=True,
@@ -494,12 +717,53 @@ def main():
print("\nProcessing applications...")
apps = process_applications(cookbook_dir, apps_dir)
- # Update sidebars.ts and index
- if recipes or apps:
- update_sidebars(recipes, apps, sidebars_file)
- update_cookbook_index(recipes, apps, docs_dir)
+ # Restore manually added entries that aren't in the cookbook repo
+ print("\nRestoring manual entries...")
+ synced_recipe_slugs = {r["slug"] for r in recipes}
+ synced_app_slugs = {a["slug"] for a in apps}
- print(f"\nDone! Generated {len(recipes)} recipes and {len(apps)} applications")
+ manual_recipes = []
+ for entry in existing_recipes:
+ if entry["slug"] not in synced_recipe_slugs:
+ # This was a manual entry - restore it
+ dest_path = recipes_dir / f"{entry['slug']}.md"
+ dest_path.write_text(entry["content"])
+ manual_recipes.append(
+ {
+ "slug": entry["slug"],
+ "title": entry["title"],
+ "id": entry["id"],
+ }
+ )
+ print(f" Restored recipe: {entry['slug']}")
+
+ manual_apps = []
+ for entry in existing_apps:
+ if entry["slug"] not in synced_app_slugs:
+ # This was a manual entry - restore it
+ dest_path = apps_dir / f"{entry['slug']}.md"
+ dest_path.write_text(entry["content"])
+ manual_apps.append(
+ {
+ "slug": entry["slug"],
+ "title": entry["title"],
+ "id": entry["id"],
+ }
+ )
+ print(f" Restored app: {entry['slug']}")
+
+ # Combine synced and manual entries
+ all_recipes = recipes + manual_recipes
+ all_apps = apps + manual_apps
+
+ # Update sidebars.ts and index
+ if all_recipes or all_apps:
+ update_sidebars(all_recipes, all_apps, sidebars_file)
+ update_cookbook_index(all_recipes, all_apps, docs_dir)
+
+ print(
+ f"\nDone! Generated {len(recipes)} recipes ({len(manual_recipes)} manual) and {len(apps)} apps ({len(manual_apps)} manual)"
+ )
if __name__ == "__main__":
diff --git a/hindsight-docs/docs/cookbook/applications/taste-ai.md b/hindsight-docs/docs/cookbook/applications/taste-ai.md
new file mode 100644
index 00000000..f5c9888d
--- /dev/null
+++ b/hindsight-docs/docs/cookbook/applications/taste-ai.md
@@ -0,0 +1,122 @@
+---
+sidebar_position: 8
+---
+
+# 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/hindsight-docs/docs/cookbook/index.mdx b/hindsight-docs/docs/cookbook/index.mdx
index 4d6d367e..cb22bd4d 100644
--- a/hindsight-docs/docs/cookbook/index.mdx
+++ b/hindsight-docs/docs/cookbook/index.mdx
@@ -1,39 +1,147 @@
---
sidebar_position: 1
+hide_table_of_contents: true
+pagination_next: null
+pagination_prev: null
+custom_edit_url: null
+sidebar_class_name: hidden-sidebar
---
import RecipeCarousel from '@site/src/components/RecipeCarousel';
+
+
# Cookbook
-Practical patterns, recipes, and complete applications for building with Hindsight.
+Learn how to build with Hindsight through practical examples:
+
+- **[Recipes](#recipes)** - Step-by-step guides and patterns for common use cases
+- **[Applications](#applications)** - Complete, runnable applications demonstrating Hindsight integration
+
+
diff --git a/hindsight-docs/docs/sdks/integrations/ai-sdk.md b/hindsight-docs/docs/sdks/integrations/ai-sdk.md
new file mode 100644
index 00000000..c538fc24
--- /dev/null
+++ b/hindsight-docs/docs/sdks/integrations/ai-sdk.md
@@ -0,0 +1,366 @@
+---
+sidebar_position: 4
+---
+
+# Vercel AI SDK
+
+Official Hindsight integration for the [Vercel AI SDK](https://ai-sdk.dev).
+
+## Features
+
+- **7 Memory Tools**: Core memory operations (retain, recall, reflect), mental models (create, query), documents (get), and directives (create)
+- **AI SDK 6 Native**: Works seamlessly with `generateText`, `streamText`, and `ToolLoopAgent`
+- **Multi-User Support**: Dynamic bank IDs per tool call for multi-user/multi-tenant scenarios
+- **Full Parameter Support**: Complete access to all Hindsight API parameters
+- **Type-Safe**: Full TypeScript support with Zod schemas for validation
+
+## Installation
+
+```bash
+npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai zod
+```
+
+## Quick Start
+
+### 1. Set up your Hindsight client
+
+```typescript
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+
+const hindsightClient = new HindsightClient({
+ apiUrl: process.env.HINDSIGHT_API_URL || 'http://localhost:8000',
+});
+```
+
+### 2. Create Hindsight tools
+
+```typescript
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+
+const tools = createHindsightTools({
+ client: hindsightClient,
+});
+```
+
+### 3. Use with AI SDK
+
+```typescript
+import { generateText } from 'ai';
+import { anthropic } from '@ai-sdk/anthropic';
+
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ prompt: 'Remember that Alice loves hiking and prefers spicy food',
+});
+
+console.log(result.text);
+```
+
+## Memory Tools
+
+The integration provides seven tools that the AI model can use to manage memory:
+
+### `retain` - Store Information
+
+The model calls this tool to store information for future recall.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID (usually the user ID)
+- `content` (required): Content to store
+- `documentId` (optional): Document ID for grouping/upserting related memories
+- `timestamp` (optional): ISO timestamp for when the memory occurred
+- `context` (optional): Additional context about the memory
+- `metadata` (optional): Key-value metadata for filtering
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ content: "Alice loves hiking and goes to Yosemite every summer",
+ context: "User preferences",
+ timestamp: "2024-01-15T10:30:00Z"
+}
+```
+
+**Returns:**
+```typescript
+{
+ success: true,
+ itemsCount: 1
+}
+```
+
+### `recall` - Search Memories
+
+The model calls this tool to search for relevant information in memory.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `query` (required): What to search for
+- `types` (optional): Filter by fact types (`['world', 'experience', 'opinion']`)
+- `maxTokens` (optional): Maximum tokens to return (default: 4096)
+- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
+- `queryTimestamp` (optional): Query from a specific time (ISO format)
+- `includeEntities` (optional): Include entity observations
+- `includeChunks` (optional): Include raw document chunks
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ query: "What does Alice like to do outdoors?",
+ types: ["world", "experience"],
+ maxTokens: 2048,
+ budget: "mid"
+}
+```
+
+**Returns:**
+```typescript
+{
+ results: [
+ {
+ id: "mem-123",
+ text: "Alice loves hiking",
+ type: "world",
+ entities: ["Alice"],
+ context: "User preferences",
+ occurred_start: "2024-01-15T10:30:00Z",
+ document_id: "doc-456",
+ metadata: { source: "chat" }
+ }
+ ],
+ entities: {
+ "Alice": {
+ canonical_name: "Alice",
+ mention_count: 15,
+ observations: [...]
+ }
+ }
+}
+```
+
+### `reflect` - Synthesize Insights
+
+The model calls this tool to analyze memories and generate contextual insights.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `query` (required): Question to reflect on
+- `context` (optional): Additional context for reflection
+- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ query: "What outdoor activities does Alice enjoy?",
+ context: "Planning a weekend trip",
+ budget: "mid"
+}
+```
+
+**Returns:**
+```typescript
+{
+ text: "Alice is an avid hiker who particularly enjoys visiting Yosemite National Park during summer months. She has expressed strong preferences for mountain trails over beach activities.",
+ basedOn: [
+ {
+ id: "mem-123",
+ text: "Alice loves hiking",
+ type: "world",
+ context: "User preferences",
+ occurred_start: "2024-01-15T10:30:00Z"
+ }
+ ]
+}
+```
+
+### `createMentalModel` - Create Knowledge Consolidation
+
+The model calls this tool to create a mental model that automatically consolidates memories into structured knowledge.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `mentalModelId` (optional): Custom ID for the mental model (auto-generated if not provided)
+- `name` (optional): Name for the mental model
+- `sourceQuery` (optional): Query defining which memories to consolidate
+- `tags` (optional): Tags for organizing mental models
+- `maxTokens` (optional): Maximum tokens for the content
+- `autoRefresh` (optional): Auto-refresh after new consolidations (default: false)
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ name: "User Preferences",
+ sourceQuery: "What are the user's preferences?",
+ tags: ["preferences"],
+ autoRefresh: true
+}
+```
+
+**Returns:**
+```typescript
+{
+ mentalModelId: "mm-456",
+ createdAt: "2024-01-15T10:30:00Z"
+}
+```
+
+### `queryMentalModel` - Retrieve Consolidated Knowledge
+
+The model calls this tool to retrieve synthesized insights from an existing mental model.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `mentalModelId` (required): ID of the mental model to query
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ mentalModelId: "mm-456"
+}
+```
+
+**Returns:**
+```typescript
+{
+ content: "The user prefers outdoor activities, particularly hiking. They enjoy mountain trails and visit Yosemite regularly during summer.",
+ name: "User Preferences",
+ updatedAt: "2024-01-20T15:45:00Z"
+}
+```
+
+### `getDocument` - Retrieve Stored Document
+
+The model calls this tool to retrieve a stored document by its ID.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `documentId` (required): ID of the document to retrieve
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ documentId: "doc-789"
+}
+```
+
+**Returns:**
+```typescript
+{
+ originalText: "User profile: Alice, Software Engineer, loves hiking...",
+ id: "doc-789",
+ createdAt: "2024-01-10T09:00:00Z",
+ updatedAt: "2024-01-15T14:30:00Z"
+}
+```
+
+### `createDirective` - Create Behavioral Rule
+
+The model calls this tool to create a directive—a hard rule injected into prompts during reflect operations.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `name` (required): Human-readable name for the directive
+- `content` (required): The directive text to inject
+- `priority` (optional): Higher priority directives are injected first (default: 0)
+- `isActive` (optional): Whether this directive is active (default: true)
+- `tags` (optional): Tags for filtering (e.g., user-specific directives)
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ name: "Response Format",
+ content: "Always provide responses in bullet-point format",
+ priority: 10,
+ tags: ["formatting"]
+}
+```
+
+**Returns:**
+```typescript
+{
+ id: "dir-321",
+ name: "Response Format",
+ content: "Always provide responses in bullet-point format",
+ tags: ["formatting"],
+ createdAt: "2024-01-15T10:30:00Z"
+}
+```
+
+## Usage Examples
+
+### Using with `generateText`
+
+```typescript
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+import { generateText } from 'ai';
+import { anthropic } from '@ai-sdk/anthropic';
+
+const hindsightClient = new HindsightClient({
+ apiUrl: 'http://localhost:8000',
+});
+
+const tools = createHindsightTools({ client: hindsightClient });
+
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You are a helpful assistant with long-term memory. Use the recall tool to check for relevant memories before responding.`,
+ prompt: 'Remember that Alice loves hiking and prefers spicy food',
+});
+
+console.log(result.text);
+```
+
+### Using with `streamText`
+
+```typescript
+import { streamText } from 'ai';
+
+const result = streamText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You have persistent memory. Use retain to store important information and recall to retrieve it.`,
+ prompt: 'What do you know about Alice?',
+});
+
+for await (const chunk of result.textStream) {
+ process.stdout.write(chunk);
+}
+```
+
+### Using with `ToolLoopAgent`
+
+```typescript
+import { ToolLoopAgent, stopWhen, stepCountIs } from 'ai';
+
+const agent = new ToolLoopAgent({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ instructions: `You are a personal assistant with long-term memory. Always check recall before responding and use retain to store important information.`,
+ stopWhen: stepCountIs(10),
+});
+
+const result = await agent.generate({
+ prompt: 'What did I say I wanted to work on this week?',
+});
+```
+
+### Multi-User Support
+
+```typescript
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You are a helpful assistant. The user's ID is: ${userId}. Always pass this as the bankId parameter to memory tools.`,
+ prompt: 'Remember that I prefer dark mode',
+});
+```
diff --git a/hindsight-docs/sidebars.ts b/hindsight-docs/sidebars.ts
index b6964bbd..acb43598 100644
--- a/hindsight-docs/sidebars.ts
+++ b/hindsight-docs/sidebars.ts
@@ -173,7 +173,7 @@ const sidebars: SidebarsConfig = {
{
type: 'doc',
id: 'sdks/embed',
- label: 'Embedded SDK',
+ label: 'Embedded Python',
},
],
},
@@ -197,6 +197,11 @@ const sidebars: SidebarsConfig = {
id: 'sdks/integrations/openclaw',
label: 'OpenClaw',
},
+ {
+ type: 'doc',
+ id: 'sdks/integrations/ai-sdk',
+ label: 'Vercel AI SDK',
+ },
{
type: 'doc',
id: 'sdks/integrations/skills',
@@ -209,111 +214,7 @@ const sidebars: SidebarsConfig = {
{
type: 'doc',
id: 'cookbook/index',
- label: 'Overview',
- },
- {
- type: 'category',
- label: 'Recipes',
- collapsible: false,
- items: [
- {
- type: 'doc',
- id: 'cookbook/recipes/quickstart',
- label: 'Hindsight Quickstart',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/per-user-memory',
- label: 'Per-User Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/support-agent-shared-knowledge',
- label: 'Support Agent with Shared Knowledge',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/litellm-memory-demo',
- label: 'Memory with LiteLLM',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/tool-learning-demo',
- label: 'Routing Tool Learning',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/fitness_tracker',
- label: 'Fitness Coach with Hindsight Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/healthcare_assistant',
- label: 'Healthcare Assistant with Hindsight Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/movie_recommendation',
- label: 'Movie Recommendation Assistant with Hindsight Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/personal_assistant',
- label: 'Personal AI Assistant with Hindsight Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/personalized_search',
- label: 'Personalized Search Agent with Hindsight Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/recipes/study_buddy',
- label: 'Study Buddy with Hindsight Memory',
- }
- ],
- },
- {
- type: 'category',
- label: 'Applications',
- collapsible: false,
- items: [
- {
- type: 'doc',
- id: 'cookbook/applications/chat-memory',
- label: 'Chat Memory App',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/deliveryman-demo',
- label: 'Deliveryman Demo',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/hindsight-litellm-demo',
- label: 'Memory Approaches Comparison Demo',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/hindsight-tool-learning-demo',
- label: 'Tool Learning Demo',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/openai-fitness-coach',
- label: 'OpenAI Agent + Hindsight Memory Integration',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/sanity-blog-memory',
- label: 'Sanity CMS Blog Memory',
- },
- {
- type: 'doc',
- id: 'cookbook/applications/stancetracker',
- label: 'Stance Tracker',
- }
- ],
+ label: 'Cookbook',
},
],
};
diff --git a/hindsight-docs/src/components/RecipeCarousel.module.css b/hindsight-docs/src/components/RecipeCarousel.module.css
index 59d4f951..24525250 100644
--- a/hindsight-docs/src/components/RecipeCarousel.module.css
+++ b/hindsight-docs/src/components/RecipeCarousel.module.css
@@ -1,61 +1,181 @@
.carouselSection {
- margin: 2rem 0;
+ margin: 3rem 0;
}
.sectionTitle {
- font-size: 1.5rem;
- margin-bottom: 1rem;
+ font-size: 1.75rem;
+ margin-bottom: 1.5rem;
font-weight: 600;
+ color: var(--ifm-font-color-base);
}
.carousel {
- /* Grid layout instead of horizontal scroll */
+ /* Grid layout */
}
.carouselTrack {
display: grid;
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
- gap: 1rem;
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+ gap: 1.25rem;
}
.card {
- padding: 1rem 1.25rem;
- border-radius: 8px;
- border: 1px solid var(--ifm-color-emphasis-300);
- background: var(--ifm-background-surface-color);
+ padding: 1.5rem;
+ border-radius: 12px;
+ border: 2px solid var(--card-border, var(--ifm-color-emphasis-300));
text-decoration: none;
color: inherit;
display: flex;
- align-items: center;
+ flex-direction: column;
justify-content: space-between;
- gap: 0.75rem;
+ gap: 1rem;
transition: all 0.2s ease;
+ position: relative;
+ overflow: hidden;
+ min-height: 200px;
}
-.card:hover {
- text-decoration: none;
- border-color: var(--ifm-color-primary);
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+/* Alternating style: Odd cards = white/solid, Even cards = colored gradient */
+
+/* ODD CARDS - White/Solid background */
+.card:nth-child(odd) {
+ background: #ffffff;
+}
+
+.card:nth-child(odd):hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+}
+
+/* EVEN CARDS - Colored gradients (cycle through 4 colors) */
+.card:nth-child(4n+2) {
+ background: linear-gradient(135deg, rgba(0, 116, 217, 0.08) 0%, rgba(0, 146, 150, 0.08) 100%);
+}
+
+.card:nth-child(4n+4) {
+ background: linear-gradient(135deg, rgba(99, 102, 241, 0.08) 0%, rgba(168, 85, 247, 0.08) 100%);
+}
+
+.card:nth-child(4n+6) {
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(5, 150, 105, 0.08) 100%);
+}
+
+.card:nth-child(4n+8) {
+ background: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(217, 119, 6, 0.08) 100%);
+}
+
+.card:nth-child(even):hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+ transform: translateY(-2px);
+}
+
+/* ============================================
+ DARK MODE
+ ============================================ */
+
+/* ODD CARDS - Dark solid background */
+[data-theme='dark'] .card:nth-child(odd) {
+ background: var(--ifm-background-surface-color);
}
[data-theme='dark'] .card {
- background: var(--ifm-background-color);
- border-color: var(--ifm-color-emphasis-400);
+ border-color: var(--card-border-dark, var(--ifm-color-emphasis-300));
}
-[data-theme='dark'] .card:hover {
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
- border-color: var(--ifm-color-primary);
+[data-theme='dark'] .card:nth-child(odd):hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
+}
+
+/* EVEN CARDS - Colored gradients (more vibrant in dark mode) */
+[data-theme='dark'] .card:nth-child(4n+2) {
+ background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(20, 184, 166, 0.15) 100%);
+}
+
+[data-theme='dark'] .card:nth-child(4n+4) {
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(217, 70, 239, 0.15) 100%);
+}
+
+[data-theme='dark'] .card:nth-child(4n+6) {
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(132, 204, 22, 0.15) 100%);
+}
+
+[data-theme='dark'] .card:nth-child(4n+8) {
+ background: linear-gradient(135deg, rgba(251, 146, 60, 0.15) 0%, rgba(239, 68, 68, 0.15) 100%);
+}
+
+[data-theme='dark'] .card:nth-child(even):hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.8);
+}
+
+.cardContent {
+ display: flex;
+ flex-direction: column;
+ gap: 0.65rem;
+ flex-grow: 1;
+}
+
+.cardFooter {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: auto;
}
.cardTitle {
- font-size: 0.95rem;
- font-weight: 500;
+ font-size: 1.05rem;
+ font-weight: 600;
color: var(--ifm-font-color-base);
+ line-height: 1.4;
+}
+
+.cardDescription {
+ font-size: 0.9rem;
+ color: var(--ifm-color-emphasis-800);
+ margin: 0;
+ line-height: 1.6;
+}
+
+[data-theme='dark'] .cardDescription {
+ color: var(--ifm-color-emphasis-700);
+}
+
+.cardTags {
+ display: flex;
+ flex-wrap: nowrap;
+ gap: 0.75rem;
+ align-items: center;
+ flex: 1;
+}
+
+.tag {
+ font-size: 0.72rem;
+ padding: 0.35rem 0.75rem;
+ border-radius: 6px;
+ background: var(--tag-bg);
+ color: var(--tag-text);
+ font-weight: 600;
+ border: 1px solid var(--tag-border);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+[data-theme='dark'] .tag {
+ background: var(--tag-bg-dark);
+ color: var(--tag-text-dark);
+ border-color: var(--tag-border-dark);
}
.cardLink {
color: var(--ifm-color-primary);
- font-weight: 500;
+ font-weight: 600;
flex-shrink: 0;
+ font-size: 1.25rem;
+ line-height: 1;
+ opacity: 0.7;
+ transition: opacity 0.2s ease;
+ margin-left: 1rem;
+}
+
+.card:hover .cardLink {
+ opacity: 1;
}
diff --git a/hindsight-docs/src/components/RecipeCarousel.tsx b/hindsight-docs/src/components/RecipeCarousel.tsx
index f31161cf..59ab7307 100644
--- a/hindsight-docs/src/components/RecipeCarousel.tsx
+++ b/hindsight-docs/src/components/RecipeCarousel.tsx
@@ -5,6 +5,11 @@ import styles from './RecipeCarousel.module.css';
export interface RecipeCard {
title: string;
href: string;
+ tags?: {
+ sdk?: string; // Package name: "hindsight-python", "hindsight-nodejs", "litellm-python", "ai-sdk", etc.
+ topic?: string; // "Learning", "Quick Start", "Recommendation", "Chat"
+ };
+ description?: string;
}
interface RecipeCarouselProps {
@@ -12,18 +17,148 @@ interface RecipeCarouselProps {
items: RecipeCard[];
}
+// Language icons using inline SVG data URIs
+const LANGUAGE_ICONS: Record = {
+ Python: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%233776ab' d='M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z'/%3E%3C/svg%3E",
+ 'Node.js': "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23339933' d='M11.998 0c-.27 0-.54.07-.772.202L2.428 5.05C1.983 5.321 1.7 5.802 1.7 6.32v11.36c0 .518.283 1 .728 1.27l2.375 1.371c.64.321 1.094.32 1.468.32 1.203 0 1.89-.73 1.89-1.996V7.362c0-.146-.117-.264-.262-.264H7.11c-.146 0-.263.118-.263.264v11.283c0 .876-.906 1.753-2.38 1.01L2.103 18.28c-.046-.026-.073-.08-.073-.132V6.754c0-.051.027-.106.073-.132l8.798-5.08c.044-.026.102-.026.145 0l8.798 5.08c.046.026.074.081.074.132v11.394c0 .051-.028.106-.074.132l-8.798 5.08c-.043.026-.101.026-.144 0l-2.248-1.336c-.064-.037-.144-.04-.21-.011-.55.307-.658.373-1.177.45-.12.019-.301.06.073.276l2.93 1.738c.23.133.49.202.772.202s.542-.069.772-.202l8.798-5.08c.476-.27.772-.772.772-1.27V6.32c0-.518-.296-.999-.772-1.27L12.77.202C12.538.07 12.268 0 11.998 0zm2.657 6.343c-2.432 0-2.945.953-2.945 2.146 0 .145.117.263.263.263h.788c.131 0 .24-.095.261-.221.177-.718.708-1.08 1.633-1.08.738 0 1.177.168 1.177.803 0 .325-.128.567-.678.73l-1.69.419c-.899.223-1.47.756-1.47 1.636 0 1.076.905 1.715 2.423 1.715 1.704 0 2.55-.593 2.656-1.866.006-.073-.018-.144-.066-.197-.047-.053-.114-.083-.186-.083h-.791c-.123 0-.23.089-.258.207-.286.644-.98.849-1.817.849-.65 0-1.16-.207-1.16-.725 0-.325.144-.424.903-.609l1.476-.367c.898-.223 1.462-.72 1.462-1.613 0-1.12-.937-1.787-2.574-1.787z'/%3E%3C/svg%3E",
+ TypeScript: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%233178c6' d='M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111.472.074.914.187 1.323.34v2.458c-.444-.223-.935-.39-1.473-.501-.539-.111-1.09-.167-1.655-.167-.562 0-1.011.062-1.349.187-.338.124-.507.335-.507.632 0 .234.095.42.285.558.19.138.503.275.94.411l1.503.434c.915.262 1.577.609 1.984 1.04.408.432.612.998.612 1.699 0 .915-.35 1.638-1.05 2.168-.7.53-1.667.795-2.9.795-.591 0-1.178-.051-1.76-.153-.582-.102-1.13-.258-1.645-.468v-2.503c.544.287 1.09.507 1.637.66.546.153 1.084.23 1.613.23.609 0 1.071-.073 1.386-.219.315-.146.472-.369.472-.669 0-.262-.106-.471-.318-.628-.212-.157-.551-.306-1.017-.447l-1.42-.395c-.877-.234-1.515-.563-1.916-.985-.4-.422-.6-.98-.6-1.673 0-.857.348-1.545 1.044-2.063.696-.518 1.633-.777 2.811-.777zm-13.6 1.77H8.45l-.031 4.18c0 .754-.13 1.314-.39 1.68-.26.367-.65.55-1.168.55-.286 0-.56-.037-.822-.11-.262-.074-.506-.173-.733-.297v1.818c.319.111.665.187 1.038.228.373.04.736.06 1.089.06.924 0 1.623-.247 2.097-.74.474-.494.711-1.254.711-2.28V11.52z'/%3E%3C/svg%3E",
+};
+
+// Get language icon based on package name
+function getPackageIcon(packageName: string): string | undefined {
+ // If it starts with @vectorize-io, it's Node.js
+ if (packageName.startsWith('@vectorize-io')) {
+ return LANGUAGE_ICONS['Node.js'];
+ }
+ // Otherwise assume Python
+ return LANGUAGE_ICONS.Python;
+}
+
+// Generate color scheme from tag text using hash
+function getTagColor(tag: string): any {
+ // Hash function to get consistent color from string
+ let hash = 0;
+ for (let i = 0; i < tag.length; i++) {
+ hash = tag.charCodeAt(i) + ((hash << 5) - hash);
+ }
+
+ // 12 vibrant color palettes with better contrast
+ const palettes = [
+ { h: 340, s: 75, l: 50 }, // Pink
+ { h: 291, s: 65, l: 45 }, // Purple
+ { h: 262, s: 55, l: 48 }, // Deep Purple
+ { h: 231, s: 50, l: 50 }, // Indigo
+ { h: 207, s: 80, l: 50 }, // Blue
+ { h: 199, s: 85, l: 45 }, // Light Blue
+ { h: 187, s: 70, l: 45 }, // Cyan
+ { h: 174, s: 70, l: 50 }, // Teal
+ { h: 142, s: 65, l: 45 }, // Green
+ { h: 88, s: 55, l: 48 }, // Light Green
+ { h: 38, s: 85, l: 50 }, // Orange
+ { h: 14, s: 85, l: 50 }, // Deep Orange
+ ];
+
+ const palette = palettes[Math.abs(hash) % palettes.length];
+ const { h, s, l } = palette;
+
+ return {
+ // Light mode: subtle background, darker text for contrast
+ bg: `hsla(${h}, ${s}%, ${l}%, 0.15)`,
+ text: `hsl(${h}, ${Math.min(s + 10, 90)}%, ${Math.max(l - 25, 25)}%)`,
+ border: `hsla(${h}, ${s}%, ${l}%, 0.35)`,
+ // Dark mode: more vibrant background, lighter text
+ bgDark: `hsla(${h}, ${Math.max(s - 10, 50)}%, ${l}%, 0.25)`,
+ textDark: `hsl(${h}, ${Math.max(s - 15, 40)}%, ${Math.min(l + 35, 85)}%)`,
+ borderDark: `hsla(${h}, ${s}%, ${l}%, 0.4)`,
+ };
+}
+
export default function RecipeCarousel({ title, items }: RecipeCarouselProps): React.ReactElement {
+ // Generate ID from title for anchor links
+ const sectionId = title.toLowerCase().replace(/\s+/g, '-');
+
return (
-
+ );
+}
diff --git a/hindsight-docs/src/theme/DocPage/Layout/Sidebar/index.tsx b/hindsight-docs/src/theme/DocPage/Layout/Sidebar/index.tsx
new file mode 100644
index 00000000..688797aa
--- /dev/null
+++ b/hindsight-docs/src/theme/DocPage/Layout/Sidebar/index.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import Sidebar from '@theme-original/DocPage/Layout/Sidebar';
+import type SidebarType from '@theme/DocPage/Layout/Sidebar';
+import type {WrapperProps} from '@docusaurus/types';
+import {useLocation} from '@docusaurus/router';
+
+type Props = WrapperProps;
+
+export default function SidebarWrapper(props: Props): JSX.Element | null {
+ const location = useLocation();
+ const isCookbook = location.pathname.includes('/cookbook');
+
+ // Don't render sidebar for cookbook pages
+ if (isCookbook) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/hindsight-docs/static/img/icons/README.md b/hindsight-docs/static/img/icons/README.md
new file mode 100644
index 00000000..96ddc0bb
--- /dev/null
+++ b/hindsight-docs/static/img/icons/README.md
@@ -0,0 +1,47 @@
+# Sidebar Icons
+
+This directory contains SVG icons for the sidebar navigation items.
+
+## Required Icons
+
+### Client Icons
+- `python.svg` - Python logo (download from https://www.python.org/community/logos/)
+- `nodejs.svg` - Node.js logo (download from https://nodejs.org/en/about/branding)
+- `terminal.svg` - Terminal/CLI icon
+- `package.svg` - Package/box icon for Embedded SDK
+
+### Integration Icons
+- `mcp.svg` - MCP Server icon
+- `litellm.svg` - LiteLLM logo (download from https://github.com/BerriAI/litellm)
+- `openclaw.svg` - OpenClaw logo
+- `vercel.svg` - Vercel triangle logo (download from https://vercel.com/design/brands)
+- `skills.svg` - Skills/star icon
+
+## Specifications
+
+- **Format**: SVG (preferred) or PNG
+- **Size**: 16x16px or larger (will be scaled to 14x14px)
+- **Style**: Monochrome or simple colors work best
+- **Color**: Icons should work on both light and dark backgrounds
+
+## Alternative: Using Remote URLs
+
+Instead of local files, you can use remote URLs directly in the CSS:
+
+```css
+a.menu__link[href*="/sdks/python"]::before {
+ background-image: url('https://cdn.jsdelivr.net/npm/simple-icons@v10/icons/python.svg');
+}
+```
+
+Popular icon CDNs:
+- Simple Icons: https://simpleicons.org/
+- cdnjs: https://cdnjs.com/
+- jsDelivr: https://www.jsdelivr.com/
+
+## Creating Icons
+
+If you need to create custom icons, use tools like:
+- Figma (https://figma.com)
+- Inkscape (https://inkscape.org)
+- SVGOMG for optimization (https://jakearchibald.github.io/svgomg/)
diff --git a/hindsight-docs/static/img/icons/litellm.png b/hindsight-docs/static/img/icons/litellm.png
new file mode 100644
index 00000000..a99b3e97
Binary files /dev/null and b/hindsight-docs/static/img/icons/litellm.png differ
diff --git a/hindsight-docs/static/img/icons/mcp.png b/hindsight-docs/static/img/icons/mcp.png
new file mode 100644
index 00000000..d920e50e
Binary files /dev/null and b/hindsight-docs/static/img/icons/mcp.png differ
diff --git a/hindsight-docs/static/img/icons/nodejs.svg b/hindsight-docs/static/img/icons/nodejs.svg
new file mode 100644
index 00000000..41d044ac
--- /dev/null
+++ b/hindsight-docs/static/img/icons/nodejs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/hindsight-docs/static/img/icons/openclaw.png b/hindsight-docs/static/img/icons/openclaw.png
new file mode 100644
index 00000000..da995cd4
Binary files /dev/null and b/hindsight-docs/static/img/icons/openclaw.png differ
diff --git a/hindsight-docs/static/img/icons/package.svg b/hindsight-docs/static/img/icons/package.svg
new file mode 100644
index 00000000..05155f37
--- /dev/null
+++ b/hindsight-docs/static/img/icons/package.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/hindsight-docs/static/img/icons/python.svg b/hindsight-docs/static/img/icons/python.svg
new file mode 100644
index 00000000..05155f37
--- /dev/null
+++ b/hindsight-docs/static/img/icons/python.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/hindsight-docs/static/img/icons/skills.png b/hindsight-docs/static/img/icons/skills.png
new file mode 100644
index 00000000..59c53037
Binary files /dev/null and b/hindsight-docs/static/img/icons/skills.png differ
diff --git a/hindsight-docs/static/img/icons/terminal.svg b/hindsight-docs/static/img/icons/terminal.svg
new file mode 100644
index 00000000..905b181d
--- /dev/null
+++ b/hindsight-docs/static/img/icons/terminal.svg
@@ -0,0 +1,44 @@
+
+
diff --git a/hindsight-docs/static/img/icons/vercel.png b/hindsight-docs/static/img/icons/vercel.png
new file mode 100644
index 00000000..84361215
Binary files /dev/null and b/hindsight-docs/static/img/icons/vercel.png differ
diff --git a/hindsight-docs/versioned_docs/version-0.4/cookbook/applications/taste-ai.md b/hindsight-docs/versioned_docs/version-0.4/cookbook/applications/taste-ai.md
new file mode 100644
index 00000000..f5c9888d
--- /dev/null
+++ b/hindsight-docs/versioned_docs/version-0.4/cookbook/applications/taste-ai.md
@@ -0,0 +1,122 @@
+---
+sidebar_position: 8
+---
+
+# 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/hindsight-docs/versioned_docs/version-0.4/cookbook/index.mdx b/hindsight-docs/versioned_docs/version-0.4/cookbook/index.mdx
index 4d6d367e..cb22bd4d 100644
--- a/hindsight-docs/versioned_docs/version-0.4/cookbook/index.mdx
+++ b/hindsight-docs/versioned_docs/version-0.4/cookbook/index.mdx
@@ -1,39 +1,147 @@
---
sidebar_position: 1
+hide_table_of_contents: true
+pagination_next: null
+pagination_prev: null
+custom_edit_url: null
+sidebar_class_name: hidden-sidebar
---
import RecipeCarousel from '@site/src/components/RecipeCarousel';
+
+
# Cookbook
-Practical patterns, recipes, and complete applications for building with Hindsight.
+Learn how to build with Hindsight through practical examples:
+
+- **[Recipes](#recipes)** - Step-by-step guides and patterns for common use cases
+- **[Applications](#applications)** - Complete, runnable applications demonstrating Hindsight integration
+
+
diff --git a/hindsight-docs/versioned_docs/version-0.4/sdks/integrations/ai-sdk.md b/hindsight-docs/versioned_docs/version-0.4/sdks/integrations/ai-sdk.md
new file mode 100644
index 00000000..c538fc24
--- /dev/null
+++ b/hindsight-docs/versioned_docs/version-0.4/sdks/integrations/ai-sdk.md
@@ -0,0 +1,366 @@
+---
+sidebar_position: 4
+---
+
+# Vercel AI SDK
+
+Official Hindsight integration for the [Vercel AI SDK](https://ai-sdk.dev).
+
+## Features
+
+- **7 Memory Tools**: Core memory operations (retain, recall, reflect), mental models (create, query), documents (get), and directives (create)
+- **AI SDK 6 Native**: Works seamlessly with `generateText`, `streamText`, and `ToolLoopAgent`
+- **Multi-User Support**: Dynamic bank IDs per tool call for multi-user/multi-tenant scenarios
+- **Full Parameter Support**: Complete access to all Hindsight API parameters
+- **Type-Safe**: Full TypeScript support with Zod schemas for validation
+
+## Installation
+
+```bash
+npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai zod
+```
+
+## Quick Start
+
+### 1. Set up your Hindsight client
+
+```typescript
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+
+const hindsightClient = new HindsightClient({
+ apiUrl: process.env.HINDSIGHT_API_URL || 'http://localhost:8000',
+});
+```
+
+### 2. Create Hindsight tools
+
+```typescript
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+
+const tools = createHindsightTools({
+ client: hindsightClient,
+});
+```
+
+### 3. Use with AI SDK
+
+```typescript
+import { generateText } from 'ai';
+import { anthropic } from '@ai-sdk/anthropic';
+
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ prompt: 'Remember that Alice loves hiking and prefers spicy food',
+});
+
+console.log(result.text);
+```
+
+## Memory Tools
+
+The integration provides seven tools that the AI model can use to manage memory:
+
+### `retain` - Store Information
+
+The model calls this tool to store information for future recall.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID (usually the user ID)
+- `content` (required): Content to store
+- `documentId` (optional): Document ID for grouping/upserting related memories
+- `timestamp` (optional): ISO timestamp for when the memory occurred
+- `context` (optional): Additional context about the memory
+- `metadata` (optional): Key-value metadata for filtering
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ content: "Alice loves hiking and goes to Yosemite every summer",
+ context: "User preferences",
+ timestamp: "2024-01-15T10:30:00Z"
+}
+```
+
+**Returns:**
+```typescript
+{
+ success: true,
+ itemsCount: 1
+}
+```
+
+### `recall` - Search Memories
+
+The model calls this tool to search for relevant information in memory.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `query` (required): What to search for
+- `types` (optional): Filter by fact types (`['world', 'experience', 'opinion']`)
+- `maxTokens` (optional): Maximum tokens to return (default: 4096)
+- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
+- `queryTimestamp` (optional): Query from a specific time (ISO format)
+- `includeEntities` (optional): Include entity observations
+- `includeChunks` (optional): Include raw document chunks
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ query: "What does Alice like to do outdoors?",
+ types: ["world", "experience"],
+ maxTokens: 2048,
+ budget: "mid"
+}
+```
+
+**Returns:**
+```typescript
+{
+ results: [
+ {
+ id: "mem-123",
+ text: "Alice loves hiking",
+ type: "world",
+ entities: ["Alice"],
+ context: "User preferences",
+ occurred_start: "2024-01-15T10:30:00Z",
+ document_id: "doc-456",
+ metadata: { source: "chat" }
+ }
+ ],
+ entities: {
+ "Alice": {
+ canonical_name: "Alice",
+ mention_count: 15,
+ observations: [...]
+ }
+ }
+}
+```
+
+### `reflect` - Synthesize Insights
+
+The model calls this tool to analyze memories and generate contextual insights.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `query` (required): Question to reflect on
+- `context` (optional): Additional context for reflection
+- `budget` (optional): Processing budget - `'low'`, `'mid'`, or `'high'`
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ query: "What outdoor activities does Alice enjoy?",
+ context: "Planning a weekend trip",
+ budget: "mid"
+}
+```
+
+**Returns:**
+```typescript
+{
+ text: "Alice is an avid hiker who particularly enjoys visiting Yosemite National Park during summer months. She has expressed strong preferences for mountain trails over beach activities.",
+ basedOn: [
+ {
+ id: "mem-123",
+ text: "Alice loves hiking",
+ type: "world",
+ context: "User preferences",
+ occurred_start: "2024-01-15T10:30:00Z"
+ }
+ ]
+}
+```
+
+### `createMentalModel` - Create Knowledge Consolidation
+
+The model calls this tool to create a mental model that automatically consolidates memories into structured knowledge.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `mentalModelId` (optional): Custom ID for the mental model (auto-generated if not provided)
+- `name` (optional): Name for the mental model
+- `sourceQuery` (optional): Query defining which memories to consolidate
+- `tags` (optional): Tags for organizing mental models
+- `maxTokens` (optional): Maximum tokens for the content
+- `autoRefresh` (optional): Auto-refresh after new consolidations (default: false)
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ name: "User Preferences",
+ sourceQuery: "What are the user's preferences?",
+ tags: ["preferences"],
+ autoRefresh: true
+}
+```
+
+**Returns:**
+```typescript
+{
+ mentalModelId: "mm-456",
+ createdAt: "2024-01-15T10:30:00Z"
+}
+```
+
+### `queryMentalModel` - Retrieve Consolidated Knowledge
+
+The model calls this tool to retrieve synthesized insights from an existing mental model.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `mentalModelId` (required): ID of the mental model to query
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ mentalModelId: "mm-456"
+}
+```
+
+**Returns:**
+```typescript
+{
+ content: "The user prefers outdoor activities, particularly hiking. They enjoy mountain trails and visit Yosemite regularly during summer.",
+ name: "User Preferences",
+ updatedAt: "2024-01-20T15:45:00Z"
+}
+```
+
+### `getDocument` - Retrieve Stored Document
+
+The model calls this tool to retrieve a stored document by its ID.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `documentId` (required): ID of the document to retrieve
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ documentId: "doc-789"
+}
+```
+
+**Returns:**
+```typescript
+{
+ originalText: "User profile: Alice, Software Engineer, loves hiking...",
+ id: "doc-789",
+ createdAt: "2024-01-10T09:00:00Z",
+ updatedAt: "2024-01-15T14:30:00Z"
+}
+```
+
+### `createDirective` - Create Behavioral Rule
+
+The model calls this tool to create a directive—a hard rule injected into prompts during reflect operations.
+
+**Parameters:**
+- `bankId` (required): Memory bank ID
+- `name` (required): Human-readable name for the directive
+- `content` (required): The directive text to inject
+- `priority` (optional): Higher priority directives are injected first (default: 0)
+- `isActive` (optional): Whether this directive is active (default: true)
+- `tags` (optional): Tags for filtering (e.g., user-specific directives)
+
+**Example tool call:**
+```typescript
+{
+ bankId: "user-123",
+ name: "Response Format",
+ content: "Always provide responses in bullet-point format",
+ priority: 10,
+ tags: ["formatting"]
+}
+```
+
+**Returns:**
+```typescript
+{
+ id: "dir-321",
+ name: "Response Format",
+ content: "Always provide responses in bullet-point format",
+ tags: ["formatting"],
+ createdAt: "2024-01-15T10:30:00Z"
+}
+```
+
+## Usage Examples
+
+### Using with `generateText`
+
+```typescript
+import { HindsightClient } from '@vectorize-io/hindsight-client';
+import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
+import { generateText } from 'ai';
+import { anthropic } from '@ai-sdk/anthropic';
+
+const hindsightClient = new HindsightClient({
+ apiUrl: 'http://localhost:8000',
+});
+
+const tools = createHindsightTools({ client: hindsightClient });
+
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You are a helpful assistant with long-term memory. Use the recall tool to check for relevant memories before responding.`,
+ prompt: 'Remember that Alice loves hiking and prefers spicy food',
+});
+
+console.log(result.text);
+```
+
+### Using with `streamText`
+
+```typescript
+import { streamText } from 'ai';
+
+const result = streamText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You have persistent memory. Use retain to store important information and recall to retrieve it.`,
+ prompt: 'What do you know about Alice?',
+});
+
+for await (const chunk of result.textStream) {
+ process.stdout.write(chunk);
+}
+```
+
+### Using with `ToolLoopAgent`
+
+```typescript
+import { ToolLoopAgent, stopWhen, stepCountIs } from 'ai';
+
+const agent = new ToolLoopAgent({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ instructions: `You are a personal assistant with long-term memory. Always check recall before responding and use retain to store important information.`,
+ stopWhen: stepCountIs(10),
+});
+
+const result = await agent.generate({
+ prompt: 'What did I say I wanted to work on this week?',
+});
+```
+
+### Multi-User Support
+
+```typescript
+const result = await generateText({
+ model: anthropic('claude-sonnet-4-20250514'),
+ tools,
+ system: `You are a helpful assistant. The user's ID is: ${userId}. Always pass this as the bankId parameter to memory tools.`,
+ prompt: 'Remember that I prefer dark mode',
+});
+```
diff --git a/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json b/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json
index eb6fea7f..99838774 100644
--- a/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json
+++ b/hindsight-docs/versioned_sidebars/version-0.4-sidebars.json
@@ -171,7 +171,7 @@
{
"type": "doc",
"id": "sdks/embed",
- "label": "Embedded SDK"
+ "label": "Embedded Python"
}
]
},
@@ -195,6 +195,11 @@
"id": "sdks/integrations/openclaw",
"label": "OpenClaw"
},
+ {
+ "type": "doc",
+ "id": "sdks/integrations/ai-sdk",
+ "label": "Vercel AI SDK"
+ },
{
"type": "doc",
"id": "sdks/integrations/skills",
@@ -207,111 +212,7 @@
{
"type": "doc",
"id": "cookbook/index",
- "label": "Overview"
- },
- {
- "type": "category",
- "label": "Recipes",
- "collapsible": false,
- "items": [
- {
- "type": "doc",
- "id": "cookbook/recipes/quickstart",
- "label": "Hindsight Quickstart"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/per-user-memory",
- "label": "Per-User Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/support-agent-shared-knowledge",
- "label": "Support Agent with Shared Knowledge"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/litellm-memory-demo",
- "label": "Memory with LiteLLM"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/tool-learning-demo",
- "label": "Routing Tool Learning"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/fitness_tracker",
- "label": "Fitness Coach with Hindsight Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/healthcare_assistant",
- "label": "Healthcare Assistant with Hindsight Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/movie_recommendation",
- "label": "Movie Recommendation Assistant with Hindsight Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/personal_assistant",
- "label": "Personal AI Assistant with Hindsight Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/personalized_search",
- "label": "Personalized Search Agent with Hindsight Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/recipes/study_buddy",
- "label": "Study Buddy with Hindsight Memory"
- }
- ]
- },
- {
- "type": "category",
- "label": "Applications",
- "collapsible": false,
- "items": [
- {
- "type": "doc",
- "id": "cookbook/applications/chat-memory",
- "label": "Chat Memory App"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/deliveryman-demo",
- "label": "Deliveryman Demo"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/hindsight-litellm-demo",
- "label": "Memory Approaches Comparison Demo"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/hindsight-tool-learning-demo",
- "label": "Tool Learning Demo"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/openai-fitness-coach",
- "label": "OpenAI Agent + Hindsight Memory Integration"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/sanity-blog-memory",
- "label": "Sanity CMS Blog Memory"
- },
- {
- "type": "doc",
- "id": "cookbook/applications/stancetracker",
- "label": "Stance Tracker"
- }
- ]
+ "label": "Cookbook"
}
]
}
diff --git a/hindsight-integrations/ai-sdk/README.md b/hindsight-integrations/ai-sdk/README.md
index 854b5598..32f9cec8 100644
--- a/hindsight-integrations/ai-sdk/README.md
+++ b/hindsight-integrations/ai-sdk/README.md
@@ -2,313 +2,61 @@
Give your AI agents persistent, human-like memory using [Hindsight](https://vectorize.io/hindsight) with the [Vercel AI SDK](https://ai-sdk.dev).
-## Features
-
-- **Three Memory Operations**: `retain` (store), `recall` (retrieve), and `reflect` (reason over memories)
-- **Multi-User Support**: Dynamic bank IDs per call for multi-user/multi-tenant scenarios
-- **Full API Coverage**: Complete parameter support for all Hindsight operations
-- **Type-Safe**: Full TypeScript support with Zod schemas for validation
-- **AI SDK 6 Native**: Works seamlessly with `generateText`, `streamText`, and `ToolLoopAgent`
-
-## Installation
-
-```bash
-npm install @vectorize-io/hindsight-ai-sdk ai zod
-```
-
-You'll also need a Hindsight client. Choose one:
-
-**Option A: TypeScript/JavaScript Client**
-```bash
-npm install @vectorize-io/hindsight-client
-```
-
-**Option B: Direct HTTP Client** (no additional dependencies)
-```typescript
-// See "HTTP Client Example" below
-```
-
## Quick Start
-### 1. Set up your Hindsight client
+```bash
+npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai zod
+```
```typescript
import { HindsightClient } from '@vectorize-io/hindsight-client';
-
-const hindsightClient = new HindsightClient({
- apiUrl: process.env.HINDSIGHT_API_URL || 'http://localhost:8000',
-});
-```
-
-### 2. Create Hindsight tools
-
-```typescript
import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
-
-const tools = createHindsightTools({
- client: hindsightClient,
-});
-```
-
-### 3. Use with AI SDK
-
-```typescript
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
+// 1. Initialize Hindsight client
+const hindsightClient = new HindsightClient({
+ apiUrl: 'http://localhost:8000',
+});
+
+// 2. Create memory tools
+const tools = createHindsightTools({ client: hindsightClient });
+
+// 3. Use with AI SDK
const result = await generateText({
model: anthropic('claude-sonnet-4-20250514'),
tools,
+ system: `You have long-term memory. Use:
+ - 'recall' to search past conversations
+ - 'retain' to remember important information
+ - 'reflect' to synthesize insights from memories`,
prompt: 'Remember that Alice loves hiking and prefers spicy food',
});
console.log(result.text);
```
-## Full Example: Memory-Enabled Chatbot
+## Features
-```typescript
-import { HindsightClient } from '@vectorize-io/hindsight-client';
-import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
-import { streamText } from 'ai';
-import { anthropic } from '@ai-sdk/anthropic';
+✅ **Three Memory Tools**: `retain` (store), `recall` (retrieve), and `reflect` (reason over memories)
+✅ **AI SDK 6 Native**: Works with `generateText`, `streamText`, and `ToolLoopAgent`
+✅ **Multi-User Support**: Dynamic bank IDs per call for multi-user scenarios
+✅ **Type-Safe**: Full TypeScript support with Zod schemas
+✅ **Flexible Client**: Works with the official TypeScript client or custom HTTP clients
-// Initialize Hindsight
-const hindsightClient = new HindsightClient({
- apiUrl: 'http://localhost:8000',
-});
+## Documentation
-const tools = createHindsightTools({ client: hindsightClient });
+📖 **[Full Documentation](https://vectorize.io/hindsight/sdks/integrations/ai-sdk)**
-// Chat with memory
-const result = await streamText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- system: `You are a helpful assistant with long-term memory.
-
-IMPORTANT:
-- Before answering questions, use the 'recall' tool to check for relevant memories
-- When users share important information, use the 'retain' tool to remember it
-- For complex questions requiring synthesis, use the 'reflect' tool
-- Always pass the user's ID as the bankId parameter
-
-Your memory persists across sessions!`,
- prompt: 'Remember that I am Alice and I love hiking',
-});
-
-for await (const chunk of result.textStream) {
- process.stdout.write(chunk);
-}
-```
-
-## API Reference
-
-### `createHindsightTools(options)`
-
-Creates AI SDK tool definitions for Hindsight memory operations.
-
-**Parameters:**
-
-- `options.client`: `HindsightClient` - Hindsight client instance
-- `options.retainDescription`: `string` (optional) - Custom description for the retain tool
-- `options.recallDescription`: `string` (optional) - Custom description for the recall tool
-- `options.reflectDescription`: `string` (optional) - Custom description for the reflect tool
-
-**Returns:** Object with three tools: `retain`, `recall`, and `reflect`
-
-### Tool: `retain`
-
-Store information in long-term memory.
-
-**Parameters:**
-- `bankId`: `string` - Memory bank ID (usually the user ID)
-- `content`: `string` - Content to store
-- `documentId`: `string` (optional) - Document ID for grouping/upserting
-- `timestamp`: `string` (optional) - ISO timestamp for when the memory occurred
-- `context`: `string` (optional) - Additional context about the memory
-
-**Returns:**
-```typescript
-{
- success: boolean;
- itemsCount: number;
-}
-```
-
-### Tool: `recall`
-
-Search memory for relevant information.
-
-**Parameters:**
-- `bankId`: `string` - Memory bank ID
-- `query`: `string` - What to search for
-- `types`: `string[]` (optional) - Filter by fact types
-- `maxTokens`: `number` (optional) - Maximum tokens to return
-- `budget`: `'low' | 'mid' | 'high'` (optional) - Processing budget
-- `queryTimestamp`: `string` (optional) - Query from a specific time (ISO format)
-- `includeEntities`: `boolean` (optional) - Include entity observations
-- `includeChunks`: `boolean` (optional) - Include raw chunks
-
-**Returns:**
-```typescript
-{
- results: Array<{
- id: string;
- text: string;
- type?: string;
- entities?: string[];
- context?: string;
- occurred_start?: string;
- occurred_end?: string;
- mentioned_at?: string;
- document_id?: string;
- metadata?: Record;
- chunk_id?: string;
- }>;
- entities?: Record;
-}
-```
-
-### Tool: `reflect`
-
-Analyze memories to form insights and generate contextual answers.
-
-**Parameters:**
-- `bankId`: `string` - Memory bank ID
-- `query`: `string` - Question to reflect on
-- `context`: `string` (optional) - Additional context for reflection
-- `budget`: `'low' | 'mid' | 'high'` (optional) - Processing budget
-
-**Returns:**
-```typescript
-{
- text: string;
- basedOn?: Array<{
- id?: string;
- text: string;
- type?: string;
- context?: string;
- occurred_start?: string;
- occurred_end?: string;
- }>;
-}
-```
-
-## Advanced Usage
-
-### Custom Tool Descriptions
-
-Customize tool descriptions to guide model behavior:
-
-```typescript
-const tools = createHindsightTools({
- client: hindsightClient,
- retainDescription: 'Store user preferences and important facts. Always include context.',
- recallDescription: 'Search past conversations. Use specific queries for best results.',
- reflectDescription: 'Synthesize insights from memories. Use for complex questions.',
-});
-```
-
-### Multi-User Scenarios
-
-Each tool call accepts a `bankId` parameter, making it easy to support multiple users:
-
-```typescript
-const result = await generateText({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- prompt: `User ID: ${userId}\n\nRemember that I prefer dark mode`,
-});
-```
-
-The model will automatically pass the user ID to the tools.
-
-### Using with ToolLoopAgent
-
-```typescript
-import { ToolLoopAgent, stopWhen, stepCountIs } from 'ai';
-
-const agent = new ToolLoopAgent({
- model: anthropic('claude-sonnet-4-20250514'),
- tools,
- instructions: `You are a personal assistant with long-term memory.
-
- Always check memory before responding using the recall tool.
- Store important user preferences with the retain tool.
- Use the reflect tool to analyze patterns in the user's behavior.`,
- stopWhen: stepCountIs(10),
-});
-
-const result = await agent.generate({
- prompt: 'What did I say I wanted to work on this week?',
-});
-```
-
-## HTTP Client Example
-
-If you prefer not to install the full Hindsight client, you can use a simple HTTP client:
-
-```typescript
-import type { HindsightClient } from '@vectorize-io/hindsight-ai-sdk';
-
-const httpClient: HindsightClient = {
- async retain(bankId, content, options = {}) {
- const response = await fetch(`${HINDSIGHT_URL}/v1/default/banks/${bankId}/memories/retain`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- content,
- timestamp: options.timestamp,
- context: options.context,
- metadata: options.metadata,
- document_id: options.documentId,
- async: options.async,
- }),
- });
- return response.json();
- },
-
- async recall(bankId, query, options = {}) {
- const response = await fetch(`${HINDSIGHT_URL}/v1/default/banks/${bankId}/memories/recall`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- query,
- types: options.types,
- max_tokens: options.maxTokens,
- budget: options.budget,
- trace: options.trace,
- query_timestamp: options.queryTimestamp,
- include_entities: options.includeEntities,
- max_entity_tokens: options.maxEntityTokens,
- include_chunks: options.includeChunks,
- max_chunk_tokens: options.maxChunkTokens,
- }),
- });
- return response.json();
- },
-
- async reflect(bankId, query, options = {}) {
- const response = await fetch(`${HINDSIGHT_URL}/v1/default/banks/${bankId}/reflect`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- query,
- context: options.context,
- budget: options.budget,
- }),
- });
- return response.json();
- },
-};
-
-const tools = createHindsightTools({ client: httpClient });
-```
+The complete documentation includes:
+- Detailed tool descriptions and parameters
+- Advanced usage patterns (streaming, multi-user, ToolLoopAgent)
+- HTTP client example (no dependencies)
+- TypeScript types and API reference
+- Best practices and system prompt examples
## Running Hindsight Locally
-The easiest way to run Hindsight for development:
-
```bash
# Install and run with embedded mode (no setup required)
uvx hindsight-embed@latest -p myapp daemon start
@@ -316,41 +64,16 @@ uvx hindsight-embed@latest -p myapp daemon start
# The API will be available at http://localhost:8000
```
-For production deployments, see the [Hindsight Documentation](https://vectorize.io/hindsight).
+## Examples
-## TypeScript Types
+Full examples are available in the [GitHub repository](https://github.com/vectorize-io/hindsight/tree/main/examples/ai-sdk).
-All types are exported for your convenience:
+## Support
-```typescript
-import type {
- Budget,
- HindsightClient,
- HindsightTools,
- HindsightToolsOptions,
- RecallResult,
- RecallResponse,
- ReflectFact,
- ReflectResponse,
- RetainResponse,
- EntityState,
- ChunkData,
-} from '@vectorize-io/hindsight-ai-sdk';
-```
-
-## Documentation & Resources
-
-- [Hindsight Documentation](https://vectorize.io/hindsight)
-- [Vercel AI SDK Documentation](https://ai-sdk.dev)
-- [GitHub Repository](https://github.com/vectorize-io/hindsight)
-- [Examples](https://github.com/vectorize-io/hindsight/tree/main/examples)
+- [Documentation](https://vectorize.io/hindsight)
+- [GitHub Issues](https://github.com/vectorize-io/hindsight/issues)
+- Email: support@vectorize.io
## License
MIT
-
-## Support
-
-For issues and questions:
-- [GitHub Issues](https://github.com/vectorize-io/hindsight/issues)
-- Email: support@vectorize.io
diff --git a/hindsight-integrations/ai-sdk/src/tools/index.ts b/hindsight-integrations/ai-sdk/src/tools/index.ts
index de9f2d73..47fd8d5b 100644
--- a/hindsight-integrations/ai-sdk/src/tools/index.ts
+++ b/hindsight-integrations/ai-sdk/src/tools/index.ts
@@ -233,11 +233,6 @@ export interface HindsightClient {
}
): Promise;
- getDirective(
- bankId: string,
- directiveId: string
- ): Promise;
-
listDirectives(
bankId: string,
options?: {
@@ -367,10 +362,6 @@ export function createHindsightTools({
tags: z.array(z.string()).optional().describe('Tags for filtering'),
});
- const getDirectiveParams = z.object({
- bankId: z.string().describe('Memory bank ID (usually the user ID)'),
- directiveId: z.string().describe('ID of the directive to retrieve'),
- });
type RetainInput = z.infer;
type RetainOutput = { success: boolean; itemsCount: number };
@@ -393,9 +384,6 @@ export function createHindsightTools({
type CreateDirectiveInput = z.infer;
type CreateDirectiveOutput = { id: string; name: string; content: string; tags: string[]; createdAt: string };
- type GetDirectiveInput = z.infer;
- type GetDirectiveOutput = { id: string; name: string; content: string; tags: string[]; isActive: boolean } | null;
-
return {
retain: tool({
description:
@@ -534,25 +522,6 @@ export function createHindsightTools({
};
},
}),
-
- getDirective: tool({
- description:
- `Retrieve a directive by its ID. Returns the directive's content, tags, and active status.`,
- inputSchema: getDirectiveParams,
- execute: async (input) => {
- const result = await client.getDirective(input.bankId, input.directiveId);
- if (!result) {
- return null;
- }
- return {
- id: result.id,
- name: result.name,
- content: result.content,
- tags: result.tags,
- isActive: result.is_active,
- };
- },
- }),
};
}