Skip to content

refactor: prompt cache internals#91

Merged
kxzk merged 6 commits into
mainfrom
codex/refactor-prompt-cache-internals
May 5, 2026
Merged

refactor: prompt cache internals#91
kxzk merged 6 commits into
mainfrom
codex/refactor-prompt-cache-internals

Conversation

@kxzk
Copy link
Copy Markdown
Collaborator

@kxzk kxzk commented May 5, 2026

TL;DR

Simplifies the prompt-cache and API-client internals. Public SDK behavior unchanged.

Why

This branch started as a refactor that introduced focused collaborator classes around prompt cache, prompt clients, and API resources. Two simplification passes later, those collaborators were unwound — the abstractions weren't earning their keep against the project's "no abstractions beyond what's required" rule. The result is a net simplification of the SDK that preserves all public behavior.

Checklist

  • Has label
  • Has linked issue: AAI-114
  • Tests added for new behavior
  • Docs updated (if user-facing)

Summary

  • Flatten PromptCacheCoordinator (288 LoC) — single dispatch (SWR > distributed lock > simple get/set), no more sentinel-param re-checks. Replaces the 326-LoC, 11-method-deep version that preceded it.
  • Delete PromptCacheCapabilities — 13 forwarding methods around cache.foo / cache.respond_to?(:foo) ? cache.foo : default. Coordinator now calls cache methods directly; the only real branch (fetch_with_lock only on RailsCacheAdapter) is one is_a? check.
  • Delete PromptClientFactory and PromptClientMetadata — inlined the case statement and the metadata attr_readers back into Client / the prompt clients.
  • Delete Resources::Batches, Resources::Traces, Resources::Datasets, Resources::Prompts — the lambda-passing pattern (each class took 4-5 callables that round-tripped back to ApiClient) added no encapsulation. Methods inlined back on ApiClient.
  • Add private request(verb, path, params:, body:) helper on ApiClient — collapses the repeated with_faraday_error_handling { handle_response(connection.<verb>(...)) } shape across ~20 call sites.
  • Use Forwardable.def_delegators for 12 trivial ClientApiClient pass-throughs (list_prompts, list_traces, prompt_cache_*, etc.). YARD @!method directives keep them documented.
  • Replace payload[:foo] = bar if bar chains with .compact in ScoreClient#build_score_event and the dataset/prompt payload builders. Drops 2 rubocop disables on score_client.
  • Update cache warmer + rake clear_cache to use public SDK cache APIs.
  • Doc clarifications for tracing_async and reserved/no-op job_queue config.

Net change

+750 / −1196 lines (−446 net). 5 collaborator classes removed plus 4 spec files for the deleted classes. Coverage delta: +0.38pp.

Verification

  • RBENV_VERSION=3.2.0 rbenv exec bundle _2.4.7_ exec rspec1342 examples, 0 failures, 96.84% coverage
  • RBENV_VERSION=3.2.0 rbenv exec bundle _2.4.7_ exec rubocop --cache false — no offenses
  • RBENV_VERSION=3.2.0 rbenv exec bundle _2.4.7_ exec ruby scratchpad/aai_114_refactor/run_local.rb — 5/5 scripts pass
  • RBENV_VERSION=3.2.0 rbenv exec bundle _2.4.7_ exec ruby scratchpad/aai_114_refactor/run_platform.rb — passed against https://us.cloud.langfuse.com; temporary prompt was deleted
  • git status --short --ignored scratchpad/aai_114_refactor — scratchpad remains ignored

@kxzk kxzk changed the title [codex] Refactor prompt cache internals refactor: prompt cache internals May 5, 2026
@kxzk kxzk added the refactor Code refactoring and tech debt reduction label May 5, 2026
@kxzk kxzk self-assigned this May 5, 2026
kxzk added 3 commits May 5, 2026 05:22
Reverses the over-abstraction introduced earlier in this branch. The
five new "collaborator" classes added ~1000 net lines for a refactor
that the PR description claimed had no behavior change — the cost
didn't match the value.

- Delete PromptCacheCapabilities (170 LoC). 13 forwarding methods
  around cache.foo / cache.respond_to?(:foo) ? cache.foo : default.
  PromptCache and RailsCacheAdapter both provide the full surface;
  only fetch_with_lock differs, handled now with one is_a? check
  in the coordinator.
- Flatten PromptCacheCoordinator (326 → 288 LoC). Single dispatch in
  fetch_cached: SWR > distributed lock > simple get/set, no more
  distributed_enabled = nil sentinel re-checks.
- Delete Resources::Batches and Resources::Traces. Each wrapped two
  methods through a lambda hop with no encapsulation gain. Methods
  inlined back onto ApiClient.
- Delete PromptClientFactory (116 LoC). Was a case statement plus
  five thin Client delegators (one of which reached private methods
  via send). Inlined back onto Client.
- Delete PromptClientMetadata mixin. The initialize_prompt_metadata
  ceremony was more cognitive load than the duplicated attr_readers
  it removed.

api_client_spec: replace mock-heavy SWR/lock/cache-detection contexts
with integration tests using real PromptCache and RailsCacheAdapter
backends. The mocked tests asserted on respond_to? plumbing that no
longer exists; the cache mechanics they covered are tested directly
in prompt_cache_spec / rails_cache_adapter_spec.

Verification:
- bundle exec rspec — 1342 examples, 0 failures, 96.91% coverage
- bundle exec rubocop --cache false — 0 offenses
- scratchpad/aai_114_refactor/run_local.rb — all 5 scripts pass
- scratchpad/aai_114_refactor/run_platform.rb — passed against
  https://us.cloud.langfuse.com; temporary prompt was deleted
… trivial passthroughs

Continues the simplification pass after the prompt-cache cleanup.

- Inline Resources::Datasets (257 LoC) and Resources::Prompts (107 LoC)
  back into ApiClient. Same shape as the just-deleted Batches/Traces:
  the classes held no state, just lambda-forwarded back to ApiClient
  private methods. Drops the 5-callable initializer + the 9 stub
  forwarder methods on each class.
- Add private `request(verb, path, params: nil, body: nil)` helper.
  Collapses the repeated `with_faraday_error_handling do
  handle_response(connection.<verb>(path, ...)) end` shape across ~20
  call sites. Faraday's verb-specific signatures still work because
  `body || params` lands in the same second positional slot.
- Use `Forwardable.def_delegators :api_client, ...` for the 12 trivial
  Client → ApiClient pass-throughs (`list_prompts`, `list_traces`,
  `get_trace`, `prompt_cache_*`, `validate_prompt_cache_backend!`,
  etc.). Kills ~250 LoC of duplicated YARD blocks; YARD `@!method`
  directives keep the methods documented on Client.
- Replace `payload[:foo] = bar if bar` chains with `.compact` in
  `ScoreClient#build_score_event` and the inlined dataset/prompt
  payload builders. Drops 2 rubocop disables on score_client and
  collapses the `add_dataset_item_fields`/`add_source_fields` micro-
  helpers that existed only to fit the 22-line cap.

Verification:
- bundle exec rspec — 1342 examples, 0 failures, 96.84% coverage
- bundle exec rubocop --cache false — 0 offenses
- scratchpad/aai_114_refactor/run_local.rb — 5/5 pass
- scratchpad/aai_114_refactor/run_platform.rb — passed against
  https://us.cloud.langfuse.com; temporary prompt was deleted

Net: -482 lines.
@kxzk kxzk marked this pull request as ready for review May 5, 2026 17:39
Copilot AI review requested due to automatic review settings May 5, 2026 17:39
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Langfuse’s prompt caching and prompt-client construction internals into smaller collaborators (notably a new PromptCacheCoordinator) while aiming to preserve the existing public SDK surface and prompt/cache semantics. It also updates internal call sites (rake tasks, cache warmer, request shaping) to rely on the public cache APIs and centralizes prompt-cache event payload construction.

Changes:

  • Introduces Langfuse::PromptCacheCoordinator and shifts prompt fetch/cache branching + invalidation behavior out of ApiClient.
  • Consolidates prompt-cache event payload construction via PromptCacheEvents.build_payload and expands cache stats to include TTL/size/max_size where applicable.
  • Updates rake/cache-warming flows and various API request builders to use public cache APIs and a shared ApiClient#request helper; refreshes docs/specs accordingly.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
spec/langfuse/rake_cache_contract_spec.rb Adds contract test ensuring rake cache tasks use public cache APIs.
spec/langfuse/prompt_cache_coordinator_spec.rb Adds focused tests for coordinator cache statuses, events, and invalidation.
spec/langfuse/api_client_spec.rb Updates cache-related specs to exercise the new coordinator-backed behavior.
lib/tasks/langfuse.rake Switches cache clear flow to Client public cache APIs and prints generation.
lib/langfuse/text_prompt_client.rb Minor documentation tweaks around prompt/compile behavior.
lib/langfuse/score_client.rb Refactors score event body building using compact to remove nils.
lib/langfuse/rails_cache_adapter.rb Enhances stats payload and makes global generation lookup safer.
lib/langfuse/prompt_cache.rb Enhances stats payload with ttl/size/max_size fields.
lib/langfuse/prompt_cache_events.rb Adds shared payload builder and improves observer arity handling.
lib/langfuse/prompt_cache_coordinator.rb New coordinator encapsulating prompt cache read/write/SWR/lock branching and invalidation.
lib/langfuse/config.rb Clarifies tracing_async and job_queue documentation.
lib/langfuse/client.rb Uses Forwardable for pass-through APIs; small prompt client/fallback refactors.
lib/langfuse/chat_prompt_client.rb Minor documentation/attr_reader reordering for prompt.
lib/langfuse/cache_warmer.rb Uses public cache stats APIs and normalizes backend names for reporting.
lib/langfuse/api_client.rb Delegates cache behavior to coordinator; introduces request helper; refactors several endpoints.
lib/langfuse.rb Requires the new coordinator.
docs/CONFIGURATION.md Updates text around tracing async scheduling and job queue being reserved/no-op.
docs/API_REFERENCE.md Updates config table descriptions for tracing_async and job_queue.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/langfuse/api_client.rb Outdated
Comment on lines 423 to 433
def list_traces(**)
list_traces_paginated(**)["data"] || []
end
# rubocop:enable Metrics/ParameterLists

# Full paginated response including "meta" for internal pagination use
#
# @api private
# @return [Hash] Full response hash with "data" array and "meta" pagination info
# rubocop:disable Metrics/ParameterLists
def list_traces_paginated(page: nil, limit: nil, user_id: nil, name: nil, session_id: nil,
from_timestamp: nil, to_timestamp: nil, order_by: nil,
tags: nil, version: nil, release: nil, environment: nil,
fields: nil, filter: nil)
with_faraday_error_handling do
params = build_traces_params(
page: page, limit: limit, user_id: user_id, name: name,
session_id: session_id, from_timestamp: from_timestamp,
to_timestamp: to_timestamp, order_by: order_by, tags: tags,
version: version, release: release, environment: environment,
fields: fields, filter: filter
)
response = connection.get("/api/public/traces", params)
handle_response(response)
end
def list_traces_paginated(**)
request(:get, "/api/public/traces", params: build_traces_params(**))
end
Comment thread lib/tasks/langfuse.rake
Comment on lines +151 to +154
generation = Langfuse.client.clear_prompt_cache
puts "Cache cleared successfully! ✓"
puts "Backend: #{Langfuse.configuration.cache_backend}"
puts "Generation: #{generation}" unless generation.nil?
- Restore explicit keyword args on `list_traces` / `list_traces_paginated`.
  The `**` forwarding I introduced was silently swallowing typoed filter
  keys (e.g. `usre_id:`) instead of raising ArgumentError. Pre-refactor
  behavior is now restored: unknown kwargs raise immediately at the
  ApiClient boundary. Forwardable on Client preserves kwarg semantics so
  the strict signature still applies through `client.list_traces`.

- Clarify the rake `langfuse:clear_cache` output. The task calls
  `clear_prompt_cache`, which is a logical generation bump — old backend
  entries remain until TTL/eviction. The previous "Cache cleared
  successfully!" message could mislead operators expecting immediate
  key deletion. New output explicitly states it's a logical clear and
  that subsequent fetches will miss the old generation and re-populate.

Verification: rspec 1342/0, rubocop clean, scratchpad local + platform
(against https://us.cloud.langfuse.com) pass.
@kxzk kxzk merged commit f1c48e0 into main May 5, 2026
11 checks passed
@kxzk kxzk deleted the codex/refactor-prompt-cache-internals branch May 5, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring and tech debt reduction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants