@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
105 lines (104 loc) • 5.99 kB
TypeScript
/**
* Codex (OpenAI ChatGPT) Proxy Routes
*
* Exposes `POST /backend-api/codex/responses` — the ChatGPT-backend Responses
* API endpoint the Codex CLI talks to — and pools it across multiple ChatGPT
* OAuth accounts, mirroring the Anthropic Claude pool engine.
*
* A Codex CLI configured with `model_providers.<name>.base_url` pointing at this
* proxy sends its own OAuth token; the proxy strips it, selects a pooled
* `codex:*` account, attaches that account's Bearer + matching chatgpt-account-id,
* forwards to https://chatgpt.com/backend-api/codex, and relays the SSE stream.
* On quota exhaustion (429 / usage limit) the account is cooled and the next
* account is tried — so the user never has to switch accounts by hand.
*
* This engine is deliberately leaner than claudeProxyRoutes.ts: it reuses the
* shared cooldown/quota persistence (keyed by the `codex:` account key so it
* never collides with anthropic entries) and does pre-commit rotation only, not
* the full transient-budget / admission machinery.
*/
import type { AccountQuota, CodexRefreshTokenStore, CodexResponseEnvelope, CodexResponseStream, CodexRuntimeAccount, CodexTokenRefresher, RateLimitCoolingReason, RouteGroup, ServerContext, ProxyRuntimeConfigProvider } from "../../types/index.js";
declare function refreshCodexTokenOnceWithDependencies(key: string, refreshToken: string, store: CodexRefreshTokenStore, refresh: CodexTokenRefresher): Promise<{
accessToken: string;
refreshToken: string;
expiresAt?: number;
}>;
/** Refresh an account's token at most once at a time. */
declare function refreshCodexTokenOnce(key: string, refreshToken: string): Promise<{
accessToken: string;
refreshToken: string;
expiresAt?: number;
}>;
/**
* Load the Codex account pool from the token store, refreshing expired OAuth
* tokens and hydrating cooldown + quota state from disk.
*/
declare function loadCodexProxyAccounts(): Promise<CodexRuntimeAccount[]>;
/**
* Order non-cooling accounts ahead of cooling ones, then defer rejected quota.
* Within each group, probe unknown quota first and otherwise fill the least-used
* session so fresh accounts get observed rather than starved.
*/
declare function orderCodexAccounts(accounts: CodexRuntimeAccount[], now: number): CodexRuntimeAccount[];
/** Build the upstream request headers, replacing client auth with the account's. */
declare function buildCodexUpstreamHeaders(clientHeaders: Record<string, string>, account: CodexRuntimeAccount): Record<string, string>;
/** Honor known quota resets while bounding unscoped or transient account cooldowns. */
declare function planCodexCooldown(quota: AccountQuota | null, retryAfterMs: number, now: number, errorText?: string): {
coolingUntil: number;
reason: RateLimitCoolingReason;
};
/**
* Drive a `CodexResponseStream`'s `frames` generator into a `Response` body,
* calling `onSettled` exactly once with the outcome: the terminal envelope on
* a normal finish, the thrown error on the (never expected, still handled)
* post-commit failure, or `cancelled` when the consumer cancels the
* `ReadableStream` first (client disconnect, or an upstream timeout/abort).
*
* `createAnthropicFallbackStream`'s generator never throws once past its own
* internal preflight (codexToAnthropicFallback.ts:220-517) — every
* post-commit failure is instead serialized as a `response.failed` frame and
* the generator completes normally — so the `catch` branch here is a
* defensive backstop, not the documented failure path.
*/
declare function wrapCodexOutboundResponseStream(codecStream: CodexResponseStream, headers: Record<string, string>, onSettled: (result: {
kind: "completed";
envelope: CodexResponseEnvelope;
} | {
kind: "error";
error: unknown;
} | {
kind: "cancelled";
}) => void): Response;
/** Core pooled handler for POST /backend-api/codex/responses. */
export declare function handleCodexResponsesRequest(ctx: ServerContext, runtimeConfigProvider?: ProxyRuntimeConfigProvider, loopbackPort?: number, internalDispatch?: (request: Request) => Response | Promise<Response>): Promise<Response>;
/**
* Create Codex proxy routes.
*
* @param basePath - Base path prefix (default "").
* @param runtimeConfigProvider - Optional runtime config snapshot reader.
* Gates the Codex-outbound fallback dispatcher at all four insertion
* points in `dispatch()`: omitting it (or a snapshot with the feature
* disabled / no targets configured) reproduces today's behavior exactly.
* @param loopbackPort - The in-process HTTP port the outbound-fallback leg's
* Anthropic-pool loopback request targets (mirrors `createOpenAIProxyRoutes`'s
* bridge). Required only for an `anthropic` target; the `vertex` target
* dispatches via `executeVertexAnthropicFallback` and never reads it.
* @param internalDispatch - Optional in-process fetch (`(request) =>
* app.fetch(request)`), so the outbound-fallback loopback never re-enters
* the public listener. Falls back to a real `fetch` against
* `loopbackPort` when omitted.
* @returns RouteGroup with the Codex backend Responses endpoint.
*/
export declare function createCodexProxyRoutes(basePath?: string, runtimeConfigProvider?: ProxyRuntimeConfigProvider, loopbackPort?: number, internalDispatch?: (request: Request) => Response | Promise<Response>): RouteGroup;
export declare const __testHooks: {
loadCodexProxyAccounts: typeof loadCodexProxyAccounts;
orderCodexAccounts: typeof orderCodexAccounts;
buildCodexUpstreamHeaders: typeof buildCodexUpstreamHeaders;
planCodexCooldown: typeof planCodexCooldown;
refreshCodexTokenOnce: typeof refreshCodexTokenOnce;
refreshCodexTokenOnceWithDependencies: typeof refreshCodexTokenOnceWithDependencies;
codexRefreshInFlightSize: () => number;
setOutboundFallbackTimeoutMsForTests: (ms: number | null) => void;
wrapCodexOutboundResponseStream: typeof wrapCodexOutboundResponseStream;
};
export {};