{% extends "admin_base.html" %} {% block extra_head %} {{ super() }} {% endblock %} {% block admin_content %}

Gateway routing

{{ providers|length }} providers, {{ models|length }} routes, {{ quota_rules|length }} quota rules

Map any requested model name onto a provider and target model, each with its own pricing economy and an optional vision model for image to text merging. Image routes use a flat per-image price. Unmapped requests fall through to the default upstream unchanged.

{% if tab == 'models' %}

Model routes

Add model route

Each source model maps to a provider and target model with its own economy. Chat uses cache-hit, cache-miss and output prices (USD per 1M tokens); embeddings use the input price; image routes use the input price as a flat USD per image; a vision model adds input and output pricing for the image description merge. An optional fallback model is retried once, automatically, whenever this route fails after its own retries are exhausted.

{% if not models %} {% endif %} {% for m in models %} {% endfor %}
Model routes
SourceProviderTargetKindVisionEconomyActions
No model routes. Requests fall through to the default upstream.
{{ m.source_model }} {{ m.provider or "default" }} {{ m.target_model }} {{ m.kind }} {% if m.vision_model %} {{ m.vision_provider or m.provider or "default" }}/{{ m.vision_model }} {% else %} - {% endif %} {% if m.context_tier_threshold_tokens %}tiered{% endif %} {% if m.off_peak_start_minute is not none %}off-peak{% endif %} {% if m.kind == 'image' and m.price_input_per_m %}${{ m.price_input_per_m }}/img{% endif %} {% if m.fallback_model %}fallback: {{ m.fallback_model }}{% endif %} {% if not m.context_tier_threshold_tokens and m.off_peak_start_minute is none and not (m.kind == 'image' and m.price_input_per_m) and not m.fallback_model %}-{% endif %} Edit
{% endif %} {% if tab == 'providers' %}

Providers

Add provider

Named upstreams reused across model routes. A model route with a blank provider uses the default below.

default (from Services config): chat {{ default_provider.model }} at {{ default_provider.base_url }}, embed {{ default_provider.embed_model }}, image {{ default_provider.image_model }}, vision {{ default_provider.vision_model }}
{% if not providers %} {% endif %} {% for p in providers %} {% endfor %}
Providers
NameBase URLActiveClient profileActions
No extra providers. Model routes with a blank provider use the default.
{{ p.name }} {{ p.base_url }} {{ "yes" if p.is_active else "no" }} {{ p.client_profile or "generic" }} Edit
{% endif %} {% if tab == 'quota' %}

Quota rules

Add quota rule

Rolling 24h USD caps on /openai/v1/*. A rule scopes by any combination of role, specific user, and app label (the X-App-Reference header); the most specific active match wins, and a rule that omits a dimension pools spend across everyone matching it. With no matching rule, the global defaults below apply per caller. A limit of 0 means unlimited.

global defaults (from Services config, apply per caller with no matching rule): member {{ "$%.2f/24h"|format(quota_defaults.user) if quota_defaults.user else "unlimited" }}, admin {{ "$%.2f/24h"|format(quota_defaults.admin) if quota_defaults.admin else "unlimited" }}, guest {{ "$%.2f/24h"|format(quota_defaults.guest) if quota_defaults.guest else "unlimited" }}, internal {{ "$%.2f/24h"|format(quota_defaults.internal) if quota_defaults.internal else "unlimited" }}, access key {{ "$%.2f/24h"|format(quota_defaults.key) if quota_defaults.key else "unlimited" }}
{% if not quota_rules %} {% endif %} {% for r in quota_rules %} {% endfor %}
Quota rules
ScopeLimit / 24hSpent 24hActiveLabelActions
No quota rules. Every caller is capped by the global defaults above.
role={{ r.owner_kind or "any" }}, user={{ r.owner_id or "any" }}, app={{ r.app_reference or "any" }} {{ "$%.2f"|format(r.limit_usd) if r.limit_usd else "unlimited" }} ${{ "%.4f"|format(r.spent_24h_usd) }} {{ "yes" if r.is_active else "no" }} {{ r.label }} Edit
{% endif %} {% if tab == 'stats' %}

Request volume, latency, and per-model reliability, computed from the gateway usage ledger you already record - no separate data collection.

Total requests
-
Success rate
-
Models tracked
-
Last updated
-

Requests over time

Success / failure

Requests by model

Requests by endpoint

HTTP status codes

Streaming vs non-streaming

Top failure reasons

Requests by hour of day (UTC)

Latency distribution

Tokens/sec distribution

Model reliability weight

Prompt vs completion tokens

Per-model detail

Pick a model above to see its own timeseries and latency distribution.

Model pool

Model pool
ModelProviderRequestsSuccessAvg latencyAvg tok/sWeightCircuitPrompt tokCompletion tok
Loading...

Recent failures

Recent failures
WhenModelProviderEndpointStatusReasonFell back to
Loading...
{% endif %} {% endblock %} {% block extra_js %} {% if tab == 'stats' %} {% endif %} {% endblock %}