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.
- agentops/__init__.py +10 -0
- agentops/__main__.py +6 -0
- agentops/agent/__init__.py +12 -0
- agentops/agent/_legacy_ids.py +92 -0
- agentops/agent/analyzer.py +207 -0
- agentops/agent/checks/__init__.py +1 -0
- agentops/agent/checks/catalog.py +880 -0
- agentops/agent/checks/errors.py +279 -0
- agentops/agent/checks/foundry_config.py +75 -0
- agentops/agent/checks/latency.py +84 -0
- agentops/agent/checks/opex.py +157 -0
- agentops/agent/checks/opex_workspace.py +874 -0
- agentops/agent/checks/posture.py +36 -0
- agentops/agent/checks/posture_rules/__init__.py +53 -0
- agentops/agent/checks/posture_rules/content_filter.py +59 -0
- agentops/agent/checks/posture_rules/diagnostics.py +74 -0
- agentops/agent/checks/posture_rules/local_auth.py +55 -0
- agentops/agent/checks/posture_rules/managed_identity.py +59 -0
- agentops/agent/checks/posture_rules/network.py +68 -0
- agentops/agent/checks/regression.py +78 -0
- agentops/agent/checks/release_readiness.py +182 -0
- agentops/agent/checks/safety.py +247 -0
- agentops/agent/checks/spec_conformance.py +375 -0
- agentops/agent/cockpit.py +5159 -0
- agentops/agent/config.py +240 -0
- agentops/agent/findings.py +113 -0
- agentops/agent/history.py +142 -0
- agentops/agent/knowledge/__init__.py +182 -0
- agentops/agent/knowledge/waf-checklist.csv +39 -0
- agentops/agent/llm_assist/__init__.py +16 -0
- agentops/agent/llm_assist/_base.py +124 -0
- agentops/agent/llm_assist/_bundle_rule.py +154 -0
- agentops/agent/llm_assist/_client.py +347 -0
- agentops/agent/llm_assist/_dataset_rules.py +191 -0
- agentops/agent/llm_assist/_engine.py +106 -0
- agentops/agent/llm_assist/_prompt_rules.py +291 -0
- agentops/agent/llm_assist/_spec_rules.py +235 -0
- agentops/agent/production_telemetry.py +430 -0
- agentops/agent/report.py +207 -0
- agentops/agent/server/__init__.py +1 -0
- agentops/agent/server/app.py +84 -0
- agentops/agent/server/auth.py +94 -0
- agentops/agent/server/chat.py +44 -0
- agentops/agent/server/protocol.py +72 -0
- agentops/agent/sources/__init__.py +1 -0
- agentops/agent/sources/azure_monitor.py +523 -0
- agentops/agent/sources/azure_resources.py +602 -0
- agentops/agent/sources/foundry_control.py +174 -0
- agentops/agent/sources/results_history.py +494 -0
- agentops/agent/sources/spec_detectors/__init__.py +42 -0
- agentops/agent/sources/spec_detectors/_base.py +58 -0
- agentops/agent/sources/spec_detectors/agents_md.py +75 -0
- agentops/agent/sources/spec_detectors/spec_kit.py +172 -0
- agentops/agent/time_range.py +117 -0
- agentops/cli/__init__.py +1 -0
- agentops/cli/app.py +4823 -0
- agentops/core/__init__.py +1 -0
- agentops/core/agentops_config.py +592 -0
- agentops/core/config_loader.py +22 -0
- agentops/core/evaluators.py +480 -0
- agentops/core/release_evidence.py +56 -0
- agentops/core/results.py +117 -0
- agentops/mcp/__init__.py +10 -0
- agentops/mcp/server.py +232 -0
- agentops/pipeline/__init__.py +8 -0
- agentops/pipeline/cloud_results.py +189 -0
- agentops/pipeline/cloud_runner.py +901 -0
- agentops/pipeline/comparison.py +108 -0
- agentops/pipeline/diagnostics.py +51 -0
- agentops/pipeline/invocations.py +535 -0
- agentops/pipeline/official_eval.py +414 -0
- agentops/pipeline/orchestrator.py +775 -0
- agentops/pipeline/prompt_deploy.py +377 -0
- agentops/pipeline/publisher.py +121 -0
- agentops/pipeline/reporter.py +202 -0
- agentops/pipeline/runtime.py +409 -0
- agentops/pipeline/thresholds.py +84 -0
- agentops/services/__init__.py +1 -0
- agentops/services/cicd.py +720 -0
- agentops/services/eval_analysis.py +848 -0
- agentops/services/evidence_pack.py +757 -0
- agentops/services/initializer.py +86 -0
- agentops/services/preflight.py +470 -0
- agentops/services/setup_wizard.py +709 -0
- agentops/services/skills.py +643 -0
- agentops/services/trace_promotion.py +300 -0
- agentops/services/workflow_analysis.py +1129 -0
- agentops/templates/.gitignore +15 -0
- agentops/templates/__init__.py +1 -0
- agentops/templates/agent-server/Dockerfile +23 -0
- agentops/templates/agent-server/README.md +61 -0
- agentops/templates/agent-server/main.bicep +94 -0
- agentops/templates/agent.yaml +87 -0
- agentops/templates/agentops.yaml +58 -0
- agentops/templates/foundry.svg +71 -0
- agentops/templates/icon.png +0 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-dev-azd.yml +118 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-dev.yml +73 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-prod-azd.yml +141 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-prod.yml +94 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-prompt-agent.yml +167 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-qa-azd.yml +118 -0
- agentops/templates/pipelines/azuredevops/agentops-deploy-qa.yml +68 -0
- agentops/templates/pipelines/azuredevops/agentops-pr-prompt-agent.yml +210 -0
- agentops/templates/pipelines/azuredevops/agentops-pr.yml +155 -0
- agentops/templates/pipelines/azuredevops/agentops-watchdog.yml +106 -0
- agentops/templates/project.gitignore +36 -0
- agentops/templates/sample-traces.jsonl +3 -0
- agentops/templates/skills/agentops-agent/SKILL.md +137 -0
- agentops/templates/skills/agentops-config/SKILL.md +113 -0
- agentops/templates/skills/agentops-dataset/SKILL.md +84 -0
- agentops/templates/skills/agentops-eval/SKILL.md +189 -0
- agentops/templates/skills/agentops-report/SKILL.md +71 -0
- agentops/templates/skills/agentops-workflow/SKILL.md +471 -0
- agentops/templates/smoke.jsonl +3 -0
- agentops/templates/waf-checklist.README.md +84 -0
- agentops/templates/waf-checklist.csv +22 -0
- agentops/templates/workflows/agentops-deploy-dev-azd.yml +166 -0
- agentops/templates/workflows/agentops-deploy-dev.yml +187 -0
- agentops/templates/workflows/agentops-deploy-prod-azd.yml +183 -0
- agentops/templates/workflows/agentops-deploy-prod.yml +171 -0
- agentops/templates/workflows/agentops-deploy-prompt-agent.yml +197 -0
- agentops/templates/workflows/agentops-deploy-qa-azd.yml +156 -0
- agentops/templates/workflows/agentops-deploy-qa.yml +145 -0
- agentops/templates/workflows/agentops-pr-prompt-agent.yml +210 -0
- agentops/templates/workflows/agentops-pr.yml +148 -0
- agentops/templates/workflows/agentops-watchdog.yml +122 -0
- agentops/utils/__init__.py +1 -0
- agentops/utils/azd_env.py +435 -0
- agentops/utils/azure_endpoints.py +62 -0
- agentops/utils/colors.py +47 -0
- agentops/utils/dotenv_loader.py +105 -0
- agentops/utils/foundry_discovery.py +229 -0
- agentops/utils/logging.py +59 -0
- agentops/utils/telemetry.py +554 -0
- agentops/utils/yaml.py +36 -0
- agentops_accelerator-0.3.0.dist-info/METADATA +278 -0
- agentops_accelerator-0.3.0.dist-info/RECORD +142 -0
- agentops_accelerator-0.3.0.dist-info/WHEEL +5 -0
- agentops_accelerator-0.3.0.dist-info/entry_points.txt +2 -0
- agentops_accelerator-0.3.0.dist-info/licenses/LICENSE +21 -0
- agentops_accelerator-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"""Prepare Microsoft Foundry AI Agent Evaluation inputs from AgentOps config."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Iterable, Mapping, Sequence
|
|
11
|
+
|
|
12
|
+
from agentops.core.agentops_config import AgentOpsConfig, classify_agent
|
|
13
|
+
from agentops.core.config_loader import load_agentops_config
|
|
14
|
+
from agentops.core.evaluators import EvaluatorPreset, detect_dataset_shape, select_evaluators
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
OFFICIAL_EVAL_RUNNER = "official-ai-agent-evaluation"
|
|
18
|
+
OFFICIAL_EVAL_ACTION = "microsoft/ai-agent-evals@v3-beta"
|
|
19
|
+
OFFICIAL_EVAL_ADO_TASK = "AIAgentEvaluation@2"
|
|
20
|
+
OFFICIAL_EVAL_ACTION_ENV = "AGENTOPS_OFFICIAL_EVAL_ACTION"
|
|
21
|
+
OFFICIAL_EVAL_ADO_TASK_ENV = "AGENTOPS_OFFICIAL_EVAL_ADO_TASK"
|
|
22
|
+
AGENTOPS_LOCAL_RUNNER = "agentops-local"
|
|
23
|
+
AGENTOPS_CLOUD_RUNNER = "agentops-cloud"
|
|
24
|
+
|
|
25
|
+
_LATENCY_PRESET = "avg_latency_seconds"
|
|
26
|
+
_OFFICIAL_EVALUATORS: dict[str, str] = {
|
|
27
|
+
"CoherenceEvaluator": "builtin.coherence",
|
|
28
|
+
"FluencyEvaluator": "builtin.fluency",
|
|
29
|
+
"SimilarityEvaluator": "builtin.text_similarity",
|
|
30
|
+
"F1ScoreEvaluator": "builtin.f1_score",
|
|
31
|
+
"GroundednessEvaluator": "builtin.groundedness",
|
|
32
|
+
"RelevanceEvaluator": "builtin.relevance",
|
|
33
|
+
"RetrievalEvaluator": "builtin.retrieval",
|
|
34
|
+
"ResponseCompletenessEvaluator": "builtin.response_completeness",
|
|
35
|
+
"ToolCallAccuracyEvaluator": "builtin.tool_call_accuracy",
|
|
36
|
+
"IntentResolutionEvaluator": "builtin.intent_resolution",
|
|
37
|
+
"TaskAdherenceEvaluator": "builtin.task_adherence",
|
|
38
|
+
}
|
|
39
|
+
_GROUND_TRUTH_EVALUATORS = {
|
|
40
|
+
"builtin.text_similarity",
|
|
41
|
+
"builtin.f1_score",
|
|
42
|
+
"builtin.response_completeness",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def official_eval_action_ref() -> str:
|
|
47
|
+
"""Return the GitHub Action ref used for Microsoft Foundry eval workflows."""
|
|
48
|
+
|
|
49
|
+
return os.getenv(OFFICIAL_EVAL_ACTION_ENV, OFFICIAL_EVAL_ACTION)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def official_eval_ado_task_ref() -> str:
|
|
53
|
+
"""Return the Azure DevOps task ref used for Microsoft Foundry eval workflows."""
|
|
54
|
+
|
|
55
|
+
return os.getenv(OFFICIAL_EVAL_ADO_TASK_ENV, OFFICIAL_EVAL_ADO_TASK)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OfficialEvalUnsupported(ValueError):
|
|
59
|
+
"""Raised when an AgentOps config cannot use Microsoft Foundry AI Agent Evaluation."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class OfficialEvalSupport:
|
|
64
|
+
"""Eligibility result for the Microsoft Foundry evaluation runner."""
|
|
65
|
+
|
|
66
|
+
eligible: bool
|
|
67
|
+
runner: str
|
|
68
|
+
reasons: tuple[str, ...]
|
|
69
|
+
warnings: tuple[str, ...]
|
|
70
|
+
official_evaluators: tuple[str, ...]
|
|
71
|
+
agent_ids: str | None
|
|
72
|
+
data_path: Path | None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class OfficialEvalPreparation:
|
|
77
|
+
"""Prepared Microsoft Foundry evaluation input and metadata."""
|
|
78
|
+
|
|
79
|
+
data_path: Path
|
|
80
|
+
metadata_path: Path
|
|
81
|
+
agent_ids: str
|
|
82
|
+
deployment_name: str
|
|
83
|
+
official_evaluators: tuple[str, ...]
|
|
84
|
+
warnings: tuple[str, ...]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class _EvalPlan:
|
|
89
|
+
config: AgentOpsConfig
|
|
90
|
+
config_path: Path
|
|
91
|
+
dataset_path: Path
|
|
92
|
+
agent_ids: str
|
|
93
|
+
official_evaluators: tuple[str, ...]
|
|
94
|
+
skipped_agentops_evaluators: tuple[str, ...]
|
|
95
|
+
warnings: tuple[str, ...]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def analyze_official_eval_support(config_path: Path) -> OfficialEvalSupport:
|
|
99
|
+
"""Report whether ``config_path`` can use the Microsoft Foundry eval runner."""
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
plan = _build_plan(config_path)
|
|
103
|
+
except OfficialEvalUnsupported as exc:
|
|
104
|
+
return OfficialEvalSupport(
|
|
105
|
+
eligible=False,
|
|
106
|
+
runner=AGENTOPS_LOCAL_RUNNER,
|
|
107
|
+
reasons=(str(exc),),
|
|
108
|
+
warnings=(),
|
|
109
|
+
official_evaluators=(),
|
|
110
|
+
agent_ids=None,
|
|
111
|
+
data_path=None,
|
|
112
|
+
)
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
return OfficialEvalSupport(
|
|
115
|
+
eligible=False,
|
|
116
|
+
runner=AGENTOPS_LOCAL_RUNNER,
|
|
117
|
+
reasons=(f"agentops.yaml cannot be prepared for Microsoft Foundry eval: {exc}",),
|
|
118
|
+
warnings=(),
|
|
119
|
+
official_evaluators=(),
|
|
120
|
+
agent_ids=None,
|
|
121
|
+
data_path=None,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return OfficialEvalSupport(
|
|
125
|
+
eligible=True,
|
|
126
|
+
runner=OFFICIAL_EVAL_RUNNER,
|
|
127
|
+
reasons=(
|
|
128
|
+
"Agent target is a Foundry prompt agent (`name:version`).",
|
|
129
|
+
"Dataset columns are compatible with Microsoft Foundry eval.",
|
|
130
|
+
),
|
|
131
|
+
warnings=plan.warnings,
|
|
132
|
+
official_evaluators=plan.official_evaluators,
|
|
133
|
+
agent_ids=plan.agent_ids,
|
|
134
|
+
data_path=plan.dataset_path,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def recommended_eval_runner(directory: Path) -> str:
|
|
139
|
+
"""Return the safest evaluation runner for generated CI/CD workflows."""
|
|
140
|
+
|
|
141
|
+
support = analyze_official_eval_support(directory / "agentops.yaml")
|
|
142
|
+
return AGENTOPS_CLOUD_RUNNER if support.eligible else AGENTOPS_LOCAL_RUNNER
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def prepare_official_eval(
|
|
146
|
+
config_path: Path,
|
|
147
|
+
output_path: Path,
|
|
148
|
+
*,
|
|
149
|
+
deployment_name: str | None = None,
|
|
150
|
+
) -> OfficialEvalPreparation:
|
|
151
|
+
"""Convert AgentOps JSONL config into Microsoft Foundry AI Agent Evaluation JSON."""
|
|
152
|
+
|
|
153
|
+
plan = _build_plan(config_path)
|
|
154
|
+
deployment = _resolve_deployment_name(deployment_name)
|
|
155
|
+
payload = _build_payload(plan)
|
|
156
|
+
|
|
157
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
output_path.write_text(
|
|
159
|
+
json.dumps(payload, indent=2, ensure_ascii=False) + "\n",
|
|
160
|
+
encoding="utf-8",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
metadata_path = output_path.with_name("metadata.json")
|
|
164
|
+
metadata = {
|
|
165
|
+
"runner": OFFICIAL_EVAL_RUNNER,
|
|
166
|
+
"action": official_eval_action_ref(),
|
|
167
|
+
"azure_devops_task": official_eval_ado_task_ref(),
|
|
168
|
+
"agent_ids": plan.agent_ids,
|
|
169
|
+
"deployment_name": deployment,
|
|
170
|
+
"data_path": str(output_path),
|
|
171
|
+
"items_total": len(payload.get("data", [])),
|
|
172
|
+
"official_evaluators": list(plan.official_evaluators),
|
|
173
|
+
"skipped_agentops_evaluators": list(plan.skipped_agentops_evaluators),
|
|
174
|
+
"machine_readable_thresholds": False,
|
|
175
|
+
"warnings": list(plan.warnings),
|
|
176
|
+
}
|
|
177
|
+
metadata_path.write_text(
|
|
178
|
+
json.dumps(metadata, indent=2, ensure_ascii=False) + "\n",
|
|
179
|
+
encoding="utf-8",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return OfficialEvalPreparation(
|
|
183
|
+
data_path=output_path,
|
|
184
|
+
metadata_path=metadata_path,
|
|
185
|
+
agent_ids=plan.agent_ids,
|
|
186
|
+
deployment_name=deployment,
|
|
187
|
+
official_evaluators=plan.official_evaluators,
|
|
188
|
+
warnings=plan.warnings,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _build_plan(config_path: Path) -> _EvalPlan:
|
|
193
|
+
config_path = config_path.resolve()
|
|
194
|
+
if not config_path.exists():
|
|
195
|
+
raise OfficialEvalUnsupported(
|
|
196
|
+
"agentops.yaml was not found; use AgentOps local eval until a release "
|
|
197
|
+
"config exists."
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
config = load_agentops_config(config_path)
|
|
201
|
+
target = classify_agent(config.agent, config.protocol)
|
|
202
|
+
if target.kind != "foundry_prompt":
|
|
203
|
+
raise OfficialEvalUnsupported(
|
|
204
|
+
"Microsoft Foundry AI Agent Evaluation only evaluates Foundry prompt agents "
|
|
205
|
+
"(`agent: name:version`); use AgentOps local eval for hosted endpoints, "
|
|
206
|
+
"HTTP agents, and model targets."
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
dataset_path = _resolve_dataset_path(config_path, config.dataset)
|
|
210
|
+
shape = detect_dataset_shape(dataset_path)
|
|
211
|
+
overrides = [item.name for item in config.evaluators or []] or None
|
|
212
|
+
presets = select_evaluators(
|
|
213
|
+
target,
|
|
214
|
+
shape,
|
|
215
|
+
overrides=overrides,
|
|
216
|
+
threshold_metrics=config.thresholds.keys(),
|
|
217
|
+
)
|
|
218
|
+
official_evaluators, skipped, warnings = _map_evaluators(presets)
|
|
219
|
+
if not official_evaluators:
|
|
220
|
+
raise OfficialEvalUnsupported(
|
|
221
|
+
"no AgentOps evaluators could be mapped to Microsoft Foundry evaluators."
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
_validate_dataset_for_official_runner(dataset_path, official_evaluators)
|
|
225
|
+
|
|
226
|
+
return _EvalPlan(
|
|
227
|
+
config=config,
|
|
228
|
+
config_path=config_path,
|
|
229
|
+
dataset_path=dataset_path,
|
|
230
|
+
agent_ids=config.agent,
|
|
231
|
+
official_evaluators=tuple(official_evaluators),
|
|
232
|
+
skipped_agentops_evaluators=tuple(skipped),
|
|
233
|
+
warnings=tuple(warnings),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _map_evaluators(
|
|
238
|
+
presets: Sequence[EvaluatorPreset],
|
|
239
|
+
) -> tuple[list[str], list[str], list[str]]:
|
|
240
|
+
official: list[str] = []
|
|
241
|
+
skipped: list[str] = []
|
|
242
|
+
warnings: list[str] = []
|
|
243
|
+
|
|
244
|
+
for preset in presets:
|
|
245
|
+
if preset.name == _LATENCY_PRESET:
|
|
246
|
+
skipped.append(preset.name)
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
official_name = _OFFICIAL_EVALUATORS.get(preset.name)
|
|
250
|
+
if official_name is None:
|
|
251
|
+
raise OfficialEvalUnsupported(
|
|
252
|
+
f"AgentOps evaluator {preset.name!r} has no official Foundry "
|
|
253
|
+
"mapping yet."
|
|
254
|
+
)
|
|
255
|
+
if official_name not in official:
|
|
256
|
+
official.append(official_name)
|
|
257
|
+
|
|
258
|
+
return official, skipped, warnings
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _build_payload(plan: _EvalPlan) -> dict[str, Any]:
|
|
262
|
+
rows = list(_convert_rows(plan.dataset_path))
|
|
263
|
+
payload: dict[str, Any] = {
|
|
264
|
+
"name": _dataset_name(plan.config_path, plan.dataset_path),
|
|
265
|
+
"evaluators": list(plan.official_evaluators),
|
|
266
|
+
"data": rows,
|
|
267
|
+
}
|
|
268
|
+
if "builtin.text_similarity" in plan.official_evaluators:
|
|
269
|
+
payload["openai_graders"] = {
|
|
270
|
+
"builtin.text_similarity": {
|
|
271
|
+
"evaluation_metric": "fuzzy_match",
|
|
272
|
+
"input": "{{sample.output_text}}",
|
|
273
|
+
"reference": "{{item.ground_truth}}",
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return payload
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _convert_rows(dataset_path: Path) -> Iterable[dict[str, Any]]:
|
|
280
|
+
with dataset_path.open("r", encoding="utf-8") as handle:
|
|
281
|
+
for line_number, line in enumerate(handle, start=1):
|
|
282
|
+
stripped = line.strip()
|
|
283
|
+
if not stripped:
|
|
284
|
+
continue
|
|
285
|
+
try:
|
|
286
|
+
row = json.loads(stripped)
|
|
287
|
+
except json.JSONDecodeError as exc:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
f"{dataset_path}: invalid JSON on line {line_number}: {exc}"
|
|
290
|
+
) from exc
|
|
291
|
+
if not isinstance(row, dict):
|
|
292
|
+
raise ValueError(f"{dataset_path}: line {line_number} is not a JSON object")
|
|
293
|
+
|
|
294
|
+
query = row.get("query", row.get("input"))
|
|
295
|
+
if not isinstance(query, str) or not query.strip():
|
|
296
|
+
raise ValueError(
|
|
297
|
+
f"{dataset_path}: line {line_number} must include a non-empty "
|
|
298
|
+
"`input` or `query` string."
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
converted = dict(row)
|
|
302
|
+
converted["query"] = query
|
|
303
|
+
if "ground_truth" not in converted and row.get("expected") is not None:
|
|
304
|
+
converted["ground_truth"] = row["expected"]
|
|
305
|
+
yield converted
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _validate_dataset_for_official_runner(
|
|
309
|
+
dataset_path: Path,
|
|
310
|
+
official_evaluators: Sequence[str],
|
|
311
|
+
) -> None:
|
|
312
|
+
needs_ground_truth = any(
|
|
313
|
+
evaluator in _GROUND_TRUTH_EVALUATORS for evaluator in official_evaluators
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
for line_number, row in enumerate(_convert_rows(dataset_path), start=1):
|
|
317
|
+
if needs_ground_truth and not row.get("ground_truth"):
|
|
318
|
+
raise OfficialEvalUnsupported(
|
|
319
|
+
f"{dataset_path}: line {line_number} needs `expected` or "
|
|
320
|
+
"`ground_truth` for the selected Microsoft Foundry evaluators."
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _resolve_dataset_path(config_path: Path, dataset: Path) -> Path:
|
|
325
|
+
dataset_path = dataset
|
|
326
|
+
if not dataset_path.is_absolute():
|
|
327
|
+
dataset_path = config_path.parent / dataset_path
|
|
328
|
+
return dataset_path.resolve()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _resolve_deployment_name(value: str | None) -> str:
|
|
332
|
+
deployment = (
|
|
333
|
+
value
|
|
334
|
+
or os.getenv("AZURE_OPENAI_DEPLOYMENT")
|
|
335
|
+
or os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME")
|
|
336
|
+
or ""
|
|
337
|
+
).strip()
|
|
338
|
+
if not deployment:
|
|
339
|
+
raise ValueError(
|
|
340
|
+
"deployment name is required. Pass --deployment-name or set "
|
|
341
|
+
"AZURE_OPENAI_DEPLOYMENT."
|
|
342
|
+
)
|
|
343
|
+
return deployment
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _dataset_name(config_path: Path, dataset_path: Path) -> str:
|
|
347
|
+
parts = [
|
|
348
|
+
config_path.parent.name or "workspace",
|
|
349
|
+
dataset_path.stem or "dataset",
|
|
350
|
+
]
|
|
351
|
+
raw = "-".join(parts)
|
|
352
|
+
safe = "".join(ch.lower() if ch.isalnum() else "-" for ch in raw)
|
|
353
|
+
return "-".join(part for part in safe.split("-") if part) or "agentops-eval"
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _append_github_output(path: Path, outputs: Mapping[str, str]) -> None:
|
|
357
|
+
with path.open("a", encoding="utf-8") as handle:
|
|
358
|
+
for key, value in outputs.items():
|
|
359
|
+
handle.write(f"{key}={value}\n")
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _emit_ado_output(outputs: Mapping[str, str]) -> None:
|
|
363
|
+
variables = {
|
|
364
|
+
"data_path": "officialDataPath",
|
|
365
|
+
"agent_ids": "officialAgentIds",
|
|
366
|
+
"deployment_name": "officialDeploymentName",
|
|
367
|
+
"metadata_path": "officialMetadataPath",
|
|
368
|
+
}
|
|
369
|
+
for key, variable_name in variables.items():
|
|
370
|
+
print(f"##vso[task.setvariable variable={variable_name};isOutput=true]{outputs[key]}")
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _command_prepare(args: argparse.Namespace) -> int:
|
|
374
|
+
prepared = prepare_official_eval(
|
|
375
|
+
Path(args.config),
|
|
376
|
+
Path(args.out),
|
|
377
|
+
deployment_name=args.deployment_name,
|
|
378
|
+
)
|
|
379
|
+
outputs = {
|
|
380
|
+
"data_path": str(prepared.data_path),
|
|
381
|
+
"agent_ids": prepared.agent_ids,
|
|
382
|
+
"deployment_name": prepared.deployment_name,
|
|
383
|
+
"metadata_path": str(prepared.metadata_path),
|
|
384
|
+
}
|
|
385
|
+
if args.github_output:
|
|
386
|
+
_append_github_output(Path(args.github_output), outputs)
|
|
387
|
+
if args.ado_output:
|
|
388
|
+
_emit_ado_output(outputs)
|
|
389
|
+
if args.print_json:
|
|
390
|
+
print(json.dumps(outputs, indent=2))
|
|
391
|
+
return 0
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
395
|
+
parser = argparse.ArgumentParser(
|
|
396
|
+
description="Prepare official Microsoft Foundry AI Agent Evaluation inputs."
|
|
397
|
+
)
|
|
398
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
399
|
+
|
|
400
|
+
prepare_parser = subparsers.add_parser("prepare")
|
|
401
|
+
prepare_parser.add_argument("--config", default="agentops.yaml")
|
|
402
|
+
prepare_parser.add_argument("--out", required=True)
|
|
403
|
+
prepare_parser.add_argument("--deployment-name")
|
|
404
|
+
prepare_parser.add_argument("--github-output")
|
|
405
|
+
prepare_parser.add_argument("--ado-output", action="store_true")
|
|
406
|
+
prepare_parser.add_argument("--print-json", action="store_true")
|
|
407
|
+
prepare_parser.set_defaults(func=_command_prepare)
|
|
408
|
+
|
|
409
|
+
args = parser.parse_args(argv)
|
|
410
|
+
return int(args.func(args))
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
if __name__ == "__main__": # pragma: no cover
|
|
414
|
+
raise SystemExit(main())
|