koishi-plugin-chatluna-google-gemini-adapter 1.2.20 → 1.2.22
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/lib/index.cjs +129 -113
- package/lib/index.mjs +133 -110
- package/lib/utils.d.ts +3 -2
- package/package.json +3 -3
package/lib/index.cjs
CHANGED
|
@@ -65,97 +65,28 @@ var import_stream = require("koishi-plugin-chatluna/utils/stream");
|
|
|
65
65
|
// src/utils.ts
|
|
66
66
|
var import_messages = require("@langchain/core/messages");
|
|
67
67
|
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
functionResponse: rawMessage.additional_kwargs?.function_call != null ? void 0 : {
|
|
78
|
-
name: rawMessage.name,
|
|
79
|
-
response: {
|
|
80
|
-
name: rawMessage.name,
|
|
81
|
-
content: (() => {
|
|
82
|
-
try {
|
|
83
|
-
const result2 = JSON.parse(
|
|
84
|
-
rawMessage.content
|
|
85
|
-
);
|
|
86
|
-
if (typeof result2 === "string") {
|
|
87
|
-
return {
|
|
88
|
-
response: result2
|
|
89
|
-
};
|
|
90
|
-
} else {
|
|
91
|
-
return result2;
|
|
92
|
-
}
|
|
93
|
-
} catch (e) {
|
|
94
|
-
return {
|
|
95
|
-
response: rawMessage.content
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
})()
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
functionCall: rawMessage.additional_kwargs?.function_call != null ? {
|
|
102
|
-
name: rawMessage.additional_kwargs.function_call.name,
|
|
103
|
-
args: (() => {
|
|
104
|
-
try {
|
|
105
|
-
const result2 = JSON.parse(
|
|
106
|
-
rawMessage.additional_kwargs.function_call.arguments
|
|
107
|
-
);
|
|
108
|
-
if (typeof result2 === "string") {
|
|
109
|
-
return {
|
|
110
|
-
input: result2
|
|
111
|
-
};
|
|
112
|
-
} else {
|
|
113
|
-
return result2;
|
|
114
|
-
}
|
|
115
|
-
} catch (e) {
|
|
116
|
-
return {
|
|
117
|
-
input: rawMessage.additional_kwargs.function_call.arguments
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
})()
|
|
121
|
-
} : void 0
|
|
122
|
-
}
|
|
123
|
-
]
|
|
124
|
-
};
|
|
68
|
+
var import_v1_shared_adapter = require("@chatluna/v1-shared-adapter");
|
|
69
|
+
var import_string = require("koishi-plugin-chatluna/utils/string");
|
|
70
|
+
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
71
|
+
return Promise.all(
|
|
72
|
+
messages.map(async (message) => {
|
|
73
|
+
const role = messageTypeToGeminiRole(message.getType());
|
|
74
|
+
const hasFunctionCall = message.additional_kwargs?.function_call != null;
|
|
75
|
+
if (role === "function" || hasFunctionCall) {
|
|
76
|
+
return processFunctionMessage(message);
|
|
125
77
|
}
|
|
126
|
-
const images = rawMessage.additional_kwargs.images;
|
|
127
78
|
const result = {
|
|
128
79
|
role,
|
|
129
|
-
parts: [
|
|
130
|
-
{
|
|
131
|
-
text: rawMessage.content
|
|
132
|
-
}
|
|
133
|
-
]
|
|
80
|
+
parts: []
|
|
134
81
|
};
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
result.parts.push({
|
|
140
|
-
inline_data: {
|
|
141
|
-
// base64 image match type
|
|
142
|
-
data,
|
|
143
|
-
mime_type: mineType ?? "image/jpeg"
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
result.parts = result.parts.filter((uncheckedPart) => {
|
|
148
|
-
const part = partAsTypeCheck(
|
|
149
|
-
uncheckedPart,
|
|
150
|
-
(part2) => part2["text"] != null
|
|
151
|
-
);
|
|
152
|
-
return part == null || part.text.length > 0;
|
|
153
|
-
});
|
|
82
|
+
result.parts = typeof message.content === "string" ? [{ text: message.content }] : await processGeminiContentParts(plugin, message.content);
|
|
83
|
+
const images = message.additional_kwargs.images;
|
|
84
|
+
if (images) {
|
|
85
|
+
processImageParts(result, images, model);
|
|
154
86
|
}
|
|
155
87
|
return result;
|
|
156
88
|
})
|
|
157
89
|
);
|
|
158
|
-
return mappedMessages;
|
|
159
90
|
}
|
|
160
91
|
__name(langchainMessageToGeminiMessage, "langchainMessageToGeminiMessage");
|
|
161
92
|
function extractSystemMessages(messages) {
|
|
@@ -188,6 +119,105 @@ function extractSystemMessages(messages) {
|
|
|
188
119
|
];
|
|
189
120
|
}
|
|
190
121
|
__name(extractSystemMessages, "extractSystemMessages");
|
|
122
|
+
function parseJsonSafely(content) {
|
|
123
|
+
try {
|
|
124
|
+
const result = JSON.parse(content);
|
|
125
|
+
return typeof result === "string" ? { response: result } : result;
|
|
126
|
+
} catch {
|
|
127
|
+
return { response: content };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
__name(parseJsonSafely, "parseJsonSafely");
|
|
131
|
+
function parseJsonArgs(args) {
|
|
132
|
+
try {
|
|
133
|
+
const result = JSON.parse(args);
|
|
134
|
+
return typeof result === "string" ? { input: result } : result;
|
|
135
|
+
} catch {
|
|
136
|
+
return { input: args };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
__name(parseJsonArgs, "parseJsonArgs");
|
|
140
|
+
function processFunctionMessage(message) {
|
|
141
|
+
const hasFunctionCall = message.additional_kwargs?.function_call != null;
|
|
142
|
+
if (hasFunctionCall) {
|
|
143
|
+
const functionCall = message.additional_kwargs.function_call;
|
|
144
|
+
return {
|
|
145
|
+
role: "function",
|
|
146
|
+
parts: [
|
|
147
|
+
{
|
|
148
|
+
functionCall: {
|
|
149
|
+
name: functionCall.name,
|
|
150
|
+
args: parseJsonArgs(functionCall.arguments)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
role: "function",
|
|
158
|
+
parts: [
|
|
159
|
+
{
|
|
160
|
+
functionResponse: {
|
|
161
|
+
name: message.name,
|
|
162
|
+
response: {
|
|
163
|
+
name: message.name,
|
|
164
|
+
content: parseJsonSafely(message.content)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
__name(processFunctionMessage, "processFunctionMessage");
|
|
172
|
+
function processImageParts(result, images, model) {
|
|
173
|
+
if (!((model.includes("vision") || model.includes("gemini") || model.includes("gemma")) && !model.includes("gemini-1.0"))) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
for (const image of images) {
|
|
177
|
+
const mineType = image.split(";")?.[0]?.split(":")?.[1] ?? "image/jpeg";
|
|
178
|
+
const data = image.replace(/^data:image\/\w+;base64,/, "");
|
|
179
|
+
result.parts.push({
|
|
180
|
+
inline_data: { data, mime_type: mineType }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
result.parts = result.parts.filter((uncheckedPart) => {
|
|
184
|
+
const part = partAsTypeCheck(
|
|
185
|
+
uncheckedPart,
|
|
186
|
+
(part2) => part2["text"] != null
|
|
187
|
+
);
|
|
188
|
+
return part == null || part.text.length > 0;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
__name(processImageParts, "processImageParts");
|
|
192
|
+
async function processGeminiImageContent(plugin, part) {
|
|
193
|
+
let url;
|
|
194
|
+
try {
|
|
195
|
+
url = await (0, import_v1_shared_adapter.fetchImageUrl)(plugin, part);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
url = typeof part.image_url === "string" ? part.image_url : part.image_url.url;
|
|
198
|
+
logger.warn(`Failed to fetch image url: ${url}`, e);
|
|
199
|
+
}
|
|
200
|
+
const mineType = url.match(/^data:([^;]+);base64,/)?.[1] ?? "image/jpeg";
|
|
201
|
+
const data = url.replace(/^data:image\/\w+;base64,/, "");
|
|
202
|
+
return {
|
|
203
|
+
inline_data: { data, mime_type: mineType }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
__name(processGeminiImageContent, "processGeminiImageContent");
|
|
207
|
+
async function processGeminiContentParts(plugin, content) {
|
|
208
|
+
return Promise.all(
|
|
209
|
+
content.map(async (part) => {
|
|
210
|
+
if ((0, import_string.isMessageContentText)(part)) {
|
|
211
|
+
return { text: part.text };
|
|
212
|
+
}
|
|
213
|
+
if ((0, import_string.isMessageContentImageUrl)(part)) {
|
|
214
|
+
return await processGeminiImageContent(plugin, part);
|
|
215
|
+
}
|
|
216
|
+
return part;
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
__name(processGeminiContentParts, "processGeminiContentParts");
|
|
191
221
|
function partAsType(part) {
|
|
192
222
|
return part;
|
|
193
223
|
}
|
|
@@ -265,7 +295,7 @@ function formatToolsToGeminiAITools(tools, config, model) {
|
|
|
265
295
|
}
|
|
266
296
|
__name(formatToolsToGeminiAITools, "formatToolsToGeminiAITools");
|
|
267
297
|
function formatToolToGeminiAITool(tool) {
|
|
268
|
-
const parameters = removeAdditionalProperties(
|
|
298
|
+
const parameters = (0, import_v1_shared_adapter.removeAdditionalProperties)(
|
|
269
299
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
270
300
|
(0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema, {
|
|
271
301
|
allowedAdditionalProperties: void 0
|
|
@@ -279,28 +309,6 @@ function formatToolToGeminiAITool(tool) {
|
|
|
279
309
|
};
|
|
280
310
|
}
|
|
281
311
|
__name(formatToolToGeminiAITool, "formatToolToGeminiAITool");
|
|
282
|
-
function removeAdditionalProperties(schema) {
|
|
283
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
284
|
-
const stack = [[schema, null]];
|
|
285
|
-
while (stack.length > 0) {
|
|
286
|
-
const [current] = stack.pop();
|
|
287
|
-
if (typeof current !== "object" || current === null) continue;
|
|
288
|
-
if (Object.hasOwn(current, "additionalProperties")) {
|
|
289
|
-
delete current["additionalProperties"];
|
|
290
|
-
}
|
|
291
|
-
if (Object.hasOwn(current, "$schema")) {
|
|
292
|
-
delete current["$schema"];
|
|
293
|
-
}
|
|
294
|
-
for (const key of Object.keys(current)) {
|
|
295
|
-
const value = current[key];
|
|
296
|
-
if (value && typeof value === "object") {
|
|
297
|
-
stack.push([value, key]);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return schema;
|
|
302
|
-
}
|
|
303
|
-
__name(removeAdditionalProperties, "removeAdditionalProperties");
|
|
304
312
|
function messageTypeToGeminiRole(type) {
|
|
305
313
|
switch (type) {
|
|
306
314
|
case "system":
|
|
@@ -376,9 +384,10 @@ function createGenerationConfig(params, modelConfig, pluginConfig) {
|
|
|
376
384
|
};
|
|
377
385
|
}
|
|
378
386
|
__name(createGenerationConfig, "createGenerationConfig");
|
|
379
|
-
async function createChatGenerationParams(params, modelConfig, pluginConfig) {
|
|
387
|
+
async function createChatGenerationParams(params, plugin, modelConfig, pluginConfig) {
|
|
380
388
|
const geminiMessages = await langchainMessageToGeminiMessage(
|
|
381
389
|
params.input,
|
|
390
|
+
plugin,
|
|
382
391
|
modelConfig.model
|
|
383
392
|
);
|
|
384
393
|
const [systemInstruction, modelMessages] = extractSystemMessages(geminiMessages);
|
|
@@ -405,7 +414,7 @@ function isChatResponse(response) {
|
|
|
405
414
|
__name(isChatResponse, "isChatResponse");
|
|
406
415
|
|
|
407
416
|
// src/requester.ts
|
|
408
|
-
var
|
|
417
|
+
var import_string2 = require("koishi-plugin-chatluna/utils/string");
|
|
409
418
|
var GeminiRequester = class extends import_api.ModelRequester {
|
|
410
419
|
constructor(ctx, _configPool, _pluginConfig, _plugin) {
|
|
411
420
|
super(ctx, _configPool, _pluginConfig, _plugin);
|
|
@@ -437,6 +446,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
437
446
|
`models/${modelConfig.model}:streamGenerateContent?alt=sse`,
|
|
438
447
|
await createChatGenerationParams(
|
|
439
448
|
params,
|
|
449
|
+
this._plugin,
|
|
440
450
|
modelConfig,
|
|
441
451
|
this._pluginConfig
|
|
442
452
|
),
|
|
@@ -458,9 +468,10 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
458
468
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
459
469
|
try {
|
|
460
470
|
const response = await this._post(
|
|
461
|
-
`models/${modelConfig.model}:generateContent
|
|
471
|
+
`models/${modelConfig.model}:generateContent`,
|
|
462
472
|
await createChatGenerationParams(
|
|
463
473
|
params,
|
|
474
|
+
this._plugin,
|
|
464
475
|
modelConfig,
|
|
465
476
|
this._pluginConfig
|
|
466
477
|
),
|
|
@@ -618,7 +629,10 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
618
629
|
yield chunk.generation;
|
|
619
630
|
}
|
|
620
631
|
}
|
|
621
|
-
const finalContent = this._handleFinalContent(
|
|
632
|
+
const finalContent = this._handleFinalContent(
|
|
633
|
+
reasoningContent,
|
|
634
|
+
groundingContent.value
|
|
635
|
+
);
|
|
622
636
|
if (finalContent != null) {
|
|
623
637
|
yield finalContent;
|
|
624
638
|
}
|
|
@@ -723,7 +737,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
723
737
|
);
|
|
724
738
|
const generationChunk = new import_outputs.ChatGenerationChunk({
|
|
725
739
|
message: messageChunk,
|
|
726
|
-
text: (0,
|
|
740
|
+
text: (0, import_string2.getMessageContent)(messageChunk.content) ?? ""
|
|
727
741
|
});
|
|
728
742
|
yield { type: "generation", generation: generationChunk };
|
|
729
743
|
}
|
|
@@ -906,8 +920,10 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
906
920
|
name: model.name,
|
|
907
921
|
maxTokens: model.inputTokenLimit,
|
|
908
922
|
type: model.name.includes("embedding") ? import_types.ModelType.embeddings : import_types.ModelType.llm,
|
|
909
|
-
|
|
910
|
-
|
|
923
|
+
capabilities: [
|
|
924
|
+
import_types.ModelCapabilities.ImageInput,
|
|
925
|
+
import_types.ModelCapabilities.ToolCall
|
|
926
|
+
]
|
|
911
927
|
};
|
|
912
928
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
913
929
|
if (!model.name.includes("-thinking")) {
|
package/lib/index.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
ChatLunaEmbeddings
|
|
31
31
|
} from "koishi-plugin-chatluna/llm-core/platform/model";
|
|
32
32
|
import {
|
|
33
|
+
ModelCapabilities,
|
|
33
34
|
ModelType
|
|
34
35
|
} from "koishi-plugin-chatluna/llm-core/platform/types";
|
|
35
36
|
import {
|
|
@@ -60,97 +61,34 @@ import {
|
|
|
60
61
|
SystemMessageChunk
|
|
61
62
|
} from "@langchain/core/messages";
|
|
62
63
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const result2 = JSON.parse(
|
|
79
|
-
rawMessage.content
|
|
80
|
-
);
|
|
81
|
-
if (typeof result2 === "string") {
|
|
82
|
-
return {
|
|
83
|
-
response: result2
|
|
84
|
-
};
|
|
85
|
-
} else {
|
|
86
|
-
return result2;
|
|
87
|
-
}
|
|
88
|
-
} catch (e) {
|
|
89
|
-
return {
|
|
90
|
-
response: rawMessage.content
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
})()
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
functionCall: rawMessage.additional_kwargs?.function_call != null ? {
|
|
97
|
-
name: rawMessage.additional_kwargs.function_call.name,
|
|
98
|
-
args: (() => {
|
|
99
|
-
try {
|
|
100
|
-
const result2 = JSON.parse(
|
|
101
|
-
rawMessage.additional_kwargs.function_call.arguments
|
|
102
|
-
);
|
|
103
|
-
if (typeof result2 === "string") {
|
|
104
|
-
return {
|
|
105
|
-
input: result2
|
|
106
|
-
};
|
|
107
|
-
} else {
|
|
108
|
-
return result2;
|
|
109
|
-
}
|
|
110
|
-
} catch (e) {
|
|
111
|
-
return {
|
|
112
|
-
input: rawMessage.additional_kwargs.function_call.arguments
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
})()
|
|
116
|
-
} : void 0
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
};
|
|
64
|
+
import {
|
|
65
|
+
fetchImageUrl,
|
|
66
|
+
removeAdditionalProperties
|
|
67
|
+
} from "@chatluna/v1-shared-adapter";
|
|
68
|
+
import {
|
|
69
|
+
isMessageContentImageUrl,
|
|
70
|
+
isMessageContentText
|
|
71
|
+
} from "koishi-plugin-chatluna/utils/string";
|
|
72
|
+
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
73
|
+
return Promise.all(
|
|
74
|
+
messages.map(async (message) => {
|
|
75
|
+
const role = messageTypeToGeminiRole(message.getType());
|
|
76
|
+
const hasFunctionCall = message.additional_kwargs?.function_call != null;
|
|
77
|
+
if (role === "function" || hasFunctionCall) {
|
|
78
|
+
return processFunctionMessage(message);
|
|
120
79
|
}
|
|
121
|
-
const images = rawMessage.additional_kwargs.images;
|
|
122
80
|
const result = {
|
|
123
81
|
role,
|
|
124
|
-
parts: [
|
|
125
|
-
{
|
|
126
|
-
text: rawMessage.content
|
|
127
|
-
}
|
|
128
|
-
]
|
|
82
|
+
parts: []
|
|
129
83
|
};
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
result.parts.push({
|
|
135
|
-
inline_data: {
|
|
136
|
-
// base64 image match type
|
|
137
|
-
data,
|
|
138
|
-
mime_type: mineType ?? "image/jpeg"
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
result.parts = result.parts.filter((uncheckedPart) => {
|
|
143
|
-
const part = partAsTypeCheck(
|
|
144
|
-
uncheckedPart,
|
|
145
|
-
(part2) => part2["text"] != null
|
|
146
|
-
);
|
|
147
|
-
return part == null || part.text.length > 0;
|
|
148
|
-
});
|
|
84
|
+
result.parts = typeof message.content === "string" ? [{ text: message.content }] : await processGeminiContentParts(plugin, message.content);
|
|
85
|
+
const images = message.additional_kwargs.images;
|
|
86
|
+
if (images) {
|
|
87
|
+
processImageParts(result, images, model);
|
|
149
88
|
}
|
|
150
89
|
return result;
|
|
151
90
|
})
|
|
152
91
|
);
|
|
153
|
-
return mappedMessages;
|
|
154
92
|
}
|
|
155
93
|
__name(langchainMessageToGeminiMessage, "langchainMessageToGeminiMessage");
|
|
156
94
|
function extractSystemMessages(messages) {
|
|
@@ -183,6 +121,105 @@ function extractSystemMessages(messages) {
|
|
|
183
121
|
];
|
|
184
122
|
}
|
|
185
123
|
__name(extractSystemMessages, "extractSystemMessages");
|
|
124
|
+
function parseJsonSafely(content) {
|
|
125
|
+
try {
|
|
126
|
+
const result = JSON.parse(content);
|
|
127
|
+
return typeof result === "string" ? { response: result } : result;
|
|
128
|
+
} catch {
|
|
129
|
+
return { response: content };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
__name(parseJsonSafely, "parseJsonSafely");
|
|
133
|
+
function parseJsonArgs(args) {
|
|
134
|
+
try {
|
|
135
|
+
const result = JSON.parse(args);
|
|
136
|
+
return typeof result === "string" ? { input: result } : result;
|
|
137
|
+
} catch {
|
|
138
|
+
return { input: args };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
__name(parseJsonArgs, "parseJsonArgs");
|
|
142
|
+
function processFunctionMessage(message) {
|
|
143
|
+
const hasFunctionCall = message.additional_kwargs?.function_call != null;
|
|
144
|
+
if (hasFunctionCall) {
|
|
145
|
+
const functionCall = message.additional_kwargs.function_call;
|
|
146
|
+
return {
|
|
147
|
+
role: "function",
|
|
148
|
+
parts: [
|
|
149
|
+
{
|
|
150
|
+
functionCall: {
|
|
151
|
+
name: functionCall.name,
|
|
152
|
+
args: parseJsonArgs(functionCall.arguments)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
role: "function",
|
|
160
|
+
parts: [
|
|
161
|
+
{
|
|
162
|
+
functionResponse: {
|
|
163
|
+
name: message.name,
|
|
164
|
+
response: {
|
|
165
|
+
name: message.name,
|
|
166
|
+
content: parseJsonSafely(message.content)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
__name(processFunctionMessage, "processFunctionMessage");
|
|
174
|
+
function processImageParts(result, images, model) {
|
|
175
|
+
if (!((model.includes("vision") || model.includes("gemini") || model.includes("gemma")) && !model.includes("gemini-1.0"))) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const image of images) {
|
|
179
|
+
const mineType = image.split(";")?.[0]?.split(":")?.[1] ?? "image/jpeg";
|
|
180
|
+
const data = image.replace(/^data:image\/\w+;base64,/, "");
|
|
181
|
+
result.parts.push({
|
|
182
|
+
inline_data: { data, mime_type: mineType }
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
result.parts = result.parts.filter((uncheckedPart) => {
|
|
186
|
+
const part = partAsTypeCheck(
|
|
187
|
+
uncheckedPart,
|
|
188
|
+
(part2) => part2["text"] != null
|
|
189
|
+
);
|
|
190
|
+
return part == null || part.text.length > 0;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
__name(processImageParts, "processImageParts");
|
|
194
|
+
async function processGeminiImageContent(plugin, part) {
|
|
195
|
+
let url;
|
|
196
|
+
try {
|
|
197
|
+
url = await fetchImageUrl(plugin, part);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
url = typeof part.image_url === "string" ? part.image_url : part.image_url.url;
|
|
200
|
+
logger.warn(`Failed to fetch image url: ${url}`, e);
|
|
201
|
+
}
|
|
202
|
+
const mineType = url.match(/^data:([^;]+);base64,/)?.[1] ?? "image/jpeg";
|
|
203
|
+
const data = url.replace(/^data:image\/\w+;base64,/, "");
|
|
204
|
+
return {
|
|
205
|
+
inline_data: { data, mime_type: mineType }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
__name(processGeminiImageContent, "processGeminiImageContent");
|
|
209
|
+
async function processGeminiContentParts(plugin, content) {
|
|
210
|
+
return Promise.all(
|
|
211
|
+
content.map(async (part) => {
|
|
212
|
+
if (isMessageContentText(part)) {
|
|
213
|
+
return { text: part.text };
|
|
214
|
+
}
|
|
215
|
+
if (isMessageContentImageUrl(part)) {
|
|
216
|
+
return await processGeminiImageContent(plugin, part);
|
|
217
|
+
}
|
|
218
|
+
return part;
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
__name(processGeminiContentParts, "processGeminiContentParts");
|
|
186
223
|
function partAsType(part) {
|
|
187
224
|
return part;
|
|
188
225
|
}
|
|
@@ -274,28 +311,6 @@ function formatToolToGeminiAITool(tool) {
|
|
|
274
311
|
};
|
|
275
312
|
}
|
|
276
313
|
__name(formatToolToGeminiAITool, "formatToolToGeminiAITool");
|
|
277
|
-
function removeAdditionalProperties(schema) {
|
|
278
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
279
|
-
const stack = [[schema, null]];
|
|
280
|
-
while (stack.length > 0) {
|
|
281
|
-
const [current] = stack.pop();
|
|
282
|
-
if (typeof current !== "object" || current === null) continue;
|
|
283
|
-
if (Object.hasOwn(current, "additionalProperties")) {
|
|
284
|
-
delete current["additionalProperties"];
|
|
285
|
-
}
|
|
286
|
-
if (Object.hasOwn(current, "$schema")) {
|
|
287
|
-
delete current["$schema"];
|
|
288
|
-
}
|
|
289
|
-
for (const key of Object.keys(current)) {
|
|
290
|
-
const value = current[key];
|
|
291
|
-
if (value && typeof value === "object") {
|
|
292
|
-
stack.push([value, key]);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return schema;
|
|
297
|
-
}
|
|
298
|
-
__name(removeAdditionalProperties, "removeAdditionalProperties");
|
|
299
314
|
function messageTypeToGeminiRole(type) {
|
|
300
315
|
switch (type) {
|
|
301
316
|
case "system":
|
|
@@ -371,9 +386,10 @@ function createGenerationConfig(params, modelConfig, pluginConfig) {
|
|
|
371
386
|
};
|
|
372
387
|
}
|
|
373
388
|
__name(createGenerationConfig, "createGenerationConfig");
|
|
374
|
-
async function createChatGenerationParams(params, modelConfig, pluginConfig) {
|
|
389
|
+
async function createChatGenerationParams(params, plugin, modelConfig, pluginConfig) {
|
|
375
390
|
const geminiMessages = await langchainMessageToGeminiMessage(
|
|
376
391
|
params.input,
|
|
392
|
+
plugin,
|
|
377
393
|
modelConfig.model
|
|
378
394
|
);
|
|
379
395
|
const [systemInstruction, modelMessages] = extractSystemMessages(geminiMessages);
|
|
@@ -432,6 +448,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
432
448
|
`models/${modelConfig.model}:streamGenerateContent?alt=sse`,
|
|
433
449
|
await createChatGenerationParams(
|
|
434
450
|
params,
|
|
451
|
+
this._plugin,
|
|
435
452
|
modelConfig,
|
|
436
453
|
this._pluginConfig
|
|
437
454
|
),
|
|
@@ -453,9 +470,10 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
453
470
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
454
471
|
try {
|
|
455
472
|
const response = await this._post(
|
|
456
|
-
`models/${modelConfig.model}:generateContent
|
|
473
|
+
`models/${modelConfig.model}:generateContent`,
|
|
457
474
|
await createChatGenerationParams(
|
|
458
475
|
params,
|
|
476
|
+
this._plugin,
|
|
459
477
|
modelConfig,
|
|
460
478
|
this._pluginConfig
|
|
461
479
|
),
|
|
@@ -613,7 +631,10 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
613
631
|
yield chunk.generation;
|
|
614
632
|
}
|
|
615
633
|
}
|
|
616
|
-
const finalContent = this._handleFinalContent(
|
|
634
|
+
const finalContent = this._handleFinalContent(
|
|
635
|
+
reasoningContent,
|
|
636
|
+
groundingContent.value
|
|
637
|
+
);
|
|
617
638
|
if (finalContent != null) {
|
|
618
639
|
yield finalContent;
|
|
619
640
|
}
|
|
@@ -901,8 +922,10 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
901
922
|
name: model.name,
|
|
902
923
|
maxTokens: model.inputTokenLimit,
|
|
903
924
|
type: model.name.includes("embedding") ? ModelType.embeddings : ModelType.llm,
|
|
904
|
-
|
|
905
|
-
|
|
925
|
+
capabilities: [
|
|
926
|
+
ModelCapabilities.ImageInput,
|
|
927
|
+
ModelCapabilities.ToolCall
|
|
928
|
+
]
|
|
906
929
|
};
|
|
907
930
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
908
931
|
if (!model.name.includes("-thinking")) {
|
package/lib/utils.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { StructuredTool } from '@langchain/core/tools';
|
|
|
3
3
|
import { ChatCompletionFunction, ChatCompletionResponseMessage, ChatCompletionResponseMessageRoleEnum, ChatPart, ChatResponse } from './types';
|
|
4
4
|
import { Config } from '.';
|
|
5
5
|
import { ModelRequestParams } from 'koishi-plugin-chatluna/llm-core/platform/api';
|
|
6
|
-
|
|
6
|
+
import { ChatLunaPlugin } from 'koishi-plugin-chatluna/services/chat';
|
|
7
|
+
export declare function langchainMessageToGeminiMessage(messages: BaseMessage[], plugin: ChatLunaPlugin, model?: string): Promise<ChatCompletionResponseMessage[]>;
|
|
7
8
|
export declare function extractSystemMessages(messages: ChatCompletionResponseMessage[]): [ChatCompletionResponseMessage, ChatCompletionResponseMessage[]];
|
|
8
9
|
export declare function partAsType<T extends ChatPart>(part: ChatPart): T;
|
|
9
10
|
export declare function partAsTypeCheck<T extends ChatPart>(part: ChatPart, check: (part: ChatPart & unknown) => boolean): T | undefined;
|
|
@@ -31,7 +32,7 @@ export declare function createGenerationConfig(params: ModelRequestParams, model
|
|
|
31
32
|
includeThoughts: boolean;
|
|
32
33
|
};
|
|
33
34
|
};
|
|
34
|
-
export declare function createChatGenerationParams(params: ModelRequestParams, modelConfig: ReturnType<typeof prepareModelConfig>, pluginConfig: Config): Promise<{
|
|
35
|
+
export declare function createChatGenerationParams(params: ModelRequestParams, plugin: ChatLunaPlugin, modelConfig: ReturnType<typeof prepareModelConfig>, pluginConfig: Config): Promise<{
|
|
35
36
|
contents: ChatCompletionResponseMessage[];
|
|
36
37
|
safetySettings: {
|
|
37
38
|
category: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-chatluna-google-gemini-adapter",
|
|
3
3
|
"description": "google-gemini adapter for chatluna",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.22",
|
|
5
5
|
"main": "lib/index.cjs",
|
|
6
6
|
"module": "lib/index.mjs",
|
|
7
7
|
"typings": "lib/index.d.ts",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"adapter"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@chatluna/v1-shared-adapter": "^1.0.
|
|
65
|
+
"@chatluna/v1-shared-adapter": "^1.0.5",
|
|
66
66
|
"@langchain/core": "^0.3.43",
|
|
67
67
|
"zod": "^3.25.0-canary.20250211T214501",
|
|
68
68
|
"zod-to-json-schema": "^3.24.5"
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"koishi": "^4.18.7",
|
|
76
|
-
"koishi-plugin-chatluna": "^1.3.0-alpha.
|
|
76
|
+
"koishi-plugin-chatluna": "^1.3.0-alpha.17"
|
|
77
77
|
},
|
|
78
78
|
"koishi": {
|
|
79
79
|
"description": {
|