Skip to content

Commit abfd849

Browse files
authored
Add telemetry (#2260)
1 parent a18f157 commit abfd849

File tree

7 files changed

+280
-19
lines changed

7 files changed

+280
-19
lines changed

src/ragas/_analytics.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,33 @@ class IsCompleteEvent(BaseEvent):
236236
is_completed: bool = True # True if the event was completed, False otherwise
237237

238238

239+
class LLMUsageEvent(BaseEvent):
240+
provider: str # "openai", "anthropic", "langchain", etc.
241+
model: t.Optional[str] = None # Model name (if available)
242+
llm_type: str # "instructor", "langchain_wrapper", "factory"
243+
num_requests: int = 1 # Number of API calls
244+
is_async: bool = False # Sync vs async usage
245+
event_type: str = "llm_usage"
246+
247+
248+
class EmbeddingUsageEvent(BaseEvent):
249+
provider: str # "openai", "google", "huggingface", etc.
250+
model: t.Optional[str] = None # Model name (if available)
251+
embedding_type: str # "modern", "legacy", "factory"
252+
num_requests: int = 1 # Number of embed calls
253+
is_async: bool = False # Sync vs async usage
254+
event_type: str = "embedding_usage"
255+
256+
257+
class PromptUsageEvent(BaseEvent):
258+
prompt_type: str # "pydantic", "few_shot", "simple", "dynamic"
259+
has_examples: bool = False # Whether prompt has few-shot examples
260+
num_examples: int = 0 # Number of examples (if applicable)
261+
has_response_model: bool = False # Whether it has a structured response model
262+
language: str = "english" # Prompt language
263+
event_type: str = "prompt_usage"
264+
265+
239266
@silent
240267
def track_was_completed(
241268
func: t.Callable[P, T],

src/ragas/embeddings/base.py

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pydantic.dataclasses import dataclass
1313
from pydantic_core import CoreSchema, core_schema
1414

15+
from ragas._analytics import EmbeddingUsageEvent, track
1516
from ragas.cache import CacheInterface, cacher
1617
from ragas.embeddings.utils import run_async_in_current_loop, validate_texts
1718
from ragas.run_config import RunConfig, add_async_retry, add_retry
@@ -310,25 +311,73 @@ def embed_query(self, text: str) -> t.List[float]:
310311
"""
311312
Embed a single query text.
312313
"""
313-
return self.embeddings.embed_query(text)
314+
result = self.embeddings.embed_query(text)
315+
316+
# Track usage
317+
track(
318+
EmbeddingUsageEvent(
319+
provider="langchain",
320+
model=getattr(self.embeddings, "model", None),
321+
embedding_type="legacy",
322+
num_requests=1,
323+
is_async=False,
324+
)
325+
)
326+
return result
314327

315328
def embed_documents(self, texts: t.List[str]) -> t.List[t.List[float]]:
316329
"""
317330
Embed multiple documents.
318331
"""
319-
return self.embeddings.embed_documents(texts)
332+
result = self.embeddings.embed_documents(texts)
333+
334+
# Track usage
335+
track(
336+
EmbeddingUsageEvent(
337+
provider="langchain",
338+
model=getattr(self.embeddings, "model", None),
339+
embedding_type="legacy",
340+
num_requests=len(texts),
341+
is_async=False,
342+
)
343+
)
344+
return result
320345

321346
async def aembed_query(self, text: str) -> t.List[float]:
322347
"""
323348
Asynchronously embed a single query text.
324349
"""
325-
return await self.embeddings.aembed_query(text)
350+
result = await self.embeddings.aembed_query(text)
351+
352+
# Track usage
353+
track(
354+
EmbeddingUsageEvent(
355+
provider="langchain",
356+
model=getattr(self.embeddings, "model", None),
357+
embedding_type="legacy",
358+
num_requests=1,
359+
is_async=True,
360+
)
361+
)
362+
return result
326363

327364
async def aembed_documents(self, texts: t.List[str]) -> t.List[t.List[float]]:
328365
"""
329366
Asynchronously embed multiple documents.
330367
"""
331-
return await self.embeddings.aembed_documents(texts)
368+
result = await self.embeddings.aembed_documents(texts)
369+
370+
# Track usage
371+
track(
372+
EmbeddingUsageEvent(
373+
provider="langchain",
374+
model=getattr(self.embeddings, "model", None),
375+
embedding_type="legacy",
376+
num_requests=len(texts),
377+
is_async=True,
378+
)
379+
)
380+
return result
332381

333382
def set_run_config(self, run_config: RunConfig):
334383
"""
@@ -638,10 +687,34 @@ def embedding_factory(
638687
openai_embeddings.request_timeout = run_config.timeout
639688
else:
640689
run_config = RunConfig()
641-
return LangchainEmbeddingsWrapper(openai_embeddings, run_config=run_config)
690+
result = LangchainEmbeddingsWrapper(openai_embeddings, run_config=run_config)
691+
692+
# Track factory usage (legacy)
693+
track(
694+
EmbeddingUsageEvent(
695+
provider="openai",
696+
model=model_name,
697+
embedding_type="factory_legacy",
698+
num_requests=1,
699+
is_async=False,
700+
)
701+
)
702+
return result
642703

643704
# Modern interface
644-
return _create_modern_embedding(provider, model, client, **kwargs)
705+
result = _create_modern_embedding(provider, model, client, **kwargs)
706+
707+
# Track factory usage (modern)
708+
track(
709+
EmbeddingUsageEvent(
710+
provider=provider,
711+
model=model,
712+
embedding_type="factory_modern",
713+
num_requests=1,
714+
is_async=False,
715+
)
716+
)
717+
return result
645718

646719

647720
def _is_legacy_embedding_call(

src/ragas/embeddings/openai_provider.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import typing as t
22

3+
from ragas._analytics import EmbeddingUsageEvent, track
4+
35
from .base import BaseRagasEmbedding
46
from .utils import validate_texts
57

@@ -26,12 +28,24 @@ def embed_text(self, text: str, **kwargs: t.Any) -> t.List[float]:
2628
For async clients, this will run the async method in the appropriate event loop.
2729
"""
2830
if self.is_async:
29-
return self._run_async_in_current_loop(self.aembed_text(text, **kwargs))
31+
result = self._run_async_in_current_loop(self.aembed_text(text, **kwargs))
3032
else:
3133
response = self.client.embeddings.create(
3234
input=text, model=self.model, **kwargs
3335
)
34-
return response.data[0].embedding
36+
result = response.data[0].embedding
37+
38+
# Track usage
39+
track(
40+
EmbeddingUsageEvent(
41+
provider="openai",
42+
model=self.model,
43+
embedding_type="modern",
44+
num_requests=1,
45+
is_async=self.is_async,
46+
)
47+
)
48+
return result
3549

3650
async def aembed_text(self, text: str, **kwargs: t.Any) -> t.List[float]:
3751
"""Asynchronously embed a single text using OpenAI."""
@@ -43,7 +57,19 @@ async def aembed_text(self, text: str, **kwargs: t.Any) -> t.List[float]:
4357
response = await self.client.embeddings.create(
4458
input=text, model=self.model, **kwargs
4559
)
46-
return response.data[0].embedding
60+
result = response.data[0].embedding
61+
62+
# Track usage
63+
track(
64+
EmbeddingUsageEvent(
65+
provider="openai",
66+
model=self.model,
67+
embedding_type="modern",
68+
num_requests=1,
69+
is_async=True,
70+
)
71+
)
72+
return result
4773

4874
def embed_texts(self, texts: t.List[str], **kwargs: t.Any) -> t.List[t.List[float]]:
4975
"""Embed multiple texts using OpenAI's batch API for optimization."""
@@ -52,13 +78,25 @@ def embed_texts(self, texts: t.List[str], **kwargs: t.Any) -> t.List[t.List[floa
5278
return []
5379

5480
if self.is_async:
55-
return self._run_async_in_current_loop(self.aembed_texts(texts, **kwargs))
81+
result = self._run_async_in_current_loop(self.aembed_texts(texts, **kwargs))
5682
else:
5783
# OpenAI supports batch embedding natively
5884
response = self.client.embeddings.create(
5985
input=texts, model=self.model, **kwargs
6086
)
61-
return [item.embedding for item in response.data]
87+
result = [item.embedding for item in response.data]
88+
89+
# Track usage
90+
track(
91+
EmbeddingUsageEvent(
92+
provider="openai",
93+
model=self.model,
94+
embedding_type="modern",
95+
num_requests=len(texts),
96+
is_async=self.is_async,
97+
)
98+
)
99+
return result
62100

63101
async def aembed_texts(
64102
self, texts: t.List[str], **kwargs: t.Any
@@ -76,7 +114,19 @@ async def aembed_texts(
76114
response = await self.client.embeddings.create(
77115
input=texts, model=self.model, **kwargs
78116
)
79-
return [item.embedding for item in response.data]
117+
result = [item.embedding for item in response.data]
118+
119+
# Track usage
120+
track(
121+
EmbeddingUsageEvent(
122+
provider="openai",
123+
model=self.model,
124+
embedding_type="modern",
125+
num_requests=len(texts),
126+
is_async=True,
127+
)
128+
)
129+
return result
80130

81131
def _get_client_info(self) -> str:
82132
"""Get client type and async status information."""

0 commit comments

Comments
 (0)