---
title: "What's new in Hindsight 0.4.15"
description: New features and improvements in Hindsight 0.4.15
authors: [nicoloboschi]
date: 2026-03-03
hide_table_of_contents: true
image: /img/blog/release0415.png
---

Hindsight 0.4.15 adds PydanticAI integration, observation scopes, richer entity labels, and several performance improvements—alongside a set of reliability and stability fixes.

<!-- truncate -->

- [**PydanticAI Integration**](#pydanticai-integration): Add persistent memory to PydanticAI agents.
- [**Observation Scopes**](#observation-scopes): Control how facts consolidate into observations across tag dimensions.
- [**Richer Entity Labels**](#richer-entity-labels): Optional labels, free-form values, and multi-value fields for entity extraction.
- [**Timestamp Unset**](#timestamp-unset): Retain timeless content without a date.
- [**Performance**](#performance): Recall and retain are faster and more scalable for large memory banks.

## PydanticAI Integration

Hindsight now integrates with [PydanticAI](https://ai.pydantic.dev/), letting you attach persistent memory to any PydanticAI agent with a few lines of code.

```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)
```

`create_hindsight_tools` registers three async tools on the agent:

- **`hindsight_retain`** — stores information to long-term memory
- **`hindsight_recall`** — searches memory for relevant facts
- **`hindsight_reflect`** — synthesizes a reasoned answer from stored memories

`memory_instructions` injects recalled context as instructions at the start of every run, so the agent always has relevant history in its context window without needing to call a tool explicitly.

Both accept the full set of Hindsight retrieval parameters—`budget`, `max_tokens`, `tags`, `recall_tags`, and `recall_tags_match`—for fine-grained control over what gets stored and retrieved.

You can also configure defaults globally:

```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")
```

See the [PydanticAI integration documentation](/sdks/integrations/pydantic-ai) for a full quickstart.

## Observation Scopes

Observations are Hindsight's higher-level summaries derived from raw facts. Previously, observations were always consolidated across all tags at once. Now you can control the _granularity_ of each consolidation pass with `observation_scopes`.

This matters when memories are tagged across multiple dimensions—say, `student:alice`, `teacher:bob`, and `session-id:s1`. You might want observations per-student, per-teacher, and per-session independently, not just one combined observation for all three.

`observation_scopes` is a per-item parameter passed in the retain request. The examples below use a lesson transcript retained with `tags: ["student:alice", "teacher:bob", "session-id:s1"]`.

**`per_tag`** — one consolidation pass per individual tag. The most common choice for multi-party content.

- Observations created: `["student:alice"]` · `["teacher:bob"]` · `["session-id:s1"]`
- Queries like _"What does Alice struggle with?"_ and _"How does Bob teach?"_ match.
- Queries like _"How does Alice perform specifically with Bob?"_ do not — no observation was built for that combination.

**`combined`** *(default)* — one pass using all tags together.

- Observations created: `["student:alice", "teacher:bob", "session-id:s1"]`
- Only the exact combination matches. Individual tags like `["student:alice"]` do not.

**`all_combinations`** — one pass per subset. For 3 tags that is 7 passes.

- Observations created: all `per_tag` scopes plus every pair and the full set.

**`custom`** — explicit list of tag sets:

```json
[["student:alice"], ["teacher:bob"], ["teacher:bob", "session-id:s1"]]
```

Only those three scopes are built — nothing more.

Scopes are fully isolated during consolidation—a memory consolidated under `["student:alice"]` will never bleed into an observation tagged `["student:alice", "teacher:bob"]`.

## Richer Entity Labels

`entity_labels` is a new bank configuration option that defines a controlled vocabulary of `key:value` classification labels. During retain, the LLM extracts these labels from each piece of content and stores them as entities. Because labels become entities, they automatically link memories in the knowledge graph and improve both semantic and BM25 retrieval.

Three field types are supported:

| Type | Behavior |
|------|----------|
| `"value"` | Single value from a fixed list |
| `"multi-values"` | One or more values from a fixed list |
| `"text"` | Free-form string (no fixed values) |

Fields can also be marked `optional: true` so the LLM skips them when the content doesn't have enough information.

```json
{
  "entity_labels": [
    {
      "key": "engagement",
      "description": "Student engagement level during the session",
      "type": "value",
      "optional": true,
      "values": [
        { "value": "active",  "description": "Student is actively participating" },
        { "value": "passive", "description": "Student is listening but not participating" }
      ]
    },
    {
      "key": "pedagogy",
      "description": "Teaching strategies used",
      "type": "multi-values",
      "values": [
        { "value": "scaffolding",          "description": "Breaking complex tasks into smaller steps" },
        { "value": "direct_instruction",   "description": "Explicit explanation by the teacher" },
        { "value": "socratic_questioning", "description": "Guiding through questions rather than answers" }
      ]
    },
    {
      "key": "topic",
      "description": "Specific subject being discussed. Examples: algebra, quadratic equations, geometry.",
      "type": "text",
      "optional": true
    }
  ]
}
```

For enum types (`"value"`, `"multi-values"`), anything outside the `values` list is silently dropped—vocabulary stays stable and graph links stay tight. For `"text"` types, the LLM writes any string; use `description` to provide examples and guidance.

Set `entity_labels` via the [bank config API](/developer/api/memory-banks#entity-labels). The control plane UI has been updated to display multi-value and free-form labels cleanly.

## Timestamp Unset

When retaining reference material—documentation, books, or any content without a meaningful event date—you can now pass `timestamp="unset"` to tell Hindsight there is no real date to associate with the content.

```python
# Timeless reference content
client.retain(
    bank_id="my-bank",
    content="The quick sort algorithm has O(n log n) average-case time complexity.",
    timestamp="unset"
)

# Mix timeless and timestamped content in a single batch
client.retain_batch(
    bank_id="my-bank",
    items=[
        {"content": "Meeting notes...", "timestamp": "2026-03-01T14:00:00Z"},
        {"content": "Company handbook...", "timestamp": "unset"},
    ]
)
```

When `"unset"` is passed, the fact-extraction prompt shows `Event Date: Unknown`, allowing the model to correctly return `N/A` for the `when` field of every extracted fact rather than anchoring facts to an arbitrary date.

## Performance

This release includes significant database-level work targeting large-scale memory banks—better indices, improved query planning, reduced lock contention, deadlock fixes, and connection warmup improvements. We benchmarked against banks with up to 100,000 memories and 100 million entity links:

- **Retain**: Up to **10x faster** at large scale.
- **Recall**: Up to **~20x better** at very large scale, with the largest gains in the graph and temporal retrievers.

No configuration is required.

## Other Updates

**Features**
- **OpenClaw auto-retain**: OpenClaw now retains the last `N*2 + 4` messages every N turns (default N=10) instead of every turn. The sliding window ensures conversation context is never lost at boundaries while significantly reducing LLM calls per session. Configure with `retainEveryNTurns` in the plugin config.
- **Gemini/Vertex AI safety settings**: Safety settings for Gemini and Vertex AI LLM calls are now configurable. Useful for deployments that need to relax or tighten content filtering on LLM calls.
- **Document tag filtering**: The list documents API now supports filtering by tags, making it easier to query which documents were retained under a given label.
- **Extension hooks**: New hooks to customize root routing behavior and add custom error headers, for deployments that need to intercept requests or decorate responses at the transport layer.

**Bug Fixes**
- Fixed reflect failing with `context_length_exceeded` on large memory banks by truncating the context window correctly.
- Fixed a consolidation deadlock caused by retrying after zombie processing tasks.
- Fixed the observations count in the control plane that always showed 0.
- Fixed JSON serialization issues and logging-related exception propagation with the `claude_code` LLM provider.
- Fixed the ZeroEntropy rerank endpoint URL.
- Fixed MCP `async_processing` parameter handling.
- Added bank-scoped request validation to prevent cross-bank operations.
- Fixed the TypeScript SDK to send `null` (not `undefined`) when `includeEntities` is `false`.

## Feedback and Community

Hindsight 0.4.15 is a drop-in replacement for 0.4.x with no breaking changes.

Share your feedback:

- [GitHub Discussions](https://github.com/vectorize-io/hindsight/discussions)
- [GitHub Issues](https://github.com/vectorize-io/hindsight/issues)

For detailed changes, see the [full changelog](/changelog).
