Skip to content

Commit 2649bab

Browse files
JeffreyCACopilot
authored andcommitted
Filter out unsupported models and locations from init prompts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7be63ed commit 2649bab

9 files changed

Lines changed: 229 additions & 41 deletions

File tree

cli/azd/extensions/azure.ai.agents/extension.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: Ship agents with Microsoft Foundry from your terminal. (Preview)
66
usage: azd ai agent <command> [options]
77
# NOTE: Make sure version.txt is in sync with this version.
88
version: 0.1.19-preview
9-
requiredAzdVersion: ">1.23.6"
9+
requiredAzdVersion: ">1.23.13"
1010
language: go
1111
capabilities:
1212
- custom-commands

cli/azd/extensions/azure.ai.agents/go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@ require (
2626
gopkg.in/yaml.v3 v3.0.1
2727
)
2828

29+
replace github.com/azure/azure-dev/cli/azd => ../..
30+
2931
require (
3032
dario.cat/mergo v1.0.2 // indirect
3133
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
3234
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
3335
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 // indirect
36+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0 // indirect
37+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 // indirect
38+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
3439
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
3540
github.com/Masterminds/semver/v3 v3.4.0 // indirect
3641
github.com/adam-lavrik/go-imath v0.0.0-20210910152346-265a42a96f0b // indirect
@@ -49,13 +54,15 @@ require (
4954
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
5055
github.com/charmbracelet/x/exp/slice v0.0.0-20260204111555-7642919e0bee // indirect
5156
github.com/charmbracelet/x/term v0.2.2 // indirect
57+
github.com/cli/browser v1.3.0 // indirect
5258
github.com/clipperhouse/displaywidth v0.9.0 // indirect
5359
github.com/clipperhouse/stringish v0.1.1 // indirect
5460
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
5561
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
5662
github.com/dlclark/regexp2 v1.11.5 // indirect
5763
github.com/go-logr/logr v1.4.3 // indirect
5864
github.com/go-logr/stdr v1.2.2 // indirect
65+
github.com/gofrs/flock v0.12.1 // indirect
5966
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
6067
github.com/golobby/container/v3 v3.3.2 // indirect
6168
github.com/gorilla/css v1.0.1 // indirect
@@ -95,6 +102,7 @@ require (
95102
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
96103
go.opentelemetry.io/otel/trace v1.42.0 // indirect
97104
go.uber.org/atomic v1.11.0 // indirect
105+
go.uber.org/multierr v1.11.0 // indirect
98106
golang.org/x/crypto v0.48.0 // indirect
99107
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
100108
golang.org/x/net v0.51.0 // indirect

cli/azd/extensions/azure.ai.agents/go.sum

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armconta
2323
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0/go.mod h1:E7ltexgRDmeJ0fJWv0D/HLwY2xbDdN+uv+X2uZtOx3w=
2424
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
2525
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
26+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
27+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38=
28+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0 h1:nnQ9vXH039UrEFxi08pPuZBE7VfqSJt343uJLw0rhWI=
29+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0/go.mod h1:4YIVtzMFVsPwBvitCDX7J9sqthSj43QD1sP6fYc1egc=
2630
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o=
2731
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0=
2832
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
2933
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
3034
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY=
3135
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo=
36+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 h1:/g8S6wk65vfC6m3FIxJ+i5QDyN9JWwXI8Hb0Img10hU=
37+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0/go.mod h1:gpl+q95AzZlKVI3xSoseF9QPrypk0hQqBiJYeB/cR/I=
38+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4=
39+
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA=
3240
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
3341
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
3442
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
@@ -51,8 +59,6 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp
5159
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
5260
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
5361
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
54-
github.com/azure/azure-dev/cli/azd v1.23.13 h1:KVPI0JvIMBblO9WJnrnOM//TwIYSTFIAwkSX+Y79hjQ=
55-
github.com/azure/azure-dev/cli/azd v1.23.13/go.mod h1:skXbQLtJw3iu4ZchM0sDc5bG/KKTG2aVdOIv3cn+g9w=
5662
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
5763
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
5864
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
@@ -85,6 +91,8 @@ github.com/charmbracelet/x/exp/slice v0.0.0-20260204111555-7642919e0bee h1:B/JPE
8591
github.com/charmbracelet/x/exp/slice v0.0.0-20260204111555-7642919e0bee/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA=
8692
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
8793
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
94+
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
95+
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
8896
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
8997
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
9098
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
@@ -112,6 +120,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
112120
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
113121
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
114122
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
123+
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
124+
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
115125
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
116126
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
117127
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
@@ -249,6 +259,8 @@ go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4Len
249259
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
250260
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
251261
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
262+
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
263+
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
252264
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
253265
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
254266
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

cli/azd/extensions/azure.ai.agents/internal/cmd/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type GitHubUrlInfo struct {
7878
}
7979

8080
const AiAgentHost = "azure.ai.agent"
81+
const agentsV2ModelCapability = "agentsV2"
8182

8283
// checkAiModelServiceAvailable is a temporary check to ensure the azd host supports
8384
// required gRPC services. Remove once azd core enforces requiredAzdVersion.

cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_resources_helpers.go

Lines changed: 112 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry"
1919
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
2020
"github.com/azure/azure-dev/cli/azd/pkg/azdext"
21+
"github.com/azure/azure-dev/cli/azd/pkg/output"
2122
"github.com/azure/azure-dev/cli/azd/pkg/ux"
2223
"google.golang.org/grpc/codes"
2324
"google.golang.org/grpc/status"
@@ -722,23 +723,27 @@ func ensureLocation(
722723
azureContext *azdext.AzureContext,
723724
envName string,
724725
) error {
725-
if azureContext.Scope.Location != "" {
726+
allowedLocations := supportedRegionsForInit()
727+
728+
if azureContext.Scope.Location != "" && locationAllowed(azureContext.Scope.Location, allowedLocations) {
726729
return nil
727730
}
731+
if azureContext.Scope.Location != "" {
732+
fmt.Printf("%s", output.WithWarningFormat(
733+
"The current AZURE_LOCATION '%s' is not supported for this agent setup. Please choose a different location.\n",
734+
azureContext.Scope.Location,
735+
))
736+
azureContext.Scope.Location = ""
737+
}
728738

729739
fmt.Println("Select an Azure location. This determines which models are available and where your Foundry project resources will be deployed.")
730740

731-
locationResponse, err := azdClient.Prompt().PromptLocation(ctx, &azdext.PromptLocationRequest{
732-
AzureContext: azureContext,
733-
})
741+
locationName, err := promptLocationForInit(ctx, azdClient, azureContext, allowedLocations)
734742
if err != nil {
735-
if exterrors.IsCancellation(err) {
736-
return exterrors.Cancelled("location selection was cancelled")
737-
}
738-
return exterrors.FromPrompt(err, "failed to prompt for location")
743+
return err
739744
}
740745

741-
azureContext.Scope.Location = locationResponse.Location.Name
746+
azureContext.Scope.Location = locationName
742747

743748
return setEnvValue(ctx, azdClient, envName, "AZURE_LOCATION", azureContext.Scope.Location)
744749
}
@@ -764,6 +769,61 @@ func ensureSubscriptionAndLocation(
764769
return newCredential, nil
765770
}
766771

772+
func normalizeLocationName(location string) string {
773+
return strings.TrimSpace(strings.ToLower(location))
774+
}
775+
776+
func locationAllowed(location string, allowedLocations []string) bool {
777+
if len(allowedLocations) == 0 {
778+
return true
779+
}
780+
781+
normalized := normalizeLocationName(location)
782+
for _, allowed := range allowedLocations {
783+
if normalized == normalizeLocationName(allowed) {
784+
return true
785+
}
786+
}
787+
788+
return false
789+
}
790+
791+
func promptLocationForInit(
792+
ctx context.Context,
793+
azdClient *azdext.AzdClient,
794+
azureContext *azdext.AzureContext,
795+
allowedLocations []string,
796+
) (string, error) {
797+
locationResponse, err := azdClient.Prompt().PromptLocation(ctx, &azdext.PromptLocationRequest{
798+
AzureContext: azureContext,
799+
AllowedLocations: allowedLocations,
800+
})
801+
if err != nil {
802+
if exterrors.IsCancellation(err) {
803+
return "", exterrors.Cancelled("location selection was cancelled")
804+
}
805+
return "", exterrors.FromPrompt(err, "failed to prompt for location")
806+
}
807+
808+
return locationResponse.Location.Name, nil
809+
}
810+
811+
func agentModelFilter(locations []string, excludeModelNames []string) *azdext.AiModelFilterOptions {
812+
filter := &azdext.AiModelFilterOptions{
813+
Capabilities: []string{agentsV2ModelCapability},
814+
}
815+
816+
if len(locations) > 0 {
817+
filter.Locations = locations
818+
}
819+
820+
if len(excludeModelNames) > 0 {
821+
filter.ExcludeModelNames = excludeModelNames
822+
}
823+
824+
return filter
825+
}
826+
767827
// --- Shared model helpers ---
768828

769829
// selectNewModel prompts the user to select a model from the AI catalog, filtered by location.
@@ -781,15 +841,13 @@ func selectNewModel(
781841

782842
promptReq := &azdext.PromptAiModelRequest{
783843
AzureContext: azureContext,
844+
Filter: agentModelFilter([]string{azureContext.Scope.Location}, nil),
784845
SelectOptions: &azdext.SelectOptions{
785846
Message: "Select a model",
786847
},
787848
Quota: &azdext.QuotaCheckOptions{
788849
MinRemainingCapacity: 1,
789850
},
790-
Filter: &azdext.AiModelFilterOptions{
791-
Locations: []string{azureContext.Scope.Location},
792-
},
793851
DefaultValue: defaultModel,
794852
}
795853

@@ -804,13 +862,13 @@ func selectNewModel(
804862
// resolveModelDeployment resolves a model deployment without prompting, selecting the best
805863
// candidate based on default versions, SKU priority, and available quota. Both init flows
806864
// use this for the "deploy new model" path in non-interactive mode.
807-
func resolveModelDeployment(
865+
func resolveModelDeployments(
808866
ctx context.Context,
809867
azdClient *azdext.AzdClient,
810868
azureContext *azdext.AzureContext,
811869
model *azdext.AiModel,
812870
location string,
813-
) (*azdext.AiModelDeployment, error) {
871+
) ([]*azdext.AiModelDeployment, error) {
814872
resolveResp, err := azdClient.Ai().ResolveModelDeployments(ctx, &azdext.ResolveModelDeploymentsRequest{
815873
AzureContext: azureContext,
816874
ModelName: model.Name,
@@ -822,19 +880,22 @@ func resolveModelDeployment(
822880
},
823881
})
824882
if err != nil {
825-
return nil, exterrors.FromAiService(err, exterrors.CodeModelResolutionFailed)
883+
return nil, err
826884
}
827885

828-
if len(resolveResp.Deployments) == 0 {
829-
return nil, exterrors.Dependency(
830-
exterrors.CodeModelResolutionFailed,
831-
fmt.Sprintf("no deployment candidates found for model '%s' in location '%s'", model.Name, location),
832-
"",
833-
)
886+
return resolveResp.Deployments, nil
887+
}
888+
889+
func selectBestModelDeploymentCandidate(
890+
model *azdext.AiModel,
891+
deployments []*azdext.AiModelDeployment,
892+
) *azdext.AiModelDeployment {
893+
if len(deployments) == 0 {
894+
return nil
834895
}
835896

836-
orderedCandidates := make([]*azdext.AiModelDeployment, len(resolveResp.Deployments))
837-
copy(orderedCandidates, resolveResp.Deployments)
897+
orderedCandidates := make([]*azdext.AiModelDeployment, len(deployments))
898+
copy(orderedCandidates, deployments)
838899

839900
defaultVersions := make(map[string]struct{}, len(model.Versions))
840901
for _, version := range model.Versions {
@@ -851,7 +912,34 @@ func resolveModelDeployment(
851912
continue
852913
}
853914

854-
return cloneDeploymentWithCapacity(candidate, capacity), nil
915+
return cloneDeploymentWithCapacity(candidate, capacity)
916+
}
917+
918+
return nil
919+
}
920+
921+
func resolveModelDeployment(
922+
ctx context.Context,
923+
azdClient *azdext.AzdClient,
924+
azureContext *azdext.AzureContext,
925+
model *azdext.AiModel,
926+
location string,
927+
) (*azdext.AiModelDeployment, error) {
928+
deployments, err := resolveModelDeployments(ctx, azdClient, azureContext, model, location)
929+
if err != nil {
930+
return nil, exterrors.FromAiService(err, exterrors.CodeModelResolutionFailed)
931+
}
932+
933+
if len(deployments) == 0 {
934+
return nil, exterrors.Dependency(
935+
exterrors.CodeModelResolutionFailed,
936+
fmt.Sprintf("no deployment candidates found for model '%s' in location '%s'", model.Name, location),
937+
"",
938+
)
939+
}
940+
941+
if candidate := selectBestModelDeploymentCandidate(model, deployments); candidate != nil {
942+
return candidate, nil
855943
}
856944

857945
return nil, fmt.Errorf("no deployment candidates found for model '%s' with a valid non-interactive capacity", model.Name)

cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_resources_helpers_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,16 @@ func TestFoundryProjectInfoFromResource(t *testing.T) {
295295
}
296296
}
297297

298+
func TestAgentModelFilter(t *testing.T) {
299+
t.Parallel()
300+
301+
filter := agentModelFilter([]string{"eastus2"}, []string{"gpt-4.1-mini"})
302+
303+
require.Equal(t, []string{agentsV2ModelCapability}, filter.Capabilities)
304+
require.Equal(t, []string{"eastus2"}, filter.Locations)
305+
require.Equal(t, []string{"gpt-4.1-mini"}, filter.ExcludeModelNames)
306+
}
307+
298308
func TestUpdateFoundryProjectInfo(t *testing.T) {
299309
t.Parallel()
300310

cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ func (a *InitFromCodeAction) createDefinitionFromLocalAgent(ctx context.Context)
594594
return nil, fmt.Errorf("failed to set AZURE_AI_MODEL_DEPLOYMENT_NAME: %w", err)
595595
}
596596
} else if selectedModel != nil {
597-
modelDetails, err := resolveModelDeployment(ctx, a.azdClient, a.azureContext, selectedModel, a.azureContext.Scope.Location)
597+
modelDetails, err := a.resolveSelectedModelDeployment(ctx, selectedModel)
598598
if err != nil {
599599
return nil, fmt.Errorf("failed to get model deployment details: %w", err)
600600
}
@@ -625,6 +625,31 @@ func (a *InitFromCodeAction) createDefinitionFromLocalAgent(ctx context.Context)
625625
return definition, nil
626626
}
627627

628+
func (a *InitFromCodeAction) resolveSelectedModelDeployment(
629+
ctx context.Context,
630+
model *azdext.AiModel,
631+
) (*azdext.AiModelDeployment, error) {
632+
deployments, err := resolveModelDeployments(ctx, a.azdClient, a.azureContext, model, a.azureContext.Scope.Location)
633+
if err == nil {
634+
if candidate := selectBestModelDeploymentCandidate(model, deployments); candidate != nil {
635+
return candidate, nil
636+
}
637+
}
638+
639+
if err != nil && !isRecoverableDeploymentSelectionError(err) {
640+
return nil, exterrors.FromAiService(err, exterrors.CodeModelResolutionFailed)
641+
}
642+
643+
selector := &InitAction{
644+
azdClient: a.azdClient,
645+
azureContext: a.azureContext,
646+
environment: a.environment,
647+
flags: a.flags,
648+
}
649+
650+
return selector.getModelDetails(ctx, model.Name)
651+
}
652+
628653
// sanitizeAgentName converts a string into a valid agent name:
629654
// lowercase, replace non-alphanumeric with hyphens, collapse consecutive hyphens,
630655
// strip leading/trailing hyphens, truncate to 63 chars.

0 commit comments

Comments
 (0)