Skip to content

Cache-stable requests, step 1: canonical replay arguments, sorted tools, ordered registrations - #132

Merged
ayman3000 merged 4 commits into
mainfrom
cache-stable-step1
Oct 9, 2026
Merged

ayman3000 merged 4 commits into
mainfrom
cache-stable-step1

Conversation

@ayman3000

Copy link
Copy Markdown
Owner

Provider prompt caches reuse the longest byte-identical start of a request. Three things in the framework changed that start between consecutive calls of one conversation:

  • Replayed tool-call arguments were re-encoded with an unordered JSONEncoder on every call. They now go out canonically (sorted keys, canonicalArguments). The same encoder turned an integer 1 into true (and 0 into false, 1.0 into true, nested values too) and persisted it; an NSNumber is now typed before the Bool check.
  • Tool order came from a dictionary, so each engine sent a different tool list. Tools are now sent sorted by name.
  • Registration order was not call order: an unchained registration task could let an earlier learn_skill win over the app's override. Every tracked registration is chained now; a later registration of the same name wins.

Also: Agent.registeredTools(), PromptDigest and firstRequestSystemDigest (a digest of the exact system prompt of a run's first request, reset per run) for the app's cache measurement, and a consecutive-call wire test through the real OpenAIProvider.prepareRequest asserting the second request's bytes start with the first's prefix.

Requires LLMProviderKit 0.1.0-alpha.39 (sorted bodies; Anthropic whole-prompt usage).

Tests: CacheStableRequestTests (8); full suite: Swift Testing 606 pass, XCTest 182 pass / 7 skipped.

🤖 Generated with Claude Code

ayman3000 and others added 4 commits October 9, 2026 07:33
…thropic usage)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The arguments of every replayed call were re-encoded with an unsorted
encoder on each model call, so the history could change bytes between calls
and the prompt cache stopped there. AnyCodable also matched Bool before
NSNumber: an integer 1 was replayed as true, 0 as false, 1.0 as true.
Calls already stored keep what was written; new calls are right.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…igest

ToolRegistry listed tools in dictionary order, which differs per instance:
every engine sent its tools in another order. Every piece of registration
work (register, registerAll, skills, setSkillStore's load, tool context,
autonomous mode, init's) now waits for the one queued before it, so an app's
override of a framework tool always wins (the learn_skill race). The agent also exposes firstRequestSystemDigest (stable
FNV-1a 64) and registeredTools(). A wire test pins that consecutive calls
of one chat are append-only on the OpenAI body.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ayman3000
ayman3000 merged commit 65f4168 into main Oct 9, 2026
1 check passed
@ayman3000
ayman3000 deleted the cache-stable-step1 branch October 9, 2026 04:49
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