koishi-plugin-chatluna-google-gemini-adapter 1.3.0-alpha.2 → 1.3.0-alpha.20
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/README.md +7 -7
- package/lib/client.d.ts +2 -1
- package/lib/index.cjs +136 -99
- package/lib/index.d.ts +2 -2
- package/lib/index.mjs +129 -92
- package/lib/requester.d.ts +3 -2
- package/lib/types.d.ts +10 -1
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
## chatluna-google-gemini-adapter
|
|
2
|
-
|
|
3
|
-
## [](https://www.npmjs.com/package/koishi-plugin-chatluna-google-gemini) [](https://www.npmjs.com/package//koishi-plugin-chatluna-google-gemini-adapter)
|
|
4
|
-
|
|
5
|
-
> 为 ChatLuna 提供 Google Gemini 支持的适配器
|
|
6
|
-
|
|
7
|
-
[Gemini 适配器文档](https://chatluna.chat/guide/configure-model-platform/google-gemini.html)
|
|
1
|
+
## chatluna-google-gemini-adapter
|
|
2
|
+
|
|
3
|
+
## [](https://www.npmjs.com/package/koishi-plugin-chatluna-google-gemini) [](https://www.npmjs.com/package//koishi-plugin-chatluna-google-gemini-adapter)
|
|
4
|
+
|
|
5
|
+
> 为 ChatLuna 提供 Google Gemini 支持的适配器
|
|
6
|
+
|
|
7
|
+
[Gemini 适配器文档](https://chatluna.chat/guide/configure-model-platform/google-gemini.html)
|
package/lib/client.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ChatLunaBaseEmbeddings, ChatLunaChatModel } from 'koishi-plugin-chatlun
|
|
|
5
5
|
import { ModelInfo } from 'koishi-plugin-chatluna/llm-core/platform/types';
|
|
6
6
|
import { Config } from '.';
|
|
7
7
|
import { ChatLunaPlugin } from 'koishi-plugin-chatluna/services/chat';
|
|
8
|
+
import { RunnableConfig } from '@langchain/core/runnables';
|
|
8
9
|
export declare class GeminiClient extends PlatformModelAndEmbeddingsClient<ClientConfig> {
|
|
9
10
|
private _config;
|
|
10
11
|
plugin: ChatLunaPlugin;
|
|
@@ -12,6 +13,6 @@ export declare class GeminiClient extends PlatformModelAndEmbeddingsClient<Clien
|
|
|
12
13
|
private _requester;
|
|
13
14
|
get logger(): import("reggol");
|
|
14
15
|
constructor(ctx: Context, _config: Config, plugin: ChatLunaPlugin);
|
|
15
|
-
refreshModels(): Promise<ModelInfo[]>;
|
|
16
|
+
refreshModels(config?: RunnableConfig): Promise<ModelInfo[]>;
|
|
16
17
|
protected _createModel(model: string): ChatLunaChatModel | ChatLunaBaseEmbeddings;
|
|
17
18
|
}
|
package/lib/index.cjs
CHANGED
|
@@ -23,14 +23,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
23
23
|
// src/locales/zh-CN.schema.yml
|
|
24
24
|
var require_zh_CN_schema = __commonJS({
|
|
25
25
|
"src/locales/zh-CN.schema.yml"(exports2, module2) {
|
|
26
|
-
module2.exports = { $inner: [{}, { $desc: "请求选项", platform: "适配器的平台名。(不懂请不要修改)", apiKeys: { $inner: ["Gemini
|
|
26
|
+
module2.exports = { $inner: [{}, { $desc: "请求选项", platform: "适配器的平台名。(不懂请不要修改)", apiKeys: { $inner: ["Gemini API Key", "Gemini API 请求地址", "是否启用此配置"], $desc: "Gemini 的 API Key 和请求地址列表。" } }, { $desc: "模型配置", maxContextRatio: "最大上下文使用比例(0~1),控制可用的模型上下文窗口大小的最大百分比。例如 0.35 表示最多使用模型上下文的 35%。", temperature: "回复的随机性程度,数值越高,回复越随机(范围:0~2)。", googleSearch: "为模型启用谷歌搜索。", thinkingBudget: "思考预算,范围:(-1~24576),设置的数值越大,思考时花费的 Token 越多,-1 为动态思考。目前仅支持 gemini 2.5 系列模型。", groundingContentDisplay: "是否显示谷歌搜索结果。", imageGeneration: "为模型启用图像生成。目前仅支持 `gemini-2.0-flash-exp` 和 `gemini-2.5-flash-image-preview` 模型。", searchThreshold: "搜索的[置信度阈值](https://ai.google.dev/gemini-api/docs/grounding?lang=rest#dynamic-retrieval),范围:0~1,设置的数值越低,则越倾向于使用谷歌搜索。(仅支持 `gemini-1.5` 系列模型。gemini 2.0 模型起使用动态的工具调用)", includeThoughts: "是否获取模型的思考内容。", codeExecution: "为模型启用代码执行工具。", urlContext: "为模型启用 URL 内容获取工具。", nonStreaming: "强制不启用流式返回。开启后,将总是以非流式发起请求,即便配置了 stream 参数。" }] };
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
// src/locales/en-US.schema.yml
|
|
31
31
|
var require_en_US_schema = __commonJS({
|
|
32
32
|
"src/locales/en-US.schema.yml"(exports2, module2) {
|
|
33
|
-
module2.exports = { $inner: [{}, { $desc: "API Configuration", platform: "Adapter platform name. (Do not modify if you do not understand)", apiKeys: { $inner: ["Gemini API Key", "Gemini API Endpoint (optional)"], $desc: "Gemini API access credentials" } }, { $desc: "Model Parameters",
|
|
33
|
+
module2.exports = { $inner: [{}, { $desc: "API Configuration", platform: "Adapter platform name. (Do not modify if you do not understand)", apiKeys: { $inner: ["Gemini API Key", "Gemini API Endpoint (optional)", "Enabled"], $desc: "Gemini API access credentials" } }, { $desc: "Model Parameters", maxContextRatio: "Maximum context usage ratio (0-1). Controls the maximum percentage of model context window available for use. For example, 0.35 means at most 35% of the model context can be used.", temperature: "Sampling temperature (0-2). Higher: more random, Lower: more deterministic", googleSearch: "Enable Google search", thinkingBudget: "Thinking budget (-1-24576). (0: dynamic thinking) Higher: more tokens spent on thinking. Currently only supports `gemini-2.5` series models.", groundingContentDisplay: "Enable display of search results", imageGeneration: "Enable image generation (only for `gemini-2.0-flash-exp` and `gemini-2.5-flash-image-preview` model)", searchThreshold: "Search confidence [threshold](https://ai.google.dev/gemini-api/docs/grounding?lang=rest#dynamic-retrieval) (0-1). Lower: more likely to use Google search", includeThoughts: "Enable retrieval of model thoughts", codeExecution: "Enable code execution tool", urlContext: "Enable URL context retrieval tool", nonStreaming: "Force disable streaming response. When enabled, requests will always be made in non-streaming mode, even if the stream parameter is configured." }] };
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -51,7 +51,7 @@ var import_koishi = require("koishi");
|
|
|
51
51
|
// src/client.ts
|
|
52
52
|
var import_client = require("koishi-plugin-chatluna/llm-core/platform/client");
|
|
53
53
|
var import_model = require("koishi-plugin-chatluna/llm-core/platform/model");
|
|
54
|
-
var
|
|
54
|
+
var import_types2 = require("koishi-plugin-chatluna/llm-core/platform/types");
|
|
55
55
|
var import_error2 = require("koishi-plugin-chatluna/utils/error");
|
|
56
56
|
|
|
57
57
|
// src/requester.ts
|
|
@@ -66,12 +66,12 @@ var import_stream = require("koishi-plugin-chatluna/utils/stream");
|
|
|
66
66
|
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
67
67
|
var import_v1_shared_adapter = require("@chatluna/v1-shared-adapter");
|
|
68
68
|
var import_string = require("koishi-plugin-chatluna/utils/string");
|
|
69
|
-
var
|
|
69
|
+
var import_types = require("@langchain/core/utils/types");
|
|
70
70
|
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
71
71
|
return Promise.all(
|
|
72
72
|
messages.map(async (message) => {
|
|
73
73
|
const role = messageTypeToGeminiRole(message.getType());
|
|
74
|
-
const hasFunctionCall = message.
|
|
74
|
+
const hasFunctionCall = message.tool_calls != null && message.tool_calls.length > 0;
|
|
75
75
|
if (role === "function" || hasFunctionCall) {
|
|
76
76
|
return processFunctionMessage(message);
|
|
77
77
|
}
|
|
@@ -117,50 +117,43 @@ function extractSystemMessages(messages) {
|
|
|
117
117
|
];
|
|
118
118
|
}
|
|
119
119
|
__name(extractSystemMessages, "extractSystemMessages");
|
|
120
|
-
function parseJsonSafely(content) {
|
|
121
|
-
try {
|
|
122
|
-
const result = JSON.parse(content);
|
|
123
|
-
return typeof result === "string" ? { response: result } : result;
|
|
124
|
-
} catch {
|
|
125
|
-
return { response: content };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
__name(parseJsonSafely, "parseJsonSafely");
|
|
129
120
|
function parseJsonArgs(args) {
|
|
130
121
|
try {
|
|
131
122
|
const result = JSON.parse(args);
|
|
132
|
-
|
|
123
|
+
if (typeof result === "string") return { response: result };
|
|
124
|
+
if (Array.isArray(result)) return { response: result };
|
|
125
|
+
return result;
|
|
133
126
|
} catch {
|
|
134
|
-
return {
|
|
127
|
+
return { response: args };
|
|
135
128
|
}
|
|
136
129
|
}
|
|
137
130
|
__name(parseJsonArgs, "parseJsonArgs");
|
|
138
131
|
function processFunctionMessage(message) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
132
|
+
if (message["tool_calls"]) {
|
|
133
|
+
message = message;
|
|
134
|
+
const toolCalls = message.tool_calls;
|
|
142
135
|
return {
|
|
143
|
-
role: "
|
|
144
|
-
parts:
|
|
145
|
-
{
|
|
136
|
+
role: "model",
|
|
137
|
+
parts: toolCalls.map((toolCall) => {
|
|
138
|
+
return {
|
|
146
139
|
functionCall: {
|
|
147
|
-
name:
|
|
148
|
-
args:
|
|
140
|
+
name: toolCall.name,
|
|
141
|
+
args: toolCall.args,
|
|
142
|
+
id: toolCall.id
|
|
149
143
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
144
|
+
};
|
|
145
|
+
})
|
|
152
146
|
};
|
|
153
147
|
}
|
|
148
|
+
const finalMessage = message;
|
|
154
149
|
return {
|
|
155
|
-
role: "
|
|
150
|
+
role: "user",
|
|
156
151
|
parts: [
|
|
157
152
|
{
|
|
158
153
|
functionResponse: {
|
|
159
154
|
name: message.name,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
content: parseJsonSafely(message.content)
|
|
163
|
-
}
|
|
155
|
+
id: finalMessage.tool_call_id,
|
|
156
|
+
response: parseJsonArgs(message.content)
|
|
164
157
|
}
|
|
165
158
|
}
|
|
166
159
|
]
|
|
@@ -294,7 +287,7 @@ function formatToolsToGeminiAITools(tools, config, model) {
|
|
|
294
287
|
__name(formatToolsToGeminiAITools, "formatToolsToGeminiAITools");
|
|
295
288
|
function formatToolToGeminiAITool(tool) {
|
|
296
289
|
const parameters = (0, import_v1_shared_adapter.removeAdditionalProperties)(
|
|
297
|
-
tool.schema
|
|
290
|
+
(0, import_types.isZodSchemaV3)(tool.schema) ? (0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema, {
|
|
298
291
|
allowedAdditionalProperties: void 0
|
|
299
292
|
}) : tool.schema
|
|
300
293
|
);
|
|
@@ -314,7 +307,7 @@ function messageTypeToGeminiRole(type) {
|
|
|
314
307
|
return "model";
|
|
315
308
|
case "human":
|
|
316
309
|
return "user";
|
|
317
|
-
case "
|
|
310
|
+
case "tool":
|
|
318
311
|
return "function";
|
|
319
312
|
default:
|
|
320
313
|
throw new Error(`Unknown message type: ${type}`);
|
|
@@ -326,7 +319,7 @@ function prepareModelConfig(params, pluginConfig) {
|
|
|
326
319
|
let enabledThinking = null;
|
|
327
320
|
if (model.includes("-thinking") && model.includes("gemini-2.5")) {
|
|
328
321
|
enabledThinking = !model.includes("-non-thinking");
|
|
329
|
-
model = model.replace("-
|
|
322
|
+
model = model.replace("-non-thinking", "").replace("-thinking", "");
|
|
330
323
|
}
|
|
331
324
|
let thinkingBudget = pluginConfig.thinkingBudget ?? -1;
|
|
332
325
|
if (!enabledThinking && !model.includes("2.5-pro")) {
|
|
@@ -412,6 +405,7 @@ __name(isChatResponse, "isChatResponse");
|
|
|
412
405
|
|
|
413
406
|
// src/requester.ts
|
|
414
407
|
var import_string2 = require("koishi-plugin-chatluna/utils/string");
|
|
408
|
+
var import_logger = require("koishi-plugin-chatluna/utils/logger");
|
|
415
409
|
var GeminiRequester = class extends import_api.ModelRequester {
|
|
416
410
|
constructor(ctx, _configPool, _pluginConfig, _plugin) {
|
|
417
411
|
super(ctx, _configPool, _pluginConfig, _plugin);
|
|
@@ -455,6 +449,13 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
455
449
|
await (0, import_sse.checkResponse)(response);
|
|
456
450
|
yield* this._processResponseStream(response);
|
|
457
451
|
} catch (e) {
|
|
452
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
453
|
+
await (0, import_logger.trackLogToLocal)(
|
|
454
|
+
"Request",
|
|
455
|
+
JSON.stringify(chatGenerationParams),
|
|
456
|
+
logger
|
|
457
|
+
);
|
|
458
|
+
}
|
|
458
459
|
if (e instanceof import_error.ChatLunaError) {
|
|
459
460
|
throw e;
|
|
460
461
|
} else {
|
|
@@ -464,15 +465,16 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
464
465
|
}
|
|
465
466
|
async completionInternal(params) {
|
|
466
467
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
468
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
469
|
+
params,
|
|
470
|
+
this._plugin,
|
|
471
|
+
modelConfig,
|
|
472
|
+
this._pluginConfig
|
|
473
|
+
);
|
|
467
474
|
try {
|
|
468
475
|
const response = await this._post(
|
|
469
476
|
`models/${modelConfig.model}:generateContent`,
|
|
470
|
-
|
|
471
|
-
params,
|
|
472
|
-
this._plugin,
|
|
473
|
-
modelConfig,
|
|
474
|
-
this._pluginConfig
|
|
475
|
-
),
|
|
477
|
+
chatGenerationParams,
|
|
476
478
|
{
|
|
477
479
|
signal: params.signal
|
|
478
480
|
}
|
|
@@ -480,6 +482,13 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
480
482
|
await (0, import_sse.checkResponse)(response);
|
|
481
483
|
return await this._processResponse(response);
|
|
482
484
|
} catch (e) {
|
|
485
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
486
|
+
await (0, import_logger.trackLogToLocal)(
|
|
487
|
+
"Request",
|
|
488
|
+
JSON.stringify(chatGenerationParams),
|
|
489
|
+
logger
|
|
490
|
+
);
|
|
491
|
+
}
|
|
483
492
|
if (e instanceof import_error.ChatLunaError) {
|
|
484
493
|
throw e;
|
|
485
494
|
} else {
|
|
@@ -529,12 +538,17 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
529
538
|
"error when calling gemini embeddings, Result: " + JSON.stringify(data)
|
|
530
539
|
);
|
|
531
540
|
}
|
|
532
|
-
async getModels() {
|
|
541
|
+
async getModels(config) {
|
|
533
542
|
try {
|
|
534
|
-
const response = await this._get("models"
|
|
543
|
+
const response = await this._get("models", {
|
|
544
|
+
signal: config?.signal
|
|
545
|
+
});
|
|
535
546
|
const data = await this._parseModelsResponse(response);
|
|
536
547
|
return this._filterAndTransformModels(data.models);
|
|
537
548
|
} catch (e) {
|
|
549
|
+
if (e instanceof import_error.ChatLunaError) {
|
|
550
|
+
throw e;
|
|
551
|
+
}
|
|
538
552
|
const error = new Error(
|
|
539
553
|
"error when listing gemini models, Error: " + e.message
|
|
540
554
|
);
|
|
@@ -671,6 +685,18 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
671
685
|
return;
|
|
672
686
|
}
|
|
673
687
|
const transformValue = typeof chunk === "string" ? JSON.parse(chunk) : chunk;
|
|
688
|
+
if (transformValue.usageMetadata) {
|
|
689
|
+
const promptTokens = transformValue.usageMetadata.promptTokenCount;
|
|
690
|
+
const totalTokens = transformValue.usageMetadata.totalTokenCount;
|
|
691
|
+
const completionTokens = transformValue.usageMetadata.candidatesTokenCount ?? totalTokens - promptTokens;
|
|
692
|
+
controller.enqueue({
|
|
693
|
+
usage: {
|
|
694
|
+
promptTokens,
|
|
695
|
+
completionTokens,
|
|
696
|
+
totalTokens
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
}
|
|
674
700
|
if (!transformValue?.candidates) {
|
|
675
701
|
return;
|
|
676
702
|
}
|
|
@@ -707,27 +733,37 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
707
733
|
async *_processChunks(iterable) {
|
|
708
734
|
let reasoningContent = "";
|
|
709
735
|
let errorCount = 0;
|
|
710
|
-
|
|
711
|
-
name: "",
|
|
712
|
-
args: "",
|
|
713
|
-
arguments: ""
|
|
714
|
-
};
|
|
736
|
+
let functionIndex = 0;
|
|
715
737
|
for await (const chunk of iterable) {
|
|
738
|
+
let parsedChunk;
|
|
739
|
+
if (parsedChunk = partAsTypeCheck(
|
|
740
|
+
chunk,
|
|
741
|
+
(chunk2) => chunk2["usage"] != null
|
|
742
|
+
)) {
|
|
743
|
+
const generationChunk = new import_outputs.ChatGenerationChunk({
|
|
744
|
+
message: new import_messages.AIMessageChunk(""),
|
|
745
|
+
text: "",
|
|
746
|
+
generationInfo: {
|
|
747
|
+
tokenUsage: parsedChunk.usage
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
yield { type: "generation", generation: generationChunk };
|
|
751
|
+
}
|
|
716
752
|
try {
|
|
717
|
-
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
753
|
+
const { updatedContent, updatedReasoning, updatedToolCalling } = await this._processChunk(
|
|
718
754
|
chunk,
|
|
719
755
|
reasoningContent,
|
|
720
|
-
|
|
756
|
+
functionIndex
|
|
721
757
|
);
|
|
722
758
|
if (updatedReasoning !== reasoningContent) {
|
|
723
759
|
reasoningContent = updatedReasoning;
|
|
724
760
|
yield { type: "reasoning", content: reasoningContent };
|
|
725
761
|
continue;
|
|
726
762
|
}
|
|
727
|
-
if (updatedContent ||
|
|
763
|
+
if (updatedContent || updatedToolCalling) {
|
|
728
764
|
const messageChunk = this._createMessageChunk(
|
|
729
765
|
updatedContent,
|
|
730
|
-
|
|
766
|
+
updatedToolCalling,
|
|
731
767
|
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
732
768
|
chunk,
|
|
733
769
|
(part) => part["inlineData"] != null
|
|
@@ -739,6 +775,9 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
739
775
|
});
|
|
740
776
|
yield { type: "generation", generation: generationChunk };
|
|
741
777
|
}
|
|
778
|
+
if (updatedToolCalling) {
|
|
779
|
+
functionIndex++;
|
|
780
|
+
}
|
|
742
781
|
} catch (e) {
|
|
743
782
|
if (errorCount > 5) {
|
|
744
783
|
logger.error("error with chunk", chunk);
|
|
@@ -753,7 +792,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
753
792
|
}
|
|
754
793
|
}
|
|
755
794
|
}
|
|
756
|
-
async _processChunk(chunk, reasoningContent,
|
|
795
|
+
async _processChunk(chunk, reasoningContent, functionIndex) {
|
|
757
796
|
const messagePart = partAsType(chunk);
|
|
758
797
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
759
798
|
const imagePart = partAsTypeCheck(
|
|
@@ -790,30 +829,25 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
790
829
|
}
|
|
791
830
|
}
|
|
792
831
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
832
|
+
let updatedToolCalling;
|
|
793
833
|
if (deltaFunctionCall) {
|
|
794
|
-
this.
|
|
834
|
+
updatedToolCalling = this._createToolCallChunk(
|
|
835
|
+
deltaFunctionCall,
|
|
836
|
+
functionIndex
|
|
837
|
+
);
|
|
795
838
|
}
|
|
796
839
|
return {
|
|
797
840
|
updatedContent: messageContent,
|
|
798
|
-
updatedReasoning: reasoningContent
|
|
841
|
+
updatedReasoning: reasoningContent,
|
|
842
|
+
updatedToolCalling
|
|
799
843
|
};
|
|
800
844
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
parsedArgs = JSON.parse(args);
|
|
809
|
-
if (typeof parsedArgs !== "string") {
|
|
810
|
-
args = parsedArgs;
|
|
811
|
-
}
|
|
812
|
-
} catch (e) {
|
|
813
|
-
}
|
|
814
|
-
functionCall.args = JSON.stringify(args);
|
|
815
|
-
functionCall.name = deltaFunctionCall.name;
|
|
816
|
-
functionCall.arguments = deltaFunctionCall.args;
|
|
845
|
+
_createToolCallChunk(deltaFunctionCall, functionIndex) {
|
|
846
|
+
return {
|
|
847
|
+
name: deltaFunctionCall?.name,
|
|
848
|
+
args: JSON.stringify(deltaFunctionCall.args),
|
|
849
|
+
id: deltaFunctionCall.id ?? `function_call_${functionIndex}`
|
|
850
|
+
};
|
|
817
851
|
}
|
|
818
852
|
_handleFinalContent(reasoningContent, groundingContent) {
|
|
819
853
|
if (reasoningContent.length > 0) {
|
|
@@ -836,15 +870,10 @@ ${groundingContent}`
|
|
|
836
870
|
}
|
|
837
871
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
838
872
|
const messageChunk = new import_messages.AIMessageChunk({
|
|
839
|
-
content: content ?? ""
|
|
873
|
+
content: content ?? "",
|
|
874
|
+
tool_call_chunks: [functionCall].filter(Boolean)
|
|
840
875
|
});
|
|
841
876
|
messageChunk.additional_kwargs = {
|
|
842
|
-
function_call: functionCall.name.length > 0 ? {
|
|
843
|
-
name: functionCall.name,
|
|
844
|
-
arguments: functionCall.args,
|
|
845
|
-
args: functionCall.arguments
|
|
846
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
847
|
-
} : void 0,
|
|
848
877
|
images: imagePart ? [
|
|
849
878
|
`data:${imagePart.inlineData.mimeType ?? "image/png"};base64,${imagePart.inlineData.data}`
|
|
850
879
|
] : void 0
|
|
@@ -867,11 +896,12 @@ ${groundingContent}`
|
|
|
867
896
|
...params
|
|
868
897
|
});
|
|
869
898
|
}
|
|
870
|
-
_get(url) {
|
|
899
|
+
_get(url, params = {}) {
|
|
871
900
|
const requestUrl = this._concatUrl(url);
|
|
872
901
|
return this._plugin.fetch(requestUrl, {
|
|
873
902
|
method: "GET",
|
|
874
|
-
headers: this._buildHeaders()
|
|
903
|
+
headers: this._buildHeaders(),
|
|
904
|
+
...params
|
|
875
905
|
});
|
|
876
906
|
}
|
|
877
907
|
_concatUrl(url) {
|
|
@@ -919,9 +949,9 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
919
949
|
get logger() {
|
|
920
950
|
return logger;
|
|
921
951
|
}
|
|
922
|
-
async refreshModels() {
|
|
952
|
+
async refreshModels(config) {
|
|
923
953
|
try {
|
|
924
|
-
const rawModels = await this._requester.getModels();
|
|
954
|
+
const rawModels = await this._requester.getModels(config);
|
|
925
955
|
if (!rawModels.length) {
|
|
926
956
|
throw new import_error2.ChatLunaError(
|
|
927
957
|
import_error2.ChatLunaErrorCode.MODEL_INIT_ERROR,
|
|
@@ -933,16 +963,16 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
933
963
|
const info = {
|
|
934
964
|
name: model.name,
|
|
935
965
|
maxTokens: model.inputTokenLimit,
|
|
936
|
-
type: model.name.includes("embedding") ?
|
|
966
|
+
type: model.name.includes("embedding") ? import_types2.ModelType.embeddings : import_types2.ModelType.llm,
|
|
937
967
|
capabilities: [
|
|
938
|
-
|
|
939
|
-
|
|
968
|
+
import_types2.ModelCapabilities.ImageInput,
|
|
969
|
+
import_types2.ModelCapabilities.ToolCall
|
|
940
970
|
]
|
|
941
971
|
};
|
|
942
972
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
943
973
|
if (!model.name.includes("-thinking")) {
|
|
944
974
|
models.push(
|
|
945
|
-
{ ...info, name: model.name + "-
|
|
975
|
+
{ ...info, name: model.name + "-non-thinking" },
|
|
946
976
|
{ ...info, name: model.name + "-thinking" },
|
|
947
977
|
info
|
|
948
978
|
);
|
|
@@ -955,6 +985,9 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
955
985
|
}
|
|
956
986
|
return models;
|
|
957
987
|
} catch (e) {
|
|
988
|
+
if (e instanceof import_error2.ChatLunaError) {
|
|
989
|
+
throw e;
|
|
990
|
+
}
|
|
958
991
|
throw new import_error2.ChatLunaError(import_error2.ChatLunaErrorCode.MODEL_INIT_ERROR, e);
|
|
959
992
|
}
|
|
960
993
|
}
|
|
@@ -963,13 +996,15 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
963
996
|
if (info == null) {
|
|
964
997
|
throw new import_error2.ChatLunaError(import_error2.ChatLunaErrorCode.MODEL_NOT_FOUND);
|
|
965
998
|
}
|
|
966
|
-
if (info.type ===
|
|
999
|
+
if (info.type === import_types2.ModelType.llm) {
|
|
967
1000
|
return new import_model.ChatLunaChatModel({
|
|
968
1001
|
modelInfo: info,
|
|
969
1002
|
requester: this._requester,
|
|
970
1003
|
model,
|
|
971
1004
|
modelMaxContextSize: info.maxTokens,
|
|
972
|
-
maxTokenLimit:
|
|
1005
|
+
maxTokenLimit: Math.floor(
|
|
1006
|
+
(info.maxTokens || 1e5) * this._config.maxContextRatio
|
|
1007
|
+
),
|
|
973
1008
|
timeout: this._config.timeout,
|
|
974
1009
|
temperature: this._config.temperature,
|
|
975
1010
|
maxRetries: this._config.maxRetries,
|
|
@@ -985,16 +1020,17 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
985
1020
|
};
|
|
986
1021
|
|
|
987
1022
|
// src/index.ts
|
|
988
|
-
var
|
|
1023
|
+
var import_logger2 = require("koishi-plugin-chatluna/utils/logger");
|
|
989
1024
|
var logger;
|
|
990
1025
|
var reusable = true;
|
|
991
1026
|
function apply(ctx, config) {
|
|
992
|
-
|
|
993
|
-
logger = (0, import_logger.createLogger)(ctx, "chatluna-gemini-adapter");
|
|
1027
|
+
logger = (0, import_logger2.createLogger)(ctx, "chatluna-gemini-adapter");
|
|
994
1028
|
ctx.on("ready", async () => {
|
|
995
|
-
plugin.
|
|
996
|
-
|
|
997
|
-
return config2.apiKeys.
|
|
1029
|
+
const plugin = new import_chat.ChatLunaPlugin(ctx, config, config.platform);
|
|
1030
|
+
plugin.parseConfig((config2) => {
|
|
1031
|
+
return config2.apiKeys.filter(([apiKey, _, enabled]) => {
|
|
1032
|
+
return apiKey.length > 0 && enabled;
|
|
1033
|
+
}).map(([apiKey, apiEndpoint]) => {
|
|
998
1034
|
return {
|
|
999
1035
|
apiKey,
|
|
1000
1036
|
apiEndpoint,
|
|
@@ -1006,8 +1042,8 @@ function apply(ctx, config) {
|
|
|
1006
1042
|
};
|
|
1007
1043
|
});
|
|
1008
1044
|
});
|
|
1009
|
-
plugin.registerClient((
|
|
1010
|
-
await plugin.
|
|
1045
|
+
plugin.registerClient(() => new GeminiClient(ctx, config, plugin));
|
|
1046
|
+
await plugin.initClient();
|
|
1011
1047
|
});
|
|
1012
1048
|
}
|
|
1013
1049
|
__name(apply, "apply");
|
|
@@ -1017,15 +1053,16 @@ var Config4 = import_koishi.Schema.intersect([
|
|
|
1017
1053
|
platform: import_koishi.Schema.string().default("gemini"),
|
|
1018
1054
|
apiKeys: import_koishi.Schema.array(
|
|
1019
1055
|
import_koishi.Schema.tuple([
|
|
1020
|
-
import_koishi.Schema.string().role("secret"),
|
|
1056
|
+
import_koishi.Schema.string().role("secret").default(""),
|
|
1021
1057
|
import_koishi.Schema.string().default(
|
|
1022
1058
|
"https://generativelanguage.googleapis.com/v1beta"
|
|
1023
|
-
)
|
|
1059
|
+
),
|
|
1060
|
+
import_koishi.Schema.boolean().default(true)
|
|
1024
1061
|
])
|
|
1025
|
-
).default([[
|
|
1062
|
+
).default([[]]).role("table")
|
|
1026
1063
|
}),
|
|
1027
1064
|
import_koishi.Schema.object({
|
|
1028
|
-
|
|
1065
|
+
maxContextRatio: import_koishi.Schema.number().min(0).max(1).step(1e-4).role("slider").default(0.35),
|
|
1029
1066
|
temperature: import_koishi.Schema.percent().min(0).max(2).step(0.1).default(1),
|
|
1030
1067
|
googleSearch: import_koishi.Schema.boolean().default(false),
|
|
1031
1068
|
codeExecution: import_koishi.Schema.boolean().default(false),
|
package/lib/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export declare let logger: Logger;
|
|
|
4
4
|
export declare const reusable = true;
|
|
5
5
|
export declare function apply(ctx: Context, config: Config): void;
|
|
6
6
|
export interface Config extends ChatLunaPlugin.Config {
|
|
7
|
-
apiKeys: [string, string][];
|
|
8
|
-
|
|
7
|
+
apiKeys: [string, string, boolean][];
|
|
8
|
+
maxContextRatio: number;
|
|
9
9
|
platform: string;
|
|
10
10
|
temperature: number;
|
|
11
11
|
googleSearch: boolean;
|
package/lib/index.mjs
CHANGED
|
@@ -8,14 +8,14 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
8
8
|
// src/locales/zh-CN.schema.yml
|
|
9
9
|
var require_zh_CN_schema = __commonJS({
|
|
10
10
|
"src/locales/zh-CN.schema.yml"(exports, module) {
|
|
11
|
-
module.exports = { $inner: [{}, { $desc: "请求选项", platform: "适配器的平台名。(不懂请不要修改)", apiKeys: { $inner: ["Gemini
|
|
11
|
+
module.exports = { $inner: [{}, { $desc: "请求选项", platform: "适配器的平台名。(不懂请不要修改)", apiKeys: { $inner: ["Gemini API Key", "Gemini API 请求地址", "是否启用此配置"], $desc: "Gemini 的 API Key 和请求地址列表。" } }, { $desc: "模型配置", maxContextRatio: "最大上下文使用比例(0~1),控制可用的模型上下文窗口大小的最大百分比。例如 0.35 表示最多使用模型上下文的 35%。", temperature: "回复的随机性程度,数值越高,回复越随机(范围:0~2)。", googleSearch: "为模型启用谷歌搜索。", thinkingBudget: "思考预算,范围:(-1~24576),设置的数值越大,思考时花费的 Token 越多,-1 为动态思考。目前仅支持 gemini 2.5 系列模型。", groundingContentDisplay: "是否显示谷歌搜索结果。", imageGeneration: "为模型启用图像生成。目前仅支持 `gemini-2.0-flash-exp` 和 `gemini-2.5-flash-image-preview` 模型。", searchThreshold: "搜索的[置信度阈值](https://ai.google.dev/gemini-api/docs/grounding?lang=rest#dynamic-retrieval),范围:0~1,设置的数值越低,则越倾向于使用谷歌搜索。(仅支持 `gemini-1.5` 系列模型。gemini 2.0 模型起使用动态的工具调用)", includeThoughts: "是否获取模型的思考内容。", codeExecution: "为模型启用代码执行工具。", urlContext: "为模型启用 URL 内容获取工具。", nonStreaming: "强制不启用流式返回。开启后,将总是以非流式发起请求,即便配置了 stream 参数。" }] };
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
// src/locales/en-US.schema.yml
|
|
16
16
|
var require_en_US_schema = __commonJS({
|
|
17
17
|
"src/locales/en-US.schema.yml"(exports, module) {
|
|
18
|
-
module.exports = { $inner: [{}, { $desc: "API Configuration", platform: "Adapter platform name. (Do not modify if you do not understand)", apiKeys: { $inner: ["Gemini API Key", "Gemini API Endpoint (optional)"], $desc: "Gemini API access credentials" } }, { $desc: "Model Parameters",
|
|
18
|
+
module.exports = { $inner: [{}, { $desc: "API Configuration", platform: "Adapter platform name. (Do not modify if you do not understand)", apiKeys: { $inner: ["Gemini API Key", "Gemini API Endpoint (optional)", "Enabled"], $desc: "Gemini API access credentials" } }, { $desc: "Model Parameters", maxContextRatio: "Maximum context usage ratio (0-1). Controls the maximum percentage of model context window available for use. For example, 0.35 means at most 35% of the model context can be used.", temperature: "Sampling temperature (0-2). Higher: more random, Lower: more deterministic", googleSearch: "Enable Google search", thinkingBudget: "Thinking budget (-1-24576). (0: dynamic thinking) Higher: more tokens spent on thinking. Currently only supports `gemini-2.5` series models.", groundingContentDisplay: "Enable display of search results", imageGeneration: "Enable image generation (only for `gemini-2.0-flash-exp` and `gemini-2.5-flash-image-preview` model)", searchThreshold: "Search confidence [threshold](https://ai.google.dev/gemini-api/docs/grounding?lang=rest#dynamic-retrieval) (0-1). Lower: more likely to use Google search", includeThoughts: "Enable retrieval of model thoughts", codeExecution: "Enable code execution tool", urlContext: "Enable URL context retrieval tool", nonStreaming: "Force disable streaming response. When enabled, requests will always be made in non-streaming mode, even if the stream parameter is configured." }] };
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -63,12 +63,12 @@ import {
|
|
|
63
63
|
isMessageContentImageUrl,
|
|
64
64
|
isMessageContentText
|
|
65
65
|
} from "koishi-plugin-chatluna/utils/string";
|
|
66
|
-
import {
|
|
66
|
+
import { isZodSchemaV3 } from "@langchain/core/utils/types";
|
|
67
67
|
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
68
68
|
return Promise.all(
|
|
69
69
|
messages.map(async (message) => {
|
|
70
70
|
const role = messageTypeToGeminiRole(message.getType());
|
|
71
|
-
const hasFunctionCall = message.
|
|
71
|
+
const hasFunctionCall = message.tool_calls != null && message.tool_calls.length > 0;
|
|
72
72
|
if (role === "function" || hasFunctionCall) {
|
|
73
73
|
return processFunctionMessage(message);
|
|
74
74
|
}
|
|
@@ -114,50 +114,43 @@ function extractSystemMessages(messages) {
|
|
|
114
114
|
];
|
|
115
115
|
}
|
|
116
116
|
__name(extractSystemMessages, "extractSystemMessages");
|
|
117
|
-
function parseJsonSafely(content) {
|
|
118
|
-
try {
|
|
119
|
-
const result = JSON.parse(content);
|
|
120
|
-
return typeof result === "string" ? { response: result } : result;
|
|
121
|
-
} catch {
|
|
122
|
-
return { response: content };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
__name(parseJsonSafely, "parseJsonSafely");
|
|
126
117
|
function parseJsonArgs(args) {
|
|
127
118
|
try {
|
|
128
119
|
const result = JSON.parse(args);
|
|
129
|
-
|
|
120
|
+
if (typeof result === "string") return { response: result };
|
|
121
|
+
if (Array.isArray(result)) return { response: result };
|
|
122
|
+
return result;
|
|
130
123
|
} catch {
|
|
131
|
-
return {
|
|
124
|
+
return { response: args };
|
|
132
125
|
}
|
|
133
126
|
}
|
|
134
127
|
__name(parseJsonArgs, "parseJsonArgs");
|
|
135
128
|
function processFunctionMessage(message) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
129
|
+
if (message["tool_calls"]) {
|
|
130
|
+
message = message;
|
|
131
|
+
const toolCalls = message.tool_calls;
|
|
139
132
|
return {
|
|
140
|
-
role: "
|
|
141
|
-
parts:
|
|
142
|
-
{
|
|
133
|
+
role: "model",
|
|
134
|
+
parts: toolCalls.map((toolCall) => {
|
|
135
|
+
return {
|
|
143
136
|
functionCall: {
|
|
144
|
-
name:
|
|
145
|
-
args:
|
|
137
|
+
name: toolCall.name,
|
|
138
|
+
args: toolCall.args,
|
|
139
|
+
id: toolCall.id
|
|
146
140
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
141
|
+
};
|
|
142
|
+
})
|
|
149
143
|
};
|
|
150
144
|
}
|
|
145
|
+
const finalMessage = message;
|
|
151
146
|
return {
|
|
152
|
-
role: "
|
|
147
|
+
role: "user",
|
|
153
148
|
parts: [
|
|
154
149
|
{
|
|
155
150
|
functionResponse: {
|
|
156
151
|
name: message.name,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
content: parseJsonSafely(message.content)
|
|
160
|
-
}
|
|
152
|
+
id: finalMessage.tool_call_id,
|
|
153
|
+
response: parseJsonArgs(message.content)
|
|
161
154
|
}
|
|
162
155
|
}
|
|
163
156
|
]
|
|
@@ -291,7 +284,7 @@ function formatToolsToGeminiAITools(tools, config, model) {
|
|
|
291
284
|
__name(formatToolsToGeminiAITools, "formatToolsToGeminiAITools");
|
|
292
285
|
function formatToolToGeminiAITool(tool) {
|
|
293
286
|
const parameters = removeAdditionalProperties(
|
|
294
|
-
tool.schema
|
|
287
|
+
isZodSchemaV3(tool.schema) ? zodToJsonSchema(tool.schema, {
|
|
295
288
|
allowedAdditionalProperties: void 0
|
|
296
289
|
}) : tool.schema
|
|
297
290
|
);
|
|
@@ -311,7 +304,7 @@ function messageTypeToGeminiRole(type) {
|
|
|
311
304
|
return "model";
|
|
312
305
|
case "human":
|
|
313
306
|
return "user";
|
|
314
|
-
case "
|
|
307
|
+
case "tool":
|
|
315
308
|
return "function";
|
|
316
309
|
default:
|
|
317
310
|
throw new Error(`Unknown message type: ${type}`);
|
|
@@ -323,7 +316,7 @@ function prepareModelConfig(params, pluginConfig) {
|
|
|
323
316
|
let enabledThinking = null;
|
|
324
317
|
if (model.includes("-thinking") && model.includes("gemini-2.5")) {
|
|
325
318
|
enabledThinking = !model.includes("-non-thinking");
|
|
326
|
-
model = model.replace("-
|
|
319
|
+
model = model.replace("-non-thinking", "").replace("-thinking", "");
|
|
327
320
|
}
|
|
328
321
|
let thinkingBudget = pluginConfig.thinkingBudget ?? -1;
|
|
329
322
|
if (!enabledThinking && !model.includes("2.5-pro")) {
|
|
@@ -409,6 +402,7 @@ __name(isChatResponse, "isChatResponse");
|
|
|
409
402
|
|
|
410
403
|
// src/requester.ts
|
|
411
404
|
import { getMessageContent } from "koishi-plugin-chatluna/utils/string";
|
|
405
|
+
import { trackLogToLocal } from "koishi-plugin-chatluna/utils/logger";
|
|
412
406
|
var GeminiRequester = class extends ModelRequester {
|
|
413
407
|
constructor(ctx, _configPool, _pluginConfig, _plugin) {
|
|
414
408
|
super(ctx, _configPool, _pluginConfig, _plugin);
|
|
@@ -452,6 +446,13 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
452
446
|
await checkResponse(response);
|
|
453
447
|
yield* this._processResponseStream(response);
|
|
454
448
|
} catch (e) {
|
|
449
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
450
|
+
await trackLogToLocal(
|
|
451
|
+
"Request",
|
|
452
|
+
JSON.stringify(chatGenerationParams),
|
|
453
|
+
logger
|
|
454
|
+
);
|
|
455
|
+
}
|
|
455
456
|
if (e instanceof ChatLunaError) {
|
|
456
457
|
throw e;
|
|
457
458
|
} else {
|
|
@@ -461,15 +462,16 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
461
462
|
}
|
|
462
463
|
async completionInternal(params) {
|
|
463
464
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
465
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
466
|
+
params,
|
|
467
|
+
this._plugin,
|
|
468
|
+
modelConfig,
|
|
469
|
+
this._pluginConfig
|
|
470
|
+
);
|
|
464
471
|
try {
|
|
465
472
|
const response = await this._post(
|
|
466
473
|
`models/${modelConfig.model}:generateContent`,
|
|
467
|
-
|
|
468
|
-
params,
|
|
469
|
-
this._plugin,
|
|
470
|
-
modelConfig,
|
|
471
|
-
this._pluginConfig
|
|
472
|
-
),
|
|
474
|
+
chatGenerationParams,
|
|
473
475
|
{
|
|
474
476
|
signal: params.signal
|
|
475
477
|
}
|
|
@@ -477,6 +479,13 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
477
479
|
await checkResponse(response);
|
|
478
480
|
return await this._processResponse(response);
|
|
479
481
|
} catch (e) {
|
|
482
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
483
|
+
await trackLogToLocal(
|
|
484
|
+
"Request",
|
|
485
|
+
JSON.stringify(chatGenerationParams),
|
|
486
|
+
logger
|
|
487
|
+
);
|
|
488
|
+
}
|
|
480
489
|
if (e instanceof ChatLunaError) {
|
|
481
490
|
throw e;
|
|
482
491
|
} else {
|
|
@@ -526,12 +535,17 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
526
535
|
"error when calling gemini embeddings, Result: " + JSON.stringify(data)
|
|
527
536
|
);
|
|
528
537
|
}
|
|
529
|
-
async getModels() {
|
|
538
|
+
async getModels(config) {
|
|
530
539
|
try {
|
|
531
|
-
const response = await this._get("models"
|
|
540
|
+
const response = await this._get("models", {
|
|
541
|
+
signal: config?.signal
|
|
542
|
+
});
|
|
532
543
|
const data = await this._parseModelsResponse(response);
|
|
533
544
|
return this._filterAndTransformModels(data.models);
|
|
534
545
|
} catch (e) {
|
|
546
|
+
if (e instanceof ChatLunaError) {
|
|
547
|
+
throw e;
|
|
548
|
+
}
|
|
535
549
|
const error = new Error(
|
|
536
550
|
"error when listing gemini models, Error: " + e.message
|
|
537
551
|
);
|
|
@@ -668,6 +682,18 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
668
682
|
return;
|
|
669
683
|
}
|
|
670
684
|
const transformValue = typeof chunk === "string" ? JSON.parse(chunk) : chunk;
|
|
685
|
+
if (transformValue.usageMetadata) {
|
|
686
|
+
const promptTokens = transformValue.usageMetadata.promptTokenCount;
|
|
687
|
+
const totalTokens = transformValue.usageMetadata.totalTokenCount;
|
|
688
|
+
const completionTokens = transformValue.usageMetadata.candidatesTokenCount ?? totalTokens - promptTokens;
|
|
689
|
+
controller.enqueue({
|
|
690
|
+
usage: {
|
|
691
|
+
promptTokens,
|
|
692
|
+
completionTokens,
|
|
693
|
+
totalTokens
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
671
697
|
if (!transformValue?.candidates) {
|
|
672
698
|
return;
|
|
673
699
|
}
|
|
@@ -704,27 +730,37 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
704
730
|
async *_processChunks(iterable) {
|
|
705
731
|
let reasoningContent = "";
|
|
706
732
|
let errorCount = 0;
|
|
707
|
-
|
|
708
|
-
name: "",
|
|
709
|
-
args: "",
|
|
710
|
-
arguments: ""
|
|
711
|
-
};
|
|
733
|
+
let functionIndex = 0;
|
|
712
734
|
for await (const chunk of iterable) {
|
|
735
|
+
let parsedChunk;
|
|
736
|
+
if (parsedChunk = partAsTypeCheck(
|
|
737
|
+
chunk,
|
|
738
|
+
(chunk2) => chunk2["usage"] != null
|
|
739
|
+
)) {
|
|
740
|
+
const generationChunk = new ChatGenerationChunk({
|
|
741
|
+
message: new AIMessageChunk(""),
|
|
742
|
+
text: "",
|
|
743
|
+
generationInfo: {
|
|
744
|
+
tokenUsage: parsedChunk.usage
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
yield { type: "generation", generation: generationChunk };
|
|
748
|
+
}
|
|
713
749
|
try {
|
|
714
|
-
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
750
|
+
const { updatedContent, updatedReasoning, updatedToolCalling } = await this._processChunk(
|
|
715
751
|
chunk,
|
|
716
752
|
reasoningContent,
|
|
717
|
-
|
|
753
|
+
functionIndex
|
|
718
754
|
);
|
|
719
755
|
if (updatedReasoning !== reasoningContent) {
|
|
720
756
|
reasoningContent = updatedReasoning;
|
|
721
757
|
yield { type: "reasoning", content: reasoningContent };
|
|
722
758
|
continue;
|
|
723
759
|
}
|
|
724
|
-
if (updatedContent ||
|
|
760
|
+
if (updatedContent || updatedToolCalling) {
|
|
725
761
|
const messageChunk = this._createMessageChunk(
|
|
726
762
|
updatedContent,
|
|
727
|
-
|
|
763
|
+
updatedToolCalling,
|
|
728
764
|
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
729
765
|
chunk,
|
|
730
766
|
(part) => part["inlineData"] != null
|
|
@@ -736,6 +772,9 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
736
772
|
});
|
|
737
773
|
yield { type: "generation", generation: generationChunk };
|
|
738
774
|
}
|
|
775
|
+
if (updatedToolCalling) {
|
|
776
|
+
functionIndex++;
|
|
777
|
+
}
|
|
739
778
|
} catch (e) {
|
|
740
779
|
if (errorCount > 5) {
|
|
741
780
|
logger.error("error with chunk", chunk);
|
|
@@ -750,7 +789,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
750
789
|
}
|
|
751
790
|
}
|
|
752
791
|
}
|
|
753
|
-
async _processChunk(chunk, reasoningContent,
|
|
792
|
+
async _processChunk(chunk, reasoningContent, functionIndex) {
|
|
754
793
|
const messagePart = partAsType(chunk);
|
|
755
794
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
756
795
|
const imagePart = partAsTypeCheck(
|
|
@@ -787,30 +826,25 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
787
826
|
}
|
|
788
827
|
}
|
|
789
828
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
829
|
+
let updatedToolCalling;
|
|
790
830
|
if (deltaFunctionCall) {
|
|
791
|
-
this.
|
|
831
|
+
updatedToolCalling = this._createToolCallChunk(
|
|
832
|
+
deltaFunctionCall,
|
|
833
|
+
functionIndex
|
|
834
|
+
);
|
|
792
835
|
}
|
|
793
836
|
return {
|
|
794
837
|
updatedContent: messageContent,
|
|
795
|
-
updatedReasoning: reasoningContent
|
|
838
|
+
updatedReasoning: reasoningContent,
|
|
839
|
+
updatedToolCalling
|
|
796
840
|
};
|
|
797
841
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
parsedArgs = JSON.parse(args);
|
|
806
|
-
if (typeof parsedArgs !== "string") {
|
|
807
|
-
args = parsedArgs;
|
|
808
|
-
}
|
|
809
|
-
} catch (e) {
|
|
810
|
-
}
|
|
811
|
-
functionCall.args = JSON.stringify(args);
|
|
812
|
-
functionCall.name = deltaFunctionCall.name;
|
|
813
|
-
functionCall.arguments = deltaFunctionCall.args;
|
|
842
|
+
_createToolCallChunk(deltaFunctionCall, functionIndex) {
|
|
843
|
+
return {
|
|
844
|
+
name: deltaFunctionCall?.name,
|
|
845
|
+
args: JSON.stringify(deltaFunctionCall.args),
|
|
846
|
+
id: deltaFunctionCall.id ?? `function_call_${functionIndex}`
|
|
847
|
+
};
|
|
814
848
|
}
|
|
815
849
|
_handleFinalContent(reasoningContent, groundingContent) {
|
|
816
850
|
if (reasoningContent.length > 0) {
|
|
@@ -833,15 +867,10 @@ ${groundingContent}`
|
|
|
833
867
|
}
|
|
834
868
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
835
869
|
const messageChunk = new AIMessageChunk({
|
|
836
|
-
content: content ?? ""
|
|
870
|
+
content: content ?? "",
|
|
871
|
+
tool_call_chunks: [functionCall].filter(Boolean)
|
|
837
872
|
});
|
|
838
873
|
messageChunk.additional_kwargs = {
|
|
839
|
-
function_call: functionCall.name.length > 0 ? {
|
|
840
|
-
name: functionCall.name,
|
|
841
|
-
arguments: functionCall.args,
|
|
842
|
-
args: functionCall.arguments
|
|
843
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
844
|
-
} : void 0,
|
|
845
874
|
images: imagePart ? [
|
|
846
875
|
`data:${imagePart.inlineData.mimeType ?? "image/png"};base64,${imagePart.inlineData.data}`
|
|
847
876
|
] : void 0
|
|
@@ -864,11 +893,12 @@ ${groundingContent}`
|
|
|
864
893
|
...params
|
|
865
894
|
});
|
|
866
895
|
}
|
|
867
|
-
_get(url) {
|
|
896
|
+
_get(url, params = {}) {
|
|
868
897
|
const requestUrl = this._concatUrl(url);
|
|
869
898
|
return this._plugin.fetch(requestUrl, {
|
|
870
899
|
method: "GET",
|
|
871
|
-
headers: this._buildHeaders()
|
|
900
|
+
headers: this._buildHeaders(),
|
|
901
|
+
...params
|
|
872
902
|
});
|
|
873
903
|
}
|
|
874
904
|
_concatUrl(url) {
|
|
@@ -916,9 +946,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
916
946
|
get logger() {
|
|
917
947
|
return logger;
|
|
918
948
|
}
|
|
919
|
-
async refreshModels() {
|
|
949
|
+
async refreshModels(config) {
|
|
920
950
|
try {
|
|
921
|
-
const rawModels = await this._requester.getModels();
|
|
951
|
+
const rawModels = await this._requester.getModels(config);
|
|
922
952
|
if (!rawModels.length) {
|
|
923
953
|
throw new ChatLunaError2(
|
|
924
954
|
ChatLunaErrorCode2.MODEL_INIT_ERROR,
|
|
@@ -939,7 +969,7 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
939
969
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
940
970
|
if (!model.name.includes("-thinking")) {
|
|
941
971
|
models.push(
|
|
942
|
-
{ ...info, name: model.name + "-
|
|
972
|
+
{ ...info, name: model.name + "-non-thinking" },
|
|
943
973
|
{ ...info, name: model.name + "-thinking" },
|
|
944
974
|
info
|
|
945
975
|
);
|
|
@@ -952,6 +982,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
952
982
|
}
|
|
953
983
|
return models;
|
|
954
984
|
} catch (e) {
|
|
985
|
+
if (e instanceof ChatLunaError2) {
|
|
986
|
+
throw e;
|
|
987
|
+
}
|
|
955
988
|
throw new ChatLunaError2(ChatLunaErrorCode2.MODEL_INIT_ERROR, e);
|
|
956
989
|
}
|
|
957
990
|
}
|
|
@@ -966,7 +999,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
966
999
|
requester: this._requester,
|
|
967
1000
|
model,
|
|
968
1001
|
modelMaxContextSize: info.maxTokens,
|
|
969
|
-
maxTokenLimit:
|
|
1002
|
+
maxTokenLimit: Math.floor(
|
|
1003
|
+
(info.maxTokens || 1e5) * this._config.maxContextRatio
|
|
1004
|
+
),
|
|
970
1005
|
timeout: this._config.timeout,
|
|
971
1006
|
temperature: this._config.temperature,
|
|
972
1007
|
maxRetries: this._config.maxRetries,
|
|
@@ -986,12 +1021,13 @@ import { createLogger } from "koishi-plugin-chatluna/utils/logger";
|
|
|
986
1021
|
var logger;
|
|
987
1022
|
var reusable = true;
|
|
988
1023
|
function apply(ctx, config) {
|
|
989
|
-
const plugin = new ChatLunaPlugin(ctx, config, config.platform);
|
|
990
1024
|
logger = createLogger(ctx, "chatluna-gemini-adapter");
|
|
991
1025
|
ctx.on("ready", async () => {
|
|
992
|
-
plugin.
|
|
993
|
-
|
|
994
|
-
return config2.apiKeys.
|
|
1026
|
+
const plugin = new ChatLunaPlugin(ctx, config, config.platform);
|
|
1027
|
+
plugin.parseConfig((config2) => {
|
|
1028
|
+
return config2.apiKeys.filter(([apiKey, _, enabled]) => {
|
|
1029
|
+
return apiKey.length > 0 && enabled;
|
|
1030
|
+
}).map(([apiKey, apiEndpoint]) => {
|
|
995
1031
|
return {
|
|
996
1032
|
apiKey,
|
|
997
1033
|
apiEndpoint,
|
|
@@ -1003,8 +1039,8 @@ function apply(ctx, config) {
|
|
|
1003
1039
|
};
|
|
1004
1040
|
});
|
|
1005
1041
|
});
|
|
1006
|
-
plugin.registerClient((
|
|
1007
|
-
await plugin.
|
|
1042
|
+
plugin.registerClient(() => new GeminiClient(ctx, config, plugin));
|
|
1043
|
+
await plugin.initClient();
|
|
1008
1044
|
});
|
|
1009
1045
|
}
|
|
1010
1046
|
__name(apply, "apply");
|
|
@@ -1014,15 +1050,16 @@ var Config4 = Schema.intersect([
|
|
|
1014
1050
|
platform: Schema.string().default("gemini"),
|
|
1015
1051
|
apiKeys: Schema.array(
|
|
1016
1052
|
Schema.tuple([
|
|
1017
|
-
Schema.string().role("secret"),
|
|
1053
|
+
Schema.string().role("secret").default(""),
|
|
1018
1054
|
Schema.string().default(
|
|
1019
1055
|
"https://generativelanguage.googleapis.com/v1beta"
|
|
1020
|
-
)
|
|
1056
|
+
),
|
|
1057
|
+
Schema.boolean().default(true)
|
|
1021
1058
|
])
|
|
1022
|
-
).default([[
|
|
1059
|
+
).default([[]]).role("table")
|
|
1023
1060
|
}),
|
|
1024
1061
|
Schema.object({
|
|
1025
|
-
|
|
1062
|
+
maxContextRatio: Schema.number().min(0).max(1).step(1e-4).role("slider").default(0.35),
|
|
1026
1063
|
temperature: Schema.percent().min(0).max(2).step(0.1).default(1),
|
|
1027
1064
|
googleSearch: Schema.boolean().default(false),
|
|
1028
1065
|
codeExecution: Schema.boolean().default(false),
|
package/lib/requester.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Config } from '.';
|
|
|
5
5
|
import { GeminiModelInfo } from './types';
|
|
6
6
|
import { ChatLunaPlugin } from 'koishi-plugin-chatluna/services/chat';
|
|
7
7
|
import { Context } from 'koishi';
|
|
8
|
+
import { RunnableConfig } from '@langchain/core/runnables';
|
|
8
9
|
export declare class GeminiRequester extends ModelRequester implements EmbeddingsRequester {
|
|
9
10
|
_pluginConfig: Config;
|
|
10
11
|
constructor(ctx: Context, _configPool: ClientConfigPool<ClientConfig>, _pluginConfig: Config, _plugin: ChatLunaPlugin);
|
|
@@ -15,7 +16,7 @@ export declare class GeminiRequester extends ModelRequester implements Embedding
|
|
|
15
16
|
private _prepareEmbeddingsInput;
|
|
16
17
|
private _createEmbeddingsRequest;
|
|
17
18
|
private _processEmbeddingsResponse;
|
|
18
|
-
getModels(): Promise<GeminiModelInfo[]>;
|
|
19
|
+
getModels(config?: RunnableConfig): Promise<GeminiModelInfo[]>;
|
|
19
20
|
private _parseModelsResponse;
|
|
20
21
|
private _filterAndTransformModels;
|
|
21
22
|
private _processResponse;
|
|
@@ -26,7 +27,7 @@ export declare class GeminiRequester extends ModelRequester implements Embedding
|
|
|
26
27
|
private _processCandidateChunk;
|
|
27
28
|
private _processChunks;
|
|
28
29
|
private _processChunk;
|
|
29
|
-
private
|
|
30
|
+
private _createToolCallChunk;
|
|
30
31
|
private _handleFinalContent;
|
|
31
32
|
private _createMessageChunk;
|
|
32
33
|
private _post;
|
package/lib/types.d.ts
CHANGED
|
@@ -2,11 +2,18 @@ export interface ChatCompletionResponseMessage {
|
|
|
2
2
|
role: string;
|
|
3
3
|
parts?: ChatPart[];
|
|
4
4
|
}
|
|
5
|
-
export type ChatPart = ChatMessagePart | ChatInlineDataPart | ChatFunctionCallingPart | ChatFunctionResponsePart | ChatUploadDataPart;
|
|
5
|
+
export type ChatPart = ChatMessagePart | ChatInlineDataPart | ChatFunctionCallingPart | ChatFunctionResponsePart | ChatUploadDataPart | ChatUsageMetadataPart;
|
|
6
6
|
export type ChatMessagePart = {
|
|
7
7
|
text: string;
|
|
8
8
|
thought?: boolean;
|
|
9
9
|
};
|
|
10
|
+
export type ChatUsageMetadataPart = {
|
|
11
|
+
usage: {
|
|
12
|
+
promptTokens: number;
|
|
13
|
+
completionTokens: number;
|
|
14
|
+
totalTokens: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
10
17
|
export type ChatInlineDataPart = {
|
|
11
18
|
inlineData: {
|
|
12
19
|
mimeType: string;
|
|
@@ -23,12 +30,14 @@ export type ChatFunctionCallingPart = {
|
|
|
23
30
|
functionCall: {
|
|
24
31
|
name: string;
|
|
25
32
|
args?: any;
|
|
33
|
+
id?: string;
|
|
26
34
|
};
|
|
27
35
|
};
|
|
28
36
|
export type ChatFunctionResponsePart = {
|
|
29
37
|
functionResponse: {
|
|
30
38
|
name: string;
|
|
31
39
|
response: any;
|
|
40
|
+
id?: string;
|
|
32
41
|
};
|
|
33
42
|
};
|
|
34
43
|
export interface ChatResponse {
|
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.3.0-alpha.
|
|
4
|
+
"version": "1.3.0-alpha.20",
|
|
5
5
|
"main": "lib/index.cjs",
|
|
6
6
|
"module": "lib/index.mjs",
|
|
7
7
|
"typings": "lib/index.d.ts",
|
|
@@ -22,28 +22,28 @@
|
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
24
|
"url": "https://github.com/ChatLunaLab/chatluna.git",
|
|
25
|
-
"directory": "packages/
|
|
25
|
+
"directory": "packages/adapter-gemini"
|
|
26
26
|
},
|
|
27
27
|
"license": "AGPL-3.0",
|
|
28
28
|
"bugs": {
|
|
29
29
|
"url": "https://github.com/ChatLunaLab/chatluna/issues"
|
|
30
30
|
},
|
|
31
|
-
"homepage": "https://github.com/ChatLunaLab/chatluna/tree/v1-dev/packages/
|
|
31
|
+
"homepage": "https://github.com/ChatLunaLab/chatluna/tree/v1-dev/packages/adapter-gemini#readme",
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "atsc -b"
|
|
34
34
|
},
|
|
35
35
|
"resolutions": {
|
|
36
36
|
"@langchain/core": "0.3.62",
|
|
37
|
-
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.
|
|
37
|
+
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
|
|
38
38
|
},
|
|
39
39
|
"overrides": {
|
|
40
40
|
"@langchain/core": "0.3.62",
|
|
41
|
-
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.
|
|
41
|
+
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
|
|
42
42
|
},
|
|
43
43
|
"pnpm": {
|
|
44
44
|
"overrides": {
|
|
45
45
|
"@langchain/core": "0.3.62",
|
|
46
|
-
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.
|
|
46
|
+
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.21"
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
@@ -62,19 +62,19 @@
|
|
|
62
62
|
"adapter"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@chatluna/v1-shared-adapter": "^1.0.
|
|
65
|
+
"@chatluna/v1-shared-adapter": "^1.0.14",
|
|
66
66
|
"@langchain/core": "0.3.62",
|
|
67
67
|
"zod": "3.25.76",
|
|
68
|
-
"zod-to-json-schema": "^3.24.
|
|
68
|
+
"zod-to-json-schema": "^3.24.6"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"atsc": "^2.1.0",
|
|
72
|
-
"koishi": "^4.18.
|
|
72
|
+
"koishi": "^4.18.9"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"koishi": "^4.18.
|
|
76
|
-
"koishi-plugin-chatluna": "^1.3.0-alpha.
|
|
77
|
-
"koishi-plugin-chatluna-storage-service": "^0.0.
|
|
75
|
+
"koishi": "^4.18.9",
|
|
76
|
+
"koishi-plugin-chatluna": "^1.3.0-alpha.68",
|
|
77
|
+
"koishi-plugin-chatluna-storage-service": "^0.0.11"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"koishi-plugin-chatluna-storage-service": {
|