Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-rag/src/lexigram/ai/rag/evaluation/context.py: 82%
38 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
1"""Context relevance evaluation metrics."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any
7from lexigram.ai.rag.evaluation.base import EvaluatorBase
8from lexigram.ai.rag.evaluation.types import EvaluationResult, MetricType
9from lexigram.contracts import ChatMessage
10from lexigram.di.decorators import inject
11from lexigram.logging import (
12 get_logger,
13)
15if TYPE_CHECKING:
16 from lexigram.contracts.ai import LLMClientProtocol
18logger = get_logger(__name__)
21@inject
22class ContextRelevanceEvaluator(EvaluatorBase):
23 """Evaluates context relevance using LLM-as-judge.
25 Measures how relevant the retrieved context is to the query.
26 """
28 def __init__(self, llm_client: LLMClientProtocol):
29 """Initialize context relevance evaluator.
31 Args:
32 llm_client: LLM client for evaluation.
33 """
34 super().__init__("context_relevance")
35 self.llm_client = llm_client
37 async def evaluate(
38 self,
39 query: str,
40 retrieved_docs: list[Any],
41 generated_answer: str,
42 reference_answer: str | None = None,
43 **kwargs,
44 ) -> EvaluationResult:
45 """Evaluate context relevance.
47 Args:
48 query: The query.
49 retrieved_docs: Retrieved documents to evaluate.
50 generated_answer: Generated answer (not used).
51 reference_answer: Not used.
52 **kwargs: Additional parameters.
54 Returns:
55 Context relevance score.
56 """
57 if not retrieved_docs:
58 return EvaluationResult(
59 metric_type=MetricType.CONTEXT_RELEVANCE,
60 score=0.0,
61 details={"reason": "No documents retrieved"},
62 )
64 # Build context
65 context_parts = []
66 for i, doc in enumerate(retrieved_docs):
67 if isinstance(doc, dict):
68 content = doc.get("content", str(doc))
69 elif hasattr(doc, "content"):
70 content = doc.content
71 else:
72 content = str(doc)
73 context_parts.append(f"[{i + 1}] {content}")
75 context_str = "\n".join(context_parts)
77 prompt = f"""Evaluate how relevant the retrieved context is to answering the query.
78Score from 0.0 (completely irrelevant) to 1.0 (perfectly relevant).
80Query: {query}
82Retrieved Context:
83{context_str}
85Provide only a number between 0.0 and 1.0 as your response."""
87 try:
88 result = await self.llm_client.complete(
89 messages=[ChatMessage(role="user", content=prompt)]
90 )
91 if result.is_err():
92 raise result.unwrap_err()
93 response = result.unwrap()
94 score_text = response.content.strip()
95 score = float(score_text)
96 score = max(0.0, min(1.0, score))
98 return EvaluationResult(
99 metric_type=MetricType.CONTEXT_RELEVANCE,
100 score=score,
101 details={
102 "llm_response": score_text,
103 "context_chunks": len(retrieved_docs),
104 },
105 )
106 except Exception as e:
107 logger.exception("Error computing evaluation metric")
108 return EvaluationResult(
109 metric_type=MetricType.CONTEXT_RELEVANCE,
110 score=0.0,
111 details={"error": str(e)},
112 )