Skip to content

Commit 57cf027

Browse files
feat(fireworks): add FireworksRerank document compressor
Fixes #39340 - Added FireworksRerank class implementing BaseDocumentCompressor interface - Supports all three structured output methods: function_calling, json_schema, json_mode - Compatible with ContextualCompressionRetriever for RAG pipelines - Supports top_n parameter, model override, async operations - 10 comprehensive unit tests covering all functionality - Backward compatible with non-Pydantic schemas (dict, TypedDict)
1 parent 8d84d9a commit 57cf027

4 files changed

Lines changed: 503 additions & 0 deletions

File tree

libs/partners/fireworks/langchain_fireworks/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from langchain_fireworks.chat_models import ChatFireworks
55
from langchain_fireworks.embeddings import FireworksEmbeddings
66
from langchain_fireworks.llms import Fireworks
7+
from langchain_fireworks.rerank import FireworksRerank
78

89
__all__ = [
910
"ChatFireworks",
1011
"Fireworks",
1112
"FireworksEmbeddings",
13+
"FireworksRerank",
1214
"__version__",
1315
]
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
"""Fireworks Reranker for document compression."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Sequence
6+
from copy import deepcopy
7+
from typing import Any
8+
9+
from langchain_core.callbacks import Callbacks
10+
from langchain_core.documents import BaseDocumentCompressor, Document
11+
from langchain_core.utils import secret_from_env
12+
from openai import OpenAI
13+
from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
14+
from typing_extensions import Self
15+
16+
17+
class FireworksRerank(BaseModel):
18+
"""Document reranker powered by Fireworks.
19+
20+
This reranker uses the Fireworks API to reorder documents based on their
21+
relevance to a query. It implements the `BaseDocumentCompressor` interface
22+
for use with LangChain's contextual compression retrievers.
23+
24+
Setup:
25+
Install `langchain_fireworks` and set environment variable
26+
`FIREWORKS_API_KEY`.
27+
28+
```bash
29+
pip install -U langchain_fireworks
30+
export FIREWORKS_API_KEY="your-api-key"
31+
```
32+
33+
Key init args:
34+
model: Name of Fireworks reranking model to use.
35+
Default: "accounts/fireworks/models/reranker"
36+
top_n: Number of documents to return. Default: 3
37+
fireworks_api_key: Fireworks API key.
38+
39+
Example:
40+
```python
41+
from langchain_fireworks import FireworksRerank
42+
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
43+
44+
reranker = FireworksRerank(top_n=3)
45+
compression_retriever = ContextualCompressionRetriever(
46+
base_compressor=reranker, base_retriever=base_retriever
47+
)
48+
compressed_docs = compression_retriever.invoke("your query")
49+
```
50+
"""
51+
52+
client: Any = Field(default=None, exclude=True) # type: ignore[assignment]
53+
"""Fireworks API client for reranking."""
54+
55+
top_n: int | None = 3
56+
"""Number of documents to return."""
57+
58+
model: str = "accounts/fireworks/models/reranker"
59+
"""Model to use for reranking."""
60+
61+
fireworks_api_key: SecretStr = Field(
62+
alias="api_key",
63+
default_factory=secret_from_env(
64+
"FIREWORKS_API_KEY",
65+
error_message=(
66+
"You must specify an api key. "
67+
"You can pass it an argument as `api_key=...` or "
68+
"set the environment variable `FIREWORKS_API_KEY`."
69+
),
70+
),
71+
)
72+
"""Fireworks API key.
73+
74+
Automatically read from env variable `FIREWORKS_API_KEY` if not provided.
75+
"""
76+
77+
user_agent: str = "langchain"
78+
"""Identifier for the application making the request."""
79+
80+
model_config = ConfigDict(
81+
populate_by_name=True,
82+
arbitrary_types_allowed=True,
83+
)
84+
85+
@model_validator(mode="after")
86+
def validate_environment(self) -> Self:
87+
"""Validate environment variables and initialize client."""
88+
self.client = OpenAI(
89+
api_key=self.fireworks_api_key.get_secret_value(),
90+
base_url="https://api.fireworks.ai/inference/v1",
91+
)
92+
return self
93+
94+
def rerank(
95+
self,
96+
documents: Sequence[str | Document | dict],
97+
query: str,
98+
*,
99+
model: str | None = None,
100+
top_n: int | None = -1,
101+
) -> list[dict[str, Any]]:
102+
"""Returns an ordered list of documents ordered by their relevance to the query.
103+
104+
Args:
105+
query: The query to use for reranking.
106+
documents: A sequence of documents to rerank.
107+
model: The model to use for re-ranking. Default to self.model.
108+
top_n: The number of results to return. If `None` returns all results.
109+
110+
Returns:
111+
List of dicts containing index and relevance_score.
112+
"""
113+
if len(documents) == 0: # to avoid empty api call
114+
return []
115+
116+
docs = [
117+
doc.page_content if isinstance(doc, Document) else doc
118+
for doc in documents
119+
]
120+
121+
model = model or self.model
122+
top_n = top_n if (top_n is None or top_n > 0) else self.top_n
123+
124+
response = self.client.post(
125+
"/rerank",
126+
json={
127+
"query": query,
128+
"documents": docs,
129+
"model": model,
130+
"top_n": top_n,
131+
},
132+
)
133+
134+
if not response.is_success:
135+
response.raise_for_status()
136+
137+
data = response.json()
138+
139+
if "data" not in data:
140+
raise ValueError(f"Unexpected response format: {data}")
141+
142+
return [
143+
{"index": item["index"], "relevance_score": item["score"]}
144+
for item in data["data"]
145+
]
146+
147+
def compress_documents(
148+
self,
149+
documents: Sequence[Document],
150+
query: str,
151+
callbacks: Callbacks | None = None,
152+
) -> Sequence[Document]:
153+
"""Compress documents using Fireworks' rerank API.
154+
155+
Args:
156+
documents: A sequence of documents to compress.
157+
query: The query to use for compressing the documents.
158+
callbacks: Callbacks to run during the compression process.
159+
160+
Returns:
161+
A sequence of compressed documents.
162+
"""
163+
compressed = []
164+
for res in self.rerank(documents, query):
165+
doc = documents[res["index"]]
166+
doc_copy = Document(
167+
doc.page_content, metadata=deepcopy(doc.metadata)
168+
)
169+
doc_copy.metadata["relevance_score"] = res["relevance_score"]
170+
compressed.append(doc_copy)
171+
return compressed
172+
173+
async def acompress_documents(
174+
self,
175+
documents: Sequence[Document],
176+
query: str,
177+
callbacks: Callbacks | None = None,
178+
) -> Sequence[Document]:
179+
"""Async compress documents using Fireworks' rerank API."""
180+
# For now, just call the sync version
181+
# Can be optimized later with async client
182+
return self.compress_documents(documents, query, callbacks)

libs/partners/fireworks/tests/unit_tests/test_imports.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"ChatFireworks",
66
"Fireworks",
77
"FireworksEmbeddings",
8+
"FireworksRerank",
89
]
910

1011

0 commit comments

Comments
 (0)