Skip to content

Commit d1d213c

Browse files
Antriksh JainCopilot
andcommitted
fix(azure.ai.agents): ResolveAfterInit suggests azd provision when project endpoint is unset
After `azd ai agent init` with "Deploy new model(s) from the catalog", the suggestion was wrongly: Next: azd ai agent run -- start the agent locally azd deploy -- when ready to deploy to Azure even though provision had not run yet and the user has no agent to run against (AZURE_AI_PROJECT_ENDPOINT empty, no Bicep outputs populated). Root cause ========== ResolveAfterInit's decision tree branched only on MissingInfraVars (derived from `${AZURE_*}` references in agent.yaml). The notetaking sample's agent.yaml has no such references, so the list stayed empty and the resolver fell through to the "Otherwise → azd ai agent run" branch. The new-models path stores its outputs (project endpoint, model deployments) as azd env vars that aren't templated into agent.yaml until provision runs, so there was no signal in the existing tree. Fix === Add `!state.HasProjectEndpoint` as the first condition. The project endpoint is the canonical "provision finished" marker — it is set by `azd provision` as a Bicep output, or by `azd ai agent init` when the user selects "Use existing project". When empty, provision hasn't run yet and `azd provision` is the correct next step regardless of whether agent.yaml directly references any AZURE_* variables. Decision tree after the fix: !HasProjectEndpoint OR MissingInfraVars → azd provision MissingManualVars → azd env set <key> <val>... Otherwise → azd ai agent run The MissingInfraVars branch is preserved for the post-provision re-provision case (user adds a new ${AZURE_*} reference to agent.yaml after the last provision run). Affected call sites (no changes needed at the call sites) ========================================================= - `init.go:1608` — the user-reported bug; now suggests `azd provision`. - `doctor.go:243` — second production caller; the doctor only renders Next: when all checks pass, so this path is reached only after the user has fixed the AZURE_AI_PROJECT_ENDPOINT failure — at which point HasProjectEndpoint=true and the existing "run locally" branch fires correctly. Tests ===== resolver_test.go::TestResolveAfterInit updated: - happy-path test now uses `&State{HasProjectEndpoint: true}` — represents a truly post-provision state. - new subtest "project endpoint not yet set → provision" pins the new branch on a zero-value State (the user's repro). - existing infra-vars subtest now also sets HasProjectEndpoint=true so it specifically exercises the re-provision case. - new subtest "project endpoint missing wins over manual vars" confirms ordering — provision unblocks both infra outputs AND manual var resolution, so it must come first. resolver_test.go::TestResolveAfterInit_ManualVarsCapAtThree updated to set HasProjectEndpoint=true (otherwise it would now hit the new provision branch instead of testing the manual-vars cap). Pre-flight ========== - gofmt -s -w . clean - go vet ./... clean - go build ./... clean - go test ./... -count=1 all green - golangci-lint run ./internal/cmd/nextstep/... 0 issues - cspell on resolver.go 0 issues Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f39e0ac commit d1d213c

2 files changed

Lines changed: 40 additions & 9 deletions

File tree

cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,18 @@ const (
3333
// successful `azd ai agent init`. Pure function over *State.
3434
//
3535
// Decision tree:
36-
// - MissingInfraVars → `azd provision` (then "run `azd ai agent run` to
37-
// start locally" tail line)
36+
// - !HasProjectEndpoint OR MissingInfraVars → `azd provision`
37+
// The project endpoint is the canonical "provision finished"
38+
// marker — it is set by `azd provision` as a Bicep output, or by
39+
// `azd ai agent init` when the user selects an existing Foundry
40+
// project. When the endpoint is empty, provision has not yet
41+
// populated the infra outputs (typical path: user selected
42+
// "Deploy new models from the catalog" in init), so `azd
43+
// provision` is the next step regardless of whether agent.yaml
44+
// directly references any AZURE_* variables. MissingInfraVars is
45+
// still consulted to cover the post-provision re-provision case
46+
// (a new ${AZURE_*} reference was added to agent.yaml after the
47+
// last provision run).
3848
// - MissingManualVars → one `azd env set <KEY> <value>` per missing var
3949
// (up to maxManualVarLines)
4050
// - Otherwise → `azd ai agent run`
@@ -48,7 +58,7 @@ func ResolveAfterInit(state *State) []Suggestion {
4858
out := make([]Suggestion, 0, 4)
4959

5060
switch {
51-
case len(state.MissingInfraVars) > 0:
61+
case !state.HasProjectEndpoint || len(state.MissingInfraVars) > 0:
5262
out = append(out, Suggestion{
5363
Command: "azd provision",
5464
Description: "set up your Foundry project, models, and connections",

cli/azd/extensions/azure.ai.agents/internal/cmd/nextstep/resolver_test.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,43 @@ func TestResolveAfterInit(t *testing.T) {
2222
wantTrailing string
2323
}{
2424
{
25-
name: "happy path → run locally",
26-
state: &State{},
25+
name: "happy path (provisioned) → run locally",
26+
state: &State{HasProjectEndpoint: true},
2727
wantPrimaryHas: "azd ai agent run",
2828
wantTrailing: "azd deploy",
2929
},
3030
{
31-
name: "infra vars missing → provision",
32-
state: &State{MissingInfraVars: []string{"AZURE_AI_FOO"}},
31+
name: "project endpoint not yet set → provision",
32+
state: &State{},
33+
wantPrimaryHas: "azd provision",
34+
wantTrailing: "azd deploy",
35+
},
36+
{
37+
name: "infra vars missing post-provision → provision (re-provision)",
38+
state: &State{
39+
HasProjectEndpoint: true,
40+
MissingInfraVars: []string{"AZURE_AI_FOO"},
41+
},
3342
wantPrimaryHas: "azd provision",
3443
wantTrailing: "azd deploy",
3544
},
3645
{
3746
name: "manual vars missing → up to 3 env set lines, sorted",
3847
state: &State{
39-
MissingManualVars: []string{"DELTA", "ALPHA", "ECHO", "BRAVO"},
48+
HasProjectEndpoint: true,
49+
MissingManualVars: []string{"DELTA", "ALPHA", "ECHO", "BRAVO"},
4050
},
4151
wantManualVarKeys: []string{"ALPHA", "BRAVO", "DELTA"},
4252
wantTrailing: "azd deploy",
4353
},
54+
{
55+
name: "project endpoint missing wins over manual vars (provision unblocks both)",
56+
state: &State{
57+
MissingManualVars: []string{"USER_API_KEY"},
58+
},
59+
wantPrimaryHas: "azd provision",
60+
wantTrailing: "azd deploy",
61+
},
4462
}
4563

4664
for _, tt := range tests {
@@ -73,7 +91,10 @@ func TestResolveAfterInit(t *testing.T) {
7391
func TestResolveAfterInit_ManualVarsCapAtThree(t *testing.T) {
7492
t.Parallel()
7593

76-
state := &State{MissingManualVars: []string{"V1", "V2", "V3", "V4", "V5"}}
94+
state := &State{
95+
HasProjectEndpoint: true,
96+
MissingManualVars: []string{"V1", "V2", "V3", "V4", "V5"},
97+
}
7798
out := ResolveAfterInit(state)
7899
// 3 manual + 1 trailing.
79100
require.Len(t, out, 4)

0 commit comments

Comments
 (0)