π Planning: llm-ports v0.1.0-alpha.30 β Persistent backends + caching #66
baabakk
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Status: Planning discussion. Target ship 2026-09-02 (two weeks after alpha.29). Extendable to 2026-09-16 if design questions surface.
Theme: Persistent backends + caching. Two items; each is substantial enough to be its own release note but they pair thematically (both close the "state that survives restart" gap).
Prior release: alpha.29 (planned 2026-08-19; capability factory ergonomics; see planning discussion filed alongside).
What ships in alpha.30
Two items, both larger than the alpha.28 / alpha.29 median scope.
Item 1 β Content-result cache primitive
Consumer: ADW (E). Also aligns with SalesCoach's response-caching hand-roll for coaching cue prompts.
Current gap.
fingerprintCacheshipped in alpha.24 caches per-provider behavioral fingerprints (reasoning-model discovery outcomes) across restarts. There is no analogous cache for actual LLM response content. Consumers that want to cache "same prompt + same schema + same task β same result" wrapgenerateTexton the app side with a SHA-256 keyed store; caching is uneven acrossgenerateText/generateStructured/ stream methods.Fix shape.
RegistryOptions.contentCache?: ContentCacheBackendanalogous tofingerprintCache. Bundled backends:InMemoryContentCache(opts?: { maxEntries?, ttlMs? })β dev / test / short-worker default.FileContentCache(path, opts?)β atomic JSON, persistent across restart.RedisContentCache(client, opts?)β cross-worker; requires the alpha.30@llm-ports/budget-redis-style optional peer dep pattern.Consumer implements
ContentCacheBackendfor custom stores (S3, DynamoDB, Postgres, etc.).Cross-method wiring.
generateText,generateStructured,streamText,streamStructured. NOTrunAgentβ multi-turn state complicates cache-key correctness; skip until a follow-up.Cache key. SHA-256 of a canonical serialization:
{ taskType, model_alias, messages, options.schema?.description, options.temperature?, options.maxOutputTokens?, options.reasoningEffort?, options.refs, options.strict?, options.cacheControl }. Refs go in the key so{ prompt: {key, version} }variants don't collide. Consumers can override viacontentCache: { keyDerivation: (opts) => string }.Semantics. On hit: adapter is bypassed entirely; the cached result is returned with
cacheHit: truein the observability event. Onmiss: adapter is called; result is stored before returning.Estimated: ~300 LoC (interface + three bundled backends + cross-method wiring + tests + conformance suite for BYO-backend).
Item 2 β
@llm-ports/budget-redispackageConsumer: BEPA (9).
Current gap.
@llm-ports/coreshipsInMemoryBudgetandInMemoryCostas the default backends forBudgetBackendandCostBackend. Both reset on worker restart. Every serious consumer that wants cost limits to survive deploys writes the same RedisINCR/EXPIRElogic against those interfaces.Fix shape.
New optional peer package
@llm-ports/budget-redis. Two factories:createRedisBudget(client: RedisClient, opts?: { keyPrefix? })returnsBudgetBackend.createRedisCost(client: RedisClient, opts?: { keyPrefix? })returnsCostBackend.Both implement the existing
BudgetBackend/CostBackendinterfaces from@llm-ports/coreunchanged. Consumers wirecreateRegistryFromEnv({ budget: createRedisBudget(client), cost: createRedisCost(client) }).Redis contract. Uses
INCR+EXPIREfor the per-hour / per-minute counters. Uses a Lua script for the atomic cost check-and-record. Requires Redis 5+ (any modern hosted instance).Cross-worker semantics. Multiple workers sharing a Redis instance see a single global budget. Restart-safe: the Redis keys survive process cycles. TTL-aligned:
req:100/hourcorrectly expires after 1 hour of quiet time.Optional dep pattern.
ioredisis an optional peer dep (peerDependenciesMeta.optional), consistent with how@llm-ports/adapter-anthropictreats@anthropic-ai/sdk. Consumers installioredisorredisthemselves.Estimated: ~250 LoC across the package + tests +
docs/adapters/budget-redis.md.Item 3 (bonus) β Item 2 subsumes named-session persistence from alpha.29
Alpha.29's Item 8 (named sessions) ships as Registry-in-memory. If
@llm-ports/budget-redisalso ships acreateRedisSessionStore(client), cross-worker session lookup lands automatically. Recommend addingcreateRedisSessionStoreto the alpha.30 package if the design review agrees.Open design questions
bypassContentCache?: booleanon call options; opt-out is per-call.@llm-ports/budget-redisvs@llm-ports/backend-redis. Naming: budget-redis is precise but excludes the session-store use case in Item 3. Recommendation: name it@llm-ports/backend-redisand ship budget + cost + session-store factories from one package.gptoss-cerebrasand consumer forcesforceProviderAlias: "gpt5"on the retry, is the cache hit valid? Recommendation: NO β cache key includes the resolved provider alias; forced-alias variants get their own cache entry.Consumer credit
Both consumers documented the pattern in their production code; both hand-rolled the same shape independently. The alpha.30 theme takes the two patterns and moves them behind first-class backends.
Cross-references
Post design-question answers below by 2026-08-26 for baseline scope.
All reactions