Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions __tests__/api/agent/references.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { buildReferences, extractRecommendedPages } from "~/app/api/agents/documentQ&A/services/references";
import type { SearchResult } from "~/lib/tools/rag";

describe("references service", () => {
it("extracts sorted unique recommended pages and ignores invalid values", () => {
const docs: SearchResult[] = [
{ pageContent: "A", metadata: { searchScope: "document", page: 3 } },
{ pageContent: "B", metadata: { searchScope: "document", page: 1 } },
{ pageContent: "C", metadata: { searchScope: "document", page: 3 } },
{ pageContent: "D", metadata: { searchScope: "document", page: 0 } },
{ pageContent: "E", metadata: { searchScope: "document" } },
];

expect(extractRecommendedPages(docs)).toEqual([1, 3]);
});

it("builds query-aware snippets using childContent when available", () => {
const docs: SearchResult[] = [
{
pageContent: "General parent content that does not include the key phrase.",
metadata: {
searchScope: "document",
chunkId: 11,
page: 4,
documentId: 99,
documentTitle: "Policy Handbook",
source: "vector_ann",
childContent: "Vacation policy states employees receive 15 days paid time off annually.",
} as SearchResult["metadata"] & { childContent: string },
},
];

const refs = buildReferences("How many vacation days do employees receive?", docs);
expect(refs).toHaveLength(1);
expect(refs[0]?.page).toBe(4);
expect(refs[0]?.documentTitle).toBe("Policy Handbook");
expect(refs[0]?.snippet.toLowerCase()).toContain("vacation");
});

it("falls back to prefix snippet and omits invalid page numbers", () => {
const docs: SearchResult[] = [
{
pageContent:
"This section explains onboarding procedures and account setup for new hires in detail.",
metadata: {
searchScope: "document",
page: 0,
documentId: 10,
},
},
];

const refs = buildReferences("What is covered here?", docs);
expect(refs).toHaveLength(1);
expect(refs[0]?.page).toBeUndefined();
expect(refs[0]?.snippet.length).toBeGreaterThan(20);
});
});
25 changes: 13 additions & 12 deletions __tests__/api/fetchDocument/fetchDocument.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { POST } from "~/app/api/fetchDocument/route";
import { auth } from "@clerk/nextjs/server";
import { validateRequestBody } from "~/lib/validation";
import { db } from "~/server/db/index";
import { dbCore } from "~/server/db/core";

jest.mock("@clerk/nextjs/server", () => ({
auth: jest.fn(),
Expand All @@ -11,8 +11,9 @@ jest.mock("~/lib/validation", () => ({
validateRequestBody: jest.fn(),
}));

jest.mock("~/server/db/index", () => ({
db: {
// Route uses dbCore from core, not db from index
jest.mock("~/server/db/core", () => ({
dbCore: {
select: jest.fn(),
},
}));
Expand Down Expand Up @@ -53,7 +54,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -91,7 +92,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -121,7 +122,7 @@ describe("POST /api/fetchDocument", () => {
where: jest.fn().mockResolvedValue([]),
}),
});
(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -199,7 +200,7 @@ describe("POST /api/fetchDocument", () => {
where: jest.fn().mockRejectedValue(new Error("Database connection failed")),
}),
});
(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -245,7 +246,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -277,7 +278,7 @@ describe("POST /api/fetchDocument", () => {
where: jest.fn().mockResolvedValue([]),
}),
});
(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -320,7 +321,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -365,7 +366,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down Expand Up @@ -409,7 +410,7 @@ describe("POST /api/fetchDocument", () => {
}),
});

(db.select as jest.Mock) = mockSelect;
(dbCore.select as jest.Mock) = mockSelect;

const request = new Request("http://localhost/api/fetchDocument", {
method: "POST",
Expand Down
9 changes: 2 additions & 7 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,8 @@ const config: NextConfig = {
// Exclude HuggingFace heavy files since we lazy load
"node_modules/.pnpm/@huggingface+transformers@*/node_modules/@huggingface/transformers/dist/**/*.wasm",
"node_modules/.pnpm/@huggingface+transformers@*/node_modules/@huggingface/transformers/models/**",
// Exclude pdfjs heavy files since we lazy load
// Exclude pdfjs heavy files since we lazy load (keep legacy build - used for Node.js/Inngest)
"node_modules/.pnpm/pdfjs-dist@*/node_modules/pdfjs-dist/build/**/*.map",
"node_modules/.pnpm/pdfjs-dist@*/node_modules/pdfjs-dist/legacy/**",
// Exclude pdf-parse test data (8MB)
"node_modules/.pnpm/pdf-parse@*/node_modules/pdf-parse/test/**",
"node_modules/pdf-parse/test/**",
Expand All @@ -79,16 +78,12 @@ const config: NextConfig = {
serverExternalPackages: [
"@huggingface/transformers",
"pdf2pic",
"pdfjs-dist",
"pdfjs-serverless",
"onnxruntime-web",
"onnxruntime-node",
"sharp",
"@img/sharp-libvips-linuxmusl-x64",
"@img/sharp-libvips-linux-x64",
"pdf-parse",
"pdf-lib",
"canvas",
"@napi-rs/canvas",
],
};

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"pdf-parse": "^1.1.1",
"pdf2pic": "^3.2.0",
"pdfjs-dist": "^5.4.530",
"pdfjs-serverless": "^1.1.0",
"postgres": "^3.4.7",
"prom-client": "^15.1.3",
"re-resizable": "^6.11.2",
Expand All @@ -107,6 +108,7 @@
"react-dom": "^18.3.1",
"react-hook-form": "^7.55.0",
"react-markdown": "^9.0.0",
"react-pdf": "^10.3.0",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.15.2",
"rehype-katex": "^7.0.1",
Expand Down
73 changes: 73 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading