@langchain/google-common 0.0.27 → 0.1.0

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.
@@ -11,73 +11,80 @@ const extractMimeType = (str) => {
11
11
  }
12
12
  return null;
13
13
  };
14
- function messageContentText(content) {
15
- if (content?.text && content?.text.length > 0) {
16
- return {
17
- text: content.text,
18
- };
19
- }
20
- else {
21
- return null;
14
+ export function getGeminiAPI(config) {
15
+ function messageContentText(content) {
16
+ if (content?.text && content?.text.length > 0) {
17
+ return {
18
+ text: content.text,
19
+ };
20
+ }
21
+ else {
22
+ return null;
23
+ }
22
24
  }
23
- }
24
- function messageContentImageUrl(content) {
25
- const url = typeof content.image_url === "string"
26
- ? content.image_url
27
- : content.image_url.url;
28
- if (!url) {
29
- throw new Error("Missing Image URL");
30
- }
31
- const mineTypeAndData = extractMimeType(url);
32
- if (mineTypeAndData) {
33
- return {
34
- inlineData: mineTypeAndData,
35
- };
25
+ function messageContentImageUrl(content) {
26
+ const url = typeof content.image_url === "string"
27
+ ? content.image_url
28
+ : content.image_url.url;
29
+ if (!url) {
30
+ throw new Error("Missing Image URL");
31
+ }
32
+ const mineTypeAndData = extractMimeType(url);
33
+ if (mineTypeAndData) {
34
+ return {
35
+ inlineData: mineTypeAndData,
36
+ };
37
+ }
38
+ else {
39
+ // FIXME - need some way to get mime type
40
+ return {
41
+ fileData: {
42
+ mimeType: "image/png",
43
+ fileUri: url,
44
+ },
45
+ };
46
+ }
36
47
  }
37
- else {
38
- // FIXME - need some way to get mime type
48
+ async function blobToFileData(blob) {
39
49
  return {
40
50
  fileData: {
41
- mimeType: "image/png",
42
- fileUri: url,
43
- },
44
- };
45
- }
46
- }
47
- function messageContentMedia(
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- content) {
50
- if ("mimeType" in content && "data" in content) {
51
- return {
52
- inlineData: {
53
- mimeType: content.mimeType,
54
- data: content.data,
51
+ fileUri: blob.path,
52
+ mimeType: blob.mimetype,
55
53
  },
56
54
  };
57
55
  }
58
- else if ("mimeType" in content && "fileUri" in content) {
59
- return {
60
- fileData: {
61
- mimeType: content.mimeType,
62
- fileUri: content.fileUri,
63
- },
64
- };
56
+ async function fileUriContentToBlob(uri) {
57
+ return config?.mediaManager?.getMediaBlob(uri);
58
+ }
59
+ async function messageContentMedia(
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ content) {
62
+ if ("mimeType" in content && "data" in content) {
63
+ return {
64
+ inlineData: {
65
+ mimeType: content.mimeType,
66
+ data: content.data,
67
+ },
68
+ };
69
+ }
70
+ else if ("mimeType" in content && "fileUri" in content) {
71
+ return {
72
+ fileData: {
73
+ mimeType: content.mimeType,
74
+ fileUri: content.fileUri,
75
+ },
76
+ };
77
+ }
78
+ else {
79
+ const uri = content.fileUri;
80
+ const blob = await fileUriContentToBlob(uri);
81
+ if (blob) {
82
+ return await blobToFileData(blob);
83
+ }
84
+ }
85
+ throw new Error("Invalid media content");
65
86
  }
66
- throw new Error("Invalid media content");
67
- }
68
- export function messageContentToParts(content) {
69
- // Convert a string to a text type MessageContent if needed
70
- const messageContent = typeof content === "string"
71
- ? [
72
- {
73
- type: "text",
74
- text: content,
75
- },
76
- ]
77
- : content;
78
- // eslint-disable-next-line array-callback-return
79
- const parts = messageContent
80
- .map((content) => {
87
+ async function messageContentComplexToPart(content) {
81
88
  switch (content.type) {
82
89
  case "text":
83
90
  if ("text" in content) {
@@ -91,464 +98,481 @@ export function messageContentToParts(content) {
91
98
  }
92
99
  break;
93
100
  case "media":
94
- return messageContentMedia(content);
101
+ return await messageContentMedia(content);
95
102
  default:
96
103
  throw new Error(`Unsupported type received while converting message to message parts`);
97
104
  }
98
105
  throw new Error(`Cannot coerce "${content.type}" message part into a string.`);
99
- })
100
- .reduce((acc, val) => {
101
- if (val) {
102
- return [...acc, val];
106
+ }
107
+ async function messageContentComplexToParts(content) {
108
+ const contents = content.map(messageContentComplexToPart);
109
+ return Promise.all(contents);
110
+ }
111
+ async function messageContentToParts(content) {
112
+ // Convert a string to a text type MessageContent if needed
113
+ const messageContent = typeof content === "string"
114
+ ? [
115
+ {
116
+ type: "text",
117
+ text: content,
118
+ },
119
+ ]
120
+ : content;
121
+ // Get all of the parts, even those that don't correctly resolve
122
+ const allParts = await messageContentComplexToParts(messageContent);
123
+ // Remove any invalid parts
124
+ const parts = allParts.reduce((acc, val) => {
125
+ if (val) {
126
+ return [...acc, val];
127
+ }
128
+ else {
129
+ return acc;
130
+ }
131
+ }, []);
132
+ return parts;
133
+ }
134
+ function messageToolCallsToParts(toolCalls) {
135
+ if (!toolCalls || toolCalls.length === 0) {
136
+ return [];
137
+ }
138
+ return toolCalls.map((tool) => {
139
+ let args = {};
140
+ if (tool?.function?.arguments) {
141
+ const argStr = tool.function.arguments;
142
+ args = JSON.parse(argStr);
143
+ }
144
+ return {
145
+ functionCall: {
146
+ name: tool.function.name,
147
+ args,
148
+ },
149
+ };
150
+ });
151
+ }
152
+ function messageKwargsToParts(kwargs) {
153
+ const ret = [];
154
+ if (kwargs?.tool_calls) {
155
+ ret.push(...messageToolCallsToParts(kwargs.tool_calls));
156
+ }
157
+ return ret;
158
+ }
159
+ async function roleMessageToContent(role, message) {
160
+ const contentParts = await messageContentToParts(message.content);
161
+ let toolParts;
162
+ if (isAIMessage(message) && !!message.tool_calls?.length) {
163
+ toolParts = message.tool_calls.map((toolCall) => ({
164
+ functionCall: {
165
+ name: toolCall.name,
166
+ args: toolCall.args,
167
+ },
168
+ }));
103
169
  }
104
170
  else {
105
- return acc;
171
+ toolParts = messageKwargsToParts(message.additional_kwargs);
172
+ }
173
+ const parts = [...contentParts, ...toolParts];
174
+ return [
175
+ {
176
+ role,
177
+ parts,
178
+ },
179
+ ];
180
+ }
181
+ async function systemMessageToContent(message, useSystemInstruction) {
182
+ return useSystemInstruction
183
+ ? roleMessageToContent("system", message)
184
+ : [
185
+ ...(await roleMessageToContent("user", message)),
186
+ ...(await roleMessageToContent("model", new AIMessage("Ok"))),
187
+ ];
188
+ }
189
+ function toolMessageToContent(message, prevMessage) {
190
+ const contentStr = typeof message.content === "string"
191
+ ? message.content
192
+ : message.content.reduce((acc, content) => {
193
+ if (content.type === "text") {
194
+ return acc + content.text;
195
+ }
196
+ else {
197
+ return acc;
198
+ }
199
+ }, "");
200
+ // Hacky :(
201
+ const responseName = (isAIMessage(prevMessage) && !!prevMessage.tool_calls?.length
202
+ ? prevMessage.tool_calls[0].name
203
+ : prevMessage.name) ?? message.tool_call_id;
204
+ try {
205
+ const content = JSON.parse(contentStr);
206
+ return [
207
+ {
208
+ role: "function",
209
+ parts: [
210
+ {
211
+ functionResponse: {
212
+ name: responseName,
213
+ response: { content },
214
+ },
215
+ },
216
+ ],
217
+ },
218
+ ];
219
+ }
220
+ catch (_) {
221
+ return [
222
+ {
223
+ role: "function",
224
+ parts: [
225
+ {
226
+ functionResponse: {
227
+ name: responseName,
228
+ response: { content: contentStr },
229
+ },
230
+ },
231
+ ],
232
+ },
233
+ ];
106
234
  }
107
- }, []);
108
- return parts;
109
- }
110
- function messageToolCallsToParts(toolCalls) {
111
- if (!toolCalls || toolCalls.length === 0) {
112
- return [];
113
235
  }
114
- return toolCalls.map((tool) => {
115
- let args = {};
116
- if (tool?.function?.arguments) {
117
- const argStr = tool.function.arguments;
118
- args = JSON.parse(argStr);
236
+ async function baseMessageToContent(message, prevMessage, useSystemInstruction) {
237
+ const type = message._getType();
238
+ switch (type) {
239
+ case "system":
240
+ return systemMessageToContent(message, useSystemInstruction);
241
+ case "human":
242
+ return roleMessageToContent("user", message);
243
+ case "ai":
244
+ return roleMessageToContent("model", message);
245
+ case "tool":
246
+ if (!prevMessage) {
247
+ throw new Error("Tool messages cannot be the first message passed to the model.");
248
+ }
249
+ return toolMessageToContent(message, prevMessage);
250
+ default:
251
+ console.log(`Unsupported message type: ${type}`);
252
+ return [];
119
253
  }
254
+ }
255
+ function textPartToMessageContent(part) {
120
256
  return {
121
- functionCall: {
122
- name: tool.function.name,
123
- args,
124
- },
257
+ type: "text",
258
+ text: part.text,
125
259
  };
126
- });
127
- }
128
- function messageKwargsToParts(kwargs) {
129
- const ret = [];
130
- if (kwargs?.tool_calls) {
131
- ret.push(...messageToolCallsToParts(kwargs.tool_calls));
132
260
  }
133
- return ret;
134
- }
135
- function roleMessageToContent(role, message) {
136
- const contentParts = messageContentToParts(message.content);
137
- let toolParts;
138
- if (isAIMessage(message) && !!message.tool_calls?.length) {
139
- toolParts = message.tool_calls.map((toolCall) => ({
140
- functionCall: {
141
- name: toolCall.name,
142
- args: toolCall.args,
143
- },
144
- }));
145
- }
146
- else {
147
- toolParts = messageKwargsToParts(message.additional_kwargs);
148
- }
149
- const parts = [...contentParts, ...toolParts];
150
- return [
151
- {
152
- role,
153
- parts,
154
- },
155
- ];
156
- }
157
- function systemMessageToContent(message, useSystemInstruction) {
158
- return useSystemInstruction
159
- ? roleMessageToContent("system", message)
160
- : [
161
- ...roleMessageToContent("user", message),
162
- ...roleMessageToContent("model", new AIMessage("Ok")),
163
- ];
164
- }
165
- function toolMessageToContent(message, prevMessage) {
166
- const contentStr = typeof message.content === "string"
167
- ? message.content
168
- : message.content.reduce((acc, content) => {
169
- if (content.type === "text") {
170
- return acc + content.text;
261
+ function inlineDataPartToMessageContent(part) {
262
+ return {
263
+ type: "image_url",
264
+ image_url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`,
265
+ };
266
+ }
267
+ function fileDataPartToMessageContent(part) {
268
+ return {
269
+ type: "image_url",
270
+ image_url: part.fileData.fileUri,
271
+ };
272
+ }
273
+ function partsToMessageContent(parts) {
274
+ return parts
275
+ .map((part) => {
276
+ if (part === undefined || part === null) {
277
+ return null;
278
+ }
279
+ else if ("text" in part) {
280
+ return textPartToMessageContent(part);
281
+ }
282
+ else if ("inlineData" in part) {
283
+ return inlineDataPartToMessageContent(part);
284
+ }
285
+ else if ("fileData" in part) {
286
+ return fileDataPartToMessageContent(part);
171
287
  }
172
288
  else {
173
- return acc;
289
+ return null;
174
290
  }
175
- }, "");
176
- // Hacky :(
177
- const responseName = (isAIMessage(prevMessage) && !!prevMessage.tool_calls?.length
178
- ? prevMessage.tool_calls[0].name
179
- : prevMessage.name) ?? message.tool_call_id;
180
- try {
181
- const content = JSON.parse(contentStr);
182
- return [
183
- {
184
- role: "function",
185
- parts: [
186
- {
187
- functionResponse: {
188
- name: responseName,
189
- response: { content },
190
- },
191
- },
192
- ],
291
+ })
292
+ .reduce((acc, content) => {
293
+ if (content) {
294
+ acc.push(content);
295
+ }
296
+ return acc;
297
+ }, []);
298
+ }
299
+ function toolRawToTool(raw) {
300
+ return {
301
+ id: raw.id,
302
+ type: raw.type,
303
+ function: {
304
+ name: raw.function.name,
305
+ arguments: JSON.stringify(raw.function.arguments),
193
306
  },
194
- ];
307
+ };
195
308
  }
196
- catch (_) {
197
- return [
198
- {
199
- role: "function",
200
- parts: [
201
- {
202
- functionResponse: {
203
- name: responseName,
204
- response: { content: contentStr },
205
- },
206
- },
207
- ],
309
+ function functionCallPartToToolRaw(part) {
310
+ return {
311
+ id: uuidv4().replace(/-/g, ""),
312
+ type: "function",
313
+ function: {
314
+ name: part.functionCall.name,
315
+ arguments: part.functionCall.args ?? {},
208
316
  },
209
- ];
317
+ };
210
318
  }
211
- }
212
- export function baseMessageToContent(message, prevMessage, useSystemInstruction) {
213
- const type = message._getType();
214
- switch (type) {
215
- case "system":
216
- return systemMessageToContent(message, useSystemInstruction);
217
- case "human":
218
- return roleMessageToContent("user", message);
219
- case "ai":
220
- return roleMessageToContent("model", message);
221
- case "tool":
222
- if (!prevMessage) {
223
- throw new Error("Tool messages cannot be the first message passed to the model.");
319
+ function partsToToolsRaw(parts) {
320
+ return parts
321
+ .map((part) => {
322
+ if (part === undefined || part === null) {
323
+ return null;
224
324
  }
225
- return toolMessageToContent(message, prevMessage);
226
- default:
227
- console.log(`Unsupported message type: ${type}`);
228
- return [];
325
+ else if ("functionCall" in part) {
326
+ return functionCallPartToToolRaw(part);
327
+ }
328
+ else {
329
+ return null;
330
+ }
331
+ })
332
+ .reduce((acc, content) => {
333
+ if (content) {
334
+ acc.push(content);
335
+ }
336
+ return acc;
337
+ }, []);
229
338
  }
230
- }
231
- function textPartToMessageContent(part) {
232
- return {
233
- type: "text",
234
- text: part.text,
235
- };
236
- }
237
- function inlineDataPartToMessageContent(part) {
238
- return {
239
- type: "image_url",
240
- image_url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`,
241
- };
242
- }
243
- function fileDataPartToMessageContent(part) {
244
- return {
245
- type: "image_url",
246
- image_url: part.fileData.fileUri,
247
- };
248
- }
249
- export function partsToMessageContent(parts) {
250
- return parts
251
- .map((part) => {
252
- if (part === undefined || part === null) {
253
- return null;
254
- }
255
- else if ("text" in part) {
256
- return textPartToMessageContent(part);
257
- }
258
- else if ("inlineData" in part) {
259
- return inlineDataPartToMessageContent(part);
260
- }
261
- else if ("fileData" in part) {
262
- return fileDataPartToMessageContent(part);
263
- }
264
- else {
265
- return null;
266
- }
267
- })
268
- .reduce((acc, content) => {
269
- if (content) {
270
- acc.push(content);
271
- }
272
- return acc;
273
- }, []);
274
- }
275
- function toolRawToTool(raw) {
276
- return {
277
- id: raw.id,
278
- type: raw.type,
279
- function: {
280
- name: raw.function.name,
281
- arguments: JSON.stringify(raw.function.arguments),
282
- },
283
- };
284
- }
285
- function functionCallPartToToolRaw(part) {
286
- return {
287
- id: uuidv4().replace(/-/g, ""),
288
- type: "function",
289
- function: {
290
- name: part.functionCall.name,
291
- arguments: part.functionCall.args ?? {},
292
- },
293
- };
294
- }
295
- export function partsToToolsRaw(parts) {
296
- return parts
297
- .map((part) => {
298
- if (part === undefined || part === null) {
299
- return null;
339
+ function toolsRawToTools(raws) {
340
+ return raws.map((raw) => toolRawToTool(raw));
341
+ }
342
+ function responseToGenerateContentResponseData(response) {
343
+ if ("nextChunk" in response.data) {
344
+ throw new Error("Cannot convert Stream to GenerateContentResponseData");
300
345
  }
301
- else if ("functionCall" in part) {
302
- return functionCallPartToToolRaw(part);
346
+ else if (Array.isArray(response.data)) {
347
+ // Collapse the array of response data as if it was a single one
348
+ return response.data.reduce((acc, val) => {
349
+ // Add all the parts
350
+ // FIXME: Handle other candidates?
351
+ const valParts = val?.candidates?.[0]?.content?.parts ?? [];
352
+ acc.candidates[0].content.parts.push(...valParts);
353
+ // FIXME: Merge promptFeedback and safety settings
354
+ acc.promptFeedback = val.promptFeedback;
355
+ return acc;
356
+ });
303
357
  }
304
358
  else {
305
- return null;
306
- }
307
- })
308
- .reduce((acc, content) => {
309
- if (content) {
310
- acc.push(content);
359
+ return response.data;
311
360
  }
312
- return acc;
313
- }, []);
314
- }
315
- export function toolsRawToTools(raws) {
316
- return raws.map((raw) => toolRawToTool(raw));
317
- }
318
- export function responseToGenerateContentResponseData(response) {
319
- if ("nextChunk" in response.data) {
320
- throw new Error("Cannot convert Stream to GenerateContentResponseData");
321
- }
322
- else if (Array.isArray(response.data)) {
323
- // Collapse the array of response data as if it was a single one
324
- return response.data.reduce((acc, val) => {
325
- // Add all the parts
326
- // FIXME: Handle other candidates?
327
- const valParts = val?.candidates?.[0]?.content?.parts ?? [];
328
- acc.candidates[0].content.parts.push(...valParts);
329
- // FIXME: Merge promptFeedback and safety settings
330
- acc.promptFeedback = val.promptFeedback;
331
- return acc;
332
- });
333
361
  }
334
- else {
335
- return response.data;
362
+ function responseToParts(response) {
363
+ const responseData = responseToGenerateContentResponseData(response);
364
+ const parts = responseData?.candidates?.[0]?.content?.parts ?? [];
365
+ return parts;
336
366
  }
337
- }
338
- export function responseToParts(response) {
339
- const responseData = responseToGenerateContentResponseData(response);
340
- const parts = responseData?.candidates?.[0]?.content?.parts ?? [];
341
- return parts;
342
- }
343
- export function partToText(part) {
344
- return "text" in part ? part.text : "";
345
- }
346
- export function responseToString(response) {
347
- const parts = responseToParts(response);
348
- const ret = parts.reduce((acc, part) => {
349
- const val = partToText(part);
350
- return acc + val;
351
- }, "");
352
- return ret;
353
- }
354
- function safeResponseTo(response, safetyHandler, responseTo) {
355
- try {
356
- const safeResponse = safetyHandler.handle(response);
357
- return responseTo(safeResponse);
367
+ function partToText(part) {
368
+ return "text" in part ? part.text : "";
358
369
  }
359
- catch (xx) {
360
- // eslint-disable-next-line no-instanceof/no-instanceof
361
- if (xx instanceof GoogleAISafetyError) {
362
- const ret = responseTo(xx.response);
363
- xx.reply = ret;
370
+ function responseToString(response) {
371
+ const parts = responseToParts(response);
372
+ const ret = parts.reduce((acc, part) => {
373
+ const val = partToText(part);
374
+ return acc + val;
375
+ }, "");
376
+ return ret;
377
+ }
378
+ function safeResponseTo(response, safetyHandler, responseTo) {
379
+ try {
380
+ const safeResponse = safetyHandler.handle(response);
381
+ return responseTo(safeResponse);
382
+ }
383
+ catch (xx) {
384
+ // eslint-disable-next-line no-instanceof/no-instanceof
385
+ if (xx instanceof GoogleAISafetyError) {
386
+ const ret = responseTo(xx.response);
387
+ xx.reply = ret;
388
+ }
389
+ throw xx;
364
390
  }
365
- throw xx;
366
391
  }
367
- }
368
- export function safeResponseToString(response, safetyHandler) {
369
- return safeResponseTo(response, safetyHandler, responseToString);
370
- }
371
- export function responseToGenerationInfo(response) {
372
- if (!Array.isArray(response.data)) {
373
- return {};
392
+ function safeResponseToString(response, safetyHandler) {
393
+ return safeResponseTo(response, safetyHandler, responseToString);
374
394
  }
375
- const data = response.data[0];
376
- return {
377
- usage_metadata: {
378
- prompt_token_count: data.usageMetadata?.promptTokenCount,
379
- candidates_token_count: data.usageMetadata?.candidatesTokenCount,
380
- total_token_count: data.usageMetadata?.totalTokenCount,
381
- },
382
- safety_ratings: data.candidates[0]?.safetyRatings?.map((rating) => ({
383
- category: rating.category,
384
- probability: rating.probability,
385
- probability_score: rating.probabilityScore,
386
- severity: rating.severity,
387
- severity_score: rating.severityScore,
388
- })),
389
- finish_reason: data.candidates[0]?.finishReason,
390
- };
391
- }
392
- export function responseToGeneration(response) {
393
- return {
394
- text: responseToString(response),
395
- generationInfo: responseToGenerationInfo(response),
396
- };
397
- }
398
- export function safeResponseToGeneration(response, safetyHandler) {
399
- return safeResponseTo(response, safetyHandler, responseToGeneration);
400
- }
401
- export function responseToChatGeneration(response) {
402
- return new ChatGenerationChunk({
403
- text: responseToString(response),
404
- message: partToMessageChunk(responseToParts(response)[0]),
405
- generationInfo: responseToGenerationInfo(response),
406
- });
407
- }
408
- export function safeResponseToChatGeneration(response, safetyHandler) {
409
- return safeResponseTo(response, safetyHandler, responseToChatGeneration);
410
- }
411
- export function chunkToString(chunk) {
412
- if (chunk === null) {
413
- return "";
414
- }
415
- else if (typeof chunk.content === "string") {
416
- return chunk.content;
395
+ function responseToGenerationInfo(response) {
396
+ if (!Array.isArray(response.data)) {
397
+ return {};
398
+ }
399
+ const data = response.data[0];
400
+ return {
401
+ usage_metadata: {
402
+ prompt_token_count: data.usageMetadata?.promptTokenCount,
403
+ candidates_token_count: data.usageMetadata?.candidatesTokenCount,
404
+ total_token_count: data.usageMetadata?.totalTokenCount,
405
+ },
406
+ safety_ratings: data.candidates[0]?.safetyRatings?.map((rating) => ({
407
+ category: rating.category,
408
+ probability: rating.probability,
409
+ probability_score: rating.probabilityScore,
410
+ severity: rating.severity,
411
+ severity_score: rating.severityScore,
412
+ })),
413
+ finish_reason: data.candidates[0]?.finishReason,
414
+ };
417
415
  }
418
- else if (chunk.content.length === 0) {
419
- return "";
416
+ function responseToChatGeneration(response) {
417
+ return new ChatGenerationChunk({
418
+ text: responseToString(response),
419
+ message: partToMessageChunk(responseToParts(response)[0]),
420
+ generationInfo: responseToGenerationInfo(response),
421
+ });
420
422
  }
421
- else if (chunk.content[0].type === "text") {
422
- return chunk.content[0].text;
423
+ function safeResponseToChatGeneration(response, safetyHandler) {
424
+ return safeResponseTo(response, safetyHandler, responseToChatGeneration);
423
425
  }
424
- else {
425
- throw new Error(`Unexpected chunk: ${chunk}`);
426
+ function chunkToString(chunk) {
427
+ if (chunk === null) {
428
+ return "";
429
+ }
430
+ else if (typeof chunk.content === "string") {
431
+ return chunk.content;
432
+ }
433
+ else if (chunk.content.length === 0) {
434
+ return "";
435
+ }
436
+ else if (chunk.content[0].type === "text") {
437
+ return chunk.content[0].text;
438
+ }
439
+ else {
440
+ throw new Error(`Unexpected chunk: ${chunk}`);
441
+ }
426
442
  }
427
- }
428
- export function partToMessageChunk(part) {
429
- const fields = partsToBaseMessageChunkFields([part]);
430
- if (typeof fields.content === "string") {
443
+ function partToMessageChunk(part) {
444
+ const fields = partsToBaseMessageChunkFields([part]);
445
+ if (typeof fields.content === "string") {
446
+ return new AIMessageChunk(fields);
447
+ }
448
+ else if (fields.content.every((item) => item.type === "text")) {
449
+ const newContent = fields.content
450
+ .map((item) => ("text" in item ? item.text : ""))
451
+ .join("");
452
+ return new AIMessageChunk({
453
+ ...fields,
454
+ content: newContent,
455
+ });
456
+ }
431
457
  return new AIMessageChunk(fields);
432
458
  }
433
- else if (fields.content.every((item) => item.type === "text")) {
434
- const newContent = fields.content
435
- .map((item) => ("text" in item ? item.text : ""))
436
- .join("");
437
- return new AIMessageChunk({
438
- ...fields,
439
- content: newContent,
459
+ function partToChatGeneration(part) {
460
+ const message = partToMessageChunk(part);
461
+ const text = partToText(part);
462
+ return new ChatGenerationChunk({
463
+ text,
464
+ message,
440
465
  });
441
466
  }
442
- return new AIMessageChunk(fields);
443
- }
444
- export function partToChatGeneration(part) {
445
- const message = partToMessageChunk(part);
446
- const text = partToText(part);
447
- return new ChatGenerationChunk({
448
- text,
449
- message,
450
- });
451
- }
452
- export function responseToChatGenerations(response) {
453
- const parts = responseToParts(response);
454
- let ret = parts.map((part) => partToChatGeneration(part));
455
- if (ret.every((item) => typeof item.message.content === "string")) {
456
- const combinedContent = ret.map((item) => item.message.content).join("");
457
- const combinedText = ret.map((item) => item.text).join("");
458
- const toolCallChunks = ret[ret.length - 1]?.message.additional_kwargs?.tool_calls?.map((toolCall, i) => ({
459
- name: toolCall.function.name,
460
- args: toolCall.function.arguments,
461
- id: toolCall.id,
462
- index: i,
463
- type: "tool_call_chunk",
464
- }));
465
- let usageMetadata;
466
- if ("usageMetadata" in response.data) {
467
- usageMetadata = {
468
- input_tokens: response.data.usageMetadata.promptTokenCount,
469
- output_tokens: response.data.usageMetadata
470
- .candidatesTokenCount,
471
- total_tokens: response.data.usageMetadata.totalTokenCount,
472
- };
473
- }
474
- ret = [
475
- new ChatGenerationChunk({
476
- message: new AIMessageChunk({
477
- content: combinedContent,
478
- additional_kwargs: ret[ret.length - 1]?.message.additional_kwargs,
479
- tool_call_chunks: toolCallChunks,
480
- usage_metadata: usageMetadata,
481
- }),
482
- text: combinedText,
483
- generationInfo: ret[ret.length - 1].generationInfo,
484
- }),
485
- ];
486
- }
487
- return ret;
488
- }
489
- export function responseToBaseMessageFields(response) {
490
- const parts = responseToParts(response);
491
- return partsToBaseMessageChunkFields(parts);
492
- }
493
- export function partsToBaseMessageChunkFields(parts) {
494
- const fields = {
495
- content: partsToMessageContent(parts),
496
- tool_call_chunks: [],
497
- tool_calls: [],
498
- invalid_tool_calls: [],
499
- };
500
- const rawTools = partsToToolsRaw(parts);
501
- if (rawTools.length > 0) {
502
- const tools = toolsRawToTools(rawTools);
503
- for (const tool of tools) {
504
- fields.tool_call_chunks?.push({
505
- name: tool.function.name,
506
- args: tool.function.arguments,
507
- id: tool.id,
467
+ function responseToChatGenerations(response) {
468
+ const parts = responseToParts(response);
469
+ let ret = parts.map((part) => partToChatGeneration(part));
470
+ if (ret.every((item) => typeof item.message.content === "string")) {
471
+ const combinedContent = ret.map((item) => item.message.content).join("");
472
+ const combinedText = ret.map((item) => item.text).join("");
473
+ const toolCallChunks = ret[ret.length - 1]?.message.additional_kwargs?.tool_calls?.map((toolCall, i) => ({
474
+ name: toolCall.function.name,
475
+ args: toolCall.function.arguments,
476
+ id: toolCall.id,
477
+ index: i,
508
478
  type: "tool_call_chunk",
509
- });
510
- try {
511
- fields.tool_calls?.push({
512
- name: tool.function.name,
513
- args: JSON.parse(tool.function.arguments),
514
- id: tool.id,
515
- type: "tool_call",
516
- });
517
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
479
+ }));
480
+ let usageMetadata;
481
+ if ("usageMetadata" in response.data) {
482
+ usageMetadata = {
483
+ input_tokens: response.data.usageMetadata.promptTokenCount,
484
+ output_tokens: response.data.usageMetadata
485
+ .candidatesTokenCount,
486
+ total_tokens: response.data.usageMetadata.totalTokenCount,
487
+ };
518
488
  }
519
- catch (e) {
520
- fields.invalid_tool_calls?.push({
489
+ ret = [
490
+ new ChatGenerationChunk({
491
+ message: new AIMessageChunk({
492
+ content: combinedContent,
493
+ additional_kwargs: ret[ret.length - 1]?.message.additional_kwargs,
494
+ tool_call_chunks: toolCallChunks,
495
+ usage_metadata: usageMetadata,
496
+ }),
497
+ text: combinedText,
498
+ generationInfo: ret[ret.length - 1].generationInfo,
499
+ }),
500
+ ];
501
+ }
502
+ return ret;
503
+ }
504
+ function responseToBaseMessageFields(response) {
505
+ const parts = responseToParts(response);
506
+ return partsToBaseMessageChunkFields(parts);
507
+ }
508
+ function partsToBaseMessageChunkFields(parts) {
509
+ const fields = {
510
+ content: partsToMessageContent(parts),
511
+ tool_call_chunks: [],
512
+ tool_calls: [],
513
+ invalid_tool_calls: [],
514
+ };
515
+ const rawTools = partsToToolsRaw(parts);
516
+ if (rawTools.length > 0) {
517
+ const tools = toolsRawToTools(rawTools);
518
+ for (const tool of tools) {
519
+ fields.tool_call_chunks?.push({
521
520
  name: tool.function.name,
522
521
  args: tool.function.arguments,
523
522
  id: tool.id,
524
- error: e.message,
525
- type: "invalid_tool_call",
523
+ type: "tool_call_chunk",
526
524
  });
525
+ try {
526
+ fields.tool_calls?.push({
527
+ name: tool.function.name,
528
+ args: JSON.parse(tool.function.arguments),
529
+ id: tool.id,
530
+ });
531
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
532
+ }
533
+ catch (e) {
534
+ fields.invalid_tool_calls?.push({
535
+ name: tool.function.name,
536
+ args: tool.function.arguments,
537
+ id: tool.id,
538
+ error: e.message,
539
+ type: "invalid_tool_call",
540
+ });
541
+ }
527
542
  }
543
+ fields.additional_kwargs = {
544
+ tool_calls: tools,
545
+ };
528
546
  }
529
- fields.additional_kwargs = {
530
- tool_calls: tools,
547
+ return fields;
548
+ }
549
+ function responseToBaseMessage(response) {
550
+ const fields = responseToBaseMessageFields(response);
551
+ return new AIMessage(fields);
552
+ }
553
+ function safeResponseToBaseMessage(response, safetyHandler) {
554
+ return safeResponseTo(response, safetyHandler, responseToBaseMessage);
555
+ }
556
+ function responseToChatResult(response) {
557
+ const generations = responseToChatGenerations(response);
558
+ return {
559
+ generations,
560
+ llmOutput: responseToGenerationInfo(response),
531
561
  };
532
562
  }
533
- return fields;
534
- }
535
- export function responseToBaseMessage(response) {
536
- const fields = responseToBaseMessageFields(response);
537
- return new AIMessage(fields);
538
- }
539
- export function safeResponseToBaseMessage(response, safetyHandler) {
540
- return safeResponseTo(response, safetyHandler, responseToBaseMessage);
541
- }
542
- export function responseToChatResult(response) {
543
- const generations = responseToChatGenerations(response);
563
+ function safeResponseToChatResult(response, safetyHandler) {
564
+ return safeResponseTo(response, safetyHandler, responseToChatResult);
565
+ }
544
566
  return {
545
- generations,
546
- llmOutput: responseToGenerationInfo(response),
567
+ messageContentToParts,
568
+ baseMessageToContent,
569
+ safeResponseToString,
570
+ safeResponseToChatGeneration,
571
+ chunkToString,
572
+ safeResponseToBaseMessage,
573
+ safeResponseToChatResult,
547
574
  };
548
575
  }
549
- export function safeResponseToChatResult(response, safetyHandler) {
550
- return safeResponseTo(response, safetyHandler, responseToChatResult);
551
- }
552
576
  export function validateGeminiParams(params) {
553
577
  if (params.maxOutputTokens && params.maxOutputTokens < 0) {
554
578
  throw new Error("`maxOutputTokens` must be a positive integer");