@firebase/ai 2.8.0 → 2.9.0-canary.78384d32c
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 +8 -8
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { Content, GenerateContentResult, GenerateContentStreamResult, Part, RequestOptions, SingleRequestOptions, StartChatParams } from '../types';
|
|
17
|
+
import { Content, FunctionCall, FunctionResponsePart, GenerateContentRequest, GenerateContentResponse, GenerateContentResult, GenerateContentStreamResult, Part, RequestOptions, SingleRequestOptions, StartChatParams } from '../types';
|
|
18
18
|
import { ApiSettings } from '../types/internal';
|
|
19
19
|
import { ChromeAdapter } from '../types/chrome-adapter';
|
|
20
20
|
/**
|
|
@@ -42,6 +42,12 @@ export declare class ChatSession {
|
|
|
42
42
|
* to history.
|
|
43
43
|
*/
|
|
44
44
|
getHistory(): Promise<Content[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Format Content into a request for generateContent or
|
|
47
|
+
* generateContentStream.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
_formatRequest(incomingContent: Content, tempHistory: Content[]): GenerateContentRequest;
|
|
45
51
|
/**
|
|
46
52
|
* Sends a chat message and receives a non-streaming
|
|
47
53
|
* {@link GenerateContentResult}
|
|
@@ -53,4 +59,19 @@ export declare class ChatSession {
|
|
|
53
59
|
* and a response promise.
|
|
54
60
|
*/
|
|
55
61
|
sendMessageStream(request: string | Array<string | Part>, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Get function calls that the SDK has references to actually call.
|
|
64
|
+
* This is all-or-nothing. If the model is requesting multiple
|
|
65
|
+
* function calls, all of them must have references in order for
|
|
66
|
+
* automatic function calling to work.
|
|
67
|
+
*
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
_getCallableFunctionCalls(response?: GenerateContentResponse): FunctionCall[] | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Call user-defined functions if requested by the model, and return
|
|
73
|
+
* the response that should be sent to the model.
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
_callFunctionsAsNeeded(functionCalls: FunctionCall[]): Promise<FunctionResponsePart[]>;
|
|
56
77
|
}
|
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { GenerateContentRequest, GenerateContentResult, GenerateContentStreamResult, SingleRequestOptions } from '../types';
|
|
17
|
+
import { GenerateContentRequest, GenerateContentResponse, GenerateContentResult, GenerateContentStreamResult, SingleRequestOptions } from '../types';
|
|
18
18
|
import { ApiSettings } from '../types/internal';
|
|
19
19
|
import { ChromeAdapter } from '../types/chrome-adapter';
|
|
20
|
-
export declare function generateContentStream(apiSettings: ApiSettings, model: string, params: GenerateContentRequest, chromeAdapter?: ChromeAdapter, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult
|
|
20
|
+
export declare function generateContentStream(apiSettings: ApiSettings, model: string, params: GenerateContentRequest, chromeAdapter?: ChromeAdapter, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult & {
|
|
21
|
+
firstValue?: GenerateContentResponse;
|
|
22
|
+
}>;
|
|
21
23
|
export declare function templateGenerateContent(apiSettings: ApiSettings, templateId: string, templateParams: object, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentResult>;
|
|
22
24
|
export declare function templateGenerateContentStream(apiSettings: ApiSettings, templateId: string, templateParams: object, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentStreamResult>;
|
|
23
25
|
export declare function generateContent(apiSettings: ApiSettings, model: string, params: GenerateContentRequest, chromeAdapter?: ChromeAdapter, singleRequestOptions?: SingleRequestOptions): Promise<GenerateContentResult>;
|
|
@@ -36,7 +36,7 @@ export declare function getText(response: GenerateContentResponse, partFilter: (
|
|
|
36
36
|
/**
|
|
37
37
|
* Returns every {@link FunctionCall} associated with first candidate.
|
|
38
38
|
*/
|
|
39
|
-
export declare function getFunctionCalls(response
|
|
39
|
+
export declare function getFunctionCalls(response?: GenerateContentResponse): FunctionCall[] | undefined;
|
|
40
40
|
/**
|
|
41
41
|
* Returns every {@link InlineDataPart} in the first candidate if present.
|
|
42
42
|
*
|
|
@@ -25,7 +25,9 @@ import { InferenceSource } from '../public-types';
|
|
|
25
25
|
*
|
|
26
26
|
* @param response - Response from a fetch call
|
|
27
27
|
*/
|
|
28
|
-
export declare function processStream(response: Response, apiSettings: ApiSettings, inferenceSource?: InferenceSource): GenerateContentStreamResult
|
|
28
|
+
export declare function processStream(response: Response, apiSettings: ApiSettings, inferenceSource?: InferenceSource): Promise<GenerateContentStreamResult & {
|
|
29
|
+
firstValue?: GenerateContentResponse;
|
|
30
|
+
}>;
|
|
29
31
|
/**
|
|
30
32
|
* Reads a raw string stream, buffers incomplete chunks, and yields parsed JSON objects.
|
|
31
33
|
*/
|
|
@@ -231,6 +231,15 @@ export interface RequestOptions {
|
|
|
231
231
|
* (used regardless of your chosen Gemini API provider).
|
|
232
232
|
*/
|
|
233
233
|
baseUrl?: string;
|
|
234
|
+
/**
|
|
235
|
+
* Limits amount of sequential function calls the SDK can make during automatic
|
|
236
|
+
* function calling, in order to prevent infinite loops. If not specified,
|
|
237
|
+
* this value defaults to 10.
|
|
238
|
+
*
|
|
239
|
+
* When it reaches this limit, it will return the last response received
|
|
240
|
+
* from the model, whether it is a text response or further function calls.
|
|
241
|
+
*/
|
|
242
|
+
maxSequentalFunctionCalls?: number;
|
|
234
243
|
}
|
|
235
244
|
/**
|
|
236
245
|
* Options that can be provided per-request.
|
|
@@ -304,6 +313,11 @@ export interface FunctionDeclaration {
|
|
|
304
313
|
* case-sensitive. For a function with no parameters, this can be left unset.
|
|
305
314
|
*/
|
|
306
315
|
parameters?: ObjectSchema | ObjectSchemaRequest;
|
|
316
|
+
/**
|
|
317
|
+
* Reference to an actual function to call. Specifying this will cause the
|
|
318
|
+
* function to be called automatically when requested by the model.
|
|
319
|
+
*/
|
|
320
|
+
functionReference?: Function;
|
|
307
321
|
}
|
|
308
322
|
/**
|
|
309
323
|
* A tool that allows a Gemini model to connect to Google Search to access and incorporate
|
package/dist/index.cjs.js
CHANGED
|
@@ -8,7 +8,7 @@ var util = require('@firebase/util');
|
|
|
8
8
|
var logger$1 = require('@firebase/logger');
|
|
9
9
|
|
|
10
10
|
var name = "@firebase/ai";
|
|
11
|
-
var version = "2.
|
|
11
|
+
var version = "2.9.0-canary.78384d32c";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -1780,6 +1780,9 @@ function getText(response, partFilter) {
|
|
|
1780
1780
|
* Returns every {@link FunctionCall} associated with first candidate.
|
|
1781
1781
|
*/
|
|
1782
1782
|
function getFunctionCalls(response) {
|
|
1783
|
+
if (!response) {
|
|
1784
|
+
return undefined;
|
|
1785
|
+
}
|
|
1783
1786
|
const functionCalls = [];
|
|
1784
1787
|
if (response.candidates?.[0].content?.parts) {
|
|
1785
1788
|
for (const part of response.candidates?.[0].content?.parts) {
|
|
@@ -2075,15 +2078,44 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
|
|
|
2075
2078
|
*
|
|
2076
2079
|
* @param response - Response from a fetch call
|
|
2077
2080
|
*/
|
|
2078
|
-
function processStream(response, apiSettings, inferenceSource) {
|
|
2081
|
+
async function processStream(response, apiSettings, inferenceSource) {
|
|
2079
2082
|
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
|
|
2080
2083
|
const responseStream = getResponseStream(inputStream);
|
|
2081
2084
|
// We split the stream so the user can iterate over partial results (stream1)
|
|
2082
2085
|
// while we aggregate the full result for history/final response (stream2).
|
|
2083
2086
|
const [stream1, stream2] = responseStream.tee();
|
|
2087
|
+
const { response: internalResponse, firstValue } = await processStreamInternal(stream2, apiSettings, inferenceSource);
|
|
2084
2088
|
return {
|
|
2085
2089
|
stream: generateResponseSequence(stream1, apiSettings, inferenceSource),
|
|
2086
|
-
response:
|
|
2090
|
+
response: internalResponse,
|
|
2091
|
+
firstValue
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Consumes streams teed from the input stream for internal needs.
|
|
2096
|
+
* The streams need to be teed because each stream can only be consumed
|
|
2097
|
+
* by one reader.
|
|
2098
|
+
*
|
|
2099
|
+
* "streamForPeek"
|
|
2100
|
+
* This tee is used to peek at the first value for relevant information
|
|
2101
|
+
* that we need to evaluate before returning the stream handle to the
|
|
2102
|
+
* client. For example, we need to check if the response is a function
|
|
2103
|
+
* call that may need to be handled by automatic function calling before
|
|
2104
|
+
* returning a response to the client.
|
|
2105
|
+
*
|
|
2106
|
+
* "streamForAggregation"
|
|
2107
|
+
* We iterate through this tee independently from the user and aggregate
|
|
2108
|
+
* it into a single response when the stream is complete. We need this
|
|
2109
|
+
* aggregate object to add to chat history when using ChatSession. It's
|
|
2110
|
+
* also provided to the user if they want it.
|
|
2111
|
+
*/
|
|
2112
|
+
async function processStreamInternal(stream, apiSettings, inferenceSource) {
|
|
2113
|
+
const [streamForPeek, streamForAggregation] = stream.tee();
|
|
2114
|
+
const reader = streamForPeek.getReader();
|
|
2115
|
+
const { value } = await reader.read();
|
|
2116
|
+
return {
|
|
2117
|
+
firstValue: value,
|
|
2118
|
+
response: getResponsePromise(streamForAggregation, apiSettings, inferenceSource)
|
|
2087
2119
|
};
|
|
2088
2120
|
}
|
|
2089
2121
|
async function getResponsePromise(stream, apiSettings, inferenceSource) {
|
|
@@ -2654,6 +2686,11 @@ function validateChatHistory(history) {
|
|
|
2654
2686
|
* by the user, preventing duplicate console logs.
|
|
2655
2687
|
*/
|
|
2656
2688
|
const SILENT_ERROR = 'SILENT_ERROR';
|
|
2689
|
+
/**
|
|
2690
|
+
* Prevent infinite loop if the model continues to request sequential
|
|
2691
|
+
* function calls during automatic function calling.
|
|
2692
|
+
*/
|
|
2693
|
+
const DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS = 10;
|
|
2657
2694
|
/**
|
|
2658
2695
|
* ChatSession class that enables sending chat messages and stores
|
|
2659
2696
|
* history of sent and received messages so far.
|
|
@@ -2688,48 +2725,89 @@ class ChatSession {
|
|
|
2688
2725
|
return this._history;
|
|
2689
2726
|
}
|
|
2690
2727
|
/**
|
|
2691
|
-
*
|
|
2692
|
-
*
|
|
2728
|
+
* Format Content into a request for generateContent or
|
|
2729
|
+
* generateContentStream.
|
|
2730
|
+
* @internal
|
|
2693
2731
|
*/
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
const newContent = formatNewContent(request);
|
|
2697
|
-
const generateContentRequest = {
|
|
2732
|
+
_formatRequest(incomingContent, tempHistory) {
|
|
2733
|
+
return {
|
|
2698
2734
|
safetySettings: this.params?.safetySettings,
|
|
2699
2735
|
generationConfig: this.params?.generationConfig,
|
|
2700
2736
|
tools: this.params?.tools,
|
|
2701
2737
|
toolConfig: this.params?.toolConfig,
|
|
2702
2738
|
systemInstruction: this.params?.systemInstruction,
|
|
2703
|
-
contents: [...this._history,
|
|
2739
|
+
contents: [...this._history, ...tempHistory, incomingContent]
|
|
2704
2740
|
};
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* Sends a chat message and receives a non-streaming
|
|
2744
|
+
* {@link GenerateContentResult}
|
|
2745
|
+
*/
|
|
2746
|
+
async sendMessage(request, singleRequestOptions) {
|
|
2705
2747
|
let finalResult = {};
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2748
|
+
await this._sendPromise;
|
|
2749
|
+
/**
|
|
2750
|
+
* Temporarily store multiple turns for cases like automatic function
|
|
2751
|
+
* calling, only writing them to official history when the entire
|
|
2752
|
+
* sequence has completed successfully.
|
|
2753
|
+
*/
|
|
2754
|
+
const tempHistory = [];
|
|
2755
|
+
this._sendPromise = this._sendPromise.then(async () => {
|
|
2756
|
+
let functionCalls;
|
|
2757
|
+
let functionCallTurnCount = 0;
|
|
2758
|
+
const functionCallMaxTurns = this.requestOptions?.maxSequentalFunctionCalls ??
|
|
2759
|
+
DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS;
|
|
2760
|
+
// Repeats until model returns a response with no function calls
|
|
2761
|
+
// or until `functionCallMaxTurns` is met or exceeded.
|
|
2762
|
+
do {
|
|
2763
|
+
let formattedContent;
|
|
2764
|
+
if (functionCalls) {
|
|
2765
|
+
functionCallTurnCount++;
|
|
2766
|
+
const functionResponseParts = await this._callFunctionsAsNeeded(functionCalls);
|
|
2767
|
+
formattedContent = formatNewContent(functionResponseParts);
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
formattedContent = formatNewContent(request);
|
|
2728
2771
|
}
|
|
2772
|
+
const formattedRequest = this._formatRequest(formattedContent, tempHistory);
|
|
2773
|
+
tempHistory.push(formattedContent);
|
|
2774
|
+
const result = await generateContent(this._apiSettings, this.model, formattedRequest, this.chromeAdapter, {
|
|
2775
|
+
...this.requestOptions,
|
|
2776
|
+
...singleRequestOptions
|
|
2777
|
+
});
|
|
2778
|
+
if (result) {
|
|
2779
|
+
finalResult = result;
|
|
2780
|
+
functionCalls = this._getCallableFunctionCalls(result.response);
|
|
2781
|
+
if (result.response.candidates &&
|
|
2782
|
+
result.response.candidates.length > 0) {
|
|
2783
|
+
// TODO: Make this update atomic. If creating `responseContent` throws,
|
|
2784
|
+
// history will contain the user message but not the response, causing
|
|
2785
|
+
// validation errors on the next request.
|
|
2786
|
+
const responseContent = {
|
|
2787
|
+
parts: result.response.candidates?.[0].content.parts || [],
|
|
2788
|
+
// Response seems to come back without a role set.
|
|
2789
|
+
role: result.response.candidates?.[0].content.role || 'model'
|
|
2790
|
+
};
|
|
2791
|
+
tempHistory.push(responseContent);
|
|
2792
|
+
}
|
|
2793
|
+
else {
|
|
2794
|
+
const blockErrorMessage = formatBlockErrorMessage(result.response);
|
|
2795
|
+
if (blockErrorMessage) {
|
|
2796
|
+
logger.warn(`sendMessage() was unsuccessful. ${blockErrorMessage}. Inspect response object for details.`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
functionCalls = undefined;
|
|
2802
|
+
}
|
|
2803
|
+
} while (functionCalls && functionCallTurnCount < functionCallMaxTurns);
|
|
2804
|
+
if (functionCalls && functionCallTurnCount >= functionCallMaxTurns) {
|
|
2805
|
+
logger.warn(`Automatic function calling exceeded the limit of` +
|
|
2806
|
+
` ${functionCallMaxTurns} function calls. Returning last model response.`);
|
|
2729
2807
|
}
|
|
2730
|
-
finalResult = result;
|
|
2731
2808
|
});
|
|
2732
2809
|
await this._sendPromise;
|
|
2810
|
+
this._history = this._history.concat(tempHistory);
|
|
2733
2811
|
return finalResult;
|
|
2734
2812
|
}
|
|
2735
2813
|
/**
|
|
@@ -2739,23 +2817,62 @@ class ChatSession {
|
|
|
2739
2817
|
*/
|
|
2740
2818
|
async sendMessageStream(request, singleRequestOptions) {
|
|
2741
2819
|
await this._sendPromise;
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2820
|
+
/**
|
|
2821
|
+
* Temporarily store multiple turns for cases like automatic function
|
|
2822
|
+
* calling, only writing them to official history when the entire
|
|
2823
|
+
* sequence has completed successfully.
|
|
2824
|
+
*/
|
|
2825
|
+
const tempHistory = [];
|
|
2826
|
+
const callGenerateContentStream = async () => {
|
|
2827
|
+
let functionCalls;
|
|
2828
|
+
let functionCallTurnCount = 0;
|
|
2829
|
+
const functionCallMaxTurns = this.requestOptions?.maxSequentalFunctionCalls ??
|
|
2830
|
+
DEFAULT_MAX_SEQUENTIAL_FUNCTION_CALLS;
|
|
2831
|
+
let result;
|
|
2832
|
+
// Repeats until model returns a response with no function calls
|
|
2833
|
+
// or until `functionCallMaxTurns` is met or exceeded.
|
|
2834
|
+
do {
|
|
2835
|
+
let formattedContent;
|
|
2836
|
+
if (functionCalls) {
|
|
2837
|
+
functionCallTurnCount++;
|
|
2838
|
+
const functionResponseParts = await this._callFunctionsAsNeeded(functionCalls);
|
|
2839
|
+
formattedContent = formatNewContent(functionResponseParts);
|
|
2840
|
+
}
|
|
2841
|
+
else {
|
|
2842
|
+
formattedContent = formatNewContent(request);
|
|
2843
|
+
}
|
|
2844
|
+
tempHistory.push(formattedContent);
|
|
2845
|
+
const formattedRequest = this._formatRequest(formattedContent, tempHistory);
|
|
2846
|
+
result = await generateContentStream(this._apiSettings, this.model, formattedRequest, this.chromeAdapter, {
|
|
2847
|
+
...this.requestOptions,
|
|
2848
|
+
...singleRequestOptions
|
|
2849
|
+
});
|
|
2850
|
+
functionCalls = this._getCallableFunctionCalls(result.firstValue);
|
|
2851
|
+
if (functionCalls &&
|
|
2852
|
+
result.firstValue &&
|
|
2853
|
+
result.firstValue.candidates &&
|
|
2854
|
+
result.firstValue.candidates.length > 0) {
|
|
2855
|
+
const responseContent = {
|
|
2856
|
+
...result.firstValue.candidates[0].content
|
|
2857
|
+
};
|
|
2858
|
+
if (!responseContent.role) {
|
|
2859
|
+
responseContent.role = 'model';
|
|
2860
|
+
}
|
|
2861
|
+
tempHistory.push(responseContent);
|
|
2862
|
+
}
|
|
2863
|
+
} while (functionCalls && functionCallTurnCount < functionCallMaxTurns);
|
|
2864
|
+
if (functionCalls && functionCallTurnCount >= functionCallMaxTurns) {
|
|
2865
|
+
logger.warn(`Automatic function calling exceeded the limit of` +
|
|
2866
|
+
` ${functionCallMaxTurns} function calls. Returning last model response.`);
|
|
2867
|
+
}
|
|
2868
|
+
return { stream: result.stream, response: result.response };
|
|
2750
2869
|
};
|
|
2751
|
-
const streamPromise =
|
|
2752
|
-
|
|
2753
|
-
...singleRequestOptions
|
|
2754
|
-
});
|
|
2755
|
-
// We hook into the chain to update history, but we don't block the
|
|
2756
|
-
// return of `streamPromise` to the user.
|
|
2870
|
+
const streamPromise = callGenerateContentStream();
|
|
2871
|
+
// Add onto the chain.
|
|
2757
2872
|
this._sendPromise = this._sendPromise
|
|
2758
|
-
.then(() => streamPromise)
|
|
2873
|
+
.then(async () => streamPromise)
|
|
2874
|
+
// This must be handled to avoid unhandled rejection, but jump
|
|
2875
|
+
// to the final catch block with a label to not log this error.
|
|
2759
2876
|
.catch(_ignored => {
|
|
2760
2877
|
// If the initial fetch fails, the user's `streamPromise` rejects.
|
|
2761
2878
|
// We swallow the error here to prevent double logging in the final catch.
|
|
@@ -2768,7 +2885,7 @@ class ChatSession {
|
|
|
2768
2885
|
// TODO: Move response validation logic upstream to `stream-reader` so
|
|
2769
2886
|
// errors propagate to the user's `result.response` promise.
|
|
2770
2887
|
if (response.candidates && response.candidates.length > 0) {
|
|
2771
|
-
this._history.
|
|
2888
|
+
this._history = this._history.concat(tempHistory);
|
|
2772
2889
|
// TODO: Validate that `response.candidates[0].content` is not null.
|
|
2773
2890
|
const responseContent = { ...response.candidates[0].content };
|
|
2774
2891
|
if (!responseContent.role) {
|
|
@@ -2791,6 +2908,75 @@ class ChatSession {
|
|
|
2791
2908
|
});
|
|
2792
2909
|
return streamPromise;
|
|
2793
2910
|
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Get function calls that the SDK has references to actually call.
|
|
2913
|
+
* This is all-or-nothing. If the model is requesting multiple
|
|
2914
|
+
* function calls, all of them must have references in order for
|
|
2915
|
+
* automatic function calling to work.
|
|
2916
|
+
*
|
|
2917
|
+
* @internal
|
|
2918
|
+
*/
|
|
2919
|
+
_getCallableFunctionCalls(response) {
|
|
2920
|
+
const functionDeclarationsTool = this.params?.tools?.find(tool => tool.functionDeclarations);
|
|
2921
|
+
if (!functionDeclarationsTool?.functionDeclarations) {
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const functionCalls = getFunctionCalls(response);
|
|
2925
|
+
if (!functionCalls) {
|
|
2926
|
+
return;
|
|
2927
|
+
}
|
|
2928
|
+
for (const functionCall of functionCalls) {
|
|
2929
|
+
const hasFunctionReference = functionDeclarationsTool.functionDeclarations?.some(declaration => declaration.name === functionCall.name &&
|
|
2930
|
+
typeof declaration.functionReference === 'function');
|
|
2931
|
+
if (!hasFunctionReference) {
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
return functionCalls;
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Call user-defined functions if requested by the model, and return
|
|
2939
|
+
* the response that should be sent to the model.
|
|
2940
|
+
* @internal
|
|
2941
|
+
*/
|
|
2942
|
+
async _callFunctionsAsNeeded(functionCalls) {
|
|
2943
|
+
const activeCallList = new Map();
|
|
2944
|
+
const promiseList = [];
|
|
2945
|
+
const functionDeclarationsTool = this.params?.tools?.find(tool => tool.functionDeclarations);
|
|
2946
|
+
if (functionDeclarationsTool &&
|
|
2947
|
+
functionDeclarationsTool.functionDeclarations) {
|
|
2948
|
+
for (const functionCall of functionCalls) {
|
|
2949
|
+
const functionDeclaration = functionDeclarationsTool.functionDeclarations.find(declaration => declaration.name === functionCall.name);
|
|
2950
|
+
if (functionDeclaration?.functionReference) {
|
|
2951
|
+
const results = Promise.resolve(functionDeclaration.functionReference(functionCall.args)).catch(e => {
|
|
2952
|
+
const wrappedError = new AIError(AIErrorCode.ERROR, `Error in user-defined function "${functionDeclaration.name}": ${e.message}`);
|
|
2953
|
+
wrappedError.stack = e.stack;
|
|
2954
|
+
throw wrappedError;
|
|
2955
|
+
});
|
|
2956
|
+
activeCallList.set(functionCall.name, {
|
|
2957
|
+
id: functionCall.id,
|
|
2958
|
+
results
|
|
2959
|
+
});
|
|
2960
|
+
promiseList.push(results);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
// Wait for promises to finish.
|
|
2964
|
+
await Promise.all(promiseList);
|
|
2965
|
+
const functionResponseParts = [];
|
|
2966
|
+
for (const [name, callData] of activeCallList) {
|
|
2967
|
+
functionResponseParts.push({
|
|
2968
|
+
functionResponse: {
|
|
2969
|
+
name,
|
|
2970
|
+
response: await callData.results
|
|
2971
|
+
}
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
return functionResponseParts;
|
|
2975
|
+
}
|
|
2976
|
+
else {
|
|
2977
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, `No function declarations were provided in "tools".`);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2794
2980
|
}
|
|
2795
2981
|
|
|
2796
2982
|
/**
|
|
@@ -2894,7 +3080,7 @@ class GenerativeModel extends AIModel {
|
|
|
2894
3080
|
*/
|
|
2895
3081
|
async generateContentStream(request, singleRequestOptions) {
|
|
2896
3082
|
const formattedParams = formatGenerateContentInput(request);
|
|
2897
|
-
|
|
3083
|
+
const { stream, response } = await generateContentStream(this._apiSettings, this.model, {
|
|
2898
3084
|
generationConfig: this.generationConfig,
|
|
2899
3085
|
safetySettings: this.safetySettings,
|
|
2900
3086
|
tools: this.tools,
|
|
@@ -2907,6 +3093,7 @@ class GenerativeModel extends AIModel {
|
|
|
2907
3093
|
...this.requestOptions,
|
|
2908
3094
|
...singleRequestOptions
|
|
2909
3095
|
});
|
|
3096
|
+
return { stream, response };
|
|
2910
3097
|
}
|
|
2911
3098
|
/**
|
|
2912
3099
|
* Gets a new {@link ChatSession} instance which can be used for
|