Vendored
+64
-29
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user