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,720 @@
|
|
|
1
|
+
"""CI/CD workflow generation service for `agentops workflow generate`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from importlib.resources import files
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, List, Mapping, Sequence, Tuple
|
|
9
|
+
|
|
10
|
+
from agentops.pipeline.official_eval import (
|
|
11
|
+
AGENTOPS_CLOUD_RUNNER,
|
|
12
|
+
AGENTOPS_LOCAL_RUNNER,
|
|
13
|
+
OFFICIAL_EVAL_ACTION_ENV,
|
|
14
|
+
OFFICIAL_EVAL_ADO_TASK_ENV,
|
|
15
|
+
OFFICIAL_EVAL_RUNNER,
|
|
16
|
+
official_eval_action_ref,
|
|
17
|
+
official_eval_ado_task_ref,
|
|
18
|
+
)
|
|
19
|
+
from agentops.services.workflow_analysis import (
|
|
20
|
+
has_ailz_preflight,
|
|
21
|
+
recommended_deploy_mode,
|
|
22
|
+
recommended_eval_runner,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_TEMPLATE_PACKAGE = "agentops.templates"
|
|
27
|
+
_CLOUD_EVAL_CONFIG_NAME = ".agentops.cloud.yaml"
|
|
28
|
+
_CI_EVAL_OUTPUT = ".agentops/results/latest"
|
|
29
|
+
|
|
30
|
+
# CI/CD platforms supported by ``agentops workflow generate``.
|
|
31
|
+
PLATFORMS: Tuple[str, ...] = ("github", "azure-devops")
|
|
32
|
+
|
|
33
|
+
# Deployment template modes. ``placeholder`` keeps the stack-agnostic
|
|
34
|
+
# scaffold; ``azd`` delegates infrastructure and app deployment to Azure
|
|
35
|
+
# Developer CLI; ``prompt-agent`` creates/evaluates a candidate Foundry prompt
|
|
36
|
+
# agent version from a source-controlled prompt file. ``auto`` selects
|
|
37
|
+
# ``azd`` when the target repo has ``azure.yaml`` and ``prompt-agent`` when
|
|
38
|
+
# ``agentops.yaml`` targets a Foundry prompt agent.
|
|
39
|
+
DEPLOY_MODES: Tuple[str, ...] = ("auto", "placeholder", "azd", "prompt-agent")
|
|
40
|
+
|
|
41
|
+
# Doctor gate severities supported by ``agentops workflow generate
|
|
42
|
+
# --doctor-gate``. The PR workflow template runs `agentops doctor
|
|
43
|
+
# --severity-fail <gate>`; ``critical`` (the default) blocks the PR on
|
|
44
|
+
# critical Doctor findings (including regression detection), ``warning``
|
|
45
|
+
# blocks on warning or higher, and ``none`` keeps Doctor advisory.
|
|
46
|
+
DOCTOR_GATES: Tuple[str, ...] = ("critical", "warning", "none")
|
|
47
|
+
DEFAULT_DOCTOR_GATE: str = "critical"
|
|
48
|
+
|
|
49
|
+
# Per-platform mapping of workflow kind -> (template path inside package,
|
|
50
|
+
# output path in repo).
|
|
51
|
+
#
|
|
52
|
+
# The default templates form a complete GenAIOps GitFlow scaffold:
|
|
53
|
+
#
|
|
54
|
+
# pr -> agentops-pr (PR gate; PRs to develop, release/**, main)
|
|
55
|
+
# dev -> agentops-deploy-dev (push to develop -> environment: dev)
|
|
56
|
+
# qa -> agentops-deploy-qa (push to release/** -> environment: qa)
|
|
57
|
+
# prod -> agentops-deploy-prod (push to main -> environment: production)
|
|
58
|
+
#
|
|
59
|
+
# A scheduled Doctor workflow is also available as an explicit optional kind:
|
|
60
|
+
#
|
|
61
|
+
# doctor -> agentops-doctor (scheduled Doctor + eval health check)
|
|
62
|
+
_TEMPLATES_BY_PLATFORM: Dict[str, Dict[str, Tuple[str, str]]] = {
|
|
63
|
+
"github": {
|
|
64
|
+
"pr": ("workflows/agentops-pr.yml", ".github/workflows/agentops-pr.yml"),
|
|
65
|
+
"dev": ("workflows/agentops-deploy-dev.yml", ".github/workflows/agentops-deploy-dev.yml"),
|
|
66
|
+
"qa": ("workflows/agentops-deploy-qa.yml", ".github/workflows/agentops-deploy-qa.yml"),
|
|
67
|
+
"prod": ("workflows/agentops-deploy-prod.yml", ".github/workflows/agentops-deploy-prod.yml"),
|
|
68
|
+
"doctor": ("workflows/agentops-watchdog.yml", ".github/workflows/agentops-doctor.yml"),
|
|
69
|
+
},
|
|
70
|
+
"azure-devops": {
|
|
71
|
+
"pr": (
|
|
72
|
+
"pipelines/azuredevops/agentops-pr.yml",
|
|
73
|
+
".azuredevops/pipelines/agentops-pr.yml",
|
|
74
|
+
),
|
|
75
|
+
"dev": (
|
|
76
|
+
"pipelines/azuredevops/agentops-deploy-dev.yml",
|
|
77
|
+
".azuredevops/pipelines/agentops-deploy-dev.yml",
|
|
78
|
+
),
|
|
79
|
+
"qa": (
|
|
80
|
+
"pipelines/azuredevops/agentops-deploy-qa.yml",
|
|
81
|
+
".azuredevops/pipelines/agentops-deploy-qa.yml",
|
|
82
|
+
),
|
|
83
|
+
"prod": (
|
|
84
|
+
"pipelines/azuredevops/agentops-deploy-prod.yml",
|
|
85
|
+
".azuredevops/pipelines/agentops-deploy-prod.yml",
|
|
86
|
+
),
|
|
87
|
+
"doctor": (
|
|
88
|
+
"pipelines/azuredevops/agentops-watchdog.yml",
|
|
89
|
+
".azuredevops/pipelines/agentops-doctor.yml",
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_AZD_TEMPLATES_BY_PLATFORM: Dict[str, Dict[str, Tuple[str, str]]] = {
|
|
95
|
+
"github": {
|
|
96
|
+
"dev": ("workflows/agentops-deploy-dev-azd.yml", ".github/workflows/agentops-deploy-dev.yml"),
|
|
97
|
+
"qa": ("workflows/agentops-deploy-qa-azd.yml", ".github/workflows/agentops-deploy-qa.yml"),
|
|
98
|
+
"prod": ("workflows/agentops-deploy-prod-azd.yml", ".github/workflows/agentops-deploy-prod.yml"),
|
|
99
|
+
},
|
|
100
|
+
"azure-devops": {
|
|
101
|
+
"dev": (
|
|
102
|
+
"pipelines/azuredevops/agentops-deploy-dev-azd.yml",
|
|
103
|
+
".azuredevops/pipelines/agentops-deploy-dev.yml",
|
|
104
|
+
),
|
|
105
|
+
"qa": (
|
|
106
|
+
"pipelines/azuredevops/agentops-deploy-qa-azd.yml",
|
|
107
|
+
".azuredevops/pipelines/agentops-deploy-qa.yml",
|
|
108
|
+
),
|
|
109
|
+
"prod": (
|
|
110
|
+
"pipelines/azuredevops/agentops-deploy-prod-azd.yml",
|
|
111
|
+
".azuredevops/pipelines/agentops-deploy-prod.yml",
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
ALL_KINDS: tuple[str, ...] = ("pr", "dev", "qa", "prod", "doctor")
|
|
117
|
+
DEFAULT_KINDS: tuple[str, ...] = ("pr", "dev", "qa", "prod")
|
|
118
|
+
LEGACY_KIND_ALIASES: Mapping[str, str] = {"watchdog": "doctor"}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass
|
|
122
|
+
class CicdResult:
|
|
123
|
+
"""Result of generating CI/CD workflow files."""
|
|
124
|
+
|
|
125
|
+
platform: str = "github"
|
|
126
|
+
deploy_mode: str = "placeholder"
|
|
127
|
+
eval_runner: str = AGENTOPS_LOCAL_RUNNER
|
|
128
|
+
doctor_gate: str = "critical"
|
|
129
|
+
kinds: List[str] = field(default_factory=list)
|
|
130
|
+
created_files: List[Path] = field(default_factory=list)
|
|
131
|
+
overwritten_files: List[Path] = field(default_factory=list)
|
|
132
|
+
skipped_files: List[Path] = field(default_factory=list)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _write_template(
|
|
136
|
+
templates_root,
|
|
137
|
+
template_path: str,
|
|
138
|
+
output_path: Path,
|
|
139
|
+
force: bool,
|
|
140
|
+
result: CicdResult,
|
|
141
|
+
substitutions: Mapping[str, str] | None = None,
|
|
142
|
+
) -> None:
|
|
143
|
+
template_resource = templates_root.joinpath(template_path)
|
|
144
|
+
template_content = template_resource.read_text(encoding="utf-8")
|
|
145
|
+
for key, value in (substitutions or {}).items():
|
|
146
|
+
template_content = template_content.replace(key, value)
|
|
147
|
+
|
|
148
|
+
existed_before = output_path.exists()
|
|
149
|
+
|
|
150
|
+
if existed_before and not force:
|
|
151
|
+
result.skipped_files.append(output_path)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
output_path.write_text(template_content, encoding="utf-8")
|
|
156
|
+
|
|
157
|
+
if existed_before:
|
|
158
|
+
result.overwritten_files.append(output_path)
|
|
159
|
+
else:
|
|
160
|
+
result.created_files.append(output_path)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _branch_block_github(*branches: str) -> str:
|
|
164
|
+
return "".join(f" - {branch}\n" for branch in branches).rstrip()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _branch_block_ado(*branches: str) -> str:
|
|
168
|
+
return "".join(f" - {branch}\n" for branch in branches).rstrip()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
_PROMPT_AGENT_VALUES: Dict[str, Dict[str, str]] = {
|
|
172
|
+
"pr": {
|
|
173
|
+
"__ENV_LABEL__": "PR",
|
|
174
|
+
"__ENV_KEY__": "pr",
|
|
175
|
+
# PR candidates are staged in the dev Foundry project so the
|
|
176
|
+
# gate evaluates the same target the deploy workflow will use.
|
|
177
|
+
# Sandbox is the author's playground only.
|
|
178
|
+
"__ENV_NAME__": "dev",
|
|
179
|
+
"__BRANCHES__": "",
|
|
180
|
+
"__EVAL_JOB_NAME__": "AgentOps eval (PR gate)",
|
|
181
|
+
},
|
|
182
|
+
"dev": {
|
|
183
|
+
"__ENV_LABEL__": "DEV",
|
|
184
|
+
"__ENV_KEY__": "dev",
|
|
185
|
+
"__ENV_NAME__": "dev",
|
|
186
|
+
"__BRANCHES__": _branch_block_github("develop"),
|
|
187
|
+
"__EVAL_JOB_NAME__": "Eval candidate (gate)",
|
|
188
|
+
},
|
|
189
|
+
"qa": {
|
|
190
|
+
"__ENV_LABEL__": "QA",
|
|
191
|
+
"__ENV_KEY__": "qa",
|
|
192
|
+
"__ENV_NAME__": "qa",
|
|
193
|
+
"__BRANCHES__": _branch_block_github('"release/**"'),
|
|
194
|
+
"__EVAL_JOB_NAME__": "Eval candidate (gate)",
|
|
195
|
+
},
|
|
196
|
+
"prod": {
|
|
197
|
+
"__ENV_LABEL__": "PROD",
|
|
198
|
+
"__ENV_KEY__": "prod",
|
|
199
|
+
"__ENV_NAME__": "production",
|
|
200
|
+
"__BRANCHES__": _branch_block_github("main"),
|
|
201
|
+
"__EVAL_JOB_NAME__": "Safety eval candidate (gate)",
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_PROMPT_AGENT_VALUES_ADO: Dict[str, Dict[str, str]] = {
|
|
206
|
+
"pr": {
|
|
207
|
+
"__ENV_LABEL__": "PR",
|
|
208
|
+
"__ENV_KEY__": "pr",
|
|
209
|
+
# PR candidates are staged in the dev Foundry project so the
|
|
210
|
+
# gate evaluates the same target the deploy pipeline will use.
|
|
211
|
+
"__ENV_NAME__": "dev",
|
|
212
|
+
"__BRANCHES__": "",
|
|
213
|
+
},
|
|
214
|
+
"dev": {
|
|
215
|
+
"__ENV_LABEL__": "dev",
|
|
216
|
+
"__ENV_KEY__": "dev",
|
|
217
|
+
"__ENV_NAME__": "dev",
|
|
218
|
+
"__BRANCHES__": _branch_block_ado("develop"),
|
|
219
|
+
},
|
|
220
|
+
"qa": {
|
|
221
|
+
"__ENV_LABEL__": "qa",
|
|
222
|
+
"__ENV_KEY__": "qa",
|
|
223
|
+
"__ENV_NAME__": "qa",
|
|
224
|
+
"__BRANCHES__": _branch_block_ado("release/*"),
|
|
225
|
+
},
|
|
226
|
+
"prod": {
|
|
227
|
+
"__ENV_LABEL__": "production",
|
|
228
|
+
"__ENV_KEY__": "prod",
|
|
229
|
+
"__ENV_NAME__": "production",
|
|
230
|
+
"__BRANCHES__": _branch_block_ado("main"),
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
_PROMPT_AGENT_TEMPLATES_BY_PLATFORM: Dict[str, Dict[str, Tuple[str, str]]] = {
|
|
235
|
+
"github": {
|
|
236
|
+
"pr": (
|
|
237
|
+
"workflows/agentops-pr-prompt-agent.yml",
|
|
238
|
+
".github/workflows/agentops-pr.yml",
|
|
239
|
+
),
|
|
240
|
+
"dev": ("workflows/agentops-deploy-prompt-agent.yml", ".github/workflows/agentops-deploy-dev.yml"),
|
|
241
|
+
"qa": ("workflows/agentops-deploy-prompt-agent.yml", ".github/workflows/agentops-deploy-qa.yml"),
|
|
242
|
+
"prod": ("workflows/agentops-deploy-prompt-agent.yml", ".github/workflows/agentops-deploy-prod.yml"),
|
|
243
|
+
},
|
|
244
|
+
"azure-devops": {
|
|
245
|
+
"pr": (
|
|
246
|
+
"pipelines/azuredevops/agentops-pr-prompt-agent.yml",
|
|
247
|
+
".azuredevops/pipelines/agentops-pr.yml",
|
|
248
|
+
),
|
|
249
|
+
"dev": (
|
|
250
|
+
"pipelines/azuredevops/agentops-deploy-prompt-agent.yml",
|
|
251
|
+
".azuredevops/pipelines/agentops-deploy-dev.yml",
|
|
252
|
+
),
|
|
253
|
+
"qa": (
|
|
254
|
+
"pipelines/azuredevops/agentops-deploy-prompt-agent.yml",
|
|
255
|
+
".azuredevops/pipelines/agentops-deploy-qa.yml",
|
|
256
|
+
),
|
|
257
|
+
"prod": (
|
|
258
|
+
"pipelines/azuredevops/agentops-deploy-prompt-agent.yml",
|
|
259
|
+
".azuredevops/pipelines/agentops-deploy-prod.yml",
|
|
260
|
+
),
|
|
261
|
+
},
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _eval_substitutions(
|
|
266
|
+
platform: str,
|
|
267
|
+
eval_runner: str,
|
|
268
|
+
config_path: str,
|
|
269
|
+
*,
|
|
270
|
+
ado_indent: int = 10,
|
|
271
|
+
) -> Mapping[str, str]:
|
|
272
|
+
if platform == "azure-devops":
|
|
273
|
+
return _ado_eval_substitutions(eval_runner, config_path, base_indent=ado_indent)
|
|
274
|
+
return _github_eval_substitutions(eval_runner, config_path)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _github_eval_substitutions(eval_runner: str, config_path: str) -> Mapping[str, str]:
|
|
278
|
+
if eval_runner == AGENTOPS_CLOUD_RUNNER:
|
|
279
|
+
return {
|
|
280
|
+
"__EVAL_STEPS__": f""" - name: Prepare AgentOps cloud eval config
|
|
281
|
+
env:
|
|
282
|
+
AGENTOPS_SOURCE_CONFIG: "{config_path}"
|
|
283
|
+
run: |
|
|
284
|
+
python - <<'PY'
|
|
285
|
+
import os
|
|
286
|
+
from pathlib import Path
|
|
287
|
+
from agentops.utils.yaml import load_yaml, save_yaml
|
|
288
|
+
|
|
289
|
+
source = Path(os.environ["AGENTOPS_SOURCE_CONFIG"])
|
|
290
|
+
target = source.with_name("{_CLOUD_EVAL_CONFIG_NAME}")
|
|
291
|
+
data = load_yaml(source)
|
|
292
|
+
data["execution"] = "cloud"
|
|
293
|
+
data["publish"] = True
|
|
294
|
+
save_yaml(target, data)
|
|
295
|
+
with Path(os.environ["GITHUB_ENV"]).open("a", encoding="utf-8") as env_file:
|
|
296
|
+
env_file.write(f"AGENTOPS_CI_CONFIG={{target}}\\n")
|
|
297
|
+
print(f"Prepared AgentOps cloud eval config: {{target}}")
|
|
298
|
+
PY
|
|
299
|
+
|
|
300
|
+
- name: Run AgentOps Foundry cloud eval
|
|
301
|
+
id: eval
|
|
302
|
+
env:
|
|
303
|
+
AZURE_AI_FOUNDRY_PROJECT_ENDPOINT: ${{{{ vars.AZURE_AI_FOUNDRY_PROJECT_ENDPOINT }}}}
|
|
304
|
+
AZURE_OPENAI_ENDPOINT: ${{{{ vars.AZURE_OPENAI_ENDPOINT }}}}
|
|
305
|
+
AZURE_OPENAI_DEPLOYMENT: ${{{{ vars.AZURE_OPENAI_DEPLOYMENT }}}}
|
|
306
|
+
APPLICATIONINSIGHTS_CONNECTION_STRING: ${{{{ secrets.APPLICATIONINSIGHTS_CONNECTION_STRING || vars.APPLICATIONINSIGHTS_CONNECTION_STRING }}}}
|
|
307
|
+
run: |
|
|
308
|
+
set +e
|
|
309
|
+
agentops eval run --config "$AGENTOPS_CI_CONFIG" --output "{_CI_EVAL_OUTPUT}"
|
|
310
|
+
ec=$?
|
|
311
|
+
echo "exit_code=$ec" >> "$GITHUB_OUTPUT"
|
|
312
|
+
if [ $ec -eq 0 ]; then
|
|
313
|
+
echo "result=pass" >> "$GITHUB_OUTPUT"
|
|
314
|
+
elif [ $ec -eq 2 ]; then
|
|
315
|
+
echo "result=threshold_failed" >> "$GITHUB_OUTPUT"
|
|
316
|
+
else
|
|
317
|
+
echo "result=error" >> "$GITHUB_OUTPUT"
|
|
318
|
+
fi
|
|
319
|
+
exit $ec""",
|
|
320
|
+
"__EVAL_ARTIFACT_PATHS__": f"""{_CI_EVAL_OUTPUT}/results.json
|
|
321
|
+
{_CI_EVAL_OUTPUT}/report.md
|
|
322
|
+
{_CI_EVAL_OUTPUT}/cloud_evaluation.json""",
|
|
323
|
+
}
|
|
324
|
+
if eval_runner == OFFICIAL_EVAL_RUNNER:
|
|
325
|
+
official_action = official_eval_action_ref()
|
|
326
|
+
return {
|
|
327
|
+
"__EVAL_STEPS__": f""" - name: Prepare official AI Agent Evaluation input
|
|
328
|
+
id: official_eval_input
|
|
329
|
+
env:
|
|
330
|
+
AZURE_OPENAI_DEPLOYMENT: ${{{{ vars.AZURE_OPENAI_DEPLOYMENT }}}}
|
|
331
|
+
{OFFICIAL_EVAL_ACTION_ENV}: {official_action}
|
|
332
|
+
run: |
|
|
333
|
+
python -m agentops.pipeline.official_eval prepare \\
|
|
334
|
+
--config \"{config_path}\" \\
|
|
335
|
+
--out \".agentops/official-eval/input.json\" \\
|
|
336
|
+
--github-output \"$GITHUB_OUTPUT\"
|
|
337
|
+
|
|
338
|
+
# Official runner for Foundry prompt agents. AgentOps keeps the prepared
|
|
339
|
+
# input/metadata as release evidence until the action exposes a stable
|
|
340
|
+
# machine-readable threshold artifact.
|
|
341
|
+
- name: Run official AI Agent Evaluation
|
|
342
|
+
id: official_eval
|
|
343
|
+
uses: {official_action}
|
|
344
|
+
with:
|
|
345
|
+
azure-ai-project-endpoint: ${{{{ vars.AZURE_AI_FOUNDRY_PROJECT_ENDPOINT }}}}
|
|
346
|
+
deployment-name: ${{{{ steps.official_eval_input.outputs.deployment_name }}}}
|
|
347
|
+
agent-ids: ${{{{ steps.official_eval_input.outputs.agent_ids }}}}
|
|
348
|
+
data-path: ${{{{ steps.official_eval_input.outputs.data_path }}}}
|
|
349
|
+
|
|
350
|
+
- name: Record official eval result
|
|
351
|
+
if: always()
|
|
352
|
+
id: eval
|
|
353
|
+
run: |
|
|
354
|
+
mkdir -p .agentops/official-eval
|
|
355
|
+
outcome=\"${{{{ steps.official_eval.outcome }}}}\"
|
|
356
|
+
conclusion=\"${{{{ steps.official_eval.conclusion }}}}\"
|
|
357
|
+
if [ \"$outcome\" = \"success\" ]; then
|
|
358
|
+
echo \"exit_code=0\" >> \"$GITHUB_OUTPUT\"
|
|
359
|
+
echo \"result=official-ai-agent-evaluation\" >> \"$GITHUB_OUTPUT\"
|
|
360
|
+
else
|
|
361
|
+
echo \"exit_code=1\" >> \"$GITHUB_OUTPUT\"
|
|
362
|
+
echo \"result=official-ai-agent-evaluation-failed\" >> \"$GITHUB_OUTPUT\"
|
|
363
|
+
fi
|
|
364
|
+
python - <<'PY'
|
|
365
|
+
import json
|
|
366
|
+
from datetime import datetime, timezone
|
|
367
|
+
from pathlib import Path
|
|
368
|
+
|
|
369
|
+
output = Path('.agentops/official-eval/result.json')
|
|
370
|
+
outcome = \"${{{{ steps.official_eval.outcome }}}}\"
|
|
371
|
+
conclusion = \"${{{{ steps.official_eval.conclusion }}}}\"
|
|
372
|
+
status = 'success' if outcome == 'success' else 'failed' if outcome else 'unknown'
|
|
373
|
+
payload = dict(
|
|
374
|
+
runner='{OFFICIAL_EVAL_RUNNER}',
|
|
375
|
+
system='github-actions',
|
|
376
|
+
action='{official_action}',
|
|
377
|
+
status=status,
|
|
378
|
+
outcome=outcome,
|
|
379
|
+
conclusion=conclusion,
|
|
380
|
+
agent_ids=\"${{{{ steps.official_eval_input.outputs.agent_ids }}}}\",
|
|
381
|
+
deployment_name=\"${{{{ steps.official_eval_input.outputs.deployment_name }}}}\",
|
|
382
|
+
data_path=\"${{{{ steps.official_eval_input.outputs.data_path }}}}\",
|
|
383
|
+
metadata_path=\"${{{{ steps.official_eval_input.outputs.metadata_path }}}}\",
|
|
384
|
+
machine_readable_thresholds=False,
|
|
385
|
+
recorded_at=datetime.now(timezone.utc).isoformat(),
|
|
386
|
+
)
|
|
387
|
+
output.write_text(json.dumps(payload, indent=2) + '\\n', encoding='utf-8')
|
|
388
|
+
PY""",
|
|
389
|
+
"__EVAL_ARTIFACT_PATHS__": """.agentops/official-eval/input.json
|
|
390
|
+
.agentops/official-eval/metadata.json
|
|
391
|
+
.agentops/official-eval/result.json""",
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
"__EVAL_STEPS__": f""" - name: Run AgentOps eval
|
|
395
|
+
id: eval
|
|
396
|
+
env:
|
|
397
|
+
AZURE_AI_FOUNDRY_PROJECT_ENDPOINT: ${{{{ vars.AZURE_AI_FOUNDRY_PROJECT_ENDPOINT }}}}
|
|
398
|
+
AZURE_OPENAI_ENDPOINT: ${{{{ vars.AZURE_OPENAI_ENDPOINT }}}}
|
|
399
|
+
AZURE_OPENAI_DEPLOYMENT: ${{{{ vars.AZURE_OPENAI_DEPLOYMENT }}}}
|
|
400
|
+
APPLICATIONINSIGHTS_CONNECTION_STRING: ${{{{ secrets.APPLICATIONINSIGHTS_CONNECTION_STRING || vars.APPLICATIONINSIGHTS_CONNECTION_STRING }}}}
|
|
401
|
+
run: |
|
|
402
|
+
set +e
|
|
403
|
+
agentops eval run --config \"{config_path}\"
|
|
404
|
+
ec=$?
|
|
405
|
+
echo \"exit_code=$ec\" >> \"$GITHUB_OUTPUT\"
|
|
406
|
+
if [ $ec -eq 0 ]; then
|
|
407
|
+
echo \"result=pass\" >> \"$GITHUB_OUTPUT\"
|
|
408
|
+
elif [ $ec -eq 2 ]; then
|
|
409
|
+
echo \"result=threshold_failed\" >> \"$GITHUB_OUTPUT\"
|
|
410
|
+
else
|
|
411
|
+
echo \"result=error\" >> \"$GITHUB_OUTPUT\"
|
|
412
|
+
fi
|
|
413
|
+
exit $ec""",
|
|
414
|
+
"__EVAL_ARTIFACT_PATHS__": """.agentops/results/latest/results.json
|
|
415
|
+
.agentops/results/latest/report.md
|
|
416
|
+
.agentops/results/latest/cloud_evaluation.json""",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _ado_eval_substitutions(
|
|
421
|
+
eval_runner: str,
|
|
422
|
+
config_path: str,
|
|
423
|
+
*,
|
|
424
|
+
base_indent: int,
|
|
425
|
+
) -> Mapping[str, str]:
|
|
426
|
+
if eval_runner == AGENTOPS_CLOUD_RUNNER:
|
|
427
|
+
return {
|
|
428
|
+
"__EVAL_TASKS__": _indent_block(
|
|
429
|
+
f"""- bash: |
|
|
430
|
+
python - <<'PY'
|
|
431
|
+
import os
|
|
432
|
+
from pathlib import Path
|
|
433
|
+
from agentops.utils.yaml import load_yaml, save_yaml
|
|
434
|
+
|
|
435
|
+
source = Path(os.environ["AGENTOPS_SOURCE_CONFIG"])
|
|
436
|
+
target = source.with_name("{_CLOUD_EVAL_CONFIG_NAME}")
|
|
437
|
+
data = load_yaml(source)
|
|
438
|
+
data["execution"] = "cloud"
|
|
439
|
+
data["publish"] = True
|
|
440
|
+
save_yaml(target, data)
|
|
441
|
+
print(f"##vso[task.setvariable variable=AGENTOPS_CI_CONFIG]{{target}}")
|
|
442
|
+
print(f"Prepared AgentOps cloud eval config: {{target}}")
|
|
443
|
+
PY
|
|
444
|
+
displayName: Prepare AgentOps cloud eval config
|
|
445
|
+
env:
|
|
446
|
+
AGENTOPS_SOURCE_CONFIG: "{config_path}"
|
|
447
|
+
|
|
448
|
+
- task: AzureCLI@2
|
|
449
|
+
displayName: Run AgentOps Foundry cloud eval
|
|
450
|
+
inputs:
|
|
451
|
+
azureSubscription: $(AZURE_SERVICE_CONNECTION)
|
|
452
|
+
scriptType: bash
|
|
453
|
+
scriptLocation: inlineScript
|
|
454
|
+
inlineScript: |
|
|
455
|
+
set +e
|
|
456
|
+
agentops eval run --config "$(AGENTOPS_CI_CONFIG)" --output "{_CI_EVAL_OUTPUT}"
|
|
457
|
+
code=$?
|
|
458
|
+
echo "##vso[task.setvariable variable=AGENTOPS_EVAL_EXIT_CODE]$code"
|
|
459
|
+
exit $code
|
|
460
|
+
env:
|
|
461
|
+
AZURE_AI_FOUNDRY_PROJECT_ENDPOINT: $(AZURE_AI_FOUNDRY_PROJECT_ENDPOINT)
|
|
462
|
+
AZURE_OPENAI_ENDPOINT: $(AZURE_OPENAI_ENDPOINT)
|
|
463
|
+
AZURE_OPENAI_DEPLOYMENT: $(AZURE_OPENAI_DEPLOYMENT)
|
|
464
|
+
APPLICATIONINSIGHTS_CONNECTION_STRING: $(APPLICATIONINSIGHTS_CONNECTION_STRING)""",
|
|
465
|
+
base_indent,
|
|
466
|
+
),
|
|
467
|
+
"__EVAL_ARTIFACT_TARGET__": _CI_EVAL_OUTPUT,
|
|
468
|
+
}
|
|
469
|
+
if eval_runner == OFFICIAL_EVAL_RUNNER:
|
|
470
|
+
official_task = official_eval_ado_task_ref()
|
|
471
|
+
return {
|
|
472
|
+
"__EVAL_TASKS__": _indent_block(
|
|
473
|
+
f"""- task: AzureCLI@2
|
|
474
|
+
name: official_eval_input
|
|
475
|
+
displayName: Prepare official AI Agent Evaluation input
|
|
476
|
+
inputs:
|
|
477
|
+
azureSubscription: $(AZURE_SERVICE_CONNECTION)
|
|
478
|
+
scriptType: bash
|
|
479
|
+
scriptLocation: inlineScript
|
|
480
|
+
inlineScript: |
|
|
481
|
+
python -m agentops.pipeline.official_eval prepare \\
|
|
482
|
+
--config \"{config_path}\" \\
|
|
483
|
+
--out \".agentops/official-eval/input.json\" \\
|
|
484
|
+
--ado-output
|
|
485
|
+
env:
|
|
486
|
+
AZURE_OPENAI_DEPLOYMENT: $(AZURE_OPENAI_DEPLOYMENT)
|
|
487
|
+
{OFFICIAL_EVAL_ADO_TASK_ENV}: {official_task}
|
|
488
|
+
|
|
489
|
+
- task: {official_task}
|
|
490
|
+
displayName: Run official AI Agent Evaluation
|
|
491
|
+
inputs:
|
|
492
|
+
azure-ai-project-endpoint: $(AZURE_AI_FOUNDRY_PROJECT_ENDPOINT)
|
|
493
|
+
deployment-name: $(official_eval_input.officialDeploymentName)
|
|
494
|
+
data-path: $(official_eval_input.officialDataPath)
|
|
495
|
+
agent-ids: $(official_eval_input.officialAgentIds)
|
|
496
|
+
|
|
497
|
+
- bash: |
|
|
498
|
+
mkdir -p .agentops/official-eval
|
|
499
|
+
python - <<'PY'
|
|
500
|
+
import json
|
|
501
|
+
from datetime import datetime, timezone
|
|
502
|
+
from pathlib import Path
|
|
503
|
+
|
|
504
|
+
job_status = "$(Agent.JobStatus)"
|
|
505
|
+
normalized = job_status.strip().lower().replace("_", "").replace("-", "")
|
|
506
|
+
status = "success" if normalized == "succeeded" else "failed" if normalized in ("failed", "canceled", "cancelled") else "unknown"
|
|
507
|
+
payload = dict(
|
|
508
|
+
runner="{OFFICIAL_EVAL_RUNNER}",
|
|
509
|
+
system="azure-devops",
|
|
510
|
+
task="{official_task}",
|
|
511
|
+
status=status,
|
|
512
|
+
job_status=job_status,
|
|
513
|
+
agent_ids="$(official_eval_input.officialAgentIds)",
|
|
514
|
+
deployment_name="$(official_eval_input.officialDeploymentName)",
|
|
515
|
+
data_path="$(official_eval_input.officialDataPath)",
|
|
516
|
+
metadata_path="$(official_eval_input.officialMetadataPath)",
|
|
517
|
+
machine_readable_thresholds=False,
|
|
518
|
+
recorded_at=datetime.now(timezone.utc).isoformat(),
|
|
519
|
+
)
|
|
520
|
+
Path(".agentops/official-eval/result.json").write_text(
|
|
521
|
+
json.dumps(payload, indent=2) + "\\n",
|
|
522
|
+
encoding="utf-8",
|
|
523
|
+
)
|
|
524
|
+
PY
|
|
525
|
+
displayName: Record official eval result
|
|
526
|
+
condition: always()""",
|
|
527
|
+
base_indent,
|
|
528
|
+
),
|
|
529
|
+
"__EVAL_ARTIFACT_TARGET__": ".agentops/official-eval",
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
"__EVAL_TASKS__": _indent_block(
|
|
533
|
+
f"""- task: AzureCLI@2
|
|
534
|
+
displayName: Run AgentOps eval
|
|
535
|
+
inputs:
|
|
536
|
+
azureSubscription: $(AZURE_SERVICE_CONNECTION)
|
|
537
|
+
scriptType: bash
|
|
538
|
+
scriptLocation: inlineScript
|
|
539
|
+
inlineScript: |
|
|
540
|
+
set +e
|
|
541
|
+
agentops eval run --config \"{config_path}\"
|
|
542
|
+
code=$?
|
|
543
|
+
echo \"##vso[task.setvariable variable=AGENTOPS_EVAL_EXIT_CODE]$code\"
|
|
544
|
+
exit $code
|
|
545
|
+
env:
|
|
546
|
+
AZURE_AI_FOUNDRY_PROJECT_ENDPOINT: $(AZURE_AI_FOUNDRY_PROJECT_ENDPOINT)
|
|
547
|
+
AZURE_OPENAI_ENDPOINT: $(AZURE_OPENAI_ENDPOINT)
|
|
548
|
+
AZURE_OPENAI_DEPLOYMENT: $(AZURE_OPENAI_DEPLOYMENT)
|
|
549
|
+
APPLICATIONINSIGHTS_CONNECTION_STRING: $(APPLICATIONINSIGHTS_CONNECTION_STRING)""",
|
|
550
|
+
base_indent,
|
|
551
|
+
),
|
|
552
|
+
"__EVAL_ARTIFACT_TARGET__": ".agentops/results/latest",
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _indent_block(block: str, spaces: int) -> str:
|
|
557
|
+
prefix = " " * spaces
|
|
558
|
+
return "\n".join(prefix + line if line else "" for line in block.splitlines())
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def normalize_workflow_kind(kind: str) -> str:
|
|
562
|
+
"""Return the canonical workflow kind, accepting legacy aliases."""
|
|
563
|
+
return LEGACY_KIND_ALIASES.get(kind, kind)
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def generate_cicd_workflows(
|
|
567
|
+
directory: Path,
|
|
568
|
+
force: bool = False,
|
|
569
|
+
kinds: Sequence[str] | None = None,
|
|
570
|
+
platform: str = "github",
|
|
571
|
+
deploy_mode: str = "auto",
|
|
572
|
+
doctor_gate: str = DEFAULT_DOCTOR_GATE,
|
|
573
|
+
) -> CicdResult:
|
|
574
|
+
"""Generate AgentOps GitFlow CI/CD workflows.
|
|
575
|
+
|
|
576
|
+
By default writes the release-path templates (``pr``, ``dev``, ``qa``,
|
|
577
|
+
``prod``) for the requested *platform*. Pass *kinds* to opt into a subset,
|
|
578
|
+
including the optional scheduled ``doctor`` workflow.
|
|
579
|
+
|
|
580
|
+
Args:
|
|
581
|
+
directory: Root directory of the consumer repository.
|
|
582
|
+
force: When True, overwrite existing workflow files.
|
|
583
|
+
kinds: Optional explicit list of workflow kinds. ``None`` means
|
|
584
|
+
"generate the default release-path templates". Unknown kinds are ignored.
|
|
585
|
+
platform: ``"github"`` (default) writes ``.github/workflows/*.yml``
|
|
586
|
+
using GitHub Actions; ``"azure-devops"`` writes
|
|
587
|
+
``.azuredevops/pipelines/*.yml`` using Azure DevOps Pipelines.
|
|
588
|
+
The conceptual workflows (PR gate + three deploy stages) are
|
|
589
|
+
identical across platforms.
|
|
590
|
+
deploy_mode: ``"placeholder"`` writes the stack-agnostic deploy
|
|
591
|
+
scaffold, ``"azd"`` writes Azure Developer CLI provision/deploy
|
|
592
|
+
workflows, ``"prompt-agent"`` writes Foundry prompt-agent
|
|
593
|
+
candidate/eval/deploy workflows, and ``"auto"`` selects
|
|
594
|
+
``"azd"`` when ``azure.yaml`` exists or ``"prompt-agent"`` when
|
|
595
|
+
``agentops.yaml`` targets a Foundry prompt agent.
|
|
596
|
+
doctor_gate: Severity floor for the PR-gate Doctor step. One of
|
|
597
|
+
``"critical"`` (default), ``"warning"``, or ``"none"``.
|
|
598
|
+
``critical`` blocks the PR on critical Doctor findings such as
|
|
599
|
+
regression drops; ``warning`` blocks on warning or higher;
|
|
600
|
+
``none`` keeps Doctor advisory (pre-1.x behavior). Only the PR
|
|
601
|
+
template uses this value; deploy templates keep the
|
|
602
|
+
hardcoded ``critical`` gate.
|
|
603
|
+
|
|
604
|
+
Returns:
|
|
605
|
+
CicdResult with platform and paths of created, overwritten, or
|
|
606
|
+
skipped files.
|
|
607
|
+
"""
|
|
608
|
+
if platform not in _TEMPLATES_BY_PLATFORM:
|
|
609
|
+
raise ValueError(
|
|
610
|
+
f"unknown platform {platform!r}; valid: {', '.join(PLATFORMS)}"
|
|
611
|
+
)
|
|
612
|
+
if deploy_mode not in DEPLOY_MODES:
|
|
613
|
+
raise ValueError(
|
|
614
|
+
f"unknown deploy mode {deploy_mode!r}; valid: {', '.join(DEPLOY_MODES)}"
|
|
615
|
+
)
|
|
616
|
+
if doctor_gate not in DOCTOR_GATES:
|
|
617
|
+
raise ValueError(
|
|
618
|
+
f"unknown doctor gate {doctor_gate!r}; valid: {', '.join(DOCTOR_GATES)}"
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
if kinds is None:
|
|
622
|
+
kinds = DEFAULT_KINDS
|
|
623
|
+
|
|
624
|
+
directory = directory.resolve()
|
|
625
|
+
effective_deploy_mode = deploy_mode
|
|
626
|
+
if effective_deploy_mode == "auto":
|
|
627
|
+
effective_deploy_mode = recommended_deploy_mode(directory)
|
|
628
|
+
effective_eval_runner = recommended_eval_runner(directory)
|
|
629
|
+
|
|
630
|
+
result = CicdResult(
|
|
631
|
+
platform=platform,
|
|
632
|
+
deploy_mode=effective_deploy_mode,
|
|
633
|
+
eval_runner=effective_eval_runner,
|
|
634
|
+
doctor_gate=doctor_gate,
|
|
635
|
+
)
|
|
636
|
+
templates_root = files(_TEMPLATE_PACKAGE)
|
|
637
|
+
template_map = _TEMPLATES_BY_PLATFORM[platform]
|
|
638
|
+
azd_template_map = _AZD_TEMPLATES_BY_PLATFORM.get(platform, {})
|
|
639
|
+
prompt_agent_template_map = _PROMPT_AGENT_TEMPLATES_BY_PLATFORM.get(platform, {})
|
|
640
|
+
azd_substitutions = _azd_substitutions(platform, has_ailz_preflight(directory))
|
|
641
|
+
|
|
642
|
+
seen: set[str] = set()
|
|
643
|
+
for requested_kind in kinds:
|
|
644
|
+
kind = normalize_workflow_kind(requested_kind)
|
|
645
|
+
if kind in seen or kind not in template_map:
|
|
646
|
+
continue
|
|
647
|
+
seen.add(kind)
|
|
648
|
+
result.kinds.append(kind)
|
|
649
|
+
substitutions: dict[str, str] = {"__DOCTOR_GATE__": doctor_gate}
|
|
650
|
+
eval_config = (
|
|
651
|
+
"${{ inputs.config || 'agentops.yaml' }}"
|
|
652
|
+
if platform == "github" and kind == "pr"
|
|
653
|
+
else "$(AGENTOPS_CONFIG)"
|
|
654
|
+
if platform == "azure-devops"
|
|
655
|
+
else "agentops.yaml"
|
|
656
|
+
)
|
|
657
|
+
if effective_deploy_mode == "azd" and kind in azd_template_map:
|
|
658
|
+
template_path, output_rel = azd_template_map[kind]
|
|
659
|
+
substitutions.update(azd_substitutions)
|
|
660
|
+
elif effective_deploy_mode == "prompt-agent" and kind in prompt_agent_template_map:
|
|
661
|
+
template_path, output_rel = prompt_agent_template_map[kind]
|
|
662
|
+
eval_config = ".agentops/deployments/agentops.candidate.yaml"
|
|
663
|
+
prompt_values = (
|
|
664
|
+
_PROMPT_AGENT_VALUES if platform == "github" else _PROMPT_AGENT_VALUES_ADO
|
|
665
|
+
)[kind]
|
|
666
|
+
substitutions.update(prompt_values)
|
|
667
|
+
else:
|
|
668
|
+
template_path, output_rel = template_map[kind]
|
|
669
|
+
ado_indent = (
|
|
670
|
+
10
|
|
671
|
+
if kind == "pr"
|
|
672
|
+
or (effective_deploy_mode == "azd" and kind in azd_template_map)
|
|
673
|
+
else 16
|
|
674
|
+
)
|
|
675
|
+
substitutions.update(
|
|
676
|
+
_eval_substitutions(
|
|
677
|
+
platform,
|
|
678
|
+
effective_eval_runner,
|
|
679
|
+
eval_config,
|
|
680
|
+
ado_indent=ado_indent,
|
|
681
|
+
)
|
|
682
|
+
)
|
|
683
|
+
output_path = (directory / output_rel).resolve()
|
|
684
|
+
_write_template(
|
|
685
|
+
templates_root,
|
|
686
|
+
template_path,
|
|
687
|
+
output_path,
|
|
688
|
+
force,
|
|
689
|
+
result,
|
|
690
|
+
substitutions=substitutions,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
return result
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def _azd_substitutions(platform: str, ailz_preflight: bool) -> Mapping[str, str]:
|
|
697
|
+
if not ailz_preflight:
|
|
698
|
+
return {"__AILZ_PREFLIGHT_COMMAND__": ""}
|
|
699
|
+
if platform == "azure-devops":
|
|
700
|
+
return {
|
|
701
|
+
"__AILZ_PREFLIGHT_COMMAND__": (
|
|
702
|
+
" echo \"Running AI Landing Zone preflight.\"\n"
|
|
703
|
+
" pwsh ./scripts/Invoke-PreflightChecks.ps1 -Strict"
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
"__AILZ_PREFLIGHT_COMMAND__": (
|
|
708
|
+
" echo \"Running AI Landing Zone preflight.\"\n"
|
|
709
|
+
" pwsh ./scripts/Invoke-PreflightChecks.ps1 -Strict"
|
|
710
|
+
)
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def generate_cicd_workflow(
|
|
715
|
+
directory: Path,
|
|
716
|
+
force: bool = False,
|
|
717
|
+
platform: str = "github",
|
|
718
|
+
) -> CicdResult:
|
|
719
|
+
"""Generate only the PR workflow template (legacy convenience)."""
|
|
720
|
+
return generate_cicd_workflows(directory, force=force, kinds=["pr"], platform=platform)
|