gateway
GitHub负责LLM网关的请求路由、提供商选择、流式处理及安全校验。涉及Redis缓存、用户URL防护、错误分类及优雅关闭,适用于网关代码编辑、端点添加或调试日志与连接问题。
Trigger Scenarios
Install
npx skills add theopenco/llmgateway --skill gateway -g -y
SKILL.md
Frontmatter
{
"name": "gateway",
"description": "Change the LLM Gateway request path in apps\/gateway — chat and other endpoints, routing and provider selection, error and finish-reason classification, streaming, request logging, Redis use, user-supplied content URLs, and graceful shutdown. Use when editing apps\/gateway\/src or packages\/actions request shaping, adding an endpoint, fetching remote content, changing fallback or routing, or debugging missing logs, hung Redis commands, or shutdown errors."
}
Gateway request path
The gateway is latency-critical and high-throughput.
Hot path
- Read no
logrows on the request path, however narrow the query or short the cache. Credit gates, spend and limit checks, and routing signals come from Redis counters maintained on the write path (incremented atinsertLog, settled by the billing worker) or small cached rows. - Route on the capability field curated for it (
supportedToolChoices,serviceTiers,supportsAssistantPrefill, …).supportedParametersis a partial list. - Provider-bound reasoning (mappings where
usesEncryptedReasoningholds:reasoning: trueon anencryptedReasoningprovider) can only verify on the account that produced it.chat/tools/reasoning-credential-pin.tspins a replaying turn to that credential, including apending_deletionkey; new credential selection must honor the pin. A retry that leaves the credential goes throughresolveProviderContextForRetry, which drops OpenAI-format encrypted reasoning; Gemini signatures cannot be dropped and still block fallback. New bound-reasoning shapes belong inproviderBoundReasoningIds. - Bare model ids route only to deployments in the model's full context-window tier (
getContextTierMinSizeinpackages/models). Do not add prompt-size estimates to provider selection for a fixed model; chars/4 misjudges per-model tokenizers.
User-supplied URLs
Fetch every content URL from a request body through processImageUrl
(packages/actions/src/process-image-url.ts), or for non-image content
assertSafeUserContentUrl from @llmgateway/shared/url-safety-node followed by
a redirect: "error" fetch. Keep validateSsrf on; only trusted
provider-response URLs pass validateSsrf: false. The guard enforces
https-only in every environment, blocks internal hosts and private, reserved,
link-local, metadata, and IPv4-mapped IPv6 addresses, and refuses redirects.
When the guard rejects a URL, fail the request; forwarding it upstream lets the
provider fetch what we refused. Download URLs in a provider response (video,
audio) are tenant-controlled under BYOK: fetch them with
fetchProviderIssuedUrl from apps/gateway/src/lib/fetch-provider.ts.
Worker requests to a BYOK base URL or webhook target pass tenantControlled
to fetchWithSignals in apps/worker/src/services/video-jobs.ts. Route every non-data: URL through the guard
wherever remote content is inlined (e.g. requiresBase64Images mappings).
Errors
Keep apps/gateway/src/chat/tools/get-finish-reason-from-error.ts conservative.
Generic 4xx wording such as "X is not supported for this model" or
unsupported_content_type stays a client error: users sending wrong requests
produce the same text, and reclassifying it triggers pointless fallback. When a
deployment rejects a capability the catalogue claims, fix that mapping's flag in
packages/models (e.g. vision: false). If asked for a broad reclassification,
raise the misclassification risk and confirm first.
Logging, streaming, shutdown
- Pass
{ retentionLevel }to everyinsertLogcall; the one-argument form drops payloads for retaining orgs. - Log a pre-provider rejection in
chat.tswithlogGatewayRejectionbefore throwing;app.onErrorwrites no log. - Import
streamSSEfromapps/gateway/src/lib/pending-work.ts, nothono/streaming, so graceful shutdown waits for the billing tail. - From response-lifecycle callbacks (
closehandlers, middlewarefinally), send Redis commands throughredisClient.pipeline().<cmd>().exec(). An auto-pipelined bare command there can wedge the shared client.
Version History
- d41bf19 Current 2026-10-19 04:12


