mas-lab-library-eval 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """mas.library.eval — output quality evaluators and MCE pipeline steps."""
4
+
5
+ from pathlib import Path
6
+
7
+ from mas.library.eval.evaluator import (
8
+ DEFAULT_METRIC_PROVIDER,
9
+ EvalProvider,
10
+ MetricScore,
11
+ evaluate_run,
12
+ get_provider,
13
+ list_available_providers,
14
+ list_metrics,
15
+ register_provider,
16
+ )
17
+
18
+
19
+ def package_root() -> Path:
20
+ """Return the directory containing ``library.yaml`` for this manifest library."""
21
+ here = Path(__file__).resolve().parent
22
+ for parent in [here, *here.parents]:
23
+ if (parent / "library.yaml").is_file():
24
+ return parent
25
+ return here
26
+
27
+
28
+ __all__ = [
29
+ "DEFAULT_METRIC_PROVIDER",
30
+ "EvalProvider",
31
+ "MetricScore",
32
+ "evaluate_run",
33
+ "get_provider",
34
+ "list_available_providers",
35
+ "list_metrics",
36
+ "package_root",
37
+ "register_provider",
38
+ ]
@@ -0,0 +1,204 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """mas-lab eval — library-eval CLI (same runner as the eval_mce pipeline step).
4
+
5
+ Usage::
6
+
7
+ mas-lab eval path/to/events.jsonl --metric goal_success_rate --json
8
+ mas-lab eval path/to/experiment/ --metric goal_success_rate --recursive
9
+ mas-lab eval --list-metrics
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import json as _json
14
+ from pathlib import Path
15
+ from typing import Optional
16
+
17
+ import click
18
+
19
+ from mas.library.eval.mce.catalog import listed_ids, resolve_metric
20
+ from mas.library.eval.mce.runner import (
21
+ compute_session_metrics,
22
+ install_openai_llm_service,
23
+ )
24
+
25
+ _DEFAULT_METRICS = ["goal_success_rate"]
26
+
27
+
28
+ @click.command("eval")
29
+ @click.argument("source", type=str, metavar="SOURCE", required=False)
30
+ @click.option(
31
+ "--fixture",
32
+ type=Path,
33
+ default=None,
34
+ help="Path to a fixture YAML that provides ground truth.",
35
+ )
36
+ @click.option(
37
+ "--metric",
38
+ "metrics",
39
+ multiple=True,
40
+ help=(
41
+ "MCE metric(s) to compute (repeatable). Snake_case ids only "
42
+ f"(goal_success_rate). Defaults to {_DEFAULT_METRICS} when omitted. "
43
+ "Use --list-metrics to show the available metrics."
44
+ ),
45
+ )
46
+ @click.option(
47
+ "--response-agent",
48
+ default=None,
49
+ show_default="auto-detect from trace",
50
+ help="agent_id whose last execution_end is the final response. Auto-detected when omitted.",
51
+ )
52
+ @click.option(
53
+ "--model",
54
+ default=None,
55
+ show_default="workspace infra default model, else gpt-4o",
56
+ help="Judge model. Default: workspace infra default, else gpt-4o.",
57
+ )
58
+ @click.option(
59
+ "--list-metrics",
60
+ is_flag=True,
61
+ default=False,
62
+ help="List the available metrics and exit.",
63
+ )
64
+ @click.option(
65
+ "--json",
66
+ "output_json",
67
+ is_flag=True,
68
+ default=False,
69
+ help="Output results as JSON instead of a human-readable table.",
70
+ )
71
+ @click.option("--overlay", default=None, help="Label for display (e.g. inject-C7-backend).")
72
+ @click.option(
73
+ "--engine",
74
+ type=click.Choice(["mce-v1"]),
75
+ default="mce-v1",
76
+ show_default=True,
77
+ help="Metric computation engine: mce-v1.",
78
+ )
79
+ @click.option(
80
+ "--api-key-env",
81
+ default=None,
82
+ help="Name of the environment variable holding the API key (default: OPENAI_API_KEY).",
83
+ )
84
+ @click.option(
85
+ "--recursive",
86
+ is_flag=True,
87
+ default=False,
88
+ help="Score every events.jsonl under SOURCE when SOURCE is a directory.",
89
+ )
90
+ def eval_cmd(
91
+ source: Optional[str],
92
+ fixture: Optional[Path],
93
+ metrics: tuple[str, ...],
94
+ response_agent: str,
95
+ model: Optional[str],
96
+ output_json: bool,
97
+ overlay: Optional[str],
98
+ engine: str,
99
+ api_key_env: Optional[str],
100
+ list_metrics: bool,
101
+ recursive: bool,
102
+ ) -> None:
103
+ """Score traces with MCE LLM-as-judge metrics (same runner as eval_mce).
104
+
105
+ SOURCE can be a file path (events.jsonl), a directory (with --recursive),
106
+ a lab shorthand (e.g. tutorials/t3-analysis/baseline/item1/r1), or a run_id.
107
+
108
+ \b
109
+ Examples:
110
+ mas-lab eval path/to/events.jsonl --metric goal_success_rate --json
111
+ mas-lab eval tutorials/t3-analysis/baseline/item1/r1 --metric groundedness
112
+ mas-lab eval --list-metrics
113
+ """
114
+ del engine # reserved; only mce-v1 is wired
115
+ if list_metrics:
116
+ click.echo("Available metrics (snake_case):")
117
+ for snake in listed_ids(session_only=True):
118
+ click.echo(f" {snake}")
119
+ return
120
+ if not source:
121
+ raise click.UsageError("SOURCE argument required (or use --list-metrics)")
122
+
123
+ try:
124
+ metric_names = [
125
+ resolve_metric(n, session_only=True).snake for n in (metrics or _DEFAULT_METRICS)
126
+ ]
127
+ except ValueError as exc:
128
+ raise click.UsageError(str(exc)) from exc
129
+
130
+ from mas.lab.cli.commands.plot import resolve_source
131
+
132
+ resolved = Path(resolve_source(source)).expanduser()
133
+ if not resolved.exists():
134
+ click.echo(f"Source not found: {source}")
135
+ raise SystemExit(1)
136
+
137
+ traces: list[Path]
138
+ if resolved.is_dir():
139
+ if not recursive:
140
+ raise click.UsageError("SOURCE is a directory; pass --recursive to score every events.jsonl")
141
+ traces = sorted(resolved.rglob("events.jsonl"))
142
+ if not traces:
143
+ click.echo(f"No events.jsonl under {resolved}")
144
+ raise SystemExit(1)
145
+ else:
146
+ traces = [resolved]
147
+
148
+ install_openai_llm_service(model, api_key_env=api_key_env)
149
+
150
+ if fixture:
151
+ if not fixture.exists():
152
+ click.echo(f"Fixture not found: {fixture}")
153
+ raise SystemExit(1)
154
+ click.echo("ℹ️ --fixture provided but not consumed by current MCE metrics.", err=True)
155
+
156
+ any_results = False
157
+ for trace_path in traces:
158
+ results_dict = compute_session_metrics(
159
+ trace_path,
160
+ metric_names,
161
+ response_agent_id=response_agent,
162
+ )
163
+ if not results_dict:
164
+ click.echo(f"No results for {trace_path} — check trace content.", err=True)
165
+ continue
166
+ any_results = True
167
+ label = overlay or (
168
+ str(trace_path.relative_to(resolved)) if resolved.is_dir() else trace_path.stem
169
+ )
170
+ if output_json:
171
+ payload = {k: v for k, v in results_dict.items() if not k.startswith("__")}
172
+ if resolved.is_dir():
173
+ click.echo(_json.dumps({"source": label, "metrics": payload}, indent=2))
174
+ else:
175
+ click.echo(_json.dumps(payload, indent=2))
176
+ else:
177
+ _print_table(results_dict, label)
178
+ if not any_results:
179
+ raise SystemExit(1)
180
+
181
+
182
+ def _print_table(results_dict: dict, label: str) -> None:
183
+ click.echo(f"\n{'─' * 72}")
184
+ click.echo(f" eval — {label}")
185
+ click.echo(f"{'─' * 72}")
186
+ click.echo(f" {'Metric':<30} {'Score':>6} Reasoning")
187
+ click.echo(f" {'─' * 30} {'─' * 6} {'─' * 30}")
188
+ for name, result in results_dict.items():
189
+ if name.startswith("__"):
190
+ continue
191
+ value = result.get("value")
192
+ error = result.get("error")
193
+ reasoning = (result.get("reasoning") or "").replace("\n", " ")[:60]
194
+ score_str = f"{value:.3f}" if value is not None and not error else "ERR"
195
+ click.echo(f" {name:<30} {score_str:>6} {reasoning}")
196
+ click.echo(f"{'─' * 72}\n")
197
+
198
+
199
+ class EvalCliComponent:
200
+ """Register ``mas-lab eval`` via ``mas.lab.cli.components``."""
201
+
202
+ def register(self, app: click.Group) -> str:
203
+ app.add_command(eval_cmd, name="eval")
204
+ return "eval"
@@ -0,0 +1,339 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Generic evaluation module — provider-agnostic metric computation.
4
+
5
+ Entry point for computing quality metrics from a knowledge graph.
6
+
7
+ Usage::
8
+
9
+ from mas.library.eval.evaluator import evaluate_run, get_provider
10
+
11
+ scores = evaluate_run(
12
+ kg_path=Path("output/baseline/item1/r1/kg.json"),
13
+ metrics=["goal_success_rate", "groundedness"],
14
+ provider="mce",
15
+ )
16
+ # {"goal_success_rate": {"value": 0.85, "reasoning": "...", "error": None}}
17
+
18
+ Provider selection (explicit — no auto-fallback)::
19
+
20
+ evaluate_run(kg_path, metrics, provider="mce") # built-in MCE provider
21
+ evaluate_run(kg_path, metrics, provider="mce_oss") # metrics_computation_engine (OSS)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import importlib
27
+ import logging
28
+ from collections import defaultdict
29
+ from pathlib import Path
30
+ from typing import TYPE_CHECKING, Any, Dict, List, NotRequired, Optional, Sequence, TypedDict
31
+
32
+ if TYPE_CHECKING:
33
+ from mas.library.eval.metrics import EvalMetric, MetricContext, RunInputs
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ DEFAULT_METRIC_PROVIDER = "mce"
38
+
39
+
40
+ class MetricScore(TypedDict):
41
+ value: Optional[float]
42
+ reasoning: str
43
+ error: Optional[str]
44
+ details: NotRequired[Optional[Dict[str, Any]]]
45
+
46
+
47
+ class EvalProvider:
48
+ """Evaluation provider — stock metrics (subclass) plus registered :class:`EvalMetric`s.
49
+
50
+ Custom metrics are registered on the existing provider (default ``mce``)
51
+ with :meth:`register_metric`. :meth:`compute_metrics` is the single engine
52
+ ``eval_mce`` uses.
53
+ """
54
+
55
+ name: str = DEFAULT_METRIC_PROVIDER
56
+
57
+ def __init__(self) -> None:
58
+ self._metrics: Dict[str, "EvalMetric"] = {}
59
+
60
+ def compute(
61
+ self,
62
+ kg_path: Path,
63
+ metric_names: List[str],
64
+ *,
65
+ response_agent_id: Optional[str] = None,
66
+ ) -> Dict[str, MetricScore]:
67
+ """Compute metrics for a run folder that contains ``kg.json``."""
68
+ import asyncio
69
+
70
+ from mas.library.eval.metrics import MetricContext
71
+
72
+ del response_agent_id
73
+ inputs = _run_inputs_from_kg(Path(kg_path))
74
+ ctx = MetricContext(run_dir=inputs.run_dir)
75
+ loop = asyncio.new_event_loop()
76
+ try:
77
+ return loop.run_until_complete(self.compute_metrics(metric_names, inputs, ctx))
78
+ finally:
79
+ loop.close()
80
+
81
+ async def compute_metrics(
82
+ self,
83
+ metric_ids: Sequence[str],
84
+ inputs: "RunInputs",
85
+ ctx: "MetricContext",
86
+ *,
87
+ extra: Sequence["EvalMetric"] = (),
88
+ ) -> Dict[str, MetricScore]:
89
+ """Run stock, registered, and step-local metrics through one engine."""
90
+ from mas.library.eval.metrics import normalize_score
91
+ from mas.library.eval.metrics.plugins import load_declared_eval_metrics
92
+
93
+ load_declared_eval_metrics()
94
+ extras = {m.metric_id: m for m in extra}
95
+ overlap = sorted(mid for mid in extras if self.resolve_metric(mid) is not None)
96
+ if overlap:
97
+ raise ValueError(f"prompt_metrics id(s) collide with stock MCE or a registered eval_metric: {overlap}")
98
+ resolved: list[EvalMetric] = []
99
+ unknown: list[str] = []
100
+ listed = set(extras)
101
+ for mid in metric_ids:
102
+ if mid in listed:
103
+ raise ValueError(f"Metric id {mid!r} is listed in both metrics and prompt_metrics")
104
+ metric = self.resolve_metric(mid)
105
+ if metric is None:
106
+ unknown.append(mid)
107
+ else:
108
+ resolved.append(metric)
109
+ if unknown:
110
+ available = self.metric_ids()
111
+ raise ValueError(
112
+ f"Unknown eval metric id(s): {unknown}. Registered on provider {self.name!r}: {available or '(none)'}"
113
+ )
114
+ resolved.extend(extra)
115
+
116
+ grouped: dict[str, list[EvalMetric]] = defaultdict(list)
117
+ unbatched: list[EvalMetric] = []
118
+ for metric in resolved:
119
+ if metric.batch_key:
120
+ grouped[metric.batch_key].append(metric)
121
+ else:
122
+ unbatched.append(metric)
123
+
124
+ results: dict[str, MetricScore] = {}
125
+ for metric in unbatched:
126
+ try:
127
+ score = await metric.compute(inputs, ctx)
128
+ except Exception as exc:
129
+ score = {"value": None, "reasoning": "", "error": str(exc)}
130
+ results[metric.metric_id] = normalize_score(score)
131
+ for _key, family in grouped.items():
132
+ cls = type(family[0])
133
+ try:
134
+ batch = await cls.compute_batch(family, inputs, ctx)
135
+ except Exception as exc:
136
+ batch = {m.metric_id: {"value": None, "reasoning": "", "error": str(exc)} for m in family}
137
+ for mid, score in batch.items():
138
+ results[mid] = normalize_score(score)
139
+ return results
140
+
141
+ def available_metrics(self) -> List[str]:
142
+ """Return metric IDs this provider supports."""
143
+ return self.metric_ids()
144
+
145
+ def resolve_metric(self, metric_id: str) -> "EvalMetric | None":
146
+ """Return a hosted metric, or ``None`` if this provider does not know *metric_id*."""
147
+ return self._hosted_metrics().get(metric_id)
148
+
149
+ def register_metric(self, metric: "EvalMetric") -> None:
150
+ """Register an :class:`EvalMetric` on this provider."""
151
+ metric_id = str(getattr(metric, "metric_id", "") or "").strip()
152
+ if not metric_id:
153
+ raise ValueError("EvalMetric.metric_id must be a non-empty snake_case id")
154
+ if self.resolve_metric(metric_id) is not None:
155
+ raise ValueError(f"Duplicate eval metric id {metric_id!r} on provider {self.name!r}")
156
+ for other_name in list_available_providers():
157
+ if other_name == self.name:
158
+ continue
159
+ try:
160
+ other = get_provider(other_name)
161
+ except (ValueError, RuntimeError):
162
+ continue
163
+ if other.resolve_metric(metric_id) is not None:
164
+ raise ValueError(f"Metric id {metric_id!r} is already registered on provider {other_name!r}")
165
+ self._hosted_metrics()[metric_id] = metric
166
+
167
+ def _hosted_metrics(self) -> Dict[str, "EvalMetric"]:
168
+ hosted = getattr(self, "_metrics", None)
169
+ if hosted is None:
170
+ self._metrics = {}
171
+ hosted = self._metrics
172
+ return hosted
173
+
174
+ @property
175
+ def metrics(self) -> Dict[str, "EvalMetric"]:
176
+ """id → registered EvalMetric (not stock catalog ids)."""
177
+ return dict(self._hosted_metrics())
178
+
179
+ def metric_ids(self) -> List[str]:
180
+ """Return ids this provider can resolve (subclass may prepend stock ids)."""
181
+ return list(self._hosted_metrics())
182
+
183
+
184
+ def _run_inputs_from_kg(kg_path: Path) -> "RunInputs":
185
+ from mas.library.eval.metrics import RunInputs
186
+
187
+ run_dir = kg_path.parent
188
+ trace = run_dir / "traces" / "events.jsonl"
189
+ return RunInputs(
190
+ run_dir=run_dir,
191
+ kg=kg_path,
192
+ native_trace=trace if trace.exists() else None,
193
+ )
194
+
195
+
196
+ # ---------------------------------------------------------------------------
197
+ # Provider registry
198
+ # ---------------------------------------------------------------------------
199
+
200
+ _BUILTIN_PROVIDERS: Dict[str, str] = {
201
+ "mce": "mas.library.eval.providers.mce",
202
+ "mce_oss": "mas.library.eval.providers.mce_oss",
203
+ "adversarial": "mas.library.eval.providers.adversarial",
204
+ }
205
+
206
+ _BUILTIN_CLASSES: Dict[str, str] = {
207
+ "mce": "MCEProvider",
208
+ "mce_oss": "MCEOSSProvider",
209
+ "adversarial": "AdversarialProvider",
210
+ }
211
+
212
+ _PROVIDER_ALIASES: Dict[str, str] = {
213
+ "mce_v1": "mce",
214
+ }
215
+
216
+ # Runtime registry — populated by register_provider(). Lab pipeline steps load
217
+ # domain-specific providers from lab-local files and register them here so that
218
+ # get_provider(name) works for the lifetime of the process without modifying
219
+ # the core evaluator module.
220
+ _RUNTIME_PROVIDERS: Dict[str, EvalProvider] = {}
221
+
222
+
223
+ def _canonical_provider_name(name: str) -> str:
224
+ return _PROVIDER_ALIASES.get(name, name)
225
+
226
+
227
+ def register_provider(name: str, provider: EvalProvider) -> None:
228
+ """Register a provider instance with the MCE wrapper at runtime.
229
+
230
+ Called by pipeline steps that load lab-local provider implementations
231
+ (e.g. ``eval_trip_planner_gt`` loading from ``eval/trip_planner_gt.py``).
232
+ After registration, ``get_provider(name)`` returns this instance for the
233
+ lifetime of the process.
234
+
235
+ Args:
236
+ name: Provider identifier, e.g. ``"trip_planner_gt"``.
237
+ provider: Fully-initialised EvalProvider instance.
238
+ """
239
+ _RUNTIME_PROVIDERS[_canonical_provider_name(name)] = provider
240
+
241
+
242
+ def list_available_providers() -> List[str]:
243
+ """Return provider names that can be instantiated in this environment."""
244
+ available = sorted(_RUNTIME_PROVIDERS.keys())
245
+ for name, module_path in _BUILTIN_PROVIDERS.items():
246
+ if name in _RUNTIME_PROVIDERS:
247
+ continue
248
+ try:
249
+ importlib.import_module(module_path)
250
+ available.append(name)
251
+ except ImportError:
252
+ continue
253
+ aliased = [alias for alias, target in _PROVIDER_ALIASES.items() if target in available]
254
+ return sorted(set(available) | set(aliased))
255
+
256
+
257
+ def get_provider(name: Optional[str] = None, **kwargs: Any) -> EvalProvider:
258
+ """Instantiate an evaluation provider by name.
259
+
260
+ Args:
261
+ name: Provider id, e.g. ``"mce"``, ``"mce_oss"``, or ``"adversarial"``.
262
+ Required — there is no ``"auto"`` fallback chain.
263
+ **kwargs: Constructor arguments forwarded to built-in providers
264
+ (e.g. ``dataset_path`` for ``"adversarial"``).
265
+
266
+ Raises:
267
+ ValueError: If *name* is missing or unknown.
268
+ RuntimeError: If the provider's dependencies are not installed.
269
+ """
270
+ if not name or name == "auto":
271
+ available = list_available_providers()
272
+ raise ValueError(
273
+ "Evaluation provider is required (no auto-fallback). "
274
+ f"Available providers: {', '.join(available) or '(none — install mce or metrics_computation_engine)'}"
275
+ )
276
+ canonical = _canonical_provider_name(name)
277
+ if canonical in _RUNTIME_PROVIDERS and not kwargs:
278
+ return _RUNTIME_PROVIDERS[canonical]
279
+ provider = _make_provider(canonical, **kwargs)
280
+ if not kwargs:
281
+ _RUNTIME_PROVIDERS[canonical] = provider
282
+ return provider
283
+
284
+
285
+ def _make_provider(name: str, **kwargs: Any) -> EvalProvider:
286
+ """Import and instantiate a provider.
287
+
288
+ Raises:
289
+ ValueError: Unknown provider name.
290
+ RuntimeError: Dependencies missing (ImportError).
291
+ """
292
+ if name in _RUNTIME_PROVIDERS and not kwargs:
293
+ return _RUNTIME_PROVIDERS[name]
294
+ if name not in _BUILTIN_PROVIDERS:
295
+ available = list_available_providers()
296
+ raise ValueError(f"Unknown eval provider: {name!r}. Available providers: {', '.join(available) or '(none)'}")
297
+ module_path = _BUILTIN_PROVIDERS[name]
298
+ class_name = _BUILTIN_CLASSES[name]
299
+ try:
300
+ mod = importlib.import_module(module_path)
301
+ except ImportError as exc:
302
+ raise RuntimeError(
303
+ f"Eval provider {name!r} is not available — missing dependencies. Install the package for {module_path}."
304
+ ) from exc
305
+ cls = getattr(mod, class_name)
306
+ return cls(**kwargs)
307
+
308
+
309
+ # ---------------------------------------------------------------------------
310
+ # Convenience API
311
+ # ---------------------------------------------------------------------------
312
+
313
+
314
+ def evaluate_run(
315
+ kg_path: Path,
316
+ metrics: List[str],
317
+ *,
318
+ provider: Optional[str] = None,
319
+ response_agent_id: Optional[str] = None,
320
+ ) -> Dict[str, MetricScore]:
321
+ """Compute metrics for a single run from its knowledge graph.
322
+
323
+ Args:
324
+ kg_path: Path to kg.json (output of normalize_events step).
325
+ metrics: List of metric IDs to compute.
326
+ provider: Provider id (``"mce"``, ``"mce_oss"``, etc.) — required.
327
+ response_agent_id: Override root agent detection.
328
+
329
+ Returns:
330
+ Dict mapping metric_id → {value, reasoning, error}.
331
+ """
332
+ p = get_provider(provider)
333
+ logger.info("evaluate_run: provider=%s, kg=%s, metrics=%s", p.name, kg_path, metrics)
334
+ return p.compute(kg_path, metrics, response_agent_id=response_agent_id)
335
+
336
+
337
+ def list_metrics(provider: Optional[str] = None) -> List[str]:
338
+ """Return available metric IDs from the selected provider."""
339
+ return get_provider(provider).available_metrics()
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ # MAS Library Metadata — Eval
4
+ #
5
+ # Declares the eval_metric plugin type and a toy metric used by tests and docs.
6
+ # Libraries register metrics onto the MCE eval provider.
7
+
8
+ apiVersion: mas/v1
9
+ kind: Library
10
+
11
+ name: eval
12
+ description: >
13
+ Evaluation metrics and LLM-as-judge scoring. Hosts the eval_metric plugin
14
+ type so other libraries can register session metrics onto an eval provider.
15
+ version: "0.2.0"
16
+
17
+ module_base: mas.library.eval
18
+
19
+ types:
20
+ - eval_metric
21
+
22
+ plugins:
23
+ - type: eval_metric
24
+ name: toy_echo
25
+ module: mas.library.eval.metrics.toy
26
+ class: ToyEchoMetric
27
+ description: Example session metric that scores 1.0 when a native trace exists.
28
+ attributes:
29
+ provider: mce
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """mas.library.eval.mce — MCE integration (session metrics via telemetry-hub)."""
4
+
5
+ from mas.library.eval.mce.catalog import ALL_SESSION_METRICS, METRIC_MAP, METRIC_REGISTRY
6
+ from mas.library.eval.mce.judge_model import (
7
+ ResolvedJudgeModel,
8
+ apply_eval_mce_model_defaults,
9
+ resolve_judge_model,
10
+ unique_application_model,
11
+ )
12
+ from mas.library.eval.mce.registry_api import (
13
+ build_session_from_trace,
14
+ compute_session_metrics,
15
+ )
16
+ from mas.library.eval.mce.runner import (
17
+ METRICS_SCHEMA_VERSION,
18
+ build_metrics_document,
19
+ get_effective_judge_model,
20
+ get_openai_client,
21
+ install_openai_llm_service,
22
+ llm_service_config,
23
+ )
24
+ from mas.library.eval.mce.runner import (
25
+ compute_session_metrics as compute_trace_metrics,
26
+ )
27
+ from mas.library.eval.mce.trace_provider import MASTraceProvider
28
+
29
+ __all__ = [
30
+ "MASTraceProvider",
31
+ "ALL_SESSION_METRICS",
32
+ "METRIC_MAP",
33
+ "METRIC_REGISTRY",
34
+ "METRICS_SCHEMA_VERSION",
35
+ "ResolvedJudgeModel",
36
+ "apply_eval_mce_model_defaults",
37
+ "build_metrics_document",
38
+ "build_session_from_trace",
39
+ "compute_session_metrics",
40
+ "compute_trace_metrics",
41
+ "get_effective_judge_model",
42
+ "get_openai_client",
43
+ "install_openai_llm_service",
44
+ "llm_service_config",
45
+ "resolve_judge_model",
46
+ "unique_application_model",
47
+ ]