From 8ecb5d3a0c7393f4da81592924f084e56d0b235a Mon Sep 17 00:00:00 2001 From: DK09876 Date: Thu, 18 Dec 2025 02:21:38 -0700 Subject: [PATCH] Add documentation code validation system (#43) * Add documentation code validation system - Create runnable example scripts in examples/api/ (19 files) - Add CodeSnippet component for extracting marked sections - Add raw-loader dependency for importing source files - Create sample retain-new.mdx showing new approach - Add README documenting coverage and gaps * Fix wheel glob expansion in test-doc-examples CI job * Fix CI issue * Fix wheel path - uv build outputs to repo root dist/ * Fix: use explicit shell expansion for wheel install * Fix: run cd in subshell so install runs from repo root * Add documentation code validation CI job - Use uv sync + uv run pattern (matches existing CI) - Add requests to test dependencies for cleanup scripts * Fix async API client usage in documents.py example * Fix main-methods.py: RecallResult and ReflectFact don't have weight attribute * Fix opinions.py: use actual API attributes instead of non-existent ones * Fix example scripts: remove non-existent API attributes - recall.py: remove .weight, fix entities iteration (dict not list) - retain.mjs: remove result.async check --- .github/workflows/test.yml | 94 +++++++- hindsight-clients/python/pyproject.toml | 1 + .../docs/developer/api/retain-new.mdx | 71 ++++++ hindsight-docs/examples/api/README.md | 82 +++++++ hindsight-docs/examples/api/cli-reference.sh | 209 ++++++++++++++++++ hindsight-docs/examples/api/documents.mjs | 68 ++++++ hindsight-docs/examples/api/documents.py | 90 ++++++++ hindsight-docs/examples/api/main-methods.py | 125 +++++++++++ hindsight-docs/examples/api/memory-banks.mjs | 47 ++++ hindsight-docs/examples/api/memory-banks.py | 52 +++++ hindsight-docs/examples/api/opinions.py | 104 +++++++++ hindsight-docs/examples/api/quickstart.mjs | 34 +++ hindsight-docs/examples/api/quickstart.py | 36 +++ hindsight-docs/examples/api/quickstart.sh | 30 +++ hindsight-docs/examples/api/recall.mjs | 61 +++++ hindsight-docs/examples/api/recall.py | 124 +++++++++++ hindsight-docs/examples/api/recall.sh | 46 ++++ hindsight-docs/examples/api/reflect.mjs | 78 +++++++ hindsight-docs/examples/api/reflect.py | 89 ++++++++ hindsight-docs/examples/api/reflect.sh | 39 ++++ hindsight-docs/examples/api/retain.mjs | 58 +++++ hindsight-docs/examples/api/retain.py | 75 +++++++ hindsight-docs/examples/api/retain.sh | 34 +++ hindsight-docs/examples/run-examples.sh | 129 +++++++++++ hindsight-docs/package.json | 1 + hindsight-docs/src/components/CodeSnippet.tsx | 110 +++++++++ package-lock.json | 39 ++++ uv.lock | 10 +- 28 files changed, 1931 insertions(+), 5 deletions(-) create mode 100644 hindsight-docs/docs/developer/api/retain-new.mdx create mode 100644 hindsight-docs/examples/api/README.md create mode 100755 hindsight-docs/examples/api/cli-reference.sh create mode 100644 hindsight-docs/examples/api/documents.mjs create mode 100644 hindsight-docs/examples/api/documents.py create mode 100644 hindsight-docs/examples/api/main-methods.py create mode 100644 hindsight-docs/examples/api/memory-banks.mjs create mode 100644 hindsight-docs/examples/api/memory-banks.py create mode 100644 hindsight-docs/examples/api/opinions.py create mode 100644 hindsight-docs/examples/api/quickstart.mjs create mode 100644 hindsight-docs/examples/api/quickstart.py create mode 100755 hindsight-docs/examples/api/quickstart.sh create mode 100644 hindsight-docs/examples/api/recall.mjs create mode 100644 hindsight-docs/examples/api/recall.py create mode 100755 hindsight-docs/examples/api/recall.sh create mode 100644 hindsight-docs/examples/api/reflect.mjs create mode 100644 hindsight-docs/examples/api/reflect.py create mode 100755 hindsight-docs/examples/api/reflect.sh create mode 100644 hindsight-docs/examples/api/retain.mjs create mode 100644 hindsight-docs/examples/api/retain.py create mode 100755 hindsight-docs/examples/api/retain.sh create mode 100755 hindsight-docs/examples/run-examples.sh create mode 100644 hindsight-docs/src/components/CodeSnippet.tsx diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4fe99f37..1c0f0ff6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -495,4 +495,96 @@ jobs: - name: Run tests working-directory: ./hindsight-integrations/litellm - run: uv run pytest tests -v \ No newline at end of file + run: uv run pytest tests -v + + test-doc-examples: + runs-on: ubuntu-latest + env: + HINDSIGHT_API_LLM_PROVIDER: groq + HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }} + HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b + HINDSIGHT_API_URL: http://localhost:8888 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + prune-cache: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: package-lock.json + + - name: Build and install API + working-directory: ./hindsight-api + run: | + uv build + uv sync --no-install-project --index-strategy unsafe-best-match + + - name: Install Python client dependencies + working-directory: ./hindsight-clients/python + run: uv sync --extra test --index-strategy unsafe-best-match + + - name: Install TypeScript client + run: | + npm ci --workspace=hindsight-clients/typescript + npm run build --workspace=hindsight-clients/typescript + + - name: Create .env file + run: | + cat > .env << EOF + HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }} + HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }} + HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }} + EOF + + - name: Start API server + run: | + ./scripts/dev/start-api.sh > /tmp/api-server.log 2>&1 & + echo "Waiting for API server to be ready..." + for i in {1..60}; do + if curl -sf http://localhost:8888/health > /dev/null 2>&1; then + echo "API server is ready after ${i}s" + break + fi + if [ $i -eq 60 ]; then + echo "API server failed to start after 60s" + cat /tmp/api-server.log + exit 1 + fi + sleep 1 + done + + - name: Run Python doc examples + working-directory: ./hindsight-clients/python + run: | + for f in ../../hindsight-docs/examples/api/*.py; do + echo "Running $f..." + uv run python "$f" + done + + - name: Run Node.js doc examples + run: | + for f in hindsight-docs/examples/api/*.mjs; do + echo "Running $f..." + node "$f" + done + + - name: Show API server logs + if: always() + run: | + echo "=== API Server Logs ===" + cat /tmp/api-server.log || echo "No API server log found" \ No newline at end of file diff --git a/hindsight-clients/python/pyproject.toml b/hindsight-clients/python/pyproject.toml index 0bdf6699..68b44435 100644 --- a/hindsight-clients/python/pyproject.toml +++ b/hindsight-clients/python/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ test = [ "pytest>=7.0.0", "pytest-asyncio>=0.21.0", + "requests>=2.28.0", ] [build-system] diff --git a/hindsight-docs/docs/developer/api/retain-new.mdx b/hindsight-docs/docs/developer/api/retain-new.mdx new file mode 100644 index 00000000..8d0b94c8 --- /dev/null +++ b/hindsight-docs/docs/developer/api/retain-new.mdx @@ -0,0 +1,71 @@ +--- +sidebar_position: 2 +--- + +# Ingest Data (New Format) + +This is a demo of the new code snippet approach. Code examples are pulled from executable script files. + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeSnippet from '@site/src/components/CodeSnippet'; + +{/* Import raw source files */} +import retainPy from '!!raw-loader!@site/examples/api/retain.py'; +import retainMjs from '!!raw-loader!@site/examples/api/retain.mjs'; +import retainSh from '!!raw-loader!@site/examples/api/retain.sh'; + +:::tip How This Works +The code examples below are extracted from actual runnable script files in `examples/api/`. +When CI runs these scripts, it validates the documentation is correct. +::: + +## Store a Single Memory + + + + + + + + + + + + + +## Store with Context + + + + + + + + + + + + + +## Batch Ingestion + + + + + + + + + + +## Async Ingestion + + + + + + + + + diff --git a/hindsight-docs/examples/api/README.md b/hindsight-docs/examples/api/README.md new file mode 100644 index 00000000..2d627236 --- /dev/null +++ b/hindsight-docs/examples/api/README.md @@ -0,0 +1,82 @@ +# API Documentation Examples + +This directory contains runnable example scripts that serve as the source of truth for code samples in the documentation. + +## How It Works + +1. **Scripts are runnable** - Each file can be executed as a smoke test +2. **Markers define sections** - Code between `# [docs:section-name]` and `# [/docs:section-name]` markers is extracted +3. **Docs import at build time** - MDX files use `raw-loader` to import scripts, then `CodeSnippet` extracts marked sections + +## File Structure + +| File | Documentation | Description | +|------|---------------|-------------| +| `quickstart.py/mjs/sh` | quickstart.md | Getting started examples | +| `retain.py/mjs/sh` | retain.md | Memory ingestion examples | +| `recall.py/mjs/sh` | recall.md | Memory retrieval examples | +| `reflect.py/mjs/sh` | reflect.md | AI reflection examples | +| `memory-banks.py/mjs` | memory-banks.md | Bank management examples | +| `documents.py/mjs` | documents.md | Document CRUD examples | +| `opinions.py` | opinions.md | Opinion management examples | +| `main-methods.py` | main-methods.md | Core method examples | +| `cli-reference.sh` | cli.md | CLI command examples | + +## Running Examples + +```bash +# Run all Python examples +for f in *.py; do python "$f"; done + +# Run all Node.js examples +for f in *.mjs; do node "$f"; done + +# Run all CLI examples +for f in *.sh; do bash "$f"; done +``` + +Requires a running Hindsight server at `http://localhost:8888` (or set `HINDSIGHT_API_URL`). + +## What's NOT Covered + +### 1. OpenAPI Auto-Generated Docs (`/api-reference/*`) + +These pages are generated directly from the OpenAPI specification. The spec itself is the source of truth, and the generated docs reflect it automatically. No manual code examples to validate. + +### 2. Interactive CLI Commands + +| Command | Reason | +|---------|--------| +| `hindsight configure` | Requires interactive user input (prompts for API URL, credentials) | +| `hindsight configure --show` | Displays sensitive configuration, not suitable for automated tests | + +### 3. Installation/Setup Instructions + +Documentation sections covering `pip install`, `npm install`, or system setup are instructions, not executable code samples. These are validated by the CI environment setup itself. + +### 4. Error Handling Examples + +Some docs show error responses (e.g., "what happens when bank doesn't exist"). These require intentionally broken states that would fail smoke tests. Error behavior is covered by unit tests instead. + +## Adding New Examples + +1. Create or edit the appropriate script file +2. Add markers around the new code section: + ```python + # [docs:my-new-section] + client.some_method(...) + # [/docs:my-new-section] + ``` +3. Reference in the MDX file: + ```mdx + import myScript from '!!raw-loader!@site/examples/api/my-script.py'; + + ``` +4. Run the script locally to verify it works + +## Marker Format + +- **Python/Bash**: `# [docs:section-name]` / `# [/docs:section-name]` +- **JavaScript**: `// [docs:section-name]` / `// [/docs:section-name]` + +Section names should be kebab-case and descriptive (e.g., `retain-with-context`, `recall-basic`). diff --git a/hindsight-docs/examples/api/cli-reference.sh b/hindsight-docs/examples/api/cli-reference.sh new file mode 100755 index 00000000..36d7b4f2 --- /dev/null +++ b/hindsight-docs/examples/api/cli-reference.sh @@ -0,0 +1,209 @@ +#!/bin/bash +# CLI Reference examples for Hindsight +# Tests all documented CLI commands and flags +# Run: bash examples/api/cli-reference.sh + +set -e + +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" +BANK_ID="cli-test-bank" +DOC_ID="test-document-001" + +# ============================================================================= +# Setup +# ============================================================================= +hindsight configure --api-url "$HINDSIGHT_URL" + +# Create test data with a known document ID +hindsight memory retain "$BANK_ID" "Alice works at Google as a software engineer" --document-id "$DOC_ID" +hindsight memory retain "$BANK_ID" "Bob is a data scientist who collaborates with Alice" --document-id "$DOC_ID" +hindsight memory retain "$BANK_ID" "Alice and Bob work on machine learning projects" + +# Wait a moment for processing +sleep 2 + +# ============================================================================= +# Configuration (cli.md - Configuration section) +# ============================================================================= + +# [docs:cli-configure] +hindsight configure --api-url http://localhost:8888 +# [/docs:cli-configure] + + +# ============================================================================= +# Core Memory Commands (cli.md - Core Commands section) +# ============================================================================= + +# [docs:cli-retain-basic] +hindsight memory retain $BANK_ID "Alice works at Google as a software engineer" +# [/docs:cli-retain-basic] + + +# [docs:cli-retain-context] +hindsight memory retain $BANK_ID "Bob loves hiking" --context "hobby discussion" +# [/docs:cli-retain-context] + + +# [docs:cli-retain-async] +hindsight memory retain $BANK_ID "Meeting notes" --async +# [/docs:cli-retain-async] + + +# [docs:cli-recall-basic] +hindsight memory recall $BANK_ID "What does Alice do?" +# [/docs:cli-recall-basic] + + +# [docs:cli-recall-options] +hindsight memory recall $BANK_ID "hiking recommendations" \ + --budget high \ + --max-tokens 8192 +# [/docs:cli-recall-options] + + +# [docs:cli-recall-fact-type] +hindsight memory recall $BANK_ID "query" --fact-type world,opinion +# [/docs:cli-recall-fact-type] + + +# [docs:cli-recall-trace] +hindsight memory recall $BANK_ID "query" --trace +# [/docs:cli-recall-trace] + + +# [docs:cli-reflect-basic] +hindsight memory reflect $BANK_ID "What do you know about Alice?" +# [/docs:cli-reflect-basic] + + +# [docs:cli-reflect-context] +hindsight memory reflect $BANK_ID "Should I learn Python?" --context "career advice" +# [/docs:cli-reflect-context] + + +# [docs:cli-reflect-budget] +hindsight memory reflect $BANK_ID "Summarize my week" --budget high +# [/docs:cli-reflect-budget] + + +# ============================================================================= +# Bank Management (cli.md - Bank Management section) +# ============================================================================= + +# [docs:cli-bank-list] +hindsight bank list +# [/docs:cli-bank-list] + + +# [docs:cli-bank-profile] +hindsight bank profile $BANK_ID +# [/docs:cli-bank-profile] + + +# [docs:cli-bank-stats] +hindsight bank stats $BANK_ID +# [/docs:cli-bank-stats] + + +# [docs:cli-bank-name] +hindsight bank name $BANK_ID "My Assistant" +# [/docs:cli-bank-name] + + +# [docs:cli-bank-background] +hindsight bank background $BANK_ID "I am a helpful AI assistant interested in technology" +# [/docs:cli-bank-background] + + +# [docs:cli-bank-background-no-disposition] +hindsight bank background $BANK_ID "Background text" --no-update-disposition +# [/docs:cli-bank-background-no-disposition] + + +# ============================================================================= +# Document Management (cli.md - Document Management section) +# ============================================================================= + +# [docs:cli-document-list] +hindsight document list $BANK_ID +# [/docs:cli-document-list] + + +# [docs:cli-document-get] +hindsight document get $BANK_ID $DOC_ID +# [/docs:cli-document-get] + + +# [docs:cli-document-delete] +# Create a temp document to delete +hindsight memory retain $BANK_ID "Temporary content" --document-id "temp-doc-to-delete" +sleep 1 +hindsight document delete $BANK_ID temp-doc-to-delete +# [/docs:cli-document-delete] + + +# ============================================================================= +# Entity Management (cli.md - Entity Management section) +# ============================================================================= + +# [docs:cli-entity-list] +hindsight entity list $BANK_ID +# [/docs:cli-entity-list] + + +# Get an entity ID from the list output and use it +ENTITY_ID=$(hindsight entity list $BANK_ID -o json 2>/dev/null | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4 || echo "") + +if [ -n "$ENTITY_ID" ]; then + # [docs:cli-entity-get] + hindsight entity get $BANK_ID $ENTITY_ID + # [/docs:cli-entity-get] + + # [docs:cli-entity-regenerate] + hindsight entity regenerate $BANK_ID $ENTITY_ID + # [/docs:cli-entity-regenerate] +else + echo "No entities found yet, skipping entity get/regenerate" +fi + + +# ============================================================================= +# Output Formats (cli.md - Output Formats section) +# ============================================================================= + +# [docs:cli-output-json] +hindsight memory recall $BANK_ID "query" -o json +# [/docs:cli-output-json] + + +# [docs:cli-output-yaml] +hindsight memory recall $BANK_ID "query" -o yaml +# [/docs:cli-output-yaml] + + +# ============================================================================= +# Global Options (cli.md - Global Options section) +# ============================================================================= + +# [docs:cli-verbose] +hindsight memory recall $BANK_ID "Alice" -v +# [/docs:cli-verbose] + + +# [docs:cli-help] +hindsight --help +# [/docs:cli-help] + + +# [docs:cli-version] +hindsight --version +# [/docs:cli-version] + + +# ============================================================================= +# Cleanup +# ============================================================================= +curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/${BANK_ID}" > /dev/null + +echo "cli-reference.sh: All examples passed" diff --git a/hindsight-docs/examples/api/documents.mjs b/hindsight-docs/examples/api/documents.mjs new file mode 100644 index 00000000..be6ba1be --- /dev/null +++ b/hindsight-docs/examples/api/documents.mjs @@ -0,0 +1,68 @@ +#!/usr/bin/env node +/** + * Documents API examples for Hindsight (Node.js) + * Run: node examples/api/documents.mjs + */ +import { HindsightClient, sdk, createClient, createConfig } from '@vectorize-io/hindsight-client'; + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Setup (not shown in docs) +// ============================================================================= +const client = new HindsightClient({ baseUrl: HINDSIGHT_URL }); + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:document-retain] +// Retain with document ID +await client.retain('my-bank', 'Alice presented the Q4 roadmap...', { + document_id: 'meeting-2024-03-15' +}); + +// Batch retain +await client.retainBatch('my-bank', [ + { content: 'Item 1: Product launch delayed to Q2' }, + { content: 'Item 2: New hiring targets announced' }, + { content: 'Item 3: Budget approved for ML team' } +], { documentId: 'meeting-2024-03-15' }); +// [/docs:document-retain] + + +// [docs:document-update] +// Original +await client.retain('my-bank', 'Project deadline: March 31', { + document_id: 'project-plan' +}); + +// Update +await client.retain('my-bank', 'Project deadline: April 15 (extended)', { + document_id: 'project-plan' +}); +// [/docs:document-update] + + +// [docs:document-get] +const apiClient = createClient(createConfig({ baseUrl: 'http://localhost:8888' })); + +// Get document to expand context from recall results +const { data: doc } = await sdk.getDocument({ + client: apiClient, + path: { bank_id: 'my-bank', document_id: 'meeting-2024-03-15' } +}); + +console.log(`Document: ${doc.id}`); +console.log(`Original text: ${doc.original_text}`); +console.log(`Memory count: ${doc.memory_unit_count}`); +console.log(`Created: ${doc.created_at}`); +// [/docs:document-get] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); + +console.log('documents.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/documents.py b/hindsight-docs/examples/api/documents.py new file mode 100644 index 00000000..58c2e668 --- /dev/null +++ b/hindsight-docs/examples/api/documents.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +""" +Documents API examples for Hindsight. +Run: python examples/api/documents.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:document-retain] +# Retain with document ID +client.retain( + bank_id="my-bank", + content="Alice presented the Q4 roadmap...", + document_id="meeting-2024-03-15" +) + +# Batch retain for a document +client.retain_batch( + bank_id="my-bank", + items=[ + {"content": "Item 1: Product launch delayed to Q2"}, + {"content": "Item 2: New hiring targets announced"}, + {"content": "Item 3: Budget approved for ML team"} + ], + document_id="meeting-2024-03-15" +) +# [/docs:document-retain] + + +# [docs:document-update] +# Original +client.retain( + bank_id="my-bank", + content="Project deadline: March 31", + document_id="project-plan" +) + +# Update (deletes old facts, creates new ones) +client.retain( + bank_id="my-bank", + content="Project deadline: April 15 (extended)", + document_id="project-plan" +) +# [/docs:document-update] + + +# [docs:document-get] +import asyncio +from hindsight_client_api import ApiClient, Configuration +from hindsight_client_api.api import DefaultApi + +async def get_document_example(): + config = Configuration(host="http://localhost:8888") + api_client = ApiClient(config) + api = DefaultApi(api_client) + + # Get document to expand context from recall results + doc = await api.get_document( + bank_id="my-bank", + document_id="meeting-2024-03-15" + ) + + print(f"Document: {doc.id}") + print(f"Original text: {doc.original_text}") + print(f"Memory count: {doc.memory_unit_count}") + print(f"Created: {doc.created_at}") + +asyncio.run(get_document_example()) +# [/docs:document-get] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") + +print("documents.py: All examples passed") diff --git a/hindsight-docs/examples/api/main-methods.py b/hindsight-docs/examples/api/main-methods.py new file mode 100644 index 00000000..fed8f864 --- /dev/null +++ b/hindsight-docs/examples/api/main-methods.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +Main Methods overview examples for Hindsight. +Run: python examples/api/main-methods.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# ============================================================================= +# Doc Examples - Retain Section +# ============================================================================= + +# [docs:main-retain] +# Store a single fact +client.retain( + bank_id="my-bank", + content="Alice joined Google in March 2024 as a Senior ML Engineer" +) + +# Store a conversation +conversation = """ +User: What did you work on today? +Assistant: I reviewed the new ML pipeline architecture. +User: How did it look? +Assistant: Promising, but needs better error handling. +""" + +client.retain( + bank_id="my-bank", + content=conversation, + context="Daily standup conversation" +) + +# Batch retain multiple items +client.retain_batch( + bank_id="my-bank", + items=[ + {"content": "Bob prefers Python for data science"}, + {"content": "Alice recommends using pytest for testing"}, + {"content": "The team uses GitHub for code reviews"} + ] +) +# [/docs:main-retain] + + +# ============================================================================= +# Doc Examples - Recall Section +# ============================================================================= + +# [docs:main-recall] +# Basic search +results = client.recall( + bank_id="my-bank", + query="What does Alice do at Google?" +) + +for result in results.results: + print(f"- {result.text}") + +# Search with options +results = client.recall( + bank_id="my-bank", + query="What happened last spring?", + budget="high", # More thorough graph traversal + max_tokens=8192, # Return more context + types=["world"] # Only world facts +) + +# Include entity information +results = client.recall( + bank_id="my-bank", + query="Tell me about Alice", + include_entities=True, + max_entity_tokens=500 +) + +# Check entity details +for entity in results.entities or []: + print(f"Entity: {entity.name}") + print(f"Observations: {entity.observations}") +# [/docs:main-recall] + + +# ============================================================================= +# Doc Examples - Reflect Section +# ============================================================================= + +# [docs:main-reflect] +# Basic reflect +response = client.reflect( + bank_id="my-bank", + query="Should we adopt TypeScript for our backend?" +) + +print(response.text) +print("\nBased on:", len(response.based_on or []), "facts") + +# Reflect with options +response = client.reflect( + bank_id="my-bank", + query="What are Alice's strengths for the team lead role?", + budget="high" # More thorough reasoning +) + +# See which facts influenced the response +for fact in response.based_on or []: + print(f"- {fact.text}") +# [/docs:main-reflect] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") + +print("main-methods.py: All examples passed") diff --git a/hindsight-docs/examples/api/memory-banks.mjs b/hindsight-docs/examples/api/memory-banks.mjs new file mode 100644 index 00000000..6051dda0 --- /dev/null +++ b/hindsight-docs/examples/api/memory-banks.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +/** + * Memory Banks API examples for Hindsight (Node.js) + * Run: node examples/api/memory-banks.mjs + */ +import { HindsightClient } from '@vectorize-io/hindsight-client'; + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Setup (not shown in docs) +// ============================================================================= +const client = new HindsightClient({ baseUrl: HINDSIGHT_URL }); + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:create-bank] +await client.createBank('my-bank', { + name: 'Research Assistant', + background: 'I am a research assistant specializing in machine learning', + disposition: { + skepticism: 4, + literalism: 3, + empathy: 3 + } +}); +// [/docs:create-bank] + + +// [docs:bank-background] +await client.createBank('financial-advisor', { + background: `I am a conservative financial advisor with 20 years of experience. + I prioritize capital preservation over aggressive growth. + I have seen multiple market crashes and believe in diversification.` +}); +// [/docs:bank-background] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); +await fetch(`${HINDSIGHT_URL}/v1/default/banks/financial-advisor`, { method: 'DELETE' }); + +console.log('memory-banks.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/memory-banks.py b/hindsight-docs/examples/api/memory-banks.py new file mode 100644 index 00000000..ba3b61f9 --- /dev/null +++ b/hindsight-docs/examples/api/memory-banks.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Memory Banks API examples for Hindsight. +Run: python examples/api/memory-banks.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:create-bank] +client.create_bank( + bank_id="my-bank", + name="Research Assistant", + background="I am a research assistant specializing in machine learning", + disposition={ + "skepticism": 4, + "literalism": 3, + "empathy": 3 + } +) +# [/docs:create-bank] + + +# [docs:bank-background] +client.create_bank( + bank_id="financial-advisor", + background="""I am a conservative financial advisor with 20 years of experience. + I prioritize capital preservation over aggressive growth. + I have seen multiple market crashes and believe in diversification.""" +) +# [/docs:bank-background] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/financial-advisor") + +print("memory-banks.py: All examples passed") diff --git a/hindsight-docs/examples/api/opinions.py b/hindsight-docs/examples/api/opinions.py new file mode 100644 index 00000000..848977e3 --- /dev/null +++ b/hindsight-docs/examples/api/opinions.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +""" +Opinions API examples for Hindsight. +Run: python examples/api/opinions.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# Seed some data about programming languages +client.retain(bank_id="my-bank", content="Python is widely used for data science and machine learning") +client.retain(bank_id="my-bank", content="Functional programming emphasizes immutability and pure functions") +client.retain(bank_id="my-bank", content="Rust has better memory safety than C++") +client.retain(bank_id="my-bank", content="C++ has a larger ecosystem and more libraries") + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:opinion-form] +# Ask a question - the system may form opinions based on stored facts +answer = client.reflect( + bank_id="my-bank", + query="What do you think about functional programming?" +) + +print(answer.text) +# [/docs:opinion-form] + + +# [docs:opinion-search] +# Search for facts about a topic +results = client.recall( + bank_id="my-bank", + query="programming languages" +) + +for result in results.results: + print(f"- {result.text}") +# [/docs:opinion-search] + + +# [docs:opinion-disposition] +# Create two memory banks with different dispositions +client.create_bank( + bank_id="open-minded", + disposition={"skepticism": 2, "literalism": 2, "empathy": 4} +) + +client.create_bank( + bank_id="conservative", + disposition={"skepticism": 5, "literalism": 5, "empathy": 2} +) + +# Store the same facts to both +facts = [ + "Rust has better memory safety than C++", + "C++ has a larger ecosystem and more libraries", + "Rust compile times are longer than C++" +] +for fact in facts: + client.retain(bank_id="open-minded", content=fact) + client.retain(bank_id="conservative", content=fact) + +# Ask both the same question - different dispositions lead to different responses +q = "Should we rewrite our C++ codebase in Rust?" + +answer1 = client.reflect(bank_id="open-minded", query=q) +print("Open-minded response:", answer1.text[:100], "...") + +answer2 = client.reflect(bank_id="conservative", query=q) +print("Conservative response:", answer2.text[:100], "...") +# [/docs:opinion-disposition] + + +# [docs:opinion-in-reflect] +answer = client.reflect(bank_id="my-bank", query="What language should I learn?") + +print("Response:", answer.text) + +# See which facts influenced the response +if answer.based_on: + print("\nBased on these facts:") + for fact in answer.based_on: + print(f" - {fact.text}") +# [/docs:opinion-in-reflect] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/open-minded") +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/conservative") + +print("opinions.py: All examples passed") diff --git a/hindsight-docs/examples/api/quickstart.mjs b/hindsight-docs/examples/api/quickstart.mjs new file mode 100644 index 00000000..7508476c --- /dev/null +++ b/hindsight-docs/examples/api/quickstart.mjs @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/** + * Quickstart examples for Hindsight API (Node.js) + * Run: node examples/api/quickstart.mjs + */ + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:quickstart-full] +import { HindsightClient } from '@vectorize-io/hindsight-client'; + +const client = new HindsightClient({ baseUrl: 'http://localhost:8888' }); + +// Retain: Store information +await client.retain('my-bank', 'Alice works at Google as a software engineer'); + +// Recall: Search memories +await client.recall('my-bank', 'What does Alice do?'); + +// Reflect: Generate response +await client.reflect('my-bank', 'Tell me about Alice'); +// [/docs:quickstart-full] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); + +console.log('quickstart.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/quickstart.py b/hindsight-docs/examples/api/quickstart.py new file mode 100644 index 00000000..b09c3580 --- /dev/null +++ b/hindsight-docs/examples/api/quickstart.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +""" +Quickstart examples for Hindsight API. +Run: python examples/api/quickstart.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:quickstart-full] +from hindsight_client import Hindsight + +client = Hindsight(base_url="http://localhost:8888") + +# Retain: Store information +client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer") + +# Recall: Search memories +client.recall(bank_id="my-bank", query="What does Alice do?") + +# Reflect: Generate disposition-aware response +client.reflect(bank_id="my-bank", query="Tell me about Alice") +# [/docs:quickstart-full] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") + +print("quickstart.py: All examples passed") diff --git a/hindsight-docs/examples/api/quickstart.sh b/hindsight-docs/examples/api/quickstart.sh new file mode 100755 index 00000000..26d444f7 --- /dev/null +++ b/hindsight-docs/examples/api/quickstart.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Quickstart examples for Hindsight CLI +# Run: bash examples/api/quickstart.sh + +set -e + +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:quickstart-full] +# Retain: Store information +hindsight memory retain my-bank "Alice works at Google as a software engineer" + +# Recall: Search memories +hindsight memory recall my-bank "What does Alice do?" + +# Reflect: Generate response +hindsight memory reflect my-bank "Tell me about Alice" +# [/docs:quickstart-full] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/my-bank" > /dev/null + +echo "quickstart.sh: All examples passed" diff --git a/hindsight-docs/examples/api/recall.mjs b/hindsight-docs/examples/api/recall.mjs new file mode 100644 index 00000000..592c820e --- /dev/null +++ b/hindsight-docs/examples/api/recall.mjs @@ -0,0 +1,61 @@ +#!/usr/bin/env node +/** + * Recall API examples for Hindsight (Node.js) + * Run: node examples/api/recall.mjs + */ +import { HindsightClient } from '@vectorize-io/hindsight-client'; + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Setup (not shown in docs) +// ============================================================================= +const client = new HindsightClient({ baseUrl: HINDSIGHT_URL }); + +// Seed some data for recall examples +await client.retain('my-bank', 'Alice works at Google as a software engineer'); +await client.retain('my-bank', 'Alice loves hiking on weekends'); +await client.retain('my-bank', 'Bob is a data scientist who works with Alice'); + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:recall-basic] +const response = await client.recall('my-bank', 'What does Alice do?'); +for (const r of response.results) { + console.log(`${r.text} (score: ${r.weight})`); +} +// [/docs:recall-basic] + + +// [docs:recall-with-options] +const detailedResponse = await client.recall('my-bank', 'What does Alice do?', { + types: ['world', 'experience'], + budget: 'high', + maxTokens: 8000, + trace: true +}); + +// Access results +for (const r of detailedResponse.results) { + console.log(`${r.text} (score: ${r.weight})`); +} +// [/docs:recall-with-options] + + +// [docs:recall-budget-levels] +// Quick lookup +const quickResults = await client.recall('my-bank', "Alice's email", { budget: 'low' }); + +// Deep exploration +const deepResults = await client.recall('my-bank', 'How are Alice and Bob connected?', { budget: 'high' }); +// [/docs:recall-budget-levels] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); + +console.log('recall.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/recall.py b/hindsight-docs/examples/api/recall.py new file mode 100644 index 00000000..aed8bdfd --- /dev/null +++ b/hindsight-docs/examples/api/recall.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +""" +Recall API examples for Hindsight. +Run: python examples/api/recall.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# Seed some data for recall examples +client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer") +client.retain(bank_id="my-bank", content="Alice loves hiking on weekends") +client.retain(bank_id="my-bank", content="Bob is a data scientist who works with Alice") + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:recall-basic] +response = client.recall(bank_id="my-bank", query="What does Alice do?") +for r in response.results: + print(f"- {r.text}") +# [/docs:recall-basic] + + +# [docs:recall-with-options] +response = client.recall( + bank_id="my-bank", + query="What does Alice do?", + types=["world", "experience"], + budget="high", + max_tokens=8000, + trace=True, + include_entities=True, + max_entity_tokens=500 +) + +# Access results +for r in response.results: + print(f"- {r.text}") + +# Access entity observations (if include_entities=True) +if response.entities: + for entity_id, entity in response.entities.items(): + print(f"Entity: {entity.canonical_name}") +# [/docs:recall-with-options] + + +# [docs:recall-world-only] +# Only world facts (objective information) +world_facts = client.recall( + bank_id="my-bank", + query="Where does Alice work?", + types=["world"] +) +# [/docs:recall-world-only] + + +# [docs:recall-experience-only] +# Only experience (conversations and events) +experience = client.recall( + bank_id="my-bank", + query="What have I recommended?", + types=["experience"] +) +# [/docs:recall-experience-only] + + +# [docs:recall-opinions-only] +# Only opinions (formed beliefs) +opinions = client.recall( + bank_id="my-bank", + query="What do I think about Python?", + types=["opinion"] +) +# [/docs:recall-opinions-only] + + +# [docs:recall-token-budget] +# Fill up to 4K tokens of context with relevant memories +results = client.recall(bank_id="my-bank", query="What do I know about Alice?", max_tokens=4096) + +# Smaller budget for quick lookups +results = client.recall(bank_id="my-bank", query="Alice's email", max_tokens=500) +# [/docs:recall-token-budget] + + +# [docs:recall-include-entities] +response = client.recall( + bank_id="my-bank", + query="What does Alice do?", + max_tokens=4096, # Budget for memories + include_entities=True, + max_entity_tokens=1000 # Budget for entity observations +) + +# Access the additional context +entities = response.entities or [] +# [/docs:recall-include-entities] + + +# [docs:recall-budget-levels] +# Quick lookup +results = client.recall(bank_id="my-bank", query="Alice's email", budget="low") + +# Deep exploration +results = client.recall(bank_id="my-bank", query="How are Alice and Bob connected?", budget="high") +# [/docs:recall-budget-levels] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") + +print("recall.py: All examples passed") diff --git a/hindsight-docs/examples/api/recall.sh b/hindsight-docs/examples/api/recall.sh new file mode 100755 index 00000000..241067cd --- /dev/null +++ b/hindsight-docs/examples/api/recall.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Recall API examples for Hindsight CLI +# Run: bash examples/api/recall.sh + +set -e + +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +hindsight memory retain my-bank "Alice works at Google as a software engineer" +hindsight memory retain my-bank "Alice loves hiking on weekends" + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:recall-basic] +hindsight memory recall my-bank "What does Alice do?" +# [/docs:recall-basic] + + +# [docs:recall-with-options] +hindsight memory recall my-bank "hiking recommendations" \ + --budget high \ + --max-tokens 8192 +# [/docs:recall-with-options] + + +# [docs:recall-fact-type] +hindsight memory recall my-bank "query" --fact-type world,opinion +# [/docs:recall-fact-type] + + +# [docs:recall-trace] +hindsight memory recall my-bank "query" --trace +# [/docs:recall-trace] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/my-bank" > /dev/null + +echo "recall.sh: All examples passed" diff --git a/hindsight-docs/examples/api/reflect.mjs b/hindsight-docs/examples/api/reflect.mjs new file mode 100644 index 00000000..695c61fe --- /dev/null +++ b/hindsight-docs/examples/api/reflect.mjs @@ -0,0 +1,78 @@ +#!/usr/bin/env node +/** + * Reflect API examples for Hindsight (Node.js) + * Run: node examples/api/reflect.mjs + */ +import { HindsightClient } from '@vectorize-io/hindsight-client'; + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Setup (not shown in docs) +// ============================================================================= +const client = new HindsightClient({ baseUrl: HINDSIGHT_URL }); + +// Seed some data for reflect examples +await client.retain('my-bank', 'Alice works at Google as a software engineer'); +await client.retain('my-bank', 'Alice has been working there for 5 years'); +await client.retain('my-bank', 'Alice recently got promoted to senior engineer'); + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:reflect-basic] +await client.reflect('my-bank', 'What should I know about Alice?'); +// [/docs:reflect-basic] + + +// [docs:reflect-with-params] +const response = await client.reflect('my-bank', 'What do you think about remote work?', { + budget: 'mid', + context: "We're considering a hybrid work policy" +}); +// [/docs:reflect-with-params] + + +// [docs:reflect-with-context] +// Context helps the LLM understand the current situation +const contextResponse = await client.reflect('my-bank', 'What do you think about the proposal?', { + context: "We're in a budget review meeting discussing Q4 spending" +}); +// [/docs:reflect-with-context] + + +// [docs:reflect-disposition] +// Create a bank with specific disposition +await client.createBank('cautious-advisor', { + background: 'I am a risk-aware financial advisor', + disposition: { + skepticism: 5, + literalism: 4, + empathy: 2 + } +}); + +// Reflect responses will reflect this disposition +const advisorResponse = await client.reflect('cautious-advisor', 'Should I invest in crypto?'); +// [/docs:reflect-disposition] + + +// [docs:reflect-sources] +const sourcesResponse = await client.reflect('my-bank', 'Tell me about Alice'); + +console.log('Response:', sourcesResponse.text); +console.log('\nBased on:'); +for (const fact of sourcesResponse.based_on || []) { + console.log(` - [${fact.type}] ${fact.text}`); +} +// [/docs:reflect-sources] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); +await fetch(`${HINDSIGHT_URL}/v1/default/banks/cautious-advisor`, { method: 'DELETE' }); + +console.log('reflect.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/reflect.py b/hindsight-docs/examples/api/reflect.py new file mode 100644 index 00000000..1487263c --- /dev/null +++ b/hindsight-docs/examples/api/reflect.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +Reflect API examples for Hindsight. +Run: python examples/api/reflect.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# Seed some data for reflect examples +client.retain(bank_id="my-bank", content="Alice works at Google as a software engineer") +client.retain(bank_id="my-bank", content="Alice has been working there for 5 years") +client.retain(bank_id="my-bank", content="Alice recently got promoted to senior engineer") + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:reflect-basic] +client.reflect(bank_id="my-bank", query="What should I know about Alice?") +# [/docs:reflect-basic] + + +# [docs:reflect-with-params] +response = client.reflect( + bank_id="my-bank", + query="What do you think about remote work?", + budget="mid", + context="We're considering a hybrid work policy" +) +# [/docs:reflect-with-params] + + +# [docs:reflect-with-context] +# Context is passed to the LLM to help it understand the situation +response = client.reflect( + bank_id="my-bank", + query="What do you think about the proposal?", + context="We're in a budget review meeting discussing Q4 spending" +) +# [/docs:reflect-with-context] + + +# [docs:reflect-disposition] +# Create a bank with specific disposition +client.create_bank( + bank_id="cautious-advisor", + background="I am a risk-aware financial advisor", + disposition={ + "skepticism": 5, # Very skeptical of claims + "literalism": 4, # Focuses on exact requirements + "empathy": 2 # Prioritizes facts over feelings + } +) + +# Reflect responses will reflect this disposition +response = client.reflect( + bank_id="cautious-advisor", + query="Should I invest in crypto?" +) +# Response will likely emphasize risks and caution +# [/docs:reflect-disposition] + + +# [docs:reflect-sources] +response = client.reflect(bank_id="my-bank", query="Tell me about Alice") + +print("Response:", response.text) +print("\nBased on:") +for fact in response.based_on or []: + print(f" - [{fact.type}] {fact.text}") +# [/docs:reflect-sources] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/cautious-advisor") + +print("reflect.py: All examples passed") diff --git a/hindsight-docs/examples/api/reflect.sh b/hindsight-docs/examples/api/reflect.sh new file mode 100755 index 00000000..30e46cc5 --- /dev/null +++ b/hindsight-docs/examples/api/reflect.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Reflect API examples for Hindsight CLI +# Run: bash examples/api/reflect.sh + +set -e + +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +hindsight memory retain my-bank "Alice works at Google as a software engineer" +hindsight memory retain my-bank "Alice has been working there for 5 years" + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:reflect-basic] +hindsight memory reflect my-bank "What do you know about Alice?" +# [/docs:reflect-basic] + + +# [docs:reflect-with-context] +hindsight memory reflect my-bank "Should I learn Python?" --context "career advice" +# [/docs:reflect-with-context] + + +# [docs:reflect-high-budget] +hindsight memory reflect my-bank "Summarize my week" --budget high +# [/docs:reflect-high-budget] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/my-bank" > /dev/null + +echo "reflect.sh: All examples passed" diff --git a/hindsight-docs/examples/api/retain.mjs b/hindsight-docs/examples/api/retain.mjs new file mode 100644 index 00000000..e41322b3 --- /dev/null +++ b/hindsight-docs/examples/api/retain.mjs @@ -0,0 +1,58 @@ +#!/usr/bin/env node +/** + * Retain API examples for Hindsight (Node.js) + * Run: node examples/api/retain.mjs + */ +import { HindsightClient } from '@vectorize-io/hindsight-client'; + +const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888'; + +// ============================================================================= +// Setup (not shown in docs) +// ============================================================================= +const client = new HindsightClient({ baseUrl: HINDSIGHT_URL }); + +// ============================================================================= +// Doc Examples +// ============================================================================= + +// [docs:retain-basic] +await client.retain('my-bank', 'Alice works at Google as a software engineer'); +// [/docs:retain-basic] + + +// [docs:retain-with-context] +await client.retain('my-bank', 'Alice got promoted to senior engineer', { + context: 'career update', + timestamp: '2024-03-15T10:00:00Z' +}); +// [/docs:retain-with-context] + + +// [docs:retain-batch] +await client.retainBatch('my-bank', [ + { content: 'Alice works at Google', context: 'career' }, + { content: 'Bob is a data scientist at Meta', context: 'career' }, + { content: 'Alice and Bob are friends', context: 'relationship' } +], { documentId: 'conversation_001' }); +// [/docs:retain-batch] + + +// [docs:retain-async] +// Start async ingestion (returns immediately) +await client.retainBatch('my-bank', [ + { content: 'Large batch item 1' }, + { content: 'Large batch item 2' }, +], { + documentId: 'large-doc', + async: true +}); +// [/docs:retain-async] + + +// ============================================================================= +// Cleanup (not shown in docs) +// ============================================================================= +await fetch(`${HINDSIGHT_URL}/v1/default/banks/my-bank`, { method: 'DELETE' }); + +console.log('retain.mjs: All examples passed'); diff --git a/hindsight-docs/examples/api/retain.py b/hindsight-docs/examples/api/retain.py new file mode 100644 index 00000000..82d11b3b --- /dev/null +++ b/hindsight-docs/examples/api/retain.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +""" +Retain API examples for Hindsight. +Run: python examples/api/retain.py +""" +import os +import requests + +HINDSIGHT_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") + +# ============================================================================= +# Setup (not shown in docs) +# ============================================================================= +from hindsight_client import Hindsight + +client = Hindsight(base_url=HINDSIGHT_URL) + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:retain-basic] +client.retain( + bank_id="my-bank", + content="Alice works at Google as a software engineer" +) +# [/docs:retain-basic] + + +# [docs:retain-with-context] +client.retain( + bank_id="my-bank", + content="Alice got promoted to senior engineer", + context="career update", + timestamp="2024-03-15T10:00:00Z" +) +# [/docs:retain-with-context] + + +# [docs:retain-batch] +client.retain_batch( + bank_id="my-bank", + items=[ + {"content": "Alice works at Google", "context": "career"}, + {"content": "Bob is a data scientist at Meta", "context": "career"}, + {"content": "Alice and Bob are friends", "context": "relationship"} + ], + document_id="conversation_001" +) +# [/docs:retain-batch] + + +# [docs:retain-async] +# Start async ingestion (returns immediately) +result = client.retain_batch( + bank_id="my-bank", + items=[ + {"content": "Large batch item 1"}, + {"content": "Large batch item 2"}, + ], + document_id="large-doc", + retain_async=True +) + +# Check if it was processed asynchronously +print(result.var_async) # True +# [/docs:retain-async] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +requests.delete(f"{HINDSIGHT_URL}/v1/default/banks/my-bank") + +print("retain.py: All examples passed") diff --git a/hindsight-docs/examples/api/retain.sh b/hindsight-docs/examples/api/retain.sh new file mode 100755 index 00000000..0b2ad2bf --- /dev/null +++ b/hindsight-docs/examples/api/retain.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Retain API examples for Hindsight CLI +# Run: bash examples/api/retain.sh + +set -e + +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" + +# ============================================================================= +# Doc Examples +# ============================================================================= + +# [docs:retain-basic] +hindsight memory retain my-bank "Alice works at Google as a software engineer" +# [/docs:retain-basic] + + +# [docs:retain-with-context] +hindsight memory retain my-bank "Alice got promoted" \ + --context "career update" +# [/docs:retain-with-context] + + +# [docs:retain-async] +hindsight memory retain my-bank "Meeting notes" --async +# [/docs:retain-async] + + +# ============================================================================= +# Cleanup (not shown in docs) +# ============================================================================= +curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/my-bank" > /dev/null + +echo "retain.sh: All examples passed" diff --git a/hindsight-docs/examples/run-examples.sh b/hindsight-docs/examples/run-examples.sh new file mode 100755 index 00000000..a8b0d59b --- /dev/null +++ b/hindsight-docs/examples/run-examples.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Run all documentation example scripts +# Usage: ./examples/run-examples.sh [python|node|cli|all] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}" + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +passed=0 +failed=0 +skipped=0 + +run_python_examples() { + echo -e "${YELLOW}Running Python examples...${NC}" + for f in "$SCRIPT_DIR"/api/*.py; do + if [ -f "$f" ]; then + echo -n " $(basename "$f"): " + if python "$f" 2>&1; then + echo -e "${GREEN}PASSED${NC}" + ((passed++)) + else + echo -e "${RED}FAILED${NC}" + ((failed++)) + fi + fi + done +} + +run_node_examples() { + echo -e "${YELLOW}Running Node.js examples...${NC}" + for f in "$SCRIPT_DIR"/api/*.mjs; do + if [ -f "$f" ]; then + echo -n " $(basename "$f"): " + if node "$f" 2>&1; then + echo -e "${GREEN}PASSED${NC}" + ((passed++)) + else + echo -e "${RED}FAILED${NC}" + ((failed++)) + fi + fi + done +} + +run_cli_examples() { + echo -e "${YELLOW}Running CLI examples...${NC}" + + # Check if hindsight CLI is available + if ! command -v hindsight &> /dev/null; then + echo -e " ${YELLOW}SKIPPED (hindsight CLI not installed)${NC}" + for f in "$SCRIPT_DIR"/api/*.sh; do + if [ -f "$f" ]; then + ((skipped++)) + fi + done + return + fi + + for f in "$SCRIPT_DIR"/api/*.sh; do + if [ -f "$f" ]; then + echo -n " $(basename "$f"): " + if bash "$f" 2>&1; then + echo -e "${GREEN}PASSED${NC}" + ((passed++)) + else + echo -e "${RED}FAILED${NC}" + ((failed++)) + fi + fi + done +} + +# Wait for server to be ready +wait_for_server() { + echo "Waiting for Hindsight server at $HINDSIGHT_URL..." + for i in {1..30}; do + if curl -s "$HINDSIGHT_URL/health" > /dev/null 2>&1; then + echo "Server is ready!" + return 0 + fi + sleep 1 + done + echo "Server not available after 30 seconds" + return 1 +} + +# Main +case "${1:-all}" in + python) + wait_for_server + run_python_examples + ;; + node) + wait_for_server + run_node_examples + ;; + cli) + wait_for_server + run_cli_examples + ;; + all) + wait_for_server + run_python_examples + echo "" + run_node_examples + echo "" + run_cli_examples + ;; + *) + echo "Usage: $0 [python|node|cli|all]" + exit 1 + ;; +esac + +echo "" +echo "========================================" +echo -e "Results: ${GREEN}$passed passed${NC}, ${RED}$failed failed${NC}, ${YELLOW}$skipped skipped${NC}" +echo "========================================" + +if [ $failed -gt 0 ]; then + exit 1 +fi diff --git a/hindsight-docs/package.json b/hindsight-docs/package.json index 71d7c87e..2747dc07 100644 --- a/hindsight-docs/package.json +++ b/hindsight-docs/package.json @@ -22,6 +22,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", + "raw-loader": "^4.0.2", "react": "^19.0.0", "react-dom": "^19.0.0", "redocusaurus": "^2.5.0" diff --git a/hindsight-docs/src/components/CodeSnippet.tsx b/hindsight-docs/src/components/CodeSnippet.tsx new file mode 100644 index 00000000..8c8a1475 --- /dev/null +++ b/hindsight-docs/src/components/CodeSnippet.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import CodeBlock from '@theme/CodeBlock'; + +interface CodeSnippetProps { + /** Raw file content (use raw-loader to import) */ + code: string; + /** Section marker name (e.g., "retain-basic" for [docs:retain-basic]) */ + section: string; + /** Language for syntax highlighting */ + language: string; + /** Optional title for the code block */ + title?: string; +} + +/** + * Extracts a marked section from source code. + * + * Markers are in the format: + * - Start: `# [docs:section-name]` (Python/Bash) or `// [docs:section-name]` (JS/TS) + * - End: `# [/docs:section-name]` (Python/Bash) or `// [/docs:section-name]` (JS/TS) + */ +function extractSection(code: string, section: string): string { + // Match both Python/Bash (#) and JS/TS (//) comment styles + const startPattern = new RegExp(`(?:#|//)\\s*\\[docs:${section}\\]`); + const endPattern = new RegExp(`(?:#|//)\\s*\\[/docs:${section}\\]`); + + const lines = code.split('\n'); + let inSection = false; + const sectionLines: string[] = []; + + for (const line of lines) { + if (startPattern.test(line)) { + inSection = true; + continue; + } + if (endPattern.test(line)) { + inSection = false; + continue; + } + if (inSection) { + sectionLines.push(line); + } + } + + if (sectionLines.length === 0) { + console.warn(`CodeSnippet: Section "${section}" not found in code`); + return `// Section "${section}" not found`; + } + + // Trim leading/trailing empty lines and normalize indentation + return trimAndNormalize(sectionLines); +} + +/** + * Trims leading/trailing empty lines and removes common leading indentation. + */ +function trimAndNormalize(lines: string[]): string { + // Remove leading empty lines + while (lines.length > 0 && lines[0].trim() === '') { + lines.shift(); + } + // Remove trailing empty lines + while (lines.length > 0 && lines[lines.length - 1].trim() === '') { + lines.pop(); + } + + if (lines.length === 0) return ''; + + // Find minimum indentation (ignoring empty lines) + const nonEmptyLines = lines.filter(l => l.trim() !== ''); + if (nonEmptyLines.length === 0) return ''; + + const minIndent = Math.min( + ...nonEmptyLines.map(line => { + const match = line.match(/^(\s*)/); + return match ? match[1].length : 0; + }) + ); + + // Remove common indentation + return lines + .map(line => line.slice(minIndent)) + .join('\n'); +} + +/** + * CodeSnippet component for embedding code from example files. + * + * Usage in MDX: + * ```mdx + * import CodeSnippet from '@site/src/components/CodeSnippet'; + * import retainPy from '!!raw-loader!@site/examples/api/retain.py'; + * + * + * ``` + */ +export default function CodeSnippet({ + code, + section, + language, + title +}: CodeSnippetProps): React.ReactElement { + const extractedCode = extractSection(code, section); + + return ( + + {extractedCode} + + ); +} diff --git a/package-lock.json b/package-lock.json index f7447d0d..6d07dc28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,6 +105,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", + "raw-loader": "^4.0.2", "react": "^19.0.0", "react-dom": "^19.0.0", "redocusaurus": "^2.5.0" @@ -24244,6 +24245,44 @@ "node": ">=0.10.0" } }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/raw-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", diff --git a/uv.lock b/uv.lock index fff8eb16..8a1150bc 100644 --- a/uv.lock +++ b/uv.lock @@ -1141,7 +1141,7 @@ wheels = [ [[package]] name = "hindsight-all" -version = "0.1.7" +version = "0.1.8" source = { editable = "hindsight" } dependencies = [ { name = "hindsight-api" }, @@ -1165,7 +1165,7 @@ provides-extras = ["test"] [[package]] name = "hindsight-api" -version = "0.1.7" +version = "0.1.8" source = { editable = "hindsight-api" } dependencies = [ { name = "alembic" }, @@ -1269,7 +1269,7 @@ dev = [ [[package]] name = "hindsight-client" -version = "0.1.7" +version = "0.1.8" source = { editable = "hindsight-clients/python" } dependencies = [ { name = "aiohttp" }, @@ -1284,6 +1284,7 @@ dependencies = [ test = [ { name = "pytest" }, { name = "pytest-asyncio" }, + { name = "requests" }, ] [package.metadata] @@ -1294,6 +1295,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'", specifier = ">=7.0.0" }, { name = "pytest-asyncio", marker = "extra == 'test'", specifier = ">=0.21.0" }, { name = "python-dateutil", specifier = ">=2.8.2" }, + { name = "requests", marker = "extra == 'test'", specifier = ">=2.28.0" }, { name = "typing-extensions", specifier = ">=4.7.1" }, { name = "urllib3", specifier = ">=2.1.0,<3.0.0" }, ] @@ -1301,7 +1303,7 @@ provides-extras = ["test"] [[package]] name = "hindsight-dev" -version = "0.1.7" +version = "0.1.8" source = { editable = "hindsight-dev" } dependencies = [ { name = "hindsight-api" },