gemini-ai-sdk
Version:
The simpler Google Gemini SDK
336 lines (333 loc) • 10.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/index.ts
import {
GoogleGenerativeAI
} from "@google/generative-ai";
import { fileTypeFromBuffer } from "file-type";
import mime from "mime-lite";
// src/constants.ts
var constants_exports = {};
__export(constants_exports, {
defaultTools: () => defaultTools,
safetyDisabledSettings: () => safetyDisabledSettings
});
import { HarmCategory, HarmBlockThreshold, DynamicRetrievalMode } from "@google/generative-ai";
var safetyDisabledSettings = [
{ category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
{ category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
{ category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE },
{ category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE }
];
var defaultTools = {
webSearch: {
googleSearchRetrieval: {
dynamicRetrievalConfig: {
mode: DynamicRetrievalMode.MODE_DYNAMIC,
dynamicThreshold: 0.7
}
}
},
codeExecution: {
codeExecution: {}
}
};
// src/index.ts
function isFileUpload(data) {
return data && data.buffer && data.filePath;
}
var supportedFileFormats = [
"image/png",
"image/jpeg",
"image/webp",
"image/heic",
"image/heif",
"audio/wav",
"audio/mp3",
"audio/mpeg",
"audio/aiff",
"audio/aac",
"audio/ogg",
"audio/flac",
"video/mp4",
"video/mpeg",
"video/mov",
"video/avi",
"video/x-flv",
"video/mpg",
"video/webm",
"video/wmv",
"video/3gpp",
"text/plain",
"text/html",
"text/css",
"text/javascript",
"application/x-javascript",
"text/x-typescript",
"application/x-typescript",
"text/csv",
"text/markdown",
"text/x-python",
"application/x-python-code",
"application/json",
"text/xml",
"application/rtf",
"text/rtf",
"application/pdf"
];
var formatMap = {
"audio/mpeg": "audio/mp3",
"video/quicktime": "video/mov"
};
var getFileType = async (buffer, filePath = void 0, { strict = false } = {}) => {
const fileType = await fileTypeFromBuffer(buffer);
let format = formatMap[fileType == null ? void 0 : fileType.mime] || (fileType == null ? void 0 : fileType.mime);
let valid = supportedFileFormats.includes(format);
if (!valid && filePath) {
format = mime.getType(filePath);
format = formatMap[format] || format;
valid = supportedFileFormats.includes(format);
}
if (!valid) {
if (strict) {
throw new Error(
"Please provide a valid file format that is accepted by Gemini. Learn more about valid formats here: https://ai.google.dev/gemini-api/docs/prompting_with_media?lang=node#supported_file_formats"
);
} else {
format = "text/plain";
}
}
return format;
};
var Chat = class {
/**
* Creates a new Chat instance.
* @param gemini - The Gemini instance to use.
* @param options - Optional parameters for the chat.
*/
constructor(gemini, options = {}) {
this.gemini = gemini;
this.history = options.history || [];
this.options = options;
}
/**
* Appends a message to the chat history.
* @param message - The message to append.
*/
appendMessage(message) {
this.history.push(message);
}
/**
* Sends a message to Gemini and returns the response.
* @param message - The message to send.
* @param options - Optional parameters for the request.
* @returns The response from Gemini. If options.stream = false, returns a single object.
* Otherwise, returns an AsyncGenerator of results.
*/
async ask(message, options = {}) {
const mergedOptions = __spreadValues(__spreadValues({}, this.options), options);
return this.gemini.ask(message, __spreadValues({
history: this.history
}, mergedOptions));
}
};
var Gemini = class {
/**
* Creates a new Gemini instance.
* @param apiKey - Your API key for the Gemini API.
* @param options - Optional parameters for initializing the instance.
*/
constructor(apiKey, options = {}) {
this.genAI = new GoogleGenerativeAI(apiKey);
this.options = __spreadValues({
apiVersion: "v1beta"
}, options);
}
/**
* Uploads a file to the Gemini API.
* @param options - Options for the file upload.
* @returns The URI of the uploaded file.
*/
async uploadFile({ file, mimeType }) {
const gemini = this.genAI;
function generateBoundary() {
let str = "";
for (let i = 0; i < 2; i++) {
str = str + Math.random().toString().slice(2);
}
return str;
}
const boundary = generateBoundary();
const apiVersion = this.options.apiVersion;
const apiKey = gemini.apiKey;
const generateBlob = (boundary2, file2, mime2) => new Blob([
`--${boundary2}\r
Content-Type: application/json; charset=utf-8\r
\r
${JSON.stringify({
file: {
mimeType: mime2
}
})}\r
--${boundary2}\r
Content-Type: ${mime2}\r
\r
`,
file2,
`\r
--${boundary2}--`
]);
const fileSendDataRaw = await fetch(
`https://generativelanguage.googleapis.com/upload/${apiVersion}/files?key=${apiKey}`,
{
method: "POST",
headers: {
"Content-Type": `multipart/related; boundary=${boundary}`,
"X-Goog-Upload-Protocol": "multipart"
},
body: generateBlob(boundary, file, mimeType)
}
).then((res) => res.json());
const fileSendData = fileSendDataRaw.file;
let waitTime = 250;
const MAX_BACKOFF = 5e3;
while (true) {
try {
const url = `https://generativelanguage.googleapis.com/${apiVersion}/${fileSendData.name}?key=${apiKey}`;
const response = await fetch(url, { method: "GET" });
const data = await response.json();
if (data.error) {
throw new Error(`Google's File API responded with an error: ${data.error.message}`);
}
if (data.state === "ACTIVE") break;
await new Promise((resolve) => setTimeout(resolve, waitTime));
waitTime = Math.min(waitTime * 1.5, MAX_BACKOFF);
} catch (error) {
throw new Error(`An error occurred while uploading to Google's File API: ${error.message}`);
}
}
return fileSendData.uri;
}
/**
* Converts a list of messages or files to Gemini API parts.
* @param messages - The messages or files to convert.
* @returns An array of Gemini API parts.
*/
async messageToParts(messages) {
const parts = [];
let totalBytes = 0;
for (const msg of messages) {
if (typeof msg === "string") {
parts.push({ text: msg });
} else if (msg instanceof ArrayBuffer || msg instanceof Uint8Array || isFileUpload(msg)) {
const is_file_upload = isFileUpload(msg);
const buffer = is_file_upload ? msg.buffer : msg;
const filePath = is_file_upload ? msg.filePath : void 0;
totalBytes += Buffer.from(buffer).byteLength;
const mimeType = await getFileType(buffer, filePath);
if (!mimeType.startsWith("video")) {
const part = {
inlineData: {
mimeType,
data: Buffer.from(buffer).toString("base64")
}
};
parts.push(part);
} else {
const fileURI = await this.uploadFile({
file: buffer,
mimeType
});
const part = {
fileData: {
mimeType,
fileUri: fileURI
}
};
parts.push(part);
}
}
}
if (totalBytes > 20 * 1024 * 1024) {
for (const idx in parts) {
const part = parts[idx];
if (part.inlineData) {
const fileURI = await this.uploadFile({
file: Buffer.from(part.inlineData.data, "base64"),
mimeType: part.inlineData.mimeType
});
const newPart = {
fileData: {
mimeType: part.inlineData.mimeType,
fileUri: fileURI
}
};
parts[idx] = newPart;
}
}
}
return parts;
}
/**
* Sends a request to the Gemini API and returns the response.
* @param message - The message to send.
* @param options - Optional parameters for the request.
* @returns The response from the Gemini API. If options.stream = false, returns a single object.
* Otherwise, returns an AsyncGenerator of results.
*/
async ask(message, options = {}) {
const model = this.genAI.getGenerativeModel(
{ model: options.model, tools: options.tools },
{ apiVersion: this.options.apiVersion }
);
const parts = typeof message === "string" ? [{ text: message }] : message;
const { generationConfig, safetySettings, systemInstruction, history } = options;
const chat = model.startChat({
history: history || [],
generationConfig,
safetySettings,
systemInstruction
});
if (options.stream) {
return chat.sendMessageStream(parts);
} else {
return chat.sendMessage(parts);
}
}
/**
* Creates a new chat session.
* @param options - Optional parameters for the chat.
* @returns A new Chat instance.
*/
createChat(options = {}) {
return new Chat(this, options);
}
};
var index_default = Gemini;
export {
Chat,
Gemini,
constants_exports as constants,
index_default as default,
getFileType,
isFileUpload
};
//# sourceMappingURL=index.mjs.map