This commit is contained in:
reanon
2025-04-26 01:37:01 -08:00
parent cbca37dd77
commit a5eda7685b
+56 -47
View File
@@ -334,11 +334,9 @@ export class OpenAIKeyChecker extends KeyCheckerBase<OpenAIKey> {
}
/**
* Tests whether the key has access to the gpt-image-1 model by making a small test request.
* Returns true if the key has access, false otherwise.
*
* Ensures that the key can actually generate images by verifying the API response
* contains the expected data structure for successful image generation.
* Tests whether the key has access to the gpt-image-1 model by making a test request.
* Returns true if the API accepts the request without specific access errors.
* Does not wait for actual image generation to complete.
*/
public async testGptImageAccess(key: OpenAIKey): Promise<boolean> {
this.log.info({ key: key.hash }, "Testing gpt-image-1 access");
@@ -346,85 +344,96 @@ export class OpenAIKeyChecker extends KeyCheckerBase<OpenAIKey> {
try {
const payload = {
model: "gpt-image-1",
prompt: "A cute baby sea otter",
prompt: "A simple test image",
n: 1,
size: "auto",
quality: "low",
};
// Make a real request to test access
// Make a minimal request just to check access
const response = await axios.post(
POST_IMAGE_GENERATIONS_URL,
payload,
{
headers: OpenAIKeyChecker.getHeaders(key),
validateStatus: (status) => status >= 200 && status < 500, // Accept any non-server error response
timeout: 200000, // 200 second timeout to allow for image generation
signal: AbortSignal.timeout(200000)
validateStatus: (status) => true, // Accept any status code to inspect errors
timeout: 5000, // 5 second timeout
signal: AbortSignal.timeout(5000)
}
);
// Status 200 means success, but we need to verify the response actually contains image data
if (response.status === 200) {
// Check if the response contains the expected data structure
if (response.data &&
response.data.data &&
Array.isArray(response.data.data) &&
response.data.data.length > 0 &&
response.data.data[0].url) {
this.log.info(
{ key: key.hash, status: response.status },
"Verified gpt-image-1 access: Successfully generated an image"
);
return true;
} else {
this.log.warn(
{ key: key.hash, status: response.status, data: response.data },
"Response status is 200 but received unexpected response format. Image may not have been generated."
);
return false;
}
// If we get a 200, 400, or 500 response, consider the key valid
if (response.status === 200 || response.status === 400 || response.status === 500) {
this.log.info(
{ key: key.hash, status: response.status },
`Verified gpt-image-1 access with status code ${response.status}`
);
return true;
}
// All other status codes indicate issues
// Check for specific error responses that indicate no access
const data = response.data as any;
const errorMessage = data?.error?.message || '';
// Explicitly check for organization verification errors
if (response.status === 403 && errorMessage.includes("organization must be verified")) {
this.log.warn(
{ key: key.hash, status: response.status, error: errorMessage },
"Key does not have access to gpt-image-1: organization verification required"
);
return false;
}
// Only 401/403/404 responses indicate the key doesn't have model access
// (we're now considering 400 as valid)
if (response.status === 401 || response.status === 403 || response.status === 404) {
this.log.warn(
{ key: key.hash, status: response.status, error: errorMessage },
`Key does not have access to gpt-image-1: received ${response.status} response`
);
return false;
}
// For other status codes, log the issue but assume no access
this.log.warn(
{ key: key.hash, status: response.status, error: data?.error?.message || 'Unknown error' },
{ key: key.hash, status: response.status, error: errorMessage },
"Unexpected response when testing gpt-image-1 access, assuming no access"
);
return false;
} catch (error) {
// Handle network errors or request failures
if (error instanceof AxiosError && error.response) {
// Check for forbidden/unauthorized responses
const status = error.response.status;
const data = error.response.data as any;
const errorMessage = data?.error?.message || 'Unknown error';
// Organization verification errors (403 forbidden)
if (status === 403) {
// Check for specific error messages related to access
if (errorMessage.includes("organization must be verified") ||
errorMessage.includes("does not have access") ||
errorMessage.includes("not available")) {
this.log.warn(
{ key: key.hash, error: errorMessage },
"Key does not have access to gpt-image-1: received 403 Forbidden response"
{ key: key.hash, status, error: errorMessage },
"Key does not have access to gpt-image-1 based on error message"
);
return false;
}
// Explicitly handle all status codes that indicate the key doesn't have access
if (status === 401 || status === 404 || status === 400) {
this.log.warn(
// Status 400 or 500 indicates the API processed the request but had validation issues
// This means the key is authorized to use the model
if (status === 400 || status === 500) {
this.log.info(
{ key: key.hash, status, error: errorMessage },
`Key does not have access to gpt-image-1: received ${status} response`
`Verified gpt-image-1 access with error response ${status}`
);
return false;
return true;
}
// Rate limit errors - we no longer assume these mean the key has access
// since the rate limit could be happening before model validation
if (status === 429) {
// 401/403/404 responses indicate the key doesn't have proper access
if (status === 401 || status === 403 || status === 404) {
this.log.warn(
{ key: key.hash, status, error: errorMessage },
"Cannot verify gpt-image-1 access: key is rate limited. Assuming no access for safety."
`Key does not have access to gpt-image-1: received ${status} error response`
);
return false;
}
@@ -433,7 +442,7 @@ export class OpenAIKeyChecker extends KeyCheckerBase<OpenAIKey> {
// For all other errors, assume no access
this.log.error(
{ key: key.hash, error: error.message },
"Unexpected error testing gpt-image-1 access. Assuming no access for safety."
"Error testing gpt-image-1 access. Assuming no access for safety."
);
return false;
}