Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ uv run lightspeed-eval --system-config <AGENTS_CONFIG.yaml> --eval-data <PROPOSA
- Context Evaluation
- [`context_recall`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_recall/)
- [`context_relevance`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/nvidia_metrics/#context-relevance)
- [`context_precision_without_reference`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/#context-precision-without-reference)
- [`context_precision_with_reference`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/#context-precision-with-reference)
- [`context_utilization`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/#context-precision-without-reference) (context precision without reference)
- [`context_precision`](https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/#context-precision-with-reference) (context precision with reference)
- `context_precision_without_reference` — **Deprecated**, use `context_utilization`
- `context_precision_with_reference` — **Deprecated**, use `context_precision`
- **Custom**
- Response Evaluation
- [`answer_correctness`](src/lightspeed_evaluation/core/metrics/custom/custom.py)
Expand Down
4 changes: 2 additions & 2 deletions config/evaluation_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
turn_metrics:
- ragas:faithfulness
- ragas:response_relevancy
- ragas:context_precision_without_reference
- ragas:context_utilization
turn_metrics_metadata:
ragas:faithfulness:
threshold: 0.99
Expand All @@ -42,7 +42,7 @@
turn_metrics:
- ragas:context_recall
- ragas:context_relevance
- ragas:context_precision_with_reference
- ragas:context_precision

- conversation_group_id: conv_group_3
description: conversation group description
Expand Down
6 changes: 3 additions & 3 deletions config/system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ agents:
quality_score:
metrics:
- "ragas:faithfulness"
- "ragas:context_precision_with_reference"
- "ragas:context_precision"
- "custom:tool_eval"
- "custom:answer_correctness"
default: true # If true, all metrics in this list get default: true
Expand All @@ -133,11 +133,11 @@ metrics_metadata:
threshold: 0.8
description: "Did we fetch every fact the answer needs?"

"ragas:context_precision_with_reference":
"ragas:context_precision":
threshold: 0.7
description: "How precise the retrieved context is (with reference)"

"ragas:context_precision_without_reference":
"ragas:context_utilization":
threshold: 0.7
description: "How precise the retrieved context is (without reference)"

Expand Down
12 changes: 7 additions & 5 deletions docs/EVALUATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,8 @@ What are you evaluating?
│ ├─ Context / Retrieval Quality?
│ │ ├─ Found everything needed? → context_recall
│ │ ├─ Is retrieved context relevant? → context_relevance
│ │ ├─ Precision (have expected answer)? → context_precision_with_reference
│ │ └─ Precision (no expected answer)? → context_precision_without_reference
│ │ ├─ Precision (have expected answer)? → context_precision
│ │ └─ Precision (no expected answer)? → context_utilization
│ │
│ ├─ Tool Calls & AI Behavior?
│ │ ├─ Right intent? → intent_eval
Expand Down Expand Up @@ -1401,7 +1401,7 @@ The Quality Score aggregates selected sub-metrics into a single number that repr
quality_score:
metrics:
- "ragas:faithfulness"
- "ragas:context_precision_with_reference"
- "ragas:context_precision"
- "custom:tool_eval"
default: true
```
Expand Down Expand Up @@ -1985,8 +1985,10 @@ lightspeed-eval --eval-data config/eval_batch2.yaml
| **ragas:faithfulness** | 0-1 | No made-up information | 0.8 | response, contexts |
| **ragas:context_recall** | 0-1 | Found all needed info | 0.8 | contexts, expected_response |
| **ragas:context_relevance** | 0-1 | Retrieved info is relevant | 0.7 | query, contexts |
| **ragas:context_precision_without_reference** | 0-1 | Retrieved info is useful | 0.7 | query, contexts, response |
| **ragas:context_precision_with_reference** | 0-1 | Retrieved info is useful (vs reference) | 0.7 | query, contexts, response, expected_response |
| **ragas:context_utilization** | 0-1 | Retrieved info is useful | 0.7 | query, contexts, response |
| **ragas:context_precision** | 0-1 | Retrieved info is useful (vs reference) | 0.7 | query, contexts, response, expected_response |
| ~~ragas:context_precision_without_reference~~ | | **Deprecated** — use `ragas:context_utilization` | | |
| ~~ragas:context_precision_with_reference~~ | | **Deprecated** — use `ragas:context_precision` | | |
| **custom:answer_correctness** | 0-1 | Matches expected answer | 0.75 | query, response, expected_response |
| **custom:intent_eval** | 0/1 | Has right intent | 1 | query, response, expected_intent |
| **custom:tool_eval** | 0/1 | Called correct tools with expected results | 1 | expected_tool_calls, tool_calls |
Expand Down
6 changes: 4 additions & 2 deletions examples/02_metrics/context_quality/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ uv run lightspeed-eval \
|-----------------------------------------------|---------------------|---------------------------------------------------------------------------------------|--------------------------------------------|-----------------------------|
| `ragas:context_recall` | Context Recall | Measures how well retrieved context covers the expected answer | query, response, contexts, expected_response | query, expected_response |
| `ragas:context_relevance` | Context Relevance | Evaluates relevance of retrieved context to the query | query, response, contexts | query |
| `ragas:context_precision_without_reference` | Context Utilization | Assesses context ranking quality without using reference answer | query, response, contexts | query |
| `ragas:context_precision_with_reference` | Context Precision | Assesses context ranking quality using expected answer | query, response, contexts, expected_response | query, expected_response |
| `ragas:context_utilization` | Context Utilization | Assesses context ranking quality without using reference answer | query, response, contexts | query |
| `ragas:context_precision` | Context Precision | Assesses context ranking quality using expected answer | query, response, contexts, expected_response | query, expected_response |
| ~~`ragas:context_precision_without_reference`~~ | | **Deprecated** — use `ragas:context_utilization` | | |
| ~~`ragas:context_precision_with_reference`~~ | | **Deprecated** — use `ragas:context_precision` | | |

**Note:** In live mode, `response` and `contexts` are fetched from live API; only `query` and expected fields go in YAML.

Expand Down
4 changes: 2 additions & 2 deletions examples/02_metrics/context_quality/system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ metrics_metadata:
threshold: 0.7
description: Measures relevance of retrieved context to the query
default: true
ragas:context_precision_without_reference: # ContextUtilization
ragas:context_utilization: # ContextUtilization
threshold: 0.7
description: Evaluates precision of context ranking (no reference needed)
default: true
ragas:context_precision_with_reference: # ContextPrecision
ragas:context_precision: # ContextPrecision
threshold: 0.7
description: Evaluates precision of context using expected answer
default: true
Expand Down
7 changes: 7 additions & 0 deletions src/lightspeed_evaluation/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,10 @@
DEFAULT_LOG_SHOW_TIMESTAMPS = True

SUPPORTED_RESULT_STATUSES = ["PASS", "FAIL", "ERROR", "SKIPPED"]

# Deprecated metric names that still work but should be migrated.
# Maps old (deprecated) full identifier -> new canonical identifier.
DEPRECATED_METRIC_NAMES: dict[str, str] = {
"ragas:context_precision_with_reference": "ragas:context_precision",
"ragas:context_precision_without_reference": "ragas:context_utilization",
}
43 changes: 30 additions & 13 deletions src/lightspeed_evaluation/core/metrics/ragas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Ragas metrics evaluation using LLM Manager with Ragas 0.4+ API."""

import errno
import logging
import math
import threading
from typing import Any, Optional
Expand Down Expand Up @@ -28,6 +29,13 @@
from lightspeed_evaluation.core.models import EvaluationScope, TurnData
from lightspeed_evaluation.core.system.exceptions import EvaluationError

logger = logging.getLogger(__name__)

_DEPRECATED_RAGAS_METRICS: dict[str, str] = {
"context_precision_with_reference": "context_precision",
"context_precision_without_reference": "context_utilization",
}


def _clamp_score(score: float) -> float:
"""Clamp score to [0, 1] range to handle floating-point precision issues.
Expand Down Expand Up @@ -88,10 +96,11 @@ def __init__(self, llm_manager: LLMManager, embedding_manager: EmbeddingManager)
# Context/Retrieval evaluation metrics
"context_recall": self._evaluate_context_recall,
"context_relevance": self._evaluate_context_relevance,
"context_precision_with_reference": self._evaluate_context_precision_with_reference,
"context_precision_without_reference": (
self._evaluate_context_precision_without_reference
),
"context_precision": self._evaluate_context_precision,
"context_utilization": self._evaluate_context_utilization,
# Deprecated aliases (backward compatibility)
"context_precision_with_reference": self._evaluate_context_precision,
"context_precision_without_reference": self._evaluate_context_utilization,
}

@property
Expand Down Expand Up @@ -133,6 +142,14 @@ def evaluate(
scope: EvaluationScope,
) -> tuple[Optional[float], str]:
"""Evaluate a Ragas metric."""
if metric_name in _DEPRECATED_RAGAS_METRICS:
new_name = _DEPRECATED_RAGAS_METRICS[metric_name]
logger.warning(
"Metric 'ragas:%s' is deprecated, use 'ragas:%s' instead.",
metric_name,
new_name,
)

if metric_name not in self.supported_metrics:
return None, f"Unsupported Ragas metric: {metric_name}"

Expand Down Expand Up @@ -220,16 +237,16 @@ def _evaluate_faithfulness(
score = _clamp_score(float(result.value))
return score, f"Ragas faithfulness: {score:.2f}"

def _evaluate_context_precision_without_reference(
def _evaluate_context_utilization(
self,
_conv_data: Any,
_turn_idx: Optional[int],
turn_data: Optional[TurnData],
is_conversation: bool,
) -> tuple[Optional[float], str]:
"""Evaluate context precision without reference using Ragas 0.4+ score()."""
"""Evaluate context utilization using Ragas 0.4+ ContextUtilization."""
if is_conversation:
return None, "Context precision without reference is a turn-level metric"
return None, "Context utilization is a turn-level metric"

query, response, contexts = self._extract_turn_data(turn_data)

Expand All @@ -242,21 +259,21 @@ def _evaluate_context_precision_without_reference(
)

score = _clamp_score(float(result.value))
return score, f"Ragas context precision without reference: {score:.2f}"
return score, f"Ragas context utilization: {score:.2f}"

def _evaluate_context_precision_with_reference(
def _evaluate_context_precision(
self,
_conv_data: Any,
_turn_idx: Optional[int],
turn_data: Optional[TurnData],
is_conversation: bool,
) -> tuple[Optional[float], str]:
"""Evaluate context precision with reference using Ragas 0.4+ score()."""
"""Evaluate context precision using Ragas 0.4+ ContextPrecision."""
if is_conversation:
return None, "Context precision with reference is a turn-level metric"
return None, "Context precision is a turn-level metric"

if turn_data is None:
return None, "TurnData is required for context precision with reference"
return None, "TurnData is required for context precision"

query, _, contexts = self._extract_turn_data(turn_data)

Expand All @@ -269,7 +286,7 @@ def _evaluate_context_precision_with_reference(
)

score = _clamp_score(float(result.value))
return score, f"Ragas context precision with reference: {score:.2f}"
return score, f"Ragas context precision: {score:.2f}"

def _evaluate_context_recall(
self,
Expand Down
22 changes: 20 additions & 2 deletions src/lightspeed_evaluation/core/system/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import yaml
from pydantic import ValidationError

from lightspeed_evaluation.core.constants import DEPRECATED_METRIC_NAMES
from lightspeed_evaluation.core.models import EvaluationData, TurnData
from lightspeed_evaluation.core.models.data import DatasetMetadata
from lightspeed_evaluation.core.system.exceptions import DataValidationError
Expand All @@ -32,6 +33,15 @@
"required_fields": ["response", "contexts"],
"description": "requires 'response' and 'contexts' fields",
},
"ragas:context_precision": {
"required_fields": ["response", "contexts", "expected_response"],
"description": "requires 'response', 'contexts', and 'expected_response' fields",
},
"ragas:context_utilization": {
"required_fields": ["response", "contexts"],
"description": "requires 'response' and 'contexts' fields",
},
# Deprecated aliases (backward compatibility)
"ragas:context_precision_with_reference": {
"required_fields": ["response", "contexts", "expected_response"],
"description": "requires 'response', 'contexts', and 'expected_response' fields",
Expand Down Expand Up @@ -455,7 +465,11 @@ def _validate_metrics_availability(self, data: EvaluationData) -> None:
for turn_data in data.turns:
if turn_data.turn_metrics:
for metric in turn_data.turn_metrics:
if metric not in self._turn_level_metrics:
canonical = DEPRECATED_METRIC_NAMES.get(metric, metric)
if (
metric not in self._turn_level_metrics
and canonical not in self._turn_level_metrics
):
turn_data.add_invalid_metric(metric)
self.validation_errors.append(
f"Conversation {conversation_id}, Turn {turn_data.turn_id}: "
Expand All @@ -465,7 +479,11 @@ def _validate_metrics_availability(self, data: EvaluationData) -> None:
# Validate conversation metrics
if data.conversation_metrics:
for metric in data.conversation_metrics:
if metric not in self._conversation_level_metrics:
canonical = DEPRECATED_METRIC_NAMES.get(metric, metric)
if (
metric not in self._conversation_level_metrics
and canonical not in self._conversation_level_metrics
):
data.add_invalid_metric(metric)
self.validation_errors.append(
f"Conversation {conversation_id}: Unknown conversation metric '{metric}'"
Expand Down
Loading