Fix: aggiunto yolo ai tool di editing immagini (evita hang in headless)
This commit is contained in:
+48
-19
@@ -282,9 +282,9 @@ function stopRecording(): Promise<string | null> {
|
||||
});
|
||||
}
|
||||
|
||||
// Ottimizza l'audio: taglia il silenzio iniziale/finale e converte in MP3 (più piccolo)
|
||||
// Ottimizza l'audio: taglia il silenzio iniziale/finale e converte in WAV 16kHz mono
|
||||
async function optimizeAudio(input: string): Promise<string> {
|
||||
const output = input.replace(/\.wav$/, ".mp3");
|
||||
const output = input.replace(/\.wav$/, "-opt.wav");
|
||||
try {
|
||||
await execFileAsync(
|
||||
"ffmpeg",
|
||||
@@ -294,10 +294,10 @@ async function optimizeAudio(input: string): Promise<string> {
|
||||
input,
|
||||
"-af",
|
||||
"silenceremove=start_periods=1:start_threshold=-50dB:start_silence=0.5,areverse,silenceremove=start_periods=1:start_threshold=-50dB:start_silence=0.5,areverse",
|
||||
"-c:a",
|
||||
"libmp3lame",
|
||||
"-q:a",
|
||||
"4",
|
||||
"-ac",
|
||||
"1",
|
||||
"-ar",
|
||||
"16000",
|
||||
output,
|
||||
],
|
||||
{ timeout: 30_000 },
|
||||
@@ -308,6 +308,28 @@ async function optimizeAudio(input: string): Promise<string> {
|
||||
}
|
||||
}
|
||||
|
||||
// Trascrizione locale affidabile con faster-whisper (agy CLI non supporta audio)
|
||||
async function transcribeWithWhisper(file: string): Promise<string> {
|
||||
const model = process.env.AGY_WHISPER_MODEL ?? "small";
|
||||
const script = `
|
||||
from faster_whisper import WhisperModel
|
||||
import sys
|
||||
model = WhisperModel('${model}', device='cpu', compute_type='int8')
|
||||
segments, info = model.transcribe(sys.argv[1], language='it')
|
||||
for seg in segments:
|
||||
print(seg.text, end='')
|
||||
`;
|
||||
try {
|
||||
const { stdout } = await execFileAsync("python3", ["-c", script, file], {
|
||||
timeout: 180_000,
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
});
|
||||
return stdout.trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function extractText(content: any): string {
|
||||
if (typeof content === "string") return content;
|
||||
if (Array.isArray(content)) {
|
||||
@@ -489,6 +511,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
stateless: true,
|
||||
filePaths: [p.baseImage],
|
||||
outputDir: p.outputDir,
|
||||
yolo: true,
|
||||
signal,
|
||||
});
|
||||
return {
|
||||
@@ -533,6 +556,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
stateless: true,
|
||||
filePaths: [p.baseImage],
|
||||
outputDir: p.outputDir,
|
||||
yolo: true,
|
||||
signal,
|
||||
});
|
||||
return {
|
||||
@@ -576,6 +600,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
stateless: true,
|
||||
filePaths: [p.baseImage],
|
||||
outputDir: p.outputDir,
|
||||
yolo: true,
|
||||
signal,
|
||||
});
|
||||
return {
|
||||
@@ -620,6 +645,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
stateless: true,
|
||||
filePaths: p.images,
|
||||
outputDir: p.outputDir,
|
||||
yolo: true,
|
||||
signal,
|
||||
});
|
||||
return {
|
||||
@@ -662,6 +688,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
stateless: true,
|
||||
filePaths: [p.referenceImage],
|
||||
outputDir: p.outputDir,
|
||||
yolo: true,
|
||||
signal,
|
||||
});
|
||||
return {
|
||||
@@ -913,33 +940,35 @@ export default function agyExtension(pi: ExtensionAPI) {
|
||||
ctx.ui.notify("Registrazione fermata, ottimizzazione audio...", "info");
|
||||
const optimized = await optimizeAudio(file);
|
||||
|
||||
ctx.ui.notify("Trascrizione in corso...", "info");
|
||||
ctx.ui.notify("Trascrizione in corso (faster-whisper)...", "info");
|
||||
const transcript = await transcribeWithWhisper(optimized);
|
||||
if (!transcript) {
|
||||
ctx.ui.setStatus("agy-rec", "");
|
||||
ctx.ui.notify("Trascrizione vuota o errore", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpreta con Gemini (testo) usando il contesto della conversazione
|
||||
ctx.ui.notify("Interpretazione con Gemini...", "info");
|
||||
const context = getConversationContext(ctx);
|
||||
const res = await executeAgy({
|
||||
prompt:
|
||||
`Trascrivi fedelmente il contenuto del file audio al percorso ${optimized}.` +
|
||||
`Ecco la trascrizione di un messaggio vocale dell'utente:\n\n${transcript}` +
|
||||
`\n\nContesto della conversazione:\n${context || "(nessuno)"}` +
|
||||
`\n\nRestituisci SOLO la trascrizione testuale, senza commenti.`,
|
||||
mode: "analyze",
|
||||
`\n\nRestituisci la trascrizione corretta e una breve interpretazione/risposta.`,
|
||||
stateless: true,
|
||||
filePaths: [optimized],
|
||||
yolo: true,
|
||||
model: "Gemini 3.6 Flash (Medium)",
|
||||
effort: "low",
|
||||
});
|
||||
ctx.ui.setStatus("agy-rec", "");
|
||||
|
||||
const transcription = res.text.trim();
|
||||
if (!transcription) {
|
||||
ctx.ui.notify("Trascrizione vuota o errore", "error");
|
||||
return;
|
||||
}
|
||||
const finalText = res.text.trim() || transcript;
|
||||
|
||||
// Inserisci il risultato come prompt su pi
|
||||
if (ctx.isIdle()) {
|
||||
pi.sendUserMessage(transcription);
|
||||
pi.sendUserMessage(finalText);
|
||||
} else {
|
||||
pi.sendUserMessage(transcription, { deliverAs: "followUp" });
|
||||
pi.sendUserMessage(finalText, { deliverAs: "followUp" });
|
||||
}
|
||||
ctx.ui.notify("✅ Trascrizione inviata come prompt a pi", "info");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user