gpt5 streaming = use only verified orgs
This commit is contained in:
@@ -31,7 +31,9 @@ export const addKey: ProxyReqMutator = (manager) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inboundApi === outboundApi) {
|
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 {
|
} else {
|
||||||
switch (outboundApi) {
|
switch (outboundApi) {
|
||||||
// If we are translating between API formats we may need to select a model
|
// If we are translating between API formats we may need to select a model
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ for service-agnostic functionality.
|
|||||||
export interface KeyProvider<T extends Key = Key> {
|
export interface KeyProvider<T extends Key = Key> {
|
||||||
readonly service: LLMService;
|
readonly service: LLMService;
|
||||||
init(): void;
|
init(): void;
|
||||||
get(model: string): T;
|
get(model: string, streaming?: boolean): T;
|
||||||
list(): Omit<T, "key">[];
|
list(): Omit<T, "key">[];
|
||||||
disable(key: T): void;
|
disable(key: T): void;
|
||||||
update(hash: string, update: Partial<T>): void;
|
update(hash: string, update: Partial<T>): void;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class KeyPool {
|
|||||||
this.scheduleRecheck();
|
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
|
// hack for some claude requests needing keys with particular permissions
|
||||||
// even though they use the same models as the non-multimodal requests
|
// even though they use the same models as the non-multimodal requests
|
||||||
if (multimodal) {
|
if (multimodal) {
|
||||||
@@ -61,7 +61,7 @@ export class KeyPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryService = service || this.getServiceForModel(model);
|
const queryService = service || this.getServiceForModel(model);
|
||||||
return this.getKeyProvider(queryService).get(model);
|
return this.getKeyProvider(queryService).get(model, streaming);
|
||||||
}
|
}
|
||||||
|
|
||||||
public list(): Omit<Key, "key">[] {
|
public list(): Omit<Key, "key">[] {
|
||||||
|
|||||||
@@ -137,13 +137,17 @@ export class OpenAIKeyProvider implements KeyProvider<OpenAIKey> {
|
|||||||
return this.keys.map((key) => Object.freeze({ ...key, key: undefined }));
|
return this.keys.map((key) => Object.freeze({ ...key, key: undefined }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(requestModel: string) {
|
public get(requestModel: string, streaming?: boolean) {
|
||||||
let model = requestModel;
|
let model = requestModel;
|
||||||
|
|
||||||
const neededFamily = getOpenAIModelFamily(model);
|
const neededFamily = getOpenAIModelFamily(model);
|
||||||
const excludeTrials = model === "text-embedding-ada-002";
|
const excludeTrials = model === "text-embedding-ada-002";
|
||||||
const isGptImageRequest = neededFamily === "gpt-image";
|
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
|
// First, filter keys based on basic criteria
|
||||||
let availableKeys = this.keys.filter(
|
let availableKeys = this.keys.filter(
|
||||||
(key) =>
|
(key) =>
|
||||||
@@ -188,7 +192,41 @@ export class OpenAIKeyProvider implements KeyProvider<OpenAIKey> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
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(
|
throw new PaymentRequiredError(
|
||||||
`No OpenAI keys available for model ${model}`
|
`No OpenAI keys available for model ${model}`
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user