Python: fix string parsing in azure openai client (#1023)

* fix string parsing in azure openai client

* test fix

* changed setup func name and check if there is a conn string

* redid parsing in safe way
This commit is contained in:
Eduard van Valkenburg
2025-09-30 21:33:12 +02:00
committed by GitHub
Unverified
parent f7e9490494
commit 3eb26632ce
9 changed files with 33 additions and 26 deletions
@@ -83,7 +83,7 @@ from azure.ai.agents.models import (
from azure.ai.projects.aio import AIProjectClient
from azure.ai.projects.models import ConnectionType
from azure.core.credentials_async import AsyncTokenCredential
from azure.core.exceptions import HttpResponseError
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
from pydantic import BaseModel, Field, PrivateAttr, ValidationError
if sys.version_info >= (3, 11):
@@ -216,17 +216,25 @@ class AzureAIAgentClient(BaseChatClient):
)
self._should_close_client = should_close_client
async def setup_observability(self) -> None:
async def setup_azure_ai_observability(self, enable_sensitive_data: bool | None = None) -> None:
"""Use this method to setup tracing in your Azure AI Project.
This will take the connection string from the project project_client.
It will override any connection string that is set in the environment variables.
It will disable any OTLP endpoint that might have been set.
"""
try:
conn_string = await self.project_client.telemetry.get_application_insights_connection_string()
except ResourceNotFoundError:
logger.warning(
"No Application Insights connection string found for the Azure AI Project, "
"please call setup_observability() manually."
)
return
from agent_framework.observability import setup_observability
setup_observability(
applicationinsights_connection_string=await self.project_client.telemetry.get_application_insights_connection_string(), # noqa: E501
applicationinsights_connection_string=conn_string, enable_sensitive_data=enable_sensitive_data
)
async def __aenter__(self) -> "Self":
@@ -4,8 +4,7 @@ from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, ClassVar
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from pydantic import ValidationError
from ..exceptions import ServiceInitializationError
from ..openai import OpenAIAssistantsClient
@@ -72,9 +71,10 @@ class AzureOpenAIAssistantsClient(OpenAIAssistantsClient):
"""
try:
azure_openai_settings = AzureOpenAISettings(
api_key=SecretStr(api_key) if api_key else None,
base_url=AnyUrl(base_url) if base_url else None,
endpoint=AnyUrl(endpoint) if endpoint else None,
# pydantic settings will see if there is a value, if not, will try the env var or .env file
api_key=api_key, # type: ignore
base_url=base_url, # type: ignore
endpoint=endpoint, # type: ignore
chat_deployment_name=deployment_name,
api_version=api_version,
env_file_path=env_file_path,
@@ -10,8 +10,7 @@ from azure.core.credentials import TokenCredential
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from openai.types.chat.chat_completion import Choice
from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from pydantic import ValidationError
from agent_framework import (
ChatResponse,
@@ -92,9 +91,10 @@ class AzureOpenAIChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient):
try:
# Filter out any None values from the arguments
azure_openai_settings = AzureOpenAISettings(
api_key=SecretStr(api_key) if api_key else None,
base_url=AnyUrl(base_url) if base_url else None,
endpoint=AnyUrl(endpoint) if endpoint else None,
# pydantic settings will see if there is a value, if not, will try the env var or .env file
api_key=api_key, # type: ignore
base_url=base_url, # type: ignore
endpoint=endpoint, # type: ignore
chat_deployment_name=deployment_name,
api_version=api_version,
env_file_path=env_file_path,
@@ -6,8 +6,7 @@ from urllib.parse import urljoin
from azure.core.credentials import TokenCredential
from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI
from pydantic import SecretStr, ValidationError
from pydantic.networks import AnyUrl
from pydantic import ValidationError
from agent_framework import use_chat_middleware, use_function_invocation
from agent_framework.exceptions import ServiceInitializationError
@@ -71,11 +70,11 @@ class AzureOpenAIResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClie
prompts could use `developer` or `system`. (Optional)
"""
try:
# Filter out any None values from the arguments
azure_openai_settings = AzureOpenAISettings(
api_key=SecretStr(api_key) if api_key else None,
base_url=AnyUrl(base_url) if base_url else None,
endpoint=AnyUrl(endpoint) if endpoint else None,
# pydantic settings will see if there is a value, if not, will try the env var or .env file
api_key=api_key, # type: ignore
base_url=base_url, # type: ignore
endpoint=endpoint, # type: ignore
responses_deployment_name=deployment_name,
api_version=api_version,
env_file_path=env_file_path,
@@ -89,9 +88,10 @@ class AzureOpenAIResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClie
if (
not azure_openai_settings.base_url
and azure_openai_settings.endpoint
and str(azure_openai_settings.endpoint).rstrip("/").endswith("openai.azure.com")
and azure_openai_settings.endpoint.host
and azure_openai_settings.endpoint.host.endswith(".openai.azure.com")
):
azure_openai_settings.base_url = AnyUrl(urljoin(str(azure_openai_settings.endpoint), "/openai/v1/"))
azure_openai_settings.base_url = urljoin(str(azure_openai_settings.endpoint), "/openai/v1/") # type: ignore
except ValidationError as exc:
raise ServiceInitializationError(f"Failed to validate settings: {exc}") from exc
@@ -131,7 +131,6 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None:
azure_chat_client = AzureOpenAIChatClient.from_dict(settings)
dumped_settings = azure_chat_client.to_dict()
assert dumped_settings["ai_model_id"] == settings["deployment_name"]
assert str(settings["endpoint"]) in str(dumped_settings["base_url"])
assert str(settings["deployment_name"]) in str(dumped_settings["base_url"])
assert settings["api_key"] == dumped_settings["api_key"]
assert settings["api_version"] == dumped_settings["api_version"]
@@ -38,7 +38,7 @@ async def main() -> None:
AzureAIAgentClient(async_credential=credential) as chat_client,
):
# enable azure-ai observability
await chat_client.setup_observability()
await chat_client.setup_azure_ai_observability()
agent = chat_client.create_agent(
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
@@ -50,7 +50,7 @@ async def main() -> None:
AzureAIAgentClient(async_credential=credential) as chat_client,
):
# enable azure-ai observability
await chat_client.setup_observability()
await chat_client.setup_azure_ai_observability()
agent = chat_client.create_agent(
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
@@ -47,7 +47,7 @@ async def main():
# This will enable tracing and configure the application to send telemetry data to the
# Application Insights instance attached to the Azure AI project.
# This will override any existing configuration.
await client.setup_observability()
await client.setup_azure_ai_observability()
questions = ["What's the weather in Amsterdam?", "and in Paris, and which is better?", "Why is the sky blue?"]
@@ -68,7 +68,7 @@ async def main() -> None:
# This will enable tracing and configure the application to send telemetry data to the
# Application Insights instance attached to the Azure AI project.
# This will override any existing configuration.
await client.setup_observability()
await client.setup_azure_ai_observability()
with get_tracer().start_as_current_span(
name="Foundry Telemetry from Agent Framework", kind=SpanKind.CLIENT