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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "agentops-accelerator",
"source": "../../plugins/agentops",
"description": "Copilot agent skills for running standardized evaluation workflows with AgentOps Toolkit and Microsoft Foundry agents.",
"version": "0.3.23",
"version": "0.4.1",
"keywords": [
"agentops",
"evaluation",
Expand Down
2 changes: 1 addition & 1 deletion .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "agentops-accelerator",
"source": "../../plugins/agentops",
"description": "Copilot agent skills for running standardized evaluation workflows with AgentOps Toolkit and Microsoft Foundry agents.",
"version": "0.3.23",
"version": "0.4.1",
"keywords": [
"agentops",
"evaluation",
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ This format follows [Keep a Changelog](https://keepachangelog.com/) and adheres

## [Unreleased]

## [0.4.1] - 2026-06-15

### Changed
- **PR-stage Foundry prompt-agent versions are now tagged at the source.** When
`agentops.pipeline.prompt_deploy stage` runs in a PR context (GitHub Actions
`pull_request` event or Azure DevOps `BUILD_REASON=PullRequest`), the version
it creates in the dev Foundry project carries metadata
`agentops:candidate=true`, `agentops:pr=<number>`, and
`agentops:created_at=<ISO timestamp>`. Portal viewers can filter the
Versions tab on `agentops:candidate` to separate abandoned PR candidates
from deployed-of-record versions, and downstream consumers that resolve
"latest" can refuse to pick up candidates. Deployed-of-record versions
(push to `main`/`develop`/`release/**` or `workflow_dispatch`) are not
tagged, so absence of `agentops:candidate` is the deployed-of-record
signal. The PR/deploy workflow templates and the prompt-agent quickstart
tutorial are updated to describe the new contract.
([#214](https://github.com/Azure/agentops/issues/214))

## [0.4.0] - 2026-06-14

### Added
Expand Down
17 changes: 12 additions & 5 deletions docs/tutorial-prompt-agent-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -1370,11 +1370,18 @@ The PR workflow now has two jobs:

> **Why does the PR workflow stage in dev, not sandbox?** The PR gate
> must evaluate the same target the deploy workflow will use. Sandbox
> is the author's playground and never receives CI traffic. PR
> candidates accumulate in dev over time and may need periodic
> cleanup according to your team's Foundry retention policy; AgentOps
> uses prompt SHAs and git SHAs as the durable identity, not old
> candidate version numbers.
> is the author's playground and never receives CI traffic.
>
> Candidate versions created by PR runs are tagged in Foundry with
> `agentops:candidate=true` plus `agentops:pr=<number>` and
> `agentops:created_at=<ISO timestamp>`. Portal viewers can filter the
> Versions tab on `agentops:candidate` to separate "abandoned PR
> candidates" from "deployed versions of record". Downstream consumers
> that resolve `<agent>` to "latest" should skip versions carrying
> `agentops:candidate=true`; the supported pinning mechanism remains
> `foundry-agent.json`, which always points at the deployed-of-record
> version. AgentOps uses prompt SHAs and git SHAs as the durable
> identity, not old candidate version numbers.

The dev deploy workflow stages a candidate (same logic), evaluates it,
summarizes the deployment via `prompt_deploy summarize`, and uploads
Expand Down
2 changes: 1 addition & 1 deletion plugins/agentops/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "agentops-accelerator",
"displayName": "AgentOps Accelerator — Skills for GitHub Copilot",
"description": "Copilot agent skills for running standardized evaluation workflows with AgentOps Accelerator and Microsoft Foundry agents.",
"version": "0.3.23",
"version": "0.4.1",
"publisher": "AgentOpsAccelerator",
"icon": "icon.png",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion plugins/agentops/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "agentops-accelerator",
"description": "Copilot agent skills for running standardized evaluation workflows with AgentOps Accelerator and Microsoft Foundry agents.",
"version": "0.3.23",
"version": "0.4.1",
"author": {
"name": "AgentOps Accelerator",
"url": "https://github.com/Azure/agentops"
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ foundry = [
"azure-ai-evaluation>=1.0,<2.0",
"azure-identity>=1.17,<2.0",
"azure-monitor-opentelemetry>=1.6,<2.0",
"pandas>=2.0,<3.0",
"pandas>=2.0,<4.0",
]
agent = [
"fastapi>=0.110,<1.0",
"uvicorn[standard]>=0.30,<1.0",
"httpx>=0.27,<1.0",
"cryptography>=42",
"markdown>=3.6,<4.0",
"azure-monitor-query>=1.3,<2.0",
"azure-monitor-query>=1.3,<3.0",
"azure-monitor-opentelemetry>=1.6,<2.0",
"azure-identity>=1.17,<2.0",
"azure-mgmt-cognitiveservices>=13.5,<14.0",
"azure-mgmt-cognitiveservices>=13.5,<15.0",
"azure-mgmt-monitor>=6.0,<7.0",
"azure-mgmt-authorization>=4.0,<5.0",
]
Expand Down
50 changes: 50 additions & 0 deletions src/agentops/pipeline/prompt_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,59 @@ def _deployment_metadata(*, environment: str, prompt_hash: str) -> Dict[str, str
workflow_url = _workflow_url()
if workflow_url:
metadata["agentops.workflow_url"] = workflow_url[:512]

# When the staging step is invoked from a PR-stage workflow, mark the
# version as a candidate so portal viewers can filter it out and naive
# consumers that resolve "latest" can refuse to pick it up. See issue
# #214 for the full rationale.
pr_number = _detect_pr_stage()
if pr_number is not None:
metadata["agentops:candidate"] = "true"
if pr_number:
metadata["agentops:pr"] = pr_number[:512]
metadata["agentops:created_at"] = datetime.now(timezone.utc).isoformat()

return {key: value for key, value in metadata.items() if value}


def _detect_pr_stage() -> Optional[str]:
"""Return the PR number string when running in a PR-stage context.

Returns:
- A PR number (e.g. ``"42"``) when both the PR context and number are
identifiable.
- An empty string when the PR context is detected but the number cannot
be parsed (the version is still flagged as a candidate).
- ``None`` when no PR context is detected (deployed-of-record path).

Detection covers the two CI platforms AgentOps generates workflows for:
GitHub Actions (``GITHUB_EVENT_NAME == 'pull_request'``) and Azure
DevOps (``BUILD_REASON == 'PullRequest'``).
"""

if os.environ.get("GITHUB_EVENT_NAME") == "pull_request":
ref = os.environ.get("GITHUB_REF", "")
# ``refs/pull/<N>/merge`` or ``refs/pull/<N>/head``.
if ref.startswith("refs/pull/"):
parts = ref.split("/")
if len(parts) >= 3 and parts[2].isdigit():
return parts[2]
ref_name = os.environ.get("GITHUB_REF_NAME", "")
# GITHUB_REF_NAME for PRs is shaped like ``<N>/merge``.
head = ref_name.split("/", 1)[0] if ref_name else ""
if head.isdigit():
return head
return ""

if os.environ.get("BUILD_REASON") == "PullRequest":
return (
os.environ.get("SYSTEM_PULLREQUEST_PULLREQUESTNUMBER")
or ""
)

return None


def _git_sha() -> str:
return (
os.environ.get("GITHUB_SHA")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
# Notes:
# - Each PR run creates or reuses a candidate version in the dev
# Foundry project. AgentOps deduplicates only when the prompt is
# byte-identical to the current seed version's instructions; PR
# candidates can therefore accumulate over time and may need to be
# cleaned up out-of-band.
# byte-identical to the current seed version's instructions.
# Candidate versions are tagged with `agentops:candidate=true` and
# `agentops:pr=<number>` so portal viewers can filter them out and
# consumers that resolve "latest" can refuse to pick them up.
# - Merge is what promotes the candidate via the deploy pipeline.
# This PR pipeline does not record the candidate as deployed.
#
Expand Down
7 changes: 4 additions & 3 deletions src/agentops/templates/workflows/agentops-pr-prompt-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
# Notes:
# - Each PR run creates or reuses a candidate version in the dev
# Foundry project. AgentOps deduplicates only when the prompt is
# byte-identical to the current seed version's instructions; PR
# candidates can therefore accumulate over time and may need to be
# cleaned up out-of-band.
# byte-identical to the current seed version's instructions.
# Candidate versions are tagged with `agentops:candidate=true` and
# `agentops:pr=<number>` so portal viewers can filter them out and
# consumers that resolve "latest" can refuse to pick them up.
# - Merge is what promotes the candidate via the deploy workflow.
# This PR workflow does not record the candidate as deployed.
#
Expand Down
Loading
Loading