Repository navigation
feat(ai): add image input support to GLM - #16895
andrewheard wants to merge 8 commits into
Conversation
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. |
Summary of ChangesThis pull request introduces image input support for the Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Activity
|
There was a problem hiding this comment.
Code Review
This pull request adds image input support (the vision capability) to GeminiLanguageModel. It introduces GeminiImageTranslator to process image attachments from CGImage, CVPixelBuffer, or file URLs, handling orientation adjustments and JPEG re-encoding. The GeminiTranscriptTranslator is updated to extract image parts from prompt segments, and the model's capabilities are expanded to include .vision. Comprehensive unit and integration tests are added to validate these changes. As there are no review comments, I have no feedback to provide.
2837ec5 to
0b726ee
Compare
Declare the `vision` capability in `GeminiLanguageModel` and translate image attachments in prompt entries into Gemini `inlineData` parts. Previously, any attachment segment in a transcript threw `unsupportedTranscriptContent`. PNG, JPEG, WebP, HEIC, and HEIF files that are already upright are sent unchanged. All other images (`CGImage` and `CVPixelBuffer` sources, other file formats, and rotated images) are rotated to their display orientation and re-encoded as JPEG at quality 0.9, which matches the format used by `Transcript`'s `Codable` conformance. Images from non-file URLs throw `unsupportedTranscriptContent` for now. They're rejected before `cgImage` is read, since reading it traps for attachments that can't be loaded, such as `gs://` URLs. Attachment labels, media resolution, and images outside of prompts aren't supported yet. Prompt segments keep their order: adjacent text segments are merged into one text part, and text-only prompts translate exactly as before.
Address code review feedback on image input support: - Convert extended-range and HDR images to sRGB before encoding. Creating an 8-bit bitmap context with an extended-range color space fails, so these valid images previously threw `encodingFailed`. - Decode the primary image of multi-image files, such as HEIC, instead of always decoding the first image. - Fill the bitmap with black before drawing instead of relying on zero-initialized memory to composite transparency onto black. - Use typed throws so that every image translation error is mapped to `unsupportedTranscriptContent`. - Describe unsupported URLs by scheme only, so error messages don't include long `data:` URLs or tokens from signed URLs. - Replace `default:` cases with explicit cases so that new cases are flagged by the compiler. - Add tests for `CVPixelBuffer` sources, extended-range, HDR, and Display P3 images, HEIC orientation, and data attachments in prompts, and skip fixture formats that the platform can't encode.
- Use the first image of multi-image files, such as HEIC, for both the pixels and the orientation. `Transcript.ImageAttachment` reads both from the first image, but the primary image was being decoded with the first image's orientation, which could send a different or sideways image. Files are now only passed through unchanged when their primary image is also their first image. - Throw `undecodableImage` for `CIImage` attachments with an empty or infinite extent instead of trapping when reading `cgImage`. - Simplify prompt part extraction by dropping empty text runs as they're flushed; translated prompts are unchanged. - Clarify that attachment segments are only supported in prompts. - Make the image translator's internal helpers private. - Wrap a line that exceeded the 100-column limit. - Document and test `CIImage` sources, test multi-image HEIC files whose primary image isn't first, and test transparency with transparent white pixels so that ignoring the alpha channel would be caught.
- Verify that each multi-image HEIC fixture really has a primary image at index 1, so the tests can't silently pass on an encoder that ignores the primary image property. - Add a test that fails if `Transcript.ImageAttachment` stops reading its orientation and pixels from the first image of a multi-image file, which the translator relies on. - Replace string-keyed test arguments for unrenderable `CIImage` attachments with an enum. - Assert the "only supported in prompts" message for responses too. - Document that files whose primary image isn't their first image aren't sent unchanged, and document `extractText(from:in:)` and `extractOptionalText(from:in:)`.
Update image input support after rebasing onto `main`: - Build `Part` and `Blob` values with the builder-style initializers from the regenerated `GeminiAPIDataModels` (#16881), whose properties are now optional, and unwrap them in tests. - Gate the `Transcript.Attachment.data` case and its test on `compiler(>=6.4.2)` instead of `canImport(FoundationModels, _version: 2.1)`, matching the gating of `Transcript.Entry.data` (#16910).
The image input feature won't ship in Firebase 13.1.0, so move its changelog entry from the 13.1.0 section into a new Unreleased section.
0b726ee to
e695ae6
Compare
- Test that files with an explicit `Orientation = 1` tag, which cameras commonly write, are passed through unchanged. - Test that grayscale images are converted to sRGB. Core Graphics accepts an 8-bit `noneSkipLast` context with a grayscale color space, so this guards the check that only RGB color spaces are reused. - Test the error description for URLs without a scheme. - Make the `InlineBlob` test type private. - Document that tools taking an `ImageReference` only work if the prompt text mentions the attachment label, since labels aren't sent to the model.
- Document that image files are read again for every request, so a file must remain available for as long as its session is used. - Read image files with `.mappedIfSafe`. - Require the orientation tag in the ImageIO comparison test's fixture and check that the image is re-encoded, so the test can't pass without rotating anything. - Check encoder availability by encoding a 1x1 image, since a type can be listed by `CGImageDestinationCopyTypeIdentifiers()` even when its encoder is unavailable (e.g., HEVC on a virtual machine). - Test that `data:` URLs throw `unsupportedURL`. - Add an integration test that each backend accepts HEIC files sent unchanged as `image/heic`. - Document the remaining test enum cases and move the unrenderable `CIImage` cases next to the other nested test types.
GeminiLanguageModelnow declares thevisioncapability. Foundation Models prompts can include image attachments, which are sent to Gemini as inline data:This is Phase 1 of image input support. It covers images created from a
CGImage,CIImage, orCVPixelBuffer, and images from local files. Images from remote URLs (https,gs, …) currently throw an error; Phase 2 will forward them as GeminifileData.How images are sent
Image attachments in prompts are translated by the new
GeminiImageTranslator:.uporientationCGImage,CIImage, orCVPixelBufferhttps,gs,data, …)LanguageModelError.unsupportedTranscriptContent(Phase 2)Details
photo.jpgis still sent asimage/png.Transcript.ImageAttachment.cgImagedoesn't applyorientation, so the translator rotates or flips the pixels itself for all 8 EXIF orientations. An explicitorientation:overrides the orientation embedded in the file, matchingImageAttachment.Transcript'sCodableconformance uses when it saves images.Transcriptsaves images.ImageAttachmentreads both its orientation and its pixels from the first image. Such a file is only sent unchanged if its primary image is also its first image; otherwise Gemini would decode a different image from the original bytes.cgImagecrashes for URL attachments that can't be loaded (for example,gs://) and forCIImages with an empty or infinite extent. The translator never readscgImagefor URL attachments, and it rejects thoseCIImages first. All translation failures surface asLanguageModelError.unsupportedTranscriptContent, with the offending prompt entry and a description. Unsupported URLs are described by their scheme only, so error messages don't includedata:payloads or signed-URL tokens.Current limitations
These are documented on
GeminiLanguageModel.capabilities:unsupportedTranscriptContent, and so doTranscript.Attachment.dataattachments.fileData.ImageReferenceonly work if the prompt text mentions the label. The stacked follow-up PR fixes this.Testing
Unit tests (
GeminiImageTranslatorTests,GeminiTranscriptTranslatorTests,GeminiLanguageModelTests), using images generated in code with no bundled resources:CGImagesources, checked against a hand-derived table of quadrant colors and against ImageIO's own EXIF transform. Also covered forCVPixelBufferandCIImagesources, and for JPEG and HEIC files with embedded orientation (including an explicit.upoverriding it).Orientation = 1tag, and the format is detected from the contents. TIFF, GIF, and BMP are re-encoded.ImageAttachmentever stops using the first image.data:URLs (including the scheme-only messages), and unrenderableCIImages.Attachmentsent throughLanguageModelSessionreaches the request as inline JPEG data (mocked transport).Integration tests (
ImageUnderstandingIntegrationTests), on all three backends (Developer API, Firebase AI Logic Developer API, and Firebase AI Logic Agent Platform):@Generableshape classification.downmust report the colors in its display orientationThese use the default sampling mode, since many Gemini 3 models don't support greedy sampling. Prompts are tightly constrained so responses stay predictable.
Toolchains: unit tests pass on Xcode 27.2 beta 2 (Swift 6.4) and beta 3 (Swift 6.4.2).
swift-format lint --strictis clean.Usage examples
Describe an image
Compare two images
Prompt segments keep their order, so text can introduce or follow each image.
Analyze a camera frame with its orientation
Camera frames are often stored sideways. Pass the display orientation, and the image is rotated before it's sent.
Send a photo from a file
Upright PNG, JPEG, WebP, HEIC, and HEIF files are sent unchanged. Photos with embedded rotation are rotated and re-encoded as JPEG. The file must stay available for as long as the session is used.
Classify an image with guided generation
Wrap the enum in a
@Generablestruct; top-level enums currently fail to decode (see "Notes for reviewers").Ask follow-up questions about an image
Images stay in the session's history, so later turns can refer to them. Earlier images are re-sent with every request, which counts toward the request size limit.