@aigne/gemini 0.14.11-beta → 0.14.12-beta

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/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.14.12-beta](https://github.com/AIGNE-io/aigne-framework/compare/gemini-v0.14.11...gemini-v0.14.12-beta) (2025-11-28)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * bump version ([ba7ad18](https://github.com/AIGNE-io/aigne-framework/commit/ba7ad184fcf32b49bf0507a3cb638d20fb00690d))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @aigne/core bumped to 1.69.2-beta
16
+ * @aigne/platform-helpers bumped to 0.6.5-beta
17
+ * devDependencies
18
+ * @aigne/test-utils bumped to 0.5.65-beta
19
+
20
+ ## [0.14.11](https://github.com/AIGNE-io/aigne-framework/compare/gemini-v0.14.11-beta.1...gemini-v0.14.11) (2025-11-28)
21
+
22
+
23
+ ### Dependencies
24
+
25
+ * The following workspace dependencies were updated
26
+ * dependencies
27
+ * @aigne/core bumped to 1.69.1
28
+ * devDependencies
29
+ * @aigne/test-utils bumped to 0.5.64
30
+
31
+ ## [0.14.11-beta.1](https://github.com/AIGNE-io/aigne-framework/compare/gemini-v0.14.11-beta...gemini-v0.14.11-beta.1) (2025-11-26)
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * **model:** handle large video files by uploading to Files API ([#769](https://github.com/AIGNE-io/aigne-framework/issues/769)) ([5fd7661](https://github.com/AIGNE-io/aigne-framework/commit/5fd76613bd7301cc76bde933de2095a6d86f8c7e))
37
+
3
38
  ## [0.14.11-beta](https://github.com/AIGNE-io/aigne-framework/compare/gemini-v0.14.10...gemini-v0.14.11-beta) (2025-11-24)
4
39
 
5
40
 
@@ -104,6 +104,7 @@ export declare class GeminiChatModel extends ChatModel {
104
104
  private processInput;
105
105
  private buildConfig;
106
106
  private buildTools;
107
+ private buildVideoContentParts;
107
108
  private buildContents;
108
109
  private ensureMessagesHasUserMessage;
109
110
  }
@@ -5,12 +5,14 @@ const core_1 = require("@aigne/core");
5
5
  const logger_js_1 = require("@aigne/core/utils/logger.js");
6
6
  const model_utils_js_1 = require("@aigne/core/utils/model-utils.js");
7
7
  const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
8
+ const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
8
9
  const uuid_1 = require("@aigne/uuid");
9
10
  const genai_1 = require("@google/genai");
10
11
  const zod_1 = require("zod");
11
12
  const zod_to_json_schema_1 = require("zod-to-json-schema");
12
13
  const GEMINI_DEFAULT_CHAT_MODEL = "gemini-2.0-flash";
13
14
  const OUTPUT_FUNCTION_NAME = "output";
15
+ const NEED_UPLOAD_MAX_FILE_SIZE_MB = 20;
14
16
  /**
15
17
  * Implementation of the ChatModel interface for Google's Gemini API
16
18
  *
@@ -64,7 +66,7 @@ class GeminiChatModel extends core_1.ChatModel {
64
66
  // References: https://ai.google.dev/gemini-api/docs/thinking#set-budget
65
67
  thinkingBudgetModelMap = [
66
68
  {
67
- pattern: /gemini-3/,
69
+ pattern: /gemini-3(?!.*-image-)/,
68
70
  support: true,
69
71
  type: "level",
70
72
  },
@@ -130,7 +132,7 @@ class GeminiChatModel extends core_1.ChatModel {
130
132
  async *processInput(input, options) {
131
133
  const modelOptions = await this.getModelOptions(input, options);
132
134
  const model = modelOptions.model || this.credential.model;
133
- const { contents, config } = await this.buildContents(input);
135
+ const { contents, config } = await this.buildContents(input, options);
134
136
  const thinkingBudget = this.getThinkingBudget(model, modelOptions.reasoningEffort);
135
137
  const parameters = {
136
138
  model,
@@ -348,7 +350,39 @@ class GeminiChatModel extends core_1.ChatModel {
348
350
  };
349
351
  return { tools, toolConfig: { functionCallingConfig } };
350
352
  }
351
- async buildContents(input) {
353
+ async buildVideoContentParts(media, options) {
354
+ const { path: filePath, mimeType: fileMimeType } = await this.transformFileType("local", media, options);
355
+ if (filePath) {
356
+ const stats = await index_js_1.nodejs.fs.stat(filePath);
357
+ const fileSizeInBytes = stats.size;
358
+ const fileSizeMB = fileSizeInBytes / (1024 * 1024);
359
+ if (fileSizeMB > NEED_UPLOAD_MAX_FILE_SIZE_MB) {
360
+ const uploadedFile = await this.googleClient.files.upload({
361
+ file: filePath,
362
+ config: { mimeType: fileMimeType },
363
+ });
364
+ let file = uploadedFile;
365
+ while (file.state === "PROCESSING") {
366
+ await new Promise((resolve) => setTimeout(resolve, 1000));
367
+ if (file.name) {
368
+ file = await this.googleClient.files.get({ name: file.name });
369
+ }
370
+ }
371
+ if (file.state !== "ACTIVE") {
372
+ throw new Error(`File ${file.name} failed to process: ${file.state}`);
373
+ }
374
+ if (file.uri && file.mimeType) {
375
+ const result = (0, genai_1.createUserContent)([(0, genai_1.createPartFromUri)(file.uri, file.mimeType), ""]);
376
+ const part = result.parts?.find((x) => x.fileData);
377
+ if (part) {
378
+ await index_js_1.nodejs.fs.rm(filePath);
379
+ return part;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ async buildContents(input, options) {
352
386
  const result = {
353
387
  contents: [],
354
388
  };
@@ -431,8 +465,12 @@ class GeminiChatModel extends core_1.ChatModel {
431
465
  return { text: item.text };
432
466
  case "url":
433
467
  return { fileData: { fileUri: item.url, mimeType: item.mimeType } };
434
- case "file":
468
+ case "file": {
469
+ const part = await this.buildVideoContentParts(item, options);
470
+ if (part)
471
+ return part;
435
472
  return { inlineData: { data: item.data, mimeType: item.mimeType } };
473
+ }
436
474
  case "local":
437
475
  throw new Error(`Unsupported local file: ${item.path}, it should be converted to base64 at ChatModel`);
438
476
  }
@@ -104,6 +104,7 @@ export declare class GeminiChatModel extends ChatModel {
104
104
  private processInput;
105
105
  private buildConfig;
106
106
  private buildTools;
107
+ private buildVideoContentParts;
107
108
  private buildContents;
108
109
  private ensureMessagesHasUserMessage;
109
110
  }
@@ -104,6 +104,7 @@ export declare class GeminiChatModel extends ChatModel {
104
104
  private processInput;
105
105
  private buildConfig;
106
106
  private buildTools;
107
+ private buildVideoContentParts;
107
108
  private buildContents;
108
109
  private ensureMessagesHasUserMessage;
109
110
  }
@@ -2,12 +2,14 @@ import { agentProcessResultToObject, ChatModel, StructuredOutputError, safeParse
2
2
  import { logger } from "@aigne/core/utils/logger.js";
3
3
  import { mergeUsage } from "@aigne/core/utils/model-utils.js";
4
4
  import { isNonNullable } from "@aigne/core/utils/type-utils.js";
5
+ import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
5
6
  import { v7 } from "@aigne/uuid";
6
- import { FunctionCallingConfigMode, GoogleGenAI, ThinkingLevel, } from "@google/genai";
7
+ import { createPartFromUri, createUserContent, FunctionCallingConfigMode, GoogleGenAI, ThinkingLevel, } from "@google/genai";
7
8
  import { z } from "zod";
8
9
  import { zodToJsonSchema } from "zod-to-json-schema";
9
10
  const GEMINI_DEFAULT_CHAT_MODEL = "gemini-2.0-flash";
10
11
  const OUTPUT_FUNCTION_NAME = "output";
12
+ const NEED_UPLOAD_MAX_FILE_SIZE_MB = 20;
11
13
  /**
12
14
  * Implementation of the ChatModel interface for Google's Gemini API
13
15
  *
@@ -61,7 +63,7 @@ export class GeminiChatModel extends ChatModel {
61
63
  // References: https://ai.google.dev/gemini-api/docs/thinking#set-budget
62
64
  thinkingBudgetModelMap = [
63
65
  {
64
- pattern: /gemini-3/,
66
+ pattern: /gemini-3(?!.*-image-)/,
65
67
  support: true,
66
68
  type: "level",
67
69
  },
@@ -127,7 +129,7 @@ export class GeminiChatModel extends ChatModel {
127
129
  async *processInput(input, options) {
128
130
  const modelOptions = await this.getModelOptions(input, options);
129
131
  const model = modelOptions.model || this.credential.model;
130
- const { contents, config } = await this.buildContents(input);
132
+ const { contents, config } = await this.buildContents(input, options);
131
133
  const thinkingBudget = this.getThinkingBudget(model, modelOptions.reasoningEffort);
132
134
  const parameters = {
133
135
  model,
@@ -345,7 +347,39 @@ export class GeminiChatModel extends ChatModel {
345
347
  };
346
348
  return { tools, toolConfig: { functionCallingConfig } };
347
349
  }
348
- async buildContents(input) {
350
+ async buildVideoContentParts(media, options) {
351
+ const { path: filePath, mimeType: fileMimeType } = await this.transformFileType("local", media, options);
352
+ if (filePath) {
353
+ const stats = await nodejs.fs.stat(filePath);
354
+ const fileSizeInBytes = stats.size;
355
+ const fileSizeMB = fileSizeInBytes / (1024 * 1024);
356
+ if (fileSizeMB > NEED_UPLOAD_MAX_FILE_SIZE_MB) {
357
+ const uploadedFile = await this.googleClient.files.upload({
358
+ file: filePath,
359
+ config: { mimeType: fileMimeType },
360
+ });
361
+ let file = uploadedFile;
362
+ while (file.state === "PROCESSING") {
363
+ await new Promise((resolve) => setTimeout(resolve, 1000));
364
+ if (file.name) {
365
+ file = await this.googleClient.files.get({ name: file.name });
366
+ }
367
+ }
368
+ if (file.state !== "ACTIVE") {
369
+ throw new Error(`File ${file.name} failed to process: ${file.state}`);
370
+ }
371
+ if (file.uri && file.mimeType) {
372
+ const result = createUserContent([createPartFromUri(file.uri, file.mimeType), ""]);
373
+ const part = result.parts?.find((x) => x.fileData);
374
+ if (part) {
375
+ await nodejs.fs.rm(filePath);
376
+ return part;
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ async buildContents(input, options) {
349
383
  const result = {
350
384
  contents: [],
351
385
  };
@@ -428,8 +462,12 @@ export class GeminiChatModel extends ChatModel {
428
462
  return { text: item.text };
429
463
  case "url":
430
464
  return { fileData: { fileUri: item.url, mimeType: item.mimeType } };
431
- case "file":
465
+ case "file": {
466
+ const part = await this.buildVideoContentParts(item, options);
467
+ if (part)
468
+ return part;
432
469
  return { inlineData: { data: item.data, mimeType: item.mimeType } };
470
+ }
433
471
  case "local":
434
472
  throw new Error(`Unsupported local file: ${item.path}, it should be converted to base64 at ChatModel`);
435
473
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/gemini",
3
- "version": "0.14.11-beta",
3
+ "version": "0.14.12-beta",
4
4
  "description": "AIGNE Gemini SDK for integrating with Google's Gemini AI models",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -39,8 +39,8 @@
39
39
  "@google/genai": "^1.30.0",
40
40
  "zod": "^3.25.67",
41
41
  "zod-to-json-schema": "^3.24.6",
42
- "@aigne/core": "^1.69.1-beta",
43
- "@aigne/platform-helpers": "^0.6.4"
42
+ "@aigne/core": "^1.69.2-beta",
43
+ "@aigne/platform-helpers": "^0.6.5-beta"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/bun": "^1.2.22",
@@ -48,7 +48,7 @@
48
48
  "npm-run-all": "^4.1.5",
49
49
  "rimraf": "^6.0.1",
50
50
  "typescript": "^5.9.2",
51
- "@aigne/test-utils": "^0.5.64-beta"
51
+ "@aigne/test-utils": "^0.5.65-beta"
52
52
  },
53
53
  "scripts": {
54
54
  "lint": "tsc --noEmit",