Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove coupling to 'fs' for GoogleAIFileManager.uploadFile #327

Open
evanlesmez opened this issue Jan 22, 2025 · 4 comments
Open

Remove coupling to 'fs' for GoogleAIFileManager.uploadFile #327

evanlesmez opened this issue Jan 22, 2025 · 4 comments
Assignees
Labels
component:js sdk Issue/PR related to JavaScript SDK status: awaiting user response Awaiting a response from the author status:triaged Issue/PR triaged to the corresponding sub-team type:feature request New feature request/enhancement

Comments

@evanlesmez
Copy link

Description of the feature request:

The following line breaks the file manager client's upload file in browser environment where 'fs' is not present.

const file = readFileSync(filePath);

Seems limiting to force access to actual file system.

What problem are you trying to solve with this feature?

No response

Any other information you'd like to share?

No response

@BrianHung
Copy link

Agreed, we should change it to uploadLocalFile versus uploadFile to work with non-node.js runtimes like workers.

@gmKeshari gmKeshari added type:feature request New feature request/enhancement status:triaged Issue/PR triaged to the corresponding sub-team component:js sdk Issue/PR related to JavaScript SDK labels Jan 30, 2025
@hkt74 hkt74 assigned hkt74 and unassigned hsubox76 Feb 26, 2025
@hkt74
Copy link
Collaborator

hkt74 commented Feb 26, 2025

Hi @evanlesmez

Thanks for bringing this issue to our attention. Just to clarify: the code in src/server is currently designed for Node environment only, which is why it uses the fs module (not compatible with web browsers).

Are you requesting that we add support for the Web File API to enable this functionality in web environments?

@evanlesmez
Copy link
Author

Hi @evanlesmez

Thanks for bringing this issue to our attention. Just to clarify: the code in src/server is currently designed for Node environment only, which is why it uses the fs module (not compatible with web browsers).

Are you requesting that we add support for the Web File API to enable this functionality in web environments?

I am not requesting integration of the Web File API.
If there was simply a method that could accept a buffer rather than the file path that would be nice.
Or if not that, making it so that the package does not break bundling builds for the browser as a target.

My example project used Vite for bundling.
When I tried building I got the following error:

vite v6.0.7 building for production...
[plugin:vite:resolve] [plugin vite:resolve] Module "fs" has been externalized for browser compatibility, imported by "/home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/@google/generative-ai/dist/server/index.mjs".  
See https://vite.dev/guide/troubleshooting.html#module-externalized-for-browser-compatibility for more details.
✓ 15 modules transformed.
x Build failed in 602ms
error during build:
node_modules/@google/generative-ai/dist/server/index.mjs (1:9): "readFileSync" is not exported by "__vite-browser-external", imported by "node_modules/@google/generative-ai/dist/server/index.mjs".

1: import { readFileSync } from 'fs';
            ^
2:
3: /**

    at getRollupError (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/parseAst.js:396:41)
    at error (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/parseAst.js:392:42)
    at Module.error (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:15715:16)
    at Module.traceVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:16164:29)
    at ModuleScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:13886:39)
    at ChildScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:5305:38)
    at ClassBodyScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:5305:38)
    at ChildScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:5305:38)
    at ChildScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:5305:38)
    at FunctionScope.findVariable (file:///home/evylz/AnimalEq/experimental/animal-abuse-investigator-ai/node_modules/rollup/dist/es/shared/node-entry.js:5305:38)

My solution was to override the fs module with a mock to avoid vite build errors complaining about fs.

// vite.config.ts
import { defineConfig } from 'vite'
import path from 'path'

// https://vite.dev/config/
export default defineConfig({
  plugins: [preact()],
  resolve: {
    alias: {
      fs: path.resolve(__dirname, 'src/mocks/fs.js')
    }
  }
})
// src/mocks/fs.js
export const readFileSync = () => "";

I also adapted the example from the rest API in Gemini vision documentation into Javascript passing an array buffer from the file reader browser API.

  async function uploadGoogleAIFileManager(buffer, file, displayName) {
    const mimeType = file.type;
    const numBytes = file.size;

    // Start resumable upload
    const startRes = await fetch(
      `${BASE_URL}/upload/v1beta/files?key=${apiKey}`,
      {
        method: "POST",
        headers: {
          "X-Goog-Upload-Protocol": "resumable",
          "X-Goog-Upload-Command": "start",
          "X-Goog-Upload-Header-Content-Length": numBytes,
          "X-Goog-Upload-Header-Content-Type": mimeType,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ file: { display_name: displayName } }),
      }
    );
    const uploadUrl = startRes.headers.get("X-Goog-Upload-URL");
    if (!uploadUrl) throw new Error("Failed to get upload URL");

    // Upload the actual file
    const uploadRes = await fetch(uploadUrl, {
      method: "POST",
      headers: {
        "Content-Length": numBytes,
        "X-Goog-Upload-Offset": "0",
        "X-Goog-Upload-Command": "upload, finalize",
      },
      body: buffer,
    });
    const fileInfo = await uploadRes.json();
    return fileInfo;
  }

IMO it is reasonable that people may want to interact with large files that need to be uploaded before inference in a browser targeted project so it would be nice to accommodate them instead of having them each re-write the same wrappers and config overwrites.

@hkt74
Copy link
Collaborator

hkt74 commented Mar 3, 2025

@evanlesmez got it, the change to support upload with buffer is in PR #365, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:js sdk Issue/PR related to JavaScript SDK status: awaiting user response Awaiting a response from the author status:triaged Issue/PR triaged to the corresponding sub-team type:feature request New feature request/enhancement
Projects
None yet
Development

No branches or pull requests

5 participants