This repository has been archived on 2026-06-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
master-ai/vibn-agent-runner/dist/llm/gemini-chat.js

158 lines
5.2 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.callGeminiChat = callGeminiChat;
exports.streamGeminiChat = streamGeminiChat;
const genai_1 = require("@google/genai");
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY || "";
const GEMINI_MODEL = process.env.VIBN_CHAT_MODEL || "gemini-3.1-pro-preview";
// Add a clear visual log so we always know exactly which model is active in the terminal
console.log(`[GeminiChat Runner] Initialized — Model: ${GEMINI_MODEL}`);
if (!GEMINI_API_KEY) {
console.warn(`[GeminiChat] WARNING: GOOGLE_API_KEY is not set. Chat stream will fail with 403 Forbidden.`);
}
const ai = new genai_1.GoogleGenAI({ apiKey: GEMINI_API_KEY });
function toGeminiContents(messages) {
const contents = [];
for (const msg of messages) {
if (msg.role === "user") {
contents.push({ role: "user", parts: [{ text: msg.content }] });
}
else if (msg.role === "assistant") {
const parts = [];
if (msg.content)
parts.push({ text: msg.content });
if (msg.toolCalls?.length) {
for (const tc of msg.toolCalls) {
const part = {
functionCall: { name: tc.name, args: tc.args },
};
if (tc.thoughtSignature) {
part.thoughtSignature = tc.thoughtSignature;
}
parts.push(part);
}
}
if (parts.length)
contents.push({ role: "model", parts });
}
else if (msg.role === "tool") {
const part = {
functionResponse: {
name: msg.toolName || "unknown",
response: { name: msg.toolName || "unknown", content: msg.content },
},
};
const last = contents[contents.length - 1];
if (last?.role === "user") {
last.parts.push(part);
}
else {
contents.push({ role: "user", parts: [part] });
}
}
}
return contents;
}
function toGeminiFunctions(tools) {
if (!tools.length)
return undefined;
return [
{
functionDeclarations: tools.map((t) => ({
name: t.name,
description: t.description,
parameters: t.parameters,
})),
},
];
}
async function callGeminiChat(opts) {
try {
const config = {
temperature: opts.temperature ?? 0.7,
maxOutputTokens: 8192,
};
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
}
const fns = toGeminiFunctions(opts.tools ?? []);
if (fns)
config.tools = fns;
const response = await ai.models.generateContent({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config,
});
let text = "";
let thoughts = "";
const toolCalls = [];
const parts = response.candidates?.[0]?.content?.parts ?? [];
for (const part of parts) {
if (part.text) {
if (part.thought)
thoughts += part.text;
else
text += part.text;
}
if (part.functionCall) {
toolCalls.push({
id: `tc-${Date.now()}-${Math.random().toString(36).slice(2)}`,
name: part.functionCall.name || "",
args: part.functionCall.args ?? {},
thoughtSignature: part.thoughtSignature,
});
}
}
return {
text,
thoughts,
toolCalls,
finishReason: response.candidates?.[0]?.finishReason,
};
}
catch (error) {
return {
text: "",
thoughts: "",
toolCalls: [],
error: `GoogleGenAI error: ${error instanceof Error ? error.message : String(error)}`,
};
}
}
async function* streamGeminiChat(opts) {
try {
const config = {
temperature: opts.temperature ?? 0.7,
maxOutputTokens: 8192,
};
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
}
const fns = toGeminiFunctions(opts.tools ?? []);
if (fns)
config.tools = fns;
const streamResult = await ai.models.generateContentStream({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config,
});
for await (const chunk of streamResult) {
const parts = chunk.candidates?.[0]?.content?.parts ?? [];
for (const part of parts) {
if (part.text) {
yield part.thought
? { type: "thinking", text: part.text }
: { type: "text", text: part.text };
}
}
}
yield { type: "done" };
}
catch (error) {
yield {
type: "error",
error: `GoogleGenAI error: ${error instanceof Error ? error.message : String(error)}`,
};
}
}