1515 * limitations under the License.
1616 */
1717import { describe , expect , it , jest , afterEach } from '@jest/globals' ;
18- import { addHelpers , formatBlockErrorMessage } from '../lib/requests/response-helpers' ;
18+ import {
19+ addHelpers ,
20+ formatBlockErrorMessage ,
21+ handlePredictResponse ,
22+ } from '../lib/requests/response-helpers' ;
1923
20- import { BlockReason , Content , FinishReason , GenerateContentResponse } from '../lib/types' ;
24+ import {
25+ BlockReason ,
26+ Content ,
27+ FinishReason ,
28+ GenerateContentResponse ,
29+ ImagenInlineImage ,
30+ ImagenGCSImage ,
31+ } from '../lib/types' ;
32+ import { getMockResponse , BackendName } from './test-utils/mock-response' ;
2133
2234const fakeResponseText : GenerateContentResponse = {
2335 candidates : [
@@ -31,6 +43,18 @@ const fakeResponseText: GenerateContentResponse = {
3143 ] ,
3244} ;
3345
46+ const fakeResponseThoughts : GenerateContentResponse = {
47+ candidates : [
48+ {
49+ index : 0 ,
50+ content : {
51+ role : 'model' ,
52+ parts : [ { text : 'Some text' } , { text : 'and some thoughts' , thought : true } ] ,
53+ } ,
54+ } ,
55+ ] ,
56+ } ;
57+
3458const functionCallPart1 = {
3559 functionCall : {
3660 name : 'find_theaters' ,
@@ -129,12 +153,14 @@ describe('response-helpers methods', () => {
129153 const enhancedResponse = addHelpers ( fakeResponseText ) ;
130154 expect ( enhancedResponse . text ( ) ) . toBe ( 'Some text and some more text' ) ;
131155 expect ( enhancedResponse . functionCalls ( ) ) . toBeUndefined ( ) ;
156+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
132157 } ) ;
133158
134159 it ( 'good response functionCall' , ( ) => {
135160 const enhancedResponse = addHelpers ( fakeResponseFunctionCall ) ;
136161 expect ( enhancedResponse . text ( ) ) . toBe ( '' ) ;
137162 expect ( enhancedResponse . functionCalls ( ) ) . toEqual ( [ functionCallPart1 . functionCall ] ) ;
163+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
138164 } ) ;
139165
140166 it ( 'good response functionCalls' , ( ) => {
@@ -144,29 +170,41 @@ describe('response-helpers methods', () => {
144170 functionCallPart1 . functionCall ,
145171 functionCallPart2 . functionCall ,
146172 ] ) ;
173+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
147174 } ) ;
148175
149176 it ( 'good response text/functionCall' , ( ) => {
150177 const enhancedResponse = addHelpers ( fakeResponseMixed1 ) ;
151178 expect ( enhancedResponse . functionCalls ( ) ) . toEqual ( [ functionCallPart2 . functionCall ] ) ;
152179 expect ( enhancedResponse . text ( ) ) . toBe ( 'some text' ) ;
180+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
153181 } ) ;
154182
155183 it ( 'good response functionCall/text' , ( ) => {
156184 const enhancedResponse = addHelpers ( fakeResponseMixed2 ) ;
157185 expect ( enhancedResponse . functionCalls ( ) ) . toEqual ( [ functionCallPart1 . functionCall ] ) ;
158186 expect ( enhancedResponse . text ( ) ) . toBe ( 'some text' ) ;
187+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
159188 } ) ;
160189
161190 it ( 'good response text/functionCall/text' , ( ) => {
162191 const enhancedResponse = addHelpers ( fakeResponseMixed3 ) ;
163192 expect ( enhancedResponse . functionCalls ( ) ) . toEqual ( [ functionCallPart1 . functionCall ] ) ;
164193 expect ( enhancedResponse . text ( ) ) . toBe ( 'some text and more text' ) ;
194+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBeUndefined ( ) ;
195+ } ) ;
196+
197+ it ( 'good response text/thought' , ( ) => {
198+ const enhancedResponse = addHelpers ( fakeResponseThoughts ) ;
199+ expect ( enhancedResponse . text ( ) ) . toBe ( 'Some text' ) ;
200+ expect ( enhancedResponse . thoughtSummary ( ) ) . toBe ( 'and some thoughts' ) ;
201+ expect ( enhancedResponse . functionCalls ( ) ) . toBeUndefined ( ) ;
165202 } ) ;
166203
167204 it ( 'bad response safety' , ( ) => {
168205 const enhancedResponse = addHelpers ( badFakeResponse ) ;
169206 expect ( ( ) => enhancedResponse . text ( ) ) . toThrow ( 'SAFETY' ) ;
207+ expect ( ( ) => enhancedResponse . thoughtSummary ( ) ) . toThrow ( 'SAFETY' ) ;
170208 } ) ;
171209 } ) ;
172210
@@ -233,4 +271,80 @@ describe('response-helpers methods', () => {
233271 expect ( message ) . toContain ( 'Candidate was blocked due to SAFETY: unsafe candidate' ) ;
234272 } ) ;
235273 } ) ;
274+
275+ describe ( 'handlePredictResponse' , ( ) => {
276+ it ( 'returns base64 images' , async ( ) => {
277+ const mockResponse = getMockResponse (
278+ BackendName . VertexAI ,
279+ 'unary-success-generate-images-base64.json' ,
280+ ) as Response ;
281+ const res = await handlePredictResponse < ImagenInlineImage > ( mockResponse ) ;
282+ expect ( res . filteredReason ) . toBeUndefined ( ) ;
283+ expect ( res . images . length ) . toBe ( 4 ) ;
284+ res . images . forEach ( image => {
285+ expect ( image . mimeType ) . toBe ( 'image/png' ) ;
286+ expect ( image . bytesBase64Encoded . length ) . toBeGreaterThan ( 0 ) ;
287+ } ) ;
288+ } ) ;
289+
290+ it ( 'returns GCS images' , async ( ) => {
291+ const mockResponse = getMockResponse (
292+ BackendName . VertexAI ,
293+ 'unary-success-generate-images-gcs.json' ,
294+ ) as Response ;
295+ const res = await handlePredictResponse < ImagenGCSImage > ( mockResponse ) ;
296+ expect ( res . filteredReason ) . toBeUndefined ( ) ;
297+ expect ( res . images . length ) . toBe ( 4 ) ;
298+ res . images . forEach ( ( image , i ) => {
299+ expect ( image . mimeType ) . toBe ( 'image/jpeg' ) ;
300+ expect ( image . gcsURI ) . toBe (
301+ `gs://test-project-id-1234.firebasestorage.app/images/1234567890123/sample_${ i } .jpg` ,
302+ ) ;
303+ } ) ;
304+ } ) ;
305+
306+ it ( 'has filtered reason and no images if all images were filtered' , async ( ) => {
307+ const mockResponse = getMockResponse (
308+ BackendName . VertexAI ,
309+ 'unary-failure-generate-images-all-filtered.json' ,
310+ ) as Response ;
311+ const res = await handlePredictResponse < ImagenInlineImage > ( mockResponse ) ;
312+ expect ( res . filteredReason ) . toBe (
313+ "Unable to show generated images. All images were filtered out because they violated Vertex AI's usage guidelines. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback. Support codes: 39322892, 29310472" ,
314+ ) ;
315+ expect ( res . images . length ) . toBe ( 0 ) ;
316+ } ) ;
317+
318+ it ( 'has filtered reason and no images if all base64 images were filtered' , async ( ) => {
319+ const mockResponse = getMockResponse (
320+ BackendName . VertexAI ,
321+ 'unary-failure-generate-images-base64-some-filtered.json' ,
322+ ) as Response ;
323+ const res = await handlePredictResponse < ImagenInlineImage > ( mockResponse ) ;
324+ expect ( res . filteredReason ) . toBe (
325+ 'Your current safety filter threshold filtered out 2 generated images. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback.' ,
326+ ) ;
327+ expect ( res . images . length ) . toBe ( 2 ) ;
328+ res . images . forEach ( image => {
329+ expect ( image . mimeType ) . toBe ( 'image/png' ) ;
330+ expect ( image . bytesBase64Encoded . length ) . toBeGreaterThan ( 0 ) ;
331+ } ) ;
332+ } ) ;
333+
334+ it ( 'has filtered reason and no images if all GCS images were filtered' , async ( ) => {
335+ const mockResponse = getMockResponse (
336+ BackendName . VertexAI ,
337+ 'unary-failure-generate-images-gcs-some-filtered.json' ,
338+ ) as Response ;
339+ const res = await handlePredictResponse < ImagenGCSImage > ( mockResponse ) ;
340+ expect ( res . filteredReason ) . toBe (
341+ 'Your current safety filter threshold filtered out 2 generated images. You will not be charged for blocked images. Try rephrasing the prompt. If you think this was an error, send feedback.' ,
342+ ) ;
343+ expect ( res . images . length ) . toBe ( 2 ) ;
344+ res . images . forEach ( image => {
345+ expect ( image . mimeType ) . toBe ( 'image/jpeg' ) ;
346+ expect ( image . gcsURI . length ) . toBeGreaterThan ( 0 ) ;
347+ } ) ;
348+ } ) ;
349+ } ) ;
236350} ) ;
0 commit comments