@firebase/ai 2.8.0 → 2.9.0-20260224183151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-public.d.ts +18 -0
- package/dist/ai.d.ts +36 -0
- package/dist/esm/index.esm.js +237 -50
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/methods/chat-session.d.ts +22 -1
- package/dist/esm/src/methods/generate-content.d.ts +4 -2
- package/dist/esm/src/requests/response-helpers.d.ts +1 -1
- package/dist/esm/src/requests/stream-reader.d.ts +3 -1
- package/dist/esm/src/types/content.d.ts +1 -0
- package/dist/esm/src/types/requests.d.ts +14 -0
- package/dist/index.cjs.js +237 -50
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +237 -50
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +237 -50
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/methods/chat-session.d.ts +22 -1
- package/dist/src/methods/generate-content.d.ts +4 -2
- package/dist/src/requests/response-helpers.d.ts +1 -1
- package/dist/src/requests/stream-reader.d.ts +3 -1
- package/dist/src/types/content.d.ts +1 -0
- package/dist/src/types/requests.d.ts +14 -0
- package/package.json +5 -5
package/dist/ai-public.d.ts
CHANGED
|
@@ -325,6 +325,7 @@ export declare class ChatSession {
|
|
|
325
325
|
* to history.
|
|
326
326
|
*/
|
|
327
327
|
getHistory(): Promise<Content[]>;
|
|
328
|
+
/* Excluded from this release type: _formatRequest */
|
|
328
329
|
/**
|
|
329
330
|
* Sends a chat message and receives a non-streaming
|
|
330
331
|
* {@link GenerateContentResult}
|
|
@@ -336,6 +337,8 @@ export declare class ChatSession {
|
|
|
336
337
|
* and a response promise.
|
|
337
338
|
*/
|
|
338
339
|
sendMessageStream(request: string | Array<string | Part>, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult>;
|
|
340
|
+
/* Excluded from this release type: _getCallableFunctionCalls */
|
|
341
|
+
/* Excluded from this release type: _callFunctionsAsNeeded */
|
|
339
342
|
}
|
|
340
343
|
|
|
341
344
|
/**
|
|
@@ -802,6 +805,11 @@ export declare interface FunctionDeclaration {
|
|
|
802
805
|
* case-sensitive. For a function with no parameters, this can be left unset.
|
|
803
806
|
*/
|
|
804
807
|
parameters?: ObjectSchema | ObjectSchemaRequest;
|
|
808
|
+
/**
|
|
809
|
+
* Reference to an actual function to call. Specifying this will cause the
|
|
810
|
+
* function to be called automatically when requested by the model.
|
|
811
|
+
*/
|
|
812
|
+
functionReference?: Function;
|
|
805
813
|
}
|
|
806
814
|
|
|
807
815
|
/**
|
|
@@ -844,6 +852,7 @@ export declare interface FunctionResponse {
|
|
|
844
852
|
id?: string;
|
|
845
853
|
name: string;
|
|
846
854
|
response: object;
|
|
855
|
+
parts?: Part[];
|
|
847
856
|
}
|
|
848
857
|
|
|
849
858
|
/**
|
|
@@ -2530,6 +2539,15 @@ export declare interface RequestOptions {
|
|
|
2530
2539
|
* (used regardless of your chosen Gemini API provider).
|
|
2531
2540
|
*/
|
|
2532
2541
|
baseUrl?: string;
|
|
2542
|
+
/**
|
|
2543
|
+
* Limits amount of sequential function calls the SDK can make during automatic
|
|
2544
|
+
* function calling, in order to prevent infinite loops. If not specified,
|
|
2545
|
+
* this value defaults to 10.
|
|
2546
|
+
*
|
|
2547
|
+
* When it reaches this limit, it will return the last response received
|
|
2548
|
+
* from the model, whether it is a text response or further function calls.
|
|
2549
|
+
*/
|
|
2550
|
+
maxSequentalFunctionCalls?: number;
|
|
2533
2551
|
}
|
|
2534
2552
|
|
|
2535
2553
|
/**
|
package/dist/ai.d.ts
CHANGED
|
@@ -371,6 +371,12 @@ export declare class ChatSession {
|
|
|
371
371
|
* to history.
|
|
372
372
|
*/
|
|
373
373
|
getHistory(): Promise<Content[]>;
|
|
374
|
+
/**
|
|
375
|
+
* Format Content into a request for generateContent or
|
|
376
|
+
* generateContentStream.
|
|
377
|
+
* @internal
|
|
378
|
+
*/
|
|
379
|
+
_formatRequest(incomingContent: Content, tempHistory: Content[]): GenerateContentRequest;
|
|
374
380
|
/**
|
|
375
381
|
* Sends a chat message and receives a non-streaming
|
|
376
382
|
* {@link GenerateContentResult}
|
|
@@ -382,6 +388,21 @@ export declare class ChatSession {
|
|
|
382
388
|
* and a response promise.
|
|
383
389
|
*/
|
|
384
390
|
sendMessageStream(request: string | Array<string | Part>, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult>;
|
|
391
|
+
/**
|
|
392
|
+
* Get function calls that the SDK has references to actually call.
|
|
393
|
+
* This is all-or-nothing. If the model is requesting multiple
|
|
394
|
+
* function calls, all of them must have references in order for
|
|
395
|
+
* automatic function calling to work.
|
|
396
|
+
*
|
|
397
|
+
* @internal
|
|
398
|
+
*/
|
|
399
|
+
_getCallableFunctionCalls(response?: GenerateContentResponse): FunctionCall[] | undefined;
|
|
400
|
+
/**
|
|
401
|
+
* Call user-defined functions if requested by the model, and return
|
|
402
|
+
* the response that should be sent to the model.
|
|
403
|
+
* @internal
|
|
404
|
+
*/
|
|
405
|
+
_callFunctionsAsNeeded(functionCalls: FunctionCall[]): Promise<FunctionResponsePart[]>;
|
|
385
406
|
}
|
|
386
407
|
|
|
387
408
|
/**
|
|
@@ -866,6 +887,11 @@ export declare interface FunctionDeclaration {
|
|
|
866
887
|
* case-sensitive. For a function with no parameters, this can be left unset.
|
|
867
888
|
*/
|
|
868
889
|
parameters?: ObjectSchema | ObjectSchemaRequest;
|
|
890
|
+
/**
|
|
891
|
+
* Reference to an actual function to call. Specifying this will cause the
|
|
892
|
+
* function to be called automatically when requested by the model.
|
|
893
|
+
*/
|
|
894
|
+
functionReference?: Function;
|
|
869
895
|
}
|
|
870
896
|
|
|
871
897
|
/**
|
|
@@ -908,6 +934,7 @@ export declare interface FunctionResponse {
|
|
|
908
934
|
id?: string;
|
|
909
935
|
name: string;
|
|
910
936
|
response: object;
|
|
937
|
+
parts?: Part[];
|
|
911
938
|
}
|
|
912
939
|
|
|
913
940
|
/**
|
|
@@ -2676,6 +2703,15 @@ export declare interface RequestOptions {
|
|
|
2676
2703
|
* (used regardless of your chosen Gemini API provider).
|
|
2677
2704
|
*/
|
|
2678
2705
|
baseUrl?: string;
|
|
2706
|
+
/**
|
|
2707
|
+
* Limits amount of sequential function calls the SDK can make during automatic
|
|
2708
|
+
* function calling, in order to prevent infinite loops. If not specified,
|
|
2709
|
+
* this value defaults to 10.
|
|
2710
|
+
*
|
|
2711
|
+
* When it reaches this limit, it will return the last response received
|
|
2712
|
+
* from the model, whether it is a text response or further function calls.
|
|
2713
|
+
*/
|
|
2714
|
+
maxSequentalFunctionCalls?: number;
|
|
2679
2715
|
}
|
|
2680
2716
|
|
|
2681
2717
|
/**
|
package/dist/esm/index.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { FirebaseError, Deferred, getModularInstance } from '@firebase/util';
|
|
|
4
4
|
import { Logger } from '@firebase/logger';
|
|
5
5
|
|
|
6
6
|
var name = "@firebase/ai";
|
|
7
|
-
var version = "2.
|
|
7
|
+
var version = "2.9.0-20260224183151";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @license
|
|
@@ -1776,6 +1776,9 @@ function getText(response, partFilter) {
|
|
|
1776
1776
|
* Returns every {@link FunctionCall} associated with first candidate.
|
|
1777
1777
|
*/
|
|
1778
1778
|
function getFunctionCalls(response) {
|
|
1779
|
+
if (!response) {
|
|
1780
|
+
return undefined;
|
|
1781
|
+
}
|
|
1779
1782
|
const functionCalls = [];
|
|
1780
1783
|
if (response.candidates?.[0].content?.parts) {
|
|
1781
1784
|
for (const part of response.candidates?.[0].content?.parts) {
|
|
@@ -2071,15 +2074,44 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
|
|
|
2071
2074
|
*
|
|
2072
2075
|
* @param response - Response from a fetch call
|
|
2073
2076
|
*/
|
|
2074
|
-
function processStream(response, apiSettings, inferenceSource) {
|
|
2077
|
+
async function processStream(response, apiSettings, inferenceSource) {
|
|
2075
2078
|
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
|
|
2076
2079
|
const responseStream = getResponseStream(inputStream);
|
|
2077
2080
|
// We split the stream so the user can iterate over partial results (stream1)
|
|
2078
2081
|
// while we aggregate the full result for history/final response (stream2).
|
|
2079
2082
|
const [stream1, stream2] = responseStream.tee();
|
|
2083
|
+
const { response: internalResponse, firstValue } = await processStreamInternal(stream2, apiSettings, inferenceSource);
|
|
2080
2084
|
return {
|
|
2081
2085
|
stream: generateResponseSequence(stream1, apiSettings, inferenceSource),
|
|
2082
|
-
response:
|
|
2086
|
+
response: internalResponse,
|
|
2087
|
+
firstValue
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Consumes streams teed from the input stream for internal needs.
|
|
2092
|
+
* The streams need to be teed because each stream can only be consumed
|
|
2093
|
+
* by one reader.
|
|
2094
|
+
*
|
|
2095
|
+
* "streamForPeek"
|
|
2096
|
+
* This tee is used to peek at the first value for relevant information
|
|
2097
|
+
* that we need to evaluate before returning the stream handle to the
|
|
2098
|
+
* client. For example, we need to check if the response is a function
|
|
2099
|
+
* call that may need to be handled by automatic function calling before
|
|
2100
|
+
* returning a response to the client.
|
|
2101
|
+
*
|
|
2102
|
+
* "streamForAggregation"
|
|
2103
|
+
* We iterate through this tee independently from the user and aggregate
|
|
2104
|
+
* it into a single response when the stream is complete. We need this
|
|
2105
|
+
* aggregate object to add to chat history when using ChatSession. It's
|
|
2106
|
+
* also provided to the user if they want it.
|
|
2107
|
+
*/
|
|
2108
|
+
async function processStreamInternal(stream, apiSettings, inferenceSource) {
|
|
2109
|
+
const [streamForPeek, streamForAggregation] = stream.tee();
|
|
2110
|
+
const reader = streamForPeek.getReader();
|
|
2111
|
+
const { value } = await reader.read();
|
|
2112
|
+
return {
|
|
2113
|
+
firstValue: value,
|
|
2114
|
+
response: getResponsePromise(streamForAggregation, apiSettings, inferenceSource)
|
|
2083
2115
|
};
|
|
2084
2116
|
}
|
|
2085
2117
|
async function getResponsePromise(stream, apiSettings, inferenceSource) {
|
|
@@ -2650,6 +2682,11 @@ function validateChatHistory(history) {
|
|
|
2650
2682
|
* by the user, preventing duplicate console logs.
|
|
2651
2683
|
*/
|
|
2652
2684
|
const SILENT_ERROR = 'SILENT_ERROR';
|
|
2685
|
+
/**
|
|
2686
|
+
* Prevent infinite loop if the model continues to request sequential
|
|
2687
|
+
* function calls during automatic function calling.
|
|
2688
|
+
*/
|
|
2689
|
+
const DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS = 10;
|
|
2653
2690
|
/**
|
|
2654
2691
|
* ChatSession class that enables sending chat messages and stores
|
|
2655
2692
|
* history of sent and received messages so far.
|
|
@@ -2684,48 +2721,89 @@ class ChatSession {
|
|
|
2684
2721
|
return this._history;
|
|
2685
2722
|
}
|
|
2686
2723
|
/**
|
|
2687
|
-
*
|
|
2688
|
-
*
|
|
2724
|
+
* Format Content into a request for generateContent or
|
|
2725
|
+
* generateContentStream.
|
|
2726
|
+
* @internal
|
|
2689
2727
|
*/
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
const newContent = formatNewContent(request);
|
|
2693
|
-
const generateContentRequest = {
|
|
2728
|
+
_formatRequest(incomingContent, tempHistory) {
|
|
2729
|
+
return {
|
|
2694
2730
|
safetySettings: this.params?.safetySettings,
|
|
2695
2731
|
generationConfig: this.params?.generationConfig,
|
|
2696
2732
|
tools: this.params?.tools,
|
|
2697
2733
|
toolConfig: this.params?.toolConfig,
|
|
2698
2734
|
systemInstruction: this.params?.systemInstruction,
|
|
2699
|
-
contents: [...this._history,
|
|
2735
|
+
contents: [...this._history, ...tempHistory, incomingContent]
|
|
2700
2736
|
};
|
|
2737
|
+
}
|
|
2738
|
+
/**
|
|
2739
|
+
* Sends a chat message and receives a non-streaming
|
|
2740
|
+
* {@link GenerateContentResult}
|
|
2741
|
+
*/
|
|
2742
|
+
async sendMessage(request, singleRequestOptions) {
|
|
2701
2743
|
let finalResult = {};
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2744
|
+
await this._sendPromise;
|
|
2745
|
+
/**
|
|
2746
|
+
* Temporarily store multiple turns for cases like automatic function
|
|
2747
|
+
* calling, only writing them to official history when the entire
|
|
2748
|
+
* sequence has completed successfully.
|
|
2749
|
+
*/
|
|
2750
|
+
const tempHistory = [];
|
|
2751
|
+
this._sendPromise = this._sendPromise.then(async () => {
|
|
2752
|
+
let functionCalls;
|
|
2753
|
+
let functionCallTurnCount = 0;
|
|
2754
|
+
const functionCallMaxTurns = this.requestOptions?.maxSequentalFunctionCalls ??
|
|
2755
|
+
DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS;
|
|
2756
|
+
// Repeats until model returns a response with no function calls
|
|
2757
|
+
// or until `functionCallMaxTurns` is met or exceeded.
|
|
2758
|
+
do {
|
|
2759
|
+
let formattedContent;
|
|
2760
|
+
if (functionCalls) {
|
|
2761
|
+
functionCallTurnCount++;
|
|
2762
|
+
const functionResponseParts = await this._callFunctionsAsNeeded(functionCalls);
|
|
2763
|
+
formattedContent = formatNewContent(functionResponseParts);
|
|
2764
|
+
}
|
|
2765
|
+
else {
|
|
2766
|
+
formattedContent = formatNewContent(request);
|
|
2724
2767
|
}
|
|
2768
|
+
const formattedRequest = this._formatRequest(formattedContent, tempHistory);
|
|
2769
|
+
tempHistory.push(formattedContent);
|
|
2770
|
+
const result = await generateContent(this._apiSettings, this.model, formattedRequest, this.chromeAdapter, {
|
|
2771
|
+
...this.requestOptions,
|
|
2772
|
+
...singleRequestOptions
|
|
2773
|
+
});
|
|
2774
|
+
if (result) {
|
|
2775
|
+
finalResult = result;
|
|
2776
|
+
functionCalls = this._getCallableFunctionCalls(result.response);
|
|
2777
|
+
if (result.response.candidates &&
|
|
2778
|
+
result.response.candidates.length > 0) {
|
|
2779
|
+
// TODO: Make this update atomic. If creating `responseContent` throws,
|
|
2780
|
+
// history will contain the user message but not the response, causing
|
|
2781
|
+
// validation errors on the next request.
|
|
2782
|
+
const responseContent = {
|
|
2783
|
+
parts: result.response.candidates?.[0].content.parts || [],
|
|
2784
|
+
// Response seems to come back without a role set.
|
|
2785
|
+
role: result.response.candidates?.[0].content.role || 'model'
|
|
2786
|
+
};
|
|
2787
|
+
tempHistory.push(responseContent);
|
|
2788
|
+
}
|
|
2789
|
+
else {
|
|
2790
|
+
const blockErrorMessage = formatBlockErrorMessage(result.response);
|
|
2791
|
+
if (blockErrorMessage) {
|
|
2792
|
+
logger.warn(`sendMessage() was unsuccessful. ${blockErrorMessage}. Inspect response object for details.`);
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
else {
|
|
2797
|
+
functionCalls = undefined;
|
|
2798
|
+
}
|
|
2799
|
+
} while (functionCalls && functionCallTurnCount < functionCallMaxTurns);
|
|
2800
|
+
if (functionCalls && functionCallTurnCount >= functionCallMaxTurns) {
|
|
2801
|
+
logger.warn(`Automatic function calling exceeded the limit of` +
|
|
2802
|
+
` ${functionCallMaxTurns} function calls. Returning last model response.`);
|
|
2725
2803
|
}
|
|
2726
|
-
finalResult = result;
|
|
2727
2804
|
});
|
|
2728
2805
|
await this._sendPromise;
|
|
2806
|
+
this._history = this._history.concat(tempHistory);
|
|
2729
2807
|
return finalResult;
|
|
2730
2808
|
}
|
|
2731
2809
|
/**
|
|
@@ -2735,23 +2813,62 @@ class ChatSession {
|
|
|
2735
2813
|
*/
|
|
2736
2814
|
async sendMessageStream(request, singleRequestOptions) {
|
|
2737
2815
|
await this._sendPromise;
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2816
|
+
/**
|
|
2817
|
+
* Temporarily store multiple turns for cases like automatic function
|
|
2818
|
+
* calling, only writing them to official history when the entire
|
|
2819
|
+
* sequence has completed successfully.
|
|
2820
|
+
*/
|
|
2821
|
+
const tempHistory = [];
|
|
2822
|
+
const callGenerateContentStream = async () => {
|
|
2823
|
+
let functionCalls;
|
|
2824
|
+
let functionCallTurnCount = 0;
|
|
2825
|
+
const functionCallMaxTurns = this.requestOptions?.maxSequentalFunctionCalls ??
|
|
2826
|
+
DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS;
|
|
2827
|
+
let result;
|
|
2828
|
+
// Repeats until model returns a response with no function calls
|
|
2829
|
+
// or until `functionCallMaxTurns` is met or exceeded.
|
|
2830
|
+
do {
|
|
2831
|
+
let formattedContent;
|
|
2832
|
+
if (functionCalls) {
|
|
2833
|
+
functionCallTurnCount++;
|
|
2834
|
+
const functionResponseParts = await this._callFunctionsAsNeeded(functionCalls);
|
|
2835
|
+
formattedContent = formatNewContent(functionResponseParts);
|
|
2836
|
+
}
|
|
2837
|
+
else {
|
|
2838
|
+
formattedContent = formatNewContent(request);
|
|
2839
|
+
}
|
|
2840
|
+
tempHistory.push(formattedContent);
|
|
2841
|
+
const formattedRequest = this._formatRequest(formattedContent, tempHistory);
|
|
2842
|
+
result = await generateContentStream(this._apiSettings, this.model, formattedRequest, this.chromeAdapter, {
|
|
2843
|
+
...this.requestOptions,
|
|
2844
|
+
...singleRequestOptions
|
|
2845
|
+
});
|
|
2846
|
+
functionCalls = this._getCallableFunctionCalls(result.firstValue);
|
|
2847
|
+
if (functionCalls &&
|
|
2848
|
+
result.firstValue &&
|
|
2849
|
+
result.firstValue.candidates &&
|
|
2850
|
+
result.firstValue.candidates.length > 0) {
|
|
2851
|
+
const responseContent = {
|
|
2852
|
+
...result.firstValue.candidates[0].content
|
|
2853
|
+
};
|
|
2854
|
+
if (!responseContent.role) {
|
|
2855
|
+
responseContent.role = 'model';
|
|
2856
|
+
}
|
|
2857
|
+
tempHistory.push(responseContent);
|
|
2858
|
+
}
|
|
2859
|
+
} while (functionCalls && functionCallTurnCount < functionCallMaxTurns);
|
|
2860
|
+
if (functionCalls && functionCallTurnCount >= functionCallMaxTurns) {
|
|
2861
|
+
logger.warn(`Automatic function calling exceeded the limit of` +
|
|
2862
|
+
` ${functionCallMaxTurns} function calls. Returning last model response.`);
|
|
2863
|
+
}
|
|
2864
|
+
return { stream: result.stream, response: result.response };
|
|
2746
2865
|
};
|
|
2747
|
-
const streamPromise =
|
|
2748
|
-
|
|
2749
|
-
...singleRequestOptions
|
|
2750
|
-
});
|
|
2751
|
-
// We hook into the chain to update history, but we don't block the
|
|
2752
|
-
// return of `streamPromise` to the user.
|
|
2866
|
+
const streamPromise = callGenerateContentStream();
|
|
2867
|
+
// Add onto the chain.
|
|
2753
2868
|
this._sendPromise = this._sendPromise
|
|
2754
|
-
.then(() => streamPromise)
|
|
2869
|
+
.then(async () => streamPromise)
|
|
2870
|
+
// This must be handled to avoid unhandled rejection, but jump
|
|
2871
|
+
// to the final catch block with a label to not log this error.
|
|
2755
2872
|
.catch(_ignored => {
|
|
2756
2873
|
// If the initial fetch fails, the user's `streamPromise` rejects.
|
|
2757
2874
|
// We swallow the error here to prevent double logging in the final catch.
|
|
@@ -2764,7 +2881,7 @@ class ChatSession {
|
|
|
2764
2881
|
// TODO: Move response validation logic upstream to `stream-reader` so
|
|
2765
2882
|
// errors propagate to the user's `result.response` promise.
|
|
2766
2883
|
if (response.candidates && response.candidates.length > 0) {
|
|
2767
|
-
this._history.
|
|
2884
|
+
this._history = this._history.concat(tempHistory);
|
|
2768
2885
|
// TODO: Validate that `response.candidates[0].content` is not null.
|
|
2769
2886
|
const responseContent = { ...response.candidates[0].content };
|
|
2770
2887
|
if (!responseContent.role) {
|
|
@@ -2787,6 +2904,75 @@ class ChatSession {
|
|
|
2787
2904
|
});
|
|
2788
2905
|
return streamPromise;
|
|
2789
2906
|
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Get function calls that the SDK has references to actually call.
|
|
2909
|
+
* This is all-or-nothing. If the model is requesting multiple
|
|
2910
|
+
* function calls, all of them must have references in order for
|
|
2911
|
+
* automatic function calling to work.
|
|
2912
|
+
*
|
|
2913
|
+
* @internal
|
|
2914
|
+
*/
|
|
2915
|
+
_getCallableFunctionCalls(response) {
|
|
2916
|
+
const functionDeclarationsTool = this.params?.tools?.find(tool => tool.functionDeclarations);
|
|
2917
|
+
if (!functionDeclarationsTool?.functionDeclarations) {
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
const functionCalls = getFunctionCalls(response);
|
|
2921
|
+
if (!functionCalls) {
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
for (const functionCall of functionCalls) {
|
|
2925
|
+
const hasFunctionReference = functionDeclarationsTool.functionDeclarations?.some(declaration => declaration.name === functionCall.name &&
|
|
2926
|
+
typeof declaration.functionReference === 'function');
|
|
2927
|
+
if (!hasFunctionReference) {
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return functionCalls;
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Call user-defined functions if requested by the model, and return
|
|
2935
|
+
* the response that should be sent to the model.
|
|
2936
|
+
* @internal
|
|
2937
|
+
*/
|
|
2938
|
+
async _callFunctionsAsNeeded(functionCalls) {
|
|
2939
|
+
const activeCallList = new Map();
|
|
2940
|
+
const promiseList = [];
|
|
2941
|
+
const functionDeclarationsTool = this.params?.tools?.find(tool => tool.functionDeclarations);
|
|
2942
|
+
if (functionDeclarationsTool &&
|
|
2943
|
+
functionDeclarationsTool.functionDeclarations) {
|
|
2944
|
+
for (const functionCall of functionCalls) {
|
|
2945
|
+
const functionDeclaration = functionDeclarationsTool.functionDeclarations.find(declaration => declaration.name === functionCall.name);
|
|
2946
|
+
if (functionDeclaration?.functionReference) {
|
|
2947
|
+
const results = Promise.resolve(functionDeclaration.functionReference(functionCall.args)).catch(e => {
|
|
2948
|
+
const wrappedError = new AIError(AIErrorCode.ERROR, `Error in user-defined function "${functionDeclaration.name}": ${e.message}`);
|
|
2949
|
+
wrappedError.stack = e.stack;
|
|
2950
|
+
throw wrappedError;
|
|
2951
|
+
});
|
|
2952
|
+
activeCallList.set(functionCall.name, {
|
|
2953
|
+
id: functionCall.id,
|
|
2954
|
+
results
|
|
2955
|
+
});
|
|
2956
|
+
promiseList.push(results);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
// Wait for promises to finish.
|
|
2960
|
+
await Promise.all(promiseList);
|
|
2961
|
+
const functionResponseParts = [];
|
|
2962
|
+
for (const [name, callData] of activeCallList) {
|
|
2963
|
+
functionResponseParts.push({
|
|
2964
|
+
functionResponse: {
|
|
2965
|
+
name,
|
|
2966
|
+
response: await callData.results
|
|
2967
|
+
}
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
return functionResponseParts;
|
|
2971
|
+
}
|
|
2972
|
+
else {
|
|
2973
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, `No function declarations were provided in "tools".`);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2790
2976
|
}
|
|
2791
2977
|
|
|
2792
2978
|
/**
|
|
@@ -2890,7 +3076,7 @@ class GenerativeModel extends AIModel {
|
|
|
2890
3076
|
*/
|
|
2891
3077
|
async generateContentStream(request, singleRequestOptions) {
|
|
2892
3078
|
const formattedParams = formatGenerateContentInput(request);
|
|
2893
|
-
|
|
3079
|
+
const { stream, response } = await generateContentStream(this._apiSettings, this.model, {
|
|
2894
3080
|
generationConfig: this.generationConfig,
|
|
2895
3081
|
safetySettings: this.safetySettings,
|
|
2896
3082
|
tools: this.tools,
|
|
@@ -2903,6 +3089,7 @@ class GenerativeModel extends AIModel {
|
|
|
2903
3089
|
...this.requestOptions,
|
|
2904
3090
|
...singleRequestOptions
|
|
2905
3091
|
});
|
|
3092
|
+
return { stream, response };
|
|
2906
3093
|
}
|
|
2907
3094
|
/**
|
|
2908
3095
|
* Gets a new {@link ChatSession} instance which can be used for
|