Skip to content

Commit cb34757

Browse files
authored
💄 style: optimize nana banana pro error message (#10378)
1 parent cdc71b2 commit cb34757

File tree

5 files changed

+54
-23
lines changed

5 files changed

+54
-23
lines changed

packages/model-runtime/src/providers/google/createImage.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { createGoogleImage } from './createImage';
88

99
const provider = 'google';
1010
const bizErrorType = 'ProviderBizError';
11+
const noImageErrorType = 'ProviderNoImageGenerated';
1112
const invalidErrorType = 'InvalidProviderAPIKey';
1213

1314
// Mock the console.error to avoid polluting test output
@@ -201,7 +202,7 @@ describe('createGoogleImage', () => {
201202
// Act & Assert - Test error behavior rather than specific text
202203
await expect(createGoogleImage(mockClient, provider, payload)).rejects.toEqual(
203204
expect.objectContaining({
204-
errorType: bizErrorType,
205+
errorType: noImageErrorType,
205206
provider,
206207
}),
207208
);
@@ -224,7 +225,7 @@ describe('createGoogleImage', () => {
224225
// Act & Assert
225226
await expect(createGoogleImage(mockClient, provider, payload)).rejects.toEqual(
226227
expect.objectContaining({
227-
errorType: bizErrorType,
228+
errorType: noImageErrorType,
228229
provider,
229230
}),
230231
);
@@ -251,7 +252,7 @@ describe('createGoogleImage', () => {
251252
// Act & Assert
252253
await expect(createGoogleImage(mockClient, provider, payload)).rejects.toEqual(
253254
expect.objectContaining({
254-
errorType: bizErrorType,
255+
errorType: noImageErrorType,
255256
provider,
256257
}),
257258
);
@@ -602,7 +603,7 @@ describe('createGoogleImage', () => {
602603
// Act & Assert
603604
await expect(createGoogleImage(mockClient, provider, payload)).rejects.toEqual(
604605
expect.objectContaining({
605-
errorType: bizErrorType,
606+
errorType: noImageErrorType,
606607
provider,
607608
}),
608609
);
@@ -627,7 +628,7 @@ describe('createGoogleImage', () => {
627628
// Act & Assert
628629
await expect(createGoogleImage(mockClient, provider, payload)).rejects.toEqual(
629630
expect.objectContaining({
630-
errorType: bizErrorType,
631+
errorType: noImageErrorType,
631632
provider,
632633
}),
633634
);

packages/model-runtime/src/providers/google/createImage.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ async function processImageForParts(imageUrl: string): Promise<Part> {
4747
*/
4848
function extractImageFromResponse(response: any): CreateImageResponse {
4949
const candidate = response.candidates?.[0];
50+
if (candidate?.finishReason === 'NO_IMAGE') {
51+
throw new Error('No image generated');
52+
}
5053
if (!candidate?.content?.parts) {
54+
// Handle cases where Google returns 200 but omits image parts (often moderation)
5155
throw new Error('No image generated');
5256
}
5357

@@ -58,6 +62,7 @@ function extractImageFromResponse(response: any): CreateImageResponse {
5862
}
5963
}
6064

65+
// Fallback when no inlineData is present (commonly moderation or policy blocks)
6166
throw new Error('No image data found in response');
6267
}
6368

@@ -79,16 +84,11 @@ async function generateByImageModel(
7984
prompt: params.prompt,
8085
});
8186

82-
if (!response.generatedImages || response.generatedImages.length === 0) {
83-
throw new Error('No images generated');
84-
}
85-
86-
const generatedImage = response.generatedImages[0];
87-
if (!generatedImage.image || !generatedImage.image.imageBytes) {
88-
throw new Error('Invalid image data');
87+
const imageBytes = response.generatedImages?.[0]?.image?.imageBytes;
88+
if (!imageBytes) {
89+
throw new Error('No image generated');
8990
}
9091

91-
const { imageBytes } = generatedImage.image;
9292
// 1. official doc use png as example
9393
// 2. no responseType param support like openai now.
9494
// I think we can just hard code png now
@@ -189,6 +189,10 @@ export async function createGoogleImage(
189189
} catch (error) {
190190
const err = error as Error;
191191

192+
if ((err as any)?.errorType) {
193+
throw err;
194+
}
195+
192196
const { errorType, error: parsedError } = parseGoogleErrorMessage(err.message);
193197
throw AgentRuntimeError.createImage({
194198
error: parsedError,

packages/model-runtime/src/types/error.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ export const AgentRuntimeErrorType = {
1919
OllamaBizError: 'OllamaBizError',
2020
OllamaServiceUnavailable: 'OllamaServiceUnavailable',
2121

22-
InvalidComfyUIArgs: 'InvalidComfyUIArgs',
23-
ComfyUIBizError: 'ComfyUIBizError',
24-
ComfyUIServiceUnavailable: 'ComfyUIServiceUnavailable',
25-
ComfyUIEmptyResult: 'ComfyUIEmptyResult',
26-
ComfyUIUploadFailed: 'ComfyUIUploadFailed',
27-
ComfyUIWorkflowError: 'ComfyUIWorkflowError',
28-
ComfyUIModelError: 'ComfyUIModelError',
29-
3022
InvalidBedrockCredentials: 'InvalidBedrockCredentials',
3123
InvalidVertexCredentials: 'InvalidVertexCredentials',
3224
StreamChunkError: 'StreamChunkError',
@@ -35,6 +27,17 @@ export const AgentRuntimeErrorType = {
3527

3628
ConnectionCheckFailed: 'ConnectionCheckFailed',
3729

30+
// ******* Image Generation Error ******* //
31+
ProviderNoImageGenerated: 'ProviderNoImageGenerated',
32+
33+
InvalidComfyUIArgs: 'InvalidComfyUIArgs',
34+
ComfyUIBizError: 'ComfyUIBizError',
35+
ComfyUIServiceUnavailable: 'ComfyUIServiceUnavailable',
36+
ComfyUIEmptyResult: 'ComfyUIEmptyResult',
37+
ComfyUIUploadFailed: 'ComfyUIUploadFailed',
38+
ComfyUIWorkflowError: 'ComfyUIWorkflowError',
39+
ComfyUIModelError: 'ComfyUIModelError',
40+
3841
/**
3942
* @deprecated
4043
*/

packages/model-runtime/src/utils/googleErrorParser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ export function parseGoogleErrorMessage(message: string): ParsedError {
104104
return { error: { message }, errorType: AgentRuntimeErrorType.LocationNotSupportError };
105105
}
106106

107+
const lowerMessage = message.toLowerCase();
108+
if (lowerMessage.includes('no image generated') || lowerMessage.includes('no image data')) {
109+
return { error: { message }, errorType: AgentRuntimeErrorType.ProviderNoImageGenerated };
110+
}
111+
107112
// Unified error type determination function
108113
const getErrorType = (code: number | null, message: string): ILobeAgentRuntimeErrorType => {
109114
if (code === 400 && message.includes('API key not valid')) {

src/server/routers/async/image.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const checkAbortSignal = (signal: AbortSignal) => {
6565
const categorizeError = (
6666
error: any,
6767
isAborted: boolean,
68+
isEditingImage: boolean,
6869
): { errorMessage: string; errorType: AsyncTaskErrorType } => {
6970
log('🔥🔥🔥 [ASYNC] categorizeError called:', {
7071
errorMessage: error?.message,
@@ -73,6 +74,7 @@ const categorizeError = (
7374
errorType: error?.errorType,
7475
fullError: JSON.stringify(error, null, 2),
7576
isAborted,
77+
isEditingImage,
7678
});
7779
// Handle Comfy UI errors
7880
if (error.errorType === AgentRuntimeErrorType.ComfyUIServiceUnavailable) {
@@ -127,6 +129,15 @@ const categorizeError = (
127129
};
128130
}
129131

132+
if (error.errorType === AgentRuntimeErrorType.ProviderNoImageGenerated) {
133+
return {
134+
errorMessage: isEditingImage
135+
? 'Provider returned no image (maybe content review). Try a safer source image or milder prompt.'
136+
: 'Provider returned no image (maybe content review). Try a milder prompt or another model.',
137+
errorType: AsyncTaskErrorType.ServerError,
138+
};
139+
}
140+
130141
// FIXME: 401 的问题应该放到 agentRuntime 中处理会更好
131142
if (error.errorType === AgentRuntimeErrorType.InvalidProviderAPIKey || error?.status === 401) {
132143
return {
@@ -195,11 +206,14 @@ export const imageRouter = router({
195206
const abortController = new AbortController();
196207
let timeoutId: ReturnType<typeof setTimeout> | null = null;
197208

209+
const isEditingImage =
210+
Boolean((params as any).imageUrl) || Boolean(params.imageUrls && params.imageUrls.length > 0);
211+
198212
try {
199213
const imageGenerationPromise = async (signal: AbortSignal) => {
200214
log('Initializing agent runtime for provider: %s', provider);
201215

202-
const agentRuntime = await initModelRuntimeWithUserPayload(provider, ctx.jwtPayload);
216+
const agentRuntime = initModelRuntimeWithUserPayload(provider, ctx.jwtPayload);
203217

204218
// Check if operation has been cancelled
205219
checkAbortSignal(signal);
@@ -328,7 +342,11 @@ export const imageRouter = router({
328342
});
329343

330344
// Improved error categorization logic
331-
const { errorType, errorMessage } = categorizeError(error, abortController.signal.aborted);
345+
const { errorType, errorMessage } = categorizeError(
346+
error,
347+
abortController.signal.aborted,
348+
isEditingImage,
349+
);
332350

333351
await ctx.asyncTaskModel.update(taskId, {
334352
error: new AsyncTaskError(errorType, errorMessage),

0 commit comments

Comments
 (0)