agentops-accelerator 0.3.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.
Files changed (142) hide show
  1. agentops/__init__.py +10 -0
  2. agentops/__main__.py +6 -0
  3. agentops/agent/__init__.py +12 -0
  4. agentops/agent/_legacy_ids.py +92 -0
  5. agentops/agent/analyzer.py +207 -0
  6. agentops/agent/checks/__init__.py +1 -0
  7. agentops/agent/checks/catalog.py +880 -0
  8. agentops/agent/checks/errors.py +279 -0
  9. agentops/agent/checks/foundry_config.py +75 -0
  10. agentops/agent/checks/latency.py +84 -0
  11. agentops/agent/checks/opex.py +157 -0
  12. agentops/agent/checks/opex_workspace.py +874 -0
  13. agentops/agent/checks/posture.py +36 -0
  14. agentops/agent/checks/posture_rules/__init__.py +53 -0
  15. agentops/agent/checks/posture_rules/content_filter.py +59 -0
  16. agentops/agent/checks/posture_rules/diagnostics.py +74 -0
  17. agentops/agent/checks/posture_rules/local_auth.py +55 -0
  18. agentops/agent/checks/posture_rules/managed_identity.py +59 -0
  19. agentops/agent/checks/posture_rules/network.py +68 -0
  20. agentops/agent/checks/regression.py +78 -0
  21. agentops/agent/checks/release_readiness.py +182 -0
  22. agentops/agent/checks/safety.py +247 -0
  23. agentops/agent/checks/spec_conformance.py +375 -0
  24. agentops/agent/cockpit.py +5159 -0
  25. agentops/agent/config.py +240 -0
  26. agentops/agent/findings.py +113 -0
  27. agentops/agent/history.py +142 -0
  28. agentops/agent/knowledge/__init__.py +182 -0
  29. agentops/agent/knowledge/waf-checklist.csv +39 -0
  30. agentops/agent/llm_assist/__init__.py +16 -0
  31. agentops/agent/llm_assist/_base.py +124 -0
  32. agentops/agent/llm_assist/_bundle_rule.py +154 -0
  33. agentops/agent/llm_assist/_client.py +347 -0
  34. agentops/agent/llm_assist/_dataset_rules.py +191 -0
  35. agentops/agent/llm_assist/_engine.py +106 -0
  36. agentops/agent/llm_assist/_prompt_rules.py +291 -0
  37. agentops/agent/llm_assist/_spec_rules.py +235 -0
  38. agentops/agent/production_telemetry.py +430 -0
  39. agentops/agent/report.py +207 -0
  40. agentops/agent/server/__init__.py +1 -0
  41. agentops/agent/server/app.py +84 -0
  42. agentops/agent/server/auth.py +94 -0
  43. agentops/agent/server/chat.py +44 -0
  44. agentops/agent/server/protocol.py +72 -0
  45. agentops/agent/sources/__init__.py +1 -0
  46. agentops/agent/sources/azure_monitor.py +523 -0
  47. agentops/agent/sources/azure_resources.py +602 -0
  48. agentops/agent/sources/foundry_control.py +174 -0
  49. agentops/agent/sources/results_history.py +494 -0
  50. agentops/agent/sources/spec_detectors/__init__.py +42 -0
  51. agentops/agent/sources/spec_detectors/_base.py +58 -0
  52. agentops/agent/sources/spec_detectors/agents_md.py +75 -0
  53. agentops/agent/sources/spec_detectors/spec_kit.py +172 -0
  54. agentops/agent/time_range.py +117 -0
  55. agentops/cli/__init__.py +1 -0
  56. agentops/cli/app.py +4823 -0
  57. agentops/core/__init__.py +1 -0
  58. agentops/core/agentops_config.py +592 -0
  59. agentops/core/config_loader.py +22 -0
  60. agentops/core/evaluators.py +480 -0
  61. agentops/core/release_evidence.py +56 -0
  62. agentops/core/results.py +117 -0
  63. agentops/mcp/__init__.py +10 -0
  64. agentops/mcp/server.py +232 -0
  65. agentops/pipeline/__init__.py +8 -0
  66. agentops/pipeline/cloud_results.py +189 -0
  67. agentops/pipeline/cloud_runner.py +901 -0
  68. agentops/pipeline/comparison.py +108 -0
  69. agentops/pipeline/diagnostics.py +51 -0
  70. agentops/pipeline/invocations.py +535 -0
  71. agentops/pipeline/official_eval.py +414 -0
  72. agentops/pipeline/orchestrator.py +775 -0
  73. agentops/pipeline/prompt_deploy.py +377 -0
  74. agentops/pipeline/publisher.py +121 -0
  75. agentops/pipeline/reporter.py +202 -0
  76. agentops/pipeline/runtime.py +409 -0
  77. agentops/pipeline/thresholds.py +84 -0
  78. agentops/services/__init__.py +1 -0
  79. agentops/services/cicd.py +720 -0
  80. agentops/services/eval_analysis.py +848 -0
  81. agentops/services/evidence_pack.py +757 -0
  82. agentops/services/initializer.py +86 -0
  83. agentops/services/preflight.py +470 -0
  84. agentops/services/setup_wizard.py +709 -0
  85. agentops/services/skills.py +643 -0
  86. agentops/services/trace_promotion.py +300 -0
  87. agentops/services/workflow_analysis.py +1129 -0
  88. agentops/templates/.gitignore +15 -0
  89. agentops/templates/__init__.py +1 -0
  90. agentops/templates/agent-server/Dockerfile +23 -0
  91. agentops/templates/agent-server/README.md +61 -0
  92. agentops/templates/agent-server/main.bicep +94 -0
  93. agentops/templates/agent.yaml +87 -0
  94. agentops/templates/agentops.yaml +58 -0
  95. agentops/templates/foundry.svg +71 -0
  96. agentops/templates/icon.png +0 -0
  97. agentops/templates/pipelines/azuredevops/agentops-deploy-dev-azd.yml +118 -0
  98. agentops/templates/pipelines/azuredevops/agentops-deploy-dev.yml +73 -0
  99. agentops/templates/pipelines/azuredevops/agentops-deploy-prod-azd.yml +141 -0
  100. agentops/templates/pipelines/azuredevops/agentops-deploy-prod.yml +94 -0
  101. agentops/templates/pipelines/azuredevops/agentops-deploy-prompt-agent.yml +167 -0
  102. agentops/templates/pipelines/azuredevops/agentops-deploy-qa-azd.yml +118 -0
  103. agentops/templates/pipelines/azuredevops/agentops-deploy-qa.yml +68 -0
  104. agentops/templates/pipelines/azuredevops/agentops-pr-prompt-agent.yml +210 -0
  105. agentops/templates/pipelines/azuredevops/agentops-pr.yml +155 -0
  106. agentops/templates/pipelines/azuredevops/agentops-watchdog.yml +106 -0
  107. agentops/templates/project.gitignore +36 -0
  108. agentops/templates/sample-traces.jsonl +3 -0
  109. agentops/templates/skills/agentops-agent/SKILL.md +137 -0
  110. agentops/templates/skills/agentops-config/SKILL.md +113 -0
  111. agentops/templates/skills/agentops-dataset/SKILL.md +84 -0
  112. agentops/templates/skills/agentops-eval/SKILL.md +189 -0
  113. agentops/templates/skills/agentops-report/SKILL.md +71 -0
  114. agentops/templates/skills/agentops-workflow/SKILL.md +471 -0
  115. agentops/templates/smoke.jsonl +3 -0
  116. agentops/templates/waf-checklist.README.md +84 -0
  117. agentops/templates/waf-checklist.csv +22 -0
  118. agentops/templates/workflows/agentops-deploy-dev-azd.yml +166 -0
  119. agentops/templates/workflows/agentops-deploy-dev.yml +187 -0
  120. agentops/templates/workflows/agentops-deploy-prod-azd.yml +183 -0
  121. agentops/templates/workflows/agentops-deploy-prod.yml +171 -0
  122. agentops/templates/workflows/agentops-deploy-prompt-agent.yml +197 -0
  123. agentops/templates/workflows/agentops-deploy-qa-azd.yml +156 -0
  124. agentops/templates/workflows/agentops-deploy-qa.yml +145 -0
  125. agentops/templates/workflows/agentops-pr-prompt-agent.yml +210 -0
  126. agentops/templates/workflows/agentops-pr.yml +148 -0
  127. agentops/templates/workflows/agentops-watchdog.yml +122 -0
  128. agentops/utils/__init__.py +1 -0
  129. agentops/utils/azd_env.py +435 -0
  130. agentops/utils/azure_endpoints.py +62 -0
  131. agentops/utils/colors.py +47 -0
  132. agentops/utils/dotenv_loader.py +105 -0
  133. agentops/utils/foundry_discovery.py +229 -0
  134. agentops/utils/logging.py +59 -0
  135. agentops/utils/telemetry.py +554 -0
  136. agentops/utils/yaml.py +36 -0
  137. agentops_accelerator-0.3.0.dist-info/METADATA +278 -0
  138. agentops_accelerator-0.3.0.dist-info/RECORD +142 -0
  139. agentops_accelerator-0.3.0.dist-info/WHEEL +5 -0
  140. agentops_accelerator-0.3.0.dist-info/entry_points.txt +2 -0
  141. agentops_accelerator-0.3.0.dist-info/licenses/LICENSE +21 -0
  142. agentops_accelerator-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,480 @@
1
+ """Evaluator catalog and auto-selection for AgentOps 1.0.
2
+
3
+ This module replaces the layered ``bundle.yaml`` system. There is no
4
+ user-facing ``scenario`` concept. Evaluators are picked from two inputs:
5
+
6
+ 1. The resolved target kind (agent vs model). Model targets only get the
7
+ baseline quality evaluators - agent-specific evaluators are skipped even
8
+ if the dataset contains those fields.
9
+ 2. The shape of the dataset rows:
10
+
11
+ * Always: baseline quality evaluators (Coherence, Fluency).
12
+ * Model-direct targets add exact/reference-answer checks (Similarity,
13
+ F1Score).
14
+ * Agent targets add answer-quality checks that tolerate free-form responses
15
+ (Similarity, ResponseCompleteness).
16
+ * If rows include ``context``: add RAG evaluators (Groundedness,
17
+ Retrieval, Relevance, ResponseCompleteness).
18
+ * If rows include ``tool_calls`` or ``tool_definitions``: add agent
19
+ evaluators (ToolCallAccuracy, IntentResolution, TaskAdherence).
20
+
21
+ The :func:`select_evaluators` function returns a list of resolved
22
+ :class:`EvaluatorPreset` objects. Each preset carries its class name, the
23
+ input mapping it requires, the score key it produces, and a default
24
+ threshold. The runner uses these presets to instantiate
25
+ ``azure-ai-evaluation`` evaluator classes against each dataset row.
26
+
27
+ Power users can override the auto-selection by listing evaluator names in
28
+ ``agentops.yaml`` under ``evaluators:``. When set, the override list is the
29
+ final word - no auto-detection runs.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ from dataclasses import dataclass, field
36
+ from pathlib import Path
37
+ from typing import Dict, FrozenSet, Iterable, List, Optional, Tuple
38
+
39
+ from agentops.core.agentops_config import TargetKind, TargetResolution, Threshold
40
+
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Catalog
44
+ # ---------------------------------------------------------------------------
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class EvaluatorPreset:
49
+ """Metadata for a single evaluator known to AgentOps.
50
+
51
+ ``input_mapping`` keys are the parameter names the evaluator class
52
+ expects; values use the placeholder syntax ``$prompt``, ``$prediction``,
53
+ ``$context``, ``$expected``, ``$tool_calls``, ``$tool_definitions``
54
+ which the runner resolves per row.
55
+ """
56
+
57
+ name: str
58
+ class_name: str
59
+ score_key: str
60
+ input_mapping: Dict[str, str]
61
+ default_threshold: Optional[Threshold] = None
62
+ #: Categories that this evaluator belongs to. Used by the inference rules.
63
+ categories: FrozenSet[str] = field(default_factory=frozenset)
64
+ #: Set when this evaluator is not safe to run for raw model deployments.
65
+ agent_only: bool = False
66
+ #: When True and the row carries ``tool_calls``, the runner upgrades the
67
+ #: ``query`` and ``response`` kwargs from plain strings to conversation
68
+ #: message lists that include the agent's tool_call + tool_result trace.
69
+ #: This is required for evaluators that judge agent reasoning (e.g.
70
+ #: TaskAdherence, IntentResolution) - without the trace they only see a
71
+ #: short final answer and consistently score it as 1/5.
72
+ needs_conversation: bool = False
73
+
74
+
75
+ def _t(metric: str, criteria: str, value: float) -> Threshold:
76
+ return Threshold(metric=metric, criteria=criteria, value=value) # type: ignore[arg-type]
77
+
78
+
79
+ _QUALITY_BASELINE: Tuple[EvaluatorPreset, ...] = (
80
+ EvaluatorPreset(
81
+ name="CoherenceEvaluator",
82
+ class_name="CoherenceEvaluator",
83
+ score_key="coherence",
84
+ input_mapping={"query": "$prompt", "response": "$prediction"},
85
+ default_threshold=_t("coherence", ">=", 3.0),
86
+ categories=frozenset({"quality"}),
87
+ ),
88
+ EvaluatorPreset(
89
+ name="FluencyEvaluator",
90
+ class_name="FluencyEvaluator",
91
+ score_key="fluency",
92
+ input_mapping={"response": "$prediction"},
93
+ default_threshold=_t("fluency", ">=", 3.0),
94
+ categories=frozenset({"quality"}),
95
+ ),
96
+ )
97
+
98
+ _SIMILARITY = EvaluatorPreset(
99
+ name="SimilarityEvaluator",
100
+ class_name="SimilarityEvaluator",
101
+ score_key="similarity",
102
+ input_mapping={
103
+ "query": "$prompt",
104
+ "response": "$prediction",
105
+ "ground_truth": "$expected",
106
+ },
107
+ default_threshold=_t("similarity", ">=", 3.0),
108
+ categories=frozenset({"quality"}),
109
+ )
110
+
111
+ _F1_SCORE = EvaluatorPreset(
112
+ name="F1ScoreEvaluator",
113
+ class_name="F1ScoreEvaluator",
114
+ score_key="f1_score",
115
+ input_mapping={
116
+ "response": "$prediction",
117
+ "ground_truth": "$expected",
118
+ },
119
+ default_threshold=_t("f1_score", ">=", 0.5),
120
+ categories=frozenset({"quality", "exact_reference"}),
121
+ )
122
+
123
+ _RESPONSE_COMPLETENESS = EvaluatorPreset(
124
+ name="ResponseCompletenessEvaluator",
125
+ class_name="ResponseCompletenessEvaluator",
126
+ score_key="response_completeness",
127
+ input_mapping={
128
+ "query": "$prompt",
129
+ "response": "$prediction",
130
+ "ground_truth": "$expected",
131
+ },
132
+ default_threshold=_t("response_completeness", ">=", 3.0),
133
+ categories=frozenset({"quality", "rag"}),
134
+ agent_only=True,
135
+ )
136
+
137
+ _MODEL_REFERENCE_EVALUATORS: Tuple[EvaluatorPreset, ...] = (
138
+ _SIMILARITY,
139
+ _F1_SCORE,
140
+ )
141
+
142
+ _AGENT_ANSWER_EVALUATORS: Tuple[EvaluatorPreset, ...] = (
143
+ _SIMILARITY,
144
+ _RESPONSE_COMPLETENESS,
145
+ )
146
+
147
+
148
+ _RAG_EVALUATORS: Tuple[EvaluatorPreset, ...] = (
149
+ EvaluatorPreset(
150
+ name="GroundednessEvaluator",
151
+ class_name="GroundednessEvaluator",
152
+ score_key="groundedness",
153
+ input_mapping={
154
+ "query": "$prompt",
155
+ "response": "$prediction",
156
+ "context": "$context",
157
+ },
158
+ default_threshold=_t("groundedness", ">=", 3.0),
159
+ categories=frozenset({"rag"}),
160
+ agent_only=True,
161
+ ),
162
+ EvaluatorPreset(
163
+ name="RelevanceEvaluator",
164
+ class_name="RelevanceEvaluator",
165
+ score_key="relevance",
166
+ input_mapping={
167
+ "query": "$prompt",
168
+ "response": "$prediction",
169
+ "context": "$context",
170
+ },
171
+ default_threshold=_t("relevance", ">=", 3.0),
172
+ categories=frozenset({"rag"}),
173
+ agent_only=True,
174
+ ),
175
+ EvaluatorPreset(
176
+ name="RetrievalEvaluator",
177
+ class_name="RetrievalEvaluator",
178
+ score_key="retrieval",
179
+ input_mapping={"query": "$prompt", "context": "$context"},
180
+ default_threshold=_t("retrieval", ">=", 3.0),
181
+ categories=frozenset({"rag"}),
182
+ agent_only=True,
183
+ ),
184
+ _RESPONSE_COMPLETENESS,
185
+ )
186
+
187
+
188
+ _TOOL_USE_EVALUATORS: Tuple[EvaluatorPreset, ...] = (
189
+ EvaluatorPreset(
190
+ name="ToolCallAccuracyEvaluator",
191
+ class_name="ToolCallAccuracyEvaluator",
192
+ score_key="tool_call_accuracy",
193
+ input_mapping={
194
+ "query": "$prompt",
195
+ "tool_calls": "$tool_calls",
196
+ "tool_definitions": "$tool_definitions",
197
+ },
198
+ default_threshold=_t("tool_call_accuracy", ">=", 0.7),
199
+ categories=frozenset({"agent"}),
200
+ agent_only=True,
201
+ ),
202
+ EvaluatorPreset(
203
+ name="IntentResolutionEvaluator",
204
+ class_name="IntentResolutionEvaluator",
205
+ score_key="intent_resolution",
206
+ input_mapping={
207
+ "query": "$prompt",
208
+ "response": "$prediction",
209
+ "tool_definitions": "$tool_definitions",
210
+ },
211
+ default_threshold=_t("intent_resolution", ">=", 3.0),
212
+ categories=frozenset({"agent"}),
213
+ agent_only=True,
214
+ needs_conversation=True,
215
+ ),
216
+ EvaluatorPreset(
217
+ name="TaskAdherenceEvaluator",
218
+ class_name="TaskAdherenceEvaluator",
219
+ score_key="task_adherence",
220
+ input_mapping={
221
+ "query": "$prompt",
222
+ "response": "$prediction",
223
+ "tool_definitions": "$tool_definitions",
224
+ },
225
+ # azure-ai-evaluation's TaskAdherenceEvaluator returns a binary
226
+ # 0/1 score (0 = flagged, 1 = adheres) - *not* a 1–5 Likert scale
227
+ # like IntentResolutionEvaluator. We default to >=0.5 so a score
228
+ # of 1.0 passes and 0.0 fails.
229
+ default_threshold=_t("task_adherence", ">=", 0.5),
230
+ categories=frozenset({"agent"}),
231
+ agent_only=True,
232
+ needs_conversation=True,
233
+ ),
234
+ )
235
+
236
+
237
+ _LATENCY = EvaluatorPreset(
238
+ name="avg_latency_seconds",
239
+ class_name="_latency",
240
+ score_key="avg_latency_seconds",
241
+ input_mapping={},
242
+ # 30s default accommodates Foundry cold-start spikes on the first
243
+ # invocation after an agent is created or scaled to zero. Production
244
+ # users should tighten this in agentops.yaml once warmup is observed.
245
+ default_threshold=_t("avg_latency_seconds", "<=", 30.0),
246
+ categories=frozenset({"runtime"}),
247
+ )
248
+
249
+
250
+ CATALOG: Dict[str, EvaluatorPreset] = {
251
+ preset.name: preset
252
+ for preset in (
253
+ *_QUALITY_BASELINE,
254
+ *_MODEL_REFERENCE_EVALUATORS,
255
+ *_AGENT_ANSWER_EVALUATORS,
256
+ *_RAG_EVALUATORS,
257
+ *_TOOL_USE_EVALUATORS,
258
+ _LATENCY,
259
+ )
260
+ }
261
+
262
+
263
+ # ---------------------------------------------------------------------------
264
+ # Dataset shape detection
265
+ # ---------------------------------------------------------------------------
266
+
267
+
268
+ @dataclass(frozen=True)
269
+ class DatasetShape:
270
+ """Boolean flags summarising the columns present in a dataset."""
271
+
272
+ has_context: bool
273
+ has_tool_calls: bool
274
+ has_tool_definitions: bool
275
+ row_count: int
276
+
277
+ @property
278
+ def looks_rag(self) -> bool:
279
+ return self.has_context
280
+
281
+ @property
282
+ def looks_tool_use(self) -> bool:
283
+ return self.has_tool_calls or self.has_tool_definitions
284
+
285
+
286
+ def detect_dataset_shape(dataset_path: Path, *, sample: int = 50) -> DatasetShape:
287
+ """Inspect up to ``sample`` rows of ``dataset_path`` and report the shape.
288
+
289
+ Truthy values are required - empty strings, empty lists, and ``None`` do
290
+ not count as the field being present.
291
+ """
292
+ if not dataset_path.exists():
293
+ raise FileNotFoundError(f"dataset file not found: {dataset_path}")
294
+
295
+ has_context = False
296
+ has_tool_calls = False
297
+ has_tool_definitions = False
298
+ count = 0
299
+
300
+ with dataset_path.open("r", encoding="utf-8") as handle:
301
+ for line in handle:
302
+ stripped = line.strip()
303
+ if not stripped:
304
+ continue
305
+ count += 1
306
+ try:
307
+ row = json.loads(stripped)
308
+ except json.JSONDecodeError as exc:
309
+ raise ValueError(
310
+ f"{dataset_path}: invalid JSON on line {count}: {exc}"
311
+ ) from exc
312
+ if not isinstance(row, dict):
313
+ raise ValueError(
314
+ f"{dataset_path}: line {count} is not a JSON object"
315
+ )
316
+
317
+ if not has_context and row.get("context"):
318
+ has_context = True
319
+ if not has_tool_calls and row.get("tool_calls"):
320
+ has_tool_calls = True
321
+ if not has_tool_definitions and row.get("tool_definitions"):
322
+ has_tool_definitions = True
323
+
324
+ if count >= sample and (
325
+ has_context and (has_tool_calls or has_tool_definitions)
326
+ ):
327
+ # Already saw both signals; no need to keep reading.
328
+ break
329
+
330
+ if count == 0:
331
+ raise ValueError(f"{dataset_path}: dataset is empty")
332
+
333
+ return DatasetShape(
334
+ has_context=has_context,
335
+ has_tool_calls=has_tool_calls,
336
+ has_tool_definitions=has_tool_definitions,
337
+ row_count=count,
338
+ )
339
+
340
+
341
+ # ---------------------------------------------------------------------------
342
+ # Selection
343
+ # ---------------------------------------------------------------------------
344
+
345
+
346
+ def select_evaluators(
347
+ target: TargetResolution,
348
+ shape: DatasetShape,
349
+ *,
350
+ overrides: Optional[List[str]] = None,
351
+ threshold_metrics: Optional[Iterable[str]] = None,
352
+ ) -> List[EvaluatorPreset]:
353
+ """Return the ordered list of evaluators to run.
354
+
355
+ When ``overrides`` is provided it wins outright - the inference rules are
356
+ bypassed. Each name must exist in :data:`CATALOG` or a ``ValueError`` is
357
+ raised.
358
+
359
+ Otherwise the rules are:
360
+
361
+ * Always include the baseline quality evaluators.
362
+ * If the target is a raw model, add exact/reference-answer evaluators and
363
+ stop here. Agent-specific evaluators are not meaningful (no tool calls,
364
+ no retrieved context).
365
+ * If the target is an agent without RAG/tool-use rows, add answer-quality
366
+ evaluators that tolerate free-form responses.
367
+ * If the dataset has ``context`` rows, add the RAG evaluators.
368
+ * If the dataset has ``tool_calls`` or ``tool_definitions``, add the agent
369
+ evaluators.
370
+ * Always append the runtime ``avg_latency_seconds`` evaluator.
371
+ """
372
+ if overrides:
373
+ resolved: List[EvaluatorPreset] = []
374
+ for name in overrides:
375
+ preset = CATALOG.get(name)
376
+ if preset is None:
377
+ known = ", ".join(sorted(CATALOG.keys()))
378
+ raise ValueError(
379
+ f"unknown evaluator override {name!r}. "
380
+ f"Known evaluators: {known}"
381
+ )
382
+ resolved.append(preset)
383
+ return resolved
384
+
385
+ selected: List[EvaluatorPreset] = list(_QUALITY_BASELINE)
386
+
387
+ if target.kind == "model_direct":
388
+ selected.extend(_MODEL_REFERENCE_EVALUATORS)
389
+ elif _is_agent_target(target.kind):
390
+ if not shape.looks_rag and not shape.looks_tool_use:
391
+ selected.extend(_AGENT_ANSWER_EVALUATORS)
392
+ if shape.looks_rag:
393
+ selected.extend(_RAG_EVALUATORS)
394
+ if shape.looks_tool_use:
395
+ selected.extend(_TOOL_USE_EVALUATORS)
396
+ # F1ScoreEvaluator and SimilarityEvaluator compare the
397
+ # assistant's natural-language reply against ``expected``. In
398
+ # tool-using datasets ``expected`` is conventionally a behavior
399
+ # description (e.g. "Calls lookup_order with order_id='ORD-12345'")
400
+ # rather than the literal reply, so token overlap and semantic
401
+ # similarity are meaningless and gate well-behaved agents on a
402
+ # metric that does not apply. Drop both from the selection.
403
+ _drop = {"F1ScoreEvaluator", "SimilarityEvaluator"}
404
+ selected = [p for p in selected if p.name not in _drop]
405
+
406
+ if threshold_metrics:
407
+ selected = _include_thresholded_quality_presets(
408
+ selected,
409
+ target.kind,
410
+ threshold_metrics,
411
+ )
412
+
413
+ selected.append(_LATENCY)
414
+ return selected
415
+
416
+
417
+ def _is_agent_target(kind: TargetKind) -> bool:
418
+ return kind in {"foundry_prompt", "foundry_hosted", "http_json"}
419
+
420
+
421
+ def _include_thresholded_quality_presets(
422
+ selected: List[EvaluatorPreset],
423
+ target_kind: TargetKind,
424
+ threshold_metrics: Iterable[str],
425
+ ) -> List[EvaluatorPreset]:
426
+ """Honor explicit thresholds for optional quality/reference metrics.
427
+
428
+ A user who already set ``thresholds: {f1_score: ...}`` should not also need
429
+ to learn the evaluator override escape hatch. This keeps exact-reference F1
430
+ available as an opt-in gate without making it a default for free-form agent
431
+ responses.
432
+ """
433
+ by_score_key = {preset.score_key: preset for preset in CATALOG.values()}
434
+ selected_names = {preset.name for preset in selected}
435
+ result = list(selected)
436
+ for metric in threshold_metrics:
437
+ preset = by_score_key.get(metric)
438
+ if preset is None or preset.name in selected_names:
439
+ continue
440
+ if "runtime" in preset.categories:
441
+ continue
442
+ if "quality" not in preset.categories and "exact_reference" not in preset.categories:
443
+ continue
444
+ if preset.agent_only and not _is_agent_target(target_kind):
445
+ continue
446
+ result.append(preset)
447
+ selected_names.add(preset.name)
448
+ return result
449
+
450
+
451
+ def merge_thresholds(
452
+ presets: List[EvaluatorPreset],
453
+ user_thresholds: List[Threshold],
454
+ ) -> List[Threshold]:
455
+ """Combine evaluator default thresholds with user overrides.
456
+
457
+ User entries override the preset default for the same metric. Metrics
458
+ listed by the user that don't correspond to any selected preset are kept
459
+ as-is - the threshold engine will report them as unmet rather than
460
+ silently drop them.
461
+ """
462
+ by_metric: Dict[str, Threshold] = {}
463
+ for preset in presets:
464
+ if preset.default_threshold is not None:
465
+ by_metric[preset.default_threshold.metric] = preset.default_threshold
466
+ for override in user_thresholds:
467
+ by_metric[override.metric] = override
468
+ # Preserve preset order, then append user-only metrics in original order.
469
+ ordered: List[Threshold] = []
470
+ seen: set[str] = set()
471
+ for preset in presets:
472
+ if preset.default_threshold is not None:
473
+ metric = preset.default_threshold.metric
474
+ ordered.append(by_metric[metric])
475
+ seen.add(metric)
476
+ for override in user_thresholds:
477
+ if override.metric not in seen:
478
+ ordered.append(override)
479
+ seen.add(override.metric)
480
+ return ordered
@@ -0,0 +1,56 @@
1
+ """Versioned schema for AgentOps release evidence artifacts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Literal, Optional
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+
10
+ ReadinessStatus = Literal["ready", "ready_with_warnings", "blocked"]
11
+ CheckStatus = Literal["ready", "warning", "blocked", "unknown"]
12
+
13
+
14
+ class ReleaseEvidenceLink(BaseModel):
15
+ """A navigable reference related to the release decision."""
16
+
17
+ label: str
18
+ url: str
19
+
20
+ model_config = ConfigDict(extra="forbid")
21
+
22
+
23
+ class ReleaseEvidenceCheck(BaseModel):
24
+ """One production-readiness check in the evidence pack."""
25
+
26
+ name: str
27
+ status: CheckStatus
28
+ summary: str
29
+ evidence: Dict[str, Any] = Field(default_factory=dict)
30
+
31
+ model_config = ConfigDict(extra="forbid")
32
+
33
+
34
+ class ReleaseEvidence(BaseModel):
35
+ """Stable machine-readable production-readiness evidence contract."""
36
+
37
+ version: int = 1
38
+ generated_at: str
39
+ workspace: str
40
+ status: ReadinessStatus
41
+ target: Optional[str] = None
42
+ blockers: List[str] = Field(default_factory=list)
43
+ warnings: List[str] = Field(default_factory=list)
44
+ ready: List[str] = Field(default_factory=list)
45
+ checks: List[ReleaseEvidenceCheck] = Field(default_factory=list)
46
+ links: List[ReleaseEvidenceLink] = Field(default_factory=list)
47
+ latest_eval: Dict[str, Any] = Field(default_factory=dict)
48
+ official_eval: Dict[str, Any] = Field(default_factory=dict)
49
+ doctor: Dict[str, Any] = Field(default_factory=dict)
50
+ workflows: Dict[str, Any] = Field(default_factory=dict)
51
+ foundry: Dict[str, Any] = Field(default_factory=dict)
52
+ monitoring: Dict[str, Any] = Field(default_factory=dict)
53
+ trace_dataset: Dict[str, Any] = Field(default_factory=dict)
54
+ ailz: Dict[str, Any] = Field(default_factory=dict)
55
+
56
+ model_config = ConfigDict(extra="forbid")
@@ -0,0 +1,117 @@
1
+ """Result dataclasses for the AgentOps 1.0 pipeline.
2
+
3
+ These shapes are written to ``results.json`` after every ``agentops eval`` run
4
+ and consumed by the reporter and comparison logic.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+
14
+ class RowMetric(BaseModel):
15
+ """A single evaluator score for one dataset row."""
16
+
17
+ name: str
18
+ value: Optional[float] = None
19
+ error: Optional[str] = None
20
+ reason: Optional[str] = None
21
+
22
+
23
+ class RowResult(BaseModel):
24
+ """One evaluated dataset row."""
25
+
26
+ row_index: int
27
+ input: str
28
+ expected: Optional[str] = None
29
+ response: str = ""
30
+ context: Optional[str] = None
31
+ latency_seconds: Optional[float] = None
32
+ tool_calls: Optional[List[Any]] = None
33
+ metrics: List[RowMetric] = Field(default_factory=list)
34
+ error: Optional[str] = None
35
+
36
+
37
+ class ThresholdEvaluation(BaseModel):
38
+ """A pass/fail check for a single metric on the run aggregate."""
39
+
40
+ metric: str
41
+ criteria: str
42
+ expected: str
43
+ actual: str
44
+ passed: bool
45
+
46
+
47
+ class RunSummary(BaseModel):
48
+ """Top-level pass/fail summary of an evaluation run."""
49
+
50
+ items_total: int
51
+ items_passed_all: int
52
+ items_pass_rate: float
53
+ thresholds_total: int
54
+ thresholds_passed: int
55
+ threshold_pass_rate: float
56
+ overall_passed: bool
57
+
58
+
59
+ class TargetInfo(BaseModel):
60
+ """Resolved target information (echoed into results.json)."""
61
+
62
+ kind: str
63
+ raw: str
64
+ protocol: Optional[str] = None
65
+ name: Optional[str] = None
66
+ version: Optional[str] = None
67
+ url: Optional[str] = None
68
+ deployment: Optional[str] = None
69
+
70
+
71
+ class ComparisonMetric(BaseModel):
72
+ """Per-metric delta between the current run and a baseline."""
73
+
74
+ metric: str
75
+ current: Optional[float] = None
76
+ baseline: Optional[float] = None
77
+ delta: Optional[float] = None
78
+ direction: str # "improved" | "regressed" | "unchanged"
79
+
80
+
81
+ class ComparisonRow(BaseModel):
82
+ """Per-row regression / improvement against a baseline."""
83
+
84
+ row_index: int
85
+ current_passed: bool
86
+ baseline_passed: Optional[bool] = None
87
+ direction: str # "improved" | "regressed" | "unchanged" | "new"
88
+
89
+
90
+ class ComparisonInfo(BaseModel):
91
+ """Comparison block included when ``--baseline`` was provided."""
92
+
93
+ baseline_path: str
94
+ baseline_started_at: Optional[str] = None
95
+ baseline_overall_passed: Optional[bool] = None
96
+ metrics: List[ComparisonMetric] = Field(default_factory=list)
97
+ rows: List[ComparisonRow] = Field(default_factory=list)
98
+
99
+
100
+ class RunResult(BaseModel):
101
+ """Full ``results.json`` payload."""
102
+
103
+ version: int = 1
104
+ started_at: str
105
+ finished_at: str
106
+ duration_seconds: float
107
+ target: TargetInfo
108
+ dataset_path: str
109
+ evaluators: List[str] = Field(default_factory=list)
110
+ rows: List[RowResult] = Field(default_factory=list)
111
+ aggregate_metrics: Dict[str, float] = Field(default_factory=dict)
112
+ thresholds: List[ThresholdEvaluation] = Field(default_factory=list)
113
+ summary: RunSummary
114
+ comparison: Optional[ComparisonInfo] = None
115
+ config: Dict[str, Any] = Field(default_factory=dict)
116
+
117
+ model_config = ConfigDict(extra="forbid")
@@ -0,0 +1,10 @@
1
+ """MCP (Model Context Protocol) server for AgentOps.
2
+
3
+ Exposes the most-used AgentOps capabilities as MCP tools over stdio so that
4
+ MCP-aware coding agents (Claude Code, Copilot Chat, etc.) can drive AgentOps
5
+ without shelling out to the CLI.
6
+
7
+ The server is opt-in: it requires the optional ``mcp`` extra
8
+ (``pip install agentops-accelerator[mcp]``) and is started via
9
+ ``agentops mcp serve``.
10
+ """