File tree 3 files changed +125
-0
lines changed
3 files changed +125
-0
lines changed Original file line number Diff line number Diff line change @@ -1791,6 +1791,61 @@ Use the file URL returned from the snippet above.
1791
1791
}
1792
1792
```
1793
1793
1794
+ ### How to generate an image with Gemini
1795
+
1796
+ ```
1797
+ import AIProxy
1798
+
1799
+ /* Uncomment for BYOK use cases */
1800
+ // let geminiService = AIProxy.geminiDirectService(
1801
+ // unprotectedAPIKey: "your-gemini-key"
1802
+ // )
1803
+
1804
+ /* Uncomment for all other production use cases */
1805
+ // let geminiService = AIProxy.geminiService(
1806
+ // partialKey: "partial-key-from-your-developer-dashboard",
1807
+ // serviceURL: "service-url-from-your-developer-dashboard"
1808
+ // )
1809
+
1810
+ let requestBody = GeminiGenerateContentRequestBody(
1811
+ contents: [
1812
+ .init(
1813
+ parts: [
1814
+ .text(
1815
+ """
1816
+ Hi, can you create a 3d rendered image of a pig with wings and a top hat
1817
+ flying over a happy futuristic scifi city with lots of greenery?
1818
+ """
1819
+ )
1820
+ ],
1821
+ role: "user"
1822
+ )
1823
+ ],
1824
+ generationConfig: .init(
1825
+ responseModalities: [
1826
+ "Text",
1827
+ "Image"
1828
+ ]
1829
+ )
1830
+ )
1831
+
1832
+ do {
1833
+ let response = try await geminiService.generateContentRequest(
1834
+ body: requestBody,
1835
+ model: "gemini-2.0-flash-exp-image-generation"
1836
+ )
1837
+ for part in response.candidates?.first?.content?.parts ?? [] {
1838
+ if case .inlineData(mimeType: let mimeType, base64Data: let base64Data) = part {
1839
+ print("Gemini generated inline data with mimetype: \(mimeType) and base64Length: \(base64Data.count)")
1840
+ }
1841
+ }
1842
+ } catch AIProxyError.unsuccessfulRequest(let statusCode, let responseBody) {
1843
+ print("Received \(statusCode) status code with response body: \(responseBody)")
1844
+ } catch {
1845
+ print("Could not create image using gemini: \(error.localizedDescription)")
1846
+ }
1847
+ ```
1848
+
1794
1849
1795
1850
***
1796
1851
Original file line number Diff line number Diff line change @@ -65,21 +65,30 @@ extension GeminiGenerateContentResponseBody.Candidate.Content {
65
65
public enum Part : Decodable {
66
66
case text( String )
67
67
case functionCall( name: String , args: [ String : Any ] ? )
68
+ case inlineData( mimeType: String , base64Data: String )
68
69
69
70
private enum CodingKeys : String , CodingKey {
70
71
case text
71
72
case functionCall
73
+ case inlineData
72
74
}
73
75
74
76
private struct _FunctionCall : Decodable {
75
77
let name : String
76
78
let args : [ String : AIProxyJSONValue ] ?
77
79
}
78
80
81
+ private struct _InlineData : Decodable {
82
+ let mimeType : String
83
+ let data : String
84
+ }
85
+
79
86
public init ( from decoder: any Decoder ) throws {
80
87
let container = try decoder. container ( keyedBy: CodingKeys . self)
81
88
if let functionCall = try container. decodeIfPresent ( _FunctionCall. self, forKey: . functionCall) {
82
89
self = . functionCall( name: functionCall. name, args: functionCall. args? . untypedDictionary)
90
+ } else if let inlineData = try container. decodeIfPresent ( _InlineData. self, forKey: . inlineData) {
91
+ self = . inlineData( mimeType: inlineData. mimeType, base64Data: inlineData. data)
83
92
} else {
84
93
self = . text( try container. decode ( String . self, forKey: . text) )
85
94
}
Original file line number Diff line number Diff line change
1
+ //
2
+ // GeminiGenerateImageResponseTests.swift
3
+ // AIProxy
4
+ //
5
+ // Created by Lou Zell on 3/17/25.
6
+ //
7
+
8
+ import XCTest
9
+ import Foundation
10
+ @testable import AIProxy
11
+
12
+
13
+ final class GeminiGenerateImageResponseTests : XCTestCase {
14
+
15
+ func testResponseIsDecodable( ) throws {
16
+ let sampleResponse = #"""
17
+ {
18
+ "candidates": [
19
+ {
20
+ "content": {
21
+ "parts": [
22
+ {
23
+ "inlineData": {
24
+ "mimeType": "image/png",
25
+ "data": "<snip>"
26
+ }
27
+ }
28
+ ],
29
+ "role": "model"
30
+ },
31
+ "finishReason": "STOP",
32
+ "index": 0
33
+ }
34
+ ],
35
+ "usageMetadata": {
36
+ "promptTokenCount": 36,
37
+ "totalTokenCount": 36,
38
+ "promptTokensDetails": [
39
+ {
40
+ "modality": "TEXT",
41
+ "tokenCount": 36
42
+ }
43
+ ]
44
+ },
45
+ "modelVersion": "gemini-2.0-flash-exp-image-generation"
46
+ }
47
+ """#
48
+
49
+ let body = try GeminiGenerateContentResponseBody . deserialize ( from: sampleResponse)
50
+ if case . inlineData( mimeType: let mimeType, base64Data: let b64Data) = body. candidates? . first? . content? . parts? . first {
51
+ XCTAssertEqual ( " image/png " , mimeType)
52
+ XCTAssertEqual ( " <snip> " , b64Data)
53
+ } else {
54
+ XCTFail ( )
55
+ }
56
+ }
57
+
58
+ }
59
+
60
+
61
+
You can’t perform that action at this time.
0 commit comments