refactor(cache_stability): graduate out of experimental to top-level

The `experimental` tier was retired (#347): new capabilities land as a
top-level submodule `pydantic_ai_harness/<name>/`, not re-exported from the
root, with ACP the sole grandfathered experimental holdout. This branch
predated that, so move `experimental/cache_stability` -> `cache_stability`:

- top-level package, private (not added to the root `__init__.py`), so
  importing the root package doesn't pull the capability in;
- drop `warn_experimental`; the README carries the preview/stability note;
- update import paths in code, tests, and README;
- satisfy the docs-parity gate that landed on main: spaced-word README H1,
  a source-module link, and a row in the top-level README matrix.

No behavior change; public import path is now
`from pydantic_ai_harness.cache_stability import CacheStabilityMonitor`.
This commit is contained in:
David SF
2026-07-15 18:26:55 -05:00
parent b1c225f0b3
commit 94396f937e
7 changed files with 33 additions and 26 deletions
+1
View File
@@ -157,6 +157,7 @@ We studied leading coding agents, agent frameworks, and Claw-style assistants to
| | **Context compaction** | LLM-powered summarization of older messages | :white_check_mark: [Docs](pydantic_ai_harness/compaction/) | [summarization-pydantic-ai](https://github.com/vstorm-co/summarization-pydantic-ai) (vstorm&#8209;co) |
| | **Limit warnings** | Warn agent before hitting context/iteration limits | :white_check_mark: [Docs](pydantic_ai_harness/compaction/) | [summarization-pydantic-ai](https://github.com/vstorm-co/summarization-pydantic-ai) (vstorm&#8209;co) |
| | **Tool output management** | Truncate, summarize, or spill large tool outputs | :white_check_mark: [Docs](pydantic_ai_harness/overflowing_tool_output/) | |
| | **Cache-bust monitoring** | Warn when a run's prompt-cache prefix collapses between model requests | :white_check_mark: [Docs](pydantic_ai_harness/cache_stability/) (preview) | |
| | **System reminders** | Inject periodic reminders to counteract instruction drift | :construction: [PR&nbsp;#181](https://github.com/pydantic/pydantic-ai-harness/pull/181) | |
| **Memory &&nbsp;persistence** | **Memory** | Persistent, namespaced notebook with bounded prompt injection, on-demand search, and concurrency-safe stores | :white_check_mark: [Docs](pydantic_ai_harness/memory/) | [pydantic-deep](https://github.com/vstorm-co/pydantic-deepagents) (vstorm&#8209;co) |
| | **Session persistence** | Save and restore full conversation state | :white_check_mark: [Docs](pydantic_ai_harness/step_persistence/) | |
@@ -1,7 +1,9 @@
# CacheStabilityMonitor
# Cache Stability Monitor
Warn when a run's prompt cache hit collapses between model requests.
[Source](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/cache_stability/)
Prompt caching pays off only while the cacheable prefix (tools, then system
instructions, then message history) stays byte-stable across a run's consecutive
requests. When something moves that prefix -- reordered tools, a timestamp
@@ -34,15 +36,22 @@ isn't mistaken for a moved prefix.
The verdict is cross-provider for free -- pyai normalizes every provider into the
`cache_read_tokens` / `cache_write_tokens` fields on `RequestUsage`.
> This capability is experimental and private. It is not re-exported from
> `pydantic_ai_harness`; import it from its own module. Its API may change or be
> removed in any release.
> [!NOTE]
> Import this capability from its submodule. It is not re-exported from `pydantic_ai_harness`:
>
> ```python
> from pydantic_ai_harness.cache_stability import CacheStabilityMonitor
> ```
This is a preview capability: its surface is not a committed public API and may
change or be removed in any release. It is the opt-in observe arm of the broader
prompt-cache-prefix-stability work.
## Minimal usage
```python
from pydantic_ai import Agent
from pydantic_ai_harness.experimental.cache_stability import CacheStabilityMonitor
from pydantic_ai_harness.cache_stability import CacheStabilityMonitor
agent = Agent('anthropic:claude-sonnet-4-5', capabilities=[CacheStabilityMonitor()])
await agent.run('...') # a CacheBustWarning fires if a cached prefix collapses mid-run
@@ -74,7 +83,7 @@ as you would manage any other `UserWarning`:
```python
import warnings
from pydantic_ai_harness.experimental.cache_stability import CacheBustWarning
from pydantic_ai_harness.cache_stability import CacheBustWarning
# Silence the whole category:
warnings.filterwarnings('ignore', category=CacheBustWarning)
@@ -90,7 +99,7 @@ warnings.filterwarnings('error', category=CacheBustWarning)
In tests, assert an intentional bust with `pytest.warns(CacheBustWarning)`, or
silence a legitimately-busting test with
`@pytest.mark.filterwarnings('ignore::pydantic_ai_harness.experimental.cache_stability.CacheBustWarning')`.
`@pytest.mark.filterwarnings('ignore::pydantic_ai_harness.cache_stability.CacheBustWarning')`.
## Logfire
@@ -0,0 +1,11 @@
"""Observational prompt-cache-collapse monitor (top-level, not re-exported at the root)."""
from pydantic_ai_harness.cache_stability._capability import (
CacheBustWarning,
CacheStabilityMonitor,
)
__all__ = [
'CacheBustWarning',
'CacheStabilityMonitor',
]
@@ -29,7 +29,7 @@ _CacheKey = tuple[str | None, str | None]
_SILENCE_HINT = (
' import warnings\n'
' from pydantic_ai_harness.experimental.cache_stability import CacheBustWarning\n'
' from pydantic_ai_harness.cache_stability import CacheBustWarning\n'
" warnings.filterwarnings('ignore', category=CacheBustWarning) # silence\n"
" warnings.filterwarnings('error', category=CacheBustWarning) # escalate in dev/CI"
)
@@ -48,7 +48,7 @@ class CacheBustWarning(UserWarning):
(no bespoke API):
import warnings
from pydantic_ai_harness.experimental.cache_stability import CacheBustWarning
from pydantic_ai_harness.cache_stability import CacheBustWarning
# Silence the whole category:
warnings.filterwarnings('ignore', category=CacheBustWarning)
@@ -63,7 +63,7 @@ class CacheBustWarning(UserWarning):
In tests, assert an intentional bust with `pytest.warns(CacheBustWarning)`, or silence
a legitimately-busting test with
`@pytest.mark.filterwarnings('ignore::pydantic_ai_harness.experimental.cache_stability.CacheBustWarning')`.
`@pytest.mark.filterwarnings('ignore::pydantic_ai_harness.cache_stability.CacheBustWarning')`.
"""
@@ -91,7 +91,7 @@ class CacheStabilityMonitor(AbstractCapability[AgentDepsT]):
```python
from pydantic_ai import Agent
from pydantic_ai_harness.experimental.cache_stability import CacheStabilityMonitor
from pydantic_ai_harness.cache_stability import CacheStabilityMonitor
agent = Agent('anthropic:claude-sonnet-4-5', capabilities=[CacheStabilityMonitor()])
await agent.run('...') # a CacheBustWarning fires if a cached prefix collapses mid-run
@@ -1,14 +0,0 @@
"""Observational prompt-cache-collapse monitor (private, not re-exported at top level)."""
from pydantic_ai_harness.experimental._warn import warn_experimental
from pydantic_ai_harness.experimental.cache_stability._capability import (
CacheBustWarning,
CacheStabilityMonitor,
)
warn_experimental('cache_stability')
__all__ = [
'CacheBustWarning',
'CacheStabilityMonitor',
]
@@ -21,7 +21,7 @@ from pydantic_ai.models.fallback import FallbackModel
from pydantic_ai.models.function import AgentInfo, FunctionModel
from pydantic_ai.usage import RequestUsage
from pydantic_ai_harness.experimental.cache_stability import (
from pydantic_ai_harness.cache_stability import (
CacheBustWarning,
CacheStabilityMonitor,
)