-
Notifications
You must be signed in to change notification settings - Fork 56
Feat/add OpenAI reranking #288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
027ea8d
chore: refactoring reranking module
Ahmath-Gadji db697e4
feat(reranker): add RERANKER_PROVIDER to select reranker backend
Ahmath-Gadji 4ffafee
fix: read reranker timeout from config instead of hardcoding
EnjoyBacon7 4f48b94
refactor: use ABC and fix type hint in BaseReranker
EnjoyBacon7 feffdae
fix: standardize config access to dot notation and fix semaphore default
EnjoyBacon7 753deeb
fix: align RERANKER_SEMAPHORE doc default with config model (5, not 40)
EnjoyBacon7 a774432
fix: remove stale RERANKER_PORT fallback
EnjoyBacon7 b54fdf9
fix: reuse httpx.AsyncClient in OpenAIReranker
EnjoyBacon7 360d9e5
refactor: lazy-import reranker backends in factory
EnjoyBacon7 905be0a
fix: add trailing newline to openai.yaml
EnjoyBacon7 2c180c6
fix: resolve CodeRabbit review findings for reranker config
EnjoyBacon7 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| x-vllm-env: &vllm_env | ||
| HUGGING_FACE_HUB_TOKEN: | ||
| VLLM_SLEEP_WHEN_IDLE: 1 # Avoid 100% CPU usage when idle | ||
|
|
||
| x-reranker: &reranker_template | ||
| networks: | ||
| default: | ||
| aliases: | ||
| - reranker | ||
| # restart: on-failure | ||
| environment: | ||
| - HUGGING_FACE_HUB_TOKEN | ||
| - VLLM_SLEEP_WHEN_IDLE=1 # Avoid 100% CPU usage when idle | ||
| ipc: "host" | ||
| volumes: | ||
| - ${VLLM_CACHE:-/root/.cache/huggingface}:/root/.cache/huggingface | ||
| command: > | ||
| --model ${RERANKER_MODEL:-BAAI/bge-reranker-v2-m3} | ||
| --trust-remote-code | ||
| --api-key ${RERANKER_API_KEY:-"EMPTY"} | ||
| --gpu_memory_utilization 0.3 | ||
| healthcheck: | ||
| test: ["CMD", "curl", "-f", "http://localhost:8000/health"] | ||
| interval: 20s | ||
| timeout: 5s | ||
| retries: 4 | ||
| start_period: 90s | ||
| # ports: | ||
| # - ${RERANKER_PORT:-8000}:8000 | ||
|
|
||
| services: | ||
| reranker-gpu: | ||
| <<: *reranker_template | ||
| image: vllm/vllm-openai:v0.17.1 | ||
| environment: | ||
| <<: *vllm_env | ||
| NVIDIA_VISIBLE_DEVICES: all | ||
| NVIDIA_DRIVER_CAPABILITIES: compute,utility | ||
| runtime: nvidia | ||
| profiles: | ||
| - "" | ||
|
EnjoyBacon7 marked this conversation as resolved.
|
||
| deploy: | ||
| resources: | ||
| reservations: | ||
| devices: | ||
| - driver: nvidia | ||
| count: all | ||
| capabilities: [gpu] | ||
|
|
||
| reranker-cpu: | ||
| <<: *reranker_template | ||
| image: vllm/vllm-openai-cpu:v0.17.1 | ||
| deploy: {} | ||
| environment: | ||
| <<: *vllm_env | ||
| VLLM_CPU_KVCACHE_SPACE: 8 | ||
| command: > | ||
| --model ${RERANKER_MODEL:-BAAI/bge-reranker-v2-m3} | ||
| --trust-remote-code | ||
| --api-key ${RERANKER_API_KEY:-"EMPTY"} | ||
| --dtype float32 | ||
| profiles: | ||
| - "cpu" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| from .base import BaseReranker | ||
|
|
||
|
|
||
| class RerankerFactory: | ||
| @staticmethod | ||
| def get_reranker(config) -> BaseReranker: | ||
| provider = config.reranker.provider | ||
| if provider == "infinity": | ||
| from .infinity import InfinityReranker | ||
|
|
||
| return InfinityReranker(config) | ||
| elif provider == "openai": | ||
| from .openai import OpenAIReranker | ||
|
|
||
| return OpenAIReranker(config) | ||
| else: | ||
| raise ValueError(f"Unsupported reranker provider: {provider}") | ||
|
EnjoyBacon7 marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| from abc import ABC, abstractmethod | ||
|
|
||
| from langchain_core.documents.base import Document | ||
|
|
||
|
|
||
| class BaseReranker(ABC): | ||
| @abstractmethod | ||
| async def rerank(self, query: str, documents: list[Document], top_k: int | None = None) -> list[Document]: | ||
| """Rerank a list of documents based on a query and an optional top_k parameter""" | ||
|
|
||
| @staticmethod | ||
| def rrf_reranking(doc_lists: list[list[Document]], k: int = 60) -> list[Document]: | ||
| """Reciprocal_rank_fusion that takes multiple lists of ranked documents | ||
| and an optional parameter k used in the RRF formula | ||
| RRF formula: \\sum_{i=1}^{n} \frac{1}{k + rank_i} | ||
| where rank_i is the rank of the document in the i-th list and n is the number of lists. | ||
|
|
||
| k small: High sensitivity to top ranks | ||
| k large: More balanced sensitivity across ranks | ||
| k = 60 a common and balanced choice in practice. | ||
| """ | ||
|
|
||
| if len(doc_lists) == 1: | ||
| return doc_lists[0] | ||
|
|
||
| # Initialize a dictionary to hold fused scores for each unique document | ||
| fused_scores = {} | ||
|
|
||
| for doc_list in doc_lists: | ||
| doc_list: list[Document] | ||
| for rank, doc in enumerate(doc_list, start=1): | ||
| doc_id = doc.metadata.get("_id") | ||
| doc_key = ("id", doc_id) if doc_id is not None else ("object", id(doc)) | ||
|
|
||
| score, d = fused_scores.get(doc_key, (0, doc)) | ||
| fused_scores[doc_key] = (score + 1 / (rank + k), d) | ||
|
|
||
| # sort the docs | ||
| reranked_docs = [doc for _, doc in sorted(fused_scores.values(), key=lambda x: x[0], reverse=True)] | ||
| return reranked_docs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import asyncio | ||
|
|
||
| from infinity_client import Client | ||
| from infinity_client.api.default import rerank | ||
| from infinity_client.models import RerankInput, ReRankResult | ||
| from langchain_core.documents.base import Document | ||
| from utils.logger import get_logger | ||
|
Ahmath-Gadji marked this conversation as resolved.
|
||
|
|
||
| from .base import BaseReranker | ||
|
|
||
| logger = get_logger() | ||
|
|
||
|
|
||
| class InfinityReranker(BaseReranker): | ||
| def __init__(self, config): | ||
| self.model_name = config.reranker.model_name | ||
| self.client = Client( | ||
| base_url=config.reranker.base_url, | ||
| timeout=config.reranker.timeout, | ||
| headers={"Authorization": f"Bearer {config.reranker.api_key}"}, | ||
| ) | ||
| self.semaphore = asyncio.Semaphore(config.reranker.semaphore) | ||
| logger.debug("Reranker initialized", model_name=self.model_name) | ||
|
|
||
| async def rerank(self, query: str, documents: list[Document], top_k: int | None = None) -> list[Document]: | ||
| async with self.semaphore: | ||
| logger.debug("Reranking documents", documents_count=len(documents), top_k=top_k) | ||
| top_k = min(top_k, len(documents)) if top_k is not None else len(documents) | ||
| rerank_input = RerankInput.from_dict( | ||
| { | ||
| "model": self.model_name, | ||
| "query": query, | ||
| "documents": [doc.page_content for doc in documents], | ||
| "top_n": top_k, | ||
| "return_documents": True, | ||
| "raw_scores": True, # Normalized score between 0 and 1 | ||
| } | ||
| ) | ||
| try: | ||
| rerank_result: ReRankResult = await rerank.asyncio(client=self.client, body=rerank_input) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| output = [] | ||
| for rerank_res in rerank_result.results: | ||
| doc = documents[rerank_res.index] | ||
| doc.metadata["relevance_score"] = rerank_res.relevance_score | ||
| output.append(doc) | ||
| return output | ||
|
|
||
| except Exception as e: | ||
| logger.error( | ||
| "Reranking failed", | ||
| error=str(e), | ||
| model_name=self.model_name, | ||
| documents_count=len(documents), | ||
| ) | ||
| return documents[:top_k] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import asyncio | ||
|
|
||
| import httpx | ||
| from langchain_core.documents.base import Document | ||
| from utils.logger import get_logger | ||
|
Ahmath-Gadji marked this conversation as resolved.
|
||
|
|
||
| from .base import BaseReranker | ||
|
|
||
| logger = get_logger() | ||
|
|
||
|
|
||
| class OpenAIReranker(BaseReranker): | ||
| def __init__(self, config): | ||
| self.model_name = config.reranker.model_name | ||
| base_url = config.reranker.base_url.rstrip("/") | ||
| self.rerank_url = f"{base_url}/rerank" | ||
| self.semaphore = asyncio.Semaphore(config.reranker.semaphore) | ||
| self.timeout = config.reranker.timeout | ||
| self.client = httpx.AsyncClient( | ||
| headers={"Authorization": f"Bearer {config.reranker.api_key}"}, | ||
| ) | ||
| logger.debug("OpenAI Reranker initialized", model_name=self.model_name) | ||
|
|
||
| async def rerank(self, query: str, documents: list[Document], top_k: int | None = None) -> list[Document]: | ||
| async with self.semaphore: | ||
| logger.debug("Reranking documents", documents_count=len(documents), top_k=top_k) | ||
| top_k = min(top_k, len(documents)) if top_k is not None else len(documents) | ||
| try: | ||
| response = await self.client.post( | ||
| self.rerank_url, | ||
| json={ | ||
| "model": self.model_name, | ||
| "query": query, | ||
| "documents": [doc.page_content for doc in documents], | ||
| "top_n": top_k, | ||
| }, | ||
| timeout=self.timeout, | ||
| ) | ||
| response.raise_for_status() | ||
| data = response.json() | ||
|
|
||
| output = [] | ||
| for result in data["results"]: | ||
| doc = documents[result["index"]] | ||
| doc.metadata["relevance_score"] = result["relevance_score"] | ||
| output.append(doc) | ||
| return output | ||
|
|
||
| except Exception as e: | ||
| logger.error( | ||
| "Reranking failed", | ||
| error=str(e), | ||
| model_name=self.model_name, | ||
| documents_count=len(documents), | ||
| ) | ||
| return documents[:top_k] | ||
3 changes: 2 additions & 1 deletion
3
openrag/components/test_rrf_reranking.py → ...components/reranker/test_rrf_reranking.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.