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> {
|
async function optimizeAudio(input: string): Promise<string> {
|
||||||
const output = input.replace(/\.wav$/, ".mp3");
|
const output = input.replace(/\.wav$/, "-opt.wav");
|
||||||
try {
|
try {
|
||||||
await execFileAsync(
|
await execFileAsync(
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
@@ -294,10 +294,10 @@ async function optimizeAudio(input: string): Promise<string> {
|
|||||||
input,
|
input,
|
||||||
"-af",
|
"-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",
|
"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",
|
"-ac",
|
||||||
"libmp3lame",
|
"1",
|
||||||
"-q:a",
|
"-ar",
|
||||||
"4",
|
"16000",
|
||||||
output,
|
output,
|
||||||
],
|
],
|
||||||
{ timeout: 30_000 },
|
{ 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 {
|
function extractText(content: any): string {
|
||||||
if (typeof content === "string") return content;
|
if (typeof content === "string") return content;
|
||||||
if (Array.isArray(content)) {
|
if (Array.isArray(content)) {
|
||||||
@@ -489,6 +511,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: [p.baseImage],
|
filePaths: [p.baseImage],
|
||||||
outputDir: p.outputDir,
|
outputDir: p.outputDir,
|
||||||
|
yolo: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -533,6 +556,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: [p.baseImage],
|
filePaths: [p.baseImage],
|
||||||
outputDir: p.outputDir,
|
outputDir: p.outputDir,
|
||||||
|
yolo: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -576,6 +600,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: [p.baseImage],
|
filePaths: [p.baseImage],
|
||||||
outputDir: p.outputDir,
|
outputDir: p.outputDir,
|
||||||
|
yolo: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -620,6 +645,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: p.images,
|
filePaths: p.images,
|
||||||
outputDir: p.outputDir,
|
outputDir: p.outputDir,
|
||||||
|
yolo: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -662,6 +688,7 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: [p.referenceImage],
|
filePaths: [p.referenceImage],
|
||||||
outputDir: p.outputDir,
|
outputDir: p.outputDir,
|
||||||
|
yolo: true,
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -913,33 +940,35 @@ export default function agyExtension(pi: ExtensionAPI) {
|
|||||||
ctx.ui.notify("Registrazione fermata, ottimizzazione audio...", "info");
|
ctx.ui.notify("Registrazione fermata, ottimizzazione audio...", "info");
|
||||||
const optimized = await optimizeAudio(file);
|
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 context = getConversationContext(ctx);
|
||||||
const res = await executeAgy({
|
const res = await executeAgy({
|
||||||
prompt:
|
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\nContesto della conversazione:\n${context || "(nessuno)"}` +
|
||||||
`\n\nRestituisci SOLO la trascrizione testuale, senza commenti.`,
|
`\n\nRestituisci la trascrizione corretta e una breve interpretazione/risposta.`,
|
||||||
mode: "analyze",
|
|
||||||
stateless: true,
|
stateless: true,
|
||||||
filePaths: [optimized],
|
|
||||||
yolo: true,
|
|
||||||
model: "Gemini 3.6 Flash (Medium)",
|
model: "Gemini 3.6 Flash (Medium)",
|
||||||
effort: "low",
|
effort: "low",
|
||||||
});
|
});
|
||||||
ctx.ui.setStatus("agy-rec", "");
|
ctx.ui.setStatus("agy-rec", "");
|
||||||
|
|
||||||
const transcription = res.text.trim();
|
const finalText = res.text.trim() || transcript;
|
||||||
if (!transcription) {
|
|
||||||
ctx.ui.notify("Trascrizione vuota o errore", "error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserisci il risultato come prompt su pi
|
// Inserisci il risultato come prompt su pi
|
||||||
if (ctx.isIdle()) {
|
if (ctx.isIdle()) {
|
||||||
pi.sendUserMessage(transcription);
|
pi.sendUserMessage(finalText);
|
||||||
} else {
|
} else {
|
||||||
pi.sendUserMessage(transcription, { deliverAs: "followUp" });
|
pi.sendUserMessage(finalText, { deliverAs: "followUp" });
|
||||||
}
|
}
|
||||||
ctx.ui.notify("✅ Trascrizione inviata come prompt a pi", "info");
|
ctx.ui.notify("✅ Trascrizione inviata come prompt a pi", "info");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user