Repository navigation
Conversation
…st and redirects Unify matchesPattern (exact/prefix/suffix/contains) and provider pattern regex compilation to be case-insensitive, so configured allowedModels and modelRedirects match client-requested model names regardless of case.
/v1/models, /v1/responses/models and /v1/chat/models previously returned all provider models even when the key's user had allowedModels configured. Apply the same case-insensitive exact-match semantics as ProxyModelGuard so the listed models match what the user can actually request.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…percent Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Release v0.9.1 # Conflicts: # drizzle/meta/0109_snapshot.json # drizzle/meta/_journal.json # src/actions/providers.ts # src/app/[locale]/settings/providers/_components/forms/provider-form/sections/options-section.tsx # src/app/v1/_lib/proxy/forwarder.ts # src/lib/codex/provider-overrides.ts # src/lib/langfuse/emit-proxy-trace.ts # src/repository/_shared/transformers.ts # tests/unit/langfuse/emit-proxy-trace.test.ts # tests/unit/proxy/response-handler-client-abort-drain.test.ts
…merged forwarder signature
Add the edgeExecutionEnabled system setting (default off) that gates whether remote edge executors may take over eligible /v1/messages requests. Includes the migration, repository column-degradation rung, cache defaults, validation and OpenAPI schemas, the settings form toggle and translations for all five locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add /api/internal/edge/{decide,next,complete,heartbeat} so a remote
executor can run the upstream call for eligible POST /v1/messages
requests while every decision stays in this service:
- decide runs the regular guard chain on a request digest (top-level
fields plus precomputed content hash, fingerprint chain and
probe/warmup flags), creates message_request, takes concurrency and
returns the first ExecutionStep. Ineligible requests are delegated
before any counting side effect.
- ExecutionStep carries the final upstream URL/headers and an ordered
body-op log whose replay on the original body is byte-identical to
the local forwarder (parity-tested).
- next feeds attempt failures through the same serial retry table as
the local forwarder (reactive rectifiers, endpoint advance, provider
switch, all-unavailable 503) and returns the next step or the exact
client error body.
- complete settles stream and non-stream outcomes with the same billing,
rate-limit accounting, circuit breaker and terminal writes as the
local response handler (parity-tested with compact SSE).
- Request state lives in Redis with a per-request lock and idempotent
replays; a watchdog settles requests whose executor stops reporting.
Legacy streaming hedge is not executed remotely yet; such requests are
delegated back to the local proxy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New Go module under edge/ that fronts client traffic and executes eligible POST /v1/messages upstream calls under the control plane's direction; everything else is reverse-proxied to the control plane. - digest, fingerprint and messages hash computed from the body and sent to /decide instead of the body itself - ordered JSON tree plus body ops and thinking-signature rectifier that replay the control plane's op log on the original body - per-step upstream transport (proxy with direct fallback, HTTP/2 with HTTP/1.1 fallback, gzip response decoding, undici-style error codes) - stream content gate, protocol observer, response fixer, client-abort metering and compact SSE capture ported from the TS proxy - failures before commit go to /next; committed streams are relayed with idle/body timeouts, heartbeats and a post-disconnect usage drain, then reported to /complete with retry and an on-disk outbox Cross-language fixtures under tests/fixtures/edge are generated from the real TS implementations (scripts/edge-fixtures) and checked by both go test and vitest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Control plane (TS): - Eligibility, digest and step builder support /v1/responses (codex) and /v1/chat/completions (openai-compatible): input normalization op + audit, codex provider overrides, chat stream_options usage op, client format and forced codex stream handling in the step. - Settlement adds codex 1M context flag and prompt_cache_key binding. - New hedge coordinator replays sendStreamingWithHedge step by step (threshold launches, shadow sessions, failure table, client abort attribution, winner commit with forced binding, loser billing). The per attempt failure handling is extracted into ProxyForwarder.handleHedgeAttemptFailure and shared with the local path. Edge executor (Go): - Routes, digest (fingerprints, rectify, compaction, codex hash) and body ops for Responses/Chat; per-format gate family, compact capture and response fixer (inert chunk filter, non-stream output normalization). - Parallel hedge execution: first-ready commit, loser cancel or bounded billing drain, peers on pre-commit client abort. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collect sibling home URLs and foreign origins for enabled providers in the same vendor/type pool, with same-origin paths remaining URL-only. Cover provider, vendor, type, and enabled-state boundaries. This utility does not change forwarding eligibility.
Record observed missing rows, the multi-worker ID reservation counterexample, and safe remediation and acceptance boundaries. No production data or configuration changes.
…eset Add codex_python_sdk as a codex-cli alias and child, and a UA rule so codex_python_sdk/* requests match both the codex-cli umbrella and the codex_python_sdk sub-client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wire the python-sdk label into user and provider client restriction editors with translations for all 5 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the /v1/models API key authentication into a shared helper and add getAvailableModelsGroupedByProviderType so callers can tell which provider type serves each model. The merged /v1/models output is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New nullable system_settings.agent_catalog_notes (Markdown), editable in Settings > Config and exposed through the v1 settings API. Includes the 0132 migration and a degradation-ladder rung for un-migrated databases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-key catalog of usable models with protocols (both directions), context window, max output, base-tier prices, capabilities, modalities and the admin notes. JSON by default, Markdown via ?format=md or Accept, and browsers are redirected to the /models page. Cached 60s per provider group and user allowedModels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/{locale}/models shows the session's models by model and by protocol, with
search, copyable machine-readable URLs and the admin notes rendered via
react-markdown + rehype-sanitize. Adds nav and my-usage entry points and
the modelCatalog namespace in all five locales.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exact (case-sensitive) names still win first. The fallback now also matches model names and aliases case-insensitively, after exact candidates, so requests like MINIMAX-M2.7-HIGHSPEED resolve to minimax-m2.7-highspeed for billing and the model catalog. The case-insensitive alias scan only runs when every indexed tier misses. Adds idx_model_prices_model_name_lower (migration 0133) so the new tier stays on an index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a nullable jsonb column for conditional Codex service tier rules and backfill non-inherit codex_service_tier_preference values into a single catch-all rule. Define the rule types (null target = unset) and zod schemas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move model-predicate and when-clause matching out of the reasoning effort evaluator into a spec-driven engine, and add evaluateServiceTierOverride on top of it. evaluateReasoningEffortOverride keeps its signature and behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Snapshot the raw client service_tier at intake and evaluate rules against original model, execution model and original tier. A null target deletes service_tier upstream. Rule evaluation is audited as serviceTierRuleEvaluation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Persist and read the new field, support set/clear in batch patches, and reject co-emission with codex_service_tier_preference or legacy-only writes once rules exist. Rules are codex-only. Regenerate OpenAPI client types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generalize the rule editor into ConditionalOverrideRuleEditor, keeping ReasoningEffortRuleEditor as a thin wrapper, and add ServiceTierRuleEditor with an unset target. Wire form state, submit payload and batch edit, and add i18n strings for all five locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Important Review skippedToo many files! This PR contains 652 files, which is 352 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (652)
You can disable this status message by setting the
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 |
There was a problem hiding this comment.
Code Review Summary - PR Already Closed
CRITICAL ISSUE DETECTED: This pull request has already been closed as of 2026-10-09 04:45:14 UTC and was NOT merged into the codebase.
PR Statistics
- Status: CLOSED (not merged)
- Size: XL (Exceeded GitHub's diff limit)
- Lines changed: 148,203 additions + 6,388 deletions = 154,591 total
- Files changed: 653 files
- Base branch: main
- Head branch: feat/service-tier-override-rules
Critical Process Violations
[CRITICAL] STANDARD-VIOLATION - Wrong Target Branch
- Issue: PR targets
mainbranch - Requirement: Per CLAUDE.md line 8, all PRs MUST target
devbranch - Quote from CLAUDE.md: "PR Target Branch:
dev(all pull requests must target the dev branch)" - Impact: Violates repository branching policy
[CRITICAL] PR-SIZE-EXCESSIVE - Unreviewable Scale
- Issue: 653 files changed with 154,591 lines modified
- Impact:
- Exceeds GitHub API diff limit (300 files)
- Cannot perform automated line-by-line review
- Impossible for human reviewers to comprehensively review
- High risk of introducing bugs undetected
- Evidence: GitHub API returned HTTP 406 with message "the diff exceeded the maximum number of files (300)"
Required Actions (If Reopening)
-
Split into Multiple PRs: This PR must be decomposed into logical, reviewable units
- Suggested splits:
- Database migrations (drizzle/*)
- Go edge service (edge/*)
- TypeScript/Node.js changes
- Documentation updates
- Configuration changes
- Suggested splits:
-
Retarget to
devBranch: Change base branch frommaintodev -
Each Split PR Should:
- Be < 500 lines changed
- Be < 20 files
- Have a clear, single purpose
- Include corresponding tests
- Pass all pre-commit checks per CLAUDE.md
Review Impossibility Statement
Due to the PR being closed and the diff exceeding GitHub's technical limits, a comprehensive line-by-line code review cannot be performed. The PR would need to be:
- Split into reviewable chunks
- Reopened as multiple smaller PRs
- Properly targeted to the
devbranch
Automated review by Claude AI - Review incomplete due to PR closure and excessive size
| const serviceTierPayload = hasServiceTierRules | ||
| ? { service_tier_override_rules: state.routing.serviceTierOverrideRules } |
There was a problem hiding this comment.
ServiceTierOverrideRuleDraft allows overrideServiceTier: "", but this payload passes the draft directly to addProvider and editProvider, which accept only ServiceTierOverrideRule[]. Both calls fail TypeScript checks, and build runs those checks before Next.js. Parse the draft into the saved rule type before submitting it.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx
Line: 433-434
Comment:
**Rule drafts break the build**
`ServiceTierOverrideRuleDraft` allows `overrideServiceTier: ""`, but this payload passes the draft directly to `addProvider` and `editProvider`, which accept only `ServiceTierOverrideRule[]`. Both calls fail TypeScript checks, and `build` runs those checks before Next.js. Parse the draft into the saved rule type before submitting it.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| ALTER TABLE "providers" ADD COLUMN "new_api_access_token" varchar;--> statement-breakpoint | ||
| ALTER TABLE "providers" ADD COLUMN "new_api_user_id" integer;--> statement-breakpoint | ||
| ALTER TABLE "system_settings" ADD COLUMN "enable_memory_admission" boolean DEFAULT false NOT NULL; |
There was a problem hiding this comment.
Existing databases cannot upgrade
Databases upgraded through the base branch already have these three columns from 0123 and 0124. Moving their changes into 0131 makes these plain ADD COLUMN statements fail, so startup exits. The rewritten journal also places new required migrations below the base database's latest applied timestamp, so they are skipped.
Keep applied migration history intact and append the new changes after it. Adding IF NOT EXISTS here alone does not fix the skipped changes.
Prompt To Fix With AI
This is a comment left during a code review.
Path: drizzle/0131_talented_adam_warlock.sql
Line: 1-3
Comment:
**Existing databases cannot upgrade**
Databases upgraded through the base branch already have these three columns from `0123` and `0124`. Moving their changes into `0131` makes these plain `ADD COLUMN` statements fail, so startup exits. The rewritten journal also places new required migrations below the base database's latest applied timestamp, so they are skipped.
Keep applied migration history intact and append the new changes after it. Adding `IF NOT EXISTS` here alone does not fix the skipped changes.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| const serviceTierPayload = hasServiceTierRules | ||
| ? { service_tier_override_rules: state.routing.serviceTierOverrideRules } | ||
| : hadServiceTierRulesInitially | ||
| ? { service_tier_override_rules: null } | ||
| : { codex_service_tier_preference: state.routing.codexServiceTierPreference }; | ||
| const submitFormData = { ...baseFormData, ...effortPayload, ...serviceTierPayload }; |
There was a problem hiding this comment.
Changing an existing Codex provider to another type leaves its service tier rules in the form and submits them with the new type. Even a Codex provider using inherit loads an empty rule array. The server rejects service_tier_override_rules for every non-Codex type, while the editor disappears after the type changes.
Clear or omit incompatible rules when changing types, and allow the server to clear old rules as part of that change.
Knowledge Base Used: Provider configuration management
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx
Line: 433-438
Comment:
**Provider type changes fail**
Changing an existing Codex provider to another type leaves its service tier rules in the form and submits them with the new type. Even a Codex provider using `inherit` loads an empty rule array. The server rejects `service_tier_override_rules` for every non-Codex type, while the editor disappears after the type changes.
Clear or omit incompatible rules when changing types, and allow the server to clear old rules as part of that change.
**Knowledge Base Used:** [Provider configuration management](https://app.greptile.com/ygxz/-/custom-context/knowledge-base/ding113/claude-code-hub/-/docs/provider-configuration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| } else if (serviceTier.action === "unset" && Object.hasOwn(output, "service_tier")) { | ||
| delete ensureCloned().service_tier; |
There was a problem hiding this comment.
Fallback loses the requested tier
An unset rule deletes service_tier from the shared request body. If that provider fails, a fallback provider with empty or unmatched rules receives the already-edited body and cannot preserve the client's original tier. For example, a client requesting priority reaches the fallback provider without that field.
Restore the original tier before applying each new provider's rules, without undoing same-provider repairs.
Knowledge Base Used: Provider protocol adapters
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/codex/provider-overrides.ts
Line: 490-491
Comment:
**Fallback loses the requested tier**
An unset rule deletes `service_tier` from the shared request body. If that provider fails, a fallback provider with empty or unmatched rules receives the already-edited body and cannot preserve the client's original tier. For example, a client requesting `priority` reaches the fallback provider without that field.
Restore the original tier before applying each new provider's rules, without undoing same-provider repairs.
**Knowledge Base Used:** [Provider protocol adapters](https://app.greptile.com/ygxz/-/custom-context/knowledge-base/ding113/claude-code-hub/-/docs/provider-protocol-adapters.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| const timer = setTimeout(() => { | ||
| if (settled) return; | ||
| settled = true; | ||
| logger.warn("[MessageRequestInsertBuffer] Timed out waiting for buffered insert", { id }); | ||
| resolve(); |
There was a problem hiding this comment.
With asynchronous inserts enabled, awaitMessageRequestInserted resolves after five seconds even if the insert has not committed. The direct winner and loser cost writers then update a row that may not exist and treat zero updated rows as success. If the insert later succeeds, those costs are already lost.
Return a failure on timeout and retry the cost write only after the insert is confirmed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/repository/message-insert-buffer.ts
Line: 286-290
Comment:
**Slow inserts lose charges**
With asynchronous inserts enabled, `awaitMessageRequestInserted` resolves after five seconds even if the insert has not committed. The direct winner and loser cost writers then update a row that may not exist and treat zero updated rows as success. If the insert later succeeds, those costs are already lost.
Return a failure on timeout and retry the cost write only after the insert is confirmed.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| } finally { | ||
| await releaseEdgeConcurrency(state); | ||
| state.phase = "settled"; | ||
| await persistRuntime(rt); |
There was a problem hiding this comment.
Recovery releases counters twice
Edge completion decrements shared concurrency counters before saving the settled state. If the worker stops between those operations, Redis still holds the earlier completing state with the original counter references. After the grace period, the watchdog loads that state and decrements the same counters again, undercounting other live requests sharing the session.
Make release happen once per request in Redis. Clearing references only in the local object cannot protect this recovery path.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/edge/handlers.ts
Line: 506-509
Comment:
**Recovery releases counters twice**
Edge completion decrements shared concurrency counters before saving the settled state. If the worker stops between those operations, Redis still holds the earlier `completing` state with the original counter references. After the grace period, the watchdog loads that state and decrements the same counters again, undercounting other live requests sharing the session.
Make release happen once per request in Redis. Clearing references only in the local object cannot protect this recovery path.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
This is a large feature branch merge containing multiple enhancements and infrastructure improvements developed over versions v0.9.1 through v0.9.5. The primary feature is conditional reasoning effort override rules, along with significant improvements to Langfuse tracing, new ClickHouse log sync infrastructure, provider routing enhancements, and a new Go-based edge gateway service.
mainbut according to CLAUDE.md, all PRs must target thedevbranch. The base branch should be changed before merging.Related Issues
Key Features
1. Conditional Reasoning Effort Override Rules
Configure dynamic reasoning effort overrides based on conditional rules per provider/client, allowing fine-grained control over model reasoning behavior.
Core changes:
2. Langfuse Tracing Enhancements
client_metadata(credentials masked)user:shortModelformat (strip provider prefix)LANGFUSE_TRACING_ENVIRONMENTandLANGFUSE_RELEASEenvironment variables3. ClickHouse Request Log Sync
New infrastructure for syncing PostgreSQL usage logs to ClickHouse for analytics and long-term storage.
New files:
src/lib/clickhouse/- HTTP client and table DDLdocs/clickhouse-request-log-sync.md- Documentation4. Provider Prefix Routing
Route requests by provider prefix and strip prefixes upstream, enabling multi-tenant provider isolation.
Changes:
provider_prefixcolumn in database schema5. Edge Gateway (Go Service)
New Go-based edge service for high-performance request processing.
Location:
edge/directory6. Memory Admission Control
Improvements to prevent memory exhaustion under high concurrency:
7. Additional Enhancements
{{header.Name}},{{session.id}},{{session.client_id}}/metricsscrape endpoint with traffic gaugesPROXY_LISTEN_PREFIXfor path-based routing/v1/modelsby user'sallowedModelsDatabase Migrations
15 new migrations (0119-0134):
Breaking Changes
None identified - changes are additive or backward-compatible.
Documentation Added
docs/api-key-validation-workflow.md- API key validation workflowdocs/clickhouse-request-log-sync.md- ClickHouse sync setupdocs/k8s-deployment.md- Kubernetes deployment guidedocs/multicore-gateway.md- Multi-core gateway configurationdocs/research/clickhouse-id-cursor-root-cause-handoff.md- Technical researchTesting
Given the scope (100 files changed), comprehensive testing is critical:
Automated Tests
bun run test)bun run typecheck)bun run lint)bun run db:generateclean)Integration Testing
Performance Testing
Deployment Considerations
Environment Variables: New variables needed:
LANGFUSE_TRACING_ENVIRONMENTLANGFUSE_RELEASEPROXY_LISTEN_PREFIX(optional)Database Migrations: Run
bun run db:migrateor enableAUTO_MIGRATE=trueEdge Gateway: Optional Go service deployment if using edge processing
Breaking CLAUDE.md Rule: This PR violates the "all PRs must target dev" rule - needs discussion
Checklist
maintodev(per CLAUDE.md)Merge Strategy
Given the size and scope of this PR (100 commits, 100 files):
🤖 Generated with Claude Code
Do not merge until the build and database upgrade failures are fixed, along with the rule, billing, and edge recovery defects.
Findings
Fix with agent prompt
Summary
This PR adds ordered service tier and reasoning effort rules, including removing
service_tier. It also adds an edge executor, asynchronous request inserts, ClickHouse log export, model catalog pages, and shared caches.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Admin[Provider settings] --> Save[Validate and save rules] Save --> Provider[Selected provider] Client[Client request] --> Intake[Capture original model and tier] Intake --> Provider Provider --> Rules[First matching rule] Rules --> Tier[Set or remove service tier] Tier --> Upstream[Forward request] Upstream --> Result[Return response and record usage] Upstream -->|Failure| Fallback[Select another provider] Fallback --> RulesReviews (1) · Last reviewed commit: "feat(ui): edit conditional service tier ..." · Reviewed by Greptile