40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { GoogleGenAI, FileState, createPartFromUri, createUserContent, type File } from '@google/genai';
|
|
import type { AudioTrack } from './recorder.js';
|
|
import { buildPrompt } from './prompt.js';
|
|
|
|
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
|
|
const MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';
|
|
|
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
async function upload(track: AudioTrack): Promise<File> {
|
|
let file = await ai.files.upload({ file: track.file, config: { mimeType: 'audio/ogg', displayName: track.name } });
|
|
while (file.state === FileState.PROCESSING) {
|
|
await sleep(2000);
|
|
file = await ai.files.get({ name: file.name! });
|
|
}
|
|
if (file.state === FileState.FAILED) throw new Error(`Gemini no pudo procesar el audio de ${track.name}`);
|
|
return file;
|
|
}
|
|
|
|
export async function generateNotes(tracks: AudioTrack[], startedAt: Date): Promise<string> {
|
|
const files: File[] = [];
|
|
try {
|
|
for (const track of tracks) files.push(await upload(track));
|
|
|
|
const parts = tracks.flatMap((track, i) => [
|
|
`Pista ${i + 1}: ${track.name}`,
|
|
createPartFromUri(files[i].uri!, files[i].mimeType!),
|
|
]);
|
|
const res = await ai.models.generateContent({
|
|
model: MODEL,
|
|
contents: createUserContent([buildPrompt(tracks.map((t) => t.name), startedAt), ...parts]),
|
|
});
|
|
const text = res.text?.trim();
|
|
if (!text) throw new Error('Gemini devolvió una respuesta vacía');
|
|
return text.replace(/^```(?:markdown|md)?\n([\s\S]*)\n```$/, '$1');
|
|
} finally {
|
|
await Promise.allSettled(files.map((f) => ai.files.delete({ name: f.name! })));
|
|
}
|
|
}
|