Skip to content

Caching v0.3: robust trajectory caching, precise parameters & observability - #311

Merged
philipph-askui merged 6 commits into
mainfrom
feat/improve-caching-params-and-logging
Aug 27, 2026
Merged

Caching v0.3: robust trajectory caching, precise parameters & observability#311
philipph-askui merged 6 commits into
mainfrom
feat/improve-caching-params-and-logging

Conversation

@philipph-askui

@philipph-askui philipph-askui commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Caching v0.3

A single PR covering the caching v0.3 work: it makes trajectory caching correct, precise, observable, and hard to misconfigure, and bumps the cache format to 0.3.

Reliability & mechanism

  • Auto-detect the trajectory by name. In execute/auto mode the SDK looks up <cache_dir>/<filename> and, if a usable trajectory exists, injects its details (path + required parameters) into the first user message so the agent hands off to the CacheExecutor immediately. The old "list available trajectories" tool and prompt workflow are removed.
  • CachingSettings.filename now actually works and may include subdirectories relative to cache_dir (e.g. filename="mytests_1/test_something".askui_cache/mytests_1/test_something.json) for both lookup and recording — with a guard so a filename can't escape cache_dir. Previously it was silently ignored, and nested recording crashed.
  • Resuming past the last (non-cacheable) step no longer crashes. start_from_step_index == len(trajectory) means "already complete"; the pause message tells the agent the exact resume index or that it was the final step.
  • execute-only mode no longer crashes (a CacheManager is always created when executing).
  • verify_cache_execution now persists outcomes: success records the attempt; failure invalidates the cache.
  • No cross-run leaks: the CACHE_USE prompt, the CacheExecutor speaker, and per-call caching tools no longer bleed into later act() calls.
  • Empty / no-cacheable-step recordings are no longer written; recording errors in teardown are logged, not masked.
  • Cache format version bumped 0.2 → 0.3.

Precise parameter identification

  • Only user-entered free text is eligible; structural inputs (coordinates, key names, action/enum values, counts, tool names) are never offered to the model.
  • The system prompt is conservative/precision-first — parameterize only when replaying the literal would clearly be wrong (dates relative to "now", generated IDs/tokens/OTPs, per-run identities); recording zero parameters is normal. The LLM isn't called when there are no candidates.
  • Identified values are validated against the offered candidates (invalid names, empty values, and hallucinated/reformatted values are dropped).

Observability (logs and reporter)

  • Caching explains what and why via the logger and the attached reporter (role Cache, so it also shows up in the HTML report): cache hit/miss, replay start, pause on a non-cacheable step (with tool name), completion, verification outcome with the agent's reason and the cache file, invalidation, and recording written/skipped.
  • Invalidation logs a concise WARNING headline with the full reason at INFO.

Safer configuration

  • CachingSettings, CacheExecutionSettings, and CacheWritingSettings reject unknown fields (extra="forbid"), so a misplaced option — e.g. CachingSettings(delay_time_between_actions=3.0) instead of nesting under execution_settings — raises a clear error instead of being silently ignored.

Testing & docs

  • pdm run qa:fix clean (typecheck + format + lint); full unit suite green (784 passed), with dedicated tests for auto-detection, resume-at-end, parameter precision/validation, nested filenames + escape guard, extra="forbid", and the observability messages.
  • docs/06_caching.md rewritten for the filename-based flow with Dynamic Parameters and Observability sections.

Reviews

Developed with three adversarial code-review passes (recording path, replay/orchestration path, and a final review of the parameter/observability changes); all substantiated findings were fixed.

Known follow-ups (intentionally out of scope)

  • Whole-field, type-preserving parameter substitution (the substring/str() limitation).
  • Recording success-gating beyond the empty-trajectory guard.
  • Tool-level input coercion so a model/replay passing a numeric as a string doesn't crash a tool (a tools change, tracked separately).

🤖 Generated with Claude Code

philipph-askui and others added 3 commits August 26, 2026 22:58
… format to 0.3

Fixes two reported trajectory-caching bugs and several logic bugs surfaced by an
adversarial review of the caching subsystem.

Reported bugs:
- Resuming after the last (non-cacheable) step crashed with
  "Invalid start_from_step_index". `start_from_step_index == len(trajectory)`
  now means "already complete" and flows into the COMPLETED/verification path;
  only truly out-of-range indices raise. Empty/all-non-cacheable trajectories are
  handled too. The non-cacheable pause message now tells the agent the exact
  resume index, or that the step was the final one (verify instead of resume).
- Replaced the "list available trajectories" workflow with automatic detection:
  in execute/auto mode the SDK looks up <cache_dir>/<filename> and, if a usable
  trajectory exists, injects its details (path + parameters) into the first user
  message so the agent can switch to the CacheExecutor immediately. In auto mode
  with no usable trajectory the agent is told none exists and the run is recorded.
  Removed retrieve_available_trajectories_tool and updated CACHE_USE_PROMPT.

Cache format version bumped 0.2 -> 0.3.

Additional bugs fixed (found during review):
- CachingSettings.filename was silently ignored (no such field); added it as a
  top-level field used for both lookup and recording.
- execute-only mode passed cache_manager=None, causing CacheExecutor to raise
  RuntimeError; a CacheManager is now always created when executing.
- verify_cache_execution(success=False) now actually invalidates the cache, and
  success=True records the completion (execution_attempts/last_executed_at).
- CACHE_USE prompt and CacheExecutor speaker no longer leak across act() calls;
  per-call caching tools no longer accumulate on the persistent tool collection
  (which could persist a later run's result to a previous run's trajectory).
- LLM-identified parameters with invalid names or empty values are dropped
  (empty values previously corrupted every string in the trajectory).
- Recording no longer writes/overwrites a cache with no cacheable steps.
- finish_recording errors in teardown are logged instead of masking the run
  result.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ervability

Two improvements to the trajectory-caching mechanism.

1) Parameter identification is far more precise (fewer false positives):
   - Only user-entered free text is eligible. Coordinates, key names,
     action/enum values, counts and tool names are filtered out before the LLM
     is asked, so they can no longer be mis-identified as parameters. When a
     recording has no free-text values, the LLM is not called at all.
   - The system prompt is rewritten to be conservative and precision-first:
     parameterize a value only if replaying the recorded literal would clearly
     be wrong on a later run; when in doubt, keep the literal. Returning no
     parameters is explicitly normal.
   - Identified values are validated against the offered candidates, so
     hallucinated/reformatted values are dropped.

2) Caching now explains what and why, via logs AND the reporter (source
   "Cache"), so the information also lands in the HTML report, not only stderr:
   - cache hit/miss, replay start, pause on a non-cacheable step (with the tool
     name), completion, verification outcome, invalidation, and recording.
   - Verification failures now include the agent's reason and the affected cache
     file instead of an unexplained "Cache verification failed!".

Stacked on the trajectory-caching-improvements branch (PR #310).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`CachingSettings.filename` may include subdirectories, resolved relative to
`cache_dir`, so a test at tests/mytests_1/test_something.py can use
filename="mytests_1/test_something" and map to
.askui_cache/mytests_1/test_something.json.

Lookup already preserved subdirectories, but recording crashed with
FileNotFoundError because the nested parent directory was never created. Create
parents on record (start_recording, _generate_cache_file) and on metadata writes
(_write_cache_file), so the save location matches the lookup location.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philipph-askui

Copy link
Copy Markdown
Contributor Author

Added nested cache filename support (commit 9f71337): CachingSettings.filename may include subdirectories relative to cache_dir, so e.g. filename="mytests_1/test_something" maps to .askui_cache/mytests_1/test_something.json for both lookup and recording. Lookup already preserved subdirectories; recording previously crashed with FileNotFoundError because the nested parent dir wasn't created — now created on record and on metadata writes. Docs + tests included.

philipph-askui and others added 2 commits August 27, 2026 15:30
…tly ignored)

The replay delay lives on CacheExecutionSettings.delay_time_between_actions and
is applied correctly by the CacheExecutor between replayed actions. However,
setting it the intuitive way - CachingSettings(delay_time_between_actions=3.0) -
was silently ignored (pydantic extra="ignore"), so the default 1.0s was used and
the delay "did not work".

Add model_config = ConfigDict(extra="forbid") to CachingSettings,
CacheExecutionSettings and CacheWritingSettings so a misplaced/misspelled field
raises a clear ValidationError (and is caught by mypy) instead of being dropped.
The correct nested form still works:
CachingSettings(execution_settings=CacheExecutionSettings(
    delay_time_between_actions=3.0)).

Docs updated to call out where the delay lives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cache invalidation logged the full (often long) reason at WARNING. Keep a
concise WARNING headline ("Cache invalidated and will not be reused.") and log
the full reason at INFO instead. report_cache_event() gains an optional `detail`
argument (logged at INFO, appended to the reporter message) to support this.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philipph-askui philipph-askui changed the title feat(caching): precision-first parameter identification + observability Caching v0.3: parameter precision, observability & safer configuration Aug 27, 2026
- Guard nested cache filenames so they stay within cache_dir: reject absolute
  paths and ".." components (ensure_relative_cache_filename) at both lookup and
  recording. Previously an absolute/traversing filename could escape cache_dir
  and auto-create arbitrary directories.
- Narrow the parameter-candidate denylist to purely structural keys: drop "id",
  "name" and "amount" so genuinely dynamic user-entered values (reference/order
  ids, usernames, typed amounts) can still be parameterized; the conservative
  prompt decides.
- Avoid a literal "None" in the cache-replay-failure message when there is no
  error message ("unknown error" instead).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philipph-askui philipph-askui changed the title Caching v0.3: parameter precision, observability & safer configuration Caching v0.3: robust trajectory caching, precise parameters & observability Aug 27, 2026
@philipph-askui
philipph-askui changed the base branch from fix/trajectory-caching-improvements to main August 27, 2026 20:25
@philipph-askui
philipph-askui marked this pull request as draft August 27, 2026 20:25
@philipph-askui
philipph-askui marked this pull request as ready for review August 27, 2026 20:26
@philipph-askui
philipph-askui merged commit 40feeec into main Aug 27, 2026
1 check passed
@philipph-askui
philipph-askui deleted the feat/improve-caching-params-and-logging branch August 27, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant