You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
InstrumentedAzureOpenAIClient does not override GetOpenAIResponseClient() or GetOpenAIResponseClient(string deploymentName). Since the instrumented client uses the protected no-arg (mock) constructor of AzureOpenAIClient rather than a real HTTP pipeline, calling either overload on the instrumented client will not correctly delegate to _client — making the Responses API effectively inaccessible through a Braintrust-wrapped Azure OpenAI client and producing no spans.
This is distinct from issue #30, which covers InstrumentedOpenAIClient (the plain OpenAI integration). InstrumentedAzureOpenAIClient is a separate class that also needs an override.
What is missing
AzureOpenAIClient (from Azure.AI.OpenAI v2.9.0-beta.1, the version pinned in this repo) exposes:
InstrumentedAzureOpenAIClient overrides several methods by delegating to _client, but GetOpenAIResponseClient is not among them:
// src/Braintrust.Sdk.AzureOpenAI/InstrumentedAzureOpenAIClient.cspublicoverrideChatClientGetChatClient(stringdeploymentName)=>InstrumentedChatClient.Create(_client.GetChatClient(deploymentName), ...);// ✅publicoverrideEmbeddingClientGetEmbeddingClient(stringdeploymentName)=>_client.GetEmbeddingClient(deploymentName);// ✅ (untraced, tracked in #71)// GetOpenAIResponseClient — ❌ not overridden at all
Because InstrumentedAzureOpenAIClient is constructed with:
:base()// uses the protected no-arg constructor (designed for mocking/subclassing)
…the inherited AzureOpenAIClient.GetOpenAIResponseClient() implementation (called when the method is not overridden) runs against an empty base-class state with no real HTTP pipeline, credentials, or Azure endpoint. The returned OpenAIResponseClient will be misconfigured and calls through it will fail.
The fix needs both a delegation to _client.GetOpenAIResponseClient(deploymentName) and wrapping the result with an instrumented client (analogous to how GetChatClient wraps the returned ChatClient with InstrumentedChatClient).
Impact
Any user who wraps an AzureOpenAIClient with BraintrustAzureOpenAI.WithBraintrust(client) and then calls GetOpenAIResponseClient() to use the Azure Responses API will get a broken client rather than a working but uninstrumented one. The Responses API on Azure OpenAI is documented as a production-ready endpoint for latest OpenAI models like gpt-4o and o1.
Braintrust docs status
not_found — the Braintrust Azure OpenAI integration docs document chat completion instrumentation but do not mention the Responses API. Issue #30 tracks the plain OpenAI side. This is the Azure-specific gap.
Upstream sources
Azure.AI.OpenAI v2.9.0-beta.1 (pinned in src/Braintrust.Sdk.AzureOpenAI/Braintrust.Sdk.AzureOpenAI.csproj): AzureOpenAIClient.GetOpenAIResponseClient(string deploymentName) → OpenAI.Responses.OpenAIResponseClient
src/Braintrust.Sdk.AzureOpenAI/InstrumentedAzureOpenAIClient.cs line 38: base() call uses the no-arg protected constructor, so unoverridden virtual methods run against an empty pipeline
Summary
InstrumentedAzureOpenAIClientdoes not overrideGetOpenAIResponseClient()orGetOpenAIResponseClient(string deploymentName). Since the instrumented client uses the protected no-arg (mock) constructor ofAzureOpenAIClientrather than a real HTTP pipeline, calling either overload on the instrumented client will not correctly delegate to_client— making the Responses API effectively inaccessible through a Braintrust-wrapped Azure OpenAI client and producing no spans.This is distinct from issue #30, which covers
InstrumentedOpenAIClient(the plain OpenAI integration).InstrumentedAzureOpenAIClientis a separate class that also needs an override.What is missing
AzureOpenAIClient(fromAzure.AI.OpenAIv2.9.0-beta.1, the version pinned in this repo) exposes:InstrumentedAzureOpenAIClientoverrides several methods by delegating to_client, butGetOpenAIResponseClientis not among them:Because
InstrumentedAzureOpenAIClientis constructed with:…the inherited
AzureOpenAIClient.GetOpenAIResponseClient()implementation (called when the method is not overridden) runs against an empty base-class state with no real HTTP pipeline, credentials, or Azure endpoint. The returnedOpenAIResponseClientwill be misconfigured and calls through it will fail.The fix needs both a delegation to
_client.GetOpenAIResponseClient(deploymentName)and wrapping the result with an instrumented client (analogous to howGetChatClientwraps the returnedChatClientwithInstrumentedChatClient).Impact
Any user who wraps an
AzureOpenAIClientwithBraintrustAzureOpenAI.WithBraintrust(client)and then callsGetOpenAIResponseClient()to use the Azure Responses API will get a broken client rather than a working but uninstrumented one. The Responses API on Azure OpenAI is documented as a production-ready endpoint for latest OpenAI models likegpt-4oando1.Braintrust docs status
not_found— the Braintrust Azure OpenAI integration docs document chat completion instrumentation but do not mention the Responses API. Issue #30 tracks the plain OpenAI side. This is the Azure-specific gap.Upstream sources
Azure.AI.OpenAIv2.9.0-beta.1 (pinned insrc/Braintrust.Sdk.AzureOpenAI/Braintrust.Sdk.AzureOpenAI.csproj):AzureOpenAIClient.GetOpenAIResponseClient(string deploymentName)→OpenAI.Responses.OpenAIResponseClientLocal files inspected
src/Braintrust.Sdk.AzureOpenAI/InstrumentedAzureOpenAIClient.cs— overridesGetChatClient,GetOpenAIFileClient,GetEmbeddingClient,GetImageClient,GetAudioClient;GetOpenAIResponseClientis absentsrc/Braintrust.Sdk.AzureOpenAI/InstrumentedAzureOpenAIClient.csline 38:base()call uses the no-arg protected constructor, so unoverridden virtual methods run against an empty pipelinesrc/Braintrust.Sdk.AzureOpenAI/Braintrust.Sdk.AzureOpenAI.csproj—Azure.AI.OpenAIv2.9.0-beta.1InstrumentedOpenAIClient.GetResponsesClient()for the plain OpenAI integrationEmbeddingClientcalls are not instrumented #71 coversInstrumentedAzureOpenAIClient.GetEmbeddingClient()(same delegation-without-instrumentation pattern)