Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
223 commits
Select commit Hold shift + click to select a range
d68e564
feat(azure.ai.evaluations): scaffold the Foundry evaluations extension
m7md7sien Jul 28, 2026
a960726
feat(azure.ai.evaluations): port the eval and dataset data-plane clients
m7md7sien Jul 28, 2026
2a8dbed
feat(azure.ai.evaluations): config model, client context, and dataset…
m7md7sien Jul 28, 2026
125cd7a
feat(azure.ai.evaluations): eval group builder and run command
m7md7sien Jul 28, 2026
035f6bb
feat(azure.ai.evaluations): evaluator and results commands
m7md7sien Jul 28, 2026
4f8a4eb
feat(azure.ai.evaluations): offline init command with round-trip tests
m7md7sien Jul 28, 2026
6b576e3
feat(azure.ai.evaluations): service-target provider with change detec…
m7md7sien Jul 28, 2026
065e84f
feat(azure.ai.evaluations): generate command with YAML write-back
m7md7sien Jul 28, 2026
38fb856
build(azure.ai.evaluations): release pipeline and foundry bundle entry
m7md7sien Jul 28, 2026
3b9cb93
fix(azure.ai.evaluations): bind dataset URIs and add live integration…
m7md7sien Jul 28, 2026
b7aba9d
fix(azure.ai.evaluations): build data mappings from each evaluator's …
m7md7sien Jul 28, 2026
4d26558
build(azure.ai.evaluations): add the build scripts the extension kit …
m7md7sien Jul 28, 2026
f92e69b
fix(azure.ai.evaluations): read the evaluator kind the listing actual…
m7md7sien Jul 28, 2026
5a670ca
fix(azure.ai.evaluations): make azd deploy work end to end
m7md7sien Jul 28, 2026
d21bbd3
fix(azure.ai.evaluations): resolve $ref includes in the service entry
m7md7sien Jul 28, 2026
ae6f43c
fix(azure.ai.evaluations): recreate an eval group when its own declar…
m7md7sien Jul 28, 2026
e71fc44
fix(azure.ai.evaluations): keep HTTP traces out of normal output
m7md7sien Jul 28, 2026
02e9e95
feat(azure.ai.evaluations): explain why agent-seeded data generation …
m7md7sien Jul 28, 2026
a049fa6
feat(azure.ai.evaluations): complete the atomic run operations
m7md7sien Jul 28, 2026
1ccb6d9
feat(azure.ai.evaluations): add run start to complete the atomic surface
m7md7sien Jul 28, 2026
4580224
fix(azure.ai.evaluations): make the dataset and evaluator write paths…
m7md7sien Jul 28, 2026
331d3fe
fix(azure.ai.evaluations): support custom evaluators in a deployed co…
m7md7sien Jul 28, 2026
4d1f827
fix(azure.ai.evaluations): honour --dataset when it names a registere…
m7md7sien Jul 28, 2026
9e2797f
fix(azure.ai.evaluations): survive the eventually consistent version …
m7md7sien Jul 28, 2026
2606b68
build(azure.ai.evaluations): add the CI scripts the release pipeline …
m7md7sien Jul 28, 2026
288927f
fix(azure.ai.evaluations): make the manifest and the binary agree
m7md7sien Jul 28, 2026
909ce14
feat(azure.ai.evaluations): detect artifact drift and read the instru…
m7md7sien Jul 28, 2026
800834e
fix(azure.ai.evaluations): make --eval-id work and catch unregistered…
m7md7sien Jul 28, 2026
d09111e
fix(azure.ai.evaluations): rebase a supplied dataset path onto the de…
m7md7sien Jul 28, 2026
ce2ce3a
feat(azure.ai.evaluations): say when a trace setting is being ignored
m7md7sien Jul 28, 2026
b10f706
fix(azure.ai.evaluations): honour the config fields that were being d…
m7md7sien Jul 28, 2026
2a101c0
build(azure.ai.evaluations): ignore the artifacts the tooling produces
m7md7sien Jul 28, 2026
7f1b067
fix(azure.ai.evaluations): make the persistence warnings useful
m7md7sien Jul 28, 2026
5853089
feat(azure.ai.evaluations): compare runs against a baseline
m7md7sien Jul 28, 2026
63a7a33
fix(azure.ai.evaluations): finish the generate step of the documented…
m7md7sien Jul 28, 2026
b122b0f
fix(azure.ai.evaluations): seed generation from the agent, not from a…
m7md7sien Jul 28, 2026
b9a3078
feat(azure.ai.evaluations): run an eval group on a schedule
m7md7sien Jul 28, 2026
1c42efa
feat(azure.ai.evaluations): evaluate an agent's traces, and say why a…
m7md7sien Jul 28, 2026
03c7026
fix(azure.ai.evaluations): send the trace window in the field the ser…
m7md7sien Jul 28, 2026
7905c97
feat(azure.ai.evaluations): evaluate stored responses by id
m7md7sien Jul 28, 2026
1d195f5
feat(azure.ai.evaluations): evaluate a model deployment directly
m7md7sien Jul 28, 2026
fbf532f
fix(azure.ai.evaluations): run the datasets that are already registered
m7md7sien Jul 28, 2026
09b7e5f
fix(azure.ai.evaluations): stop a second eval group from taking the f…
m7md7sien Jul 28, 2026
d961d3b
feat(azure.ai.evaluations): reach a group by name, and remember its r…
m7md7sien Jul 28, 2026
ca09620
fix(azure.ai.evaluations): publish the dataset version the config dec…
m7md7sien Jul 28, 2026
131f172
fix(azure.ai.evaluations): refuse an evaluator version the service wi…
m7md7sien Jul 28, 2026
1c42eb2
fix(azure.ai.evaluations): read the built-in evaluators' published sc…
m7md7sien Jul 28, 2026
ed62aee
test(azure.ai.evaluations): cover the lookup that supplies evaluator …
m7md7sien Jul 28, 2026
36b51c9
test(azure.ai.evaluations): pin the decisions that fail quietly
m7md7sien Jul 28, 2026
b28dfaa
Honour a hand-set EVAL_GROUP_ID again for single-group configs
m7md7sien Jul 28, 2026
69a6ec8
Emit a JSON array from every list command
m7md7sien Jul 28, 2026
ab06f36
Accept --eval-id on every command that takes an eval group
m7md7sien Jul 28, 2026
3a2fb1e
Survive the quoted NaN the service sends for an undefined statistic
m7md7sien Jul 28, 2026
fe590ed
Explain the two schedule failures the E2E scenarios turned up
m7md7sien Jul 28, 2026
42f8fe1
Declare the eval service in the root azure.yaml instead of describing it
m7md7sien Jul 28, 2026
2713a8e
Add the eval service through azd, and catch two things the corner cas…
m7md7sien Jul 28, 2026
f3a8ace
Order the eval service after the Foundry project when the repo declar…
m7md7sien Jul 28, 2026
cfe1140
Address review: drop "eval group", split the model flags, show output…
m7md7sien Jul 28, 2026
69335b5
Drop agent.context.tools, and the dataset update synonym
m7md7sien Jul 28, 2026
da39cb8
Finish the eval-group rename in user-facing strings, and add run list…
m7md7sien Jul 29, 2026
c919ed6
Add custom code evaluators
m7md7sien Jul 29, 2026
41d1f8b
Fix code evaluator publishing against the live service
m7md7sien Jul 29, 2026
32b01ba
Publish code evaluators as a single script, not a folder
m7md7sien Jul 30, 2026
53c08cb
Cover schedules end to end, and explain the role they need
m7md7sien Aug 2, 2026
423035b
Move scheduling out of M1 onto its own branch
m7md7sien Aug 2, 2026
e8c1f0c
Correct two stale claims in the command reference
m7md7sien Aug 2, 2026
b0879e4
Drive the CLI as a subprocess, and fix what that found
m7md7sien Aug 2, 2026
45a1cd1
Record the azd up gap for code evaluators as a TODO
m7md7sien Aug 2, 2026
488317b
Let azd up configure a code evaluator, and stop TestLiveRun skipping
m7md7sien Aug 2, 2026
f9bb2b0
Cancel runs through the OpenAI-spec route
m7md7sien Aug 2, 2026
042ae63
Cover the remaining commands with end-to-end CLI tests
m7md7sien Aug 2, 2026
d98d5c5
Record the publish-to-reference consistency lag
m7md7sien Aug 3, 2026
5d6663a
Take code evaluators and comparison out of M1
m7md7sien Aug 3, 2026
34f9c0c
Leave M1 with the one target it ships
m7md7sien Aug 3, 2026
4bbcd92
Give datasets the same surface as evaluators
m7md7sien Aug 3, 2026
f7ae563
Add the eval and job groups
m7md7sien Aug 3, 2026
975e1a5
Turn results into run output, and make run a group
m7md7sien Aug 3, 2026
6dc1594
Split generation per artifact, and add CI gating
m7md7sien Aug 3, 2026
4199cd2
Match the gate wording the scenario documents
m7md7sien Aug 3, 2026
a367176
Ask the direct read whether an evaluator exists
m7md7sien Aug 3, 2026
6023995
One service per eval: restructure the configuration model
m7md7sien Aug 3, 2026
0aa5ec5
Refuse a wrong flag before a missing one
m7md7sien Aug 3, 2026
9bf1505
Drive the hero scenarios through real azd
m7md7sien Aug 3, 2026
e24dc74
Pin the command surface, and fix the flag it caught
m7md7sien Aug 3, 2026
8dd23d0
Stop pointing users at a command that was renamed away
m7md7sien Aug 3, 2026
c176044
Note the platforms nothing has run on yet
m7md7sien Aug 3, 2026
35aa699
Correct what CI already covers
m7md7sien Aug 3, 2026
5feb5c5
Type-check the tagged suites in CI
m7md7sien Aug 3, 2026
52cedc3
Stop an update from overwriting the version it is updating
m7md7sien Aug 3, 2026
badd670
Print the job id in the line that tells you to use it
m7md7sien Aug 3, 2026
425171b
Stop reporting a run that could not run as one that passed
m7md7sien Aug 3, 2026
5e43f3c
Report the verdict the run was started to get
m7md7sien Aug 3, 2026
d01832c
Give CI a shape to read, and stop a slow token from failing a test
m7md7sien Aug 3, 2026
a2fd7e3
Drive the CI reattach path end to end
m7md7sien Aug 3, 2026
7fe9946
Satisfy the two repo-wide checks this extension was failing
m7md7sien Aug 4, 2026
bb5362e
Keep a command from shadowing the flags M1 promises everywhere
m7md7sien Aug 4, 2026
d2b4d19
Prove a dataset version cannot be written over, and drop a working no…
m7md7sien Aug 4, 2026
11d37c0
Print the run summary and the row listing the way the spec describes …
m7md7sien Aug 4, 2026
ac0e7c2
Stop a stale version listing from failing a dataset update
m7md7sien Aug 4, 2026
a244703
Keep azure.ai.evaluations out of the foundry bundle
m7md7sien Aug 4, 2026
359430a
Drop the azure.ai.projects dependency
m7md7sien Aug 4, 2026
cc4febb
Drop COMMANDS.md
m7md7sien Aug 4, 2026
64c02db
Move the configuration to one file holding many evals
m7md7sien Aug 5, 2026
4591118
Keep an eval across a rename, and bound the first trace run
m7md7sien Aug 5, 2026
7c052ad
Say which eval on one flag, and let a run read another dataset
m7md7sien Aug 5, 2026
7da5c1b
Nest generation jobs under what produced them, and add eval create
m7md7sien Aug 5, 2026
b8d4027
Send the sources --from named, and pick a default that works
m7md7sien Aug 5, 2026
2fb79a7
Point CI at templates that exist, and verify the provider it declares
m7md7sien Aug 6, 2026
a0bb3e0
Read the agent's instructions from the project before asking the service
m7md7sien Aug 6, 2026
2d4ce00
Test the export writers and portal URLs, and delete what duplicated them
m7md7sien Aug 6, 2026
b5b56d2
Cover the mechanisms that fail silently when they drift
m7md7sien Aug 6, 2026
0fae3f9
Check the run flags against the spec's own table
m7md7sien Aug 6, 2026
f7e4d6a
Repair the tagged suites, and stop them rotting unnoticed
m7md7sien Aug 6, 2026
21a8b5f
Suggest commands this extension actually serves
m7md7sien Aug 6, 2026
79d2da4
Add the azure.ai.dataset extension
m7md7sien Aug 6, 2026
f1efd4c
Tidy the dataset module's dependencies
m7md7sien Aug 6, 2026
9c4dad5
Cover the new extension, and drop what the move left behind
m7md7sien Aug 6, 2026
a6850d7
Build the portal links the spec describes, and stop suggesting a dead…
m7md7sien Aug 6, 2026
aca6aac
Resolve the project endpoint the way the spec says, in five levels no…
m7md7sien Aug 6, 2026
45635ba
Test the wire, where a wrong URL costs a round trip to find out
m7md7sien Aug 6, 2026
aeb4b57
Pin the exit-code contract against the spec's CI scenario
m7md7sien Aug 6, 2026
86b0dd8
Give evaluator show a detail view, closing the last known deviation
m7md7sien Aug 6, 2026
4fb2091
Make every show a detail view, and stop pointing at the other extension
m7md7sien Aug 6, 2026
9cf157a
Put the rule back under list headers
m7md7sien Aug 6, 2026
68bb20c
Give run list the columns the scenario reads
m7md7sien Aug 6, 2026
a6fa0ec
Pin eval.yaml's keys, which are the contract with whoever wrote one
m7md7sien Aug 6, 2026
eb4f400
Stop escaping the path twice, which renamed the resource
m7md7sien Aug 6, 2026
2c87804
Cover the dataset client's download, which guesses at what it was given
m7md7sien Aug 6, 2026
5998473
Cover the poller and the evaluator contract, where waiting is the fea…
m7md7sien Aug 6, 2026
3d9dfff
Say the endpoint order the code actually follows
m7md7sien Aug 6, 2026
eb66497
Cover the dataset extension's output, which was copied without its tests
m7md7sien Aug 6, 2026
20776f0
Guard the dataset half of drift, and how --eval finds an eval
m7md7sien Aug 6, 2026
553e751
Leave generation with the extension that owns eval.yaml
m7md7sien Aug 6, 2026
bb07f76
Say what went into the catalog, not that something did
m7md7sien Aug 6, 2026
08725da
Let run find a configuration that init did not put in ./evals
m7md7sien Aug 6, 2026
160e75b
Write down the two files that decide whether this ships
m7md7sien Aug 6, 2026
553542d
Keep the eval branch to the eval extension
m7md7sien Aug 9, 2026
4c7cf08
Run the evals init already knows how to write
m7md7sien Aug 9, 2026
c2135cd
Cover the run data source, which nothing tested
m7md7sien Aug 9, 2026
f190ee9
Take the generation model from the agent, as the spec says
m7md7sien Aug 9, 2026
8946cdd
Give run list the dataset the scenario compares on
m7md7sien Aug 9, 2026
7c991ab
Put every message the CLI shows in one file
m7md7sien Aug 9, 2026
b384bf8
Refuse at load what the run would refuse anyway
m7md7sien Aug 9, 2026
d205450
Stop the two panics and the leak a bug bash would find
m7md7sien Aug 9, 2026
a6f9626
Bound the wait, and say when the answer was 'log in again'
m7md7sien Aug 9, 2026
c95645b
Tell people to retry before telling them to log in
m7md7sien Aug 9, 2026
8eb72d6
Name the configuration for azd
m7md7sien Aug 9, 2026
f2cea91
One generate, one job group, selected by a flag
m7md7sien Aug 9, 2026
47f3dca
Refuse an empty dataset early, bound the blob calls, guard the nil lists
m7md7sien Aug 9, 2026
5e8a6d7
Fix what the new command surface broke, and the tests that let it
m7md7sien Aug 9, 2026
eec8fe8
Fix what the Sol review found in the new surface
m7md7sien Aug 9, 2026
0680b53
Detect the agent instead of demanding it
m7md7sien Aug 10, 2026
fe8c31c
Name the dataset in the run summary and the CI handoff
m7md7sien Aug 10, 2026
2c8967f
Put the run summary in the order the scenario reads it
m7md7sien Aug 10, 2026
5904367
Show a version list what changed, and accept a file Windows wrote
m7md7sien Aug 10, 2026
17f0025
Run the tagged CLI suite and fix what it caught
m7md7sien Aug 10, 2026
bf0176f
Match the spec's run output renderings, and review findings
m7md7sien Aug 10, 2026
e67f065
Answer three not-found cases as answers, not as service noise
m7md7sien Aug 10, 2026
ec5161a
Report a downloaded artifact the way every other step reports
m7md7sien Aug 10, 2026
91d136a
Stop promising an exit code azd cannot deliver
m7md7sien Aug 10, 2026
8ed8354
Upload the dataset that was declared, not its neighbour
m7md7sien Aug 10, 2026
6a27a74
Notice a rubric field the author deleted
m7md7sien Aug 10, 2026
c82553d
Read every page of a run's rows
m7md7sien Aug 10, 2026
13d94b0
Refuse a configuration key rather than read it as nothing
m7md7sien Aug 10, 2026
00a2760
Recreate the eval whose artifact changed, not every eval in the file
m7md7sien Aug 10, 2026
f5d920b
Reconcile an eval against what it declares, not what it resolves to
m7md7sien Aug 10, 2026
2890575
Find gotestsum on the shell Windows actually runs
m7md7sien Aug 10, 2026
d9e4e49
Validate on the path a deploy actually takes, and honour a pinned ver…
m7md7sien Aug 10, 2026
42dc70d
Reconcile artifacts in `eval create`, and resolve `eval show` by name
m7md7sien Aug 10, 2026
a8a3d8e
Make `init` say it has no judge model instead of writing one without
m7md7sien Aug 11, 2026
67f49a6
Release 1.0.0-beta.3
m7md7sien Aug 11, 2026
9af1729
Send `--from traces` a request the service accepts
m7md7sien Aug 11, 2026
f006f1c
Say a rubric has not been generated yet, rather than naming a syscall
m7md7sien Aug 11, 2026
aff1fa3
Answer the way the sibling extension does: TYPE, absent, and not-found
m7md7sien Aug 11, 2026
e1180f7
Release 1.0.0-beta.4
m7md7sien Aug 11, 2026
cfe3aae
Print a path someone can copy back
m7md7sien Aug 11, 2026
6993e6a
Name a mistyped key, not the Go type it was not found in
m7md7sien Aug 11, 2026
8ceafa0
Release 1.0.0-beta.5
m7md7sien Aug 11, 2026
7e02ff0
Pin the live tests to what the service does now
m7md7sien Aug 11, 2026
6e558e7
Tell a reader holding the agents extension's eval.yaml where it belongs
m7md7sien Aug 11, 2026
1cec1ac
Release 1.0.0-beta.6
m7md7sien Aug 11, 2026
ba69287
Keep the whole rubric on the way to disk, and say what create actuall…
m7md7sien Aug 11, 2026
fb43305
Name the missing configuration, and stop a cancel race failing the suite
m7md7sien Aug 11, 2026
2ddaaf2
Release 1.0.0-beta.7
m7md7sien Aug 11, 2026
f52ee94
Answer a 404 in one line, and name the dataset that was never generated
m7md7sien Aug 11, 2026
bbda6d6
Release 1.0.0-beta.8
m7md7sien Aug 11, 2026
686d15b
Accept an eval's name where its id is accepted, unless the name is am…
m7md7sien Aug 11, 2026
4cae58e
Bump version.txt with extension.yaml
m7md7sien Aug 11, 2026
a2ccce8
Carry the dataset fixes into this extension's own copy of those commands
m7md7sien Aug 11, 2026
11b000a
Stop a concurrent reader from seeing a config that lost its evals
m7md7sien Aug 11, 2026
74ddca5
Ask which evaluators to grade with
m7md7sien Aug 11, 2026
15a829d
Refuse the flags and values that were accepted and ignored
m7md7sien Aug 12, 2026
971de0d
Report the two failures that were only ever written to a discarded log
m7md7sien Aug 12, 2026
467e2b0
Share the one replacement that survived being wrong twice
m7md7sien Aug 12, 2026
4a1b61a
Follow the cursor these listings were already being sent
m7md7sien Aug 12, 2026
c666084
Follow nextLink, and stop paying a dataset version to learn a config …
m7md7sien Aug 12, 2026
5c68347
Walk the container listing's marker, the last of the truncated pages
m7md7sien Aug 12, 2026
dc02ad1
Tell an absent verdict apart from a failing one
m7md7sien Aug 12, 2026
bdfece3
Name the deploy command that actually works in this project
m7md7sien Aug 12, 2026
6940e01
Report the listing that failed, not an eval it never found
m7md7sien Aug 12, 2026
5366250
Ship the fixes as 1.0.1-beta, one version in the feed
m7md7sien Aug 12, 2026
c779987
Name the working deploy command everywhere, not just in init
m7md7sien Aug 12, 2026
7c1c75a
Ship the deploy-command fix as 1.0.2-beta
m7md7sien Aug 12, 2026
a55a753
Give the release its changelog entry back
m7md7sien Aug 12, 2026
3271ce2
Clear the release gates the PR build was failing
m7md7sien Aug 12, 2026
e98c342
Finish the lint sweep: the last unhandled close, walk and words
m7md7sien Aug 12, 2026
ee9697e
Say the login expired only when it did
m7md7sien Aug 12, 2026
007d31f
Take the review's five, including two the comments were wrong about
m7md7sien Aug 12, 2026
e4627bb
Ship as 1.0.3-beta
m7md7sien Aug 12, 2026
bd6e956
Fix cspell failure on the eval extension
m7md7sien Aug 13, 2026
d1d5614
Point the multi-eval error at a flag that exists
m7md7sien Aug 13, 2026
b74972a
Retry once when the azd token subprocess overruns
m7md7sien Aug 13, 2026
76cdf8c
Cut 1.0.4-beta
m7md7sien Aug 13, 2026
5c73c41
Wire the token retry into the clients that actually mint tokens
m7md7sien Aug 13, 2026
0dad3a8
Stop leaking storage SAS credentials in logs and errors
m7md7sien Aug 13, 2026
3182548
Fix cspell and the gating help assertion after the exit-code wording …
m7md7sien Aug 13, 2026
1c23a2f
Say whether a deploy created the eval or reused it
m7md7sien Aug 13, 2026
50abef7
Stop a failed version listing reading as a new dataset
m7md7sien Aug 13, 2026
e512df3
Align both eval paginators with the dataset extension
m7md7sien Aug 13, 2026
a305b93
Surface a failed drift check, and say what the output means
m7md7sien Aug 13, 2026
0aba2de
Merge pull request #1 from m7md7sien/fix/eval-drift-and-output-nits
m7md7sien Aug 13, 2026
cd4d567
Pin the eval paginator behaviour the dataset extension already pins
m7md7sien Aug 13, 2026
686ce5d
Pin the eval_api paginator too
m7md7sien Aug 13, 2026
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
28 changes: 28 additions & 0 deletions .github/workflows/lint-ext-azure-ai-evaluations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: ext-azure-ai-evaluations-ci

on:
pull_request:
paths:
- "cli/azd/extensions/azure.ai.evaluations/**"
- ".github/workflows/lint-ext-azure-ai-evaluations.yml"
- ".github/workflows/verify-ext-providers.yml"
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write # required by reusable workflow lint-go.yml

jobs:
lint:
uses: ./.github/workflows/lint-go.yml
with:
working-directory: cli/azd/extensions/azure.ai.evaluations

verify-providers:
uses: ./.github/workflows/verify-ext-providers.yml
with:
working-directory: cli/azd/extensions/azure.ai.evaluations
5 changes: 5 additions & 0 deletions cli/azd/extensions/azure.ai.evaluations/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Test report written by ci-test.ps1 for the pipeline to publish.
junitTestReport.xml

# Debug log written when --debug or AZD_EXT_DEBUG is set.
azd-ai-eval-*.log
21 changes: 21 additions & 0 deletions cli/azd/extensions/azure.ai.evaluations/.golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: "2"

linters:
default: none
enable:
- gosec
- lll
- unused
- errorlint
settings:
lll:
line-length: 220
tab-width: 4
gosec:
excludes:
- G204 # Subprocess launched with variable (bicep build invoked in tests)
- G304 # Potential file inclusion via variable

formatters:
enable:
- gofmt
29 changes: 29 additions & 0 deletions cli/azd/extensions/azure.ai.evaluations/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Release History

## 1.0.4-beta (Unreleased)

First release of the Foundry evaluations extension.

### Features Added

- Initial release of the Foundry evaluations extension, `azd ai eval`.
- `init` scaffolds `evals/azure.eval.yaml` next to an agent and adds the service
entry that `$ref`s it to `azure.yaml`, making no service calls.
- `generate` synthesizes a rubric and dataset from the agent's context, writes
them under `evals/`, and merges `source:` references into the deployment spec
while preserving comments, ordering and neighboring entries.
- `run` creates the eval group when it does not exist, starts a run, and
summarizes the result.
- `azure.ai.eval` service-target provider deploys datasets, evaluators and eval
groups during a deploy, reconciling them in dependency order.
- Change detection so a repeated deploy publishes no redundant versions:
datasets are fingerprinted locally, evaluator definitions are compared on the
keys the author wrote, and eval groups are recreated only when their own
declaration changes.
- Atomic commands for every operation: `dataset`, `evaluator`, `run` and
`results` subcommands, all supporting `-o json` and `--no-prompt`.
- Testing criteria are shaped from each evaluator's published contract, so
evaluators requiring inputs beyond the agent shape — `ground_truth`,
`context`, `instruction_id_list` — work by binding them to dataset columns.
A required column the dataset does not carry is reported before the request
is sent, naming the column.
173 changes: 173 additions & 0 deletions cli/azd/extensions/azure.ai.evaluations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Azure Developer CLI (azd) Evaluations Extension

Define Foundry evaluations alongside your agent in `azure.yaml`, deploy them
with `azd up`, and run them from the terminal.

```bash
azd ai eval init # scaffold evals/ next to your agent
azd ai eval generate # synthesize a rubric and dataset from the agent
azd up # register datasets and evaluators, create the eval group
azd ai eval run # run the evaluation and summarize the results
```

## What gets deployed

Eval resources are one service entry in `azure.yaml`, normally a `$ref` to a
file under `evals/`:

```yaml
# azure.yaml
services:
ai-project:
host: azure.ai.project
evals:
host: azure.ai.eval
uses: [ai-project]
$ref: ./evals/azure.yaml
```

```yaml
# evals/azure.yaml
datasets:
- name: support-golden
source: ./datasets/support-golden.jsonl

evaluators:
- name: support-quality
source: ./evaluators/support-quality.json

evalGroups:
- name: support-quality
dataset: support-golden
evaluators:
- builtin.task_adherence
- support-quality
target:
type: agent
name: support-agent
options:
eval_model: gpt-4.1-nano
```

`azd up` reconciles **datasets → evaluators → eval groups**, in that order,
because a group references the versions the first two resolve to.

Relative paths inside a `$ref`'d file resolve against **that file's**
directory, so `./datasets/x.jsonl` above means `evals/datasets/x.jsonl`.

### Repeated deploys do not create redundant versions

Datasets are fingerprinted locally, because the dataset API exposes no content
hash and comparing against the service would mean downloading the blob on every
deploy. Evaluator definitions are compared against the service, but only on the
keys you authored — the service adds `data_schema`, `init_parameters` and
`metrics` of its own.

Eval groups are immutable, so a change to a group's evaluators, target or
options creates a new group and a new id. The id is cached in the azd
environment so repeat runs stay comparable.

## Commands

| Group | Commands |
|---|---|
| `azd ai eval` | `init` · `generate` · `run` |
| `azd ai eval dataset` | `create` · `list` · `show` · `update` · `delete` |
| `azd ai eval evaluator` | `upload` · `list` · `show` · `update` · `delete` · `builtins` |
| `azd ai eval run` | `start` · `list` · `show` · `cancel` |
| `azd ai eval results` | `show` · `export` |

`create` and `update` both publish a new immutable version; the server
auto-increments and nothing mutates in place.

Every command supports `-o json` and `--no-prompt`, so the whole surface is
usable from CI.

## Evaluators

Built-ins need no declaration — reference them as `builtin.<name>` and list
them with `azd ai eval evaluator builtins`.

Evaluators do not share an input contract, so the CLI reads each one's
published contract and shapes the request to match. An evaluator needing an
input your dataset does not carry is reported before the request is sent, with
the column named, rather than as a service-side rejection.

A custom rubric is a JSON list of weighted dimensions:

```json
{
"dimensions": [
{ "id": "accuracy", "description": "The answer is factually correct.", "weight": 5 },
{ "id": "tone", "description": "The answer is polite and professional.", "weight": 2 }
]
}
```

`weight` is an **integer from 1 to 10**. Weights do not need to sum to
anything.

## Choosing a project

The project endpoint is resolved in this order:

1. `--project-endpoint`
2. `FOUNDRY_PROJECT_ENDPOINT` in the active azd environment
3. the host environment variable of the same name

## Local development

### Prerequisites

- Go (the version in `go.mod`; `GOTOOLCHAIN=auto` fetches it)
- [azd](https://aka.ms/azd) and the extension developer kit:
`azd ext install microsoft.azd.extensions`

### Build, test, install

```bash
azd x build # compile and install into the local azd
azd x pack # package the artifacts
azd x publish # register in the local extension source
azd ext install azure.ai.evaluations --source local
```

```bash
go test ./internal/... # unit tests
```

### Live integration tests

These talk to a real Foundry project, so they are excluded from the default
build by the `live` tag and additionally gated on an environment variable:

```bash
export AZURE_AI_EVAL_E2E_LIVE=1
export FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
export AZURE_AI_EVAL_MODEL=gpt-4.1-nano # optional judge model
export AZURE_AI_EVAL_AGENT=<agent-name> # optional, enables the run phase

go test -tags live ./internal/cmd/ ./tests/live/
```

They clean up every resource they create.

### Debug logging

Request tracing is off by default. `--debug`, or `AZD_EXT_DEBUG=true`, writes
it to a dated log file rather than the terminal.

## TODO before release

Both are files the azd extensions team owns, so they are not changed here:

- [ ] **`cli/azd/extensions/registry.json`** — add the `azure.ai.evaluations`
entry. Until it exists `azd extension install azure.ai.evaluations` cannot
resolve, so the extension is only reachable through `azd x pack` +
`azd x publish` into the local source registry.
- [ ] **`.github/CODEOWNERS`** — add `/cli/azd/extensions/azure.ai.evaluations/`.
Every sibling Foundry extension has an entry; without one, PRs here get no
reviewer routing.
- [ ] **`microsoft.foundry/extension.yaml`** — add the dependency, but only
after the registry entry lands. Declaring a dependency that cannot resolve
breaks installing the bundle.
78 changes: 78 additions & 0 deletions cli/azd/extensions/azure.ai.evaluations/build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Ensure script fails on any error
$ErrorActionPreference = 'Stop'

# Get the directory of the script
$EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path

# Change to the script directory
Set-Location -Path $EXTENSION_DIR

# Create a safe version of EXTENSION_ID replacing dots with dashes
$EXTENSION_ID_SAFE = $env:EXTENSION_ID -replace '\.', '-'

# Define output directory
$OUTPUT_DIR = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { Join-Path $EXTENSION_DIR "bin" }

# Create output directory if it doesn't exist
if (-not (Test-Path -Path $OUTPUT_DIR)) {
New-Item -ItemType Directory -Path $OUTPUT_DIR | Out-Null
}

# Get Git commit hash and build date
$COMMIT = git rev-parse HEAD
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Failed to get git commit hash"
exit 1
}
$BUILD_DATE = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")

# List of OS and architecture combinations
if ($env:EXTENSION_PLATFORM) {
$PLATFORMS = @($env:EXTENSION_PLATFORM)
}
else {
$PLATFORMS = @(
"windows/amd64",
"windows/arm64",
"darwin/amd64",
"darwin/arm64",
"linux/amd64",
"linux/arm64"
)
}

$VERSION_PATH = "azureaieval/internal/version"

# Loop through platforms and build
foreach ($PLATFORM in $PLATFORMS) {
$OS, $ARCH = $PLATFORM -split '/'

$OUTPUT_NAME = Join-Path $OUTPUT_DIR "$EXTENSION_ID_SAFE-$OS-$ARCH"

if ($OS -eq "windows") {
$OUTPUT_NAME += ".exe"
}

Write-Host "Building for $OS/$ARCH..."

# Delete the output file if it already exists
if (Test-Path -Path $OUTPUT_NAME) {
Remove-Item -Path $OUTPUT_NAME -Force
}

# Set environment variables for Go build
$env:GOOS = $OS
$env:GOARCH = $ARCH

go build `
-ldflags="-X '$VERSION_PATH.Version=$env:EXTENSION_VERSION' -X '$VERSION_PATH.Commit=$COMMIT' -X '$VERSION_PATH.BuildDate=$BUILD_DATE'" `
-o $OUTPUT_NAME

if ($LASTEXITCODE -ne 0) {
Write-Host "An error occurred while building for $OS/$ARCH"
exit 1
}
}

Write-Host "Build completed successfully!"
Write-Host "Binaries are located in the $OUTPUT_DIR directory."
Loading
Loading