koishi-plugin-chatluna-google-gemini-adapter 1.3.0-alpha.2 → 1.3.0-alpha.21
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 +137 -102
- package/lib/index.d.ts +2 -2
- package/lib/index.mjs +130 -95
- package/lib/requester.d.ts +3 -2
- package/lib/types.d.ts +10 -1
- package/package.json +14 -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
|
|
@@ -63,15 +63,15 @@ var import_sse = require("koishi-plugin-chatluna/utils/sse");
|
|
|
63
63
|
var import_stream = require("koishi-plugin-chatluna/utils/stream");
|
|
64
64
|
|
|
65
65
|
// src/utils.ts
|
|
66
|
-
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
67
66
|
var import_v1_shared_adapter = require("@chatluna/v1-shared-adapter");
|
|
68
67
|
var import_string = require("koishi-plugin-chatluna/utils/string");
|
|
69
|
-
var
|
|
68
|
+
var import_types = require("@langchain/core/utils/types");
|
|
69
|
+
var import_zod_openapi = require("@anatine/zod-openapi");
|
|
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,9 +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
|
|
298
|
-
allowedAdditionalProperties: void 0
|
|
299
|
-
}) : tool.schema
|
|
290
|
+
(0, import_types.isZodSchemaV3)(tool.schema) ? (0, import_zod_openapi.generateSchema)(tool.schema, true, "3.0") : tool.schema
|
|
300
291
|
);
|
|
301
292
|
return {
|
|
302
293
|
name: tool.name,
|
|
@@ -314,7 +305,7 @@ function messageTypeToGeminiRole(type) {
|
|
|
314
305
|
return "model";
|
|
315
306
|
case "human":
|
|
316
307
|
return "user";
|
|
317
|
-
case "
|
|
308
|
+
case "tool":
|
|
318
309
|
return "function";
|
|
319
310
|
default:
|
|
320
311
|
throw new Error(`Unknown message type: ${type}`);
|
|
@@ -326,7 +317,7 @@ function prepareModelConfig(params, pluginConfig) {
|
|
|
326
317
|
let enabledThinking = null;
|
|
327
318
|
if (model.includes("-thinking") && model.includes("gemini-2.5")) {
|
|
328
319
|
enabledThinking = !model.includes("-non-thinking");
|
|
329
|
-
model = model.replace("-
|
|
320
|
+
model = model.replace("-non-thinking", "").replace("-thinking", "");
|
|
330
321
|
}
|
|
331
322
|
let thinkingBudget = pluginConfig.thinkingBudget ?? -1;
|
|
332
323
|
if (!enabledThinking && !model.includes("2.5-pro")) {
|
|
@@ -412,6 +403,7 @@ __name(isChatResponse, "isChatResponse");
|
|
|
412
403
|
|
|
413
404
|
// src/requester.ts
|
|
414
405
|
var import_string2 = require("koishi-plugin-chatluna/utils/string");
|
|
406
|
+
var import_logger = require("koishi-plugin-chatluna/utils/logger");
|
|
415
407
|
var GeminiRequester = class extends import_api.ModelRequester {
|
|
416
408
|
constructor(ctx, _configPool, _pluginConfig, _plugin) {
|
|
417
409
|
super(ctx, _configPool, _pluginConfig, _plugin);
|
|
@@ -455,6 +447,13 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
455
447
|
await (0, import_sse.checkResponse)(response);
|
|
456
448
|
yield* this._processResponseStream(response);
|
|
457
449
|
} catch (e) {
|
|
450
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
451
|
+
await (0, import_logger.trackLogToLocal)(
|
|
452
|
+
"Request",
|
|
453
|
+
JSON.stringify(chatGenerationParams),
|
|
454
|
+
logger
|
|
455
|
+
);
|
|
456
|
+
}
|
|
458
457
|
if (e instanceof import_error.ChatLunaError) {
|
|
459
458
|
throw e;
|
|
460
459
|
} else {
|
|
@@ -464,15 +463,16 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
464
463
|
}
|
|
465
464
|
async completionInternal(params) {
|
|
466
465
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
466
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
467
|
+
params,
|
|
468
|
+
this._plugin,
|
|
469
|
+
modelConfig,
|
|
470
|
+
this._pluginConfig
|
|
471
|
+
);
|
|
467
472
|
try {
|
|
468
473
|
const response = await this._post(
|
|
469
474
|
`models/${modelConfig.model}:generateContent`,
|
|
470
|
-
|
|
471
|
-
params,
|
|
472
|
-
this._plugin,
|
|
473
|
-
modelConfig,
|
|
474
|
-
this._pluginConfig
|
|
475
|
-
),
|
|
475
|
+
chatGenerationParams,
|
|
476
476
|
{
|
|
477
477
|
signal: params.signal
|
|
478
478
|
}
|
|
@@ -480,6 +480,13 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
480
480
|
await (0, import_sse.checkResponse)(response);
|
|
481
481
|
return await this._processResponse(response);
|
|
482
482
|
} catch (e) {
|
|
483
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
484
|
+
await (0, import_logger.trackLogToLocal)(
|
|
485
|
+
"Request",
|
|
486
|
+
JSON.stringify(chatGenerationParams),
|
|
487
|
+
logger
|
|
488
|
+
);
|
|
489
|
+
}
|
|
483
490
|
if (e instanceof import_error.ChatLunaError) {
|
|
484
491
|
throw e;
|
|
485
492
|
} else {
|
|
@@ -529,12 +536,17 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
529
536
|
"error when calling gemini embeddings, Result: " + JSON.stringify(data)
|
|
530
537
|
);
|
|
531
538
|
}
|
|
532
|
-
async getModels() {
|
|
539
|
+
async getModels(config) {
|
|
533
540
|
try {
|
|
534
|
-
const response = await this._get("models"
|
|
541
|
+
const response = await this._get("models", {
|
|
542
|
+
signal: config?.signal
|
|
543
|
+
});
|
|
535
544
|
const data = await this._parseModelsResponse(response);
|
|
536
545
|
return this._filterAndTransformModels(data.models);
|
|
537
546
|
} catch (e) {
|
|
547
|
+
if (e instanceof import_error.ChatLunaError) {
|
|
548
|
+
throw e;
|
|
549
|
+
}
|
|
538
550
|
const error = new Error(
|
|
539
551
|
"error when listing gemini models, Error: " + e.message
|
|
540
552
|
);
|
|
@@ -671,6 +683,18 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
671
683
|
return;
|
|
672
684
|
}
|
|
673
685
|
const transformValue = typeof chunk === "string" ? JSON.parse(chunk) : chunk;
|
|
686
|
+
if (transformValue.usageMetadata) {
|
|
687
|
+
const promptTokens = transformValue.usageMetadata.promptTokenCount;
|
|
688
|
+
const totalTokens = transformValue.usageMetadata.totalTokenCount;
|
|
689
|
+
const completionTokens = transformValue.usageMetadata.candidatesTokenCount ?? totalTokens - promptTokens;
|
|
690
|
+
controller.enqueue({
|
|
691
|
+
usage: {
|
|
692
|
+
promptTokens,
|
|
693
|
+
completionTokens,
|
|
694
|
+
totalTokens
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
}
|
|
674
698
|
if (!transformValue?.candidates) {
|
|
675
699
|
return;
|
|
676
700
|
}
|
|
@@ -707,27 +731,37 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
707
731
|
async *_processChunks(iterable) {
|
|
708
732
|
let reasoningContent = "";
|
|
709
733
|
let errorCount = 0;
|
|
710
|
-
|
|
711
|
-
name: "",
|
|
712
|
-
args: "",
|
|
713
|
-
arguments: ""
|
|
714
|
-
};
|
|
734
|
+
let functionIndex = 0;
|
|
715
735
|
for await (const chunk of iterable) {
|
|
736
|
+
let parsedChunk;
|
|
737
|
+
if (parsedChunk = partAsTypeCheck(
|
|
738
|
+
chunk,
|
|
739
|
+
(chunk2) => chunk2["usage"] != null
|
|
740
|
+
)) {
|
|
741
|
+
const generationChunk = new import_outputs.ChatGenerationChunk({
|
|
742
|
+
message: new import_messages.AIMessageChunk(""),
|
|
743
|
+
text: "",
|
|
744
|
+
generationInfo: {
|
|
745
|
+
tokenUsage: parsedChunk.usage
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
yield { type: "generation", generation: generationChunk };
|
|
749
|
+
}
|
|
716
750
|
try {
|
|
717
|
-
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
751
|
+
const { updatedContent, updatedReasoning, updatedToolCalling } = await this._processChunk(
|
|
718
752
|
chunk,
|
|
719
753
|
reasoningContent,
|
|
720
|
-
|
|
754
|
+
functionIndex
|
|
721
755
|
);
|
|
722
756
|
if (updatedReasoning !== reasoningContent) {
|
|
723
757
|
reasoningContent = updatedReasoning;
|
|
724
758
|
yield { type: "reasoning", content: reasoningContent };
|
|
725
759
|
continue;
|
|
726
760
|
}
|
|
727
|
-
if (updatedContent ||
|
|
761
|
+
if (updatedContent || updatedToolCalling) {
|
|
728
762
|
const messageChunk = this._createMessageChunk(
|
|
729
763
|
updatedContent,
|
|
730
|
-
|
|
764
|
+
updatedToolCalling,
|
|
731
765
|
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
732
766
|
chunk,
|
|
733
767
|
(part) => part["inlineData"] != null
|
|
@@ -739,6 +773,9 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
739
773
|
});
|
|
740
774
|
yield { type: "generation", generation: generationChunk };
|
|
741
775
|
}
|
|
776
|
+
if (updatedToolCalling) {
|
|
777
|
+
functionIndex++;
|
|
778
|
+
}
|
|
742
779
|
} catch (e) {
|
|
743
780
|
if (errorCount > 5) {
|
|
744
781
|
logger.error("error with chunk", chunk);
|
|
@@ -753,7 +790,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
753
790
|
}
|
|
754
791
|
}
|
|
755
792
|
}
|
|
756
|
-
async _processChunk(chunk, reasoningContent,
|
|
793
|
+
async _processChunk(chunk, reasoningContent, functionIndex) {
|
|
757
794
|
const messagePart = partAsType(chunk);
|
|
758
795
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
759
796
|
const imagePart = partAsTypeCheck(
|
|
@@ -790,30 +827,25 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
790
827
|
}
|
|
791
828
|
}
|
|
792
829
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
830
|
+
let updatedToolCalling;
|
|
793
831
|
if (deltaFunctionCall) {
|
|
794
|
-
this.
|
|
832
|
+
updatedToolCalling = this._createToolCallChunk(
|
|
833
|
+
deltaFunctionCall,
|
|
834
|
+
functionIndex
|
|
835
|
+
);
|
|
795
836
|
}
|
|
796
837
|
return {
|
|
797
838
|
updatedContent: messageContent,
|
|
798
|
-
updatedReasoning: reasoningContent
|
|
839
|
+
updatedReasoning: reasoningContent,
|
|
840
|
+
updatedToolCalling
|
|
799
841
|
};
|
|
800
842
|
}
|
|
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;
|
|
843
|
+
_createToolCallChunk(deltaFunctionCall, functionIndex) {
|
|
844
|
+
return {
|
|
845
|
+
name: deltaFunctionCall?.name,
|
|
846
|
+
args: JSON.stringify(deltaFunctionCall.args),
|
|
847
|
+
id: deltaFunctionCall.id ?? `function_call_${functionIndex}`
|
|
848
|
+
};
|
|
817
849
|
}
|
|
818
850
|
_handleFinalContent(reasoningContent, groundingContent) {
|
|
819
851
|
if (reasoningContent.length > 0) {
|
|
@@ -836,15 +868,10 @@ ${groundingContent}`
|
|
|
836
868
|
}
|
|
837
869
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
838
870
|
const messageChunk = new import_messages.AIMessageChunk({
|
|
839
|
-
content: content ?? ""
|
|
871
|
+
content: content ?? "",
|
|
872
|
+
tool_call_chunks: [functionCall].filter(Boolean)
|
|
840
873
|
});
|
|
841
874
|
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
875
|
images: imagePart ? [
|
|
849
876
|
`data:${imagePart.inlineData.mimeType ?? "image/png"};base64,${imagePart.inlineData.data}`
|
|
850
877
|
] : void 0
|
|
@@ -867,11 +894,12 @@ ${groundingContent}`
|
|
|
867
894
|
...params
|
|
868
895
|
});
|
|
869
896
|
}
|
|
870
|
-
_get(url) {
|
|
897
|
+
_get(url, params = {}) {
|
|
871
898
|
const requestUrl = this._concatUrl(url);
|
|
872
899
|
return this._plugin.fetch(requestUrl, {
|
|
873
900
|
method: "GET",
|
|
874
|
-
headers: this._buildHeaders()
|
|
901
|
+
headers: this._buildHeaders(),
|
|
902
|
+
...params
|
|
875
903
|
});
|
|
876
904
|
}
|
|
877
905
|
_concatUrl(url) {
|
|
@@ -919,9 +947,9 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
919
947
|
get logger() {
|
|
920
948
|
return logger;
|
|
921
949
|
}
|
|
922
|
-
async refreshModels() {
|
|
950
|
+
async refreshModels(config) {
|
|
923
951
|
try {
|
|
924
|
-
const rawModels = await this._requester.getModels();
|
|
952
|
+
const rawModels = await this._requester.getModels(config);
|
|
925
953
|
if (!rawModels.length) {
|
|
926
954
|
throw new import_error2.ChatLunaError(
|
|
927
955
|
import_error2.ChatLunaErrorCode.MODEL_INIT_ERROR,
|
|
@@ -933,16 +961,16 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
933
961
|
const info = {
|
|
934
962
|
name: model.name,
|
|
935
963
|
maxTokens: model.inputTokenLimit,
|
|
936
|
-
type: model.name.includes("embedding") ?
|
|
964
|
+
type: model.name.includes("embedding") ? import_types2.ModelType.embeddings : import_types2.ModelType.llm,
|
|
937
965
|
capabilities: [
|
|
938
|
-
|
|
939
|
-
|
|
966
|
+
import_types2.ModelCapabilities.ImageInput,
|
|
967
|
+
import_types2.ModelCapabilities.ToolCall
|
|
940
968
|
]
|
|
941
969
|
};
|
|
942
970
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
943
971
|
if (!model.name.includes("-thinking")) {
|
|
944
972
|
models.push(
|
|
945
|
-
{ ...info, name: model.name + "-
|
|
973
|
+
{ ...info, name: model.name + "-non-thinking" },
|
|
946
974
|
{ ...info, name: model.name + "-thinking" },
|
|
947
975
|
info
|
|
948
976
|
);
|
|
@@ -955,6 +983,9 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
955
983
|
}
|
|
956
984
|
return models;
|
|
957
985
|
} catch (e) {
|
|
986
|
+
if (e instanceof import_error2.ChatLunaError) {
|
|
987
|
+
throw e;
|
|
988
|
+
}
|
|
958
989
|
throw new import_error2.ChatLunaError(import_error2.ChatLunaErrorCode.MODEL_INIT_ERROR, e);
|
|
959
990
|
}
|
|
960
991
|
}
|
|
@@ -963,13 +994,15 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
963
994
|
if (info == null) {
|
|
964
995
|
throw new import_error2.ChatLunaError(import_error2.ChatLunaErrorCode.MODEL_NOT_FOUND);
|
|
965
996
|
}
|
|
966
|
-
if (info.type ===
|
|
997
|
+
if (info.type === import_types2.ModelType.llm) {
|
|
967
998
|
return new import_model.ChatLunaChatModel({
|
|
968
999
|
modelInfo: info,
|
|
969
1000
|
requester: this._requester,
|
|
970
1001
|
model,
|
|
971
1002
|
modelMaxContextSize: info.maxTokens,
|
|
972
|
-
maxTokenLimit:
|
|
1003
|
+
maxTokenLimit: Math.floor(
|
|
1004
|
+
(info.maxTokens || 1e5) * this._config.maxContextRatio
|
|
1005
|
+
),
|
|
973
1006
|
timeout: this._config.timeout,
|
|
974
1007
|
temperature: this._config.temperature,
|
|
975
1008
|
maxRetries: this._config.maxRetries,
|
|
@@ -985,16 +1018,17 @@ var GeminiClient = class extends import_client.PlatformModelAndEmbeddingsClient
|
|
|
985
1018
|
};
|
|
986
1019
|
|
|
987
1020
|
// src/index.ts
|
|
988
|
-
var
|
|
1021
|
+
var import_logger2 = require("koishi-plugin-chatluna/utils/logger");
|
|
989
1022
|
var logger;
|
|
990
1023
|
var reusable = true;
|
|
991
1024
|
function apply(ctx, config) {
|
|
992
|
-
|
|
993
|
-
logger = (0, import_logger.createLogger)(ctx, "chatluna-gemini-adapter");
|
|
1025
|
+
logger = (0, import_logger2.createLogger)(ctx, "chatluna-gemini-adapter");
|
|
994
1026
|
ctx.on("ready", async () => {
|
|
995
|
-
plugin.
|
|
996
|
-
|
|
997
|
-
return config2.apiKeys.
|
|
1027
|
+
const plugin = new import_chat.ChatLunaPlugin(ctx, config, config.platform);
|
|
1028
|
+
plugin.parseConfig((config2) => {
|
|
1029
|
+
return config2.apiKeys.filter(([apiKey, _, enabled]) => {
|
|
1030
|
+
return apiKey.length > 0 && enabled;
|
|
1031
|
+
}).map(([apiKey, apiEndpoint]) => {
|
|
998
1032
|
return {
|
|
999
1033
|
apiKey,
|
|
1000
1034
|
apiEndpoint,
|
|
@@ -1006,8 +1040,8 @@ function apply(ctx, config) {
|
|
|
1006
1040
|
};
|
|
1007
1041
|
});
|
|
1008
1042
|
});
|
|
1009
|
-
plugin.registerClient((
|
|
1010
|
-
await plugin.
|
|
1043
|
+
plugin.registerClient(() => new GeminiClient(ctx, config, plugin));
|
|
1044
|
+
await plugin.initClient();
|
|
1011
1045
|
});
|
|
1012
1046
|
}
|
|
1013
1047
|
__name(apply, "apply");
|
|
@@ -1017,15 +1051,16 @@ var Config4 = import_koishi.Schema.intersect([
|
|
|
1017
1051
|
platform: import_koishi.Schema.string().default("gemini"),
|
|
1018
1052
|
apiKeys: import_koishi.Schema.array(
|
|
1019
1053
|
import_koishi.Schema.tuple([
|
|
1020
|
-
import_koishi.Schema.string().role("secret"),
|
|
1054
|
+
import_koishi.Schema.string().role("secret").default(""),
|
|
1021
1055
|
import_koishi.Schema.string().default(
|
|
1022
1056
|
"https://generativelanguage.googleapis.com/v1beta"
|
|
1023
|
-
)
|
|
1057
|
+
),
|
|
1058
|
+
import_koishi.Schema.boolean().default(true)
|
|
1024
1059
|
])
|
|
1025
|
-
).default([[
|
|
1060
|
+
).default([[]]).role("table")
|
|
1026
1061
|
}),
|
|
1027
1062
|
import_koishi.Schema.object({
|
|
1028
|
-
|
|
1063
|
+
maxContextRatio: import_koishi.Schema.number().min(0).max(1).step(1e-4).role("slider").default(0.35),
|
|
1029
1064
|
temperature: import_koishi.Schema.percent().min(0).max(2).step(0.1).default(1),
|
|
1030
1065
|
googleSearch: import_koishi.Schema.boolean().default(false),
|
|
1031
1066
|
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
|
|
|
@@ -54,7 +54,6 @@ import { checkResponse, sseIterable } from "koishi-plugin-chatluna/utils/sse";
|
|
|
54
54
|
import { readableStreamToAsyncIterable } from "koishi-plugin-chatluna/utils/stream";
|
|
55
55
|
|
|
56
56
|
// src/utils.ts
|
|
57
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
58
57
|
import {
|
|
59
58
|
fetchImageUrl,
|
|
60
59
|
removeAdditionalProperties
|
|
@@ -63,12 +62,13 @@ import {
|
|
|
63
62
|
isMessageContentImageUrl,
|
|
64
63
|
isMessageContentText
|
|
65
64
|
} from "koishi-plugin-chatluna/utils/string";
|
|
66
|
-
import {
|
|
65
|
+
import { isZodSchemaV3 } from "@langchain/core/utils/types";
|
|
66
|
+
import { generateSchema } from "@anatine/zod-openapi";
|
|
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,9 +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
|
|
295
|
-
allowedAdditionalProperties: void 0
|
|
296
|
-
}) : tool.schema
|
|
287
|
+
isZodSchemaV3(tool.schema) ? generateSchema(tool.schema, true, "3.0") : tool.schema
|
|
297
288
|
);
|
|
298
289
|
return {
|
|
299
290
|
name: tool.name,
|
|
@@ -311,7 +302,7 @@ function messageTypeToGeminiRole(type) {
|
|
|
311
302
|
return "model";
|
|
312
303
|
case "human":
|
|
313
304
|
return "user";
|
|
314
|
-
case "
|
|
305
|
+
case "tool":
|
|
315
306
|
return "function";
|
|
316
307
|
default:
|
|
317
308
|
throw new Error(`Unknown message type: ${type}`);
|
|
@@ -323,7 +314,7 @@ function prepareModelConfig(params, pluginConfig) {
|
|
|
323
314
|
let enabledThinking = null;
|
|
324
315
|
if (model.includes("-thinking") && model.includes("gemini-2.5")) {
|
|
325
316
|
enabledThinking = !model.includes("-non-thinking");
|
|
326
|
-
model = model.replace("-
|
|
317
|
+
model = model.replace("-non-thinking", "").replace("-thinking", "");
|
|
327
318
|
}
|
|
328
319
|
let thinkingBudget = pluginConfig.thinkingBudget ?? -1;
|
|
329
320
|
if (!enabledThinking && !model.includes("2.5-pro")) {
|
|
@@ -409,6 +400,7 @@ __name(isChatResponse, "isChatResponse");
|
|
|
409
400
|
|
|
410
401
|
// src/requester.ts
|
|
411
402
|
import { getMessageContent } from "koishi-plugin-chatluna/utils/string";
|
|
403
|
+
import { trackLogToLocal } from "koishi-plugin-chatluna/utils/logger";
|
|
412
404
|
var GeminiRequester = class extends ModelRequester {
|
|
413
405
|
constructor(ctx, _configPool, _pluginConfig, _plugin) {
|
|
414
406
|
super(ctx, _configPool, _pluginConfig, _plugin);
|
|
@@ -452,6 +444,13 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
452
444
|
await checkResponse(response);
|
|
453
445
|
yield* this._processResponseStream(response);
|
|
454
446
|
} catch (e) {
|
|
447
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
448
|
+
await trackLogToLocal(
|
|
449
|
+
"Request",
|
|
450
|
+
JSON.stringify(chatGenerationParams),
|
|
451
|
+
logger
|
|
452
|
+
);
|
|
453
|
+
}
|
|
455
454
|
if (e instanceof ChatLunaError) {
|
|
456
455
|
throw e;
|
|
457
456
|
} else {
|
|
@@ -461,15 +460,16 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
461
460
|
}
|
|
462
461
|
async completionInternal(params) {
|
|
463
462
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
463
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
464
|
+
params,
|
|
465
|
+
this._plugin,
|
|
466
|
+
modelConfig,
|
|
467
|
+
this._pluginConfig
|
|
468
|
+
);
|
|
464
469
|
try {
|
|
465
470
|
const response = await this._post(
|
|
466
471
|
`models/${modelConfig.model}:generateContent`,
|
|
467
|
-
|
|
468
|
-
params,
|
|
469
|
-
this._plugin,
|
|
470
|
-
modelConfig,
|
|
471
|
-
this._pluginConfig
|
|
472
|
-
),
|
|
472
|
+
chatGenerationParams,
|
|
473
473
|
{
|
|
474
474
|
signal: params.signal
|
|
475
475
|
}
|
|
@@ -477,6 +477,13 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
477
477
|
await checkResponse(response);
|
|
478
478
|
return await this._processResponse(response);
|
|
479
479
|
} catch (e) {
|
|
480
|
+
if (this.ctx.chatluna.config.isLog) {
|
|
481
|
+
await trackLogToLocal(
|
|
482
|
+
"Request",
|
|
483
|
+
JSON.stringify(chatGenerationParams),
|
|
484
|
+
logger
|
|
485
|
+
);
|
|
486
|
+
}
|
|
480
487
|
if (e instanceof ChatLunaError) {
|
|
481
488
|
throw e;
|
|
482
489
|
} else {
|
|
@@ -526,12 +533,17 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
526
533
|
"error when calling gemini embeddings, Result: " + JSON.stringify(data)
|
|
527
534
|
);
|
|
528
535
|
}
|
|
529
|
-
async getModels() {
|
|
536
|
+
async getModels(config) {
|
|
530
537
|
try {
|
|
531
|
-
const response = await this._get("models"
|
|
538
|
+
const response = await this._get("models", {
|
|
539
|
+
signal: config?.signal
|
|
540
|
+
});
|
|
532
541
|
const data = await this._parseModelsResponse(response);
|
|
533
542
|
return this._filterAndTransformModels(data.models);
|
|
534
543
|
} catch (e) {
|
|
544
|
+
if (e instanceof ChatLunaError) {
|
|
545
|
+
throw e;
|
|
546
|
+
}
|
|
535
547
|
const error = new Error(
|
|
536
548
|
"error when listing gemini models, Error: " + e.message
|
|
537
549
|
);
|
|
@@ -668,6 +680,18 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
668
680
|
return;
|
|
669
681
|
}
|
|
670
682
|
const transformValue = typeof chunk === "string" ? JSON.parse(chunk) : chunk;
|
|
683
|
+
if (transformValue.usageMetadata) {
|
|
684
|
+
const promptTokens = transformValue.usageMetadata.promptTokenCount;
|
|
685
|
+
const totalTokens = transformValue.usageMetadata.totalTokenCount;
|
|
686
|
+
const completionTokens = transformValue.usageMetadata.candidatesTokenCount ?? totalTokens - promptTokens;
|
|
687
|
+
controller.enqueue({
|
|
688
|
+
usage: {
|
|
689
|
+
promptTokens,
|
|
690
|
+
completionTokens,
|
|
691
|
+
totalTokens
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
671
695
|
if (!transformValue?.candidates) {
|
|
672
696
|
return;
|
|
673
697
|
}
|
|
@@ -704,27 +728,37 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
704
728
|
async *_processChunks(iterable) {
|
|
705
729
|
let reasoningContent = "";
|
|
706
730
|
let errorCount = 0;
|
|
707
|
-
|
|
708
|
-
name: "",
|
|
709
|
-
args: "",
|
|
710
|
-
arguments: ""
|
|
711
|
-
};
|
|
731
|
+
let functionIndex = 0;
|
|
712
732
|
for await (const chunk of iterable) {
|
|
733
|
+
let parsedChunk;
|
|
734
|
+
if (parsedChunk = partAsTypeCheck(
|
|
735
|
+
chunk,
|
|
736
|
+
(chunk2) => chunk2["usage"] != null
|
|
737
|
+
)) {
|
|
738
|
+
const generationChunk = new ChatGenerationChunk({
|
|
739
|
+
message: new AIMessageChunk(""),
|
|
740
|
+
text: "",
|
|
741
|
+
generationInfo: {
|
|
742
|
+
tokenUsage: parsedChunk.usage
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
yield { type: "generation", generation: generationChunk };
|
|
746
|
+
}
|
|
713
747
|
try {
|
|
714
|
-
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
748
|
+
const { updatedContent, updatedReasoning, updatedToolCalling } = await this._processChunk(
|
|
715
749
|
chunk,
|
|
716
750
|
reasoningContent,
|
|
717
|
-
|
|
751
|
+
functionIndex
|
|
718
752
|
);
|
|
719
753
|
if (updatedReasoning !== reasoningContent) {
|
|
720
754
|
reasoningContent = updatedReasoning;
|
|
721
755
|
yield { type: "reasoning", content: reasoningContent };
|
|
722
756
|
continue;
|
|
723
757
|
}
|
|
724
|
-
if (updatedContent ||
|
|
758
|
+
if (updatedContent || updatedToolCalling) {
|
|
725
759
|
const messageChunk = this._createMessageChunk(
|
|
726
760
|
updatedContent,
|
|
727
|
-
|
|
761
|
+
updatedToolCalling,
|
|
728
762
|
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
729
763
|
chunk,
|
|
730
764
|
(part) => part["inlineData"] != null
|
|
@@ -736,6 +770,9 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
736
770
|
});
|
|
737
771
|
yield { type: "generation", generation: generationChunk };
|
|
738
772
|
}
|
|
773
|
+
if (updatedToolCalling) {
|
|
774
|
+
functionIndex++;
|
|
775
|
+
}
|
|
739
776
|
} catch (e) {
|
|
740
777
|
if (errorCount > 5) {
|
|
741
778
|
logger.error("error with chunk", chunk);
|
|
@@ -750,7 +787,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
750
787
|
}
|
|
751
788
|
}
|
|
752
789
|
}
|
|
753
|
-
async _processChunk(chunk, reasoningContent,
|
|
790
|
+
async _processChunk(chunk, reasoningContent, functionIndex) {
|
|
754
791
|
const messagePart = partAsType(chunk);
|
|
755
792
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
756
793
|
const imagePart = partAsTypeCheck(
|
|
@@ -787,30 +824,25 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
787
824
|
}
|
|
788
825
|
}
|
|
789
826
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
827
|
+
let updatedToolCalling;
|
|
790
828
|
if (deltaFunctionCall) {
|
|
791
|
-
this.
|
|
829
|
+
updatedToolCalling = this._createToolCallChunk(
|
|
830
|
+
deltaFunctionCall,
|
|
831
|
+
functionIndex
|
|
832
|
+
);
|
|
792
833
|
}
|
|
793
834
|
return {
|
|
794
835
|
updatedContent: messageContent,
|
|
795
|
-
updatedReasoning: reasoningContent
|
|
836
|
+
updatedReasoning: reasoningContent,
|
|
837
|
+
updatedToolCalling
|
|
796
838
|
};
|
|
797
839
|
}
|
|
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;
|
|
840
|
+
_createToolCallChunk(deltaFunctionCall, functionIndex) {
|
|
841
|
+
return {
|
|
842
|
+
name: deltaFunctionCall?.name,
|
|
843
|
+
args: JSON.stringify(deltaFunctionCall.args),
|
|
844
|
+
id: deltaFunctionCall.id ?? `function_call_${functionIndex}`
|
|
845
|
+
};
|
|
814
846
|
}
|
|
815
847
|
_handleFinalContent(reasoningContent, groundingContent) {
|
|
816
848
|
if (reasoningContent.length > 0) {
|
|
@@ -833,15 +865,10 @@ ${groundingContent}`
|
|
|
833
865
|
}
|
|
834
866
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
835
867
|
const messageChunk = new AIMessageChunk({
|
|
836
|
-
content: content ?? ""
|
|
868
|
+
content: content ?? "",
|
|
869
|
+
tool_call_chunks: [functionCall].filter(Boolean)
|
|
837
870
|
});
|
|
838
871
|
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
872
|
images: imagePart ? [
|
|
846
873
|
`data:${imagePart.inlineData.mimeType ?? "image/png"};base64,${imagePart.inlineData.data}`
|
|
847
874
|
] : void 0
|
|
@@ -864,11 +891,12 @@ ${groundingContent}`
|
|
|
864
891
|
...params
|
|
865
892
|
});
|
|
866
893
|
}
|
|
867
|
-
_get(url) {
|
|
894
|
+
_get(url, params = {}) {
|
|
868
895
|
const requestUrl = this._concatUrl(url);
|
|
869
896
|
return this._plugin.fetch(requestUrl, {
|
|
870
897
|
method: "GET",
|
|
871
|
-
headers: this._buildHeaders()
|
|
898
|
+
headers: this._buildHeaders(),
|
|
899
|
+
...params
|
|
872
900
|
});
|
|
873
901
|
}
|
|
874
902
|
_concatUrl(url) {
|
|
@@ -916,9 +944,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
916
944
|
get logger() {
|
|
917
945
|
return logger;
|
|
918
946
|
}
|
|
919
|
-
async refreshModels() {
|
|
947
|
+
async refreshModels(config) {
|
|
920
948
|
try {
|
|
921
|
-
const rawModels = await this._requester.getModels();
|
|
949
|
+
const rawModels = await this._requester.getModels(config);
|
|
922
950
|
if (!rawModels.length) {
|
|
923
951
|
throw new ChatLunaError2(
|
|
924
952
|
ChatLunaErrorCode2.MODEL_INIT_ERROR,
|
|
@@ -939,7 +967,7 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
939
967
|
if (model.name.includes("gemini-2.5") && !model.name.includes("pro") && !model.name.includes("image")) {
|
|
940
968
|
if (!model.name.includes("-thinking")) {
|
|
941
969
|
models.push(
|
|
942
|
-
{ ...info, name: model.name + "-
|
|
970
|
+
{ ...info, name: model.name + "-non-thinking" },
|
|
943
971
|
{ ...info, name: model.name + "-thinking" },
|
|
944
972
|
info
|
|
945
973
|
);
|
|
@@ -952,6 +980,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
952
980
|
}
|
|
953
981
|
return models;
|
|
954
982
|
} catch (e) {
|
|
983
|
+
if (e instanceof ChatLunaError2) {
|
|
984
|
+
throw e;
|
|
985
|
+
}
|
|
955
986
|
throw new ChatLunaError2(ChatLunaErrorCode2.MODEL_INIT_ERROR, e);
|
|
956
987
|
}
|
|
957
988
|
}
|
|
@@ -966,7 +997,9 @@ var GeminiClient = class extends PlatformModelAndEmbeddingsClient {
|
|
|
966
997
|
requester: this._requester,
|
|
967
998
|
model,
|
|
968
999
|
modelMaxContextSize: info.maxTokens,
|
|
969
|
-
maxTokenLimit:
|
|
1000
|
+
maxTokenLimit: Math.floor(
|
|
1001
|
+
(info.maxTokens || 1e5) * this._config.maxContextRatio
|
|
1002
|
+
),
|
|
970
1003
|
timeout: this._config.timeout,
|
|
971
1004
|
temperature: this._config.temperature,
|
|
972
1005
|
maxRetries: this._config.maxRetries,
|
|
@@ -986,12 +1019,13 @@ import { createLogger } from "koishi-plugin-chatluna/utils/logger";
|
|
|
986
1019
|
var logger;
|
|
987
1020
|
var reusable = true;
|
|
988
1021
|
function apply(ctx, config) {
|
|
989
|
-
const plugin = new ChatLunaPlugin(ctx, config, config.platform);
|
|
990
1022
|
logger = createLogger(ctx, "chatluna-gemini-adapter");
|
|
991
1023
|
ctx.on("ready", async () => {
|
|
992
|
-
plugin.
|
|
993
|
-
|
|
994
|
-
return config2.apiKeys.
|
|
1024
|
+
const plugin = new ChatLunaPlugin(ctx, config, config.platform);
|
|
1025
|
+
plugin.parseConfig((config2) => {
|
|
1026
|
+
return config2.apiKeys.filter(([apiKey, _, enabled]) => {
|
|
1027
|
+
return apiKey.length > 0 && enabled;
|
|
1028
|
+
}).map(([apiKey, apiEndpoint]) => {
|
|
995
1029
|
return {
|
|
996
1030
|
apiKey,
|
|
997
1031
|
apiEndpoint,
|
|
@@ -1003,8 +1037,8 @@ function apply(ctx, config) {
|
|
|
1003
1037
|
};
|
|
1004
1038
|
});
|
|
1005
1039
|
});
|
|
1006
|
-
plugin.registerClient((
|
|
1007
|
-
await plugin.
|
|
1040
|
+
plugin.registerClient(() => new GeminiClient(ctx, config, plugin));
|
|
1041
|
+
await plugin.initClient();
|
|
1008
1042
|
});
|
|
1009
1043
|
}
|
|
1010
1044
|
__name(apply, "apply");
|
|
@@ -1014,15 +1048,16 @@ var Config4 = Schema.intersect([
|
|
|
1014
1048
|
platform: Schema.string().default("gemini"),
|
|
1015
1049
|
apiKeys: Schema.array(
|
|
1016
1050
|
Schema.tuple([
|
|
1017
|
-
Schema.string().role("secret"),
|
|
1051
|
+
Schema.string().role("secret").default(""),
|
|
1018
1052
|
Schema.string().default(
|
|
1019
1053
|
"https://generativelanguage.googleapis.com/v1beta"
|
|
1020
|
-
)
|
|
1054
|
+
),
|
|
1055
|
+
Schema.boolean().default(true)
|
|
1021
1056
|
])
|
|
1022
|
-
).default([[
|
|
1057
|
+
).default([[]]).role("table")
|
|
1023
1058
|
}),
|
|
1024
1059
|
Schema.object({
|
|
1025
|
-
|
|
1060
|
+
maxContextRatio: Schema.number().min(0).max(1).step(1e-4).role("slider").default(0.35),
|
|
1026
1061
|
temperature: Schema.percent().min(0).max(2).step(0.1).default(1),
|
|
1027
1062
|
googleSearch: Schema.boolean().default(false),
|
|
1028
1063
|
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.21",
|
|
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,21 @@
|
|
|
62
62
|
"adapter"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@
|
|
65
|
+
"@anatine/zod-openapi": "^2.2.8",
|
|
66
|
+
"@chatluna/v1-shared-adapter": "^1.0.14",
|
|
66
67
|
"@langchain/core": "0.3.62",
|
|
68
|
+
"openapi3-ts": "^4.5.0",
|
|
67
69
|
"zod": "3.25.76",
|
|
68
|
-
"zod-to-json-schema": "^3.24.
|
|
70
|
+
"zod-to-json-schema": "^3.24.6"
|
|
69
71
|
},
|
|
70
72
|
"devDependencies": {
|
|
71
73
|
"atsc": "^2.1.0",
|
|
72
|
-
"koishi": "^4.18.
|
|
74
|
+
"koishi": "^4.18.9"
|
|
73
75
|
},
|
|
74
76
|
"peerDependencies": {
|
|
75
|
-
"koishi": "^4.18.
|
|
76
|
-
"koishi-plugin-chatluna": "^1.3.0-alpha.
|
|
77
|
-
"koishi-plugin-chatluna-storage-service": "^0.0.
|
|
77
|
+
"koishi": "^4.18.9",
|
|
78
|
+
"koishi-plugin-chatluna": "^1.3.0-alpha.73",
|
|
79
|
+
"koishi-plugin-chatluna-storage-service": "^0.0.11"
|
|
78
80
|
},
|
|
79
81
|
"peerDependenciesMeta": {
|
|
80
82
|
"koishi-plugin-chatluna-storage-service": {
|