This commit is contained in:
seb
2026-10-07 21:23:10 +02:00
parent a29ad50b74
commit 7d537aaa6b
33 changed files with 598 additions and 327 deletions

View File

@@ -236,7 +236,7 @@ function extractImageInputs(args = {}) {
/**
* Fetches a database image by its content hash using store or authenticated local endpoint.
* @param {string} hash 64-char SHA-256 hex
* @param {string} hash 8-char SHA-256 Base62
* @param {number} googleAuthId
* @returns {Promise<Buffer>}
*/
@@ -323,7 +323,7 @@ export function createAnalyzeImageTool({ googleAuthId, model }) {
// ── Load the bytes ────────────────────────────────────────────
const items = [];
for (const item of inputs) {
// Only a path that actually carries a 64-char content hash is a database image.
// Only a path that actually carries an 8-char content hash is a database image.
// A bare "images/…" prefix is just the VFS folder, so it must NOT be treated as
// a DB lookup or `images/candidates/x.avif` would be misread as a hash path.
const hash = extractFullHash(item.path);
@@ -355,7 +355,7 @@ export function createAnalyzeImageTool({ googleAuthId, model }) {
throw new ToolError(
`Image not found or empty: "${item.path}". ` +
(hash
? 'Database images must be addressed as "image/<fullHash>" with a 64-char hash.'
? 'Database images must be addressed as "image/<fullHash>" with an 8-char hash.'
: 'Store it first (search_images writes to "images/…") or check the path with find_files.')
);
}

View File

@@ -134,7 +134,7 @@ function resolveImagesApiUrl() {
}
/**
* Fetches a database image by its 64-char content hash using store or authenticated local endpoint.
* Fetches a database image by its 8-char content hash using store or authenticated local endpoint.
* @param {string} hash
* @param {number} googleAuthId
* @returns {Promise<Buffer>}

View File

@@ -4,7 +4,7 @@ import { getStore } from '../db/store.js';
import env from '../config/env.js';
import { getDefaultSkills } from './skill.js';
const FULL_HASH_REGEX = /^[a-fA-F0-9]{64}$/;
const FULL_HASH_REGEX = /^[a-zA-Z0-9]{8}$/;
const VIRTUAL_IMAGE_FOLDERS = new Set(['image', 'images']);
export function getMimeType(filePath) {
@@ -55,18 +55,26 @@ export function getMimeType(filePath) {
export function extractFullHash(p) {
if (typeof p !== 'string' || !p.trim()) return null;
const clean = p.trim().replace(/^vfs:\/+/i, '').replace(/^([a-zA-Z]:|\/)+/, '');
const parts = clean.split('/');
const parts = clean.split('/').filter(Boolean);
if (parts.length === 0) return null;
const first = parts[0].toLowerCase();
const last = parts[parts.length - 1];
const ext = path.extname(last).toLowerCase();
const withoutExt = last.replace(/\.[a-zA-Z0-9]+$/, '');
if (FULL_HASH_REGEX.test(withoutExt)) {
return withoutExt.toLowerCase();
}
for (const part of parts) {
const seg = part.replace(/\.[a-zA-Z0-9]+$/, '');
if (FULL_HASH_REGEX.test(seg)) {
return seg.toLowerCase();
// Under virtual image folder (e.g. image/aB1cD2eF, images/aB1cD2eF.avif)
if (VIRTUAL_IMAGE_FOLDERS.has(first)) {
if (parts.length === 2 && FULL_HASH_REGEX.test(withoutExt)) {
return withoutExt;
}
}
// Direct hash (e.g. "aB1cD2eF" or "aB1cD2eF.avif")
if (parts.length === 1 && FULL_HASH_REGEX.test(withoutExt) && (!ext || ext === '.avif')) {
return withoutExt;
}
return null;
}