document-qa-dev / document_qa /custom_embeddings.py
lfoppiano's picture
Upload folder using huggingface_hub
916dea4 verified
"""Custom LangChain-compatible embedding client.
Provides :class:`ModalEmbeddings`, a drop-in ``Embeddings`` implementation
that calls any service exposing an ``/embeddings`` endpoint (OpenAI,
vLLM, Modal, LM Studio, etc.).
"""
from typing import List
import requests
from langchain_core.embeddings import Embeddings
class ModalEmbeddings(Embeddings):
"""LangChain ``Embeddings`` backed by an OpenAI-compatible HTTP API.
The service must expose a ``POST /embeddings`` endpoint that accepts
``{"model": "…", "input": ["…"]}`` and returns the standard OpenAI
response shape.
Args:
url: Base URL of the embedding service (e.g. ``"http://localhost:1234/v1"``).
model_name: Model identifier(e.g. ``"intfloat/multilingual-e5-large-instruct"``).
api_key: Optional bearer token for authenticated endpoints.
"""
def __init__(self, url: str, model_name: str, api_key: str = None):
self.url = url
self.model_name = model_name
self.api_key = api_key
def embed(self, text: List[str]) -> List[List[float]]:
"""Embed a list of texts via the configured API.
Newlines are replaced with spaces before sending, since most
embedding models treat them as noise.
Args:
text: Strings to embed.
Returns:
list[list[float]]: One embedding vector per input string.
Raises:
requests.HTTPError: If the API returns a non-2xx status.
"""
# Newlines degrade embedding quality for most models
cleaned_text = [t.replace("\n", " ") for t in text]
headers = {
"Content-Type": "application/json"
}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
response = requests.post(
f"{self.url}/embeddings",
json={
"model": self.model_name,
"input": cleaned_text
},
headers=headers
)
response.raise_for_status()
data = response.json()["data"]
return [item["embedding"] for item in data]
def embed_documents(self, text: List[str]) -> List[List[float]]:
"""Embed multiple documents (LangChain interface).
Args:
text: Document strings to embed.
Returns:
list[list[float]]: One embedding vector per document.
"""
return self.embed(text)
def embed_query(self, text: str) -> List[float]:
"""Embed a single query string (LangChain interface).
Args:
text: The query string.
Returns:
list[float]: The embedding vector for *text*.
"""
return self.embed([text])[0]
def get_model_name(self) -> str:
"""Return the model identifier used for embedding requests."""
return self.model_name
if __name__ == "__main__":
embeds = ModalEmbeddings(
url="https://lfoppiano--intfloat-multilingual-e5-large-instruct-embed-5da184.modal.run/",
model_name="intfloat/multilingual-e5-large-instruct"
)
print(embeds.embed(
["We are surrounded by stupid kids",
"We are interested in the future of AI"]
))