Skip to content
Open
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/whole-birds-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@openrouter/ai-sdk-provider": minor
---

Add reranking support via `rerankingModel()` and `reranking()` provider methods, implementing the `RerankingModelV3` interface for use with the `rerank()` function from the Vercel AI SDK.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,57 @@ OpenRouter supports various embedding models including:
- `openai/text-embedding-ada-002`
- And more available on [OpenRouter](https://openrouter.ai/models?output_modalities=embeddings)

## Reranking

OpenRouter supports reranking models for relevance scoring, search result ordering, and RAG pipeline optimization.

### Basic Usage

```ts
import { rerank } from 'ai';
import { openrouter } from '@openrouter/ai-sdk-provider';

const { rerankedDocuments } = await rerank({
model: openrouter.rerankingModel('cohere/rerank-v3.5'),
query: 'What is the capital of France?',
documents: [
'Berlin is the capital of Germany.',
'Paris is the capital of France.',
'Madrid is the capital of Spain.',
],
});

console.log(rerankedDocuments); // ['Paris is the capital of France.', ...]
```

### Limiting Results with topN

```ts
import { rerank } from 'ai';
import { openrouter } from '@openrouter/ai-sdk-provider';

const { rerankedDocuments } = await rerank({
model: openrouter.rerankingModel('cohere/rerank-v3.5'),
query: 'Famous landmarks in Paris',
documents: [
'The Eiffel Tower is in Paris.',
'The Colosseum is in Rome.',
'The Sagrada Familia is in Barcelona.',
'Big Ben is in London.',
],
topN: 2,
});

console.log(rerankedDocuments); // Top 2 most relevant documents
```

### Supported Reranking Models

OpenRouter supports various reranking models including:

- `cohere/rerank-v3.5`
- And more available on [OpenRouter](https://openrouter.ai/models?output_modalities=rerank)

## Passing Extra Body to OpenRouter

There are 3 ways to pass extra body to OpenRouter:
Expand Down
66 changes: 66 additions & 0 deletions e2e/rerank/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { rerank } from 'ai';
import { describe, expect, it, vi } from 'vitest';
import { createOpenRouter } from '../../src/index';

vi.setConfig({ testTimeout: 30_000 });

describe('OpenRouter Reranking E2E', () => {
const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
baseUrl: `${process.env.OPENROUTER_API_BASE}/api/v1`,
});

it('reranks documents and returns the most relevant first', async () => {
const documents = [
'Berlin is the capital of Germany.',
'Paris is the capital of France.',
'Madrid is the capital of Spain.',
'Rome is the capital of Italy.',
];
const query = 'What is the capital of France?';

const result = await rerank({
model: openrouter.rerankingModel('cohere/rerank-v3.5'),
documents,
query,
});

expect(result.rerankedDocuments).toHaveLength(documents.length);
expect(result.rerankedDocuments[0]).toBe('Paris is the capital of France.');

expect(result.ranking).toHaveLength(documents.length);
expect(result.ranking![0]!.score).toBeGreaterThan(0.5);
});

it('respects topN and returns only the top N results', async () => {
const documents = [
'The Eiffel Tower is in Paris.',
'The Colosseum is in Rome.',
'The Sagrada Familia is in Barcelona.',
'Big Ben is in London.',
'The Acropolis is in Athens.',
];

const result = await rerank({
model: openrouter.rerankingModel('cohere/rerank-v3.5'),
documents,
query: 'Famous landmark in Paris',
topN: 2,
});

expect(result.rerankedDocuments).toHaveLength(2);
expect(result.rerankedDocuments[0]).toBe('The Eiffel Tower is in Paris.');
});

it('reranking() alias works identically to rerankingModel()', async () => {
const result = await rerank({
model: openrouter.reranking('cohere/rerank-v3.5'),
documents: ['cat', 'dog', 'fish'],
query: 'aquatic animal',
topN: 1,
});

expect(result.rerankedDocuments).toHaveLength(1);
expect(result.rerankedDocuments[0]).toBe('fish');
});
});
25 changes: 25 additions & 0 deletions src/facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import type {
OpenRouterEmbeddingModelId,
OpenRouterEmbeddingSettings,
} from './types/openrouter-embedding-settings';
import type {
OpenRouterRerankModelId,
OpenRouterRerankSettings,
} from './types/openrouter-rerank-settings';

import { loadApiKey, withoutTrailingSlash } from '@ai-sdk/provider-utils';
import { OpenRouterChatLanguageModel } from './chat';
import { OpenRouterCompletionLanguageModel } from './completion';
import { OpenRouterEmbeddingModel } from './embedding';
import { OpenRouterRerankingModel } from './rerank';

/**
@deprecated Use `createOpenRouter` instead.
Expand Down Expand Up @@ -128,4 +133,24 @@ Custom headers to include in the requests.
) {
return this.textEmbeddingModel(modelId, settings);
}

rerankingModel(
modelId: OpenRouterRerankModelId,
settings: OpenRouterRerankSettings = {},
) {
return new OpenRouterRerankingModel(modelId, settings, {
...this.baseConfig,
url: ({ path }: { path: string }) => `${this.baseURL}${path}`,
});
}

/**
* @deprecated Use rerankingModel instead
*/
reranking(
modelId: OpenRouterRerankModelId,
settings: OpenRouterRerankSettings = {},
) {
return this.rerankingModel(modelId, settings);
}
}
2 changes: 2 additions & 0 deletions src/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ export * from '../chat';
export * from '../completion';
export * from '../embedding';
export * from '../image';
export * from '../rerank';
export * from '../types';
export * from '../types/openrouter-chat-settings';
export * from '../types/openrouter-completion-settings';
export * from '../types/openrouter-image-settings';
export * from '../types/openrouter-rerank-settings';
export * from '../types/openrouter-video-settings';
export * from '../video';
34 changes: 34 additions & 0 deletions src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import type {
OpenRouterImageModelId,
OpenRouterImageSettings,
} from './types/openrouter-image-settings';
import type {
OpenRouterRerankModelId,
OpenRouterRerankSettings,
} from './types/openrouter-rerank-settings';
import type {
OpenRouterVideoModelId,
OpenRouterVideoSettings,
Expand All @@ -27,6 +31,7 @@ import { OpenRouterChatLanguageModel } from './chat';
import { OpenRouterCompletionLanguageModel } from './completion';
import { OpenRouterEmbeddingModel } from './embedding';
import { OpenRouterImageModel } from './image';
import { OpenRouterRerankingModel } from './rerank';
import { webSearch } from './tool/web-search';
import { withUserAgentSuffix } from './utils/with-user-agent-suffix';
import { VERSION } from './version';
Expand Down Expand Up @@ -115,6 +120,22 @@ Creates an OpenRouter video model for video generation.
settings?: OpenRouterVideoSettings,
): OpenRouterVideoModel;

/**
Creates an OpenRouter reranking model.
*/
rerankingModel(
modelId: OpenRouterRerankModelId,
settings?: OpenRouterRerankSettings,
): OpenRouterRerankingModel;

/**
Creates an OpenRouter reranking model. Alias for rerankingModel.
*/
reranking(
modelId: OpenRouterRerankModelId,
settings?: OpenRouterRerankSettings,
): OpenRouterRerankingModel;

/**
* Provider-defined tools for OpenRouter server tools.
*/
Expand Down Expand Up @@ -280,6 +301,17 @@ export function createOpenRouter(
extraBody: options.extraBody,
});

const createRerankingModel = (
modelId: OpenRouterRerankModelId,
settings: OpenRouterRerankSettings = {},
) =>
new OpenRouterRerankingModel(modelId, settings, {
url: ({ path }) => `${baseURL}${path}`,
headers: getHeaders,
fetch: options.fetch,
extraBody: options.extraBody,
});

const createLanguageModel = (
modelId: OpenRouterChatModelId | OpenRouterCompletionModelId,
settings?: OpenRouterChatSettings | OpenRouterCompletionSettings,
Expand Down Expand Up @@ -312,6 +344,8 @@ export function createOpenRouter(
provider.embedding = createEmbeddingModel; // deprecated alias for v4 compatibility
provider.imageModel = createImageModel;
provider.videoModel = createVideoModel;
provider.rerankingModel = createRerankingModel;
provider.reranking = createRerankingModel;
provider.tools = {
webSearch: webSearch,
};
Expand Down
Loading
Loading