Skip to content

Commit 673bcb3

Browse files
qiaodevcopybara-github
authored andcommitted
chore: Move functionCalls quick accessor in Candidate interface to a seperate helper function
PiperOrigin-RevId: 618039934
1 parent af6dc00 commit 673bcb3

File tree

7 files changed

+68
-44
lines changed

7 files changed

+68
-44
lines changed

src/functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
export {countTokens} from './count_tokens';
1919
export {postRequest} from './post_request';
2020
export {generateContent, generateContentStream} from './generate_content';
21+
export {ResponseHandler} from './response_handler';

src/functions/post_fetch_processing.ts

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async function* generateResponseSequence(
5858
if (done) {
5959
break;
6060
}
61-
yield addMissingFields(value);
61+
yield addMissingIndexAndRole(value);
6262
}
6363
}
6464

@@ -340,37 +340,6 @@ function addMissingIndexAndRole(
340340
return generateContentResponse;
341341
}
342342

343-
function addCandidateFunctionCalls(
344-
response: GenerateContentResponse
345-
): GenerateContentResponse {
346-
if (!response.candidates) {
347-
return response;
348-
}
349-
for (const candidate of response.candidates) {
350-
if (
351-
!candidate.content ||
352-
!candidate.content.parts ||
353-
candidate.content.parts.length === 0
354-
) {
355-
continue;
356-
}
357-
const functionCalls = candidate.content.parts
358-
.filter((part: Part) => !!part.functionCall)
359-
.map((part: Part) => part.functionCall!);
360-
if (functionCalls.length > 0) {
361-
candidate.functionCalls = functionCalls;
362-
}
363-
}
364-
return response;
365-
}
366-
367-
function addMissingFields(
368-
response: GenerateContentResponse
369-
): GenerateContentResponse {
370-
const generateContentResponse = addMissingIndexAndRole(response);
371-
return addCandidateFunctionCalls(generateContentResponse);
372-
}
373-
374343
/**
375344
* Process model responses from generateContent
376345
* @ignore
@@ -383,7 +352,7 @@ export async function processUnary(
383352
const responseJson = await response.json();
384353
const generateContentResponse = addMissingIndexAndRole(responseJson);
385354
return Promise.resolve({
386-
response: addCandidateFunctionCalls(generateContentResponse),
355+
response: generateContentResponse,
387356
});
388357
}
389358

src/functions/response_handler.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import {GenerateContentCandidate, FunctionCall, Part} from '../types';
19+
20+
/** Helper class to render any extra properties out of {@link GenerateContentResponse}. */
21+
export class ResponseHandler {
22+
/**
23+
* Extracts function calls from a {@link GenerateContentCandidate}.
24+
*
25+
* @param candidate - The candidate to extract function calls from.
26+
* @returns the array of function calls in a {@link GenerateContentCandidate}.
27+
*/
28+
static getCandidateFunctionCalls(
29+
candidate?: GenerateContentCandidate
30+
): FunctionCall[] {
31+
if (!candidate) return [] as FunctionCall[];
32+
return candidate.content.parts
33+
.filter((part: Part | undefined) => !!part && !!part.functionCall)
34+
.map((part: Part) => part.functionCall!);
35+
}
36+
}

src/functions/test/functions_test.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {constants} from '../../util';
3636
import {countTokens} from '../count_tokens';
3737
import {generateContent, generateContentStream} from '../generate_content';
3838
import * as StreamFunctions from '../post_fetch_processing';
39+
import {ResponseHandler} from '../response_handler';
3940

4041
const TEST_PROJECT = 'test-project';
4142
const TEST_LOCATION = 'test-location';
@@ -150,7 +151,6 @@ const TEST_CANDIDATES_WITH_FUNCTION_CALL = [
150151
finishReason: FinishReason.STOP,
151152
finishMessage: '',
152153
safetyRatings: TEST_SAFETY_RATINGS,
153-
functionCalls: [TEST_FUNCTION_CALL_RESPONSE.functionCall],
154154
},
155155
];
156156
const TEST_MODEL_RESPONSE_WITH_FUNCTION_CALL = {
@@ -617,8 +617,16 @@ describe('generateContent', () => {
617617
TEST_API_ENDPOINT
618618
);
619619
expect(actualResult).toEqual(expectedResult);
620-
expect(actualResult.response.candidates![0].functionCalls).toHaveSize(1);
621-
expect(actualResult.response.candidates![0].functionCalls).toEqual([
620+
expect(
621+
ResponseHandler.getCandidateFunctionCalls(
622+
actualResult.response.candidates![0]
623+
)
624+
).toHaveSize(1);
625+
expect(
626+
ResponseHandler.getCandidateFunctionCalls(
627+
actualResult.response.candidates![0]
628+
)
629+
).toEqual([
622630
expectedResult.response.candidates![0].content.parts[0].functionCall!,
623631
]);
624632
});
@@ -653,8 +661,10 @@ describe('generateContent', () => {
653661
);
654662
expect(actualResult).toEqual(expectedResult);
655663
expect(
656-
actualResult.response.candidates![0].functionCalls
657-
).not.toBeDefined();
664+
ResponseHandler.getCandidateFunctionCalls(
665+
actualResult.response.candidates?.[0]
666+
)
667+
).toHaveSize(0);
658668
});
659669

660670
it('returns empty candidates when response is empty', async () => {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
export {VertexAI} from './vertex_ai';
1919
export * from './types';
2020
export * from './models';
21+
export {ResponseHandler} from './functions';

src/types/content.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,6 @@ export declare interface GenerateContentCandidate {
525525
citationMetadata?: CitationMetadata;
526526
/** Optional. {@link GroundingMetadata}. */
527527
groundingMetadata?: GroundingMetadata;
528-
/* Optional. Array of {@link FunctionCall}. */
529-
functionCalls?: FunctionCall[];
530528
}
531529

532530
/**

system_test/end_to_end_sample_test.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
Part,
2424
TextPart,
2525
VertexAI,
26+
ResponseHandler,
2627
} from '../src';
2728
import {FunctionDeclarationSchemaType} from '../src/types';
2829

@@ -472,7 +473,9 @@ describe('generateContentStream', () => {
472473
.candidates![0].content.parts.filter(part => !!part.functionCall)
473474
.map(part => part.functionCall!);
474475
expect(functionCalls).toHaveSize(1);
475-
expect(item.candidates![0].functionCalls!).toEqual(functionCalls!);
476+
expect(
477+
ResponseHandler.getCandidateFunctionCalls(item.candidates?.[0])
478+
).toEqual(functionCalls!);
476479
}
477480
});
478481
it('in preview should return a FunctionCall when passed a FunctionDeclaration', async () => {
@@ -494,7 +497,9 @@ describe('generateContentStream', () => {
494497
.candidates![0].content.parts.filter(part => !!part.functionCall)
495498
.map(part => part.functionCall!);
496499
expect(functionCalls).toHaveSize(1);
497-
expect(item.candidates![0].functionCalls!).toEqual(functionCalls!);
500+
expect(
501+
ResponseHandler.getCandidateFunctionCalls(item.candidates?.[0])
502+
).toEqual(functionCalls!);
498503
}
499504
});
500505
xit('should return grounding metadata when passed GoogleSearchRetriever in getGenerativeModel', async () => {
@@ -770,7 +775,9 @@ describe('generateContent', () => {
770775
.candidates![0].content.parts.filter((part: Part) => !!part.functionCall)
771776
.map((part: Part) => part.functionCall!);
772777
expect(functionCalls).toHaveSize(1);
773-
expect(resp.response.candidates![0].functionCalls!).toEqual(functionCalls!);
778+
expect(
779+
ResponseHandler.getCandidateFunctionCalls(resp.response.candidates![0])
780+
).toEqual(functionCalls!);
774781
});
775782
it('in preview should return a FunctionCall when passed a FunctionDeclaration', async () => {
776783
const request = {
@@ -789,7 +796,9 @@ describe('generateContent', () => {
789796
.candidates![0].content.parts.filter((part: Part) => !!part.functionCall)
790797
.map((part: Part) => part.functionCall!);
791798
expect(functionCalls).toHaveSize(1);
792-
expect(resp.response.candidates![0].functionCalls!).toEqual(functionCalls!);
799+
expect(
800+
ResponseHandler.getCandidateFunctionCalls(resp.response.candidates![0])
801+
).toEqual(functionCalls!);
793802
});
794803
});
795804

0 commit comments

Comments
 (0)