From 20c992019903b58fa4ff6b6e1566ae4e5c73996d Mon Sep 17 00:00:00 2001 From: reanon <> Date: Thu, 7 Aug 2025 22:51:38 +0200 Subject: [PATCH] gpt5 streaming = use only verified orgs --- .../middleware/request/mutators/add-key.ts | 4 +- src/shared/key-management/index.ts | 2 +- src/shared/key-management/key-pool.ts | 4 +- src/shared/key-management/openai/provider.ts | 40 ++++++++++++++++++- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/proxy/middleware/request/mutators/add-key.ts b/src/proxy/middleware/request/mutators/add-key.ts index 57a5c75..effa40a 100644 --- a/src/proxy/middleware/request/mutators/add-key.ts +++ b/src/proxy/middleware/request/mutators/add-key.ts @@ -31,7 +31,9 @@ export const addKey: ProxyReqMutator = (manager) => { } if (inboundApi === outboundApi) { - assignedKey = keyPool.get(body.model, service, needsMultimodal); + // Pass streaming information for GPT-5 models that require verified keys for streaming + const isStreaming = body.stream === true; + assignedKey = keyPool.get(body.model, service, needsMultimodal, isStreaming); } else { switch (outboundApi) { // If we are translating between API formats we may need to select a model diff --git a/src/shared/key-management/index.ts b/src/shared/key-management/index.ts index 5bc382d..4f273ae 100644 --- a/src/shared/key-management/index.ts +++ b/src/shared/key-management/index.ts @@ -61,7 +61,7 @@ for service-agnostic functionality. export interface KeyProvider { readonly service: LLMService; init(): void; - get(model: string): T; + get(model: string, streaming?: boolean): T; list(): Omit[]; disable(key: T): void; update(hash: string, update: Partial): void; diff --git a/src/shared/key-management/key-pool.ts b/src/shared/key-management/key-pool.ts index 8b1a13b..ca743e5 100644 --- a/src/shared/key-management/key-pool.ts +++ b/src/shared/key-management/key-pool.ts @@ -53,7 +53,7 @@ export class KeyPool { this.scheduleRecheck(); } - public get(model: string, service?: LLMService, multimodal?: boolean): Key { + public get(model: string, service?: LLMService, multimodal?: boolean, streaming?: boolean): Key { // hack for some claude requests needing keys with particular permissions // even though they use the same models as the non-multimodal requests if (multimodal) { @@ -61,7 +61,7 @@ export class KeyPool { } const queryService = service || this.getServiceForModel(model); - return this.getKeyProvider(queryService).get(model); + return this.getKeyProvider(queryService).get(model, streaming); } public list(): Omit[] { diff --git a/src/shared/key-management/openai/provider.ts b/src/shared/key-management/openai/provider.ts index c15d6fc..e9f2600 100644 --- a/src/shared/key-management/openai/provider.ts +++ b/src/shared/key-management/openai/provider.ts @@ -137,12 +137,16 @@ export class OpenAIKeyProvider implements KeyProvider { return this.keys.map((key) => Object.freeze({ ...key, key: undefined })); } - public get(requestModel: string) { + public get(requestModel: string, streaming?: boolean) { let model = requestModel; const neededFamily = getOpenAIModelFamily(model); const excludeTrials = model === "text-embedding-ada-002"; const isGptImageRequest = neededFamily === "gpt-image"; + + // GPT-5 models (gpt-5, gpt-5-mini, gpt-5-nano) require verified keys for streaming + const isGpt5Model = /^gpt-5(-mini|-nano)?(-\d{4}-\d{2}-\d{2})?$/.test(model) || model === "gpt-5-chat-latest"; + const isGpt5StreamingRequest = isGpt5Model && streaming; // First, filter keys based on basic criteria let availableKeys = this.keys.filter( @@ -187,8 +191,42 @@ export class OpenAIKeyProvider implements KeyProvider { ); } } + + // For GPT-5 streaming requests, we need to use only verified keys + // GPT-5 models (gpt-5, gpt-5-mini, gpt-5-nano) require verified organizations for streaming + if (isGpt5StreamingRequest) { + this.log.debug( + { model, keyCount: availableKeys.length, streaming }, + "Filtering keys for GPT-5 streaming request to ensure verified organization status" + ); + + // Filter to only include keys from verified organizations + // We piggyback on the existing verification logic: verified keys still have gpt-image access + const verifiedKeys = availableKeys.filter(key => key.modelFamilies.includes("gpt-image")); + + if (verifiedKeys.length > 0) { + this.log.info( + { model, totalKeys: availableKeys.length, verifiedKeys: verifiedKeys.length, streaming }, + "Using only verified organization keys for GPT-5 streaming request" + ); + availableKeys = verifiedKeys; + } else { + this.log.warn( + { model, totalKeys: availableKeys.length, streaming }, + "No verified organization keys available for GPT-5 streaming request" + ); + // Set availableKeys to empty array to trigger the error below + availableKeys = []; + } + } if (availableKeys.length === 0) { + // Provide specific error message for GPT-5 streaming requests + if (isGpt5StreamingRequest) { + throw new PaymentRequiredError( + `No verified OpenAI keys available for streaming ${model}. GPT-5 models require verified organization keys for streaming. Please disable streaming.` + ); + } throw new PaymentRequiredError( `No OpenAI keys available for model ${model}` );