Conversation
sfreeman422
commented
Mar 22, 2026
- Improved gemini error logging
- Changed .debug to .info
There was a problem hiding this comment.
Pull request overview
This PR aims to improve observability around Gemini image-generation responses in AIService, primarily by adding response-shape logging and guarding against missing/empty candidates/parts.
Changes:
- Add Gemini response structure logging and explicit checks for missing
candidates/partsbefore parsing image bytes. - Type Gemini
partsiteration usingPartfrom@google/genai. - Update AIService logger mock in tests to include
debug.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| packages/backend/src/ai/ai.service.ts | Adds Gemini response structure logging and defensive checks when extracting image data from Gemini responses. |
| packages/backend/src/ai/ai.service.spec.ts | Extends the logger mock shape to include a debug method. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| firstCandidateKeys: response.candidates?.[0] ? Object.keys(response.candidates[0]) : null, | ||
| contentKeys: response.candidates?.[0]?.content ? Object.keys(response.candidates[0].content) : null, | ||
| partsLength: response.candidates?.[0]?.content?.parts?.length, | ||
| fullResponse: JSON.stringify(response, null, 2), |
There was a problem hiding this comment.
Logging the full Gemini response at info level (including JSON.stringify(response)) will likely include the base64 image payload in inlineData.data. This can leak sensitive content into logs and can generate extremely large log lines. Prefer removing fullResponse, or redacting/truncating binary fields and logging only at debug level (or only when an error/unexpected structure occurs).
| fullResponse: JSON.stringify(response, null, 2), |
| if (part.inlineData?.data) { | ||
| imageBytes = Buffer.concat([imageBytes, Buffer.from(part.inlineData.data, 'base64')]); | ||
| } else { | ||
| this.aiServiceLogger.info('Part does not have inlineData.data:', Object.keys(part)); |
There was a problem hiding this comment.
These info logs fire on every non-inline part encountered, which can become noisy in production and make it harder to spot real issues. Consider downgrading to debug and/or logging only once with a summary of part types when the response is unexpected.
| this.aiServiceLogger.info('Part does not have inlineData.data:', Object.keys(part)); | |
| this.aiServiceLogger.debug('Part does not have inlineData.data:', Object.keys(part)); |
| this.aiServiceLogger.info('Gemini generateImage response structure:', { | ||
| hasCandidates: !!response.candidates, | ||
| candidatesLength: response.candidates?.length, | ||
| firstCandidateKeys: response.candidates?.[0] ? Object.keys(response.candidates[0]) : null, | ||
| contentKeys: response.candidates?.[0]?.content ? Object.keys(response.candidates[0].content) : null, |
There was a problem hiding this comment.
The "Gemini generateImage response structure" message is logged at info for every image generation, which is likely to be high-volume and may bloat logs. Consider using debug, sampling, or logging this structure only when candidates/parts are missing (i.e., on the warning/error paths).
| if (part.inlineData?.data) { | ||
| imageBytes = Buffer.concat([imageBytes, Buffer.from(part.inlineData.data, 'base64')]); | ||
| } else { | ||
| this.aiServiceLogger.info('GenerateImage part does not have inlineData.data:', Object.keys(part)); |
There was a problem hiding this comment.
This per-part info log in generateImage can be emitted many times and will add noise at scale. Consider moving it to debug and/or including more actionable context (e.g., part type) while avoiding high-cardinality logs.
| this.aiServiceLogger.info('GenerateImage part does not have inlineData.data:', Object.keys(part)); | |
| this.aiServiceLogger.debug('GenerateImage part does not have inlineData.data:', Object.keys(part)); |