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 .changeset/reranker-topn-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chatcops/core": patch
---

Validate topN in cohereReranker and voyageReranker to immediately return an empty array for NaN.
2 changes: 1 addition & 1 deletion packages/core/src/retrieval/cohere-reranker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function cohereReranker(options: CohereRerankerOptions): Reranker {
return {
async rerank(query, chunks, topN, rerankOptions = {}) {
const limit = Math.floor(topN);
if (chunks.length === 0 || limit <= 0) return [];
if (chunks.length === 0 || !(limit > 0)) return [];

const response = await postWithRetries(
'Cohere',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/retrieval/voyage-reranker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function voyageReranker(options: VoyageRerankerOptions): Reranker {
return {
async rerank(query, chunks, topN, rerankOptions = {}) {
const limit = Math.floor(topN);
if (chunks.length === 0 || limit <= 0) return [];
if (chunks.length === 0 || !(limit > 0)) return [];

const response = await postWithRetries(
'Voyage',
Expand Down
15 changes: 15 additions & 0 deletions packages/core/tests/retrieval/cohere-reranker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ describe('cohereReranker', () => {
expect(send).not.toHaveBeenCalled();
});

it('returns no results and makes no request for NaN and -Infinity topN values', async () => {
const send = vi.fn(async () => json({ results: [] }));
const reranker = cohereReranker({ apiKey: 'k', fetch: send });
expect(await reranker.rerank('q', chunks, Number.NaN)).toEqual([]);
expect(await reranker.rerank('q', chunks, -Infinity)).toEqual([]);
expect(send).not.toHaveBeenCalled();
});

it('asks for no more than available documents when topN is Infinity', async () => {
const send = vi.fn(async () => json({ results: [] }));
const reranker = cohereReranker({ apiKey: 'k', fetch: send });
await reranker.rerank('q', chunks, Infinity);
expect(JSON.parse(String((send.mock.calls[0] as unknown as [string, RequestInit])[1].body))).toMatchObject({ top_n: 3 });
});

it('surfaces client API errors without retrying', async () => {
const send = vi.fn(async () => json({ message: 'Invalid model' }, 400));
const reranker = cohereReranker({ apiKey: 'k', fetch: send });
Expand Down
15 changes: 15 additions & 0 deletions packages/core/tests/retrieval/voyage-reranker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ describe('voyageReranker', () => {
expect(send).not.toHaveBeenCalled();
});

it('returns no results and makes no request for NaN and -Infinity topN values', async () => {
const send = vi.fn(async () => json({ object: 'list', data: [] }));
const reranker = voyageReranker({ apiKey: 'k', fetch: send });
expect(await reranker.rerank('q', chunks, Number.NaN)).toEqual([]);
expect(await reranker.rerank('q', chunks, -Infinity)).toEqual([]);
expect(send).not.toHaveBeenCalled();
});

it('caps top_k to available documents when topN is Infinity', async () => {
const send = vi.fn(async () => json({ object: 'list', data: [] }));
const reranker = voyageReranker({ apiKey: 'k', fetch: send });
await reranker.rerank('q', chunks, Infinity);
expect(JSON.parse(String((send.mock.calls[0] as unknown as [string, RequestInit])[1].body))).toMatchObject({ top_k: 3 });
});

it('surfaces client API errors without retrying', async () => {
const send = vi.fn(async () => json({ detail: 'Provided model is not supported.' }, 400));
const reranker = voyageReranker({ apiKey: 'k', fetch: send });
Expand Down
Loading