Skip to content

feat(cache): HTTP management API, config validation, and observability (2/2) - #110

Merged
yacosta738 merged 5 commits into
mainfrom
read-cache
Jun 5, 2026
Merged

feat(cache): HTTP management API, config validation, and observability (2/2)#110
yacosta738 merged 5 commits into
mainfrom
read-cache

Conversation

@yacosta738

Copy link
Copy Markdown
Contributor

Issue

Closes #50 (Part 2 of 2)

Summary

Completes read cache implementation with HTTP management API, configuration validation, and full observability integration.

Depends on: #106 (merged)

What Changed

Phase 3: Ports

  • ✅ Add stats() method to CachePort trait
  • ✅ Add delete_by_signature() for HTTP endpoint support
  • ✅ Implement both methods in InMemoryCache

Phase 4: Configuration

  • ✅ Add max_entries: Option<usize> field to CacheConfig
  • ✅ Implement validate() rejecting TTL > 24h and max_entries = Some(0)
  • ✅ Wire validation at config load (fail-fast)
  • ✅ Pass max_entries to cache constructor in DI

Phase 5: Application

  • ✅ Add cache() accessor to RouteRequest
  • ✅ Verified stats already tracking in get()

Phase 6: Transport (HTTP Management API)

  • ✅ Create cache.rs handler module
  • ✅ Implement GET /api/cache/stats (200 with CacheStats JSON)
  • ✅ Implement DELETE /api/cache (204 clear all)
  • ✅ Implement DELETE /api/cache/:signature (204/404)
  • ✅ Wire cache routes (management API, requires auth)
  • ✅ Extend /health with cache_entries, cache_hit_rate, cache_utilization

Phase 7: Observability

  • ✅ Add rook_cache_evictions counter description
  • ✅ Wire eviction metric in InMemoryCache

Tests

  • ✅ 5 config validation tests
  • ✅ 6 cache HTTP endpoint integration tests
  • ✅ All 459 tests passing

Files Changed

  • crates/domain/rook-core/src/ports.rs — Added stats() and delete_by_signature() to CachePort
  • crates/infrastructure/cache-memory/src/lib.rs — Implemented new methods + metrics
  • apps/rook/src/config.rs — Added max_entries + validate()
  • crates/infrastructure/transport-axum/src/handlers/cache.rs — New cache management handlers
  • crates/infrastructure/transport-axum/src/routes.rs — Cache routes + health integration
  • apps/rook/tests/config_tests.rs — 5 validation tests
  • crates/infrastructure/transport-axum/tests/cache_routes.rs — 6 HTTP endpoint tests

Total: ~635 lines added

Verification

✅ 459 tests passed (11 new tests)
✅ cargo clippy: 0 warnings
✅ cargo fmt: formatted
✅ All HTTP endpoints tested
✅ Config validation tested
✅ Health endpoint integration tested

API Examples

Get Cache Stats

GET /api/cache/stats
{
  "hits": 1234,
  "misses": 567,
  "evictions": 89,
  "entries": 450,
  "max_entries": 1000,
  "hit_rate": 0.685,
  "utilization": 0.45
}

Clear Cache

DELETE /api/cache
# Returns 204 No Content

Delete by Signature

DELETE /api/cache/abc123def456...
# Returns 204 (deleted) or 404 (not found)

Health Endpoint

GET /health
{
  "status": "healthy",
  "cache_entries": 450,
  "cache_hit_rate": 0.685,
  "cache_utilization": 0.45,
  ...
}

Configuration

[cache]
enabled = true
ttl_secs = 300        # 5 minutes (max 86400 = 24h)
max_entries = 1000    # Optional, None = unlimited

Next Steps

After merge:

  • Run sdd-verify to validate against specs
  • Run sdd-archive to close SDD cycle
  • Feature complete ✅

Implement content-based caching for issue #50:
- Add signature field to CacheKey (SHA-256 hex string)
- Update CompletionRequest.cache_key() to hash (model + messages + params)
- Add CacheStats struct with hit_rate() and utilization() methods
- Add LRU eviction to InMemoryCache with max_entries support
- Add AtomicU64 counters (hits, misses, evictions)
- Implement stats() method in InMemoryCache
- Add comprehensive unit tests for hashing, LRU, and stats

Breaking change: CacheKey now requires signature field (documented in proposal)
- Add documentation for approximate LRU under concurrency
- Fix evict_if_needed to only trigger on new keys (not overwrites)
- Add test for no-eviction-on-overwrite behavior
- Clarify spec: None=unlimited, Some(0)=rejected (usize cannot be negative)
- Update tasks to specify max_entries validation rules
…y (2/2)

Completes issue #50 implementation:

**Phase 3: Ports**
- Add stats() method to CachePort trait
- Add delete_by_signature() for HTTP endpoint support
- Implement both methods in InMemoryCache

**Phase 4: Configuration**
- Add max_entries field to CacheConfig
- Implement validate() rejecting ttl > 24h and max_entries = Some(0)
- Wire validation at config load (fail-fast)
- Pass max_entries to cache constructor in DI

**Phase 5: Application**
- Add cache() accessor to RouteRequest (already tracking stats)

**Phase 6: Transport**
- Create cache.rs handler module
- Implement GET /api/cache/stats (200 with CacheStats JSON)
- Implement DELETE /api/cache (204 clear all)
- Implement DELETE /api/cache/:signature (204/404)
- Wire cache routes (management API, requires auth)
- Extend /health with cache_entries, cache_hit_rate, cache_utilization

**Phase 7: Observability**
- Add rook_cache_evictions counter description
- Wire eviction metric in InMemoryCache

**Tests**
- 5 config validation tests
- 6 cache HTTP endpoint integration tests
- All 450+ tests passing
@github-actions github-actions Bot added the area/testing Tests and testing infrastructure label Jun 5, 2026
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@yacosta738, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 44 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7614486d-7cd9-42cf-9b63-0c0d63bffb74

📥 Commits

Reviewing files that changed from the base of the PR and between 0f010d9 and 4348eb4.

📒 Files selected for processing (5)
  • apps/rook/tests/config_tests.rs
  • crates/infrastructure/cache-memory/src/lib.rs
  • crates/infrastructure/transport-axum/src/routes.rs
  • crates/infrastructure/transport-axum/tests/cache_routes.rs
  • openspec/changes/read-cache/tasks.md
📝 Walkthrough

Walkthrough

This PR implements cache management and observability features by extending the CachePort trait with stats() and delete_by_signature() methods, implementing them in InMemoryCache with Prometheus metrics, adding configurable max_entries with validation, exposing cache operations via three new HTTP endpoints, integrating cache stats into the health check, and providing comprehensive integration tests.

Changes

Cache Management API & Observability

Layer / File(s) Summary
CachePort trait extension
crates/domain/rook-core/src/ports.rs
CachePort gains stats() to retrieve cache metrics and delete_by_signature(signature: &str) to perform signature-based bulk invalidation.
Configuration and validation
apps/rook/src/config.rs, apps/rook/tests/config_tests.rs
CacheConfig gains optional max_entries field and validate() method enforcing TTL ≤ 86400 seconds and max_entries > 0 or None; RookConfig::load applies validation at startup; test suite covers all validation paths.
InMemoryCache implementation of new methods
crates/infrastructure/cache-memory/Cargo.toml, crates/infrastructure/cache-memory/src/lib.rs, crates/infrastructure/observability/src/metrics.rs
InMemoryCache adds delete_by_signature() for signature-based entry removal; LRU eviction increments Prometheus rook_cache_evictions counter; CachePort impl wraps both as async methods; metrics dependency and counter descriptor added.
DI container and application exposure
apps/rook/src/di.rs, crates/application/rook-usecases/src/route_request.rs, crates/application/rook-usecases/tests/route_request_restrictions.rs
RookContainer passes config.cache.max_entries to InMemoryCache; RouteRequest exposes public cache() accessor; NoOpCache and test doubles implement new trait methods.
HTTP handler implementations
crates/infrastructure/transport-axum/src/handlers/cache.rs, crates/infrastructure/transport-axum/src/handlers/mod.rs, crates/infrastructure/transport-axum/Cargo.toml
New cache.rs module with three async handlers: get_cache_stats (GET) returns CacheStats JSON; clear_cache (DELETE) returns 204; delete_cache_entry (DELETE :signature) validates 64-char hex signature, returns 400 for invalid input, 204 for valid (idempotent), 500 for errors; cache-memory added to dev-dependencies.
Route wiring and health check integration
crates/infrastructure/transport-axum/src/routes.rs
Main router merges cache_routes defining /api/cache/stats, /api/cache, and /api/cache/:signature; health_check() fetches cache stats asynchronously with zeroed default fallback; health response JSON extended with cache_stats object including hits, misses, evictions, entries, max_entries, hit_rate, and utilization.
HTTP cache endpoint tests
crates/infrastructure/transport-axum/tests/cache_routes.rs
Integration test suite exercises GET stats returning 200 with metrics; DELETE clear returning 204 and emptying entries; DELETE signature returning 204 for existing/missing entries (idempotent); DELETE signature returning 400 for malformed hex; validates hit rate and stats accuracy.
Test double updates across integration tests
crates/infrastructure/transport-axum/tests/format_translation_integration.rs, crates/infrastructure/transport-axum/src/bootstrap_helpers.rs
Test doubles (StubCache, NoopCache) updated to implement stats() returning zeroed CacheStats and delete_by_signature() returning 0 across all test environments.
Feature tracking and verification
openspec/changes/read-cache/tasks.md
Task checklist updated: Phases 3–10 marked complete including trait methods, InMemoryCache implementation, config validation, HTTP endpoints, health integration, metrics, and verification commands.

Sequence Diagram

sequenceDiagram
  participant Client
  participant HealthCheck
  participant CacheRoutes
  participant InMemoryCache
  participant Prometheus
  Client->>HealthCheck: GET /health
  HealthCheck->>InMemoryCache: stats()
  InMemoryCache-->>HealthCheck: CacheStats{hits, misses, evictions, entries}
  HealthCheck-->>Client: 200 with cache_stats in JSON
  Client->>CacheRoutes: GET /api/cache/stats
  CacheRoutes->>InMemoryCache: stats()
  InMemoryCache-->>CacheRoutes: CacheStats
  CacheRoutes-->>Client: 200 JSON CacheStats
  Client->>CacheRoutes: DELETE /api/cache/:signature
  CacheRoutes->>InMemoryCache: delete_by_signature(signature)
  InMemoryCache->>Prometheus: increment rook_cache_evictions
  InMemoryCache-->>CacheRoutes: count of deleted entries
  CacheRoutes-->>Client: 204 No Content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A cache now speaks in whispers clear,
Stats and signatures draw near,
LRU counts each eviction fall,
Endpoints serve the health check's call,
Hits and misses dance in sight—
Cortex caches with delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: HTTP management API, config validation, and observability work for the cache feature (part 2/2).
Description check ✅ Passed The description comprehensively relates to the changeset, detailing each phase of implementation, file changes, verification, and API examples.
Linked Issues check ✅ Passed The PR addresses all major objectives from issue #50: cache statistics API, management endpoints (GET /api/cache/stats, DELETE /api/cache, DELETE /api/cache/:signature), config validation (TTL max 24h), LRU eviction with max_entries, and health integration.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #50 objectives: cache trait extensions, config validation, HTTP handlers, routes, and metrics are all in-scope for implementing the read cache feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch read-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/infrastructure/cache-memory/src/lib.rs (1)

94-100: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Only count/emit eviction when an entry is actually evicted.

Line 94 discards the removal result, while Lines 97-99 always increment eviction counters. With stale last_accessed keys during concurrent mutations, this can report false evictions and still leave cache at/over capacity.

Proposed fix
-                    self.store.remove(&oldest);
-                    self.expiry.remove(&oldest);
-                    self.last_accessed.remove(&oldest);
-                    self.evictions.fetch_add(1, Ordering::Relaxed);
-                    // Emit Prometheus metric
-                    metrics::counter!("rook_cache_evictions").increment(1);
+                    if self.store.remove(&oldest).is_some() {
+                        self.expiry.remove(&oldest);
+                        self.last_accessed.remove(&oldest);
+                        self.evictions.fetch_add(1, Ordering::Relaxed);
+                        metrics::counter!("rook_cache_evictions").increment(1);
+                    } else {
+                        // stale LRU index entry
+                        self.last_accessed.remove(&oldest);
+                    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/infrastructure/cache-memory/src/lib.rs` around lines 94 - 100, The
code is incrementing eviction metrics unconditionally even when no entry was
actually removed; update the logic around self.store.remove(&oldest) to check
its return value and only proceed to remove associated keys (self.expiry.remove,
self.last_accessed.remove) and to call self.evictions.fetch_add(...) and
metrics::counter!("rook_cache_evictions").increment(1) when the store removal
returned Some (i.e., an actual eviction occurred). Ensure the conditional uses
the same key/value identity for removal so concurrent stale last_accessed
entries don't trigger false evictions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/rook/tests/config_tests.rs`:
- Around line 92-205: Add a file-based test that exercises RookConfig::load to
ensure validation errors are wrapped with the startup prefix; create a temp file
containing a TOML config with an invalid cache (e.g. ttl_secs = 86401), call
RookConfig::load(path) (the startup path that reads from disk), and assert the
returned Err string contains the prefix "invalid cache config:" and the
underlying message like "exceeds 24h maximum" to lock that contract.

In `@crates/infrastructure/cache-memory/src/lib.rs`:
- Around line 65-70: In delete_by_signature, the loop always increments deleted
without checking whether an entry was actually removed, which can overreport
under concurrency; change the loop to check the removal result from
self.store.remove(&key) (or whichever primary map determines existence) and only
increment deleted when that remove returns Some(...); still call
self.expiry.remove(&key) and self.last_accessed.remove(&key) for cleanup but
base the deleted++ on the actual remove result of self.store (use
Option.is_some()) so the returned count reflects real deletions; refer to
delete_by_signature, keys_to_delete, self.store, self.expiry,
self.last_accessed, and deleted when making the change.

In `@crates/infrastructure/transport-axum/src/routes.rs`:
- Around line 877-879: The route path for the cache DELETE uses old colon syntax
and must be changed from "/api/cache/:signature" to Axum 0.8's single-segment
capture syntax "/api/cache/{signature}"; update the route definition that
registers handlers::cache::delete_cache_entry so the application doesn't panic
during route setup and DELETE /api/cache/<hash> correctly captures the signature
path param.

In `@crates/infrastructure/transport-axum/tests/cache_routes.rs`:
- Around line 64-67: Add an integration test that hits the fully-wired router
(not the handler directly) to assert auth is required for cache endpoints:
create a test (e.g., test_cache_routes_require_auth) that builds the complete
axum app/router used in production (so it includes the auth middleware), send an
HTTP GET to "/api/cache/stats" without any auth header using the same request
harness (tower::ServiceExt::oneshot or an HTTP client against the test server)
and assert the response status is Unauthorized (401); locate usage of
get_cache_stats and the existing Router construction in the test module to
replace the direct handler-mounted Router with the full application router so
the auth layer is exercised.

In `@openspec/changes/read-cache/tasks.md`:
- Line 99: The dependency list on the checklist item "10.1 Run `cargo test`"
incorrectly uses a broad "9.*" range; update the text so Dependencies explicitly
list only the actually completed Phase 9 integration tasks (replace "9.*" with
the precise subset, e.g. "9.1–9.4" or enumerated items), leaving "8.*" as-is if
correct, so the progress audit is unambiguous.
- Line 67: Update the checklist wording to match the implemented DELETE
contract: change the task for the `delete_cache_entry` handler so it references
calling `delete_by_signature(&str)` (idempotent) and expecting a 204 for both
present and missing signatures instead of constructing a `CacheKey` and
returning 404; remove or replace the mention of `cache.delete()`/404 and ensure
the task explicitly documents the bulk deletion/idempotent behavior and expected
204 response.

---

Outside diff comments:
In `@crates/infrastructure/cache-memory/src/lib.rs`:
- Around line 94-100: The code is incrementing eviction metrics unconditionally
even when no entry was actually removed; update the logic around
self.store.remove(&oldest) to check its return value and only proceed to remove
associated keys (self.expiry.remove, self.last_accessed.remove) and to call
self.evictions.fetch_add(...) and
metrics::counter!("rook_cache_evictions").increment(1) when the store removal
returned Some (i.e., an actual eviction occurred). Ensure the conditional uses
the same key/value identity for removal so concurrent stale last_accessed
entries don't trigger false evictions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5ed481b8-47df-4968-be30-15f0a03b4cab

📥 Commits

Reviewing files that changed from the base of the PR and between 91f140a and 0f010d9.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • apps/rook/src/config.rs
  • apps/rook/src/di.rs
  • apps/rook/tests/config_tests.rs
  • crates/application/rook-usecases/src/route_request.rs
  • crates/application/rook-usecases/tests/route_request_restrictions.rs
  • crates/domain/rook-core/src/ports.rs
  • crates/infrastructure/cache-memory/Cargo.toml
  • crates/infrastructure/cache-memory/src/lib.rs
  • crates/infrastructure/observability/src/metrics.rs
  • crates/infrastructure/transport-axum/Cargo.toml
  • crates/infrastructure/transport-axum/src/bootstrap_helpers.rs
  • crates/infrastructure/transport-axum/src/handlers/cache.rs
  • crates/infrastructure/transport-axum/src/handlers/mod.rs
  • crates/infrastructure/transport-axum/src/routes.rs
  • crates/infrastructure/transport-axum/tests/cache_routes.rs
  • crates/infrastructure/transport-axum/tests/format_translation_integration.rs
  • openspec/changes/read-cache/tasks.md

Comment thread apps/rook/tests/config_tests.rs
Comment thread crates/infrastructure/cache-memory/src/lib.rs
Comment thread crates/infrastructure/transport-axum/src/routes.rs Outdated
Comment on lines +64 to +67
let app = axum::Router::new()
.route("/api/cache/stats", axum::routing::get(get_cache_stats))
.layer(Extension(cache));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add auth-enforcement coverage for cache management routes.

These tests mount handlers directly, so they don’t verify the “auth required” contract for /api/cache*. Please add an integration test that exercises the fully wired router and asserts unauthorized requests are rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/infrastructure/transport-axum/tests/cache_routes.rs` around lines 64 -
67, Add an integration test that hits the fully-wired router (not the handler
directly) to assert auth is required for cache endpoints: create a test (e.g.,
test_cache_routes_require_auth) that builds the complete axum app/router used in
production (so it includes the auth middleware), send an HTTP GET to
"/api/cache/stats" without any auth header using the same request harness
(tower::ServiceExt::oneshot or an HTTP client against the test server) and
assert the response status is Unauthorized (401); locate usage of
get_cache_stats and the existing Router construction in the test module to
replace the direct handler-mounted Router with the full application router so
the auth layer is exercised.

Comment thread openspec/changes/read-cache/tasks.md Outdated
Comment thread openspec/changes/read-cache/tasks.md Outdated
- cache-memory: only increment deleted/evictions when store.remove returns Some
- routes.rs: use Axum 0.8 path syntax {signature}
- cache_routes.rs: add test_cache_routes_require_management_auth
- config_tests.rs: fix assertion message to match actual validation error
- tasks.md: fix dependencies (9.* → 9.1-9.4), update delete behavior description
@yacosta738
yacosta738 merged commit 0ca01d2 into main Jun 5, 2026
10 of 11 checks passed
@yacosta738
yacosta738 deleted the read-cache branch June 5, 2026 07:39
yacosta738 added a commit that referenced this pull request Jun 5, 2026
…109)

* feat: add model alias domain model and SQLite repository

- Add ModelAlias struct and ModelAliasRepositoryPort trait
- Create alias-sqlite crate with SqliteModelAliasRepository
- Add V5 migration for model_aliases table
- Include 26 built-in aliases (OpenAI, Anthropic, Google, Mistral, Groq)
- Add 11 unit tests for repository operations
- Implement cycle prevention and idempotent seeding

Part of #47

* feat: add model alias resolution and HTTP API (#111)

- Add [model_aliases] config section with enabled and auto_seed flags
- Wire SqliteModelAliasRepository into DI container with startup seeding
- Implement alias resolution in RouteRequest before restrictions check
- Add GET/POST/DELETE endpoints for alias management at /api/models/aliases
- Add cycle prevention validation in create endpoint
- Add 7 E2E tests for alias resolution and seeding
- Add 10 HTTP API integration tests
- Add 5 config tests for model aliases section

Part of #47

* feat(cache): HTTP management API, config validation, and observability (2/2) (#110)

* feat(cache): content-based cache keys with SHA-256 signatures

Implement content-based caching for issue #50:
- Add signature field to CacheKey (SHA-256 hex string)
- Update CompletionRequest.cache_key() to hash (model + messages + params)
- Add CacheStats struct with hit_rate() and utilization() methods
- Add LRU eviction to InMemoryCache with max_entries support
- Add AtomicU64 counters (hits, misses, evictions)
- Implement stats() method in InMemoryCache
- Add comprehensive unit tests for hashing, LRU, and stats

Breaking change: CacheKey now requires signature field (documented in proposal)

* fix(cache): address code review findings

- Add documentation for approximate LRU under concurrency
- Fix evict_if_needed to only trigger on new keys (not overwrites)
- Add test for no-eviction-on-overwrite behavior
- Clarify spec: None=unlimited, Some(0)=rejected (usize cannot be negative)
- Update tasks to specify max_entries validation rules

* feat(cache): HTTP management API, config validation, and observability (2/2)

Completes issue #50 implementation:

**Phase 3: Ports**
- Add stats() method to CachePort trait
- Add delete_by_signature() for HTTP endpoint support
- Implement both methods in InMemoryCache

**Phase 4: Configuration**
- Add max_entries field to CacheConfig
- Implement validate() rejecting ttl > 24h and max_entries = Some(0)
- Wire validation at config load (fail-fast)
- Pass max_entries to cache constructor in DI

**Phase 5: Application**
- Add cache() accessor to RouteRequest (already tracking stats)

**Phase 6: Transport**
- Create cache.rs handler module
- Implement GET /api/cache/stats (200 with CacheStats JSON)
- Implement DELETE /api/cache (204 clear all)
- Implement DELETE /api/cache/:signature (204/404)
- Wire cache routes (management API, requires auth)
- Extend /health with cache_entries, cache_hit_rate, cache_utilization

**Phase 7: Observability**
- Add rook_cache_evictions counter description
- Wire eviction metric in InMemoryCache

**Tests**
- 5 config validation tests
- 6 cache HTTP endpoint integration tests
- All 450+ tests passing

* fix: apply remaining code review findings from PR #110

- cache-memory: only increment deleted/evictions when store.remove returns Some
- routes.rs: use Axum 0.8 path syntax {signature}
- cache_routes.rs: add test_cache_routes_require_management_auth
- config_tests.rs: fix assertion message to match actual validation error
- tasks.md: fix dependencies (9.* → 9.1-9.4), update delete behavior description

* feat: add model alias domain model and SQLite repository

- Add ModelAlias struct and ModelAliasRepositoryPort trait
- Create alias-sqlite crate with SqliteModelAliasRepository
- Add V5 migration for model_aliases table
- Include 26 built-in aliases (OpenAI, Anthropic, Google, Mistral, Groq)
- Add 11 unit tests for repository operations
- Implement cycle prevention and idempotent seeding

Part of #47

* feat: add model alias resolution and HTTP API (#111)

- Add [model_aliases] config section with enabled and auto_seed flags
- Wire SqliteModelAliasRepository into DI container with startup seeding
- Implement alias resolution in RouteRequest before restrictions check
- Add GET/POST/DELETE endpoints for alias management at /api/models/aliases
- Add cycle prevention validation in create endpoint
- Add 7 E2E tests for alias resolution and seeding
- Add 10 HTTP API integration tests
- Add 5 config tests for model aliases section

Part of #47

* fix: address code review findings for model aliasing

- Add alias resolution to execute_stream_with_format for streaming requests
- Change ModelAlias.created_at from String to DateTime<Utc> for consistency
- Generate unique timestamps per alias in builtin_aliases()
- Update builtin.rs comment to reflect provider-scoped aliases
- Clarify cycle detection as depth-1 only in repository
- Replace string matching with enum matching for AlreadyExists error
- Make cycle check provider-scoped in handler and repository query
- Add db_migration import to fix test initialization

All changes verified with full CI passing.
@dallay-bot dallay-bot Bot mentioned this pull request Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/testing Tests and testing infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read Cache (Response Caching)

1 participant