koishi-plugin-chatluna-google-gemini-adapter 1.2.23 → 1.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +44 -27
- package/lib/index.d.ts +4 -1
- package/lib/index.mjs +44 -32
- package/lib/utils.d.ts +1 -2
- package/package.json +12 -7
package/lib/index.cjs
CHANGED
|
@@ -55,7 +55,7 @@ var import_types = 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
|
|
58
|
-
var
|
|
58
|
+
var import_messages = require("@langchain/core/messages");
|
|
59
59
|
var import_outputs = require("@langchain/core/outputs");
|
|
60
60
|
var import_api = require("koishi-plugin-chatluna/llm-core/platform/api");
|
|
61
61
|
var import_error = require("koishi-plugin-chatluna/utils/error");
|
|
@@ -63,10 +63,10 @@ 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_messages = require("@langchain/core/messages");
|
|
67
66
|
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
68
67
|
var import_v1_shared_adapter = require("@chatluna/v1-shared-adapter");
|
|
69
68
|
var import_string = require("koishi-plugin-chatluna/utils/string");
|
|
69
|
+
var import_zod = require("zod");
|
|
70
70
|
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
71
71
|
return Promise.all(
|
|
72
72
|
messages.map(async (message) => {
|
|
@@ -90,23 +90,21 @@ async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
|
90
90
|
}
|
|
91
91
|
__name(langchainMessageToGeminiMessage, "langchainMessageToGeminiMessage");
|
|
92
92
|
function extractSystemMessages(messages) {
|
|
93
|
-
let
|
|
93
|
+
let lastSystemMessageIndex = -1;
|
|
94
94
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
95
95
|
if (messages[i].role === "system") {
|
|
96
|
-
|
|
96
|
+
lastSystemMessageIndex = i;
|
|
97
97
|
break;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
if (
|
|
100
|
+
if (lastSystemMessageIndex === -1) {
|
|
101
101
|
return [void 0, messages];
|
|
102
102
|
}
|
|
103
103
|
const systemMessages = messages.slice(
|
|
104
104
|
0,
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
const modelMessages = messages.slice(
|
|
108
|
-
messages.indexOf(lastSystemMessage) + 1
|
|
105
|
+
Math.max(1, lastSystemMessageIndex)
|
|
109
106
|
);
|
|
107
|
+
const modelMessages = messages.slice(lastSystemMessageIndex + 1);
|
|
110
108
|
return [
|
|
111
109
|
{
|
|
112
110
|
role: "user",
|
|
@@ -296,10 +294,9 @@ function formatToolsToGeminiAITools(tools, config, model) {
|
|
|
296
294
|
__name(formatToolsToGeminiAITools, "formatToolsToGeminiAITools");
|
|
297
295
|
function formatToolToGeminiAITool(tool) {
|
|
298
296
|
const parameters = (0, import_v1_shared_adapter.removeAdditionalProperties)(
|
|
299
|
-
|
|
300
|
-
(0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema, {
|
|
297
|
+
tool.schema instanceof import_zod.ZodSchema ? (0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema, {
|
|
301
298
|
allowedAdditionalProperties: void 0
|
|
302
|
-
})
|
|
299
|
+
}) : tool.schema
|
|
303
300
|
);
|
|
304
301
|
return {
|
|
305
302
|
name: tool.name,
|
|
@@ -441,15 +438,16 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
441
438
|
return;
|
|
442
439
|
}
|
|
443
440
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
441
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
442
|
+
params,
|
|
443
|
+
this._plugin,
|
|
444
|
+
modelConfig,
|
|
445
|
+
this._pluginConfig
|
|
446
|
+
);
|
|
444
447
|
try {
|
|
445
448
|
const response = await this._post(
|
|
446
449
|
`models/${modelConfig.model}:streamGenerateContent?alt=sse`,
|
|
447
|
-
|
|
448
|
-
params,
|
|
449
|
-
this._plugin,
|
|
450
|
-
modelConfig,
|
|
451
|
-
this._pluginConfig
|
|
452
|
-
),
|
|
450
|
+
chatGenerationParams,
|
|
453
451
|
{
|
|
454
452
|
signal: params.signal
|
|
455
453
|
}
|
|
@@ -716,7 +714,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
716
714
|
};
|
|
717
715
|
for await (const chunk of iterable) {
|
|
718
716
|
try {
|
|
719
|
-
const { updatedContent, updatedReasoning } = this._processChunk(
|
|
717
|
+
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
720
718
|
chunk,
|
|
721
719
|
reasoningContent,
|
|
722
720
|
functionCall
|
|
@@ -730,7 +728,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
730
728
|
const messageChunk = this._createMessageChunk(
|
|
731
729
|
updatedContent,
|
|
732
730
|
functionCall,
|
|
733
|
-
partAsTypeCheck(
|
|
731
|
+
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
734
732
|
chunk,
|
|
735
733
|
(part) => part["inlineData"] != null
|
|
736
734
|
)
|
|
@@ -755,7 +753,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
755
753
|
}
|
|
756
754
|
}
|
|
757
755
|
}
|
|
758
|
-
_processChunk(chunk, reasoningContent, functionCall) {
|
|
756
|
+
async _processChunk(chunk, reasoningContent, functionCall) {
|
|
759
757
|
const messagePart = partAsType(chunk);
|
|
760
758
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
761
759
|
const imagePart = partAsTypeCheck(
|
|
@@ -772,8 +770,24 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
772
770
|
}
|
|
773
771
|
messageContent = messagePart.text;
|
|
774
772
|
} else if (imagePart) {
|
|
775
|
-
|
|
776
|
-
|
|
773
|
+
const storageService = this.ctx.chatluna_storage;
|
|
774
|
+
if (!storageService) {
|
|
775
|
+
messagePart.text = ``;
|
|
776
|
+
messageContent = messagePart.text;
|
|
777
|
+
} else {
|
|
778
|
+
const buffer = Buffer.from(imagePart.inlineData.data, "base64");
|
|
779
|
+
const file = await storageService.createTempFile(
|
|
780
|
+
buffer,
|
|
781
|
+
"image_random"
|
|
782
|
+
);
|
|
783
|
+
messagePart.text = "[image]";
|
|
784
|
+
messageContent = [
|
|
785
|
+
{
|
|
786
|
+
type: "image_url",
|
|
787
|
+
image_url: file.url
|
|
788
|
+
}
|
|
789
|
+
];
|
|
790
|
+
}
|
|
777
791
|
}
|
|
778
792
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
779
793
|
if (deltaFunctionCall) {
|
|
@@ -808,7 +822,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
|
|
|
808
822
|
if (groundingContent.length > 0) {
|
|
809
823
|
logger.debug(`grounding content: ${groundingContent}`);
|
|
810
824
|
if (this._pluginConfig.groundingContentDisplay) {
|
|
811
|
-
const groundingMessage = new
|
|
825
|
+
const groundingMessage = new import_messages.AIMessageChunk(
|
|
812
826
|
`
|
|
813
827
|
${groundingContent}`
|
|
814
828
|
);
|
|
@@ -821,8 +835,8 @@ ${groundingContent}`
|
|
|
821
835
|
}
|
|
822
836
|
}
|
|
823
837
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
824
|
-
const messageChunk = new
|
|
825
|
-
content
|
|
838
|
+
const messageChunk = new import_messages.AIMessageChunk({
|
|
839
|
+
content: content ?? ""
|
|
826
840
|
});
|
|
827
841
|
messageChunk.additional_kwargs = {
|
|
828
842
|
function_call: functionCall.name.length > 0 ? {
|
|
@@ -1028,7 +1042,10 @@ var Config4 = import_koishi.Schema.intersect([
|
|
|
1028
1042
|
"en-US": require_en_US_schema()
|
|
1029
1043
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1030
1044
|
});
|
|
1031
|
-
var inject =
|
|
1045
|
+
var inject = {
|
|
1046
|
+
required: ["chatluna"],
|
|
1047
|
+
optional: ["chatluna_storage"]
|
|
1048
|
+
};
|
|
1032
1049
|
var name = "chatluna-google-gemini-adapter";
|
|
1033
1050
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1034
1051
|
0 && (module.exports = {
|
package/lib/index.d.ts
CHANGED
|
@@ -19,5 +19,8 @@ export interface Config extends ChatLunaPlugin.Config {
|
|
|
19
19
|
includeThoughts: boolean;
|
|
20
20
|
}
|
|
21
21
|
export declare const Config: Schema<Config>;
|
|
22
|
-
export declare const inject:
|
|
22
|
+
export declare const inject: {
|
|
23
|
+
required: string[];
|
|
24
|
+
optional: string[];
|
|
25
|
+
};
|
|
23
26
|
export declare const name = "chatluna-google-gemini-adapter";
|
package/lib/index.mjs
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
|
|
41
41
|
// src/requester.ts
|
|
42
42
|
import {
|
|
43
|
-
AIMessageChunk
|
|
43
|
+
AIMessageChunk
|
|
44
44
|
} from "@langchain/core/messages";
|
|
45
45
|
import { ChatGenerationChunk } from "@langchain/core/outputs";
|
|
46
46
|
import {
|
|
@@ -54,12 +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 {
|
|
58
|
-
AIMessageChunk,
|
|
59
|
-
ChatMessageChunk,
|
|
60
|
-
HumanMessageChunk,
|
|
61
|
-
SystemMessageChunk
|
|
62
|
-
} from "@langchain/core/messages";
|
|
63
57
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
64
58
|
import {
|
|
65
59
|
fetchImageUrl,
|
|
@@ -69,6 +63,7 @@ import {
|
|
|
69
63
|
isMessageContentImageUrl,
|
|
70
64
|
isMessageContentText
|
|
71
65
|
} from "koishi-plugin-chatluna/utils/string";
|
|
66
|
+
import { ZodSchema } from "zod";
|
|
72
67
|
async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
73
68
|
return Promise.all(
|
|
74
69
|
messages.map(async (message) => {
|
|
@@ -92,23 +87,21 @@ async function langchainMessageToGeminiMessage(messages, plugin, model) {
|
|
|
92
87
|
}
|
|
93
88
|
__name(langchainMessageToGeminiMessage, "langchainMessageToGeminiMessage");
|
|
94
89
|
function extractSystemMessages(messages) {
|
|
95
|
-
let
|
|
90
|
+
let lastSystemMessageIndex = -1;
|
|
96
91
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
97
92
|
if (messages[i].role === "system") {
|
|
98
|
-
|
|
93
|
+
lastSystemMessageIndex = i;
|
|
99
94
|
break;
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
|
-
if (
|
|
97
|
+
if (lastSystemMessageIndex === -1) {
|
|
103
98
|
return [void 0, messages];
|
|
104
99
|
}
|
|
105
100
|
const systemMessages = messages.slice(
|
|
106
101
|
0,
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
const modelMessages = messages.slice(
|
|
110
|
-
messages.indexOf(lastSystemMessage) + 1
|
|
102
|
+
Math.max(1, lastSystemMessageIndex)
|
|
111
103
|
);
|
|
104
|
+
const modelMessages = messages.slice(lastSystemMessageIndex + 1);
|
|
112
105
|
return [
|
|
113
106
|
{
|
|
114
107
|
role: "user",
|
|
@@ -298,10 +291,9 @@ function formatToolsToGeminiAITools(tools, config, model) {
|
|
|
298
291
|
__name(formatToolsToGeminiAITools, "formatToolsToGeminiAITools");
|
|
299
292
|
function formatToolToGeminiAITool(tool) {
|
|
300
293
|
const parameters = removeAdditionalProperties(
|
|
301
|
-
|
|
302
|
-
zodToJsonSchema(tool.schema, {
|
|
294
|
+
tool.schema instanceof ZodSchema ? zodToJsonSchema(tool.schema, {
|
|
303
295
|
allowedAdditionalProperties: void 0
|
|
304
|
-
})
|
|
296
|
+
}) : tool.schema
|
|
305
297
|
);
|
|
306
298
|
return {
|
|
307
299
|
name: tool.name,
|
|
@@ -443,15 +435,16 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
443
435
|
return;
|
|
444
436
|
}
|
|
445
437
|
const modelConfig = prepareModelConfig(params, this._pluginConfig);
|
|
438
|
+
const chatGenerationParams = await createChatGenerationParams(
|
|
439
|
+
params,
|
|
440
|
+
this._plugin,
|
|
441
|
+
modelConfig,
|
|
442
|
+
this._pluginConfig
|
|
443
|
+
);
|
|
446
444
|
try {
|
|
447
445
|
const response = await this._post(
|
|
448
446
|
`models/${modelConfig.model}:streamGenerateContent?alt=sse`,
|
|
449
|
-
|
|
450
|
-
params,
|
|
451
|
-
this._plugin,
|
|
452
|
-
modelConfig,
|
|
453
|
-
this._pluginConfig
|
|
454
|
-
),
|
|
447
|
+
chatGenerationParams,
|
|
455
448
|
{
|
|
456
449
|
signal: params.signal
|
|
457
450
|
}
|
|
@@ -718,7 +711,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
718
711
|
};
|
|
719
712
|
for await (const chunk of iterable) {
|
|
720
713
|
try {
|
|
721
|
-
const { updatedContent, updatedReasoning } = this._processChunk(
|
|
714
|
+
const { updatedContent, updatedReasoning } = await this._processChunk(
|
|
722
715
|
chunk,
|
|
723
716
|
reasoningContent,
|
|
724
717
|
functionCall
|
|
@@ -732,7 +725,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
732
725
|
const messageChunk = this._createMessageChunk(
|
|
733
726
|
updatedContent,
|
|
734
727
|
functionCall,
|
|
735
|
-
partAsTypeCheck(
|
|
728
|
+
this.ctx.chatluna_storage != null ? void 0 : partAsTypeCheck(
|
|
736
729
|
chunk,
|
|
737
730
|
(part) => part["inlineData"] != null
|
|
738
731
|
)
|
|
@@ -757,7 +750,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
757
750
|
}
|
|
758
751
|
}
|
|
759
752
|
}
|
|
760
|
-
_processChunk(chunk, reasoningContent, functionCall) {
|
|
753
|
+
async _processChunk(chunk, reasoningContent, functionCall) {
|
|
761
754
|
const messagePart = partAsType(chunk);
|
|
762
755
|
const chatFunctionCallingPart = partAsType(chunk);
|
|
763
756
|
const imagePart = partAsTypeCheck(
|
|
@@ -774,8 +767,24 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
774
767
|
}
|
|
775
768
|
messageContent = messagePart.text;
|
|
776
769
|
} else if (imagePart) {
|
|
777
|
-
|
|
778
|
-
|
|
770
|
+
const storageService = this.ctx.chatluna_storage;
|
|
771
|
+
if (!storageService) {
|
|
772
|
+
messagePart.text = ``;
|
|
773
|
+
messageContent = messagePart.text;
|
|
774
|
+
} else {
|
|
775
|
+
const buffer = Buffer.from(imagePart.inlineData.data, "base64");
|
|
776
|
+
const file = await storageService.createTempFile(
|
|
777
|
+
buffer,
|
|
778
|
+
"image_random"
|
|
779
|
+
);
|
|
780
|
+
messagePart.text = "[image]";
|
|
781
|
+
messageContent = [
|
|
782
|
+
{
|
|
783
|
+
type: "image_url",
|
|
784
|
+
image_url: file.url
|
|
785
|
+
}
|
|
786
|
+
];
|
|
787
|
+
}
|
|
779
788
|
}
|
|
780
789
|
const deltaFunctionCall = chatFunctionCallingPart?.functionCall;
|
|
781
790
|
if (deltaFunctionCall) {
|
|
@@ -810,7 +819,7 @@ var GeminiRequester = class extends ModelRequester {
|
|
|
810
819
|
if (groundingContent.length > 0) {
|
|
811
820
|
logger.debug(`grounding content: ${groundingContent}`);
|
|
812
821
|
if (this._pluginConfig.groundingContentDisplay) {
|
|
813
|
-
const groundingMessage = new
|
|
822
|
+
const groundingMessage = new AIMessageChunk(
|
|
814
823
|
`
|
|
815
824
|
${groundingContent}`
|
|
816
825
|
);
|
|
@@ -823,8 +832,8 @@ ${groundingContent}`
|
|
|
823
832
|
}
|
|
824
833
|
}
|
|
825
834
|
_createMessageChunk(content, functionCall, imagePart) {
|
|
826
|
-
const messageChunk = new
|
|
827
|
-
content
|
|
835
|
+
const messageChunk = new AIMessageChunk({
|
|
836
|
+
content: content ?? ""
|
|
828
837
|
});
|
|
829
838
|
messageChunk.additional_kwargs = {
|
|
830
839
|
function_call: functionCall.name.length > 0 ? {
|
|
@@ -1030,7 +1039,10 @@ var Config4 = Schema.intersect([
|
|
|
1030
1039
|
"en-US": require_en_US_schema()
|
|
1031
1040
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1032
1041
|
});
|
|
1033
|
-
var inject =
|
|
1042
|
+
var inject = {
|
|
1043
|
+
required: ["chatluna"],
|
|
1044
|
+
optional: ["chatluna_storage"]
|
|
1045
|
+
};
|
|
1034
1046
|
var name = "chatluna-google-gemini-adapter";
|
|
1035
1047
|
export {
|
|
1036
1048
|
Config4 as Config,
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseMessage, MessageType } from '@langchain/core/messages';
|
|
2
2
|
import { StructuredTool } from '@langchain/core/tools';
|
|
3
3
|
import { ChatCompletionFunction, ChatCompletionResponseMessage, ChatCompletionResponseMessageRoleEnum, ChatPart, ChatResponse } from './types';
|
|
4
4
|
import { Config } from '.';
|
|
@@ -52,5 +52,4 @@ export declare function createChatGenerationParams(params: ModelRequestParams, p
|
|
|
52
52
|
system_instruction: ChatCompletionResponseMessage;
|
|
53
53
|
tools: Record<string, any>;
|
|
54
54
|
}>;
|
|
55
|
-
export declare function convertDeltaToMessageChunk(delta: Record<string, any>, defaultRole?: ChatCompletionResponseMessageRoleEnum): HumanMessageChunk | AIMessageChunk | SystemMessageChunk | ChatMessageChunk;
|
|
56
55
|
export declare function isChatResponse(response: any): response is 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.
|
|
4
|
+
"version": "1.3.0-alpha.1",
|
|
5
5
|
"main": "lib/index.cjs",
|
|
6
6
|
"module": "lib/index.mjs",
|
|
7
7
|
"typings": "lib/index.d.ts",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"build": "atsc -b"
|
|
34
34
|
},
|
|
35
35
|
"resolutions": {
|
|
36
|
-
"@langchain/core": "0.3.
|
|
36
|
+
"@langchain/core": "0.3.62",
|
|
37
37
|
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.19"
|
|
38
38
|
},
|
|
39
39
|
"overrides": {
|
|
40
|
-
"@langchain/core": "0.3.
|
|
40
|
+
"@langchain/core": "0.3.62",
|
|
41
41
|
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.19"
|
|
42
42
|
},
|
|
43
43
|
"pnpm": {
|
|
44
44
|
"overrides": {
|
|
45
|
-
"@langchain/core": "0.3.
|
|
45
|
+
"@langchain/core": "0.3.62",
|
|
46
46
|
"js-tiktoken": "npm:@dingyi222666/js-tiktoken@^1.0.19"
|
|
47
47
|
}
|
|
48
48
|
},
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"adapter"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@chatluna/v1-shared-adapter": "^1.0.
|
|
66
|
-
"@langchain/core": "
|
|
65
|
+
"@chatluna/v1-shared-adapter": "^1.0.6",
|
|
66
|
+
"@langchain/core": "0.3.62",
|
|
67
67
|
"zod": "^3.25.0-canary.20250211T214501",
|
|
68
68
|
"zod-to-json-schema": "^3.24.5"
|
|
69
69
|
},
|
|
@@ -73,9 +73,14 @@
|
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"koishi": "^4.18.7",
|
|
76
|
-
"koishi-plugin-chatluna": "^1.3.0-alpha.
|
|
76
|
+
"koishi-plugin-chatluna": "^1.3.0-alpha.26",
|
|
77
77
|
"koishi-plugin-chatluna-storage-service": "^0.0.8"
|
|
78
78
|
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"koishi-plugin-chatluna-storage-service": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
79
84
|
"koishi": {
|
|
80
85
|
"description": {
|
|
81
86
|
"zh": "ChatLuna 的 Google Gemini 适配器",
|