koishi-plugin-chatluna-google-gemini-adapter 1.1.0-beta.2 → 1.1.0-beta.4

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 CHANGED
@@ -56,7 +56,6 @@ var import_error2 = require("koishi-plugin-chatluna/utils/error");
56
56
  // src/requester.ts
57
57
  var import_messages2 = require("@langchain/core/messages");
58
58
  var import_outputs = require("@langchain/core/outputs");
59
- var import_json = require("@streamparser/json");
60
59
  var import_api = require("koishi-plugin-chatluna/llm-core/platform/api");
61
60
  var import_error = require("koishi-plugin-chatluna/utils/error");
62
61
  var import_sse = require("koishi-plugin-chatluna/utils/sse");
@@ -243,14 +242,27 @@ function formatToolsToGeminiAITools(tools, config, model) {
243
242
  }
244
243
  const functions = tools.map(formatToolToGeminiAITool);
245
244
  const result = [];
246
- if (functions.length > 0 && !config.googleSearch) {
245
+ const unsupportedModels = [
246
+ "gemini-1.0",
247
+ "gemini-2.0-flash-lite",
248
+ "gemini-1.5-flash"
249
+ ];
250
+ let googleSearch = config.googleSearch;
251
+ if (functions.length > 0 && !googleSearch) {
247
252
  result.push({
248
253
  functionDeclarations: functions
249
254
  });
250
- } else if (functions.length > 0 && config.googleSearch) {
255
+ } else if (functions.length > 0 && googleSearch) {
251
256
  logger.warn("Google search is enabled, tool calling will be disable.");
257
+ } else if (unsupportedModels.some(
258
+ (unsupportedModel) => model.includes(unsupportedModel)
259
+ ) && googleSearch) {
260
+ logger.warn(
261
+ `The model ${model} does not support google search. google search will be disable.`
262
+ );
263
+ googleSearch = false;
252
264
  }
253
- if (config.googleSearch) {
265
+ if (googleSearch) {
254
266
  if (model.includes("gemini-2")) {
255
267
  result.push({
256
268
  google_search: {}
@@ -336,7 +348,7 @@ var GeminiRequester = class extends import_api.ModelRequester {
336
348
  async *completionStream(params) {
337
349
  try {
338
350
  const response = await this._post(
339
- `models/${params.model}:streamGenerateContent`,
351
+ `models/${params.model}:streamGenerateContent?alt=sse`,
340
352
  {
341
353
  contents: await langchainMessageToGeminiMessage(
342
354
  params.input,
@@ -345,23 +357,23 @@ var GeminiRequester = class extends import_api.ModelRequester {
345
357
  safetySettings: [
346
358
  {
347
359
  category: "HARM_CATEGORY_HARASSMENT",
348
- threshold: "BLOCK_NONE"
360
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
349
361
  },
350
362
  {
351
363
  category: "HARM_CATEGORY_HATE_SPEECH",
352
- threshold: "BLOCK_NONE"
364
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
353
365
  },
354
366
  {
355
367
  category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
356
- threshold: "BLOCK_NONE"
368
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
357
369
  },
358
370
  {
359
371
  category: "HARM_CATEGORY_DANGEROUS_CONTENT",
360
- threshold: "BLOCK_NONE"
372
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
361
373
  },
362
374
  {
363
375
  category: "HARM_CATEGORY_CIVIC_INTEGRITY",
364
- threshold: "BLOCK_NONE"
376
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
365
377
  }
366
378
  ],
367
379
  generationConfig: {
@@ -382,44 +394,41 @@ var GeminiRequester = class extends import_api.ModelRequester {
382
394
  }
383
395
  );
384
396
  let errorCount = 0;
385
- const stream = new TransformStream();
386
- const iterable = (0, import_stream.readableStreamToAsyncIterable)(
387
- stream.readable
388
- );
389
- const jsonParser = new import_json.JSONParser();
390
- const writable = stream.writable.getWriter();
391
397
  let groundingContent = "";
392
398
  let currentGroudingIndex = 0;
393
- jsonParser.onEnd = async () => {
394
- await writable.close();
395
- };
396
- jsonParser.onValue = async ({ value }) => {
397
- const transformValue = value;
398
- if (!transformValue.candidates) {
399
- return;
400
- }
401
- for (const candidate of transformValue.candidates) {
402
- const parts = candidate.content?.parts;
403
- if (parts == null || parts.length < 1) {
404
- throw new Error(JSON.stringify(value));
399
+ await (0, import_sse.checkResponse)(response);
400
+ const readableStream = new ReadableStream({
401
+ async start(controller) {
402
+ for await (const chunk of (0, import_sse.sseIterable)(response)) {
403
+ controller.enqueue(chunk.data);
405
404
  }
406
- for (const part of parts) {
407
- await writable.write(part);
405
+ controller.close();
406
+ }
407
+ });
408
+ const transformToChatPartStream = new TransformStream({
409
+ async transform(chunk, controller) {
410
+ const parsedValue = JSON.parse(chunk);
411
+ const transformValue = parsedValue;
412
+ if (!transformValue.candidates) {
413
+ return;
408
414
  }
409
- for (const source of candidate.groundingMetadata?.groundingChunks ?? []) {
410
- groundingContent += `[^${currentGroudingIndex++}]: [${source.web.title}](${source.web.uri})
415
+ for (const candidate of transformValue.candidates) {
416
+ const parts = candidate.content?.parts;
417
+ if (parts == null || parts.length < 1) {
418
+ throw new Error(chunk);
419
+ }
420
+ for (const part of parts) {
421
+ controller.enqueue(part);
422
+ }
423
+ for (const source of candidate.groundingMetadata?.groundingChunks ?? []) {
424
+ groundingContent += `[^${currentGroudingIndex++}]: [${source.web.title}](${source.web.uri})
411
425
  `;
426
+ }
412
427
  }
413
428
  }
414
- };
415
- await (0, import_sse.checkResponse)(response);
416
- (0, import_sse.sse)(
417
- response,
418
- async (rawData) => {
419
- jsonParser.write(rawData);
420
- return true;
421
- },
422
- 0
429
+ });
430
+ const iterable = (0, import_stream.readableStreamToAsyncIterable)(
431
+ readableStream.pipeThrough(transformToChatPartStream)
423
432
  );
424
433
  let reasoningContent = "";
425
434
  let content = "";
@@ -602,10 +611,15 @@ ${groundingContent}`
602
611
  }
603
612
  _concatUrl(url) {
604
613
  const apiEndPoint = this._config.apiEndpoint;
614
+ let baseURL;
605
615
  if (apiEndPoint.endsWith("/")) {
606
- return apiEndPoint + url + `?key=${this._config.apiKey}`;
616
+ baseURL = new URL(apiEndPoint + url);
617
+ } else {
618
+ baseURL = new URL(apiEndPoint + "/" + url);
607
619
  }
608
- return apiEndPoint + "/" + url + `?key=${this._config.apiKey}`;
620
+ const searchParams = baseURL.searchParams;
621
+ searchParams.set("key", this._config.apiKey);
622
+ return baseURL.toString();
609
623
  }
610
624
  _buildHeaders() {
611
625
  return {
package/lib/index.mjs CHANGED
@@ -40,7 +40,6 @@ import {
40
40
  // src/requester.ts
41
41
  import { AIMessageChunk as AIMessageChunk2 } from "@langchain/core/messages";
42
42
  import { ChatGenerationChunk } from "@langchain/core/outputs";
43
- import { JSONParser } from "@streamparser/json";
44
43
  import {
45
44
  ModelRequester
46
45
  } from "koishi-plugin-chatluna/llm-core/platform/api";
@@ -48,7 +47,7 @@ import {
48
47
  ChatLunaError,
49
48
  ChatLunaErrorCode
50
49
  } from "koishi-plugin-chatluna/utils/error";
51
- import { checkResponse, sse } from "koishi-plugin-chatluna/utils/sse";
50
+ import { checkResponse, sseIterable } from "koishi-plugin-chatluna/utils/sse";
52
51
  import { readableStreamToAsyncIterable } from "koishi-plugin-chatluna/utils/stream";
53
52
 
54
53
  // src/utils.ts
@@ -237,14 +236,27 @@ function formatToolsToGeminiAITools(tools, config, model) {
237
236
  }
238
237
  const functions = tools.map(formatToolToGeminiAITool);
239
238
  const result = [];
240
- if (functions.length > 0 && !config.googleSearch) {
239
+ const unsupportedModels = [
240
+ "gemini-1.0",
241
+ "gemini-2.0-flash-lite",
242
+ "gemini-1.5-flash"
243
+ ];
244
+ let googleSearch = config.googleSearch;
245
+ if (functions.length > 0 && !googleSearch) {
241
246
  result.push({
242
247
  functionDeclarations: functions
243
248
  });
244
- } else if (functions.length > 0 && config.googleSearch) {
249
+ } else if (functions.length > 0 && googleSearch) {
245
250
  logger.warn("Google search is enabled, tool calling will be disable.");
251
+ } else if (unsupportedModels.some(
252
+ (unsupportedModel) => model.includes(unsupportedModel)
253
+ ) && googleSearch) {
254
+ logger.warn(
255
+ `The model ${model} does not support google search. google search will be disable.`
256
+ );
257
+ googleSearch = false;
246
258
  }
247
- if (config.googleSearch) {
259
+ if (googleSearch) {
248
260
  if (model.includes("gemini-2")) {
249
261
  result.push({
250
262
  google_search: {}
@@ -330,7 +342,7 @@ var GeminiRequester = class extends ModelRequester {
330
342
  async *completionStream(params) {
331
343
  try {
332
344
  const response = await this._post(
333
- `models/${params.model}:streamGenerateContent`,
345
+ `models/${params.model}:streamGenerateContent?alt=sse`,
334
346
  {
335
347
  contents: await langchainMessageToGeminiMessage(
336
348
  params.input,
@@ -339,23 +351,23 @@ var GeminiRequester = class extends ModelRequester {
339
351
  safetySettings: [
340
352
  {
341
353
  category: "HARM_CATEGORY_HARASSMENT",
342
- threshold: "BLOCK_NONE"
354
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
343
355
  },
344
356
  {
345
357
  category: "HARM_CATEGORY_HATE_SPEECH",
346
- threshold: "BLOCK_NONE"
358
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
347
359
  },
348
360
  {
349
361
  category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
350
- threshold: "BLOCK_NONE"
362
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
351
363
  },
352
364
  {
353
365
  category: "HARM_CATEGORY_DANGEROUS_CONTENT",
354
- threshold: "BLOCK_NONE"
366
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
355
367
  },
356
368
  {
357
369
  category: "HARM_CATEGORY_CIVIC_INTEGRITY",
358
- threshold: "BLOCK_NONE"
370
+ threshold: params.model.includes("gemini-2.0") ? "OFF" : "BLOCK_NONE"
359
371
  }
360
372
  ],
361
373
  generationConfig: {
@@ -376,44 +388,41 @@ var GeminiRequester = class extends ModelRequester {
376
388
  }
377
389
  );
378
390
  let errorCount = 0;
379
- const stream = new TransformStream();
380
- const iterable = readableStreamToAsyncIterable(
381
- stream.readable
382
- );
383
- const jsonParser = new JSONParser();
384
- const writable = stream.writable.getWriter();
385
391
  let groundingContent = "";
386
392
  let currentGroudingIndex = 0;
387
- jsonParser.onEnd = async () => {
388
- await writable.close();
389
- };
390
- jsonParser.onValue = async ({ value }) => {
391
- const transformValue = value;
392
- if (!transformValue.candidates) {
393
- return;
394
- }
395
- for (const candidate of transformValue.candidates) {
396
- const parts = candidate.content?.parts;
397
- if (parts == null || parts.length < 1) {
398
- throw new Error(JSON.stringify(value));
393
+ await checkResponse(response);
394
+ const readableStream = new ReadableStream({
395
+ async start(controller) {
396
+ for await (const chunk of sseIterable(response)) {
397
+ controller.enqueue(chunk.data);
399
398
  }
400
- for (const part of parts) {
401
- await writable.write(part);
399
+ controller.close();
400
+ }
401
+ });
402
+ const transformToChatPartStream = new TransformStream({
403
+ async transform(chunk, controller) {
404
+ const parsedValue = JSON.parse(chunk);
405
+ const transformValue = parsedValue;
406
+ if (!transformValue.candidates) {
407
+ return;
402
408
  }
403
- for (const source of candidate.groundingMetadata?.groundingChunks ?? []) {
404
- groundingContent += `[^${currentGroudingIndex++}]: [${source.web.title}](${source.web.uri})
409
+ for (const candidate of transformValue.candidates) {
410
+ const parts = candidate.content?.parts;
411
+ if (parts == null || parts.length < 1) {
412
+ throw new Error(chunk);
413
+ }
414
+ for (const part of parts) {
415
+ controller.enqueue(part);
416
+ }
417
+ for (const source of candidate.groundingMetadata?.groundingChunks ?? []) {
418
+ groundingContent += `[^${currentGroudingIndex++}]: [${source.web.title}](${source.web.uri})
405
419
  `;
420
+ }
406
421
  }
407
422
  }
408
- };
409
- await checkResponse(response);
410
- sse(
411
- response,
412
- async (rawData) => {
413
- jsonParser.write(rawData);
414
- return true;
415
- },
416
- 0
423
+ });
424
+ const iterable = readableStreamToAsyncIterable(
425
+ readableStream.pipeThrough(transformToChatPartStream)
417
426
  );
418
427
  let reasoningContent = "";
419
428
  let content = "";
@@ -596,10 +605,15 @@ ${groundingContent}`
596
605
  }
597
606
  _concatUrl(url) {
598
607
  const apiEndPoint = this._config.apiEndpoint;
608
+ let baseURL;
599
609
  if (apiEndPoint.endsWith("/")) {
600
- return apiEndPoint + url + `?key=${this._config.apiKey}`;
610
+ baseURL = new URL(apiEndPoint + url);
611
+ } else {
612
+ baseURL = new URL(apiEndPoint + "/" + url);
601
613
  }
602
- return apiEndPoint + "/" + url + `?key=${this._config.apiKey}`;
614
+ const searchParams = baseURL.searchParams;
615
+ searchParams.set("key", this._config.apiKey);
616
+ return baseURL.toString();
603
617
  }
604
618
  _buildHeaders() {
605
619
  return {
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.1.0-beta.2",
4
+ "version": "1.1.0-beta.4",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "lib/index.mjs",
7
7
  "typings": "lib/index.d.ts",
@@ -63,7 +63,6 @@
63
63
  ],
64
64
  "dependencies": {
65
65
  "@langchain/core": "^0.3.18",
66
- "@streamparser/json": "^0.0.21",
67
66
  "zod": "^3.24.0-canary.20241107T043915",
68
67
  "zod-to-json-schema": "^3.23.5"
69
68
  },
@@ -73,7 +72,7 @@
73
72
  },
74
73
  "peerDependencies": {
75
74
  "koishi": "^4.18.4",
76
- "koishi-plugin-chatluna": "^1.1.0-beta.14"
75
+ "koishi-plugin-chatluna": "^1.1.0-beta.16"
77
76
  },
78
77
  "koishi": {
79
78
  "description": {