Skip to content
Open
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
1 change: 0 additions & 1 deletion cli/azd/extensions/azure.ai.agents/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.26.0
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.1.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices/v2 v2.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0
Expand Down
2 changes: 0 additions & 2 deletions cli/azd/extensions/azure.ai.agents/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.1.0 h1:fdAOz6TFldGDoEcRa975i5L5QvWU8ptut+SJAIfuWUY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.1.0/go.mod h1:qV+BWew22CAalRTwJEAHs+aSLP49k/csNlspqhMIDRU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 h1:JI8PcWOImyvIUEZ0Bbmfe05FOlWkMi2KhjG+cAKaUms=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0/go.mod h1:nJLFPGJkyKfDDyJiPuHIXsCi/gpJkm07EvRgiX7SGlI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 h1:Hp+EScFOu9HeCbeW8WU2yQPJd4gGwhMgKxWe+G6jNzw=
Expand Down
28 changes: 3 additions & 25 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ type initFlags struct {
model string
manifestPointer string
src string
host string
env string
}

Expand Down Expand Up @@ -79,7 +78,6 @@ type GitHubUrlInfo struct {
}

const AiAgentHost = "azure.ai.agent"
const ContainerAppHost = "containerapp"

// checkAiModelServiceAvailable is a temporary check to ensure the azd host supports
// required gRPC services. Remove once azd core enforces requiredAzdVersion.
Expand Down Expand Up @@ -354,9 +352,6 @@ func newInitCommand(rootFlags *rootFlagsDefinition) *cobra.Command {
cmd.Flags().StringVarP(&flags.src, "src", "s", "",
"Directory to download the agent definition to (defaults to 'src/<agent-id>')")

cmd.Flags().StringVarP(&flags.host, "host", "", "",
"For container based agents, can override the default host to target a container app instead. Accepted values: 'containerapp'")

cmd.Flags().StringVarP(&flags.env, "environment", "e", "", "The name of the azd environment to use.")

return cmd
Expand Down Expand Up @@ -384,14 +379,6 @@ func (a *InitAction) Run(ctx context.Context) error {
isValidURL := false
isValidFile := false

if a.flags.host != "" && a.flags.host != "containerapp" {
return exterrors.Validation(
exterrors.CodeUnsupportedHost,
fmt.Sprintf("unsupported host value: '%s' is not supported", a.flags.host),
"use '--host containerapp' or omit '--host'",
)
}

if _, err := url.ParseRequestURI(a.flags.manifestPointer); err == nil {
isValidURL = true
} else if _, fileErr := os.Stat(a.flags.manifestPointer); fileErr == nil {
Expand Down Expand Up @@ -424,7 +411,7 @@ func (a *InitAction) Run(ctx context.Context) error {
}

// Add the agent to the azd project (azure.yaml) services
if err := a.addToProject(ctx, targetDir, agentManifest, a.flags.host); err != nil {
if err := a.addToProject(ctx, targetDir, agentManifest); err != nil {
return fmt.Errorf("failed to add agent to azure.yaml: %w", err)
}

Expand Down Expand Up @@ -1083,7 +1070,7 @@ func writeAgentDefinitionFile(targetDir string, agentManifest *agent_yaml.AgentM
return nil
}

func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentManifest *agent_yaml.AgentManifest, host string) error {
func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentManifest *agent_yaml.AgentManifest) error {
// Convert the template to bytes
templateBytes, err := json.Marshal(agentManifest.Template)
if err != nil {
Expand All @@ -1108,15 +1095,6 @@ func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentMa
return fmt.Errorf("failed to unmarshal JSON to AgentDefinition: %w", err)
}

var serviceHost string

switch host {
case "containerapp":
serviceHost = ContainerAppHost
default:
serviceHost = AiAgentHost
}

var agentConfig = project.ServiceTargetAgentConfig{}

resourceDetails := []project.Resource{}
Expand Down Expand Up @@ -1178,7 +1156,7 @@ func (a *InitAction) addToProject(ctx context.Context, targetDir string, agentMa
serviceConfig := &azdext.ServiceConfig{
Name: strings.ReplaceAll(agentDef.Name, " ", ""),
RelativePath: targetDir,
Host: serviceHost,
Host: AiAgentHost,
Language: "docker",
Config: agentConfigStruct,
}
Expand Down
67 changes: 16 additions & 51 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,19 @@ func newListenCommand() *cobra.Command {
}
defer azdClient.Close()

projectParser := &project.FoundryParser{AzdClient: azdClient}
// IMPORTANT: service target name here must match the name used in the extension manifest.
host := azdext.NewExtensionHost(azdClient).
WithServiceTarget(AiAgentHost, func() azdext.ServiceTargetProvider {
return project.NewAgentServiceTargetProvider(azdClient)
}).
WithProjectEventHandler("preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error {
return preprovisionHandler(ctx, azdClient, projectParser, args)
return preprovisionHandler(ctx, azdClient, args)
}).
WithProjectEventHandler("predeploy", func(ctx context.Context, args *azdext.ProjectEventArgs) error {
return predeployHandler(ctx, azdClient, projectParser, args)
return predeployHandler(ctx, azdClient, args)
}).
WithProjectEventHandler("postdeploy", func(ctx context.Context, args *azdext.ProjectEventArgs) error {
return postdeployHandler(ctx, projectParser, args)
return postdeployHandler(ctx, azdClient, args)
})

// Start listening for events
Expand All @@ -66,11 +65,7 @@ func newListenCommand() *cobra.Command {
}
}

func preprovisionHandler(ctx context.Context, azdClient *azdext.AzdClient, projectParser *project.FoundryParser, args *azdext.ProjectEventArgs) error {
if err := projectParser.SetIdentity(ctx, args); err != nil {
return fmt.Errorf("failed to set identity: %w", err)
}

func preprovisionHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error {
for _, svc := range args.Project.Services {
switch svc.Host {
case AiAgentHost:
Expand All @@ -80,21 +75,13 @@ func preprovisionHandler(ctx context.Context, azdClient *azdext.AzdClient, proje
if err := envUpdate(ctx, azdClient, args.Project, svc); err != nil {
return fmt.Errorf("failed to update environment for service %q: %w", svc.Name, err)
}
case ContainerAppHost:
if err := containerAgentHandling(ctx, azdClient, args.Project, svc); err != nil {
return fmt.Errorf("failed to handle container agent for service %q: %w", svc.Name, err)
}
}
}

return nil
}

func predeployHandler(ctx context.Context, azdClient *azdext.AzdClient, projectParser *project.FoundryParser, args *azdext.ProjectEventArgs) error {
if err := projectParser.SetIdentity(ctx, args); err != nil {
return fmt.Errorf("failed to set identity: %w", err)
}

func predeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error {
for _, svc := range args.Project.Services {
switch svc.Host {
case AiAgentHost:
Expand All @@ -110,14 +97,21 @@ func predeployHandler(ctx context.Context, azdClient *azdext.AzdClient, projectP
return nil
}

func postdeployHandler(ctx context.Context, projectParser *project.FoundryParser, args *azdext.ProjectEventArgs) error {
if err := projectParser.CoboPostDeploy(ctx, args); err != nil {
return err
func postdeployHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azdext.ProjectEventArgs) error {
hasAgent := false
for _, svc := range args.Project.Services {
if svc.Host == AiAgentHost {
hasAgent = true
break
}
}
if !hasAgent {
return nil
}

// Ensure agent identity RBAC is configured when vnext is enabled.
// Runs post-deploy because the platform provisions the identity during agent deployment.
if err := projectParser.EnsureAgentIdentityRBAC(ctx); err != nil {
if err := project.EnsureAgentIdentityRBAC(ctx, azdClient); err != nil {
return fmt.Errorf("agent identity RBAC setup failed: %w", err)
}

Expand Down Expand Up @@ -233,35 +227,6 @@ func resourcesEnvUpdate(ctx context.Context, resources []project.Resource, azdCl
return setEnvVar(ctx, azdClient, envName, "AI_PROJECT_DEPENDENT_RESOURCES", escapedJsonString)
}

func containerAgentHandling(ctx context.Context, azdClient *azdext.AzdClient, project *azdext.ProjectConfig, svc *azdext.ServiceConfig) error {
servicePath := svc.RelativePath
fullPath := filepath.Join(project.Path, servicePath)
agentYamlPath := filepath.Join(fullPath, "agent.yaml")

//nolint:gosec // agentYamlPath is resolved from project/service paths in current workspace
data, err := os.ReadFile(agentYamlPath)
if err != nil {
return nil
}

var agentDef agent_yaml.AgentDefinition
if err := yaml.Unmarshal(data, &agentDef); err != nil {
return fmt.Errorf("YAML content is not valid: %w", err)
}

// If there is an agent.yaml in the project, and it can be properly parsed into an agent definition, add the env var to enable container agents
currentEnvResponse, err := azdClient.Environment().GetCurrent(ctx, &azdext.EmptyRequest{})
if err != nil {
return err
}

if err := setEnvVar(ctx, azdClient, currentEnvResponse.Environment.Name, "ENABLE_CONTAINER_AGENTS", "true"); err != nil {
return err
}

return nil
}

func setEnvVar(ctx context.Context, azdClient *azdext.AzdClient, envName string, key string, value string) error {
_, err := azdClient.Environment().SetValue(ctx, &azdext.SetEnvRequest{
EnvName: envName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const (
CodeInvalidServiceConfig = "invalid_service_config"
CodeInvalidAgentRequest = "invalid_agent_request"
CodeInvalidSessionId = "invalid_session_id"
CodeUnsupportedHost = "unsupported_host"
CodeUnsupportedAgentKind = "unsupported_agent_kind"
CodeMissingAgentKind = "missing_agent_kind"
CodeAgentDefinitionNotFound = "agent_definition_not_found"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func agentIdentityDisplayName(accountName, projectName string) string {
//
// The platform provisions the agent identity automatically when an agent is deployed.
// This function assumes the identity already exists and assigns permissions to it.
func (p *FoundryParser) EnsureAgentIdentityRBAC(ctx context.Context) error {
azdEnvClient := p.AzdClient.Environment()
func EnsureAgentIdentityRBAC(ctx context.Context, azdClient *azdext.AzdClient) error {
azdEnvClient := azdClient.Environment()
cEnvResponse, err := azdEnvClient.GetCurrent(ctx, &azdext.EmptyRequest{})
if err != nil {
return fmt.Errorf("failed to get current environment: %w", err)
Expand Down Expand Up @@ -137,7 +137,7 @@ func (p *FoundryParser) EnsureAgentIdentityRBAC(ctx context.Context) error {
}

// Get tenant ID and create credential
tenantResponse, err := p.AzdClient.Account().LookupTenant(ctx, &azdext.LookupTenantRequest{
tenantResponse, err := azdClient.Account().LookupTenant(ctx, &azdext.LookupTenantRequest{
SubscriptionId: info.SubscriptionID,
})
if err != nil {
Expand Down
Loading
Loading