Refine multi-image target resolution

This commit is contained in:
134 2026-06-24 19:02:20 +08:00
parent cc80bd5dac
commit d10d638119
2 changed files with 93 additions and 29 deletions

View File

@ -6484,6 +6484,10 @@ export function CanvasChatPanel({
const selectedContextNodes = selectedImageNodesForContext.slice(0, 20);
generationAnchorNodesRef.current = selectedContextNodes.slice(0, 4);
const selectedIds = new Set(selectedContextNodes.map((node) => node.id));
const selectedPayloadNodeIds =
selectedContextNodes.length > 0
? selectedContextNodes.map((node) => node.id).slice(0, 20)
: selectedCanvasNodes.map((node) => node.id).slice(0, 20);
const prioritizedNodes =
shouldIsolateDirectModeContext
? selectedContextNodes
@ -6715,7 +6719,7 @@ export function CanvasChatPanel({
return {
source: "canvas" as const,
projectId,
selectedNodeIds: selectedCanvasNodes.map((node) => node.id).slice(0, 20),
selectedNodeIds: selectedPayloadNodeIds,
selectedAssetIds,
focusPolicy: agentFocusPolicy,
canvasImageNodes: contextNodes.map((node) => ({

View File

@ -97,25 +97,76 @@ function getPositionLabel(ref: ResolvedImageRef, refs: ResolvedImageRef[]): stri
return `${vertical}${horizontal}`;
}
function collectOrdinalIndexes(text: string): number[] {
const indexes = new Set<number>();
const normalizedText = String(text || "");
const chineseOrdinalPattern =
/第\s*([一二三四五六七八九十两\d]+)\s*(?:张|个|幅)?\s*(?:图|图片|照片)?/gi;
const numberedImagePattern = /(?:图|图片|照片|image|photo|reference)\s*([1-9]\d*)\b/gi;
const englishOrdinals: Array<{ index: number; pattern: RegExp }> = [
{ index: 0, pattern: /\b(?:first|1st)\s+(?:image|photo|reference)\b/i },
{ index: 1, pattern: /\b(?:second|2nd)\s+(?:image|photo|reference)\b/i },
{ index: 2, pattern: /\b(?:third|3rd)\s+(?:image|photo|reference)\b/i },
{ index: 3, pattern: /\b(?:fourth|4th)\s+(?:image|photo|reference)\b/i },
{ index: 4, pattern: /\b(?:fifth|5th)\s+(?:image|photo|reference)\b/i },
{ index: 5, pattern: /\b(?:sixth|6th)\s+(?:image|photo|reference)\b/i },
{ index: 6, pattern: /\b(?:seventh|7th)\s+(?:image|photo|reference)\b/i },
{ index: 7, pattern: /\b(?:eighth|8th)\s+(?:image|photo|reference)\b/i },
];
for (const match of normalizedText.matchAll(chineseOrdinalPattern)) {
const numeric = chineseNumberToInt(match[1] || "");
if (numeric && numeric > 0) indexes.add(numeric - 1);
}
for (const match of normalizedText.matchAll(numberedImagePattern)) {
const numeric = Number.parseInt(match[1] || "", 10);
if (Number.isFinite(numeric) && numeric > 0) indexes.add(numeric - 1);
}
for (const item of englishOrdinals) {
if (item.pattern.test(normalizedText)) indexes.add(item.index);
}
return Array.from(indexes).sort((left, right) => left - right);
}
function pickByOrdinal(text: string, refs: ResolvedImageRef[]): ResolvedImageRef[] {
const matched = text.match(/第\s*([一二三四五六七八九十两\d]+)\s*(?:张|个|幅|图)/i);
const numeric = matched ? chineseNumberToInt(matched[1]) : null;
if (!numeric || numeric <= 0) return [];
const ordinalIndexes = collectOrdinalIndexes(text);
if (ordinalIndexes.length === 0) return [];
const ordered = sortRefsForCanvasOrdering(refs);
return ordered[numeric - 1] ? [ordered[numeric - 1]] : [];
return ordinalIndexes
.map((index) => ordered[index])
.filter((ref): ref is ResolvedImageRef => Boolean(ref));
}
function pickBySpatialHint(text: string, refs: ResolvedImageRef[]): ResolvedImageRef[] {
if (!refs.length) return [];
const normalizedText = text.replace(/\s+/g, "");
const hasLeft = /左边|左侧|左面|左上|左下|靠左/.test(normalizedText);
const hasRight = /右边|右侧|右面|右上|右下|靠右/.test(normalizedText);
const hasTop = /上面|上方|上边|上侧|顶部|靠上|最上/.test(normalizedText);
const hasBottom = /下面|下方|下边|下侧|底部|靠下|最下/.test(normalizedText);
const hasLeftTop = /左上/.test(normalizedText);
const hasLeftBottom = /左下/.test(normalizedText);
const hasRightTop = /右上/.test(normalizedText);
const hasRightBottom = /右下/.test(normalizedText);
const hasLeft = /左图|左边|左侧|左面|靠左/.test(normalizedText);
const hasRight = /右图|右边|右侧|右面|靠右/.test(normalizedText);
const hasTop = /上图|上面|上方|上边|上侧|顶部|靠上|最上/.test(normalizedText);
const hasBottom = /下图|下面|下方|下边|下侧|底部|靠下|最下/.test(normalizedText);
const hasCenter = /中间|居中|正中|中央/.test(normalizedText);
if (!(hasLeft || hasRight || hasTop || hasBottom || hasCenter)) return [];
if (
!(
hasLeftTop ||
hasLeftBottom ||
hasRightTop ||
hasRightBottom ||
hasLeft ||
hasRight ||
hasTop ||
hasBottom ||
hasCenter
)
) {
return [];
}
const withLabels = refs.map((ref) => ({
ref,
@ -124,12 +175,18 @@ function pickBySpatialHint(text: string, refs: ResolvedImageRef[]): ResolvedImag
return withLabels
.filter(({ label }) => {
if (hasLeft && !label.includes("左")) return false;
if (hasRight && !label.includes("右")) return false;
if (hasTop && !label.includes("上")) return false;
if (hasBottom && !label.includes("下")) return false;
if (hasCenter && !(label.includes("中") || label === "中间")) return false;
return true;
const matchesCompound =
(hasLeftTop && label.includes("左") && label.includes("上")) ||
(hasLeftBottom && label.includes("左") && label.includes("下")) ||
(hasRightTop && label.includes("右") && label.includes("上")) ||
(hasRightBottom && label.includes("右") && label.includes("下"));
const matchesAxis =
(hasLeft && label.includes("左")) ||
(hasRight && label.includes("右")) ||
(hasTop && label.includes("上")) ||
(hasBottom && label.includes("下")) ||
(hasCenter && (label.includes("中") || label === "中间"));
return matchesCompound || matchesAxis;
})
.map(({ ref }) => ref);
}
@ -204,6 +261,22 @@ export function resolveTargetAssets(
);
})();
const ordinalRefs = pickByOrdinal(text, canvasRefs);
if (ordinalRefs.length > 0) {
return buildResolvedTargetAssets({
targetRefs: ordinalRefs,
candidateRefs,
resolutionMode: "text_reference",
confidence: ordinalRefs.length > 1 ? 91 : 88,
needsClarification: false,
reason:
ordinalRefs.length > 1
? "根据用户文本中的多个序号定位到多张画布图片"
: "根据用户文本中的序号定位到画布图片",
fromHistory: true,
});
}
if (ORIGINAL_IMAGE_REGEX.test(text) && originalRefs.length > 0) {
return buildResolvedTargetAssets({
targetRefs: originalRefs,
@ -252,19 +325,6 @@ export function resolveTargetAssets(
});
}
const ordinalRefs = pickByOrdinal(text, canvasRefs);
if (ordinalRefs.length > 0) {
return buildResolvedTargetAssets({
targetRefs: ordinalRefs,
candidateRefs,
resolutionMode: "text_reference",
confidence: 88,
needsClarification: false,
reason: "根据用户文本中的序号定位到画布图片",
fromHistory: true,
});
}
const spatialRefs = pickBySpatialHint(text, canvasRefs);
if (spatialRefs.length > 0) {
return buildResolvedTargetAssets({