Updates
Rolling release / release (push) Successful in 8m31s

This commit is contained in:
2026-09-13 22:24:14 -04:00
parent 1ca4224377
commit a097adf4eb
62 changed files with 2707 additions and 957 deletions
+64 -29
View File
@@ -188,38 +188,72 @@ function extForMime(mime) {
return '.jpg';
}
// Qwen VL / llama.cpp formatPrompt loads images then requires a user question.
const VISION_FOLLOWUP_QUESTION =
'Describe what you see in the attached still. Answer the user. Do not mention file paths.';
function attachmentsFromImages(msg, dir) {
const attachments = [];
for (let i = 0; i < Math.min(4, msg.images.length); i++) {
const img = msg.images[i] || {};
let buf = null;
let mime = img.mime || 'image/jpeg';
if (img.dataUrl) {
const d = decodeDataUrl(img.dataUrl);
if (d) {
buf = d.buf;
mime = d.mime;
}
} else if (img.dataBase64) {
buf = Buffer.from(img.dataBase64, 'base64');
} else if (img.path && fs.existsSync(img.path)) {
attachments.push({ path: img.path });
continue;
}
if (!buf) continue;
const file = path.join(dir, 'img_' + Date.now() + '_' + i + extForMime(mime));
fs.writeFileSync(file, buf);
attachments.push({ path: file });
}
return attachments;
}
function withVisionAttachments(msg, dir) {
if (!msg || !Array.isArray(msg.images) || !msg.images.length) return msg;
const attachments = attachmentsFromImages(msg, dir);
const copy = Object.assign({}, msg);
delete copy.images;
if (attachments.length) copy.attachments = (copy.attachments || []).concat(attachments);
return copy;
}
function ensureVisionQuestion(msg) {
if (!msg || !Array.isArray(msg.attachments) || !msg.attachments.length) return msg;
if (String(msg.content || '').trim()) return msg;
return Object.assign({}, msg, { content: VISION_FOLLOWUP_QUESTION });
}
function hoistToolVision(messages) {
const out = [];
for (const msg of messages) {
const role = msg && msg.role;
if (msg && (role === 'tool' || role === 'function') && Array.isArray(msg.attachments) && msg.attachments.length) {
const copy = Object.assign({}, msg);
const attachments = copy.attachments;
delete copy.attachments;
out.push(copy);
out.push({ role: 'user', content: VISION_FOLLOWUP_QUESTION, attachments });
continue;
}
out.push(ensureVisionQuestion(msg));
}
return out;
}
function prepareVisionHistory(history) {
const dir = paths.ensureDir(path.join(paths.ensureQvacRoot(), 'vision'));
const list = Array.isArray(history) ? history : [];
return list.map((msg) => {
if (!msg || !Array.isArray(msg.images) || !msg.images.length) return msg;
const attachments = [];
for (let i = 0; i < Math.min(4, msg.images.length); i++) {
const img = msg.images[i] || {};
let buf = null;
let mime = img.mime || 'image/jpeg';
if (img.dataUrl) {
const d = decodeDataUrl(img.dataUrl);
if (d) {
buf = d.buf;
mime = d.mime;
}
} else if (img.dataBase64) {
buf = Buffer.from(img.dataBase64, 'base64');
} else if (img.path && fs.existsSync(img.path)) {
attachments.push({ path: img.path });
continue;
}
if (!buf) continue;
const file = path.join(dir, 'img_' + Date.now() + '_' + i + extForMime(mime));
fs.writeFileSync(file, buf);
attachments.push({ path: file });
}
const copy = Object.assign({}, msg);
delete copy.images;
if (attachments.length) copy.attachments = (copy.attachments || []).concat(attachments);
return copy;
});
return hoistToolVision(list.map((msg) => withVisionAttachments(msg, dir)));
}
async function resolveSrc(s, name) {
@@ -573,6 +607,7 @@ module.exports = {
cancel,
getLoaded,
resources,
VISION_FOLLOWUP_QUESTION,
prepareVisionHistory,
hold,
release,