From fc74bf590f5d2adc9d7bbc82673ef8d78a2310a6 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 26 Nov 2025 19:23:08 +0000 Subject: [PATCH 1/3] Support debug logging in AgentClient via `--debug` or `AZD_EXT_DEBUG` --- cli/azd/cmd/middleware/extensions.go | 7 +- .../azure.ai.agents/internal/cmd/listen.go | 14 +- .../pkg/agents/agent_api/logging_policy.go | 135 ++++++++++++++++++ .../pkg/agents/agent_api/operations.go | 88 +++++------- .../internal/project/service_target_agent.go | 16 ++- 5 files changed, 203 insertions(+), 57 deletions(-) create mode 100644 cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go diff --git a/cli/azd/cmd/middleware/extensions.go b/cli/azd/cmd/middleware/extensions.go index 85768d9f828..ed21d5c4b68 100644 --- a/cli/azd/cmd/middleware/extensions.go +++ b/cli/azd/cmd/middleware/extensions.go @@ -132,8 +132,13 @@ func (m *ExtensionsMiddleware) Run(ctx context.Context, next NextFn) (*actions.A allEnv = append(allEnv, "FORCE_COLOR=1") } + args := []string{"listen"} + if debugEnabled, _ := m.options.Flags.GetBool("debug"); debugEnabled { + args = append(args, "--debug") + } + options := &extensions.InvokeOptions{ - Args: []string{"listen"}, + Args: args, Env: allEnv, StdIn: ext.StdIn(), StdOut: ext.StdOut(), diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go index b579cb63a83..822a6e268b2 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go @@ -9,6 +9,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "strings" "azureaiagent/internal/pkg/agents/agent_yaml" @@ -17,6 +18,7 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/azdext" "github.com/braydonk/yaml" "github.com/spf13/cobra" + "github.com/spf13/pflag" "google.golang.org/protobuf/types/known/structpb" ) @@ -40,7 +42,7 @@ func newListenCommand() *cobra.Command { // 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) + return project.NewAgentServiceTargetProvider(azdClient, isDebug(cmd.Flags())) }). WithProjectEventHandler("preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error { return preprovisionHandler(ctx, azdClient, projectParser, args) @@ -315,3 +317,13 @@ func populateContainerSettings(ctx context.Context, azdClient *azdext.AzdClient, return nil } + +// isDebug checks if debug mode is enabled via --debug flag or AZD_EXT_DEBUG environment variable +func isDebug(flags *pflag.FlagSet) bool { + if debugFlag, err := flags.GetBool("debug"); err == nil && debugFlag { + return true + } + + debug, _ := strconv.ParseBool(os.Getenv("AZD_EXT_DEBUG")) + return debug +} diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go new file mode 100644 index 00000000000..486c6d1543f --- /dev/null +++ b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package agent_api + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "maps" + "net/http" + "os" + "slices" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" +) + +// loggingPolicy is a custom pipeline policy that logs HTTP requests and responses +type loggingPolicy struct { + logFile *os.File +} + +// NewLoggingPolicy creates a new logging policy that writes to the specified file +func NewLoggingPolicy(logFile *os.File) policy.Policy { + return &loggingPolicy{logFile: logFile} +} + +// Do implements the policy.Policy interface +func (p *loggingPolicy) Do(req *policy.Request) (*http.Response, error) { + p.logRequest(req) + + resp, err := req.Next() + if err != nil { + p.logError(err) + return resp, err + } + + p.logResponse(resp) + + return resp, nil +} + +func (p *loggingPolicy) logRequest(req *policy.Request) { + if p.logFile == nil { + return + } + + fmt.Fprintf(p.logFile, "\n=== REQUEST [%s] ===\n", time.Now().Format(time.RFC3339)) + fmt.Fprintf(p.logFile, "%s %s\n", req.Raw().Method, req.Raw().URL.String()) + + // Log request body if present + if req.Raw().Body == nil { + fmt.Fprintf(p.logFile, "Body: (none)\n") + return + } + + body, err := io.ReadAll(req.Raw().Body) + // Always restore the body, even if read failed (restore what we got) + req.Raw().Body = io.NopCloser(bytes.NewReader(body)) + + if err != nil { + fmt.Fprintf(p.logFile, "Body: (error reading: %v)\n", err) + return + } + + if len(body) == 0 { + fmt.Fprintf(p.logFile, "Body: (empty)\n") + return + } + + fmt.Fprintf(p.logFile, "Body:\n") + var prettyPayload interface{} + if err := json.Unmarshal(body, &prettyPayload); err == nil { + prettyJSON, _ := json.MarshalIndent(prettyPayload, "", " ") + fmt.Fprintf(p.logFile, "%s\n", string(prettyJSON)) + } else { + fmt.Fprintf(p.logFile, "%s\n", string(body)) + } +} + +func (p *loggingPolicy) logResponse(resp *http.Response) { + if p.logFile == nil || resp == nil { + return + } + + fmt.Fprintf(p.logFile, "\n=== RESPONSE [%s] ===\n", time.Now().Format(time.RFC3339)) + fmt.Fprintf(p.logFile, "Status Code: %d\n", resp.StatusCode) + + fmt.Fprintf(p.logFile, "Headers:\n") + for _, key := range slices.Sorted(maps.Keys(resp.Header)) { + for _, value := range resp.Header[key] { + fmt.Fprintf(p.logFile, " %s: %s\n", key, value) + } + } + + // Read and log response body, then restore it + if resp.Body == nil { + fmt.Fprintf(p.logFile, "Body: (none)\n\n") + return + } + + body, err := io.ReadAll(resp.Body) + // Always restore the body, even if read failed (restore what we got) + resp.Body = io.NopCloser(bytes.NewReader(body)) + + if err != nil { + fmt.Fprintf(p.logFile, "Body: (error reading: %v)\n\n", err) + return + } + + fmt.Fprintf(p.logFile, "Body:\n") + if len(body) == 0 { + fmt.Fprintf(p.logFile, "(empty)\n\n") + return + } + + var jsonResponse interface{} + if err := json.Unmarshal(body, &jsonResponse); err == nil { + prettyJSON, _ := json.MarshalIndent(jsonResponse, "", " ") + fmt.Fprintf(p.logFile, "%s\n\n", string(prettyJSON)) + } else { + fmt.Fprintf(p.logFile, "%s\n\n", string(body)) + } +} + +func (p *loggingPolicy) logError(err error) { + if p.logFile == nil { + return + } + + fmt.Fprintf(p.logFile, "\n=== ERROR [%s] ===\n", time.Now().Format(time.RFC3339)) + fmt.Fprintf(p.logFile, "Error: %v\n\n", err) +} diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go index 667b141a2e2..0c0f59d6b1a 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go +++ b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go @@ -29,29 +29,53 @@ type AgentClient struct { pipeline runtime.Pipeline } +// AgentClientOptions configures the AgentClient +type AgentClientOptions struct { + // Debug enables debug logging to ai-agents-debug.log + Debug bool +} + // NewAgentClient creates a new AgentClient -func NewAgentClient(endpoint string, cred azcore.TokenCredential) *AgentClient { +func NewAgentClient(endpoint string, cred azcore.TokenCredential, options *AgentClientOptions) *AgentClient { + var logFile *os.File + if options != nil && options.Debug { + var err error + logFile, err = os.OpenFile("ai-agents-debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + // Fallback to stderr if file creation fails + logFile = os.Stderr + } + } + + return &AgentClient{ + endpoint: endpoint, + pipeline: createPipeline(cred, logFile), + } +} + +func createPipeline(cred azcore.TokenCredential, logFile *os.File) runtime.Pipeline { userAgent := fmt.Sprintf("azd-ext-azure-ai-agents/%s", version.Version) + perCallPolicies := []policy.Policy{ + runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil), + azsdk.NewMsCorrelationPolicy(), + azsdk.NewUserAgentPolicy(userAgent), + } + + if logFile != nil { + perCallPolicies = append(perCallPolicies, NewLoggingPolicy(logFile)) + } + clientOptions := &policy.ClientOptions{ - PerCallPolicies: []policy.Policy{ - runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil), - azsdk.NewMsCorrelationPolicy(), - azsdk.NewUserAgentPolicy(userAgent), - }, + PerCallPolicies: perCallPolicies, } - pipeline := runtime.NewPipeline( + return runtime.NewPipeline( "azure-ai-agents", "v1.0.0", runtime.PipelineOptions{}, clientOptions, ) - - return &AgentClient{ - endpoint: endpoint, - pipeline: pipeline, - } } // GetAgent retrieves a specific agent by name @@ -104,8 +128,6 @@ func (c *AgentClient) CreateAgent(ctx context.Context, request *CreateAgentReque return nil, fmt.Errorf("failed to set request body: %w", err) } - c.logRequest("POST", url, payload) - resp, err := c.pipeline.Do(req) if err != nil { return nil, fmt.Errorf("HTTP request failed: %w", err) @@ -126,7 +148,6 @@ func (c *AgentClient) CreateAgent(ctx context.Context, request *CreateAgentReque return nil, fmt.Errorf("failed to parse response: %w", err) } - c.logResponse(body) return &agent, nil } @@ -148,8 +169,6 @@ func (c *AgentClient) UpdateAgent(ctx context.Context, agentName string, request return nil, fmt.Errorf("failed to set request body: %w", err) } - c.logRequest("POST", url, payload) - resp, err := c.pipeline.Do(req) if err != nil { return nil, fmt.Errorf("HTTP request failed: %w", err) @@ -170,7 +189,6 @@ func (c *AgentClient) UpdateAgent(ctx context.Context, agentName string, request return nil, fmt.Errorf("failed to parse response: %w", err) } - c.logResponse(body) return &agent, nil } @@ -284,8 +302,6 @@ func (c *AgentClient) CreateAgentVersion(ctx context.Context, agentName string, return nil, fmt.Errorf("failed to set request body: %w", err) } - c.logRequest("POST", url, payload) - resp, err := c.pipeline.Do(req) if err != nil { return nil, fmt.Errorf("HTTP request failed: %w", err) @@ -306,7 +322,6 @@ func (c *AgentClient) CreateAgentVersion(ctx context.Context, agentName string, return nil, fmt.Errorf("failed to parse response: %w", err) } - c.logResponse(body) return &agentVersion, nil } @@ -579,8 +594,6 @@ func (c *AgentClient) StartAgentContainer(ctx context.Context, agentName, agentV return nil, fmt.Errorf("failed to set request body: %w", err) } - c.logRequest("POST", url, payload) - resp, err := c.pipeline.Do(req) if err != nil { return nil, fmt.Errorf("HTTP request failed: %w", err) @@ -606,7 +619,6 @@ func (c *AgentClient) StartAgentContainer(ctx context.Context, agentName, agentV Body: operation, } - c.logResponse(body) return result, nil } @@ -809,31 +821,3 @@ func (c *AgentClient) GetAgentContainerOperation(ctx context.Context, agentName, return &operation, nil } - -// Helper methods - -// logRequest logs the request details to stderr for debugging -func (c *AgentClient) logRequest(method, url string, payload []byte) { - fmt.Fprintf(os.Stderr, "%s %s\n", method, url) - if len(payload) > 0 { - var prettyPayload interface{} - if err := json.Unmarshal(payload, &prettyPayload); err == nil { - prettyJSON, _ := json.MarshalIndent(prettyPayload, "", " ") - fmt.Fprintf(os.Stderr, "Payload:\n%s\n", string(prettyJSON)) - } else { - fmt.Fprintf(os.Stderr, "Payload: %s\n", string(payload)) - } - } -} - -// logResponse logs the response body to stderr for debugging -func (c *AgentClient) logResponse(body []byte) { - fmt.Fprintln(os.Stderr, "Response:") - var jsonResponse interface{} - if err := json.Unmarshal(body, &jsonResponse); err == nil { - prettyJSON, _ := json.MarshalIndent(jsonResponse, "", " ") - fmt.Fprintln(os.Stderr, string(prettyJSON)) - } else { - fmt.Fprintln(os.Stderr, string(body)) - } -} diff --git a/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go b/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go index 2e4b77b2f93..6e4cc1522be 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go +++ b/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go @@ -40,12 +40,14 @@ type AgentServiceTargetProvider struct { tenantId string env *azdext.Environment foundryProject *arm.ResourceID + debug bool } // NewAgentServiceTargetProvider creates a new AgentServiceTargetProvider instance -func NewAgentServiceTargetProvider(azdClient *azdext.AzdClient) azdext.ServiceTargetProvider { +func NewAgentServiceTargetProvider(azdClient *azdext.AzdClient, debug bool) azdext.ServiceTargetProvider { return &AgentServiceTargetProvider{ azdClient: azdClient, + debug: debug, } } @@ -678,7 +680,11 @@ func (p *AgentServiceTargetProvider) createAgent( azdEnv map[string]string, ) (*agent_api.AgentVersionObject, error) { // Create agent client - agentClient := agent_api.NewAgentClient(azdEnv["AZURE_AI_PROJECT_ENDPOINT"], p.credential) + agentClient := agent_api.NewAgentClient( + azdEnv["AZURE_AI_PROJECT_ENDPOINT"], + p.credential, + &agent_api.AgentClientOptions{Debug: p.debug}, + ) // Use constant API version const apiVersion = "2025-05-15-preview" @@ -725,7 +731,11 @@ func (p *AgentServiceTargetProvider) startAgentContainer( fmt.Fprintln(os.Stderr) // Create agent client - agentClient := agent_api.NewAgentClient(azdEnv["AZURE_AI_PROJECT_ENDPOINT"], p.credential) + agentClient := agent_api.NewAgentClient( + azdEnv["AZURE_AI_PROJECT_ENDPOINT"], + p.credential, + &agent_api.AgentClientOptions{Debug: p.debug}, + ) var minReplicas, maxReplicas *int32 if foundryAgentConfig.Container != nil && foundryAgentConfig.Container.Scale != nil { From b427e7e758d1bc54b7aa72b8ebce409cbed0d74d Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 3 Dec 2025 01:32:11 +0000 Subject: [PATCH 2/3] Address feedback and use built-in log functionality in azure-sdk-for-go --- .../azure.ai.agents/internal/cmd/listen.go | 19 ++- .../pkg/agents/agent_api/logging_policy.go | 135 ------------------ .../pkg/agents/agent_api/operations.go | 52 ++----- .../internal/pkg/azure/logging.go | 5 + .../internal/project/service_target_agent.go | 6 +- 5 files changed, 39 insertions(+), 178 deletions(-) delete mode 100644 cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go create mode 100644 cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go index 822a6e268b2..9c195b9c932 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go @@ -11,10 +11,13 @@ import ( "path/filepath" "strconv" "strings" + "time" "azureaiagent/internal/pkg/agents/agent_yaml" + "azureaiagent/internal/pkg/azure" "azureaiagent/internal/project" + azcorelog "github.com/Azure/azure-sdk-for-go/sdk/azcore/log" "github.com/azure/azure-dev/cli/azd/pkg/azdext" "github.com/braydonk/yaml" "github.com/spf13/cobra" @@ -31,6 +34,20 @@ func newListenCommand() *cobra.Command { // Create a new context that includes the AZD access token. ctx := azdext.WithAccessToken(cmd.Context()) + if isDebug(cmd.Flags()) { + currentDate := time.Now().Format("2006-01-02") + logFileName := fmt.Sprintf("azd-ai-agents-%s.log", currentDate) + + logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + logFile = os.Stderr + } + azcorelog.SetListener(func(event azcorelog.Event, msg string) { + msg = azure.ConnectionStringJSONRegex.ReplaceAllString(msg, `${1}"REDACTED"`) + fmt.Fprintf(logFile, "[%s] %s: %s\n", time.Now().Format(time.RFC3339), event, msg) + }) + } + // Create a new AZD client. azdClient, err := azdext.NewAzdClient() if err != nil { @@ -42,7 +59,7 @@ func newListenCommand() *cobra.Command { // 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, isDebug(cmd.Flags())) + return project.NewAgentServiceTargetProvider(azdClient) }). WithProjectEventHandler("preprovision", func(ctx context.Context, args *azdext.ProjectEventArgs) error { return preprovisionHandler(ctx, azdClient, projectParser, args) diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go deleted file mode 100644 index 486c6d1543f..00000000000 --- a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/logging_policy.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package agent_api - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "maps" - "net/http" - "os" - "slices" - "time" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" -) - -// loggingPolicy is a custom pipeline policy that logs HTTP requests and responses -type loggingPolicy struct { - logFile *os.File -} - -// NewLoggingPolicy creates a new logging policy that writes to the specified file -func NewLoggingPolicy(logFile *os.File) policy.Policy { - return &loggingPolicy{logFile: logFile} -} - -// Do implements the policy.Policy interface -func (p *loggingPolicy) Do(req *policy.Request) (*http.Response, error) { - p.logRequest(req) - - resp, err := req.Next() - if err != nil { - p.logError(err) - return resp, err - } - - p.logResponse(resp) - - return resp, nil -} - -func (p *loggingPolicy) logRequest(req *policy.Request) { - if p.logFile == nil { - return - } - - fmt.Fprintf(p.logFile, "\n=== REQUEST [%s] ===\n", time.Now().Format(time.RFC3339)) - fmt.Fprintf(p.logFile, "%s %s\n", req.Raw().Method, req.Raw().URL.String()) - - // Log request body if present - if req.Raw().Body == nil { - fmt.Fprintf(p.logFile, "Body: (none)\n") - return - } - - body, err := io.ReadAll(req.Raw().Body) - // Always restore the body, even if read failed (restore what we got) - req.Raw().Body = io.NopCloser(bytes.NewReader(body)) - - if err != nil { - fmt.Fprintf(p.logFile, "Body: (error reading: %v)\n", err) - return - } - - if len(body) == 0 { - fmt.Fprintf(p.logFile, "Body: (empty)\n") - return - } - - fmt.Fprintf(p.logFile, "Body:\n") - var prettyPayload interface{} - if err := json.Unmarshal(body, &prettyPayload); err == nil { - prettyJSON, _ := json.MarshalIndent(prettyPayload, "", " ") - fmt.Fprintf(p.logFile, "%s\n", string(prettyJSON)) - } else { - fmt.Fprintf(p.logFile, "%s\n", string(body)) - } -} - -func (p *loggingPolicy) logResponse(resp *http.Response) { - if p.logFile == nil || resp == nil { - return - } - - fmt.Fprintf(p.logFile, "\n=== RESPONSE [%s] ===\n", time.Now().Format(time.RFC3339)) - fmt.Fprintf(p.logFile, "Status Code: %d\n", resp.StatusCode) - - fmt.Fprintf(p.logFile, "Headers:\n") - for _, key := range slices.Sorted(maps.Keys(resp.Header)) { - for _, value := range resp.Header[key] { - fmt.Fprintf(p.logFile, " %s: %s\n", key, value) - } - } - - // Read and log response body, then restore it - if resp.Body == nil { - fmt.Fprintf(p.logFile, "Body: (none)\n\n") - return - } - - body, err := io.ReadAll(resp.Body) - // Always restore the body, even if read failed (restore what we got) - resp.Body = io.NopCloser(bytes.NewReader(body)) - - if err != nil { - fmt.Fprintf(p.logFile, "Body: (error reading: %v)\n\n", err) - return - } - - fmt.Fprintf(p.logFile, "Body:\n") - if len(body) == 0 { - fmt.Fprintf(p.logFile, "(empty)\n\n") - return - } - - var jsonResponse interface{} - if err := json.Unmarshal(body, &jsonResponse); err == nil { - prettyJSON, _ := json.MarshalIndent(jsonResponse, "", " ") - fmt.Fprintf(p.logFile, "%s\n\n", string(prettyJSON)) - } else { - fmt.Fprintf(p.logFile, "%s\n\n", string(body)) - } -} - -func (p *loggingPolicy) logError(err error) { - if p.logFile == nil { - return - } - - fmt.Fprintf(p.logFile, "\n=== ERROR [%s] ===\n", time.Now().Format(time.RFC3339)) - fmt.Fprintf(p.logFile, "Error: %v\n\n", err) -} diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go index 0c0f59d6b1a..f5763244a5b 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go +++ b/cli/azd/extensions/azure.ai.agents/internal/pkg/agents/agent_api/operations.go @@ -11,7 +11,6 @@ import ( "io" "net/http" "net/url" - "os" "strconv" "azureaiagent/internal/version" @@ -29,53 +28,32 @@ type AgentClient struct { pipeline runtime.Pipeline } -// AgentClientOptions configures the AgentClient -type AgentClientOptions struct { - // Debug enables debug logging to ai-agents-debug.log - Debug bool -} - // NewAgentClient creates a new AgentClient -func NewAgentClient(endpoint string, cred azcore.TokenCredential, options *AgentClientOptions) *AgentClient { - var logFile *os.File - if options != nil && options.Debug { - var err error - logFile, err = os.OpenFile("ai-agents-debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - if err != nil { - // Fallback to stderr if file creation fails - logFile = os.Stderr - } - } - - return &AgentClient{ - endpoint: endpoint, - pipeline: createPipeline(cred, logFile), - } -} - -func createPipeline(cred azcore.TokenCredential, logFile *os.File) runtime.Pipeline { +func NewAgentClient(endpoint string, cred azcore.TokenCredential) *AgentClient { userAgent := fmt.Sprintf("azd-ext-azure-ai-agents/%s", version.Version) - perCallPolicies := []policy.Policy{ - runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil), - azsdk.NewMsCorrelationPolicy(), - azsdk.NewUserAgentPolicy(userAgent), - } - - if logFile != nil { - perCallPolicies = append(perCallPolicies, NewLoggingPolicy(logFile)) - } - clientOptions := &policy.ClientOptions{ - PerCallPolicies: perCallPolicies, + Logging: policy.LogOptions{ + IncludeBody: true, + }, + PerCallPolicies: []policy.Policy{ + runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil), + azsdk.NewMsCorrelationPolicy(), + azsdk.NewUserAgentPolicy(userAgent), + }, } - return runtime.NewPipeline( + pipeline := runtime.NewPipeline( "azure-ai-agents", "v1.0.0", runtime.PipelineOptions{}, clientOptions, ) + + return &AgentClient{ + endpoint: endpoint, + pipeline: pipeline, + } } // GetAgent retrieves a specific agent by name diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go new file mode 100644 index 00000000000..63fad0089c1 --- /dev/null +++ b/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go @@ -0,0 +1,5 @@ +package azure + +import "regexp" + +var ConnectionStringJSONRegex = regexp.MustCompile(`("[\w]*(?:CONNECTION_STRING|ConnectionString)":\s*)"[^"]*"`) diff --git a/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go b/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go index 6e4cc1522be..1bc1288fa1e 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go +++ b/cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go @@ -40,14 +40,12 @@ type AgentServiceTargetProvider struct { tenantId string env *azdext.Environment foundryProject *arm.ResourceID - debug bool } // NewAgentServiceTargetProvider creates a new AgentServiceTargetProvider instance -func NewAgentServiceTargetProvider(azdClient *azdext.AzdClient, debug bool) azdext.ServiceTargetProvider { +func NewAgentServiceTargetProvider(azdClient *azdext.AzdClient) azdext.ServiceTargetProvider { return &AgentServiceTargetProvider{ azdClient: azdClient, - debug: debug, } } @@ -683,7 +681,6 @@ func (p *AgentServiceTargetProvider) createAgent( agentClient := agent_api.NewAgentClient( azdEnv["AZURE_AI_PROJECT_ENDPOINT"], p.credential, - &agent_api.AgentClientOptions{Debug: p.debug}, ) // Use constant API version @@ -734,7 +731,6 @@ func (p *AgentServiceTargetProvider) startAgentContainer( agentClient := agent_api.NewAgentClient( azdEnv["AZURE_AI_PROJECT_ENDPOINT"], p.credential, - &agent_api.AgentClientOptions{Debug: p.debug}, ) var minReplicas, maxReplicas *int32 From c21ab6727579ea129c9faaa5b4fc21dfec93be1b Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 3 Dec 2025 01:34:32 +0000 Subject: [PATCH 3/3] Add copyright header --- .../extensions/azure.ai.agents/internal/pkg/azure/logging.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go b/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go index 63fad0089c1..fdbd74e3cdb 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go +++ b/cli/azd/extensions/azure.ai.agents/internal/pkg/azure/logging.go @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package azure import "regexp"