From e393d6f4f3b265edd12b8f82c414cb53de59b97b Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Fri, 20 Dec 2024 17:29:22 -0500 Subject: [PATCH 1/8] fix: Handle HTTP 400 Bad Request responses for ai.generateImage - Updated AIImageInterfaceResponse to handle both success and failure cases. - Added error handling in OpenAIImageInterface.ts to check for HTTP 400 Bad Request responses. - Return a proper response to the user when provided parameters are not accepted by the selected model. --- src/aux-records/AIController.ts | 7 +++-- src/aux-records/AIImageInterface.ts | 34 ++++++++++++++++++++++--- src/aux-records/OpenAIImageInterface.ts | 24 ++++++++++++++++- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/aux-records/AIController.ts b/src/aux-records/AIController.ts index f23b42892c..425753d32a 100644 --- a/src/aux-records/AIController.ts +++ b/src/aux-records/AIController.ts @@ -12,7 +12,6 @@ import { AIChatInterfaceResponse, AIChatInterfaceStreamResponse, AIChatMessage, - AIChatStreamMessage, } from './AIChatInterface'; import { AIGenerateSkyboxInterface, @@ -1074,7 +1073,7 @@ export class AIController { } const result = await provider.generateImage({ - model, + model: model, prompt: request.prompt, negativePrompt: request.negativePrompt, width: width, @@ -1092,6 +1091,10 @@ export class AIController { userId: request.userId, }); + if (!result.success) { + return result; + } + await this._metrics.recordImageMetrics({ userId: request.userId, createdAtMs: Date.now(), diff --git a/src/aux-records/AIImageInterface.ts b/src/aux-records/AIImageInterface.ts index fbba529b37..2a5d7d90d4 100644 --- a/src/aux-records/AIImageInterface.ts +++ b/src/aux-records/AIImageInterface.ts @@ -1,3 +1,13 @@ +import { + InvalidSubscriptionTierError, + NotAuthorizedError, + NotLoggedInError, + NotSubscribedError, + NotSupportedError, + ServerError, + SubscriptionLimitReached, +} from '@casual-simulation/aux-common/Errors'; + /** * Defines an interface that is able to generate images from text prompts. */ @@ -79,13 +89,29 @@ export interface AIGenerateImageInterfaceRequest { userId?: string; } -export interface AIGenerateImageInterfaceResponse { - /** - * The list of images that were generated. - */ +export type AIGenerateImageInterfaceResponse = + | AIGenerateImageInterfaceSuccess + | AIGenerateImageInterfaceFailure; + +export interface AIGenerateImageInterfaceSuccess { + success: true; images: AIGeneratedImage[]; } +export interface AIGenerateImageInterfaceFailure { + success: false; + errorCode: + | ServerError + | NotLoggedInError + | NotSubscribedError + | InvalidSubscriptionTierError + | NotSupportedError + | SubscriptionLimitReached + | NotAuthorizedError + | 'invalid_model'; + errorMessage: string; +} + /** * Defines an image that was generated by the AI. */ diff --git a/src/aux-records/OpenAIImageInterface.ts b/src/aux-records/OpenAIImageInterface.ts index 0f35132182..faf7b4f988 100644 --- a/src/aux-records/OpenAIImageInterface.ts +++ b/src/aux-records/OpenAIImageInterface.ts @@ -13,7 +13,12 @@ import { } from './AIImageInterface'; import { handleAxiosErrors } from './Utils'; import { traced } from './tracing/TracingDecorators'; -import { SpanKind, SpanOptions } from '@opentelemetry/api'; +import { + SpanKind, + SpanOptions, + SpanStatusCode, + trace, +} from '@opentelemetry/api'; const TRACE_NAME = 'OpenAIImageInterface'; const SPAN_OPTIONS: SpanOptions = { @@ -93,9 +98,26 @@ export class OpenAIImageInterface implements AIImageInterface { ); return { + success: true, images, }; } catch (err) { + if (axios.isAxiosError(err)) { + if (err.response.status === 400) { + const span = trace.getActiveSpan(); + span?.recordException(err); + span?.setStatus({ code: SpanStatusCode.ERROR }); + + console.error( + `[OpenAIChatInterface] [${request.userId}] [generateImage]: Bad request: ${err.response.data.error.message}` + ); + return { + success: false, + errorCode: 'server_error', + errorMessage: err.response.data.error.message, + }; + } + } handleAxiosErrors(err); } } From e1203859c2cb8b8f2fd569b9b974777c153dde27 Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 13:35:17 -0500 Subject: [PATCH 2/8] fix: OpenAIImageInterface.ts errorCode now properly returns 'invalid_request' fix: AIController.ts and AIImageInterface.ts now include a property to handle 'invalid_request' --- src/aux-records/AIController.ts | 1 + src/aux-records/AIImageInterface.ts | 1 + src/aux-records/OpenAIImageInterface.ts | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/aux-records/AIController.ts b/src/aux-records/AIController.ts index 425753d32a..24c083188a 100644 --- a/src/aux-records/AIController.ts +++ b/src/aux-records/AIController.ts @@ -1705,6 +1705,7 @@ export interface AIGenerateImageFailure { | NotSupportedError | SubscriptionLimitReached | NotAuthorizedError + | 'invalid_request' | 'invalid_model'; errorMessage: string; diff --git a/src/aux-records/AIImageInterface.ts b/src/aux-records/AIImageInterface.ts index 2a5d7d90d4..fb611c5f76 100644 --- a/src/aux-records/AIImageInterface.ts +++ b/src/aux-records/AIImageInterface.ts @@ -108,6 +108,7 @@ export interface AIGenerateImageInterfaceFailure { | NotSupportedError | SubscriptionLimitReached | NotAuthorizedError + | 'invalid_request' | 'invalid_model'; errorMessage: string; } diff --git a/src/aux-records/OpenAIImageInterface.ts b/src/aux-records/OpenAIImageInterface.ts index faf7b4f988..1e35ddde22 100644 --- a/src/aux-records/OpenAIImageInterface.ts +++ b/src/aux-records/OpenAIImageInterface.ts @@ -113,7 +113,7 @@ export class OpenAIImageInterface implements AIImageInterface { ); return { success: false, - errorCode: 'server_error', + errorCode: 'invalid_request', errorMessage: err.response.data.error.message, }; } From da852912f7313cc1ca1c35b22ceb37c2b83b68bc Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 13:42:23 -0500 Subject: [PATCH 3/8] chore: update CHANGELOG.md --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c393f83670..f6fc7c33a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # CasualOS Changelog +## V3.3.16 + +#### Date: TBA + +### :rocket: Features + +### :bug: Bug Fixes + +### :bug: Bug Fixes + +- Improved error handling for `ai.generateImage` requests with unacceptable parameters. + - The server now returns an `invalid_request` error code when the parameters provided are not accepted by the selected model (e.g., OpenAI, Google). + - This ensures that users receive clear and actionable feedback when their requests fail due to invalid parameters. + ## V3.3.15 #### Date: 12/19/2024 From 33fcec9ee6e2d7241f36a67519919084b8f513a1 Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 13:49:19 -0500 Subject: [PATCH 4/8] - Added error handling in StabilityAIImageInterface.ts to check for HTTP 400 Bad Request responses. --- src/aux-records/StabilityAIImageInterface.ts | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/aux-records/StabilityAIImageInterface.ts b/src/aux-records/StabilityAIImageInterface.ts index 203075f77a..f756da4765 100644 --- a/src/aux-records/StabilityAIImageInterface.ts +++ b/src/aux-records/StabilityAIImageInterface.ts @@ -9,6 +9,7 @@ import axios from 'axios'; import { handleAxiosErrors } from './Utils'; import { traced } from './tracing/TracingDecorators'; import { z } from 'zod'; +import { SpanStatusCode, trace } from '@opentelemetry/api'; const TRACE_NAME = 'StabilityAIImageInterface'; @@ -115,9 +116,26 @@ export class StabilityAIImageInterface implements AIImageInterface { ); return { + success: true, images, }; } catch (err) { + if (axios.isAxiosError(err)) { + if (err.response.status === 400) { + const span = trace.getActiveSpan(); + span?.recordException(err); + span?.setStatus({ code: SpanStatusCode.ERROR }); + + console.error( + `[OpenAIChatInterface] [${request.userId}] [generateImage]: Bad request: ${err.response.data.error.message}` + ); + return { + success: false, + errorCode: 'invalid_request', + errorMessage: err.response.data.error.message, + }; + } + } handleAxiosErrors(err); } } @@ -157,6 +175,7 @@ export class StabilityAIImageInterface implements AIImageInterface { const data = schema.parse(result.data); return { + success: true, images: [ { base64: data.image, @@ -203,6 +222,7 @@ export class StabilityAIImageInterface implements AIImageInterface { const data = schema.parse(result.data); return { + success: true, images: [ { base64: data.image, @@ -252,6 +272,7 @@ export class StabilityAIImageInterface implements AIImageInterface { const data = schema.parse(result.data); return { + success: true, images: [ { base64: data.image, From 66675eccb3785a4c730e43f7fcf76c1fa98daba2 Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 13:52:19 -0500 Subject: [PATCH 5/8] fix: removed duplicate text --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6fc7c33a8..634dae06ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,6 @@ ### :bug: Bug Fixes -### :bug: Bug Fixes - - Improved error handling for `ai.generateImage` requests with unacceptable parameters. - The server now returns an `invalid_request` error code when the parameters provided are not accepted by the selected model (e.g., OpenAI, Google). - This ensures that users receive clear and actionable feedback when their requests fail due to invalid parameters. From de25a9e3a135adb6dc1874410b0d8faf6d47a79a Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 14:08:43 -0500 Subject: [PATCH 6/8] fix: update CHANGELOG.md fix: StabilityAIImageInterface.ts now returns 'StabilityAIChatIngerface' instead of 'OpenAIChatInterface' --- CHANGELOG.md | 2 +- src/aux-records/StabilityAIImageInterface.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 634dae06ac..3dd6d1c6ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## V3.3.16 -#### Date: TBA +#### Date: TBD ### :rocket: Features diff --git a/src/aux-records/StabilityAIImageInterface.ts b/src/aux-records/StabilityAIImageInterface.ts index f756da4765..0a699d5c37 100644 --- a/src/aux-records/StabilityAIImageInterface.ts +++ b/src/aux-records/StabilityAIImageInterface.ts @@ -127,7 +127,7 @@ export class StabilityAIImageInterface implements AIImageInterface { span?.setStatus({ code: SpanStatusCode.ERROR }); console.error( - `[OpenAIChatInterface] [${request.userId}] [generateImage]: Bad request: ${err.response.data.error.message}` + `[StabilityAIChatIngerface] [${request.userId}] [generateImage]: Bad request: ${err.response.data.error.message}` ); return { success: false, From a34ba240e3a7fb3b92a032b528b7d824dd6bef04 Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 14:30:46 -0500 Subject: [PATCH 7/8] fix: update AIController.spec.ts --- src/aux-records/AIController.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/aux-records/AIController.spec.ts b/src/aux-records/AIController.spec.ts index 2c82fe5487..785c400df0 100644 --- a/src/aux-records/AIController.spec.ts +++ b/src/aux-records/AIController.spec.ts @@ -2765,6 +2765,7 @@ describe('AIController', () => { it('should return the result from the generateImage interface', async () => { generateImageInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', @@ -2860,6 +2861,7 @@ describe('AIController', () => { otherInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', @@ -2979,6 +2981,7 @@ describe('AIController', () => { it('should work when the controller is configured to allow all subscription tiers and the user does not have a subscription', async () => { generateImageInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', @@ -3090,6 +3093,7 @@ describe('AIController', () => { generateImageInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', @@ -3120,6 +3124,7 @@ describe('AIController', () => { it('should reject the request if it would exceed the subscription request limits', async () => { generateImageInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', @@ -3150,6 +3155,7 @@ describe('AIController', () => { it('should reject the request if it would exceed the subscription period limits', async () => { generateImageInterface.generateImage.mockReturnValueOnce( Promise.resolve({ + success: true, images: [ { base64: 'base64', From c5320683b55fb1ab9ce30bb99cbf01b891981d3d Mon Sep 17 00:00:00 2001 From: TroyceGowdy Date: Mon, 23 Dec 2024 15:22:12 -0500 Subject: [PATCH 8/8] fix: update RecordsServer.spec.ts --- src/aux-records/RecordsServer.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aux-records/RecordsServer.spec.ts b/src/aux-records/RecordsServer.spec.ts index 6a3219fe28..db568cf479 100644 --- a/src/aux-records/RecordsServer.spec.ts +++ b/src/aux-records/RecordsServer.spec.ts @@ -16239,6 +16239,7 @@ describe('RecordsServer', () => { }); imageInterface.generateImage.mockResolvedValueOnce({ + success: true, images: [ { base64: 'base64',