Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
696d6cec44 | ||
|
|
dc4e9e4d1e |
@@ -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';
|
||||
|
||||
<div className="cookbook-page">
|
||||
|
||||
# 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
|
||||
|
||||
<RecipeCarousel
|
||||
title="Recipes"
|
||||
@@ -444,6 +603,8 @@ Practical patterns, recipes, and complete applications for building with Hindsig
|
||||
{apps_json}
|
||||
]}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
"""
|
||||
|
||||
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__":
|
||||
|
||||
@@ -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)
|
||||
@@ -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';
|
||||
|
||||
<div className="cookbook-page">
|
||||
|
||||
# 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
|
||||
|
||||
<RecipeCarousel
|
||||
title="Recipes"
|
||||
items={[
|
||||
{ title: "Hindsight Quickstart", href: "/cookbook/recipes/quickstart" },
|
||||
{ title: "Per-User Memory", href: "/cookbook/recipes/per-user-memory" },
|
||||
{ title: "Support Agent with Shared Knowledge", href: "/cookbook/recipes/support-agent-shared-knowledge" },
|
||||
{ title: "Memory with LiteLLM", href: "/cookbook/recipes/litellm-memory-demo" },
|
||||
{ title: "Routing Tool Learning", href: "/cookbook/recipes/tool-learning-demo" },
|
||||
{ title: "Fitness Coach with Hindsight Memory", href: "/cookbook/recipes/fitness_tracker" },
|
||||
{ title: "Healthcare Assistant with Hindsight Memory", href: "/cookbook/recipes/healthcare_assistant" },
|
||||
{ title: "Movie Recommendation Assistant with Hindsight Memory", href: "/cookbook/recipes/movie_recommendation" },
|
||||
{ title: "Personal AI Assistant with Hindsight Memory", href: "/cookbook/recipes/personal_assistant" },
|
||||
{ title: "Personalized Search Agent with Hindsight Memory", href: "/cookbook/recipes/personalized_search" },
|
||||
{ title: "Study Buddy with Hindsight Memory", href: "/cookbook/recipes/study_buddy" }
|
||||
{
|
||||
title: "Hindsight Quickstart",
|
||||
href: "/cookbook/recipes/quickstart",
|
||||
description: "Learn the basics: retain, recall, and reflect",
|
||||
tags: { sdk: "hindsight-client", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Per-User Memory",
|
||||
href: "/cookbook/recipes/per-user-memory",
|
||||
description: "Build a chatbot with per-user memory isolation",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Support Agent with Shared Knowledge",
|
||||
href: "/cookbook/recipes/support-agent-shared-knowledge",
|
||||
description: "Combine per-user memory with shared product documentation",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Memory with LiteLLM",
|
||||
href: "/cookbook/recipes/litellm-memory-demo",
|
||||
description: "Add automatic memory to any LLM app using LiteLLM callbacks",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Routing Tool Learning",
|
||||
href: "/cookbook/recipes/tool-learning-demo",
|
||||
description: "Teach an LLM which tool to use through feedback and memory",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Fitness Coach with Hindsight Memory",
|
||||
href: "/cookbook/recipes/fitness_tracker",
|
||||
description: "Track workouts, diet, and progress with a personalized fitness coach",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Healthcare Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/healthcare_assistant",
|
||||
description: "A supportive chatbot that remembers patient history and preferences",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Movie Recommendation Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/movie_recommendation",
|
||||
description: "Get personalized movie recommendations that improve over time",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Personal AI Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/personal_assistant",
|
||||
description: "A general-purpose assistant that remembers your life and preferences",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Personalized Search Agent with Hindsight Memory",
|
||||
href: "/cookbook/recipes/personalized_search",
|
||||
description: "Search assistant that learns your location, diet, and lifestyle",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Study Buddy with Hindsight Memory",
|
||||
href: "/cookbook/recipes/study_buddy",
|
||||
description: "Track study sessions, identify knowledge gaps, and get personalized review suggestions",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<RecipeCarousel
|
||||
title="Applications"
|
||||
items={[
|
||||
{ title: "Chat Memory App", href: "/cookbook/applications/chat-memory" },
|
||||
{ title: "Deliveryman Demo", href: "/cookbook/applications/deliveryman-demo" },
|
||||
{ title: "Memory Approaches Comparison Demo", href: "/cookbook/applications/hindsight-litellm-demo" },
|
||||
{ title: "Tool Learning Demo", href: "/cookbook/applications/hindsight-tool-learning-demo" },
|
||||
{ title: "OpenAI Agent + Hindsight Memory Integration", href: "/cookbook/applications/openai-fitness-coach" },
|
||||
{ title: "Sanity CMS Blog Memory", href: "/cookbook/applications/sanity-blog-memory" },
|
||||
{ title: "Stance Tracker", href: "/cookbook/applications/stancetracker" }
|
||||
{
|
||||
title: "Chat Memory App",
|
||||
href: "/cookbook/applications/chat-memory",
|
||||
description: "Real-time chat app with per-user memory using Groq and Hindsight",
|
||||
tags: { sdk: "hindsight-client", topic: "Chat" }
|
||||
},
|
||||
{
|
||||
title: "Deliveryman Demo",
|
||||
href: "/cookbook/applications/deliveryman-demo",
|
||||
description: "Delivery agent simulation demonstrating learning through mental models",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Memory Approaches Comparison Demo",
|
||||
href: "/cookbook/applications/hindsight-litellm-demo",
|
||||
description: "Interactive comparison of memory approaches: none, full history, and semantic retrieval",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Tool Learning Demo",
|
||||
href: "/cookbook/applications/hindsight-tool-learning-demo",
|
||||
description: "Show how Hindsight helps LLMs learn which tool to use when names are ambiguous",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "OpenAI Agent + Hindsight Memory Integration",
|
||||
href: "/cookbook/applications/openai-fitness-coach",
|
||||
description: "Fitness coach using OpenAI Assistants with Hindsight as memory backend",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Sanity CMS Blog Memory",
|
||||
href: "/cookbook/applications/sanity-blog-memory",
|
||||
description: "Sync Sanity CMS blog posts to Hindsight for semantic search and AI insights",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Stance Tracker",
|
||||
href: "/cookbook/applications/stancetracker",
|
||||
description: "Track political candidates' stances over time with automated web scraping",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Hindsight AI SDK - Personal Chef",
|
||||
href: "/cookbook/applications/taste-ai",
|
||||
description: "Personal food assistant with AI SDK v6 showcasing recall, mental models, and directives",
|
||||
tags: { sdk: "@vectorize-io/hindsight-ai-sdk", topic: "Recommendation" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
```
|
||||
@@ -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',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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 (
|
||||
<div className={styles.carouselSection}>
|
||||
<div className={styles.carouselSection} id={sectionId}>
|
||||
<h2 className={styles.sectionTitle}>{title}</h2>
|
||||
<div className={styles.carousel}>
|
||||
<div className={styles.carouselTrack}>
|
||||
{items.map((item, index) => (
|
||||
<Link key={index} to={item.href} className={styles.card}>
|
||||
<span className={styles.cardTitle}>{item.title}</span>
|
||||
<span className={styles.cardLink}>→</span>
|
||||
</Link>
|
||||
))}
|
||||
{items.map((item, index) => {
|
||||
// Get topic color for card border
|
||||
const topicColors = item.tags?.topic ? getTagColor(item.tags.topic) : null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={index}
|
||||
to={item.href}
|
||||
className={styles.card}
|
||||
style={{
|
||||
'--card-border': topicColors?.border,
|
||||
'--card-border-dark': topicColors?.borderDark,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
<div className={styles.cardContent}>
|
||||
<span className={styles.cardTitle}>{item.title}</span>
|
||||
{item.description && (
|
||||
<p className={styles.cardDescription}>{item.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.cardFooter}>
|
||||
{item.tags && (
|
||||
<div className={styles.cardTags}>
|
||||
{item.tags.sdk && (() => {
|
||||
const colors = getTagColor(item.tags.sdk);
|
||||
const icon = getPackageIcon(item.tags.sdk);
|
||||
return (
|
||||
<span
|
||||
className={styles.tag}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.4rem',
|
||||
'--tag-bg': colors.bg,
|
||||
'--tag-text': colors.text,
|
||||
'--tag-border': colors.border,
|
||||
'--tag-bg-dark': colors.bgDark,
|
||||
'--tag-text-dark': colors.textDark,
|
||||
'--tag-border-dark': colors.borderDark,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{icon && (
|
||||
<img
|
||||
src={icon}
|
||||
alt=""
|
||||
style={{ width: '13px', height: '13px', flexShrink: 0 }}
|
||||
/>
|
||||
)}
|
||||
{item.tags.sdk}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
{item.tags.topic && (() => {
|
||||
const colors = getTagColor(item.tags.topic);
|
||||
return (
|
||||
<span
|
||||
className={styles.tag}
|
||||
style={{
|
||||
'--tag-bg': colors.bg,
|
||||
'--tag-text': colors.text,
|
||||
'--tag-border': colors.border,
|
||||
'--tag-bg-dark': colors.bgDark,
|
||||
'--tag-text-dark': colors.textDark,
|
||||
'--tag-border-dark': colors.borderDark,
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{item.tags.topic}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
<span className={styles.cardLink}>→</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -429,6 +429,84 @@
|
||||
color: var(--ifm-color-emphasis-500);
|
||||
}
|
||||
|
||||
/* Client sidebar icons */
|
||||
a.menu__link[href*="/sdks/python"]::before,
|
||||
a.menu__link[href*="/sdks/nodejs"]::before,
|
||||
a.menu__link[href*="/sdks/cli"]::before,
|
||||
a.menu__link[href*="/sdks/embed"]::before {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
content: '';
|
||||
}
|
||||
|
||||
/* Python logo */
|
||||
a.menu__link[href*="/sdks/python"]::before {
|
||||
background-image: url('/img/icons/python.svg');
|
||||
}
|
||||
|
||||
/* Node.js logo */
|
||||
a.menu__link[href*="/sdks/nodejs"]::before {
|
||||
background-image: url('/img/icons/nodejs.svg');
|
||||
}
|
||||
|
||||
/* CLI - terminal icon */
|
||||
a.menu__link[href*="/sdks/cli"]::before {
|
||||
background-image: url('/img/icons/terminal.svg');
|
||||
}
|
||||
|
||||
/* Embedded SDK - package icon */
|
||||
a.menu__link[href*="/sdks/embed"]::before {
|
||||
background-image: url('/img/icons/package.svg');
|
||||
}
|
||||
|
||||
/* Integration sidebar icons */
|
||||
a.menu__link[href*="/sdks/integrations/local-mcp"]::before,
|
||||
a.menu__link[href*="/sdks/integrations/litellm"]::before,
|
||||
a.menu__link[href*="/sdks/integrations/openclaw"]::before,
|
||||
a.menu__link[href*="/sdks/integrations/ai-sdk"]::before,
|
||||
a.menu__link[href*="/sdks/integrations/skills"]::before {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
content: '';
|
||||
}
|
||||
|
||||
/* Local MCP Server */
|
||||
a.menu__link[href*="/sdks/integrations/local-mcp"]::before {
|
||||
background-image: url('/img/icons/mcp.png');
|
||||
}
|
||||
|
||||
/* LiteLLM */
|
||||
a.menu__link[href*="/sdks/integrations/litellm"]::before {
|
||||
background-image: url('/img/icons/litellm.png');
|
||||
}
|
||||
|
||||
/* OpenClaw */
|
||||
a.menu__link[href*="/sdks/integrations/openclaw"]::before {
|
||||
background-image: url('/img/icons/openclaw.png');
|
||||
}
|
||||
|
||||
/* Vercel AI SDK */
|
||||
a.menu__link[href*="/sdks/integrations/ai-sdk"]::before {
|
||||
background-image: url('/img/icons/vercel.png');
|
||||
}
|
||||
|
||||
/* Skills */
|
||||
a.menu__link[href*="/sdks/integrations/skills"]::before {
|
||||
background-image: url('/img/icons/skills.png');
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre,
|
||||
pre code,
|
||||
@@ -1306,3 +1384,75 @@ ul[class*="suggestion"] {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ============================================
|
||||
Cookbook: Hide sidebar for OpenAI-style layout
|
||||
============================================ */
|
||||
|
||||
/* Hide sidebar completely on cookbook pages - use multiple selectors for reliability */
|
||||
[class*="docPage"] aside[class*="docSidebarContainer"],
|
||||
aside[class*="docSidebarContainer"]:has(+ * .cookbook-page),
|
||||
body:has(.cookbook-page) aside[class*="docSidebarContainer"],
|
||||
.hidden-sidebar aside,
|
||||
article[id="cookbook-index"] ~ aside,
|
||||
div:has(> article[id="cookbook-index"]) aside {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
/* Make main wrapper full width */
|
||||
body:has(.cookbook-page) .main-wrapper,
|
||||
.hidden-sidebar ~ * .main-wrapper,
|
||||
div:has(> article[id="cookbook-index"]) {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Make doc page container full width */
|
||||
body:has(.cookbook-page) [class*="docMainContainer"],
|
||||
.hidden-sidebar [class*="docMainContainer"],
|
||||
div:has(> .cookbook-page) > div {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Make the content column full width */
|
||||
body:has(.cookbook-page) [class*="docItemCol"],
|
||||
.hidden-sidebar [class*="docItemCol"],
|
||||
.cookbook-page ~ * [class*="col"] {
|
||||
max-width: 100% !important;
|
||||
flex: 1 1 100% !important;
|
||||
}
|
||||
|
||||
/* Container adjustments */
|
||||
.cookbook-page .container,
|
||||
body:has(.cookbook-page) .container {
|
||||
max-width: 1400px !important;
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 2rem !important;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 996px) {
|
||||
.cookbook-page .container {
|
||||
padding-left: 1rem !important;
|
||||
padding-right: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Additional fallback selectors for hiding cookbook sidebar */
|
||||
[data-route="/cookbook"] aside,
|
||||
[data-route="/cookbook/"] aside,
|
||||
div[class*="docPage"]:has(article[id*="cookbook"]) > aside:first-child {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Force full width on cookbook route */
|
||||
[data-route="/cookbook"] div[class*="docRoot"],
|
||||
[data-route="/cookbook/"] div[class*="docRoot"] {
|
||||
grid-template-columns: 0 auto !important;
|
||||
}
|
||||
|
||||
[data-route="/cookbook"] main,
|
||||
[data-route="/cookbook/"] main {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import Main from '@theme-original/DocPage/Layout/Main';
|
||||
import type MainType from '@theme/DocPage/Layout/Main';
|
||||
import type {WrapperProps} from '@docusaurus/types';
|
||||
import {useLocation} from '@docusaurus/router';
|
||||
|
||||
type Props = WrapperProps<typeof MainType>;
|
||||
|
||||
export default function MainWrapper(props: Props): JSX.Element {
|
||||
const location = useLocation();
|
||||
const isCookbook = location.pathname.includes('/cookbook');
|
||||
|
||||
return (
|
||||
<div style={isCookbook ? {maxWidth: '100%', width: '100%'} : undefined}>
|
||||
<Main {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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<typeof SidebarType>;
|
||||
|
||||
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 <Sidebar {...props} />;
|
||||
}
|
||||
@@ -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/)
|
||||
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 521 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="110px" height="110px" viewBox="0.21 -0.077 110 110" enable-background="new 0.21 -0.077 110 110" xml:space="preserve"><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="63.8159" y1="56.6829" x2="118.4934" y2="1.8225" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" style="stop-color:#387EB8"/> <stop offset="1" style="stop-color:#366994"/></linearGradient><path fill="url(#SVGID_1_)" d="M55.023-0.077c-25.971,0-26.25,10.081-26.25,12.156c0,3.148,0,12.594,0,12.594h26.75v3.781 c0,0-27.852,0-37.375,0c-7.949,0-17.938,4.833-17.938,26.25c0,19.673,7.792,27.281,15.656,27.281c2.335,0,9.344,0,9.344,0 s0-9.765,0-13.125c0-5.491,2.721-15.656,15.406-15.656c15.91,0,19.971,0,26.531,0c3.902,0,14.906-1.696,14.906-14.406 c0-13.452,0-17.89,0-24.219C82.054,11.426,81.515-0.077,55.023-0.077z M40.273,8.392c2.662,0,4.813,2.15,4.813,4.813 c0,2.661-2.151,4.813-4.813,4.813s-4.813-2.151-4.813-4.813C35.46,10.542,37.611,8.392,40.273,8.392z"/><linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="97.0444" y1="21.6321" x2="155.6665" y2="-34.5308" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" style="stop-color:#FFE052"/> <stop offset="1" style="stop-color:#FFC331"/></linearGradient><path fill="url(#SVGID_2_)" d="M55.397,109.923c25.959,0,26.282-10.271,26.282-12.156c0-3.148,0-12.594,0-12.594H54.897v-3.781 c0,0,28.032,0,37.375,0c8.009,0,17.938-4.954,17.938-26.25c0-23.322-10.538-27.281-15.656-27.281c-2.336,0-9.344,0-9.344,0 s0,10.216,0,13.125c0,5.491-2.631,15.656-15.406,15.656c-15.91,0-19.476,0-26.532,0c-3.892,0-14.906,1.896-14.906,14.406 c0,14.475,0,18.265,0,24.219C28.366,100.497,31.562,109.923,55.397,109.923z M70.148,101.454c-2.662,0-4.813-2.151-4.813-4.813 s2.15-4.813,4.813-4.813c2.661,0,4.813,2.151,4.813,4.813S72.809,101.454,70.148,101.454z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="110px" height="110px" viewBox="0.21 -0.077 110 110" enable-background="new 0.21 -0.077 110 110" xml:space="preserve"><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="63.8159" y1="56.6829" x2="118.4934" y2="1.8225" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" style="stop-color:#387EB8"/> <stop offset="1" style="stop-color:#366994"/></linearGradient><path fill="url(#SVGID_1_)" d="M55.023-0.077c-25.971,0-26.25,10.081-26.25,12.156c0,3.148,0,12.594,0,12.594h26.75v3.781 c0,0-27.852,0-37.375,0c-7.949,0-17.938,4.833-17.938,26.25c0,19.673,7.792,27.281,15.656,27.281c2.335,0,9.344,0,9.344,0 s0-9.765,0-13.125c0-5.491,2.721-15.656,15.406-15.656c15.91,0,19.971,0,26.531,0c3.902,0,14.906-1.696,14.906-14.406 c0-13.452,0-17.89,0-24.219C82.054,11.426,81.515-0.077,55.023-0.077z M40.273,8.392c2.662,0,4.813,2.15,4.813,4.813 c0,2.661-2.151,4.813-4.813,4.813s-4.813-2.151-4.813-4.813C35.46,10.542,37.611,8.392,40.273,8.392z"/><linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="97.0444" y1="21.6321" x2="155.6665" y2="-34.5308" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" style="stop-color:#FFE052"/> <stop offset="1" style="stop-color:#FFC331"/></linearGradient><path fill="url(#SVGID_2_)" d="M55.397,109.923c25.959,0,26.282-10.271,26.282-12.156c0-3.148,0-12.594,0-12.594H54.897v-3.781 c0,0,28.032,0,37.375,0c8.009,0,17.938-4.954,17.938-26.25c0-23.322-10.538-27.281-15.656-27.281c-2.336,0-9.344,0-9.344,0 s0,10.216,0,13.125c0,5.491-2.631,15.656-15.406,15.656c-15.91,0-19.476,0-26.532,0c-3.892,0-14.906,1.896-14.906,14.406 c0,14.475,0,18.265,0,24.219C28.366,100.497,31.562,109.923,55.397,109.923z M70.148,101.454c-2.662,0-4.813-2.151-4.813-4.813 s2.15-4.813,4.813-4.813c2.661,0,4.813,2.151,4.813,4.813S72.809,101.454,70.148,101.454z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128" style="display:inline;enable-background:new" version="1.0" id="svg11300" height="128" width="128">
|
||||
|
||||
|
||||
<title id="title4162">Adwaita Icon Template</title>
|
||||
<defs id="defs3">
|
||||
<linearGradient id="linearGradient1948">
|
||||
<stop id="stop1944" offset="0" style="stop-color:#2d2839;stop-opacity:1;"/>
|
||||
<stop id="stop1946" offset="1" style="stop-color:#282433;stop-opacity:1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient1020">
|
||||
<stop id="stop1016" offset="0" style="stop-color:#ffffff;stop-opacity:1;"/>
|
||||
<stop id="stop1018" offset="1" style="stop-color:#ffffff;stop-opacity:0.09411765"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient1001">
|
||||
<stop id="stop989" offset="0" style="stop-color:#77767b;stop-opacity:1"/>
|
||||
<stop style="stop-color:#c0bfbc;stop-opacity:1" offset="0.05" id="stop991"/>
|
||||
<stop id="stop993" offset="0.09999998" style="stop-color:#9a9996;stop-opacity:1"/>
|
||||
<stop style="stop-color:#9a9996;stop-opacity:1" offset="0.89999938" id="stop995"/>
|
||||
<stop id="stop997" offset="0.94999999" style="stop-color:#c0bfbc;stop-opacity:1"/>
|
||||
<stop id="stop999" offset="1" style="stop-color:#77767b;stop-opacity:1"/>
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" y2="44" x2="464" y1="44" x1="48" id="linearGradient965" xlink:href="#linearGradient1001"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(-4.7272726,7.935912e-7,-3.0301491e-7,-1.6363636,238.54547,49.766183)" r="44" fy="194.19048" fx="63.999996" cy="194.19048" cx="63.999996" id="radialGradient1030" xlink:href="#linearGradient1020"/>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" y2="269.13693" x2="70.346565" y1="245.39511" x1="70.346565" id="linearGradient1950" xlink:href="#linearGradient1948"/>
|
||||
</defs>
|
||||
<metadata id="metadata4">
|
||||
|
||||
</metadata>
|
||||
<g transform="translate(0,-172)" style="display:inline" id="layer1">
|
||||
<g style="display:inline" id="layer9">
|
||||
<g transform="rotate(-30,420.69873,288.4192)" id="g1710" style="display:inline;enable-background:new"/>
|
||||
<rect transform="matrix(0.25,0,0,0.25,0,225)" style="display:inline;opacity:1;fill:url(#linearGradient965);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;enable-background:new" id="rect953" width="416" height="376" x="48" y="-124" rx="32" ry="32"/>
|
||||
<rect ry="32" rx="32" y="-164" x="48" height="384" width="416" id="rect950" style="display:inline;opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;enable-background:new" transform="matrix(0.25,0,0,0.25,0,225)"/>
|
||||
<rect transform="scale(1,-1)" ry="3.9999695" rx="4" y="-276" x="16" height="87.999969" width="96" id="rect1004" style="display:inline;opacity:1;vector-effect:none;fill:#241f31;fill-opacity:1;stroke:none;stroke-width:0.01121096px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;enable-background:new"/>
|
||||
<rect transform="scale(-1)" style="display:inline;opacity:0.05;vector-effect:none;fill:url(#radialGradient1030);fill-opacity:1;stroke:none;stroke-width:0.01121096px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;enable-background:new" id="rect968" width="88" height="78" x="-108" y="-272"/>
|
||||
<g id="g976" transform="translate(-2,-2)" style="fill:#ffffff">
|
||||
<path d="M 44.012301,210.88755 30,203.27182 V 208 l 9.710724,4.62951 v 0.1422 L 30,218 v 4.72818 l 14.012301,-8.21451 z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:1.25;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24999999" id="path972"/>
|
||||
<path d="m 47.999998,226 2e-6,4 h 16.00001 l -2e-6,-4 z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:1.25;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24999999" id="path974"/>
|
||||
</g>
|
||||
<path d="m 100,244 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 84,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 76,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 84,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 76,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 84,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m 76,4 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z m -8,0 h 4 v 4 h -4 z" style="opacity:1;vector-effect:none;fill:url(#linearGradient1950);fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal" id="rect1059"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -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)
|
||||
@@ -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';
|
||||
|
||||
<div className="cookbook-page">
|
||||
|
||||
# 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
|
||||
|
||||
<RecipeCarousel
|
||||
title="Recipes"
|
||||
items={[
|
||||
{ title: "Hindsight Quickstart", href: "/cookbook/recipes/quickstart" },
|
||||
{ title: "Per-User Memory", href: "/cookbook/recipes/per-user-memory" },
|
||||
{ title: "Support Agent with Shared Knowledge", href: "/cookbook/recipes/support-agent-shared-knowledge" },
|
||||
{ title: "Memory with LiteLLM", href: "/cookbook/recipes/litellm-memory-demo" },
|
||||
{ title: "Routing Tool Learning", href: "/cookbook/recipes/tool-learning-demo" },
|
||||
{ title: "Fitness Coach with Hindsight Memory", href: "/cookbook/recipes/fitness_tracker" },
|
||||
{ title: "Healthcare Assistant with Hindsight Memory", href: "/cookbook/recipes/healthcare_assistant" },
|
||||
{ title: "Movie Recommendation Assistant with Hindsight Memory", href: "/cookbook/recipes/movie_recommendation" },
|
||||
{ title: "Personal AI Assistant with Hindsight Memory", href: "/cookbook/recipes/personal_assistant" },
|
||||
{ title: "Personalized Search Agent with Hindsight Memory", href: "/cookbook/recipes/personalized_search" },
|
||||
{ title: "Study Buddy with Hindsight Memory", href: "/cookbook/recipes/study_buddy" }
|
||||
{
|
||||
title: "Hindsight Quickstart",
|
||||
href: "/cookbook/recipes/quickstart",
|
||||
description: "Learn the basics: retain, recall, and reflect",
|
||||
tags: { sdk: "hindsight-client", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Per-User Memory",
|
||||
href: "/cookbook/recipes/per-user-memory",
|
||||
description: "Build a chatbot with per-user memory isolation",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Support Agent with Shared Knowledge",
|
||||
href: "/cookbook/recipes/support-agent-shared-knowledge",
|
||||
description: "Combine per-user memory with shared product documentation",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Memory with LiteLLM",
|
||||
href: "/cookbook/recipes/litellm-memory-demo",
|
||||
description: "Add automatic memory to any LLM app using LiteLLM callbacks",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Routing Tool Learning",
|
||||
href: "/cookbook/recipes/tool-learning-demo",
|
||||
description: "Teach an LLM which tool to use through feedback and memory",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Fitness Coach with Hindsight Memory",
|
||||
href: "/cookbook/recipes/fitness_tracker",
|
||||
description: "Track workouts, diet, and progress with a personalized fitness coach",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Healthcare Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/healthcare_assistant",
|
||||
description: "A supportive chatbot that remembers patient history and preferences",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Movie Recommendation Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/movie_recommendation",
|
||||
description: "Get personalized movie recommendations that improve over time",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Personal AI Assistant with Hindsight Memory",
|
||||
href: "/cookbook/recipes/personal_assistant",
|
||||
description: "A general-purpose assistant that remembers your life and preferences",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Personalized Search Agent with Hindsight Memory",
|
||||
href: "/cookbook/recipes/personalized_search",
|
||||
description: "Search assistant that learns your location, diet, and lifestyle",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Study Buddy with Hindsight Memory",
|
||||
href: "/cookbook/recipes/study_buddy",
|
||||
description: "Track study sessions, identify knowledge gaps, and get personalized review suggestions",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<RecipeCarousel
|
||||
title="Applications"
|
||||
items={[
|
||||
{ title: "Chat Memory App", href: "/cookbook/applications/chat-memory" },
|
||||
{ title: "Deliveryman Demo", href: "/cookbook/applications/deliveryman-demo" },
|
||||
{ title: "Memory Approaches Comparison Demo", href: "/cookbook/applications/hindsight-litellm-demo" },
|
||||
{ title: "Tool Learning Demo", href: "/cookbook/applications/hindsight-tool-learning-demo" },
|
||||
{ title: "OpenAI Agent + Hindsight Memory Integration", href: "/cookbook/applications/openai-fitness-coach" },
|
||||
{ title: "Sanity CMS Blog Memory", href: "/cookbook/applications/sanity-blog-memory" },
|
||||
{ title: "Stance Tracker", href: "/cookbook/applications/stancetracker" }
|
||||
{
|
||||
title: "Chat Memory App",
|
||||
href: "/cookbook/applications/chat-memory",
|
||||
description: "Real-time chat app with per-user memory using Groq and Hindsight",
|
||||
tags: { sdk: "hindsight-client", topic: "Chat" }
|
||||
},
|
||||
{
|
||||
title: "Deliveryman Demo",
|
||||
href: "/cookbook/applications/deliveryman-demo",
|
||||
description: "Delivery agent simulation demonstrating learning through mental models",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Memory Approaches Comparison Demo",
|
||||
href: "/cookbook/applications/hindsight-litellm-demo",
|
||||
description: "Interactive comparison of memory approaches: none, full history, and semantic retrieval",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Quick Start" }
|
||||
},
|
||||
{
|
||||
title: "Tool Learning Demo",
|
||||
href: "/cookbook/applications/hindsight-tool-learning-demo",
|
||||
description: "Show how Hindsight helps LLMs learn which tool to use when names are ambiguous",
|
||||
tags: { sdk: "hindsight-litellm", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "OpenAI Agent + Hindsight Memory Integration",
|
||||
href: "/cookbook/applications/openai-fitness-coach",
|
||||
description: "Fitness coach using OpenAI Assistants with Hindsight as memory backend",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Sanity CMS Blog Memory",
|
||||
href: "/cookbook/applications/sanity-blog-memory",
|
||||
description: "Sync Sanity CMS blog posts to Hindsight for semantic search and AI insights",
|
||||
tags: { sdk: "hindsight-client", topic: "Learning" }
|
||||
},
|
||||
{
|
||||
title: "Stance Tracker",
|
||||
href: "/cookbook/applications/stancetracker",
|
||||
description: "Track political candidates' stances over time with automated web scraping",
|
||||
tags: { sdk: "hindsight-client", topic: "Recommendation" }
|
||||
},
|
||||
{
|
||||
title: "Hindsight AI SDK - Personal Chef",
|
||||
href: "/cookbook/applications/taste-ai",
|
||||
description: "Personal food assistant with AI SDK v6 showcasing recall, mental models, and directives",
|
||||
tags: { sdk: "@vectorize-io/hindsight-ai-sdk", topic: "Recommendation" }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
```
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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<string, string>;
|
||||
chunk_id?: string;
|
||||
}>;
|
||||
entities?: Record<string, EntityState>;
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
@@ -233,11 +233,6 @@ export interface HindsightClient {
|
||||
}
|
||||
): Promise<CreateDirectiveResponse>;
|
||||
|
||||
getDirective(
|
||||
bankId: string,
|
||||
directiveId: string
|
||||
): Promise<DirectiveResponse | null>;
|
||||
|
||||
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<typeof retainParams>;
|
||||
type RetainOutput = { success: boolean; itemsCount: number };
|
||||
@@ -393,9 +384,6 @@ export function createHindsightTools({
|
||||
type CreateDirectiveInput = z.infer<typeof createDirectiveParams>;
|
||||
type CreateDirectiveOutput = { id: string; name: string; content: string; tags: string[]; createdAt: string };
|
||||
|
||||
type GetDirectiveInput = z.infer<typeof getDirectiveParams>;
|
||||
type GetDirectiveOutput = { id: string; name: string; content: string; tags: string[]; isActive: boolean } | null;
|
||||
|
||||
return {
|
||||
retain: tool<RetainInput, RetainOutput>({
|
||||
description:
|
||||
@@ -534,25 +522,6 @@ export function createHindsightTools({
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
getDirective: tool<GetDirectiveInput, GetDirectiveOutput>({
|
||||
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,
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||