Repository navigation
docs: sync docs with gateway/API implementation #3360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,16 +65,14 @@ To use caching: | |
|
|
||
| The cache key is generated from these request parameters: | ||
|
|
||
| - Model identifier | ||
| - Provider and model identifier | ||
| - Messages array (roles and content) | ||
| - Temperature | ||
| - Temperature, Top P, frequency/presence penalty | ||
| - Max tokens | ||
| - Top P | ||
| - Tools/functions | ||
| - Tool choice | ||
| - Response format | ||
| - System prompt | ||
| - Other model-specific parameters | ||
| - Reasoning effort and reasoning max tokens | ||
| - Prompt-cache key, retention, and options | ||
| - `n` (number of choices) and service tier | ||
|
Comment on lines
+68
to
+75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Expect both fields in the serialized cache key, or an explicit cache bypass.
rg -n -C 8 \
'cache[_-]?key|cacheKey|tool_choice|tools' \
apps/api apps/gateway \
--glob '*.{ts,tsx}'Repository: theopenco/llmgateway Length of output: 50377 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- cache key generation definitions/usages ---\n'
rg -n "function generateCacheKey|function generateStreamingCacheKey|generateCacheKey|generateStreamingCacheKey|cachePayload|cacheKey" apps/gateway apps/api --glob '*.{ts,tsx}' | head -n 200
printf '\n--- file with cachePayload definition ---\n'
sed -n '5430,5475p' apps/gateway/src/chat/chat.ts
printf '\n--- cache key test files ---\n'
fd -a ".*cache.*spec.*|.*cache.*test.*|.*caching.*spec.*|.*caching.*test.*" apps --glob '*.{ts,tsx}' | sed 's#^\./##'Repository: theopenco/llmgateway Length of output: 249 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- cache key generation definitions/usages ---'
rg -n "function generateCacheKey|function generateStreamingCacheKey|generateCacheKey|generateStreamingCacheKey|cachePayload|cacheKey" apps/gateway apps/api --glob '*.{ts,tsx}' | head -n 200 || true
echo
echo '--- payload around gateway cachePayload definition ---'
sed -n '5430,5475p' apps/gateway/src/chat/chat.ts
echo
echo '--- cache spec/test files ---'
fd -a '.*cache.*spec.*|.*cache.*test.*|.*caching.*spec.*|.*caching.*test.*' apps --glob '*.{ts,tsx}' | sed 's#^\./##' | head -n 100 || trueRepository: theopenco/llmgateway Length of output: 3033 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- locate cacheKey module ---'
rg -n "generateCacheKey|generateStreamingCacheKey" apps/gateway/src apps/gateway --glob '*.{ts,tsx}' | head -n 160
echo
echo '--- cache key implementation files ---'
git ls-files 'apps/gateway/src/**/*.ts' 'apps/gateway/src/**/*.tsx' | xargs rg -l "generateCacheKey|generateStreamingCacheKey" | sed 's#^\./##'Repository: theopenco/llmgateway Length of output: 849 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- apps/gateway/src/chat/chat.ts imports and cache key exports ---'
sed -n '1,150p' apps/gateway/src/chat/chat.ts
echo
echo '--- all cache-related references in gateway chat file ---'
rg -n -C 5 "cache|Cache|Caching|caching|no-cache|x-no-cache|generateCacheKey|generateStreamingCacheKey" apps/gateway/src/chat/chat.ts | head -n 240 || true
echo
echo '--- package/config hints ---'
git ls-files | rg -n '(^|/)cache|cache$|gateway-caching|caching' || true
rg -n "generateCacheKey|generateStreamingCacheKey" . --glob '!node_modules' --glob '!dist' --glob '!build' | head -n 200 || trueRepository: theopenco/llmgateway Length of output: 15444 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- packages/cache/src/cache.ts ---'
cat -n packages/cache/src/cache.ts
echo
echo '--- deterministic cache key shape probe from source payload mapping ---'
python3 - <<'PY'
payload = {
"provider": "provider-A",
"model": "model-1",
"messages": [{"role": "user", "content": "same"}],
"temperature": 0.5,
"max_tokens": 100,
"top_p": 0.9,
"frequency_penalty": 0,
"presence_penalty": 0,
"response_format": {"type": "json_object"},
"reasoning_effort": "medium",
"reasoning_max_tokens": 256,
"prompt_cache_key": "key",
"prompt_cache_retention": None,
"prompt_cache_options": None,
"n": 1,
"service_tier": None,
}
payload_with_tools_a = {
**payload,
"tools": [{"type": "function", "name": "a"}],
"tool_choice": None,
}
payload_with_tools_b = {
**payload,
"tools": [{"type": "function", "name": "b"}],
"tool_choice": None,
}
# model implementation serializes only this fixed object key order via JSON.stringify(payload)
import json
print(json.dumps([
json.dumps(payload),
json.dumps(payload_with_tools_a),
json.dumps(payload_with_tools_b),
json.dumps(payload_with_tools_a) == json.dumps(payload_with_tools_b),
]))
PYRepository: theopenco/llmgateway Length of output: 4874 Add
🤖 Prompt for AI Agents |
||
|
|
||
| <Callout type="info"> | ||
| Requests with different parameter values, even slight variations, will not | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,6 +176,16 @@ Anthropic returns a per-TTL breakdown of cache writes when you mix `5m` and `1h` | |
|
|
||
| For providers that publish a separate explicit-cache read rate (for example, Alibaba Qwen charges 10% for explicit cache reads vs. 20% for automatic cache reads), LLM Gateway detects the `cache_control` markers on your request and applies the explicit rate automatically. | ||
|
|
||
| ## OpenAI Prompt Caching Fields | ||
|
|
||
| For OpenAI models, LLM Gateway also forwards OpenAI's own prompt-caching request fields: | ||
|
|
||
| - `prompt_cache_key` — a string used to improve cache routing for requests that share a prompt prefix. It also doubles as a fallback [sticky session](/features/sessions) key if no session header is set. | ||
| - `prompt_cache_retention` — `"in_memory"` or `"24h"`, OpenAI's cache retention policy for eligible models. | ||
| - `prompt_cache_options` — explicit prompt caching controls for GPT-5.6 and later: `{ "mode": "implicit" | "explicit", "ttl": "30m" }`. In `implicit` mode (the default) OpenAI places an automatic cache breakpoint at the latest message. In `explicit` mode, only content parts you mark with a `prompt_cache_breakpoint` object are cached. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
file="apps/docs/content/features/caching/provider-cache-control.mdx"
if [ -f "$file" ]; then
echo "== file excerpt =="
sed -n '160,200p' "$file" | cat -n
else
echo "missing $file"
fi
echo "== nearby references to prompt_cache_options / prompt_cache_breakpoint ========="
rg -n "prompt_cache_options|prompt_cache_breakpoint|GPT-5\.6|implicit|explicit" apps/docs/content -S || true
echo "== JSON syntax probe for the exact object text =="
python3 - <<'PY'
import json, pathlib, re
p=pathlib.Path('apps/docs/content/features/caching/provider-cache-control.mdx')
s=p.read_text()
m=re.search(r'`prompt_cache_options`.+?\[(.*?)\]', s, re.S)
print("has_match", bool(m))
if m:
print("object_text")
print(m.group(0))
try:
json.loads(m.group(0))
print("json_parse=ok")
except Exception as e:
print("json_parse_error", type(e).__name__, e)
PYRepository: theopenco/llmgateway Length of output: 12345 Use valid JSON for The object at line 185 includes 🤖 Prompt for AI Agents📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== target file excerpt =="
sed -n '160,205p' apps/docs/content/features/caching/provider-cache-control.mdx || true
echo
echo "== occurrences of GPT-5.6 and later in target =="
rg -n "GPT-5\.6|later|prompt_cache_breakpoint|prompt_cache_options" apps/docs/content/features/caching/provider-cache-control.mdx || true
echo
echo "== docs catalogue/live-page clues =="
rg -n "catalogue|catalog|gpt-5|GPT-5|model.*capability|capability.*page|implicit|explicit" apps/docs/content -g '*.md' -g '*.mdx' | head -200 || trueRepository: theopenco/llmgateway Length of output: 40209 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== repository files related to catalog/models/caching =="
git ls-files | rg '(^|/)(catalog|model|provider|caching|capability|gateway|README|.*\.ts$|.*\.js$|.*\.json$)' | head -300
echo
echo "== prompt_cache_options definitions/usages in repo =="
rg -n "prompt_cache_options|prompt_cache_breakpoint|prompt_cache_retention|prompt_cache_key|cache_control|openai" . --glob '!**/node_modules/**' --glob '!**/.git/**' | head -300 || true
echo
echo "== docs catalogue/live link patterns nearby =="
rg -n "llmgateway\.io/models|models\.llmgateway|provider catalog|catalogue|catalog" apps/docs/content/features apps/docs/content/learn apps/docs/content/developers apps/docs/content/guides -g '*.md' -g '*.mdx' | head -200 || true
echo
echo "== read docs models/caching docs relevant links =="
for f in apps/docs/content/learn/models.mdx apps/docs/content/features/models-directory.mdx apps/docs/content/features/caching/index.mdx; do
if [ -f "$f" ]; then
echo "--- $f ---"
sed -n '1,80p' "$f"
fi
doneRepository: theopenco/llmgateway Length of output: 11320 Replace the
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| These fields are only forwarded to OpenAI models that support them; they have no effect on other providers. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 6 \
'prompt_cache_key|prompt_cache_retention|prompt_cache_options' \
apps/api apps/gateway apps/docs \
--glob '*.{ts,tsx,md,mdx}'Repository: theopenco/llmgateway Length of output: 39968 Narrow the OpenAI-only forwarding statement. The list includes 🤖 Prompt for AI Agents |
||
|
|
||
| ## Related | ||
|
|
||
| - [Gateway Caching](/features/caching/gateway-caching) — serve byte-identical requests entirely from LLM Gateway at $0 cost | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||
| title: Multiple Choices | ||||||||||||||||||||||||||
| description: Generate several completions for one prompt in a single request with the `n` parameter. | ||||||||||||||||||||||||||
| icon: Copy | ||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { Callout } from "fumadocs-ui/components/callout"; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Multiple Choices | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| The OpenAI-compatible `n` parameter requests multiple independent completions for the same prompt in a single call, returned as separate entries in the `choices` array. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||
| curl -X POST "https://api.llmgateway.io/v1/chat/completions" \ | ||||||||||||||||||||||||||
| -H "Authorization: Bearer $LLM_GATEWAY_API_KEY" \ | ||||||||||||||||||||||||||
| -H "Content-Type: application/json" \ | ||||||||||||||||||||||||||
| -d '{ | ||||||||||||||||||||||||||
| "model": "gpt-5", | ||||||||||||||||||||||||||
| "n": 3, | ||||||||||||||||||||||||||
| "messages": [ | ||||||||||||||||||||||||||
| { "role": "user", "content": "Give me a tagline for a coffee shop." } | ||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||
| }' | ||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Each entry in the response `choices` array has its own `index` and `message`, so you can compare or pick among the generated variants. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Supported Providers | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| `n` is only forwarded when the resolved model supports it upstream: | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| - **OpenAI Chat Completions** models | ||||||||||||||||||||||||||
| - **Google Gemini** models (mapped to Gemini's native `candidateCount` parameter) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+28
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Replace the hardcoded provider list with a live catalogue link. This section hardcodes a catalogue-derived provider list. Describe support in terms of resolved model mappings and link to the live model catalogue. Proposed direction `n` is only forwarded when the resolved model supports it upstream:
-- **OpenAI Chat Completions** models
-- **Google Gemini** models (mapped to Gemini's native `candidateCount` parameter)
+See the [live model catalogue](/models) for current mappings that support `n`.As per coding guidelines, 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||||||||
| Requests with `n` for any other provider/model are rejected with a `400` error. | ||||||||||||||||||||||||||
|
Comment on lines
+30
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Scope the rejection statement to The model mapping contract in Lines 400-420 of -Requests with `n` for any other provider/model are rejected with a `400` error.
+Requests with `n > 1` for any other provider/model are rejected with a `400` error.📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| ## Streaming | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Streaming is supported for OpenAI models: choice deltas for all `n` choices are demultiplexed on a single SSE stream, distinguished by `choices[].index`. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| <Callout type="warn"> | ||||||||||||||||||||||||||
| A few combinations are rejected with a `400` error instead of silently | ||||||||||||||||||||||||||
| falling back to `n: 1`: | ||||||||||||||||||||||||||
| - `n > 1` with `stream: true` **and** function `tools` — the streaming | ||||||||||||||||||||||||||
| tool-call aggregator can't disambiguate concurrent tool calls across | ||||||||||||||||||||||||||
| choices. Native `web_search` tools and the `web_search: true` flag are | ||||||||||||||||||||||||||
| exempt from this restriction. | ||||||||||||||||||||||||||
| - `n > 1` with `stream: true` on Google models — Gemini's | ||||||||||||||||||||||||||
| `streamGenerateContent` does not support `candidateCount`. | ||||||||||||||||||||||||||
| - `n > 8` on Google models — Gemini caps `candidateCount` at 8. | ||||||||||||||||||||||||||
| </Callout> | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -208,8 +208,11 @@ Sticky session routing solves this: attach a session identifier and LLMGateway p | |
| For chat completions, the session key is resolved in priority order: | ||
|
|
||
| 1. The `x-session-id` header | ||
| 2. The `prompt_cache_key` body field (OpenAI-compatible) | ||
| 3. The `user` body field (OpenAI-compatible) | ||
| 2. The `x-session-affinity` header (sent by some coding agents, e.g. opencode) | ||
| 3. The `session_id` header | ||
| 4. The `session-id` header | ||
| 5. The `prompt_cache_key` body field (OpenAI-compatible) | ||
| 6. The `user` body field (OpenAI-compatible) | ||
|
|
||
| ```bash | ||
| curl -X POST "https://api.llmgateway.io/v1/chat/completions" \ | ||
|
|
@@ -400,7 +403,12 @@ Automatic retry to a different provider is disabled when: | |
| - The maximum retry count (2) has been exhausted | ||
|
|
||
| Retries can still happen within the same provider when multiple keys are | ||
| configured and the current key fails with a retryable error. | ||
| configured and the current key fails with a retryable error — including a | ||
| **401/403 auth failure**, since that's often isolated to a single bad | ||
| credential rather than the whole provider. Same-key retries back off | ||
| exponentially (1s, 2s, 4s, ...) up to **2 retries** by default (configurable | ||
|
Comment on lines
405
to
+409
|
||
| via the `SAME_KEY_MAX_RETRIES` environment variable), unlike cross-provider | ||
| fallback which retries immediately against a different upstream. | ||
|
Comment on lines
405
to
+411
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Distinguish same-provider key retries from cross-provider fallback. Lines 391-394 state that retries do not trigger for 401/403 failures. This paragraph states that same-provider retries can include 401/403. Rewrite the earlier rule as applying to cross-provider fallback, and state that 401/403 retries occur only against another key for the same provider. 🤖 Prompt for AI Agents |
||
|
|
||
| ### Routing Transparency | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ The Billing page is your central hub for managing credits, plans, and payment me | |
|
|
||
| Displays your current credit balance. Credits are consumed as you make API requests through the gateway. Click **Top Up Credits** to add more credits to your account. | ||
|
|
||
| Once your organization has enough usage history, the Credits page also shows an estimated **runway** — approximately how many days your current balance will last at your recent average daily spend. | ||
|
|
||
|
Comment on lines
+17
to
+18
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Document the actual runway window and no-usage condition. The Credits page reads Suggested wording-Once your organization has enough usage history, the Credits page also shows an estimated **runway** — approximately how many days your current balance will last at your recent average daily spend.
+When your organization has recent usage, the Credits page also shows an estimated **runway** — approximately how many days your current balance will last based on your average daily spend over the last seven days.🧰 Tools🪛 LanguageTool[style] ~17-~17: This is not the usual sequence for adjectives that have no special emphasis. (EN_ADJ_ORDER) 🤖 Prompt for AI Agents |
||
| ## Fees | ||
|
|
||
| Top-ups are charged the credit amount plus the following fees: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ description: Manage team members and their roles within your organization | |
| icon: UsersRound | ||
| --- | ||
|
|
||
| import { Callout } from "fumadocs-ui/components/callout"; | ||
| import { ThemedImage } from "@/components/themed-image"; | ||
|
|
||
| The Team page lets you invite team members, assign roles, and control access to your organization. | ||
|
|
@@ -19,6 +20,15 @@ Click **Add Member** to invite someone by email. You'll need to: | |
|
|
||
| Your plan includes up to **5 team seats**. The current count is displayed, and the Add button is disabled when all seats are used. Contact sales for additional seats. | ||
|
|
||
| <Callout type="info"> | ||
| Assigning the **Developer** role requires the Enterprise plan, since it scopes | ||
| the member's access to a specific set of projects. On other plans, the | ||
| Developer option is disabled — invite as Admin instead, or upgrade to | ||
| Enterprise. | ||
| </Callout> | ||
|
|
||
| When inviting a Developer, you must also select at least one project — the developer is limited to those projects and can be granted or revoked access to additional projects later from their detail page. | ||
|
|
||
| ## Team Members List | ||
|
|
||
| Each member shows: | ||
|
|
@@ -58,3 +68,7 @@ For the full rule semantics, see [Member-Level IAM Rules](/features/api-keys#mem | |
| | **Developer** | View and use resources only. Cannot modify settings or manage team | | ||
|
|
||
| Developers can also be given **restricted access** at the API key level, limiting which keys they can view and use. | ||
|
|
||
| ## Default Developer Budget | ||
|
|
||
| Owners and admins on Enterprise can set an org-wide **default developer budget** — a usage limit and period usage limit applied to every Developer who doesn't have their own individual budget set. A member's own budget, when configured via **Manage budget**, always overrides the default. | ||
|
Comment on lines
+71
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 \
'SSO_TEAM_DEFAULT_DEVELOPER_BUDGET|default developer budget|periodUsageLimit|Manage budget' \
packages appsRepository: theopenco/llmgateway Length of output: 50379 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== candidate files =="
git ls-files | rg '(^packages/shared/src/member-budget-limits\.ts$|^packages/db/src/member-budget\.ts$|^apps/docs/content/learn/team\.mdx$|Manage budget|ManageBudget|memberBudget)' || true
echo
echo "== docs section =="
sed -n '55,85p' apps/docs/content/learn/team.mdx | cat -n
echo
echo "== UI/budget mentions excluding docs broad search =="
rg -n -C 4 \
'defaultDeveloperMaxApiKeys|defaultDeveloperUsageLimit|defaultDeveloperPeriodUsageLimit|SSO_TEAM_DEFAULT_DEVELOPER_BUDGET|ManageBudget|Manage budget|Manage Budget|maxApiKeys|periodUsageLimit' \
apps packages \
--glob '!apps/docs/**' \
--glob '!packages/db/src/seed.ts' \
--max-count 200 || true
echo
echo "== focused budget resolver tests =="
if git ls-files | grep -q 'packages/db/src/member-budget.spec.ts'; then
sed -n '1,220p' packages/db/src/member-budget.spec.ts | cat -n
fiRepository: theopenco/llmgateway Length of output: 50377 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== sso provisioning budget handling =="
sed -n '68,130p' apps/api/src/routes/sso.ts | cat -n
sed -n '420,448p' apps/api/src/routes/sso.ts | cat -n
echo
echo "== organization route default developer budget handling =="
sed -n '140,230p' apps/api/src/routes/organization.ts | cat -n
rg -n -C 4 'defaultDeveloper(MaxApiKeys|UsageLimit|PeriodUsageLimit|PeriodUsageDuration)' apps/api/src/routes/organization.ts
echo
echo "== route docs for defaultDeveloperMaxApiKeys =="
rg -n -C 8 'defaultDeveloperMaxApiKeys' apps/docs apps/api/src/routes -g '!apps/docs/content/**' || trueRepository: theopenco/llmgateway Length of output: 21063 Document the full default developer budget cap. The SSO default is a $500/month recurring budget plus a separate 3 active API key cap. Include both parts of the org default, and replace “usage limit” with the actual recurring/period budget wording so members do not expect an uncapped lifetime limit. 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,13 +25,21 @@ Each transaction entry includes: | |
|
|
||
| ## Transaction Types | ||
|
|
||
| | Type | Description | | ||
| | ----------------------- | ----------------------------------- | | ||
| | **Credit Top-up** | Manual or automatic credit purchase | | ||
| | **Credit Refund** | Credits refunded to your account | | ||
| | **Subscription Start** | New plan subscription started | | ||
| | **Subscription Cancel** | Plan subscription canceled | | ||
| | **Subscription End** | Plan subscription period ended | | ||
| | Type | Description | | ||
| | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | ||
| | **Credit Top-up / Refund / Gift** | A manual/automatic credit purchase, a refund, or credits granted by an admin | | ||
| | **Subscription Start / Cancel / End** | Legacy plan subscription lifecycle events | | ||
| | **Dev Plan Start / Upgrade / Downgrade / Cancel / Resume / End / Renewal** | [DevPass](https://llmgateway.io/devpass) subscription lifecycle events | | ||
| | **Dev Plan Reset Pass** | A purchased, rewarded, or gifted DevPass Reset Pass | | ||
| | **Chat Plan Start / Upgrade / Downgrade / Cancel / Resume / End / Renewal** | Chat plan subscription lifecycle events | | ||
| | **End-User Top-up / Refund / Bonus** | LLM SDK end-user wallet activity, for organizations issuing end-user wallets | | ||
| | **End-User Margin Accrual / Payout** | Margin earned on end-user wallet usage and its payout to the organization | | ||
|
Comment on lines
+28
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 6 \
'transaction\.type|devpass|dev_plan|chat_plan|end.?user|credit_(topup|refund|gift)|subscription_(start|cancel|end)' \
apps packagesRepository: theopenco/llmgateway Length of output: 50379 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== candidate transaction files =="
git ls-files | rg -n 'transaction|Transaction|TransactionsClient|transactions\.mdx' || true
echo
echo "== docs snippet =="
sed -n '1,80p' apps/docs/content/learn/transactions.mdx
echo
echo "== focused source matches (top 200) =="
rg -n -C 4 \
'getTransactionLabel|formatTransactionLabel|transaction\.type|TransactionType|devPlan|chatPlan|EndUser|credit_(topup|refund|gift)|subscription_(start|cancel|end)|DevPass|Reset Pass' \
apps packages --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' | head -n 500Repository: theopenco/llmgateway Length of output: 37245 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== dashboard transaction clients =="
wc -l apps/ui/src/components/billing/transactions-client.tsx apps/ui/src/components/dashboard/transactions-client.tsx apps/ui/src/app/dashboard/[orgId]/org/transactions/page.tsx
echo
ast-grep outline apps/ui/src/components/billing/transactions-client.tsx apps/ui/src/components/dashboard/transactions-client.tsx --view expanded || true
echo
echo "== relevant transaction-client snippets =="
sed -n '1,260p' apps/ui/src/components/billing/transactions-client.ts
sed -n '1,260p' apps/ui/src/components/dashboard/transactions-client.tsx
echo
echo "== transaction schema/files =="
git ls-files | rg 'transaction' || true
rg -n -C 3 'createTable\("transaction"|transaction.*Table|enum\s*\(|dev_plan|chat_plan|end_user|credit_{topup,refund,gift}|subscription_{start,cancel,end}' packages apps/db apps/worker -g '!**/migrations/meta/**' | head -n 400Repository: theopenco/llmgateway Length of output: 1621 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== billing transaction-client snippets =="
sed -n '1,330p' apps/ui/src/components/billing/transactions-client.tsx
echo
sed -n '330,620p' apps/ui/src/components/billing/transactions-client.tsx
echo
echo "== dashboard transaction-client =="
sed -n '1,280p' apps/ui/src/components/dashboard/transactions-client.tsx
echo
echo "== transaction schema/files =="
git ls-files | rg 'transaction' || true
rg -n -C 3 'createTable\("transaction"|transaction.*Table|type: .*enum|credit_topup|credit_refund|credit_gift|subscription_start|subscription_cancel|subscription_end' packages apps/db apps/worker -g '!**/migrations/meta/**' | head -n 400Repository: theopenco/llmgateway Length of output: 37136 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== transaction schema =="
sed -n '404,455p' packages/db/src/schema.ts
echo
echo "== transaction backend handlers =="
git ls-files | rg 'transaction' | rg -v '/migrations/meta/'
rg -n -C 4 'transaction|isInvoiceableTransaction|getTransaction|dev_plan_|chat_plan_|end_user|refund|credit_gift' packages apps/worker apps/api -g '!**/migrations/meta/**' | head -n 800
echo
echo "== deterministic label shape for missing type branch =="
python3 - <<'PY'
from pathlib import Path
p = Path('apps/ui/src/components/billing/transactions-client.tsx')
s = p.read_text()
cases = [
'credit_topup', 'credit_refund', 'credit_gift',
'subscription_start', 'subscription_cancel', 'subscription_end',
'dev_plan_start', 'dev_plan_upgrade', 'dev_plan_downgrade',
'dev_plan_renewal', 'dev_plan_reset_pass',
'dev_plan_cancel', 'dev_plan_resume', 'chat_plan_start',
'chat_plan_upgrade', 'chat_plan_downgrade', 'chat_plan_renewal',
'chat_plan_cancel', 'chat_plan_resume', 'chat_plan_end',
'chat_plan_reset_pass',
'end_user_topup', 'end_user_refund', 'end_user_bonus',
'end_user_margin_accrual', 'end_user_margin_payout',
]
print('cases_not_defined_in_switch=', sorted([t for t in cases if f'case "{t}"' not in s]))
PYRepository: theopenco/llmgateway Length of output: 50376 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== schema tail for transaction enum =="
sed -n '455,485p' packages/db/src/schema.ts
echo
echo "== UI TransactionCard getTypeLabel complete block (targeted) =="
python3 - <<'PY'
from pathlib import Path
p = Path('apps/ui/src/components/billing/transactions-client.tsx')
text = p.read_text()
for line in range(1, p.stat().st_size):
if text.find('const getTypeLabel = (type: Transaction["type"])') != -1:
start = text.rfind('\n', 0, text.find('const getTypeLabel'))
break
end = text.find('const getStatusColor =', start)
print(text[start:end])
cases = sorted({
'credit_topup', 'credit_refund', 'credit_gift',
'subscription_start', 'subscription_cancel', 'subscription_end',
'dev_plan_start', 'dev_plan_upgrade', 'dev_plan_downgrade',
'dev_plan_cancel', 'dev_plan_pause' or 'dev_plan_resume',
'dev_plan_end', 'dev_plan_renewal',
'dev_plan_reset_pass', 'dev_plan_reset_pass_reward', 'dev_plan_reset_pass_gift',
'chat_plan_start', 'chat_plan_upgrade', 'chat_plan_downgrade',
'chat_plan_cancel', 'chat_plan_pause' or 'chat_plan_resume',
'chat_plan_end', 'chat_plan_renewal',
'end_user_topup', 'end_user_refund', 'end_user_bonus',
'end_user_margin_accrual', 'end_user_margin_payout',
})
PY
echo
echo "== all case labels in TransactionsClient GetTypeLabel =="
rg -n 'case ".*"' apps/ui/src/components/billing/transactions-client.tsxRepository: theopenco/llmgateway Length of output: 1963 Add UI transaction type labels for the new transaction rows.
🤖 Prompt for AI Agents |
||
|
|
||
| ## Refunds and Invoices | ||
|
|
||
| Eligible transactions — credit top-ups and DevPass/Chat plan payments, within a short window and below a usage threshold — show a **Request Refund** action so the organization owner can self-serve a refund without contacting support. Refunds are processed through Stripe; the transaction and credit balance update once Stripe confirms the refund via webhook. | ||
|
|
||
| Completed payments with an associated Stripe invoice offer a **Download Invoice** action to get a PDF invoice or credit note for that transaction. | ||
|
Comment on lines
+40
to
+42
|
||
|
|
||
| ## Status Badges | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Scope provider fallback to the routing mode that supports it.
apps/docs/content/features/routing.mdxdocuments provider-prefixed requests as having no alternate-provider fallback. This paragraph does not distinguish a session pin from an explicitprovider/modelprefix. State that fallback applies to model-id or session-selected providers. State that an explicit provider prefix orX-No-Fallback: truedisables cross-provider fallback.🤖 Prompt for AI Agents