feat(server): add /health/ready readiness endpoint#405
Conversation
Adds a readiness probe distinct from the existing /health liveness check, following the gateway's actual failure model. - GET /health/ready pings gateway-owned dependencies with a bounded 2s per-probe timeout. Storage is required (down -> not_ready, HTTP 503); the Redis exact cache is a performance optimization (down -> degraded, HTTP 200, still serving). Upstream providers are deliberately excluded so a provider outage cannot pull a healthy gateway out of rotation. - New ReadinessProbe interface keeps the server decoupled from concrete storage/cache types; storage.HealthChecker and cache.Pinger expose Ping(ctx) on the backends, wired through app.go. - CLI: add --ready / --ready-timeout (default 4s, larger than the server's per-probe timeout) returning 0 on ready/degraded, non-zero on not_ready. Liveness (--health) and the Docker HEALTHCHECK are unchanged. - Docs: README endpoint table, docs/advanced/cli.mdx readiness section with K8s readinessProbe vs Docker HEALTHCHECK guidance, regenerated swagger/OpenAPI. - Tests: server handler (incl. auth-skip), CLI probe, responsecache UsesRedis/Ping, and storage Ping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a ChangesReadiness Probe Feature
Sequence Diagram(s)sequenceDiagram
participant Client
participant Server as GET /health/ready
participant StorageProbe as storage.HealthChecker
participant CacheProbe as ResponseCacheMiddleware
Client->>Server: GET /health/ready (no auth required)
Server->>StorageProbe: pingWithTimeout(ctx, readinessProbeTimeout)
alt storage ping fails
StorageProbe-->>Server: error
Server-->>Client: 503 {"status":"not_ready","components":{"storage":"down"}}
else storage ping ok
StorageProbe-->>Server: nil
Server->>CacheProbe: pingWithTimeout(ctx, readinessProbeTimeout)
alt cache ping fails
CacheProbe-->>Server: error
Server-->>Client: 200 {"status":"degraded","components":{"storage":"ok","cache":"down"}}
else cache ping ok
CacheProbe-->>Server: nil
Server-->>Client: 200 {"status":"ready","components":{"storage":"ok","cache":"ok"}}
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR adds a readiness probe separate from the existing liveness check. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (1): Last reviewed commit: "feat(server): add /health/ready readines..." | Re-trigger Greptile |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@docs/advanced/cli.mdx`:
- Around line 65-97: The phrases "Liveness (`--health`) is the right signal" and
"Readiness (`/health/ready`) is the right signal" repeat the same structure and
wording in consecutive sentences. To improve readability, vary the phrasing by
either replacing "right" with an alternative adjective such as "appropriate" in
one of the sentences, or restructure both sentences using different grammatical
patterns (for example, converting one to an imperative or different verb
structure). Choose whichever approach better matches the documentation's tone
and style.
In `@docs/openapi.json`:
- Around line 2119-2138: The `/health/ready` endpoint response schemas in both
the 200 and 503 responses currently use generic objects with
additionalProperties set to true, which does not match the documented contract
that should include specific status and components fields. Replace the
permissive schema definitions (the object type with additionalProperties: true)
in both response definitions with a properly defined schema that explicitly
declares the status and components properties as required fields, removing
additionalProperties: true to enforce the contract structure and generate
accurate client types.
In `@internal/server/readiness.go`:
- Around line 49-50: The Swagger annotations for the Ready function in
internal/server/readiness.go use generic map[string]interface{} types in both
the `@Success` and `@Failure` annotations, but the function actually returns a
concrete readinessResponse type. Replace map[string]interface{} with
readinessResponse in both the `@Success` and `@Failure` Swagger annotations to align
the OpenAPI schema with the actual return type and maintain type consistency in
the API documentation.
🪄 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: eaafb5dc-3a5d-4c1d-ae99-925d7b43fad8
📒 Files selected for processing (22)
README.mdcmd/gomodel/docs/docs.gocmd/gomodel/flags.gocmd/gomodel/health.gocmd/gomodel/main.gocmd/gomodel/ready_test.godocs/advanced/cli.mdxdocs/openapi.jsoninternal/app/app.gointernal/cache/redis.gointernal/cache/store.gointernal/responsecache/readiness_test.gointernal/responsecache/responsecache.gointernal/server/handlers.gointernal/server/http.gointernal/server/readiness.gointernal/server/readiness_test.gointernal/storage/mongodb.gointernal/storage/postgresql.gointernal/storage/sqlite.gointernal/storage/sqlite_test.gointernal/storage/storage.go
| ## Readiness probe | ||
|
|
||
| `--ready` probes `/health/ready`, which reports whether the instance should | ||
| receive traffic. Unlike liveness, readiness checks the dependencies the gateway | ||
| owns: | ||
|
|
||
| - **Storage** is required. If the backend (SQLite/PostgreSQL/MongoDB) is | ||
| unreachable, readiness reports `not_ready` (HTTP `503`). | ||
| - **Redis exact cache** (when configured) is a performance optimization. If it | ||
| is unreachable, readiness reports `degraded` but stays HTTP `200` — the | ||
| gateway still serves requests. | ||
|
|
||
| Upstream **provider** reachability is deliberately excluded: a provider outage | ||
| must not pull a healthy gateway out of rotation. | ||
|
|
||
| ```bash | ||
| gomodel --ready | ||
| ``` | ||
|
|
||
| - Exits `0` when the endpoint returns HTTP `200` (`ready` or `degraded`). | ||
| - Exits non-zero on `not_ready` (HTTP `503`), connection refused, or an | ||
| unexpected status value. | ||
|
|
||
| ```json | ||
| { "status": "ready", "components": { "storage": "ok", "cache": "ok" } } | ||
| ``` | ||
|
|
||
| Liveness (`--health`) is the right signal for a Docker `HEALTHCHECK` (restart on | ||
| crash). Readiness (`/health/ready`) is the right signal for a Kubernetes | ||
| `readinessProbe` (gate traffic) — point it at the HTTP endpoint directly or run | ||
| `gomodel --ready` as an exec probe. Keep `--ready-timeout` larger than the | ||
| server's internal per-dependency probe timeout (`2s`) so a slow backend returns | ||
| a clean `not_ready` instead of the client timing out first. |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider minor wording variation for stylistic polish (optional).
Lines 92-93 repeat "right signal" in consecutive sentences. While the parallel structure effectively emphasizes the liveness/readiness distinction, you could vary the phrasing for smoother reading:
"Liveness (
--health) is the appropriate signal for a DockerHEALTHCHECK(restart on crash). Readiness (/health/ready) is the right signal for a KubernetesreadinessProbe..."
or
"Use liveness (
--health) for DockerHEALTHCHECK(restart on crash) and readiness (/health/ready) for KubernetesreadinessProbe..."
🧰 Tools
🪛 LanguageTool
[style] ~93-~93: You have already used ‘right’ in nearby sentences. Consider using an alternative word to let your writing stand out and sound more polished.
Context: ...sh). Readiness (/health/ready) is the right signal for a Kubernetes `readinessProbe...
(REP_RIGHT)
🤖 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 `@docs/advanced/cli.mdx` around lines 65 - 97, The phrases "Liveness
(`--health`) is the right signal" and "Readiness (`/health/ready`) is the right
signal" repeat the same structure and wording in consecutive sentences. To
improve readability, vary the phrasing by either replacing "right" with an
alternative adjective such as "appropriate" in one of the sentences, or
restructure both sentences using different grammatical patterns (for example,
converting one to an imperative or different verb structure). Choose whichever
approach better matches the documentation's tone and style.
Source: Linters/SAST tools
| "responses": { | ||
| "200": { | ||
| "description": "ready or degraded", | ||
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "type": "object", | ||
| "additionalProperties": true | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "503": { | ||
| "description": "not ready", | ||
| "content": { | ||
| "application/json": { | ||
| "schema": { | ||
| "type": "object", | ||
| "additionalProperties": true | ||
| } |
There was a problem hiding this comment.
Tighten /health/ready response schema to match the declared contract.
Both 200 and 503 currently allow arbitrary objects (additionalProperties: true), but this endpoint is documented to return a stable readiness shape (status + components). Leaving it fully open weakens generated client types and can mask contract drift.
Suggested OpenAPI schema refinement
"responses": {
"200": {
"description": "ready or degraded",
"content": {
"application/json": {
"schema": {
"type": "object",
- "additionalProperties": true
+ "required": ["status", "components"],
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": ["ready", "degraded"]
+ },
+ "components": {
+ "type": "object",
+ "properties": {
+ "storage": { "type": "string" },
+ "cache": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
}
}
}
},
"503": {
"description": "not ready",
"content": {
"application/json": {
"schema": {
"type": "object",
- "additionalProperties": true
+ "required": ["status", "components"],
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": ["not_ready"]
+ },
+ "components": {
+ "type": "object",
+ "properties": {
+ "storage": { "type": "string" },
+ "cache": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
}
}
}
}
},🤖 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 `@docs/openapi.json` around lines 2119 - 2138, The `/health/ready` endpoint
response schemas in both the 200 and 503 responses currently use generic objects
with additionalProperties set to true, which does not match the documented
contract that should include specific status and components fields. Replace the
permissive schema definitions (the object type with additionalProperties: true)
in both response definitions with a properly defined schema that explicitly
declares the status and components properties as required fields, removing
additionalProperties: true to enforce the contract structure and generate
accurate client types.
| // @Success 200 {object} map[string]interface{} "ready or degraded" | ||
| // @Failure 503 {object} map[string]interface{} "not ready" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Use the concrete readiness response type in Swagger annotations.
Ready returns a stable typed payload (readinessResponse), but the docs currently advertise a generic map[string]interface{}. Using the concrete type keeps the OpenAPI schema aligned and prevents drift.
Suggested diff
-// `@Success` 200 {object} map[string]interface{} "ready or degraded"
-// `@Failure` 503 {object} map[string]interface{} "not ready"
+// `@Success` 200 {object} readinessResponse "ready or degraded"
+// `@Failure` 503 {object} readinessResponse "not ready"Based on learnings: “Do not use interface{} or map[string]interface{} for API request/response payload types; prefer strongly-typed structs.”
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // @Success 200 {object} map[string]interface{} "ready or degraded" | |
| // @Failure 503 {object} map[string]interface{} "not ready" | |
| // `@Success` 200 {object} readinessResponse "ready or degraded" | |
| // `@Failure` 503 {object} readinessResponse "not ready" |
🤖 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 `@internal/server/readiness.go` around lines 49 - 50, The Swagger annotations
for the Ready function in internal/server/readiness.go use generic
map[string]interface{} types in both the `@Success` and `@Failure` annotations, but
the function actually returns a concrete readinessResponse type. Replace
map[string]interface{} with readinessResponse in both the `@Success` and `@Failure`
Swagger annotations to align the OpenAPI schema with the actual return type and
maintain type consistency in the API documentation.
Source: Learnings
PR #395 added OpenCode Go to the generated docs.go/openapi.json by hand without updating the @description annotation in main.go, so the generated spec drifted from its source. Regenerating swagger for /health/ready re-synced both files and dropped the manual edit. Add OpenCode Go to main.go's @description (the source of truth) and regenerate, so the provider stays listed and survives future swagger regenerations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
cmd/gomodel/docs/docs.go (1)
1594-1616:⚠️ Potential issue | 🟡 MinorUse the actual response struct type in the swagger annotation, not a generic map.
The
/health/readyhandler returnsreadinessResponsewith fieldsStatus(string) andComponents(map[string]string, optional), but the swagger annotation at lines 49–50 ofinternal/server/readiness.godeclares the response asmap[string]interface{}, which generates an overly permissive OpenAPI schema. Update the annotations toreadinessResponseso generated clients can see the actual response fields:Annotation change in internal/server/readiness.go
- // `@Success` 200 {object} map[string]interface{} "ready or degraded" - // `@Failure` 503 {object} map[string]interface{} "not ready" + // `@Success` 200 {object} readinessResponse "ready or degraded" + // `@Failure` 503 {object} readinessResponse "not ready"🤖 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 `@cmd/gomodel/docs/docs.go` around lines 1594 - 1616, The swagger annotations for the readiness handler in internal/server/readiness.go at lines 49-50 use a generic map[string]interface{} type instead of the actual readinessResponse struct type. Replace the `@Success` and `@Failure` annotations to reference the readinessResponse struct type instead of map[string]interface{} so that the generated OpenAPI schema accurately reflects the actual response fields (Status string and optional Components map). The changes in the docs.go file will be automatically regenerated once the source annotations are corrected.Source: Learnings
♻️ Duplicate comments (1)
docs/openapi.json (1)
2119-2138:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTighten
/health/readyresponse schemas to the actual readiness contract.At Line 2125 and Line 2136, both 200 and 503 are defined as fully open objects. That does not match the runtime contract (
status+components) and weakens client typing/compat checks.Proposed OpenAPI schema refinement
"responses": { "200": { "description": "ready or degraded", "content": { "application/json": { "schema": { "type": "object", - "additionalProperties": true + "required": ["status", "components"], + "properties": { + "status": { + "type": "string", + "enum": ["ready", "degraded"] + }, + "components": { + "type": "object", + "properties": { + "storage": { "type": "string" }, + "cache": { "type": "string" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } }, "503": { "description": "not ready", "content": { "application/json": { "schema": { "type": "object", - "additionalProperties": true + "required": ["status", "components"], + "properties": { + "status": { + "type": "string", + "enum": ["not_ready"] + }, + "components": { + "type": "object", + "properties": { + "storage": { "type": "string" }, + "cache": { "type": "string" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } } },Based on current PR objectives and the supplied runtime readiness handler contract in
internal/server/readiness.go,/health/readyis intended to return a stable structured payload.🤖 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 `@docs/openapi.json` around lines 2119 - 2138, The OpenAPI schema for the /health/ready endpoint defines both the 200 and 503 responses with overly permissive object schemas that use additionalProperties: true, which does not match the actual runtime contract. Update both response schemas (200 and 503) to remove additionalProperties: true and instead define a strict schema with the required properties that match the actual readiness handler contract from internal/server/readiness.go, which returns status and components fields, ensuring client typing and compatibility checks are properly enforced.
🤖 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 `@cmd/gomodel/main.go`:
- Line 80: The Swagger description at line 80 in cmd/gomodel/main.go lists
specific LLM providers but omits providers that are actually registered in the
same file, such as Azure, Bedrock, Vertex, and vLLM. This causes a mismatch
between the documented and actual supported providers. Either update the
description to be non-exhaustive by removing the explicit provider list and
keeping it generic, or fully synchronize the description to include all
registered providers (Azure, Bedrock, Vertex, vLLM along with the currently
listed ones) to ensure the generated API documentation remains accurate and
maintainable.
---
Outside diff comments:
In `@cmd/gomodel/docs/docs.go`:
- Around line 1594-1616: The swagger annotations for the readiness handler in
internal/server/readiness.go at lines 49-50 use a generic map[string]interface{}
type instead of the actual readinessResponse struct type. Replace the `@Success`
and `@Failure` annotations to reference the readinessResponse struct type instead
of map[string]interface{} so that the generated OpenAPI schema accurately
reflects the actual response fields (Status string and optional Components map).
The changes in the docs.go file will be automatically regenerated once the
source annotations are corrected.
---
Duplicate comments:
In `@docs/openapi.json`:
- Around line 2119-2138: The OpenAPI schema for the /health/ready endpoint
defines both the 200 and 503 responses with overly permissive object schemas
that use additionalProperties: true, which does not match the actual runtime
contract. Update both response schemas (200 and 503) to remove
additionalProperties: true and instead define a strict schema with the required
properties that match the actual readiness handler contract from
internal/server/readiness.go, which returns status and components fields,
ensuring client typing and compatibility checks are properly enforced.
🪄 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: f8c3abcd-3c95-4ccc-84e8-e2fd2612a6f6
📒 Files selected for processing (3)
cmd/gomodel/docs/docs.gocmd/gomodel/main.godocs/openapi.json
| // @title GoModel API | ||
| // @version 1.0 | ||
| // @description AI gateway routing requests to multiple LLM providers (OpenAI, Anthropic, Gemini, Groq, OpenRouter, DeepSeek, Z.ai, xAI, MiniMax, Xiaomi MiMo, Oracle, Ollama, Bailian). Drop-in OpenAI-compatible API. | ||
| // @description AI gateway routing requests to multiple LLM providers (OpenAI, Anthropic, Gemini, Groq, OpenRouter, DeepSeek, Z.ai, xAI, MiniMax, Xiaomi MiMo, OpenCode Go, Oracle, Ollama, Bailian). Drop-in OpenAI-compatible API. |
There was a problem hiding this comment.
Keep the Swagger provider description from drifting.
Line 80 lists providers but omits providers registered in this same file (e.g., Azure, Bedrock, Vertex, vLLM), so the generated API description can be misleading. Prefer a non-exhaustive description or keep the list fully synchronized.
Proposed fix
-// `@description` AI gateway routing requests to multiple LLM providers (OpenAI, Anthropic, Gemini, Groq, OpenRouter, DeepSeek, Z.ai, xAI, MiniMax, Xiaomi MiMo, OpenCode Go, Oracle, Ollama, Bailian). Drop-in OpenAI-compatible API.
+// `@description` AI gateway routing requests to multiple LLM providers. Drop-in OpenAI-compatible API.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // @description AI gateway routing requests to multiple LLM providers (OpenAI, Anthropic, Gemini, Groq, OpenRouter, DeepSeek, Z.ai, xAI, MiniMax, Xiaomi MiMo, OpenCode Go, Oracle, Ollama, Bailian). Drop-in OpenAI-compatible API. | |
| // `@description` AI gateway routing requests to multiple LLM providers. Drop-in OpenAI-compatible API. |
🤖 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 `@cmd/gomodel/main.go` at line 80, The Swagger description at line 80 in
cmd/gomodel/main.go lists specific LLM providers but omits providers that are
actually registered in the same file, such as Azure, Bedrock, Vertex, and vLLM.
This causes a mismatch between the documented and actual supported providers.
Either update the description to be non-exhaustive by removing the explicit
provider list and keeping it generic, or fully synchronize the description to
include all registered providers (Azure, Bedrock, Vertex, vLLM along with the
currently listed ones) to ensure the generated API documentation remains
accurate and maintainable.
Summary
Adds a readiness probe (
GET /health/ready) distinct from the existing/healthliveness check, matching the gateway's actual failure model.The key design choice: readiness probes only dependencies the gateway owns; upstream providers are deliberately excluded. Gating readiness on provider reachability would let a transient OpenAI/Anthropic outage pull every healthy replica out of rotation simultaneously — turning a recoverable degradation into an outage.
/health(liveness, unchanged)/health/ready(readiness, new)readydegraded(still serving)not_readyResponse shape:
{ "status": "ready", "components": { "storage": "ok", "cache": "ok" } }What changed
internal/server/readiness.go): public route (added toauthSkipPaths), bounded 2s per-dependency ping that also honors request-context cancellation. Storage failure →not_ready(503); Redis exact-cache failure →degraded(200, non-blocking).server.ReadinessProbeinterface;storage.HealthCheckerandcache.PingerexposePing(ctx)on the SQLite/PostgreSQL/MongoDB backends andRedisStore. Wired inapp.go— storage always, cache only when a Redis exact cache is configured (ResponseCacheMiddleware.UsesRedis()).--ready/--ready-timeout(default4s, larger than the server's 2s per-probe timeout so a slow backend returns a cleannot_readyinstead of the client cutting off). Exits 0 onready/degraded, non-zero onnot_ready.--healthand the DockerHEALTHCHECKare unchanged (liveness is the right restart signal).docs/advanced/cli.mdxreadiness section with K8sreadinessProbevs DockerHEALTHCHECKguidance; regenerated swagger/OpenAPI (additions only).User-visible impact
GET /health/readyfor orchestrator readiness gating (e.g. K8sreadinessProbe).gomodel --readyCLI probe./health,--health, or the DockerHEALTHCHECK.Provider-specific behavior
None. Provider reachability is intentionally not part of any orchestrator-facing probe.
Tests
internal/server/readiness_test.go— table-driven handler (ready/degraded/not_ready precedence) + auth-skip.cmd/gomodel/ready_test.go— CLI probe URL + status handling.internal/responsecache/readiness_test.go—UsesRedis/Pingfor pinger vs in-memory stores.internal/storage/sqlite_test.go—TestSQLitePing.All pre-commit hooks pass (race tests, lint, mint validate).
🤖 Generated with Claude Code
Summary by CodeRabbit
/health/readyreadiness endpoint that checks storage and cache, returning200forready/degradedand503fornot_ready/v1/realtimeendpoint for OpenAI-compatible realtime (websocket) sessions--readyand--ready-timeoutto perform readiness checks at startup/health/readyand/v1/realtime