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
5 changes: 5 additions & 0 deletions cortex/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ function dot(a: Float32Array, b: Float32Array): number {
return sum;
}

/**
* Concatenates an array of equal-length vectors into a single flat buffer.
* @param vectors - Must be non-empty; every element must have the same length.
*/
function concatVectors(vectors: Float32Array[]): Float32Array {
const dim = vectors[0].length;
const out = new Float32Array(vectors.length * dim);
Expand Down Expand Up @@ -129,6 +133,7 @@ export async function query(
}

const combined = [...hotpathResults, ...coldResults];
combined.sort((a, b) => b.score - a.score);

// Update activity for returned pages
await Promise.all(combined.map(async ({ page }) => {
Expand Down
6 changes: 5 additions & 1 deletion storage/IndexedDbMetadataStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ export class IndexedDbMetadataStore implements MetadataStore {
return this._get<Page>(STORE.pages, pageId);
}

/** Returns all pages in the store. Used for warm/cold fallbacks in query. */
/**
* Returns all pages in the store. Used for warm/cold fallbacks in query.
* TODO: Replace with a paginated or indexed scan before production use —
* loading every page into memory is expensive for large corpora.
*/
async getAllPages(): Promise<Page[]> {
return new Promise((resolve, reject) => {
const tx = this.db.transaction(STORE.pages, "readonly");
Expand Down
4 changes: 3 additions & 1 deletion tests/SalienceEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ class MockMetadataStore implements MetadataStore {

// --- Stubs for unused MetadataStore methods ---
async putPage(): Promise<void> { /* stub */ }
async getPage(): Promise<undefined> { return undefined; } async getAllPages(): Promise<any[]> { return []; } async putBook(): Promise<void> { /* stub */ }
async getPage(): Promise<undefined> { return undefined; }
async getAllPages(): Promise<any[]> { return []; }
async putBook(): Promise<void> { /* stub */ }
async getBook(): Promise<undefined> { return undefined; }
async putVolume(): Promise<void> { /* stub */ }
async getVolume(): Promise<undefined> { return undefined; }
Expand Down
94 changes: 94 additions & 0 deletions tests/cortex/Query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,44 @@ describe("cortex query (minimal)", () => {
(globalThis as any).IDBKeyRange = FakeIDBKeyRange;
});

it("returns empty results for an empty corpus", async () => {
const metadataStore = await IndexedDbMetadataStore.open(freshDbName());
const vectorStore = new MemoryVectorStore();

const backend = new DeterministicDummyEmbeddingBackend({ dimension: 4 });
const vectorBackend = new TestVectorBackend();

const runner = new EmbeddingRunner(async () => ({
backend,
selectedKind: "dummy" as const,
reason: "forced" as const,
supportedKinds: ["dummy" as const],
measurements: [],
}));

const profile: ModelProfile = {
modelId: "test-model",
embeddingDimension: 4,
contextWindowTokens: 64,
truncationTokens: 48,
maxChunkTokens: 5,
source: "metadata",
};

const result = await query("anything", {
modelProfile: profile,
embeddingRunner: runner,
vectorStore,
metadataStore,
vectorBackend,
topK: 5,
});

expect(result.pages).toHaveLength(0);
expect(result.scores).toHaveLength(0);
expect(result.metadata.returned).toBe(0);
});

it("returns the most relevant page and updates activity", async () => {
const metadataStore = await IndexedDbMetadataStore.open(freshDbName());
const vectorStore = new MemoryVectorStore();
Expand Down Expand Up @@ -121,4 +159,60 @@ describe("cortex query (minimal)", () => {
expect(activity?.queryHitCount).toBe(1);
expect(activity?.lastQueryAt).toBeDefined();
});

it("returns results in descending score order (relevance)", async () => {
const metadataStore = await IndexedDbMetadataStore.open(freshDbName());
const vectorStore = new MemoryVectorStore();
const keyPair = await generateKeyPair();

const backend = new DeterministicDummyEmbeddingBackend({ dimension: 4 });
const vectorBackend = new TestVectorBackend();

const runner = new EmbeddingRunner(async () => ({
backend,
selectedKind: "dummy" as const,
reason: "forced" as const,
supportedKinds: ["dummy" as const],
measurements: [],
}));

const profile: ModelProfile = {
modelId: "test-model",
embeddingDimension: 4,
contextWindowTokens: 64,
truncationTokens: 48,
maxChunkTokens: 5,
source: "metadata",
};

const text = "One two three four five six seven eight nine ten.";
const ingestResult = await ingestText(text, {
modelProfile: profile,
embeddingRunner: runner,
vectorStore,
metadataStore,
keyPair,
});

expect(ingestResult.pages.length).toBeGreaterThanOrEqual(2);

const targetPage = ingestResult.pages[0];

const result = await query(targetPage.content, {
modelProfile: profile,
embeddingRunner: runner,
vectorStore,
metadataStore,
vectorBackend,
topK: ingestResult.pages.length,
});

// Results must include the page whose content matches the query.
expect(result.pages.map((p) => p.pageId)).toContain(targetPage.pageId);

// Scores must be in non-increasing order.
for (let i = 1; i < result.scores.length; i++) {
expect(result.scores[i]).toBeLessThanOrEqual(result.scores[i - 1]);
}
});
});
Loading