From ecf609c8aa3491f246c958aa2b7b47f9cca90a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Mon, 2 Mar 2026 15:40:37 +0100 Subject: [PATCH] feat: add Pydantic AI integration to CI, release pipeline, and docs (#467) * feat: add Pydantic AI integration to CI, release pipeline, and docs - Add test-pydantic-ai-integration job to CI (test.yml) - Add build, publish, and artifact steps to release workflow (release.yml) - Add hindsight-integrations/pydantic-ai to release.sh version bumping - Add Pydantic AI documentation page (sdks/integrations/pydantic-ai.md) - Add Pydantic AI entry to sidebar with icon * docs: remove Requirements section from pydantic-ai integration page --- .github/workflows/release.yml | 12 ++ .github/workflows/test.yml | 29 +++ .../docs/sdks/integrations/pydantic-ai.md | 184 ++++++++++++++++++ hindsight-docs/sidebars.ts | 8 + .../static/img/icons/pydanticai.png | Bin 0 -> 802 bytes scripts/release.sh | 4 +- 6 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 hindsight-docs/docs/sdks/integrations/pydantic-ai.md create mode 100644 hindsight-docs/static/img/icons/pydanticai.png diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 520e23d0..ce7efc9c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,10 @@ jobs: working-directory: ./hindsight-integrations/crewai run: uv build --out-dir dist + - name: Build hindsight-pydantic-ai + working-directory: ./hindsight-integrations/pydantic-ai + run: uv build --out-dir dist + # Publish in order (client and api first, then hindsight-all which depends on them) - name: Publish hindsight-client to PyPI uses: pypa/gh-action-pypi-publish@release/v1 @@ -87,6 +91,12 @@ jobs: packages-dir: ./hindsight-integrations/crewai/dist skip-existing: true + - name: Publish hindsight-pydantic-ai to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: ./hindsight-integrations/pydantic-ai/dist + skip-existing: true + # Upload artifacts for GitHub release - name: Upload artifacts uses: actions/upload-artifact@v4 @@ -99,6 +109,7 @@ jobs: hindsight-integrations/litellm/dist/* hindsight-embed/dist/* hindsight-integrations/crewai/dist/* + hindsight-integrations/pydantic-ai/dist/* retention-days: 1 release-typescript-client: @@ -629,6 +640,7 @@ jobs: cp artifacts/python-packages/hindsight-api/dist/* release-assets/ || true cp artifacts/python-packages/hindsight/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-integrations/litellm/dist/* release-assets/ || true + cp artifacts/python-packages/hindsight-integrations/pydantic-ai/dist/* release-assets/ || true cp artifacts/python-packages/hindsight-embed/dist/* release-assets/ || true # TypeScript client cp artifacts/typescript-client/*.tgz release-assets/ || true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4644a54b..f574903e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1162,6 +1162,35 @@ jobs: working-directory: ./hindsight-integrations/litellm run: uv run pytest tests -v + test-pydantic-ai-integration: + runs-on: ubuntu-latest + + 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: Build pydantic-ai integration + working-directory: ./hindsight-integrations/pydantic-ai + run: uv build + + - name: Install dependencies + working-directory: ./hindsight-integrations/pydantic-ai + run: uv sync --frozen + + - name: Run tests + working-directory: ./hindsight-integrations/pydantic-ai + run: uv run pytest tests -v + test-embed: runs-on: ubuntu-latest env: diff --git a/hindsight-docs/docs/sdks/integrations/pydantic-ai.md b/hindsight-docs/docs/sdks/integrations/pydantic-ai.md new file mode 100644 index 00000000..8f6e6baa --- /dev/null +++ b/hindsight-docs/docs/sdks/integrations/pydantic-ai.md @@ -0,0 +1,184 @@ +--- +sidebar_position: 6 +--- + +# Pydantic AI + +Persistent memory tools for [Pydantic AI](https://ai.pydantic.dev/) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — all async-native with no thread-pool hacks. + +## Features + +- **Async-Native Tools** — Uses Pydantic AI's async tool interface directly (`aretain`, `arecall`, `areflect`) +- **Memory Instructions** — Auto-inject relevant memories into every agent run via `instructions=[...]` +- **Three Memory Tools** — Retain (store), Recall (search), Reflect (synthesize) — include any combination +- **Simple Configuration** — Configure once globally, or pass a client directly +- **Lightweight** — Depends on `pydantic-ai-slim` to avoid pulling in all model providers + +## Installation + +```bash +pip install hindsight-pydantic-ai +``` + +## Quick Start + +```python +from hindsight_client import Hindsight +from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions +from pydantic_ai import Agent + +client = Hindsight(base_url="http://localhost:8888") + +agent = Agent( + "openai:gpt-4o", + tools=create_hindsight_tools(client=client, bank_id="user-123"), + instructions=[memory_instructions(client=client, bank_id="user-123")], +) + +result = await agent.run("What do you remember about my preferences?") +print(result.output) +``` + +The agent now has three tools it can call: + +- **`hindsight_retain`** — Store information to long-term memory +- **`hindsight_recall`** — Search long-term memory for relevant facts +- **`hindsight_reflect`** — Synthesize a reasoned answer from memories + +The `memory_instructions` callable automatically recalls relevant memories and injects them into the system prompt on every run. + +## Tools Only (No Auto-Injection) + +If you want the agent to decide when to use memory rather than always injecting context: + +```python +agent = Agent( + "openai:gpt-4o", + tools=create_hindsight_tools(client=client, bank_id="user-123"), +) +``` + +## Instructions Only (No Tools) + +If you just want memories auto-injected without giving the agent explicit memory tools: + +```python +agent = Agent( + "openai:gpt-4o", + instructions=[memory_instructions(client=client, bank_id="user-123")], +) +``` + +## Selecting Tools + +Include only the tools you need: + +```python +tools = create_hindsight_tools( + client=client, + bank_id="user-123", + include_retain=True, + include_recall=True, + include_reflect=False, # Omit reflect +) +``` + +## Global Configuration + +Instead of passing a client to every call, configure once: + +```python +from hindsight_pydantic_ai import configure, create_hindsight_tools + +configure( + hindsight_api_url="http://localhost:8888", + api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var + budget="mid", # Recall budget: low/mid/high + max_tokens=4096, # Max tokens for recall results + tags=["env:prod"], # Tags for stored memories + recall_tags=["scope:global"], # Tags to filter recall + recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict +) + +# Now create tools without passing client — uses global config +tools = create_hindsight_tools(bank_id="user-123") +``` + +## Per-Tool Overrides + +Constructor arguments override global configuration: + +```python +tools = create_hindsight_tools( + bank_id="user-123", + budget="high", # Override global budget + max_tokens=8192, # Override global max_tokens + tags=["session:abc"], # Override global tags +) +``` + +## Memory Instructions Options + +Customize what memories get injected and how: + +```python +instructions_fn = memory_instructions( + client=client, + bank_id="user-123", + query="relevant context about the user", # What to search for + budget="low", # Keep it fast + max_results=5, # Limit injected memories + max_tokens=4096, # Max recall tokens + prefix="Relevant memories:\n", # Text before the memory list + tags=["scope:global"], # Filter by tags + tags_match="any", # Tag match mode +) +``` + +## API Reference + +### `create_hindsight_tools()` + +| Parameter | Default | Description | +|---|---|---| +| `bank_id` | *required* | Hindsight memory bank ID | +| `client` | `None` | Pre-configured Hindsight client | +| `hindsight_api_url` | `None` | API URL (used if no client provided) | +| `api_key` | `None` | API key (used if no client provided) | +| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) | +| `max_tokens` | `4096` | Maximum tokens for recall results | +| `tags` | `None` | Tags applied when storing memories | +| `recall_tags` | `None` | Tags to filter when searching | +| `recall_tags_match` | `"any"` | Tag matching mode | +| `include_retain` | `True` | Include the retain (store) tool | +| `include_recall` | `True` | Include the recall (search) tool | +| `include_reflect` | `True` | Include the reflect (synthesize) tool | + +### `memory_instructions()` + +| Parameter | Default | Description | +|---|---|---| +| `bank_id` | *required* | Hindsight memory bank ID | +| `client` | `None` | Pre-configured Hindsight client | +| `hindsight_api_url` | `None` | API URL (used if no client provided) | +| `api_key` | `None` | API key (used if no client provided) | +| `query` | `"relevant context about the user"` | Recall query for memory injection | +| `budget` | `"low"` | Recall budget level | +| `max_results` | `5` | Maximum memories to inject | +| `max_tokens` | `4096` | Maximum tokens for recall results | +| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list | +| `tags` | `None` | Tags to filter recall results | +| `tags_match` | `"any"` | Tag matching mode | + +### `configure()` + +| Parameter | Default | Description | +|---|---|---| +| `hindsight_api_url` | Production API | Hindsight API URL | +| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication | +| `budget` | `"mid"` | Default recall budget level | +| `max_tokens` | `4096` | Default max tokens for recall | +| `tags` | `None` | Default tags for retain operations | +| `recall_tags` | `None` | Default tags to filter recall | +| `recall_tags_match` | `"any"` | Default tag matching mode | +| `verbose` | `False` | Enable verbose logging | diff --git a/hindsight-docs/sidebars.ts b/hindsight-docs/sidebars.ts index 817b26bf..bca8e41f 100644 --- a/hindsight-docs/sidebars.ts +++ b/hindsight-docs/sidebars.ts @@ -226,6 +226,14 @@ const sidebars: SidebarsConfig = { icon: "/img/icons/crewai.png" } }, + { + type: 'doc', + id: 'sdks/integrations/pydantic-ai', + label: 'Pydantic AI', + customProps: { + icon: "/img/icons/pydanticai.png" + } + }, { type: 'doc', id: 'sdks/integrations/skills', diff --git a/hindsight-docs/static/img/icons/pydanticai.png b/hindsight-docs/static/img/icons/pydanticai.png new file mode 100644 index 0000000000000000000000000000000000000000..02e7fb6418220ce293c06e36fc21ca1e879e7e73 GIT binary patch literal 802 zcmV+-1Ks?IP)>UH=w@@|^LXijLMM*0%CKtw>Mx! z*7>hg7^;#h zDM4?jZx-CXF;y)l>v*|#T+RetA4rvC=jD0MZxUZ!UO!OOyD-hS8a7%=QB>akYk87;Cvj>)cuBT$Q3!oUd9wEr@ znqLO4)t*_5q7M>|nMFS-X1A>xbkoFV`9v<7h^CS#QTL{MWOffgHR$ae1vpj?_CSp0 z8DIk2TgjD{#*efFas>8OfPN?@rMs5M_k}hSs@2S1!k37%l-A;L!Gg8wv7SCm>DEQ_RR`#+l=) zg-QUhfC+4H*t{S<81R5muDIxS8R+j;BIY4S{1t#b*b59`IpV9$YL2aO7^AyN<4w{n z-yMw$tAjs~=jDQk`tAh$Yy7V!KvmLA=a=ID(3~tLJ`6p_?Ju{LT6;3WcQPt|xfUaC z07wVC2WxodXb%j0#(^2ouRDG^89YPzX8`&#K?;HRHh3ltT+9m>q(3!MLnMYml-81T gB_^&7#UsE`03Sl|xt+;PlK=n!07*qoM6N<$f@9Ea8vp