aiwrapper
Version:
A Universal AI Wrapper for JavaScript & TypeScript
325 lines (322 loc) • 12 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 __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { LanguageProvider } from "../language-provider.js";
import { httpRequestWithRetry as fetch } from "../../http-request.js";
import { models } from "aimodels";
import { calculateModelResponseTokens } from "../utils/token-calculator.js";
import { LangMessage, LangMessages } from "../messages.js";
import { addInstructionAboutSchema } from "../prompt-for-json.js";
class GoogleLang extends LanguageProvider {
constructor(options) {
const modelName = options.model || "gemini-1.5-flash";
super(modelName);
__publicField(this, "_apiKey");
__publicField(this, "_model");
__publicField(this, "_systemPrompt");
__publicField(this, "_maxTokens");
__publicField(this, "modelInfo");
const modelInfo = models.id(modelName);
if (!modelInfo) {
console.error(`Invalid Google model: ${modelName}. Model not found in aimodels database.`);
}
this.modelInfo = modelInfo;
this._apiKey = options.apiKey;
this._model = modelName;
this._systemPrompt = options.systemPrompt || "";
this._maxTokens = options.maxTokens;
}
async ask(prompt, options) {
const messages = new LangMessages();
if (this._systemPrompt) {
messages.instructions = this._systemPrompt;
}
messages.addUserMessage(prompt);
return await this.chat(messages, options);
}
async chat(messages, options) {
var _a, _b;
const messageCollection = messages instanceof LangMessages ? messages : new LangMessages(messages);
const instructions = this.buildInstructions(messageCollection, options);
const contents = this.transformMessagesForProvider(messageCollection);
const maxOutputTokens = this.computeMaxTokens(messageCollection);
const tools = this.buildTools(messageCollection.availableTools);
const generationConfig = {};
if (typeof maxOutputTokens === "number") {
generationConfig.max_output_tokens = maxOutputTokens;
}
const requestBody = __spreadValues(__spreadValues(__spreadValues(__spreadValues({
contents
}, instructions ? {
system_instruction: {
role: "system",
parts: [{ text: instructions }]
}
} : {}), Object.keys(generationConfig).length > 0 ? { generation_config: generationConfig } : {}), tools ? { tools } : {}), (_a = options == null ? void 0 : options.providerSpecificBody) != null ? _a : {});
try {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/${this._model}:generateContent?key=${this._apiKey}`,
{
method: "POST",
headers: __spreadValues({
"Content-Type": "application/json",
// Some setups still expect the header, so keep both
"x-goog-api-key": this._apiKey
}, (_b = options == null ? void 0 : options.providerSpecificHeaders) != null ? _b : {}),
body: JSON.stringify(requestBody),
signal: options == null ? void 0 : options.signal
}
);
if (!response.ok) {
const body = await response.text().catch(() => "");
throw new Error(
`Google API request failed with status ${response.status}${body ? `: ${body}` : ""}`
);
}
const data = await response.json();
this.applyCandidates(data == null ? void 0 : data.candidates, messageCollection, options == null ? void 0 : options.onResult);
} catch (error) {
if ((error == null ? void 0 : error.name) === "AbortError") {
messageCollection.aborted = true;
error.partialResult = messageCollection;
}
throw error;
}
messageCollection.finished = true;
const toolsResults = await messageCollection.executeRequestedTools();
if ((options == null ? void 0 : options.onResult) && toolsResults) options.onResult(toolsResults);
return messageCollection;
}
transformMessagesForProvider(messages) {
var _a;
const mapped = [];
for (const msg of messages) {
if (msg.role === "tool-results") {
const parts2 = msg.toolResults.map((tr) => {
let response;
if (Array.isArray(tr.result)) {
response = { result: tr.result };
} else if (typeof tr.result === "object" && tr.result !== null) {
response = tr.result;
} else {
response = { result: tr.result };
}
return {
functionResponse: {
name: tr.name,
response
}
};
});
mapped.push({ role: "user", parts: parts2 });
continue;
}
if (msg.role !== "user" && msg.role !== "assistant") {
continue;
}
const parts = [];
const legacyContent = msg.content;
if (Array.isArray(legacyContent)) {
parts.push(...this.mapPartsToGemini(legacyContent));
} else {
for (const item of msg.items) {
if (item.type === "text") {
parts.push({ text: item.text });
} else if (item.type === "image") {
const imagePart = this.mapImageItemToGemini(item);
if (imagePart) parts.push(imagePart);
} else if (item.type === "tool") {
const toolItem = item;
const part = {
function_call: {
name: toolItem.name,
args: (_a = toolItem.arguments) != null ? _a : {}
}
};
if (toolItem.thoughtSignature) {
part.thoughtSignature = toolItem.thoughtSignature;
}
parts.push(part);
}
}
}
if (parts.length === 0) {
parts.push({ text: "" });
}
mapped.push({
role: msg.role === "assistant" ? "model" : "user",
parts
});
}
return mapped;
}
mapPartsToGemini(parts) {
const out = [];
for (const p of parts) {
if (p.type === "text") {
out.push({ text: p.text });
} else if (p.type === "image") {
const inlineData = this.imageInputToGeminiInlineData(p.image);
out.push({ inlineData });
}
}
return out;
}
imageInputToGeminiInlineData(image) {
const kind = image.kind;
if (kind === "base64") {
const base64 = image.base64;
const mimeType = image.mimeType || "image/png";
return { mimeType, data: base64 };
}
if (kind === "url") {
const url = image.url;
if (url.startsWith("data:")) {
const match = url.match(/^data:([^;]+);base64,(.*)$/);
if (!match) throw new Error("Invalid data URL for Gemini image");
const mimeType = match[1];
const data = match[2];
return { mimeType, data };
}
throw new Error("Gemini inline image requires base64 or data URL. Provide base64+mimeType or a data: URL.");
}
if (kind === "bytes" || kind === "blob") {
throw new Error("Gemini image input requires base64. Convert bytes/blob to base64 first.");
}
throw new Error("Unknown image input kind for Gemini");
}
mapImageItemToGemini(image) {
if (typeof image.base64 === "string" && image.base64.length > 0) {
return {
inlineData: {
mimeType: image.mimeType || "image/png",
data: image.base64
}
};
}
if (typeof image.url === "string" && image.url.length > 0) {
if (image.url.startsWith("data:")) {
const match = image.url.match(/^data:([^;]+);base64,(.*)$/);
if (!match) return null;
const mimeType = match[1];
const data = match[2];
return {
inlineData: { mimeType, data }
};
}
return { fileData: { fileUri: image.url } };
}
return null;
}
buildInstructions(messageCollection, options) {
let instructions = messageCollection.instructions || "";
if (this._systemPrompt) {
instructions = instructions ? `${this._systemPrompt}
${instructions}` : this._systemPrompt;
}
if (options == null ? void 0 : options.schema) {
const baseInstruction = instructions !== "" ? `${instructions}
` : "";
instructions = baseInstruction + addInstructionAboutSchema(options.schema);
messageCollection.instructions = instructions;
}
return instructions;
}
computeMaxTokens(messageCollection) {
if (this._maxTokens !== void 0) return this._maxTokens;
if (!this.modelInfo) return void 0;
return calculateModelResponseTokens(
this.modelInfo,
messageCollection,
this._maxTokens
);
}
buildTools(availableTools) {
if (!availableTools || !Array.isArray(availableTools) || availableTools.length === 0) {
return void 0;
}
const functionDeclarations = availableTools.map((tool) => ({
name: tool.name,
description: tool.description || "",
parameters: tool.parameters
}));
return functionDeclarations.length > 0 ? [{ function_declarations: functionDeclarations }] : void 0;
}
applyCandidates(candidates, result, onResult) {
var _a, _b;
if (!Array.isArray(candidates) || candidates.length === 0) {
return;
}
const candidate = candidates[0];
const parts = (_a = candidate == null ? void 0 : candidate.content) == null ? void 0 : _a.parts;
if (!Array.isArray(parts) || parts.length === 0) return;
const assistantMessage = new LangMessage("assistant", []);
let toolIndex = 0;
for (const part of parts) {
if (!part) continue;
if (typeof part.text === "string" && part.text.length > 0) {
assistantMessage.items.push({ type: "text", text: part.text });
}
if (part.inlineData && (part.inlineData.data || part.inlineData.b64_json)) {
const base64 = part.inlineData.data || part.inlineData.b64_json;
const mimeType = part.inlineData.mimeType || "image/png";
assistantMessage.items.push({ type: "image", base64, mimeType });
}
if ((_b = part.fileData) == null ? void 0 : _b.fileUri) {
assistantMessage.items.push({ type: "image", url: part.fileData.fileUri });
}
const funcCall = part.functionCall || part.function_call;
if (funcCall) {
const name = funcCall.name || `function_call_${toolIndex}`;
const callId = `function_call_${toolIndex++}`;
const rawArgs = funcCall.args || funcCall.arguments;
const args = this.parseFunctionArgs(rawArgs);
const toolItem = {
type: "tool",
callId,
name,
arguments: args
};
const thoughtSignature = part.thoughtSignature || (funcCall == null ? void 0 : funcCall.thoughtSignature);
if (thoughtSignature) {
toolItem.thoughtSignature = thoughtSignature;
}
assistantMessage.items.push(toolItem);
}
}
if (assistantMessage.items.length > 0) {
result.push(assistantMessage);
onResult == null ? void 0 : onResult(assistantMessage);
}
}
parseFunctionArgs(rawArgs) {
if (!rawArgs) return {};
if (typeof rawArgs === "object") return rawArgs;
if (typeof rawArgs === "string") {
try {
return JSON.parse(rawArgs);
} catch (e) {
return {};
}
}
return {};
}
}
export {
GoogleLang
};
//# sourceMappingURL=google-lang.js.map