@langchain/google-common 0.0.0 → 0.0.1
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/chat_models.cjs +12 -3
- package/dist/chat_models.d.ts +3 -2
- package/dist/chat_models.js +13 -4
- package/dist/llms.cjs +89 -22
- package/dist/llms.d.ts +20 -11
- package/dist/llms.js +91 -24
- package/dist/types.d.ts +35 -9
- package/dist/utils/failed_handler.cjs +37 -0
- package/dist/utils/failed_handler.d.ts +3 -0
- package/dist/utils/failed_handler.js +32 -0
- package/dist/utils/gemini.cjs +177 -10
- package/dist/utils/gemini.d.ts +29 -1
- package/dist/utils/gemini.js +168 -9
- package/dist/utils/safety.cjs +23 -0
- package/dist/utils/safety.d.ts +6 -0
- package/dist/utils/safety.js +19 -0
- package/index.d.cts +1 -0
- package/package.json +12 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const STATUS_NO_RETRY = [
|
|
2
|
+
400,
|
|
3
|
+
401,
|
|
4
|
+
402,
|
|
5
|
+
403,
|
|
6
|
+
404,
|
|
7
|
+
405,
|
|
8
|
+
406,
|
|
9
|
+
407,
|
|
10
|
+
408,
|
|
11
|
+
409, // Conflict
|
|
12
|
+
];
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export function failedAttemptHandler(error) {
|
|
15
|
+
const status = error?.response?.status ?? 0;
|
|
16
|
+
if (status === 0) {
|
|
17
|
+
// What is this?
|
|
18
|
+
console.error("failedAttemptHandler", error);
|
|
19
|
+
}
|
|
20
|
+
// What errors shouldn't be retried?
|
|
21
|
+
if (STATUS_NO_RETRY.includes(+status)) {
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
export function ensureParams(params) {
|
|
27
|
+
const base = params ?? {};
|
|
28
|
+
return {
|
|
29
|
+
onFailedAttempt: failedAttemptHandler,
|
|
30
|
+
...base,
|
|
31
|
+
};
|
|
32
|
+
}
|
package/dist/utils/gemini.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isModelGemini = exports.validateGeminiParams = exports.responseToChatResult = exports.responseToBaseMessage = exports.responseToMessageContent = exports.responseToChatGenerations = exports.partToChatGeneration = exports.partToMessage = exports.responseToChatGeneration = exports.responseToGeneration = exports.responseToString = exports.partToText = exports.responseToParts = exports.responseToGenerateContentResponseData = exports.partsToMessageContent = exports.baseMessageToContent = exports.messageContentToParts = void 0;
|
|
3
|
+
exports.MessageGeminiSafetyHandler = exports.DefaultGeminiSafetyHandler = exports.isModelGemini = exports.validateGeminiParams = exports.safeResponseToChatResult = exports.responseToChatResult = exports.safeResponseToBaseMessage = exports.responseToBaseMessage = exports.responseToMessageContent = exports.responseToChatGenerations = exports.partToChatGeneration = exports.partToMessage = exports.chunkToString = exports.safeResponseToChatGeneration = exports.responseToChatGeneration = exports.safeResponseToGeneration = exports.responseToGeneration = exports.safeResponseToString = exports.responseToString = exports.partToText = exports.responseToParts = exports.responseToGenerateContentResponseData = exports.partsToMessageContent = exports.baseMessageToContent = exports.messageContentToParts = void 0;
|
|
4
4
|
const messages_1 = require("@langchain/core/messages");
|
|
5
5
|
const outputs_1 = require("@langchain/core/outputs");
|
|
6
|
+
const safety_js_1 = require("./safety.cjs");
|
|
6
7
|
function messageContentText(content) {
|
|
7
8
|
return {
|
|
8
9
|
text: content.text,
|
|
@@ -17,15 +18,19 @@ function messageContentImageUrl(content) {
|
|
|
17
18
|
}
|
|
18
19
|
if (url.startsWith("data:")) {
|
|
19
20
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
inlineData: {
|
|
22
|
+
mimeType: url.split(":")[1].split(";")[0],
|
|
23
|
+
data: url.split(",")[1],
|
|
24
|
+
},
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
27
|
else {
|
|
25
28
|
// FIXME - need some way to get mime type
|
|
26
29
|
return {
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
fileData: {
|
|
31
|
+
mimeType: "image/png",
|
|
32
|
+
fileUri: url,
|
|
33
|
+
},
|
|
29
34
|
};
|
|
30
35
|
}
|
|
31
36
|
}
|
|
@@ -90,25 +95,28 @@ function textPartToMessageContent(part) {
|
|
|
90
95
|
function inlineDataPartToMessageContent(part) {
|
|
91
96
|
return {
|
|
92
97
|
type: "image_url",
|
|
93
|
-
image_url: `data:${part.mimeType};base64,${part.data}`,
|
|
98
|
+
image_url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`,
|
|
94
99
|
};
|
|
95
100
|
}
|
|
96
101
|
function fileDataPartToMessageContent(part) {
|
|
97
102
|
return {
|
|
98
103
|
type: "image_url",
|
|
99
|
-
image_url: part.fileUri,
|
|
104
|
+
image_url: part.fileData.fileUri,
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
function partsToMessageContent(parts) {
|
|
103
108
|
return parts
|
|
104
109
|
.map((part) => {
|
|
105
|
-
if (
|
|
110
|
+
if (part === undefined || part === null) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
else if ("text" in part) {
|
|
106
114
|
return textPartToMessageContent(part);
|
|
107
115
|
}
|
|
108
|
-
else if ("
|
|
116
|
+
else if ("inlineData" in part) {
|
|
109
117
|
return inlineDataPartToMessageContent(part);
|
|
110
118
|
}
|
|
111
|
-
else if ("
|
|
119
|
+
else if ("fileData" in part) {
|
|
112
120
|
return fileDataPartToMessageContent(part);
|
|
113
121
|
}
|
|
114
122
|
else {
|
|
@@ -163,6 +171,24 @@ function responseToString(response) {
|
|
|
163
171
|
return ret;
|
|
164
172
|
}
|
|
165
173
|
exports.responseToString = responseToString;
|
|
174
|
+
function safeResponseTo(response, safetyHandler, responseTo) {
|
|
175
|
+
try {
|
|
176
|
+
const safeResponse = safetyHandler.handle(response);
|
|
177
|
+
return responseTo(safeResponse);
|
|
178
|
+
}
|
|
179
|
+
catch (xx) {
|
|
180
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
181
|
+
if (xx instanceof safety_js_1.GoogleAISafetyError) {
|
|
182
|
+
const ret = responseTo(xx.response);
|
|
183
|
+
xx.reply = ret;
|
|
184
|
+
}
|
|
185
|
+
throw xx;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function safeResponseToString(response, safetyHandler) {
|
|
189
|
+
return safeResponseTo(response, safetyHandler, responseToString);
|
|
190
|
+
}
|
|
191
|
+
exports.safeResponseToString = safeResponseToString;
|
|
166
192
|
function responseToGeneration(response) {
|
|
167
193
|
return {
|
|
168
194
|
text: responseToString(response),
|
|
@@ -170,6 +196,10 @@ function responseToGeneration(response) {
|
|
|
170
196
|
};
|
|
171
197
|
}
|
|
172
198
|
exports.responseToGeneration = responseToGeneration;
|
|
199
|
+
function safeResponseToGeneration(response, safetyHandler) {
|
|
200
|
+
return safeResponseTo(response, safetyHandler, responseToGeneration);
|
|
201
|
+
}
|
|
202
|
+
exports.safeResponseToGeneration = safeResponseToGeneration;
|
|
173
203
|
function responseToChatGeneration(response) {
|
|
174
204
|
return new outputs_1.ChatGenerationChunk({
|
|
175
205
|
text: responseToString(response),
|
|
@@ -178,6 +208,28 @@ function responseToChatGeneration(response) {
|
|
|
178
208
|
});
|
|
179
209
|
}
|
|
180
210
|
exports.responseToChatGeneration = responseToChatGeneration;
|
|
211
|
+
function safeResponseToChatGeneration(response, safetyHandler) {
|
|
212
|
+
return safeResponseTo(response, safetyHandler, responseToChatGeneration);
|
|
213
|
+
}
|
|
214
|
+
exports.safeResponseToChatGeneration = safeResponseToChatGeneration;
|
|
215
|
+
function chunkToString(chunk) {
|
|
216
|
+
if (chunk === null) {
|
|
217
|
+
return "";
|
|
218
|
+
}
|
|
219
|
+
else if (typeof chunk.content === "string") {
|
|
220
|
+
return chunk.content;
|
|
221
|
+
}
|
|
222
|
+
else if (chunk.content.length === 0) {
|
|
223
|
+
return "";
|
|
224
|
+
}
|
|
225
|
+
else if (chunk.content[0].type === "text") {
|
|
226
|
+
return chunk.content[0].text;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
throw new Error(`Unexpected chunk: ${chunk}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.chunkToString = chunkToString;
|
|
181
233
|
function partToMessage(part) {
|
|
182
234
|
const content = partsToMessageContent([part]);
|
|
183
235
|
return new messages_1.AIMessageChunk({ content });
|
|
@@ -209,6 +261,10 @@ function responseToBaseMessage(response) {
|
|
|
209
261
|
});
|
|
210
262
|
}
|
|
211
263
|
exports.responseToBaseMessage = responseToBaseMessage;
|
|
264
|
+
function safeResponseToBaseMessage(response, safetyHandler) {
|
|
265
|
+
return safeResponseTo(response, safetyHandler, responseToBaseMessage);
|
|
266
|
+
}
|
|
267
|
+
exports.safeResponseToBaseMessage = safeResponseToBaseMessage;
|
|
212
268
|
function responseToChatResult(response) {
|
|
213
269
|
const generations = responseToChatGenerations(response);
|
|
214
270
|
return {
|
|
@@ -217,6 +273,10 @@ function responseToChatResult(response) {
|
|
|
217
273
|
};
|
|
218
274
|
}
|
|
219
275
|
exports.responseToChatResult = responseToChatResult;
|
|
276
|
+
function safeResponseToChatResult(response, safetyHandler) {
|
|
277
|
+
return safeResponseTo(response, safetyHandler, responseToChatResult);
|
|
278
|
+
}
|
|
279
|
+
exports.safeResponseToChatResult = safeResponseToChatResult;
|
|
220
280
|
function validateGeminiParams(params) {
|
|
221
281
|
if (params.maxOutputTokens && params.maxOutputTokens < 0) {
|
|
222
282
|
throw new Error("`maxOutputTokens` must be a positive integer");
|
|
@@ -237,3 +297,110 @@ function isModelGemini(modelName) {
|
|
|
237
297
|
return modelName.toLowerCase().startsWith("gemini");
|
|
238
298
|
}
|
|
239
299
|
exports.isModelGemini = isModelGemini;
|
|
300
|
+
class DefaultGeminiSafetyHandler {
|
|
301
|
+
constructor(settings) {
|
|
302
|
+
Object.defineProperty(this, "errorFinish", {
|
|
303
|
+
enumerable: true,
|
|
304
|
+
configurable: true,
|
|
305
|
+
writable: true,
|
|
306
|
+
value: ["SAFETY", "RECITATION", "OTHER"]
|
|
307
|
+
});
|
|
308
|
+
this.errorFinish = settings?.errorFinish ?? this.errorFinish;
|
|
309
|
+
}
|
|
310
|
+
handleDataPromptFeedback(response, data) {
|
|
311
|
+
// Check to see if our prompt was blocked in the first place
|
|
312
|
+
const promptFeedback = data?.promptFeedback;
|
|
313
|
+
const blockReason = promptFeedback?.blockReason;
|
|
314
|
+
if (blockReason) {
|
|
315
|
+
throw new safety_js_1.GoogleAISafetyError(response, `Prompt blocked: ${blockReason}`);
|
|
316
|
+
}
|
|
317
|
+
return data;
|
|
318
|
+
}
|
|
319
|
+
handleDataFinishReason(response, data) {
|
|
320
|
+
const firstCandidate = data?.candidates?.[0];
|
|
321
|
+
const finishReason = firstCandidate?.finishReason;
|
|
322
|
+
if (this.errorFinish.includes(finishReason)) {
|
|
323
|
+
throw new safety_js_1.GoogleAISafetyError(response, `Finish reason: ${finishReason}`);
|
|
324
|
+
}
|
|
325
|
+
return data;
|
|
326
|
+
}
|
|
327
|
+
handleData(response, data) {
|
|
328
|
+
let ret = data;
|
|
329
|
+
ret = this.handleDataPromptFeedback(response, ret);
|
|
330
|
+
ret = this.handleDataFinishReason(response, ret);
|
|
331
|
+
return ret;
|
|
332
|
+
}
|
|
333
|
+
handle(response) {
|
|
334
|
+
let newdata;
|
|
335
|
+
if ("nextChunk" in response.data) {
|
|
336
|
+
// TODO: This is a stream. How to handle?
|
|
337
|
+
newdata = response.data;
|
|
338
|
+
}
|
|
339
|
+
else if (Array.isArray(response.data)) {
|
|
340
|
+
// If it is an array, try to handle every item in the array
|
|
341
|
+
try {
|
|
342
|
+
newdata = response.data.map((item) => this.handleData(response, item));
|
|
343
|
+
}
|
|
344
|
+
catch (xx) {
|
|
345
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
346
|
+
if (xx instanceof safety_js_1.GoogleAISafetyError) {
|
|
347
|
+
throw new safety_js_1.GoogleAISafetyError(response, xx.message);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
throw xx;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
const data = response.data;
|
|
356
|
+
newdata = this.handleData(response, data);
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
...response,
|
|
360
|
+
data: newdata,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.DefaultGeminiSafetyHandler = DefaultGeminiSafetyHandler;
|
|
365
|
+
class MessageGeminiSafetyHandler extends DefaultGeminiSafetyHandler {
|
|
366
|
+
constructor(settings) {
|
|
367
|
+
super(settings);
|
|
368
|
+
Object.defineProperty(this, "msg", {
|
|
369
|
+
enumerable: true,
|
|
370
|
+
configurable: true,
|
|
371
|
+
writable: true,
|
|
372
|
+
value: ""
|
|
373
|
+
});
|
|
374
|
+
Object.defineProperty(this, "forceNewMessage", {
|
|
375
|
+
enumerable: true,
|
|
376
|
+
configurable: true,
|
|
377
|
+
writable: true,
|
|
378
|
+
value: false
|
|
379
|
+
});
|
|
380
|
+
this.msg = settings?.msg ?? this.msg;
|
|
381
|
+
this.forceNewMessage = settings?.forceNewMessage ?? this.forceNewMessage;
|
|
382
|
+
}
|
|
383
|
+
setMessage(data) {
|
|
384
|
+
const ret = data;
|
|
385
|
+
if (this.forceNewMessage ||
|
|
386
|
+
!data?.candidates?.[0]?.content?.parts?.length) {
|
|
387
|
+
ret.candidates = data.candidates ?? [];
|
|
388
|
+
ret.candidates[0] = data.candidates[0] ?? {};
|
|
389
|
+
ret.candidates[0].content = data.candidates[0].content ?? {};
|
|
390
|
+
ret.candidates[0].content = {
|
|
391
|
+
role: "model",
|
|
392
|
+
parts: [{ text: this.msg }],
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
return ret;
|
|
396
|
+
}
|
|
397
|
+
handleData(response, data) {
|
|
398
|
+
try {
|
|
399
|
+
return super.handleData(response, data);
|
|
400
|
+
}
|
|
401
|
+
catch (xx) {
|
|
402
|
+
return this.setMessage(data);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
exports.MessageGeminiSafetyHandler = MessageGeminiSafetyHandler;
|
package/dist/utils/gemini.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseMessage, BaseMessageChunk, MessageContent } from "@langchain/core/messages";
|
|
2
2
|
import { ChatGeneration, ChatGenerationChunk, ChatResult, Generation } from "@langchain/core/outputs";
|
|
3
|
-
import type { GoogleLLMResponse, GoogleAIModelParams, GeminiPart, GeminiContent, GenerateContentResponseData } from "../types.js";
|
|
3
|
+
import type { GoogleLLMResponse, GoogleAIModelParams, GeminiPart, GeminiContent, GenerateContentResponseData, GoogleAISafetyHandler } from "../types.js";
|
|
4
4
|
export declare function messageContentToParts(content: MessageContent): GeminiPart[];
|
|
5
5
|
export declare function baseMessageToContent(message: BaseMessage): GeminiContent[];
|
|
6
6
|
export declare function partsToMessageContent(parts: GeminiPart[]): MessageContent;
|
|
@@ -8,13 +8,41 @@ export declare function responseToGenerateContentResponseData(response: GoogleLL
|
|
|
8
8
|
export declare function responseToParts(response: GoogleLLMResponse): GeminiPart[];
|
|
9
9
|
export declare function partToText(part: GeminiPart): string;
|
|
10
10
|
export declare function responseToString(response: GoogleLLMResponse): string;
|
|
11
|
+
export declare function safeResponseToString(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): string;
|
|
11
12
|
export declare function responseToGeneration(response: GoogleLLMResponse): Generation;
|
|
13
|
+
export declare function safeResponseToGeneration(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): Generation;
|
|
12
14
|
export declare function responseToChatGeneration(response: GoogleLLMResponse): ChatGenerationChunk;
|
|
15
|
+
export declare function safeResponseToChatGeneration(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): ChatGenerationChunk;
|
|
16
|
+
export declare function chunkToString(chunk: BaseMessageChunk): string;
|
|
13
17
|
export declare function partToMessage(part: GeminiPart): BaseMessageChunk;
|
|
14
18
|
export declare function partToChatGeneration(part: GeminiPart): ChatGeneration;
|
|
15
19
|
export declare function responseToChatGenerations(response: GoogleLLMResponse): ChatGeneration[];
|
|
16
20
|
export declare function responseToMessageContent(response: GoogleLLMResponse): MessageContent;
|
|
17
21
|
export declare function responseToBaseMessage(response: GoogleLLMResponse): BaseMessage;
|
|
22
|
+
export declare function safeResponseToBaseMessage(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): BaseMessage;
|
|
18
23
|
export declare function responseToChatResult(response: GoogleLLMResponse): ChatResult;
|
|
24
|
+
export declare function safeResponseToChatResult(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): ChatResult;
|
|
19
25
|
export declare function validateGeminiParams(params: GoogleAIModelParams): void;
|
|
20
26
|
export declare function isModelGemini(modelName: string): boolean;
|
|
27
|
+
export interface DefaultGeminiSafetySettings {
|
|
28
|
+
errorFinish?: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare class DefaultGeminiSafetyHandler implements GoogleAISafetyHandler {
|
|
31
|
+
errorFinish: string[];
|
|
32
|
+
constructor(settings?: DefaultGeminiSafetySettings);
|
|
33
|
+
handleDataPromptFeedback(response: GoogleLLMResponse, data: GenerateContentResponseData): GenerateContentResponseData;
|
|
34
|
+
handleDataFinishReason(response: GoogleLLMResponse, data: GenerateContentResponseData): GenerateContentResponseData;
|
|
35
|
+
handleData(response: GoogleLLMResponse, data: GenerateContentResponseData): GenerateContentResponseData;
|
|
36
|
+
handle(response: GoogleLLMResponse): GoogleLLMResponse;
|
|
37
|
+
}
|
|
38
|
+
export interface MessageGeminiSafetySettings extends DefaultGeminiSafetySettings {
|
|
39
|
+
msg?: string;
|
|
40
|
+
forceNewMessage?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare class MessageGeminiSafetyHandler extends DefaultGeminiSafetyHandler {
|
|
43
|
+
msg: string;
|
|
44
|
+
forceNewMessage: boolean;
|
|
45
|
+
constructor(settings?: MessageGeminiSafetySettings);
|
|
46
|
+
setMessage(data: GenerateContentResponseData): GenerateContentResponseData;
|
|
47
|
+
handleData(response: GoogleLLMResponse, data: GenerateContentResponseData): GenerateContentResponseData;
|
|
48
|
+
}
|
package/dist/utils/gemini.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AIMessage, AIMessageChunk, } from "@langchain/core/messages";
|
|
2
2
|
import { ChatGenerationChunk, } from "@langchain/core/outputs";
|
|
3
|
+
import { GoogleAISafetyError } from "./safety.js";
|
|
3
4
|
function messageContentText(content) {
|
|
4
5
|
return {
|
|
5
6
|
text: content.text,
|
|
@@ -14,15 +15,19 @@ function messageContentImageUrl(content) {
|
|
|
14
15
|
}
|
|
15
16
|
if (url.startsWith("data:")) {
|
|
16
17
|
return {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
inlineData: {
|
|
19
|
+
mimeType: url.split(":")[1].split(";")[0],
|
|
20
|
+
data: url.split(",")[1],
|
|
21
|
+
},
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
24
|
else {
|
|
22
25
|
// FIXME - need some way to get mime type
|
|
23
26
|
return {
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
fileData: {
|
|
28
|
+
mimeType: "image/png",
|
|
29
|
+
fileUri: url,
|
|
30
|
+
},
|
|
26
31
|
};
|
|
27
32
|
}
|
|
28
33
|
}
|
|
@@ -85,25 +90,28 @@ function textPartToMessageContent(part) {
|
|
|
85
90
|
function inlineDataPartToMessageContent(part) {
|
|
86
91
|
return {
|
|
87
92
|
type: "image_url",
|
|
88
|
-
image_url: `data:${part.mimeType};base64,${part.data}`,
|
|
93
|
+
image_url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`,
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
function fileDataPartToMessageContent(part) {
|
|
92
97
|
return {
|
|
93
98
|
type: "image_url",
|
|
94
|
-
image_url: part.fileUri,
|
|
99
|
+
image_url: part.fileData.fileUri,
|
|
95
100
|
};
|
|
96
101
|
}
|
|
97
102
|
export function partsToMessageContent(parts) {
|
|
98
103
|
return parts
|
|
99
104
|
.map((part) => {
|
|
100
|
-
if (
|
|
105
|
+
if (part === undefined || part === null) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
else if ("text" in part) {
|
|
101
109
|
return textPartToMessageContent(part);
|
|
102
110
|
}
|
|
103
|
-
else if ("
|
|
111
|
+
else if ("inlineData" in part) {
|
|
104
112
|
return inlineDataPartToMessageContent(part);
|
|
105
113
|
}
|
|
106
|
-
else if ("
|
|
114
|
+
else if ("fileData" in part) {
|
|
107
115
|
return fileDataPartToMessageContent(part);
|
|
108
116
|
}
|
|
109
117
|
else {
|
|
@@ -153,12 +161,32 @@ export function responseToString(response) {
|
|
|
153
161
|
}, "");
|
|
154
162
|
return ret;
|
|
155
163
|
}
|
|
164
|
+
function safeResponseTo(response, safetyHandler, responseTo) {
|
|
165
|
+
try {
|
|
166
|
+
const safeResponse = safetyHandler.handle(response);
|
|
167
|
+
return responseTo(safeResponse);
|
|
168
|
+
}
|
|
169
|
+
catch (xx) {
|
|
170
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
171
|
+
if (xx instanceof GoogleAISafetyError) {
|
|
172
|
+
const ret = responseTo(xx.response);
|
|
173
|
+
xx.reply = ret;
|
|
174
|
+
}
|
|
175
|
+
throw xx;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export function safeResponseToString(response, safetyHandler) {
|
|
179
|
+
return safeResponseTo(response, safetyHandler, responseToString);
|
|
180
|
+
}
|
|
156
181
|
export function responseToGeneration(response) {
|
|
157
182
|
return {
|
|
158
183
|
text: responseToString(response),
|
|
159
184
|
generationInfo: response,
|
|
160
185
|
};
|
|
161
186
|
}
|
|
187
|
+
export function safeResponseToGeneration(response, safetyHandler) {
|
|
188
|
+
return safeResponseTo(response, safetyHandler, responseToGeneration);
|
|
189
|
+
}
|
|
162
190
|
export function responseToChatGeneration(response) {
|
|
163
191
|
return new ChatGenerationChunk({
|
|
164
192
|
text: responseToString(response),
|
|
@@ -166,6 +194,26 @@ export function responseToChatGeneration(response) {
|
|
|
166
194
|
generationInfo: response,
|
|
167
195
|
});
|
|
168
196
|
}
|
|
197
|
+
export function safeResponseToChatGeneration(response, safetyHandler) {
|
|
198
|
+
return safeResponseTo(response, safetyHandler, responseToChatGeneration);
|
|
199
|
+
}
|
|
200
|
+
export function chunkToString(chunk) {
|
|
201
|
+
if (chunk === null) {
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
else if (typeof chunk.content === "string") {
|
|
205
|
+
return chunk.content;
|
|
206
|
+
}
|
|
207
|
+
else if (chunk.content.length === 0) {
|
|
208
|
+
return "";
|
|
209
|
+
}
|
|
210
|
+
else if (chunk.content[0].type === "text") {
|
|
211
|
+
return chunk.content[0].text;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
throw new Error(`Unexpected chunk: ${chunk}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
169
217
|
export function partToMessage(part) {
|
|
170
218
|
const content = partsToMessageContent([part]);
|
|
171
219
|
return new AIMessageChunk({ content });
|
|
@@ -192,6 +240,9 @@ export function responseToBaseMessage(response) {
|
|
|
192
240
|
content: responseToMessageContent(response),
|
|
193
241
|
});
|
|
194
242
|
}
|
|
243
|
+
export function safeResponseToBaseMessage(response, safetyHandler) {
|
|
244
|
+
return safeResponseTo(response, safetyHandler, responseToBaseMessage);
|
|
245
|
+
}
|
|
195
246
|
export function responseToChatResult(response) {
|
|
196
247
|
const generations = responseToChatGenerations(response);
|
|
197
248
|
return {
|
|
@@ -199,6 +250,9 @@ export function responseToChatResult(response) {
|
|
|
199
250
|
llmOutput: response,
|
|
200
251
|
};
|
|
201
252
|
}
|
|
253
|
+
export function safeResponseToChatResult(response, safetyHandler) {
|
|
254
|
+
return safeResponseTo(response, safetyHandler, responseToChatResult);
|
|
255
|
+
}
|
|
202
256
|
export function validateGeminiParams(params) {
|
|
203
257
|
if (params.maxOutputTokens && params.maxOutputTokens < 0) {
|
|
204
258
|
throw new Error("`maxOutputTokens` must be a positive integer");
|
|
@@ -217,3 +271,108 @@ export function validateGeminiParams(params) {
|
|
|
217
271
|
export function isModelGemini(modelName) {
|
|
218
272
|
return modelName.toLowerCase().startsWith("gemini");
|
|
219
273
|
}
|
|
274
|
+
export class DefaultGeminiSafetyHandler {
|
|
275
|
+
constructor(settings) {
|
|
276
|
+
Object.defineProperty(this, "errorFinish", {
|
|
277
|
+
enumerable: true,
|
|
278
|
+
configurable: true,
|
|
279
|
+
writable: true,
|
|
280
|
+
value: ["SAFETY", "RECITATION", "OTHER"]
|
|
281
|
+
});
|
|
282
|
+
this.errorFinish = settings?.errorFinish ?? this.errorFinish;
|
|
283
|
+
}
|
|
284
|
+
handleDataPromptFeedback(response, data) {
|
|
285
|
+
// Check to see if our prompt was blocked in the first place
|
|
286
|
+
const promptFeedback = data?.promptFeedback;
|
|
287
|
+
const blockReason = promptFeedback?.blockReason;
|
|
288
|
+
if (blockReason) {
|
|
289
|
+
throw new GoogleAISafetyError(response, `Prompt blocked: ${blockReason}`);
|
|
290
|
+
}
|
|
291
|
+
return data;
|
|
292
|
+
}
|
|
293
|
+
handleDataFinishReason(response, data) {
|
|
294
|
+
const firstCandidate = data?.candidates?.[0];
|
|
295
|
+
const finishReason = firstCandidate?.finishReason;
|
|
296
|
+
if (this.errorFinish.includes(finishReason)) {
|
|
297
|
+
throw new GoogleAISafetyError(response, `Finish reason: ${finishReason}`);
|
|
298
|
+
}
|
|
299
|
+
return data;
|
|
300
|
+
}
|
|
301
|
+
handleData(response, data) {
|
|
302
|
+
let ret = data;
|
|
303
|
+
ret = this.handleDataPromptFeedback(response, ret);
|
|
304
|
+
ret = this.handleDataFinishReason(response, ret);
|
|
305
|
+
return ret;
|
|
306
|
+
}
|
|
307
|
+
handle(response) {
|
|
308
|
+
let newdata;
|
|
309
|
+
if ("nextChunk" in response.data) {
|
|
310
|
+
// TODO: This is a stream. How to handle?
|
|
311
|
+
newdata = response.data;
|
|
312
|
+
}
|
|
313
|
+
else if (Array.isArray(response.data)) {
|
|
314
|
+
// If it is an array, try to handle every item in the array
|
|
315
|
+
try {
|
|
316
|
+
newdata = response.data.map((item) => this.handleData(response, item));
|
|
317
|
+
}
|
|
318
|
+
catch (xx) {
|
|
319
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
320
|
+
if (xx instanceof GoogleAISafetyError) {
|
|
321
|
+
throw new GoogleAISafetyError(response, xx.message);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
throw xx;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
const data = response.data;
|
|
330
|
+
newdata = this.handleData(response, data);
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
...response,
|
|
334
|
+
data: newdata,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
export class MessageGeminiSafetyHandler extends DefaultGeminiSafetyHandler {
|
|
339
|
+
constructor(settings) {
|
|
340
|
+
super(settings);
|
|
341
|
+
Object.defineProperty(this, "msg", {
|
|
342
|
+
enumerable: true,
|
|
343
|
+
configurable: true,
|
|
344
|
+
writable: true,
|
|
345
|
+
value: ""
|
|
346
|
+
});
|
|
347
|
+
Object.defineProperty(this, "forceNewMessage", {
|
|
348
|
+
enumerable: true,
|
|
349
|
+
configurable: true,
|
|
350
|
+
writable: true,
|
|
351
|
+
value: false
|
|
352
|
+
});
|
|
353
|
+
this.msg = settings?.msg ?? this.msg;
|
|
354
|
+
this.forceNewMessage = settings?.forceNewMessage ?? this.forceNewMessage;
|
|
355
|
+
}
|
|
356
|
+
setMessage(data) {
|
|
357
|
+
const ret = data;
|
|
358
|
+
if (this.forceNewMessage ||
|
|
359
|
+
!data?.candidates?.[0]?.content?.parts?.length) {
|
|
360
|
+
ret.candidates = data.candidates ?? [];
|
|
361
|
+
ret.candidates[0] = data.candidates[0] ?? {};
|
|
362
|
+
ret.candidates[0].content = data.candidates[0].content ?? {};
|
|
363
|
+
ret.candidates[0].content = {
|
|
364
|
+
role: "model",
|
|
365
|
+
parts: [{ text: this.msg }],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
return ret;
|
|
369
|
+
}
|
|
370
|
+
handleData(response, data) {
|
|
371
|
+
try {
|
|
372
|
+
return super.handleData(response, data);
|
|
373
|
+
}
|
|
374
|
+
catch (xx) {
|
|
375
|
+
return this.setMessage(data);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GoogleAISafetyError = void 0;
|
|
4
|
+
class GoogleAISafetyError extends Error {
|
|
5
|
+
constructor(response, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
Object.defineProperty(this, "response", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true,
|
|
11
|
+
value: void 0
|
|
12
|
+
});
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
Object.defineProperty(this, "reply", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: ""
|
|
19
|
+
});
|
|
20
|
+
this.response = response;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.GoogleAISafetyError = GoogleAISafetyError;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class GoogleAISafetyError extends Error {
|
|
2
|
+
constructor(response, message) {
|
|
3
|
+
super(message);
|
|
4
|
+
Object.defineProperty(this, "response", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: void 0
|
|
9
|
+
});
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
Object.defineProperty(this, "reply", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: ""
|
|
16
|
+
});
|
|
17
|
+
this.response = response;
|
|
18
|
+
}
|
|
19
|
+
}
|
package/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/index.js'
|