161 lines
4.5 KiB
TypeScript
161 lines
4.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
allocateDocumentTextBudgets,
|
|
buildDocumentBundle,
|
|
sortDocumentImagesForVision,
|
|
type ParsedDocumentPart,
|
|
} from '@/lib/document/bundle';
|
|
|
|
function part(order: number, overrides: Partial<ParsedDocumentPart> = {}): ParsedDocumentPart {
|
|
return {
|
|
source: {
|
|
id: `source-${order}`,
|
|
name: `Source ${order}.pdf`,
|
|
size: 1024,
|
|
lastModified: order,
|
|
mimeType: 'application/pdf',
|
|
order,
|
|
},
|
|
text: `Document ${order} references image_${order}.`,
|
|
rawTextLength: `Document ${order} references image_${order}.`.length,
|
|
pageCount: order + 1,
|
|
images: [
|
|
{
|
|
id: `image_${order}`,
|
|
src: `data:image/png;base64,${order}`,
|
|
pageNumber: order,
|
|
description: `figure ${order}`,
|
|
width: 100 + order,
|
|
height: 80 + order,
|
|
},
|
|
],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('document bundle', () => {
|
|
it('allocates a base text budget before proportional remainder', () => {
|
|
const budgets = allocateDocumentTextBudgets([100, 5000, 10000], 6000);
|
|
|
|
expect(budgets).toHaveLength(3);
|
|
expect(budgets[0]).toBe(100);
|
|
expect(budgets[1]).toBeGreaterThan(1500);
|
|
expect(budgets[2]).toBeGreaterThan(budgets[1]);
|
|
expect(budgets.reduce((sum, value) => sum + value, 0)).toBe(6000);
|
|
});
|
|
|
|
it('merges documents in source order and rewrites image IDs globally', () => {
|
|
const bundle = buildDocumentBundle([part(2), part(1)], {
|
|
maxChars: 2000,
|
|
maxVisionImages: 4,
|
|
});
|
|
|
|
expect(bundle.text.indexOf('## Source Document 1: Source 1.pdf')).toBeLessThan(
|
|
bundle.text.indexOf('## Source Document 2: Source 2.pdf'),
|
|
);
|
|
expect(bundle.text).toContain('Document 1 references img_1.');
|
|
expect(bundle.text).toContain('Document 2 references img_2.');
|
|
expect(bundle.images).toMatchObject([
|
|
{
|
|
id: 'img_1',
|
|
originalId: 'image_1',
|
|
sourceDocumentId: 'source-1',
|
|
sourceDocumentName: 'Source 1.pdf',
|
|
sourceDocumentOrder: 1,
|
|
},
|
|
{
|
|
id: 'img_2',
|
|
originalId: 'image_2',
|
|
sourceDocumentId: 'source-2',
|
|
sourceDocumentName: 'Source 2.pdf',
|
|
sourceDocumentOrder: 2,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('assigns vision priority round-robin across source documents', () => {
|
|
const bundle = buildDocumentBundle(
|
|
[
|
|
part(1, {
|
|
images: [
|
|
{
|
|
id: 'a1',
|
|
src: 'data:image/png;base64,a1',
|
|
pageNumber: 1,
|
|
description: 'first source primary',
|
|
width: 500,
|
|
height: 500,
|
|
},
|
|
{
|
|
id: 'a2',
|
|
src: 'data:image/png;base64,a2',
|
|
pageNumber: 2,
|
|
description: 'first source secondary',
|
|
width: 500,
|
|
height: 500,
|
|
},
|
|
],
|
|
}),
|
|
part(2, {
|
|
images: [
|
|
{
|
|
id: 'b1',
|
|
src: 'data:image/png;base64,b1',
|
|
pageNumber: 1,
|
|
description: 'second source primary',
|
|
width: 500,
|
|
height: 500,
|
|
},
|
|
],
|
|
}),
|
|
],
|
|
{ maxChars: 2000, maxVisionImages: 2 },
|
|
);
|
|
|
|
const priorities = new Map(
|
|
bundle.images.map((image) => [image.originalId, image.visionPriority]),
|
|
);
|
|
|
|
expect(priorities.get('a1')).toBe(2);
|
|
expect(priorities.get('b1')).toBe(1);
|
|
expect(priorities.get('a2')).toBe(0);
|
|
});
|
|
|
|
it('does not truncate into a rewritten image ID', () => {
|
|
const images = Array.from({ length: 11 }, (_, index) => ({
|
|
id: `image_${index + 1}`,
|
|
src: `data:image/png;base64,${index + 1}`,
|
|
pageNumber: 1,
|
|
}));
|
|
const header =
|
|
'## Source Document 1: Source 1.pdf\n' +
|
|
'- Order: 1\n' +
|
|
'- MIME type: application/pdf\n' +
|
|
'- Pages: 2\n\n';
|
|
|
|
const bundle = buildDocumentBundle(
|
|
[
|
|
part(1, {
|
|
text: 'prefix image_11 suffix',
|
|
rawTextLength: 'prefix image_11 suffix'.length,
|
|
images,
|
|
}),
|
|
],
|
|
{ maxChars: header.length + 'prefix img_1'.length, maxVisionImages: 4 },
|
|
);
|
|
|
|
expect(bundle.text).not.toContain('prefix img_1');
|
|
});
|
|
|
|
it('sorts same-priority img_N IDs numerically', () => {
|
|
const sorted = sortDocumentImagesForVision([
|
|
{ id: 'img_10', pageNumber: 1, visionPriority: 0 },
|
|
{ id: 'img_2', pageNumber: 1, visionPriority: 0 },
|
|
{ id: 'img_1', pageNumber: 1, visionPriority: 0 },
|
|
]);
|
|
|
|
expect(sorted.map((image) => image.id)).toEqual(['img_1', 'img_2', 'img_10']);
|
|
});
|
|
});
|