diff --git a/.github/workflows/python-merge-tests.yml b/.github/workflows/python-merge-tests.yml index 3c5e58ec7a..da231883c3 100644 --- a/.github/workflows/python-merge-tests.yml +++ b/.github/workflows/python-merge-tests.yml @@ -61,6 +61,9 @@ jobs: OPENAI_RESPONSES_MODEL_ID: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} LOCAL_MCP_URL: ${{ vars.LOCAL_MCP__URL }} + AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} PACKAGE_NAME: "main" defaults: run: @@ -76,6 +79,13 @@ jobs: env: # Configure a constant location for the uv cache UV_CACHE_DIR: /tmp/.uv-cache + - name: Azure CLI Login + if: github.event_name != 'pull_request' + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Test with pytest timeout-minutes: 10 run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml @@ -104,8 +114,8 @@ jobs: fail-on-empty: true title: Test results - python-tests-azure: - name: Python Tests - Azure + python-tests-azure-ai: + name: Python Tests - AzureAI needs: paths-filter if: github.event_name != 'pull_request' && needs.paths-filter.outputs.pythonChanges == 'true' runs-on: ${{ matrix.os }} @@ -118,10 +128,9 @@ jobs: environment: ["integration"] env: UV_PYTHON: ${{ matrix.python-version }} - AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} - AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} - AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} - PACKAGE_NAME: "azure" + AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }} + AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREAI__DEPLOYMENTNAME }} + PACKAGE_NAME: "azure-ai" defaults: run: working-directory: python @@ -150,7 +159,7 @@ jobs: - name: Test azure samples timeout-minutes: 10 if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "azure" + run: uv run pytest tests/samples/ -m "azure-ai" working-directory: ./python - name: Move coverage file run: | @@ -171,72 +180,6 @@ jobs: fail-on-empty: true title: Test results - python-tests-foundry: - name: Python Tests - Foundry - needs: paths-filter - if: github.event_name != 'pull_request' && needs.paths-filter.outputs.pythonChanges == 'true' - runs-on: ${{ matrix.os }} - environment: ${{ matrix.environment }} - strategy: - fail-fast: true - matrix: - python-version: ["3.10"] - os: [ubuntu-latest] - environment: ["integration"] - env: - UV_PYTHON: ${{ matrix.python-version }} - FOUNDRY_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }} - FOUNDRY_MODEL_DEPLOYMENT_NAME: ${{ vars.AZUREAI__DEPLOYMENTNAME }} - PACKAGE_NAME: "foundry" - defaults: - run: - working-directory: python - steps: - - uses: actions/checkout@v5 - - name: Set up python and install the project - id: python-setup - uses: ./.github/actions/python-setup - with: - python-version: ${{ matrix.python-version }} - os: ${{ runner.os }} - env: - # Configure a constant location for the uv cache - UV_CACHE_DIR: /tmp/.uv-cache - - name: Azure CLI Login - if: github.event_name != 'pull_request' - uses: azure/login@v2 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - name: Test with pytest - timeout-minutes: 10 - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml - working-directory: ./python - - name: Test foundry samples - timeout-minutes: 10 - if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "foundry" - working-directory: ./python - - name: Move coverage file - run: | - mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ env.PACKAGE_NAME }}.xml - working-directory: ./python - - name: Upload coverage artifact - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ env.PACKAGE_NAME }} - path: ./python/coverage_${{ env.PACKAGE_NAME }}.xml - - name: Surface failing tests - if: always() - uses: pmeier/pytest-results-action@v0.7.2 - with: - path: ./python/**.xml - summary: true - display-options: fEX - fail-on-empty: true - title: Test results - # TODO: Add python-tests-lab python-integration-tests-check: @@ -245,8 +188,7 @@ jobs: needs: [ python-tests-main, - python-tests-azure, - python-tests-foundry + python-tests-azure-ai ] steps: diff --git a/.github/workflows/python-test-coverage.yml b/.github/workflows/python-test-coverage.yml index 11b665adad..f4bb9e1473 100644 --- a/.github/workflows/python-test-coverage.yml +++ b/.github/workflows/python-test-coverage.yml @@ -36,7 +36,7 @@ jobs: # Configure a constant location for the uv cache UV_CACHE_DIR: /tmp/.uv-cache - name: Run all tests with coverage report - run: uv run poe all-tests -n logical --dist loadfile --dist worksteal --cov-report=xml:python-coverage.xml -q --junitxml=pytest.xml + run: uv run poe all-tests-cov --cov-report=xml:python-coverage.xml -q --junitxml=pytest.xml - name: Upload coverage report uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 0b979a1a7d..10ce37e472 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -36,100 +36,10 @@ jobs: env: # Configure a constant location for the uv cache UV_CACHE_DIR: /tmp/.uv-cache - # Main package tests - - name: Set environment variables - main - win - if: ${{ matrix.os == 'windows-latest' }} - run: | - echo "PACKAGE_NAME=main" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: Set environment variables - main - if: ${{ matrix.os != 'windows-latest' }} - run: | - echo "PACKAGE_NAME=main" >> $GITHUB_ENV - - name: Test with pytest - main - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml + # Unit tests + - name: Run all tests + run: uv run poe all-tests working-directory: ./python - - name: Move coverage file - main - run: | - mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - working-directory: ./python - - name: Upload coverage artifact - main - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} - path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - - # Azure package tests - - name: Set environment variables - azure - win - if: ${{ matrix.os == 'windows-latest' }} - run: | - echo "PACKAGE_NAME=azure" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: Set environment variables - azure - if: ${{ matrix.os != 'windows-latest' }} - run: | - echo "PACKAGE_NAME=azure" >> $GITHUB_ENV - - name: Test with pytest - azure - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml - working-directory: ./python - - name: Move coverage file - azure - run: | - mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - working-directory: ./python - - name: Upload coverage artifact - azure - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} - path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - - # Foundry package tests - - name: Set environment variables - foundry - win - if: ${{ matrix.os == 'windows-latest' }} - run: | - echo "PACKAGE_NAME=foundry" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: Set environment variables - foundry - if: ${{ matrix.os != 'windows-latest' }} - run: | - echo "PACKAGE_NAME=foundry" >> $GITHUB_ENV - - name: Test with pytest - foundry - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml - working-directory: ./python - - name: Move coverage file - foundry - run: | - mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - working-directory: ./python - - name: Upload coverage artifact - foundry - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} - path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml - - # Lab package tests - - name: Set environment variables - lab - win - if: ${{ matrix.os == 'windows-latest' }} - run: | - echo "PACKAGE_NAME=lab" | Out-File -FilePath $env:GITHUB_ENV -Append - - name: Set environment variables - lab - if: ${{ matrix.os != 'windows-latest' }} - run: | - echo "PACKAGE_NAME=lab" >> $GITHUB_ENV - - name: Test with pytest - lab - gaia - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }}/gaia test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml - working-directory: ./python - - name: Test with pytest - lab - tau2 - run: | - uv run poe --directory ./packages/${{ env.PACKAGE_NAME }}/tau2 setup-data - uv run poe --directory ./packages/${{ env.PACKAGE_NAME }}/tau2 test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml - uv run poe --directory ./packages/${{ env.PACKAGE_NAME }}/tau2 purge-data - working-directory: ./python - - name: Move coverage file - lab - run: | - mv ./packages/${{ env.PACKAGE_NAME }}/gaia/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}_gaia.xml - mv ./packages/${{ env.PACKAGE_NAME }}/tau2/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}_tau2.xml - working-directory: ./python - - name: Upload coverage artifact - lab - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} - path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}_*.xml # Surface failing tests - name: Surface failing tests @@ -139,5 +49,5 @@ jobs: path: ./python/**.xml summary: true display-options: fEX - fail-on-empty: true + fail-on-empty: false title: Test results diff --git a/python/.env.example b/python/.env.example index d26d44279a..291f0637a2 100644 --- a/python/.env.example +++ b/python/.env.example @@ -1,6 +1,6 @@ -# Foundry -FOUNDRY_PROJECT_ENDPOINT="" -FOUNDRY_MODEL_DEPLOYMENT_NAME="" +# Azure AI +AZURE_AI_PROJECT_ENDPOINT="" +AZURE_AI_MODEL_DEPLOYMENT_NAME="" # OpenAI OPENAI_API_KEY="" OPENAI_CHAT_MODEL_ID="" diff --git a/python/DEV_SETUP.md b/python/DEV_SETUP.md index a041712443..90afcf66fa 100644 --- a/python/DEV_SETUP.md +++ b/python/DEV_SETUP.md @@ -275,7 +275,7 @@ The package follows a flat import structure: - **Connectors**: Import from `agent_framework.` ```python from agent_framework.openai import OpenAIChatClient - from agent_framework.azure import AzureChatClient + from agent_framework.azure import AzureOpenAIChatClient ``` ## Testing diff --git a/python/README.md b/python/README.md index c5f3b61a97..8ac85a4538 100644 --- a/python/README.md +++ b/python/README.md @@ -5,12 +5,14 @@ ```bash # Base package including workflow support pip install agent-framework -# Optional: Add Azure integration -pip install agent-framework[azure] -# Optional: Add Foundry integration -pip install agent-framework[foundry] +# Optional: Add Azure AI integration (Foundry) +pip install agent-framework[azure-ai] +# Optional: Add Microsoft integrations, currently only CopilotStudioAgents +pip install agent-framework[microsoft] # Optional: Both -pip install agent-framework[azure,foundry] +pip install agent-framework[microsoft,azure-ai] +# All-in-one +pip install agent-framework[all] ``` Supported Platforms: @@ -29,16 +31,16 @@ AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=... AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=... ... -FOUNDRY_PROJECT_ENDPOINT=... -FOUNDRY_MODEL_DEPLOYMENT_NAME=... +AZURE_AI_PROJECT_ENDPOINT=... +AZURE_AI_MODEL_DEPLOYMENT_NAME=... ``` You can also override environment variables by explicitly passing configuration parameters to the chat client constructor: ```python -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient -chat_client = AzureChatClient( +chat_client = AzureOpenAIChatClient( api_key='', endpoint='', deployment_name='', @@ -215,8 +217,8 @@ if __name__ == "__main__": - [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage - [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns -- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration -- [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET) +- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration +- [Workflow Samples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/workflow): Advanced multi-agent patterns ## Agent Framework Documentation diff --git a/python/packages/azure/LICENSE b/python/packages/azure-ai/LICENSE similarity index 100% rename from python/packages/azure/LICENSE rename to python/packages/azure-ai/LICENSE diff --git a/python/packages/azure/README.md b/python/packages/azure-ai/README.md similarity index 67% rename from python/packages/azure/README.md rename to python/packages/azure-ai/README.md index a1a5e05847..5b4a4740a0 100644 --- a/python/packages/azure/README.md +++ b/python/packages/azure-ai/README.md @@ -1,9 +1,9 @@ -# Get Started with Microsoft Agent Framework Azure +# Get Started with Microsoft Agent Framework Azure AI Please install this package as the extra for `agent-framework`: ```bash -pip install agent-framework[azure] +pip install agent-framework[azure_ai] ``` and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information. diff --git a/python/packages/foundry/agent_framework_foundry/__init__.py b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py similarity index 71% rename from python/packages/foundry/agent_framework_foundry/__init__.py rename to python/packages/azure-ai/agent_framework_azure_ai/__init__.py index f81b7faa16..cbd836099e 100644 --- a/python/packages/foundry/agent_framework_foundry/__init__.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py @@ -2,7 +2,7 @@ import importlib.metadata -from ._chat_client import FoundryChatClient, FoundrySettings +from ._chat_client import AzureAIAgentClient, AzureAISettings try: __version__ = importlib.metadata.version(__name__) @@ -10,7 +10,7 @@ except importlib.metadata.PackageNotFoundError: __version__ = "0.0.0" # Fallback for development mode __all__ = [ - "FoundryChatClient", - "FoundrySettings", + "AzureAIAgentClient", + "AzureAISettings", "__version__", ] diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py similarity index 88% rename from python/packages/foundry/agent_framework_foundry/_chat_client.py rename to python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py index 34d913a7f8..abf28dbc8d 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_chat_client.py @@ -92,46 +92,42 @@ else: from typing_extensions import Self # pragma: no cover -logger = get_logger("agent_framework.foundry") +logger = get_logger("agent_framework.azure") -class FoundrySettings(AFBaseSettings): - """Foundry model settings. +class AzureAISettings(AFBaseSettings): + """Azure AI Project settings. - The settings are first loaded from environment variables with the prefix 'FOUNDRY_'. + The settings are first loaded from environment variables with the prefix 'AZURE_AI_'. If the environment variables are not found, the settings can be loaded from a .env file with the encoding 'utf-8'. If the settings are not found in the .env file, the settings are ignored; however, validation will fail alerting that the settings are missing. - Attributes: - project_endpoint: The Azure AI Foundry project endpoint URL. - (Env var FOUNDRY_PROJECT_ENDPOINT) + Args: + project_endpoint: The Azure AI Project endpoint URL. + (Env var AZURE_AI_PROJECT_ENDPOINT) model_deployment_name: The name of the model deployment to use. - (Env var FOUNDRY_MODEL_DEPLOYMENT_NAME) - agent_name: Default name for automatically created agents. - (Env var FOUNDRY_AGENT_NAME) - Parameters: + (Env var AZURE_AI_MODEL_DEPLOYMENT_NAME) env_file_path: If provided, the .env settings are read from this file path location. env_file_encoding: The encoding of the .env file, defaults to 'utf-8'. """ - env_prefix: ClassVar[str] = "FOUNDRY_" + env_prefix: ClassVar[str] = "AZURE_AI_" project_endpoint: str | None = None model_deployment_name: str | None = None - agent_name: str | None = "UnnamedAgent" -TFoundryChatClient = TypeVar("TFoundryChatClient", bound="FoundryChatClient") +TAzureAIAgentClient = TypeVar("TAzureAIAgentClient", bound="AzureAIAgentClient") @use_function_invocation @use_observability -class FoundryChatClient(BaseChatClient): - """Azure AI Foundry Chat client.""" +class AzureAIAgentClient(BaseChatClient): + """Azure AI Agent Chat client.""" - OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai.foundry" # type: ignore[reportIncompatibleVariableOverride, misc] - client: AIProjectClient = Field(...) + OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai" # type: ignore[reportIncompatibleVariableOverride, misc] + project_client: AIProjectClient = Field(...) credential: AsyncTokenCredential | None = Field(...) agent_id: str | None = Field(default=None) agent_name: str | None = Field(default=None) @@ -143,7 +139,7 @@ class FoundryChatClient(BaseChatClient): def __init__( self, *, - client: AIProjectClient | None = None, + project_client: AIProjectClient | None = None, agent_id: str | None = None, agent_name: str | None = None, thread_id: str | None = None, @@ -154,82 +150,83 @@ class FoundryChatClient(BaseChatClient): env_file_encoding: str | None = None, **kwargs: Any, ) -> None: - """Initialize a FoundryChatClient. + """Initialize a AzureAIAgentClient. Args: - client: An existing AIProjectClient to use. If not provided, one will be created. - agent_id: The ID of an existing agent to use. If not provided and client is provided, - a new agent will be created (and deleted after the request). If neither client + project_client: An existing AIProjectClient to use. If not provided, one will be created. + agent_id: The ID of an existing agent to use. If not provided and project_client is provided, + a new agent will be created (and deleted after the request). If neither project_client nor agent_id is provided, both will be created and managed automatically. agent_name: The name to use when creating new agents. thread_id: Default thread ID to use for conversations. Can be overridden by conversation_id property, when making a request. - project_endpoint: The Azure AI Foundry project endpoint URL. Used if client is not provided. + project_endpoint: The Azure AI Project endpoint URL, can also be set via + 'AZURE_AI_PROJECT_ENDPOINT' environment variable. Is ignored when a project_client is passed. model_deployment_name: The model deployment name to use for agent creation. + Can also be set via 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable. async_credential: Azure async credential to use for authentication. - setup_tracing: Whether to setup tracing for the client. Defaults to True. + setup_tracing: Whether to setup tracing for the project_client. Defaults to True. env_file_path: Path to environment file for loading settings. env_file_encoding: Encoding of the environment file. **kwargs: Additional keyword arguments passed to the parent class. """ try: - foundry_settings = FoundrySettings( + azure_ai_settings = AzureAISettings( project_endpoint=project_endpoint, model_deployment_name=model_deployment_name, - agent_name=agent_name, env_file_path=env_file_path, env_file_encoding=env_file_encoding, ) except ValidationError as ex: - raise ServiceInitializationError("Failed to create Foundry settings.", ex) from ex + raise ServiceInitializationError("Failed to create Azure AI settings.", ex) from ex - # If no client is provided, create one + # If no project_client is provided, create one should_close_client = False - if client is None: - if not foundry_settings.project_endpoint: + if project_client is None: + if not azure_ai_settings.project_endpoint: raise ServiceInitializationError( - "Foundry project endpoint is required. Set via 'project_endpoint' parameter " - "or 'FOUNDRY_PROJECT_ENDPOINT' environment variable." + "Azure AI project endpoint is required. Set via 'project_endpoint' parameter " + "or 'AZURE_AI_PROJECT_ENDPOINT' environment variable." ) - if agent_id is None and not foundry_settings.model_deployment_name: + if agent_id is None and not azure_ai_settings.model_deployment_name: raise ServiceInitializationError( - "Foundry model deployment name is required. Set via 'model_deployment_name' parameter " - "or 'FOUNDRY_MODEL_DEPLOYMENT_NAME' environment variable." + "Azure AI model deployment name is required. Set via 'model_deployment_name' parameter " + "or 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable." ) # Use provided credential if not async_credential: - raise ServiceInitializationError("Azure credential is required when client is not provided.") - client = AIProjectClient( - endpoint=foundry_settings.project_endpoint, + raise ServiceInitializationError("Azure credential is required when project_client is not provided.") + project_client = AIProjectClient( + endpoint=azure_ai_settings.project_endpoint, credential=async_credential, user_agent=AGENT_FRAMEWORK_USER_AGENT, ) should_close_client = True super().__init__( - client=client, # type: ignore[reportCallIssue] + project_client=project_client, # type: ignore[reportCallIssue] credential=async_credential, # type: ignore[reportCallIssue] agent_id=agent_id, # type: ignore[reportCallIssue] thread_id=thread_id, # type: ignore[reportCallIssue] - agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue] - ai_model_id=foundry_settings.model_deployment_name, # type: ignore[reportCallIssue] + agent_name=agent_name, # type: ignore[reportCallIssue] + ai_model_id=azure_ai_settings.model_deployment_name, # type: ignore[reportCallIssue] **kwargs, ) self._should_close_client = should_close_client - async def setup_foundry_observability(self, enable_live_metrics: bool = False) -> None: - """Call this method to setup tracing with Foundry. + async def setup_observability(self, enable_live_metrics: bool = False) -> None: + """Use this method to setup tracing in your Azure AI Project. - This will take the connection string from the project client. + 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. """ from agent_framework.observability import setup_observability setup_observability( - applicationinsights_connection_string=await self.client.telemetry.get_application_insights_connection_string(), # noqa: E501 + applicationinsights_connection_string=await self.project_client.telemetry.get_application_insights_connection_string(), # noqa: E501 enable_live_metrics=enable_live_metrics, ) @@ -242,19 +239,19 @@ class FoundryChatClient(BaseChatClient): await self.close() async def close(self) -> None: - """Close the client and clean up any agents we created.""" + """Close the project_client and clean up any agents we created.""" await self._cleanup_agent_if_needed() await self._close_client_if_needed() @classmethod - def from_dict(cls: type[TFoundryChatClient], settings: dict[str, Any]) -> TFoundryChatClient: - """Initialize a FoundryChatClient from a dictionary of settings. + def from_dict(cls: type[TAzureAIAgentClient], settings: dict[str, Any]) -> TAzureAIAgentClient: + """Initialize a AzureAIAgentClient from a dictionary of settings. Args: settings: A dictionary of settings for the service. """ return cls( - client=settings.get("client"), + project_client=settings.get("project_client"), agent_id=settings.get("agent_id"), thread_id=settings.get("thread_id"), project_endpoint=settings.get("project_endpoint"), @@ -315,8 +312,8 @@ class FoundryChatClient(BaseChatClient): if not self.ai_model_id: raise ServiceInitializationError("Model deployment name is required for agent creation.") - agent_name = self.agent_name - args = {"model": self.ai_model_id, "name": agent_name} + agent_name: str = self.agent_name or "UnnamedAgent" + args: dict[str, Any] = {"model": self.ai_model_id, "name": agent_name} if run_options: if "tools" in run_options: args["tools"] = run_options["tools"] @@ -324,8 +321,8 @@ class FoundryChatClient(BaseChatClient): args["instructions"] = run_options["instructions"] if "response_format" in run_options: args["response_format"] = run_options["response_format"] - created_agent = await self.client.agents.create_agent(**args) # type: ignore[arg-type] - self.agent_id = created_agent.id + created_agent = await self.project_client.agents.create_agent(**args) + self.agent_id = str(created_agent.id) self._should_delete_agent = True return self.agent_id @@ -367,7 +364,7 @@ class FoundryChatClient(BaseChatClient): args["tool_outputs"] = tool_outputs if tool_approvals: args["tool_approvals"] = tool_approvals - await self.client.agents.runs.submit_tool_outputs_stream(**args) # type: ignore[reportUnknownMemberType] + await self.project_client.agents.runs.submit_tool_outputs_stream(**args) # type: ignore[reportUnknownMemberType] # Pass the handler to the stream to continue processing stream = handler # type: ignore final_thread_id = thread_run.thread_id @@ -377,7 +374,7 @@ class FoundryChatClient(BaseChatClient): # Now create a new run and stream the results. run_options.pop("conversation_id", None) - stream = await self.client.agents.runs.stream( # type: ignore[reportUnknownMemberType] + stream = await self.project_client.agents.runs.stream( # type: ignore[reportUnknownMemberType] final_thread_id, agent_id=agent_id, **run_options ) @@ -388,7 +385,9 @@ class FoundryChatClient(BaseChatClient): if thread_id is None: return None - async for run in self.client.agents.runs.list(thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING): # type: ignore[reportUnknownMemberType] + async for run in self.project_client.agents.runs.list( + thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING + ): # type: ignore[reportUnknownMemberType] if run.status not in [ RunStatus.COMPLETED, RunStatus.CANCELLED, @@ -405,12 +404,12 @@ class FoundryChatClient(BaseChatClient): if thread_id is not None: if thread_run is not None: # There was an active run; we need to cancel it before starting a new run. - await self.client.agents.runs.cancel(thread_id, thread_run.id) + await self.project_client.agents.runs.cancel(thread_id, thread_run.id) return thread_id # No thread ID was provided, so create a new thread. - thread = await self.client.agents.threads.create( + thread = await self.project_client.agents.threads.create( tool_resources=run_options.get("tool_resources"), metadata=run_options.get("metadata") ) thread_id = thread.id @@ -419,7 +418,7 @@ class FoundryChatClient(BaseChatClient): # once fixed, in the function above, readd: # `messages=run_options.pop("additional_messages")` for msg in run_options.pop("additional_messages", []): - await self.client.agents.messages.create( + await self.project_client.agents.messages.create( thread_id=thread_id, role=msg.role, content=msg.content, metadata=msg.metadata ) # and remove until here. @@ -607,14 +606,14 @@ class FoundryChatClient(BaseChatClient): return [] async def _close_client_if_needed(self) -> None: - """Close client session if we created it.""" + """Close project_client session if we created it.""" if self._should_close_client: - await self.client.close() + await self.project_client.close() async def _cleanup_agent_if_needed(self) -> None: """Clean up the agent if we created it.""" if self._should_delete_agent and self.agent_id is not None: - await self.client.agents.delete_agent(self.agent_id) + await self.project_client.agents.delete_agent(self.agent_id) self.agent_id = None self._should_delete_agent = False @@ -666,7 +665,7 @@ class FoundryChatClient(BaseChatClient): additional_messages: list[ThreadMessageOptions] | None = None - # System/developer messages are turned into instructions, since there is no such message roles in Foundry. + # System/developer messages are turned into instructions, since there is no such message roles in Azure AI. # All other messages are added 1:1, treating assistant messages as agent messages # and everything else as user messages. for chat_message in messages: @@ -742,10 +741,13 @@ class FoundryChatClient(BaseChatClient): bing_search = BingGroundingTool(connection_id=connection_id, **config_args) if custom_connection_name and custom_configuration_name: try: - bing_custom_connection = await self.client.connections.get(name=custom_connection_name) + bing_custom_connection = await self.project_client.connections.get( + name=custom_connection_name + ) except HttpResponseError as err: raise ServiceInitializationError( - f"Bing custom connection '{custom_connection_name}' not found in Foundry.", err + f"Bing custom connection '{custom_connection_name}' not found in the Azure AI Project.", + err, ) from err else: bing_search = BingCustomSearchTool( @@ -781,15 +783,18 @@ class FoundryChatClient(BaseChatClient): index_name = additional_props.get("index_name") or os.getenv("AZURE_AI_SEARCH_INDEX_NAME") if not index_name: raise ServiceInitializationError( - "File search tool requires at least one vector store input, for file search in Foundry " + "File search tool requires at least one vector store input, " + "for file search in the Azure AI Project " "or an 'index_name' to use Azure AI Search, " "in additional_properties or environment variable 'AZURE_AI_SEARCH_INDEX_NAME'." ) try: - azs_conn_id = await self.client.connections.get_default(ConnectionType.AZURE_AI_SEARCH) + azs_conn_id = await self.project_client.connections.get_default( + ConnectionType.AZURE_AI_SEARCH + ) except HttpResponseError as err: raise ServiceInitializationError( - "No default Azure AI Search connection found in Foundry. " + "No default Azure AI Search connection found in the Azure AI Project. " "Please create one or provide vector store inputs for the file search tool.", err, ) from err @@ -890,4 +895,4 @@ class FoundryChatClient(BaseChatClient): Returns: The service URL for the chat client, or None if not set. """ - return self.client._config.endpoint + return self.project_client._config.endpoint diff --git a/python/packages/azure/agent_framework_azure/py.typed b/python/packages/azure-ai/agent_framework_azure_ai/py.typed similarity index 100% rename from python/packages/azure/agent_framework_azure/py.typed rename to python/packages/azure-ai/agent_framework_azure_ai/py.typed diff --git a/python/packages/foundry/pyproject.toml b/python/packages/azure-ai/pyproject.toml similarity index 90% rename from python/packages/foundry/pyproject.toml rename to python/packages/azure-ai/pyproject.toml index a7aa1cf86a..1f5810c284 100644 --- a/python/packages/foundry/pyproject.toml +++ b/python/packages/azure-ai/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "agent-framework-foundry" +name = "agent-framework-azure-ai" description = "Azure AI Foundry integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" @@ -74,15 +74,15 @@ disallow_incomplete_defs = true disallow_untyped_decorators = true [tool.bandit] -targets = ["agent_framework_foundry"] +targets = ["agent_framework_azure_ai"] exclude_dirs = ["tests"] [tool.poe] executor.type = "uv" include = "../../shared_tasks.toml" [tool.poe.tasks] -mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry" -test = "pytest --cov=agent_framework_foundry --cov-report=term-missing:skip-covered tests" +mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai" +test = "pytest --cov=agent_framework_azure_ai --cov-report=term-missing:skip-covered tests" [build-system] requires = ["flit-core >= 3.9,<4.0"] diff --git a/python/packages/foundry/tests/conftest.py b/python/packages/azure-ai/tests/conftest.py similarity index 85% rename from python/packages/foundry/tests/conftest.py rename to python/packages/azure-ai/tests/conftest.py index eb858f7a1b..13764c24f0 100644 --- a/python/packages/foundry/tests/conftest.py +++ b/python/packages/azure-ai/tests/conftest.py @@ -18,8 +18,8 @@ def override_env_param_dict(request: Any) -> dict[str, str]: @fixture() -def foundry_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # type: ignore - """Fixture to set environment variables for FoundrySettings.""" +def azure_ai_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # type: ignore + """Fixture to set environment variables for AzureAISettings.""" if exclude_list is None: exclude_list = [] @@ -28,9 +28,8 @@ def foundry_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): override_env_param_dict = {} env_vars = { - "FOUNDRY_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/", - "FOUNDRY_MODEL_DEPLOYMENT_NAME": "test-gpt-4o", - "FOUNDRY_AGENT_NAME": "TestAgent", + "AZURE_AI_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/", + "AZURE_AI_MODEL_DEPLOYMENT_NAME": "test-gpt-4o", } env_vars.update(override_env_param_dict) # type: ignore diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py similarity index 64% rename from python/packages/foundry/tests/test_foundry_chat_client.py rename to python/packages/azure-ai/tests/test_azure_ai_agent_client.py index f591f86d7e..8c23e209bd 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/azure-ai/tests/test_azure_ai_agent_client.py @@ -23,8 +23,8 @@ from agent_framework import ( TextContent, UriContent, ) +from agent_framework.azure import AzureAIAgentClient, AzureAISettings from agent_framework.exceptions import ServiceInitializationError -from agent_framework.foundry import FoundryChatClient, FoundrySettings from azure.ai.agents.models import ( RequiredFunctionToolCall, SubmitToolOutputsAction, @@ -34,95 +34,91 @@ from azure.core.credentials_async import AsyncTokenCredential from azure.identity.aio import AzureCliCredential from pydantic import Field, ValidationError -skip_if_foundry_integration_tests_disabled = pytest.mark.skipif( +skip_if_azure_ai_integration_tests_disabled = pytest.mark.skipif( os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true" - or os.getenv("FOUNDRY_PROJECT_ENDPOINT", "") in ("", "https://test-project.cognitiveservices.azure.com/"), - reason="No real FOUNDRY_PROJECT_ENDPOINT provided; skipping integration tests." + or os.getenv("AZURE_AI_PROJECT_ENDPOINT", "") in ("", "https://test-project.cognitiveservices.azure.com/"), + reason="No real AZURE_AI_PROJECT_ENDPOINT provided; skipping integration tests." if os.getenv("RUN_INTEGRATION_TESTS", "false").lower() == "true" else "Integration tests are disabled.", ) -def create_test_foundry_chat_client( +def create_test_azure_ai_chat_client( mock_ai_project_client: MagicMock, agent_id: str | None = None, thread_id: str | None = None, - foundry_settings: FoundrySettings | None = None, + azure_ai_settings: AzureAISettings | None = None, should_delete_agent: bool = False, -) -> FoundryChatClient: - """Helper function to create FoundryChatClient instances for testing, bypassing Pydantic validation.""" - if foundry_settings is None: - foundry_settings = FoundrySettings(env_file_path="test.env") +) -> AzureAIAgentClient: + """Helper function to create AzureAIAgentClient instances for testing, bypassing Pydantic validation.""" + if azure_ai_settings is None: + azure_ai_settings = AzureAISettings(env_file_path="test.env") - return FoundryChatClient.model_construct( - client=mock_ai_project_client, + return AzureAIAgentClient.model_construct( + project_client=mock_ai_project_client, agent_id=agent_id, thread_id=thread_id, _should_delete_agent=should_delete_agent, - agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue] - ai_model_id=foundry_settings.model_deployment_name, + ai_model_id=azure_ai_settings.model_deployment_name, ) -def test_foundry_settings_init(foundry_unit_test_env: dict[str, str]) -> None: - """Test FoundrySettings initialization.""" - settings = FoundrySettings() +def test_azure_ai_settings_init(azure_ai_unit_test_env: dict[str, str]) -> None: + """Test AzureAISettings initialization.""" + settings = AzureAISettings() - assert settings.project_endpoint == foundry_unit_test_env["FOUNDRY_PROJECT_ENDPOINT"] - assert settings.model_deployment_name == foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"] - assert settings.agent_name == foundry_unit_test_env["FOUNDRY_AGENT_NAME"] + assert settings.project_endpoint == azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"] + assert settings.model_deployment_name == azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"] -def test_foundry_settings_init_with_explicit_values() -> None: - """Test FoundrySettings initialization with explicit values.""" - settings = FoundrySettings( +def test_azure_ai_settings_init_with_explicit_values() -> None: + """Test AzureAISettings initialization with explicit values.""" + settings = AzureAISettings( project_endpoint="https://custom-endpoint.com/", model_deployment_name="custom-model", - agent_name="CustomAgent", ) assert settings.project_endpoint == "https://custom-endpoint.com/" assert settings.model_deployment_name == "custom-model" - assert settings.agent_name == "CustomAgent" -def test_foundry_chat_client_init_with_client(mock_ai_project_client: MagicMock) -> None: - """Test FoundryChatClient initialization with existing client.""" - chat_client = create_test_foundry_chat_client( +def test_azure_ai_chat_client_init_with_client(mock_ai_project_client: MagicMock) -> None: + """Test AzureAIAgentClient initialization with existing project_client.""" + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="existing-agent-id", thread_id="test-thread-id" ) - assert chat_client.client is mock_ai_project_client + assert chat_client.project_client is mock_ai_project_client assert chat_client.agent_id == "existing-agent-id" assert chat_client.thread_id == "test-thread-id" assert not chat_client._should_delete_agent # type: ignore assert isinstance(chat_client, ChatClientProtocol) -def test_foundry_chat_client_init_auto_create_client( - foundry_unit_test_env: dict[str, str], +def test_azure_ai_chat_client_init_auto_create_client( + azure_ai_unit_test_env: dict[str, str], mock_ai_project_client: MagicMock, ) -> None: - """Test FoundryChatClient initialization with auto-created client.""" - foundry_settings = FoundrySettings(**foundry_unit_test_env) # type: ignore - chat_client = FoundryChatClient.model_construct( - client=mock_ai_project_client, + """Test AzureAIAgentClient initialization with auto-created project_client.""" + azure_ai_settings = AzureAISettings(**azure_ai_unit_test_env) # type: ignore + chat_client = AzureAIAgentClient.model_construct( + project_client=mock_ai_project_client, agent_id=None, thread_id=None, _should_delete_agent=False, - _foundry_settings=foundry_settings, + _azure_ai_settings=azure_ai_settings, credential=None, ) - assert chat_client.client is mock_ai_project_client + assert chat_client.project_client is mock_ai_project_client assert chat_client.agent_id is None assert not chat_client._should_delete_agent # type: ignore -def test_foundry_chat_client_init_missing_project_endpoint() -> None: - """Test FoundryChatClient initialization when project_endpoint is missing and no client provided.""" - # Mock FoundrySettings to return settings with None project_endpoint - with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings: +def test_azure_ai_chat_client_init_missing_project_endpoint() -> None: + """Test AzureAIAgentClient initialization when project_endpoint is missing and no project_client provided.""" + # Mock AzureAISettings to return settings with None project_endpoint + with patch("agent_framework_azure_ai._chat_client.AzureAISettings") as mock_settings: mock_settings_instance = MagicMock() mock_settings_instance.project_endpoint = None # This should trigger the error mock_settings_instance.model_deployment_name = "test-model" @@ -130,8 +126,8 @@ def test_foundry_chat_client_init_missing_project_endpoint() -> None: mock_settings.return_value = mock_settings_instance with pytest.raises(ServiceInitializationError, match="project endpoint is required"): - FoundryChatClient( - client=None, + AzureAIAgentClient( + project_client=None, agent_id=None, project_endpoint=None, # Missing endpoint model_deployment_name="test-model", @@ -139,10 +135,10 @@ def test_foundry_chat_client_init_missing_project_endpoint() -> None: ) -def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation() -> None: - """Test FoundryChatClient initialization when model deployment is missing for agent creation.""" - # Mock FoundrySettings to return settings with None model_deployment_name - with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings: +def test_azure_ai_chat_client_init_missing_model_deployment_for_agent_creation() -> None: + """Test AzureAIAgentClient initialization when model deployment is missing for agent creation.""" + # Mock AzureAISettings to return settings with None model_deployment_name + with patch("agent_framework_azure_ai._chat_client.AzureAISettings") as mock_settings: mock_settings_instance = MagicMock() mock_settings_instance.project_endpoint = "https://test.com" mock_settings_instance.model_deployment_name = None # This should trigger the error @@ -150,8 +146,8 @@ def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation() mock_settings.return_value = mock_settings_instance with pytest.raises(ServiceInitializationError, match="model deployment name is required"): - FoundryChatClient( - client=None, + AzureAIAgentClient( + project_client=None, agent_id=None, # No existing agent project_endpoint="https://test.com", model_deployment_name=None, # Missing for agent creation @@ -159,10 +155,10 @@ def test_foundry_chat_client_init_missing_model_deployment_for_agent_creation() ) -def test_foundry_chat_client_from_dict(mock_ai_project_client: MagicMock) -> None: - """Test FoundryChatClient.from_dict method.""" +def test_azure_ai_chat_client_from_dict(mock_ai_project_client: MagicMock) -> None: + """Test AzureAIAgentClient.from_dict method.""" settings = { - "client": mock_ai_project_client, + "project_client": mock_ai_project_client, "agent_id": "test-agent-id", "thread_id": "test-thread-id", "project_endpoint": "https://test-endpoint.com/", @@ -170,55 +166,57 @@ def test_foundry_chat_client_from_dict(mock_ai_project_client: MagicMock) -> Non "agent_name": "TestAgent", } - foundry_settings = FoundrySettings( + azure_ai_settings = AzureAISettings( project_endpoint=settings["project_endpoint"], model_deployment_name=settings["model_deployment_name"], agent_name=settings["agent_name"], ) - chat_client: FoundryChatClient = create_test_foundry_chat_client( + chat_client: AzureAIAgentClient = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id=settings["agent_id"], # type: ignore thread_id=settings["thread_id"], # type: ignore - foundry_settings=foundry_settings, + azure_ai_settings=azure_ai_settings, ) - assert chat_client.client is mock_ai_project_client + assert chat_client.project_client is mock_ai_project_client assert chat_client.agent_id == "test-agent-id" assert chat_client.thread_id == "test-thread-id" -def test_foundry_chat_client_init_missing_credential(foundry_unit_test_env: dict[str, str]) -> None: - """Test FoundryChatClient.__init__ when async_credential is missing and no client provided.""" - with pytest.raises(ServiceInitializationError, match="Azure credential is required when client is not provided"): - FoundryChatClient( - client=None, +def test_azure_ai_chat_client_init_missing_credential(azure_ai_unit_test_env: dict[str, str]) -> None: + """Test AzureAIAgentClient.__init__ when async_credential is missing and no project_client provided.""" + with pytest.raises( + ServiceInitializationError, match="Azure credential is required when project_client is not provided" + ): + AzureAIAgentClient( + project_client=None, agent_id="existing-agent", - project_endpoint=foundry_unit_test_env["FOUNDRY_PROJECT_ENDPOINT"], - model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"], + project_endpoint=azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"], + model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"], async_credential=None, # Missing credential ) -def test_foundry_chat_client_init_validation_error(mock_azure_credential: MagicMock) -> None: - """Test that ValidationError in FoundrySettings is properly handled.""" - with patch("agent_framework_foundry._chat_client.FoundrySettings") as mock_settings: +def test_azure_ai_chat_client_init_validation_error(mock_azure_credential: MagicMock) -> None: + """Test that ValidationError in AzureAISettings is properly handled.""" + with patch("agent_framework_azure_ai._chat_client.AzureAISettings") as mock_settings: # Create a proper ValidationError with empty errors list and model dict - mock_settings.side_effect = ValidationError.from_exception_data("FoundrySettings", []) + mock_settings.side_effect = ValidationError.from_exception_data("AzureAISettings", []) - with pytest.raises(ServiceInitializationError, match="Failed to create Foundry settings"): - FoundryChatClient( + with pytest.raises(ServiceInitializationError, match="Failed to create Azure AI settings."): + AzureAIAgentClient( project_endpoint="https://test.com", model_deployment_name="test-model", async_credential=mock_azure_credential, ) -async def test_foundry_chat_client_get_agent_id_or_create_existing_agent( +async def test_azure_ai_chat_client_get_agent_id_or_create_existing_agent( mock_ai_project_client: MagicMock, ) -> None: """Test _get_agent_id_or_create when agent_id is already provided.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="existing-agent-id") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="existing-agent-id") agent_id = await chat_client._get_agent_id_or_create() # type: ignore @@ -226,15 +224,15 @@ async def test_foundry_chat_client_get_agent_id_or_create_existing_agent( assert not chat_client._should_delete_agent # type: ignore -async def test_foundry_chat_client_get_agent_id_or_create_create_new( +async def test_azure_ai_chat_client_get_agent_id_or_create_create_new( mock_ai_project_client: MagicMock, - foundry_unit_test_env: dict[str, str], + azure_ai_unit_test_env: dict[str, str], ) -> None: """Test _get_agent_id_or_create when creating a new agent.""" - foundry_settings = FoundrySettings( - model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent" + azure_ai_settings = AzureAISettings( + model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent" ) - chat_client = create_test_foundry_chat_client(mock_ai_project_client, foundry_settings=foundry_settings) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, azure_ai_settings=azure_ai_settings) agent_id = await chat_client._get_agent_id_or_create() # type: ignore @@ -242,11 +240,11 @@ async def test_foundry_chat_client_get_agent_id_or_create_create_new( assert chat_client._should_delete_agent # type: ignore -async def test_foundry_chat_client_tool_results_without_thread_error_via_public_api( +async def test_azure_ai_chat_client_tool_results_without_thread_error_via_public_api( mock_ai_project_client: MagicMock, ) -> None: """Test that tool results without thread ID raise error through public API.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent") # Create messages with tool results but no thread/conversation ID messages = [ @@ -262,9 +260,9 @@ async def test_foundry_chat_client_tool_results_without_thread_error_via_public_ pass -async def test_foundry_chat_client_thread_management_through_public_api(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_thread_management_through_public_api(mock_ai_project_client: MagicMock) -> None: """Test thread creation and management through public API.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent") mock_thread = MagicMock() mock_thread.id = "new-thread-456" @@ -293,22 +291,22 @@ async def test_foundry_chat_client_thread_management_through_public_api(mock_ai_ mock_ai_project_client.agents.threads.create.assert_called_once() -@pytest.mark.parametrize("exclude_list", [["FOUNDRY_MODEL_DEPLOYMENT_NAME"]], indirect=True) -async def test_foundry_chat_client_get_agent_id_or_create_missing_model( - mock_ai_project_client: MagicMock, foundry_unit_test_env: dict[str, str] +@pytest.mark.parametrize("exclude_list", [["AZURE_AI_MODEL_DEPLOYMENT_NAME"]], indirect=True) +async def test_azure_ai_chat_client_get_agent_id_or_create_missing_model( + mock_ai_project_client: MagicMock, azure_ai_unit_test_env: dict[str, str] ) -> None: """Test _get_agent_id_or_create when model_deployment_name is missing.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) with pytest.raises(ServiceInitializationError, match="Model deployment name is required"): await chat_client._get_agent_id_or_create() # type: ignore -async def test_foundry_chat_client_cleanup_agent_if_needed_should_delete( +async def test_azure_ai_chat_client_cleanup_agent_if_needed_should_delete( mock_ai_project_client: MagicMock, ) -> None: """Test _cleanup_agent_if_needed when agent should be deleted.""" - chat_client = create_test_foundry_chat_client( + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True ) @@ -318,11 +316,11 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_should_delete( assert not chat_client._should_delete_agent # type: ignore -async def test_foundry_chat_client_cleanup_agent_if_needed_should_not_delete( +async def test_azure_ai_chat_client_cleanup_agent_if_needed_should_not_delete( mock_ai_project_client: MagicMock, ) -> None: """Test _cleanup_agent_if_needed when agent should not be deleted.""" - chat_client = create_test_foundry_chat_client( + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="agent-to-keep", should_delete_agent=False ) @@ -333,11 +331,11 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_should_not_delete( assert not chat_client._should_delete_agent # type: ignore -async def test_foundry_chat_client_cleanup_agent_if_needed_exception_handling( +async def test_azure_ai_chat_client_cleanup_agent_if_needed_exception_handling( mock_ai_project_client: MagicMock, ) -> None: """Test _cleanup_agent_if_needed propagates exceptions (it doesn't handle them).""" - chat_client = create_test_foundry_chat_client( + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True ) mock_ai_project_client.agents.delete_agent.side_effect = Exception("Deletion failed") @@ -346,9 +344,9 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_exception_handling( await chat_client._cleanup_agent_if_needed() # type: ignore -async def test_foundry_chat_client_aclose(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_aclose(mock_ai_project_client: MagicMock) -> None: """Test aclose method calls cleanup.""" - chat_client = create_test_foundry_chat_client( + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True ) @@ -358,9 +356,9 @@ async def test_foundry_chat_client_aclose(mock_ai_project_client: MagicMock) -> mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete") -async def test_foundry_chat_client_async_context_manager(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_async_context_manager(mock_ai_project_client: MagicMock) -> None: """Test async context manager functionality.""" - chat_client = create_test_foundry_chat_client( + chat_client = create_test_azure_ai_chat_client( mock_ai_project_client, agent_id="agent-to-delete", should_delete_agent=True ) @@ -372,9 +370,9 @@ async def test_foundry_chat_client_async_context_manager(mock_ai_project_client: mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete") -async def test_foundry_chat_client_create_run_options_basic(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_create_run_options_basic(mock_ai_project_client: MagicMock) -> None: """Test _create_run_options with basic ChatOptions.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) messages = [ChatMessage(role=Role.USER, text="Hello")] chat_options = ChatOptions(max_tokens=100, temperature=0.7) @@ -385,9 +383,9 @@ async def test_foundry_chat_client_create_run_options_basic(mock_ai_project_clie assert tool_results is None -async def test_foundry_chat_client_create_run_options_no_chat_options(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_create_run_options_no_chat_options(mock_ai_project_client: MagicMock) -> None: """Test _create_run_options with no ChatOptions.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) messages = [ChatMessage(role=Role.USER, text="Hello")] @@ -397,10 +395,10 @@ async def test_foundry_chat_client_create_run_options_no_chat_options(mock_ai_pr assert tool_results is None -async def test_foundry_chat_client_create_run_options_with_image_content(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_create_run_options_with_image_content(mock_ai_project_client: MagicMock) -> None: """Test _create_run_options with image content.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent") image_content = UriContent(uri="https://example.com/image.jpg", media_type="image/jpeg") messages = [ChatMessage(role=Role.USER, contents=[image_content])] @@ -414,9 +412,9 @@ async def test_foundry_chat_client_create_run_options_with_image_content(mock_ai assert len(message.content) == 1 -def test_foundry_chat_client_convert_function_results_to_tool_output_none(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_convert_function_results_to_tool_output_none(mock_ai_project_client: MagicMock) -> None: """Test _convert_required_action_to_tool_output with None input.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) run_id, tool_outputs, tool_approvals = chat_client._convert_required_action_to_tool_output(None) # type: ignore @@ -425,9 +423,9 @@ def test_foundry_chat_client_convert_function_results_to_tool_output_none(mock_a assert tool_approvals is None -async def test_foundry_chat_client_close_client_when_should_close_true(mock_ai_project_client: MagicMock) -> None: - """Test _close_client_if_needed closes client when should_close_client is True.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) +async def test_azure_ai_chat_client_close_client_when_should_close_true(mock_ai_project_client: MagicMock) -> None: + """Test _close_client_if_needed closes project_client when should_close_client is True.""" + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) chat_client._should_close_client = True # type: ignore mock_ai_project_client.close = AsyncMock() @@ -437,9 +435,9 @@ async def test_foundry_chat_client_close_client_when_should_close_true(mock_ai_p mock_ai_project_client.close.assert_called_once() -async def test_foundry_chat_client_close_client_when_should_close_false(mock_ai_project_client: MagicMock) -> None: - """Test _close_client_if_needed does not close client when should_close_client is False.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) +async def test_azure_ai_chat_client_close_client_when_should_close_false(mock_ai_project_client: MagicMock) -> None: + """Test _close_client_if_needed does not close project_client when should_close_client is False.""" + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) chat_client._should_close_client = False # type: ignore await chat_client._close_client_if_needed() # type: ignore @@ -447,9 +445,9 @@ async def test_foundry_chat_client_close_client_when_should_close_false(mock_ai_ mock_ai_project_client.close.assert_not_called() -def test_foundry_chat_client_update_agent_name_when_current_is_none(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_update_agent_name_when_current_is_none(mock_ai_project_client: MagicMock) -> None: """Test _update_agent_name updates name when current agent_name is None.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) chat_client.agent_name = None # type: ignore chat_client._update_agent_name("NewAgentName") # type: ignore @@ -457,9 +455,9 @@ def test_foundry_chat_client_update_agent_name_when_current_is_none(mock_ai_proj assert chat_client.agent_name == "NewAgentName" -def test_foundry_chat_client_update_agent_name_when_current_exists(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_update_agent_name_when_current_exists(mock_ai_project_client: MagicMock) -> None: """Test _update_agent_name does not update when current agent_name exists.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) chat_client.agent_name = "ExistingName" # type: ignore chat_client._update_agent_name("NewAgentName") # type: ignore @@ -467,9 +465,9 @@ def test_foundry_chat_client_update_agent_name_when_current_exists(mock_ai_proje assert chat_client.agent_name == "ExistingName" -def test_foundry_chat_client_update_agent_name_with_none_input(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_update_agent_name_with_none_input(mock_ai_project_client: MagicMock) -> None: """Test _update_agent_name with None input.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) chat_client.agent_name = None # type: ignore chat_client._update_agent_name(None) # type: ignore @@ -477,9 +475,9 @@ def test_foundry_chat_client_update_agent_name_with_none_input(mock_ai_project_c assert chat_client.agent_name is None -async def test_foundry_chat_client_create_run_options_with_messages(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_create_run_options_with_messages(mock_ai_project_client: MagicMock) -> None: """Test _create_run_options with different message types.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) # Test with system message (becomes instruction) messages = [ @@ -495,9 +493,9 @@ async def test_foundry_chat_client_create_run_options_with_messages(mock_ai_proj assert len(run_options["additional_messages"]) == 1 # Only user message -async def test_foundry_chat_client_inner_get_response(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_inner_get_response(mock_ai_project_client: MagicMock) -> None: """Test _inner_get_response method.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent") messages = [ChatMessage(role=Role.USER, text="Hello")] chat_options = ChatOptions() @@ -517,14 +515,14 @@ async def test_foundry_chat_client_inner_get_response(mock_ai_project_client: Ma mock_from_generator.assert_called_once() -async def test_foundry_chat_client_get_agent_id_or_create_with_run_options( - mock_ai_project_client: MagicMock, foundry_unit_test_env: dict[str, str] +async def test_azure_ai_chat_client_get_agent_id_or_create_with_run_options( + mock_ai_project_client: MagicMock, azure_ai_unit_test_env: dict[str, str] ) -> None: """Test _get_agent_id_or_create with run_options containing tools and instructions.""" - foundry_settings = FoundrySettings( - model_deployment_name=foundry_unit_test_env["FOUNDRY_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent" + azure_ai_settings = AzureAISettings( + model_deployment_name=azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"], agent_name="TestAgent" ) - chat_client = create_test_foundry_chat_client(mock_ai_project_client, foundry_settings=foundry_settings) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, azure_ai_settings=azure_ai_settings) run_options = { "tools": [{"type": "function", "function": {"name": "test_tool"}}], @@ -543,9 +541,9 @@ async def test_foundry_chat_client_get_agent_id_or_create_with_run_options( assert "response_format" in call_args -async def test_foundry_chat_client_prepare_thread_cancels_active_run(mock_ai_project_client: MagicMock) -> None: +async def test_azure_ai_chat_client_prepare_thread_cancels_active_run(mock_ai_project_client: MagicMock) -> None: """Test _prepare_thread cancels active thread run when provided.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client, agent_id="test-agent") + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent") mock_thread_run = MagicMock() mock_thread_run.id = "run_123" @@ -559,9 +557,9 @@ async def test_foundry_chat_client_prepare_thread_cancels_active_run(mock_ai_pro mock_ai_project_client.agents.runs.cancel.assert_called_once_with("test-thread", "run_123") -def test_foundry_chat_client_create_function_call_contents_basic(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_create_function_call_contents_basic(mock_ai_project_client: MagicMock) -> None: """Test _create_function_call_contents with basic function call.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) mock_tool_call = MagicMock(spec=RequiredFunctionToolCall) mock_tool_call.id = "call_123" @@ -582,9 +580,9 @@ def test_foundry_chat_client_create_function_call_contents_basic(mock_ai_project assert result[0].call_id == '["response_123", "call_123"]' -def test_foundry_chat_client_create_function_call_contents_no_submit_action(mock_ai_project_client: MagicMock) -> None: +def test_azure_ai_chat_client_create_function_call_contents_no_submit_action(mock_ai_project_client: MagicMock) -> None: """Test _create_function_call_contents when required_action is not SubmitToolOutputsAction.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) mock_event_data = MagicMock(spec=ThreadRun) mock_event_data.required_action = MagicMock() @@ -594,11 +592,11 @@ def test_foundry_chat_client_create_function_call_contents_no_submit_action(mock assert result == [] -def test_foundry_chat_client_create_function_call_contents_non_function_tool_call( +def test_azure_ai_chat_client_create_function_call_contents_non_function_tool_call( mock_ai_project_client: MagicMock, ) -> None: """Test _create_function_call_contents with non-function tool call.""" - chat_client = create_test_foundry_chat_client(mock_ai_project_client) + chat_client = create_test_azure_ai_chat_client(mock_ai_project_client) mock_tool_call = MagicMock() @@ -620,11 +618,11 @@ def get_weather( return f"The weather in {location} is sunny with a high of 25°C." -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_get_response() -> None: - """Test Foundry Chat Client response.""" - async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client: - assert isinstance(foundry_chat_client, ChatClientProtocol) +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_get_response() -> None: + """Test Azure AI Chat Client response.""" + async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client: + assert isinstance(azure_ai_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] messages.append( @@ -636,25 +634,25 @@ async def test_foundry_chat_client_get_response() -> None: ) messages.append(ChatMessage(role="user", text="What's the weather like today?")) - # Test that the client can be used to get a response - response = await foundry_chat_client.get_response(messages=messages) + # Test that the project_client can be used to get a response + response = await azure_ai_chat_client.get_response(messages=messages) assert response is not None assert isinstance(response, ChatResponse) assert any(word in response.text.lower() for word in ["sunny", "25"]) -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_get_response_tools() -> None: - """Test Foundry Chat Client response with tools.""" - async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client: - assert isinstance(foundry_chat_client, ChatClientProtocol) +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_get_response_tools() -> None: + """Test Azure AI Chat Client response with tools.""" + async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client: + assert isinstance(azure_ai_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] messages.append(ChatMessage(role="user", text="What's the weather like in Seattle?")) - # Test that the client can be used to get a response - response = await foundry_chat_client.get_response( + # Test that the project_client can be used to get a response + response = await azure_ai_chat_client.get_response( messages=messages, tools=[get_weather], tool_choice="auto", @@ -665,11 +663,11 @@ async def test_foundry_chat_client_get_response_tools() -> None: assert any(word in response.text.lower() for word in ["sunny", "25"]) -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_streaming() -> None: - """Test Foundry Chat Client streaming response.""" - async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client: - assert isinstance(foundry_chat_client, ChatClientProtocol) +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_streaming() -> None: + """Test Azure AI Chat Client streaming response.""" + async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client: + assert isinstance(azure_ai_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] messages.append( @@ -681,8 +679,8 @@ async def test_foundry_chat_client_streaming() -> None: ) messages.append(ChatMessage(role="user", text="What's the weather like today?")) - # Test that the client can be used to get a response - response = foundry_chat_client.get_streaming_response(messages=messages) + # Test that the project_client can be used to get a response + response = azure_ai_chat_client.get_streaming_response(messages=messages) full_message: str = "" async for chunk in response: @@ -695,17 +693,17 @@ async def test_foundry_chat_client_streaming() -> None: assert any(word in full_message.lower() for word in ["sunny", "25"]) -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_streaming_tools() -> None: - """Test Foundry Chat Client streaming response with tools.""" - async with FoundryChatClient(async_credential=AzureCliCredential()) as foundry_chat_client: - assert isinstance(foundry_chat_client, ChatClientProtocol) +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_streaming_tools() -> None: + """Test Azure AI Chat Client streaming response with tools.""" + async with AzureAIAgentClient(async_credential=AzureCliCredential()) as azure_ai_chat_client: + assert isinstance(azure_ai_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] messages.append(ChatMessage(role="user", text="What's the weather like in Seattle?")) - # Test that the client can be used to get a response - response = foundry_chat_client.get_streaming_response( + # Test that the project_client can be used to get a response + response = azure_ai_chat_client.get_streaming_response( messages=messages, tools=[get_weather], tool_choice="auto", @@ -721,11 +719,11 @@ async def test_foundry_chat_client_streaming_tools() -> None: assert any(word in full_message.lower() for word in ["sunny", "25"]) -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_basic_run() -> None: - """Test ChatAgent basic run functionality with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_basic_run() -> None: + """Test ChatAgent basic run functionality with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), ) as agent: # Run a simple query response = await agent.run("Hello! Please respond with 'Hello World' exactly.") @@ -737,11 +735,11 @@ async def test_foundry_chat_client_agent_basic_run() -> None: assert "Hello World" in response.text -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_basic_run_streaming() -> None: - """Test ChatAgent basic streaming functionality with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_basic_run_streaming() -> None: + """Test ChatAgent basic streaming functionality with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), ) as agent: # Run streaming query full_message: str = "" @@ -756,11 +754,11 @@ async def test_foundry_chat_client_agent_basic_run_streaming() -> None: assert "streaming response test" in full_message.lower() -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_thread_persistence() -> None: - """Test ChatAgent thread persistence across runs with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_thread_persistence() -> None: + """Test ChatAgent thread persistence across runs with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as agent: # Create a new thread that will be reused @@ -781,11 +779,11 @@ async def test_foundry_chat_client_agent_thread_persistence() -> None: assert "42" in second_response.text -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_existing_thread_id() -> None: - """Test ChatAgent existing thread ID functionality with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_existing_thread_id() -> None: + """Test ChatAgent existing thread ID functionality with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as first_agent: # Start a conversation and get the thread ID @@ -802,7 +800,7 @@ async def test_foundry_chat_client_agent_existing_thread_id() -> None: # Now continue with the same thread ID in a new agent instance async with ChatAgent( - chat_client=FoundryChatClient(thread_id=existing_thread_id, async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as second_agent: # Create a thread with the existing ID @@ -818,12 +816,12 @@ async def test_foundry_chat_client_agent_existing_thread_id() -> None: assert "alice" in response2.text.lower() -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_code_interpreter(): - """Test ChatAgent with code interpreter through FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_code_interpreter(): + """Test ChatAgent with code interpreter through AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), instructions="You are a helpful assistant that can write and execute Python code.", tools=[HostedCodeInterpreterTool()], ) as agent: @@ -837,11 +835,11 @@ async def test_foundry_chat_client_agent_code_interpreter(): assert "120" in response.text or "factorial" in response.text.lower() -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_with_mcp_tools() -> None: - """Test MCP tools defined at agent creation with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_with_mcp_tools() -> None: + """Test MCP tools defined at agent creation with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), name="DocsAgent", instructions="You are a helpful assistant that can help with microsoft documentation questions.", tools=MCPStreamableHTTPTool( @@ -858,11 +856,11 @@ async def test_foundry_chat_client_agent_with_mcp_tools() -> None: assert any(term in response.text.lower() for term in ["app service", "azure", "web", "application"]) -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_agent_level_tool_persistence(): - """Test that agent-level tools persist across multiple runs with FoundryChatClient.""" +@skip_if_azure_ai_integration_tests_disabled +async def test_azure_ai_chat_client_agent_level_tool_persistence(): + """Test that agent-level tools persist across multiple runs with AzureAIAgentClient.""" async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + chat_client=AzureAIAgentClient(async_credential=AzureCliCredential()), instructions="You are a helpful assistant that uses available tools.", tools=[get_weather], ) as agent: diff --git a/python/packages/azure/agent_framework_azure/__init__.py b/python/packages/azure/agent_framework_azure/__init__.py deleted file mode 100644 index aaaa369379..0000000000 --- a/python/packages/azure/agent_framework_azure/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import importlib.metadata - -from ._assistants_client import AzureAssistantsClient -from ._chat_client import AzureChatClient -from ._entra_id_authentication import get_entra_auth_token -from ._responses_client import AzureResponsesClient -from ._shared import AzureOpenAISettings - -try: - __version__ = importlib.metadata.version(__name__) -except importlib.metadata.PackageNotFoundError: - __version__ = "0.0.0" # Fallback for development mode - -__all__ = [ - "AzureAssistantsClient", - "AzureChatClient", - "AzureOpenAISettings", - "AzureResponsesClient", - "__version__", - "get_entra_auth_token", -] diff --git a/python/packages/azure/pyproject.toml b/python/packages/azure/pyproject.toml deleted file mode 100644 index 019278b2df..0000000000 --- a/python/packages/azure/pyproject.toml +++ /dev/null @@ -1,87 +0,0 @@ -[project] -name = "agent-framework-azure" -description = "Azure integrations for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] -readme = "README.md" -requires-python = ">=3.10" -version = "0.1.0b1" -license-files = ["LICENSE"] -urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/" -urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" -urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true" -urls.issues = "https://github.com/microsoft/agent-framework/issues" -classifiers = [ - "License :: OSI Approved :: MIT License", - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Framework :: Pydantic :: 2", - "Typing :: Typed", -] -dependencies = [ - "agent-framework", - "azure-identity >= 1.13" -] - -[tool.uv] -prerelease = "if-necessary-or-explicit" -environments = [ - "sys_platform == 'darwin'", - "sys_platform == 'linux'", - "sys_platform == 'win32'" -] - -[tool.uv-dynamic-versioning] -fallback-version = "0.0.0" -[tool.pytest.ini_options] -testpaths = 'tests' -addopts = "-ra -q -r fEX" -asyncio_mode = "auto" -asyncio_default_fixture_loop_scope = "function" -filterwarnings = [] -timeout = 120 - -[tool.ruff] -extend = "../../pyproject.toml" - -[tool.coverage.run] -omit = [ - "**/__init__.py" -] - -[tool.pyright] -extend = "../../pyproject.toml" -exclude = ['tests'] - -[tool.mypy] -plugins = ['pydantic.mypy'] -strict = true -python_version = "3.10" -ignore_missing_imports = true -disallow_untyped_defs = true -no_implicit_optional = true -check_untyped_defs = true -warn_return_any = true -show_error_codes = true -warn_unused_ignores = false -disallow_incomplete_defs = true -disallow_untyped_decorators = true - -[tool.bandit] -targets = ["agent_framework_azure"] -exclude_dirs = ["tests"] - -[tool.poe] -executor.type = "uv" -include = "../../shared_tasks.toml" -[tool.poe.tasks] -mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure" -test = "pytest --cov=agent_framework_azure --cov-report=term-missing:skip-covered tests" - -[build-system] -requires = ["flit-core >= 3.9,<4.0"] -build-backend = "flit_core.buildapi" diff --git a/python/packages/azure/tests/test_cross_package.py b/python/packages/azure/tests/test_cross_package.py deleted file mode 100644 index 78cec4b623..0000000000 --- a/python/packages/azure/tests/test_cross_package.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - - -def test_self_through_main(): - try: - from agent_framework.azure import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_self(): - try: - from agent_framework_azure import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_agent_framework(): - try: - from agent_framework import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None diff --git a/python/packages/copilotstudio/README.md b/python/packages/copilotstudio/README.md index 76c3a1f22b..4a0cc5a854 100644 --- a/python/packages/copilotstudio/README.md +++ b/python/packages/copilotstudio/README.md @@ -31,7 +31,7 @@ The following environment variables are used for configuration: ```python import asyncio -from agent_framework.copilotstudio import CopilotStudioAgent +from agent_framework.microsoft import CopilotStudioAgent async def main(): # Create agent using environment variables @@ -49,7 +49,7 @@ asyncio.run(main()) ```python import asyncio import os -from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token +from agent_framework.microsoft import CopilotStudioAgent, acquire_token from microsoft_agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformCloud, AgentType async def main(): diff --git a/python/packages/copilotstudio/pyproject.toml b/python/packages/copilotstudio/pyproject.toml index aae8a4c274..c192c8c55b 100644 --- a/python/packages/copilotstudio/pyproject.toml +++ b/python/packages/copilotstudio/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-copilotstudio" description = "Copilot Studio integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" diff --git a/python/packages/copilotstudio/tests/test_cross_package.py b/python/packages/copilotstudio/tests/test_cross_package.py deleted file mode 100644 index 228dafc504..0000000000 --- a/python/packages/copilotstudio/tests/test_cross_package.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - - -def test_self_through_main() -> None: - try: - from agent_framework.copilotstudio import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_self() -> None: - try: - from agent_framework_copilotstudio import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_agent_framework() -> None: - try: - from agent_framework import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None diff --git a/python/packages/devui/agent_framework_devui/models/_openai_custom.py b/python/packages/devui/agent_framework_devui/models/_openai_custom.py index bc2b006c64..28a6997395 100644 --- a/python/packages/devui/agent_framework_devui/models/_openai_custom.py +++ b/python/packages/devui/agent_framework_devui/models/_openai_custom.py @@ -8,7 +8,7 @@ to support Agent Framework specific features like workflows, traces, and functio from typing import Any, Literal -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict # Custom Agent Framework OpenAI event types for structured data @@ -111,8 +111,7 @@ class AgentFrameworkExtraBody(BaseModel): thread_id: str | None = None input_data: dict[str, Any] | None = None - class Config: - extra = "allow" # Allow additional fields + model_config = ConfigDict(extra="allow") # Agent Framework Request Model - Extending real OpenAI types @@ -138,12 +137,10 @@ class AgentFrameworkRequest(BaseModel): # Agent Framework extension - strongly typed extra_body: AgentFrameworkExtraBody | None = None - class Config: - # Allow extra fields from OpenAI spec - extra = "allow" - entity_id: str | None = None # Allow entity_id as top-level field + model_config = ConfigDict(extra="allow") + def get_entity_id(self) -> str | None: """Get entity_id from either top-level field or extra_body.""" # Priority 1: Top-level entity_id field diff --git a/python/packages/devui/pyproject.toml b/python/packages/devui/pyproject.toml index c7df268757..2f09809dfc 100644 --- a/python/packages/devui/pyproject.toml +++ b/python/packages/devui/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-devui" description = "Debug UI for Microsoft Agent Framework with OpenAI-compatible API server." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" diff --git a/python/packages/devui/samples/weather_agent_azure/agent.py b/python/packages/devui/samples/weather_agent_azure/agent.py index 00df357810..68c18e5d34 100644 --- a/python/packages/devui/samples/weather_agent_azure/agent.py +++ b/python/packages/devui/samples/weather_agent_azure/agent.py @@ -5,7 +5,7 @@ import os from typing import Annotated from agent_framework import ChatAgent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient def get_weather( @@ -42,7 +42,7 @@ agent = ChatAgent( and forecasts for any location. Always be helpful and provide detailed weather information when asked. """, - chat_client=AzureChatClient( + chat_client=AzureOpenAIChatClient( api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""), ), tools=[get_weather, get_forecast], diff --git a/python/packages/foundry/LICENSE b/python/packages/foundry/LICENSE deleted file mode 100644 index 9e841e7a26..0000000000 --- a/python/packages/foundry/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/python/packages/foundry/README.md b/python/packages/foundry/README.md deleted file mode 100644 index 021a83b574..0000000000 --- a/python/packages/foundry/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Get Started with Microsoft Agent Framework Foundry - -Please install this package as the extra for `agent-framework`: - -```bash -pip install agent-framework[foundry] -``` - -and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information. diff --git a/python/packages/foundry/agent_framework_foundry/py.typed b/python/packages/foundry/agent_framework_foundry/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/packages/foundry/tests/test_cross_package.py b/python/packages/foundry/tests/test_cross_package.py deleted file mode 100644 index 55486e5a42..0000000000 --- a/python/packages/foundry/tests/test_cross_package.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - - -def test_self_through_main(): - try: - from agent_framework.foundry import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_self(): - try: - from agent_framework_foundry import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_agent_framework(): - try: - from agent_framework import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None diff --git a/python/packages/lab/cookiecutter-agent-framework-lab/README.md b/python/packages/lab/cookiecutter-agent-framework-lab/README.md index 25694c7bf9..6a3ba77dca 100644 --- a/python/packages/lab/cookiecutter-agent-framework-lab/README.md +++ b/python/packages/lab/cookiecutter-agent-framework-lab/README.md @@ -16,7 +16,7 @@ You will be prompted for the following information: - **package_description**: Brief description of the package (auto-generated from display name) - **version**: Starting version (default: 0.1.0b1) - **author_name**: Author name (default: Microsoft) -- **author_email**: Author email (default: SK-Support@microsoft.com) +- **author_email**: Author email (default: af-support@microsoft.com) - **include_cli_script**: Whether to include a CLI script (y/n) - **cli_script_name**: Name of CLI script if included @@ -55,4 +55,4 @@ Don't forget to add your new package to the workspace: 1. Add to `python/pyproject.toml` dependencies 2. Add to `[tool.uv.sources]` section -3. Test installation with `uv run python -c "from agent_framework.lab.{name} import *"` \ No newline at end of file +3. Test installation with `uv run python -c "from agent_framework.lab.{name} import *"` diff --git a/python/packages/lab/cookiecutter-agent-framework-lab/cookiecutter.json b/python/packages/lab/cookiecutter-agent-framework-lab/cookiecutter.json index 69098cc01f..1322ed6d8c 100644 --- a/python/packages/lab/cookiecutter-agent-framework-lab/cookiecutter.json +++ b/python/packages/lab/cookiecutter-agent-framework-lab/cookiecutter.json @@ -4,7 +4,7 @@ "package_description": "{{ cookiecutter.package_display_name }} module for Microsoft Agent Framework.", "version": "0.1.0b1", "author_name": "Microsoft", - "author_email": "SK-Support@microsoft.com", + "author_email": "af-support@microsoft.com", "license": "MIT", "include_cli_script": ["y", "n"], "cli_script_name": "{{ cookiecutter.package_name }}_cli", @@ -17,4 +17,4 @@ "_copy_without_render": [ "*.py.typed" ] -} \ No newline at end of file +} diff --git a/python/packages/lab/cookiecutter-agent-framework-lab/{{cookiecutter.package_name}}/pyproject.toml b/python/packages/lab/cookiecutter-agent-framework-lab/{{cookiecutter.package_name}}/pyproject.toml index ae45ae4e41..5dc5a22d10 100644 --- a/python/packages/lab/cookiecutter-agent-framework-lab/{{cookiecutter.package_name}}/pyproject.toml +++ b/python/packages/lab/cookiecutter-agent-framework-lab/{{cookiecutter.package_name}}/pyproject.toml @@ -92,4 +92,4 @@ addopts = "--strict-markers --strict-config" markers = [ "unit: marks tests as unit tests", "integration: marks tests as integration tests", -] \ No newline at end of file +] diff --git a/python/packages/lab/gaia/pyproject.toml b/python/packages/lab/gaia/pyproject.toml index 0824b02c54..fac593bd3d 100644 --- a/python/packages/lab/gaia/pyproject.toml +++ b/python/packages/lab/gaia/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-lab-gaia" description = "GAIA benchmark module for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" @@ -83,4 +83,4 @@ addopts = "--strict-markers --strict-config" markers = [ "unit: marks tests as unit tests", "integration: marks tests as integration tests", -] \ No newline at end of file +] diff --git a/python/packages/lab/gaia/samples/gaia_sample.py b/python/packages/lab/gaia/samples/gaia_sample.py index 4599668733..f6d62a4531 100644 --- a/python/packages/lab/gaia/samples/gaia_sample.py +++ b/python/packages/lab/gaia/samples/gaia_sample.py @@ -10,7 +10,7 @@ To run this sample, execute it from the root directory of the agent-framework re This avoids namespace package conflicts that occur when running from within the gaia package directory. """ -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from agent_framework.lab.gaia import GAIA, Evaluation, GAIATelemetryConfig, Prediction, Task @@ -39,7 +39,7 @@ async def main() -> None: # Create a single agent once and reuse it for all tasks async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="GaiaAgent", instructions="Solve tasks to your best ability.", ) as agent, diff --git a/python/packages/lab/lightning/pyproject.toml b/python/packages/lab/lightning/pyproject.toml index 8e153dc6b8..68d333ee17 100644 --- a/python/packages/lab/lightning/pyproject.toml +++ b/python/packages/lab/lightning/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-lab-lightning" description = "RL Module for Microsoft Agent Framework" -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" @@ -84,4 +84,4 @@ addopts = "--strict-markers --strict-config" markers = [ "unit: marks tests as unit tests", "integration: marks tests as integration tests", -] \ No newline at end of file +] diff --git a/python/packages/lab/tau2/pyproject.toml b/python/packages/lab/tau2/pyproject.toml index 6c49d0b3fc..3610304cd8 100644 --- a/python/packages/lab/tau2/pyproject.toml +++ b/python/packages/lab/tau2/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-lab-tau2" description = "Tau2 Benchmark for Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" diff --git a/python/packages/main/README.md b/python/packages/main/README.md index 61e5b5ca0a..71030f0ab6 100644 --- a/python/packages/main/README.md +++ b/python/packages/main/README.md @@ -5,7 +5,7 @@ Highlights - Flexible Agent Framework: build, orchestrate, and deploy AI agents and multi-agent systems - Multi-Agent Orchestration: Group chat, sequential, concurrent, and handoff patterns - Plugin Ecosystem: Extend with native functions, OpenAPI, Model Context Protocol (MCP), and more -- LLM Support: OpenAI, Azure OpenAI, Azure AI Foundry, and more +- LLM Support: OpenAI, Azure OpenAI, Azure AI, and more - Runtime Support: In-process and distributed agent execution - Multimodal: Text, vision, and function calling - Cross-Platform: .NET and Python implementations @@ -13,13 +13,11 @@ Highlights ## Quick Install ```bash -pip install agent-framework -# Optional: Add Azure integration -pip install agent-framework[azure] -# Optional: Add Foundry integration -pip install agent-framework[foundry] +pip install agent-framework[all] +# Optional: Add Azure AI integration +pip install agent-framework-azure-ai # Optional: Both -pip install agent-framework[azure,foundry] +pip install agent-framework-azure-ai agent-framework-copilotstudio ``` Supported Platforms: @@ -40,16 +38,16 @@ AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=... AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=... ... -FOUNDRY_PROJECT_ENDPOINT=... -FOUNDRY_MODEL_DEPLOYMENT_NAME=... +AZURE_AI_PROJECT_ENDPOINT=... +AZURE_AI_MODEL_DEPLOYMENT_NAME=... ``` You can also override environment variables by explicitly passing configuration parameters to the chat client constructor: ```python -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient -chat_client = AzureChatClient( +chat_client = AzureOpenAIChatClient( api_key="", endpoint="", deployment_name="", @@ -223,7 +221,7 @@ if __name__ == "__main__": - [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage - [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns -- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration +- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration - [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET) ## Agent Framework Documentation diff --git a/python/packages/main/agent_framework/azure/__init__.py b/python/packages/main/agent_framework/azure/__init__.py index 1e28004537..1ee279a986 100644 --- a/python/packages/main/agent_framework/azure/__init__.py +++ b/python/packages/main/agent_framework/azure/__init__.py @@ -1,31 +1,33 @@ # Copyright (c) Microsoft. All rights reserved. + import importlib from typing import Any -PACKAGE_NAME = "agent_framework_azure" -PACKAGE_EXTRA = "azure" -_IMPORTS = [ - "AzureAssistantsClient", - "AzureChatClient", - "AzureOpenAISettings", - "AzureResponsesClient", - "get_entra_auth_token", - "__version__", -] +_IMPORTS: dict[str, tuple[str, list[str]]] = { + "AzureAIAgentClient": ("agent_framework_azure_ai", ["azure_ai", "azure"]), + "AzureOpenAIAssistantsClient": ("agent_framework.azure._assistants_client", []), + "AzureOpenAIChatClient": ("agent_framework.azure._chat_client", []), + "AzureAISettings": ("agent_framework_azure_ai", ["azure_ai", "azure"]), + "AzureOpenAISettings": ("agent_framework.azure._shared", []), + "AzureOpenAIResponsesClient": ("agent_framework.azure._responses_client", []), + "get_entra_auth_token": ("agent_framework.azure._entra_id_authentication", []), +} def __getattr__(name: str) -> Any: if name in _IMPORTS: + package_name, package_extra = _IMPORTS[name] try: - return getattr(importlib.import_module(PACKAGE_NAME), name) + return getattr(importlib.import_module(package_name), name) except ModuleNotFoundError as exc: raise ModuleNotFoundError( - f"The '{PACKAGE_EXTRA}' extra is not installed, " - f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`" + f"The {' or '.join(package_extra)} extra is not installed, " + f"please use `pip install agent-framework[{package_extra[0]}]`, " + "or update your requirements.txt or pyproject.toml file." ) from exc - raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.") + raise AttributeError(f"Module `azure` has no attribute {name}.") def __dir__() -> list[str]: - return _IMPORTS + return list(_IMPORTS.keys()) diff --git a/python/packages/main/agent_framework/azure/__init__.pyi b/python/packages/main/agent_framework/azure/__init__.pyi deleted file mode 100644 index 69d9995ce0..0000000000 --- a/python/packages/main/agent_framework/azure/__init__.pyi +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -from agent_framework_azure import ( - AzureAssistantsClient, - AzureChatClient, - AzureOpenAISettings, - AzureResponsesClient, - __version__, - get_entra_auth_token, -) - -__all__ = [ - "AzureAssistantsClient", - "AzureChatClient", - "AzureOpenAISettings", - "AzureResponsesClient", - "__version__", - "get_entra_auth_token", -] diff --git a/python/packages/azure/agent_framework_azure/_assistants_client.py b/python/packages/main/agent_framework/azure/_assistants_client.py similarity index 96% rename from python/packages/azure/agent_framework_azure/_assistants_client.py rename to python/packages/main/agent_framework/azure/_assistants_client.py index 6c5bc6a2f2..e22d188a49 100644 --- a/python/packages/azure/agent_framework_azure/_assistants_client.py +++ b/python/packages/main/agent_framework/azure/_assistants_client.py @@ -3,21 +3,21 @@ from collections.abc import Mapping from typing import TYPE_CHECKING, Any, ClassVar -from agent_framework.exceptions import ServiceInitializationError -from agent_framework.openai import OpenAIAssistantsClient from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI from pydantic import SecretStr, ValidationError from pydantic.networks import AnyUrl +from ..exceptions import ServiceInitializationError +from ..openai import OpenAIAssistantsClient from ._shared import AzureOpenAISettings if TYPE_CHECKING: from azure.core.credentials import TokenCredential -__all__ = ["AzureAssistantsClient"] +__all__ = ["AzureOpenAIAssistantsClient"] -class AzureAssistantsClient(OpenAIAssistantsClient): +class AzureOpenAIAssistantsClient(OpenAIAssistantsClient): """Azure OpenAI Assistants client.""" DEFAULT_AZURE_API_VERSION: ClassVar[str] = "2024-05-01-preview" diff --git a/python/packages/azure/agent_framework_azure/_chat_client.py b/python/packages/main/agent_framework/azure/_chat_client.py similarity index 93% rename from python/packages/azure/agent_framework_azure/_chat_client.py rename to python/packages/main/agent_framework/azure/_chat_client.py index 41e3d54021..045a22aba7 100644 --- a/python/packages/azure/agent_framework_azure/_chat_client.py +++ b/python/packages/main/agent_framework/azure/_chat_client.py @@ -6,16 +6,6 @@ import sys from collections.abc import Mapping from typing import Any, TypeVar -from agent_framework import ( - ChatResponse, - ChatResponseUpdate, - CitationAnnotation, - TextContent, - use_function_invocation, -) -from agent_framework.exceptions import ServiceInitializationError -from agent_framework.observability import use_observability -from agent_framework.openai._chat_client import OpenAIBaseChatClient from azure.core.credentials import TokenCredential from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI from openai.types.chat.chat_completion import Choice @@ -23,6 +13,16 @@ from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice from pydantic import SecretStr, ValidationError from pydantic.networks import AnyUrl +from .._tools import use_function_invocation +from .._types import ( + ChatResponse, + ChatResponseUpdate, + CitationAnnotation, + TextContent, +) +from ..exceptions import ServiceInitializationError +from ..observability import use_observability +from ..openai._chat_client import OpenAIBaseChatClient from ._shared import ( AzureOpenAIConfigMixin, AzureOpenAISettings, @@ -36,13 +36,13 @@ else: logger: logging.Logger = logging.getLogger(__name__) TChatResponse = TypeVar("TChatResponse", ChatResponse, ChatResponseUpdate) -TAzureChatClient = TypeVar("TAzureChatClient", bound="AzureChatClient") +TAzureOpenAIChatClient = TypeVar("TAzureOpenAIChatClient", bound="AzureOpenAIChatClient") @use_function_invocation @use_observability -class AzureChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient): - """Azure Chat completion class.""" +class AzureOpenAIChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient): + """Azure OpenAI Chat completion class.""" def __init__( self, @@ -123,7 +123,7 @@ class AzureChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient): ) @classmethod - def from_dict(cls: type[TAzureChatClient], settings: dict[str, Any]) -> TAzureChatClient: + def from_dict(cls: type[TAzureOpenAIChatClient], settings: dict[str, Any]) -> TAzureOpenAIChatClient: """Initialize an Azure OpenAI service from a dictionary of settings. Args: diff --git a/python/packages/azure/agent_framework_azure/_entra_id_authentication.py b/python/packages/main/agent_framework/azure/_entra_id_authentication.py similarity index 97% rename from python/packages/azure/agent_framework_azure/_entra_id_authentication.py rename to python/packages/main/agent_framework/azure/_entra_id_authentication.py index 638047608b..d108d13c9a 100644 --- a/python/packages/azure/agent_framework_azure/_entra_id_authentication.py +++ b/python/packages/main/agent_framework/azure/_entra_id_authentication.py @@ -3,9 +3,10 @@ import logging from typing import TYPE_CHECKING, Any -from agent_framework.exceptions import ServiceInvalidAuthError from azure.core.exceptions import ClientAuthenticationError +from ..exceptions import ServiceInvalidAuthError + if TYPE_CHECKING: from azure.core.credentials import TokenCredential from azure.core.credentials_async import AsyncTokenCredential diff --git a/python/packages/azure/agent_framework_azure/_responses_client.py b/python/packages/main/agent_framework/azure/_responses_client.py similarity index 91% rename from python/packages/azure/agent_framework_azure/_responses_client.py rename to python/packages/main/agent_framework/azure/_responses_client.py index 29d512e631..5f61690a81 100644 --- a/python/packages/azure/agent_framework_azure/_responses_client.py +++ b/python/packages/main/agent_framework/azure/_responses_client.py @@ -4,26 +4,26 @@ from collections.abc import Mapping from typing import Any, TypeVar from urllib.parse import urljoin -from agent_framework import use_function_invocation -from agent_framework.exceptions import ServiceInitializationError -from agent_framework.observability import use_observability -from agent_framework.openai._responses_client import OpenAIBaseResponsesClient from azure.core.credentials import TokenCredential from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI from pydantic import SecretStr, ValidationError from pydantic.networks import AnyUrl +from .._tools import use_function_invocation +from ..exceptions import ServiceInitializationError +from ..observability import use_observability +from ..openai._responses_client import OpenAIBaseResponsesClient from ._shared import ( AzureOpenAIConfigMixin, AzureOpenAISettings, ) -TAzureResponsesClient = TypeVar("TAzureResponsesClient", bound="AzureResponsesClient") +TAzureOpenAIResponsesClient = TypeVar("TAzureOpenAIResponsesClient", bound="AzureOpenAIResponsesClient") @use_observability @use_function_invocation -class AzureResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient): +class AzureOpenAIResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient): """Azure Responses completion class.""" def __init__( @@ -115,7 +115,7 @@ class AzureResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient): ) @classmethod - def from_dict(cls: type[TAzureResponsesClient], settings: dict[str, Any]) -> TAzureResponsesClient: + def from_dict(cls: type[TAzureOpenAIResponsesClient], settings: dict[str, Any]) -> TAzureOpenAIResponsesClient: """Initialize an Open AI service from a dictionary of settings. Args: diff --git a/python/packages/azure/agent_framework_azure/_shared.py b/python/packages/main/agent_framework/azure/_shared.py similarity index 77% rename from python/packages/azure/agent_framework_azure/_shared.py rename to python/packages/main/agent_framework/azure/_shared.py index 0e30a25dc1..2b80efd711 100644 --- a/python/packages/azure/agent_framework_azure/_shared.py +++ b/python/packages/main/agent_framework/azure/_shared.py @@ -6,14 +6,14 @@ from collections.abc import Awaitable, Callable, Mapping from copy import copy from typing import Any, ClassVar, Final -from agent_framework._pydantic import AFBaseSettings, HTTPsUrl -from agent_framework._telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent -from agent_framework.exceptions import ServiceInitializationError -from agent_framework.openai._shared import OpenAIBase from azure.core.credentials import TokenCredential from openai.lib.azure import AsyncAzureOpenAI from pydantic import ConfigDict, SecretStr, model_validator, validate_call +from .._pydantic import AFBaseSettings, HTTPsUrl +from .._telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent +from ..exceptions import ServiceInitializationError +from ..openai._shared import OpenAIBase from ._entra_id_authentication import get_entra_auth_token if sys.version_info >= (3, 11): @@ -37,7 +37,12 @@ class AzureOpenAISettings(AFBaseSettings): with the encoding 'utf-8'. If the settings are not found in the .env file, the settings are ignored; however, validation will fail alerting that the settings are missing. - Attributes: + Args: + endpoint: The endpoint of the Azure deployment. This value + can be found in the Keys & Endpoint section when examining + your resource from the Azure portal, the endpoint should end in openai.azure.com. + If both base_url and endpoint are supplied, base_url will be used. + (Env var AZURE_OPENAI_ENDPOINT) chat_deployment_name: The name of the Azure Chat deployment. This value will correspond to the custom name you chose for your deployment when you deployed a model. This value can be found under @@ -50,59 +55,18 @@ class AzureOpenAISettings(AFBaseSettings): Resource Management > Deployments in the Azure portal or, alternatively, under Management > Deployments in Azure AI Foundry. (Env var AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME) - text_deployment_name: The name of the Azure Text deployment. This value - will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_TEXT_DEPLOYMENT_NAME) - embedding_deployment_name: The name of the Azure Embedding deployment. This value - will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME) - text_to_image_deployment_name: The name of the Azure Text to Image deployment. This - value will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_TEXT_TO_IMAGE_DEPLOYMENT_NAME) - audio_to_text_deployment_name: The name of the Azure Audio to Text deployment. This - value will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_AUDIO_TO_TEXT_DEPLOYMENT_NAME) - text_to_audio_deployment_name: The name of the Azure Text to Audio deployment. This - value will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_TEXT_TO_AUDIO_DEPLOYMENT_NAME) - realtime_deployment_name: The name of the Azure Realtime deployment. This value - will correspond to the custom name you chose for your deployment - when you deployed a model. This value can be found under - Resource Management > Deployments in the Azure portal or, alternatively, - under Management > Deployments in Azure AI Foundry. - (Env var AZURE_OPENAI_REALTIME_DEPLOYMENT_NAME) api_key: The API key for the Azure deployment. This value can be found in the Keys & Endpoint section when examining your resource in the Azure portal. You can use either KEY1 or KEY2. (Env var AZURE_OPENAI_API_KEY) + api_version: The API version to use. The default value is `default_api_version`. + (Env var AZURE_OPENAI_API_VERSION) base_url: The url of the Azure deployment. This value can be found in the Keys & Endpoint section when examining your resource from the Azure portal, the base_url consists of the endpoint, followed by /openai/deployments/{deployment_name}/, use endpoint if you only want to supply the endpoint. (Env var AZURE_OPENAI_BASE_URL) - endpoint: The endpoint of the Azure deployment. This value - can be found in the Keys & Endpoint section when examining - your resource from the Azure portal, the endpoint should end in openai.azure.com. - If both base_url and endpoint are supplied, base_url will be used. - (Env var AZURE_OPENAI_ENDPOINT) - api_version: The API version to use. The default value is `default_api_version`. - (Env var AZURE_OPENAI_API_VERSION) token_endpoint: The token endpoint to use to retrieve the authentication token. The default value is `default_token_endpoint`. (Env var AZURE_OPENAI_TOKEN_ENDPOINT) @@ -110,8 +74,6 @@ class AzureOpenAISettings(AFBaseSettings): The default value is "2024-10-21". default_token_endpoint: The default token endpoint to use if not specified. The default value is "https://cognitiveservices.azure.com/.default". - - Parameters: env_file_path: The path to the .env file to load settings from. env_file_encoding: The encoding of the .env file, defaults to 'utf-8'. """ @@ -120,12 +82,6 @@ class AzureOpenAISettings(AFBaseSettings): chat_deployment_name: str | None = None responses_deployment_name: str | None = None - text_deployment_name: str | None = None - embedding_deployment_name: str | None = None - text_to_image_deployment_name: str | None = None - audio_to_text_deployment_name: str | None = None - text_to_audio_deployment_name: str | None = None - realtime_deployment_name: str | None = None endpoint: HTTPsUrl | None = None base_url: HTTPsUrl | None = None api_key: SecretStr | None = None diff --git a/python/packages/main/agent_framework/copilotstudio/__init__.py b/python/packages/main/agent_framework/copilotstudio/__init__.py deleted file mode 100644 index aff666452b..0000000000 --- a/python/packages/main/agent_framework/copilotstudio/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import importlib -from typing import Any - -PACKAGE_NAME = "agent_framework_copilotstudio" -PACKAGE_EXTRA = "copilotstudio" -_IMPORTS = ["CopilotStudioAgent", "__version__", "acquire_token"] - - -def __getattr__(name: str) -> Any: - if name in _IMPORTS: - try: - return getattr(importlib.import_module(PACKAGE_NAME), name) - except ModuleNotFoundError as exc: - raise ModuleNotFoundError( - f"The '{PACKAGE_EXTRA}' extra is not installed, " - f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`" - ) from exc - raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.") - - -def __dir__() -> list[str]: - return _IMPORTS diff --git a/python/packages/main/agent_framework/foundry/__init__.py b/python/packages/main/agent_framework/foundry/__init__.py deleted file mode 100644 index 671b4d7857..0000000000 --- a/python/packages/main/agent_framework/foundry/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import importlib -from typing import Any - -PACKAGE_NAME = "agent_framework_foundry" -PACKAGE_EXTRA = "foundry" -_IMPORTS = ["__version__", "FoundryChatClient", "FoundrySettings"] - - -def __getattr__(name: str) -> Any: - if name in _IMPORTS: - try: - return getattr(importlib.import_module(PACKAGE_NAME), name) - except ModuleNotFoundError as exc: - raise ModuleNotFoundError( - f"The '{PACKAGE_EXTRA}' extra is not installed, " - f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`" - ) from exc - raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.") - - -def __dir__() -> list[str]: - return _IMPORTS diff --git a/python/packages/main/agent_framework/foundry/__init__.pyi b/python/packages/main/agent_framework/foundry/__init__.pyi deleted file mode 100644 index bed6035918..0000000000 --- a/python/packages/main/agent_framework/foundry/__init__.pyi +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -from agent_framework_foundry import FoundryChatClient, FoundrySettings, __version__ - -__all__ = ["FoundryChatClient", "FoundrySettings", "__version__"] diff --git a/python/packages/main/agent_framework/microsoft/__init__.py b/python/packages/main/agent_framework/microsoft/__init__.py new file mode 100644 index 0000000000..a2dd9e81e5 --- /dev/null +++ b/python/packages/main/agent_framework/microsoft/__init__.py @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft. All rights reserved. + +import importlib +from typing import Any + +PACKAGE_NAME = "agent_framework_copilotstudio" +PACKAGE_EXTRA = ["microsoft-copilotstudio", "copilotstudio"] +_IMPORTS: dict[str, tuple[str, list[str]]] = { + "CopilotStudioAgent": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]), + "__version__": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]), + "acquire_token": ("agent_framework_copilotstudio", ["microsoft-copilotstudio", "copilotstudio"]), +} + + +def __getattr__(name: str) -> Any: + if name in _IMPORTS: + package_name, package_extra = _IMPORTS[name] + try: + return getattr(importlib.import_module(package_name), name) + except ModuleNotFoundError as exc: + raise ModuleNotFoundError( + f"The {' or '.join(package_extra)} extra is not installed, " + f"please use `pip install agent-framework[{package_extra[0]}]`, " + "or update your requirements.txt or pyproject.toml file." + ) from exc + raise AttributeError(f"Module `azure` has no attribute {name}.") + + +def __dir__() -> list[str]: + return list(_IMPORTS.keys()) diff --git a/python/packages/main/agent_framework/copilotstudio/__init__.pyi b/python/packages/main/agent_framework/microsoft/__init__.pyi similarity index 100% rename from python/packages/main/agent_framework/copilotstudio/__init__.pyi rename to python/packages/main/agent_framework/microsoft/__init__.pyi diff --git a/python/packages/main/agent_framework/openai/_shared.py b/python/packages/main/agent_framework/openai/_shared.py index 4123754444..513cc682c5 100644 --- a/python/packages/main/agent_framework/openai/_shared.py +++ b/python/packages/main/agent_framework/openai/_shared.py @@ -82,7 +82,7 @@ class OpenAISettings(AFBaseSettings): encoding 'utf-8'. If the settings are not found in the .env file, the settings are ignored; however, validation will fail alerting that the settings are missing. - Attributes: + Args: api_key: OpenAI API key, see https://platform.openai.com/account/api-keys (Env var OPENAI_API_KEY) base_url: The base URL for the OpenAI API. @@ -93,21 +93,6 @@ class OpenAISettings(AFBaseSettings): (Env var OPENAI_CHAT_MODEL_ID) responses_model_id: The OpenAI responses model ID to use, for example, gpt-4o or o1. (Env var OPENAI_RESPONSES_MODEL_ID) - text_model_id: The OpenAI text model ID to use, for example, gpt-3.5-turbo-instruct. - (Env var OPENAI_TEXT_MODEL_ID) - embedding_model_id: The OpenAI embedding model ID to use, for example, text-embedding-ada-002. - (Env var OPENAI_EMBEDDING_MODEL_ID) - text_to_image_model_id: The OpenAI text to image model ID to use, for example, dall-e-3. - (Env var OPENAI_TEXT_TO_IMAGE_MODEL_ID) - audio_to_text_model_id: The OpenAI audio to text model ID to use, for example, whisper-1. - (Env var OPENAI_AUDIO_TO_TEXT_MODEL_ID) - text_to_audio_model_id: The OpenAI text to audio model ID to use, for example, jukebox-1. - (Env var OPENAI_TEXT_TO_AUDIO_MODEL_ID) - realtime_model_id: The OpenAI realtime model ID to use, - for example, gpt-4o-realtime-preview-2024-12-17. - (Env var OPENAI_REALTIME_MODEL_ID) - - Parameters: env_file_path: The path to the .env file to load settings from. env_file_encoding: The encoding of the .env file, defaults to 'utf-8'. """ @@ -119,12 +104,6 @@ class OpenAISettings(AFBaseSettings): org_id: str | None = None chat_model_id: str | None = None responses_model_id: str | None = None - text_model_id: str | None = None - embedding_model_id: str | None = None - text_to_image_model_id: str | None = None - audio_to_text_model_id: str | None = None - text_to_audio_model_id: str | None = None - realtime_model_id: str | None = None class OpenAIBase(AFBaseModel): diff --git a/python/packages/main/pyproject.toml b/python/packages/main/pyproject.toml index 212c5e2584..5977dccbe5 100644 --- a/python/packages/main/pyproject.toml +++ b/python/packages/main/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "agent-framework" description = "Microsoft Agent Framework for building AI Agents with Python." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" -version = "0.1.0b1" +version = "0.1.0b1" # TODO: decide on initial version and versioning strategy license-files = ["LICENSE"] urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/" urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" @@ -24,31 +24,38 @@ classifiers = [ ] dependencies = [ "openai>=1.99.0", - "pydantic>=2.11.7", - "pydantic-settings>=2.10.1", - "typing-extensions>=4.14.0", - "opentelemetry-api ~= 1.24", - "opentelemetry-sdk ~= 1.24", + "pydantic>=2,<3", + "pydantic-settings>=2,<3", + "typing-extensions", + "opentelemetry-api>=1.24", + "opentelemetry-sdk>=1.24", "mcp[ws]>=1.13", "azure-monitor-opentelemetry>=1.7.0", "azure-monitor-opentelemetry-exporter>=1.0.0b41", "opentelemetry-exporter-otlp-proto-grpc>=1.36.0", "opentelemetry-semantic-conventions-ai>=0.4.13", - "aiofiles>=24.1.0" + "aiofiles>=24.1.0", + "azure-identity>=1,<2" ] [project.optional-dependencies] -azure = [ - "agent-framework-azure" +azure-ai = [ + "agent-framework-azure-ai" ] -foundry = [ - "agent-framework-foundry" +azure = [ + "agent-framework-azure-ai" +] +microsoft-copilotstudio = [ + "agent-framework-copilotstudio" +] +microsoft = [ + "agent-framework-copilotstudio" ] redis = [ "agent-framework-redis" ] viz = [ - "graphviz>=0.20.0", + "graphviz>=0.20.0" ] runtime = [ "agent-framework-runtime" @@ -60,12 +67,13 @@ devui = [ "agent-framework-devui" ] all = [ - "agent-framework-azure", - "agent-framework-foundry", + "agent_framework_copilotstudio", + "agent-framework-azure-ai", "agent-framework-runtime", "agent-framework-mem0", "agent-framework-redis", - "agent-framework-devui" + "agent-framework-devui", + "graphviz>=0.20.0" ] [tool.uv] @@ -81,10 +89,9 @@ fallback-version = "0.0.0" [tool.pytest.ini_options] testpaths = [ - 'tests', + 'tests', 'packages/main/tests', - 'packages/azure/tests', - 'packages/foundry/tests', + 'packages/azure-ai/tests', 'packages/copilotstudio/tests', 'packages/mem0/tests', 'packages/runtime/tests' diff --git a/python/packages/azure/tests/conftest.py b/python/packages/main/tests/azure/conftest.py similarity index 99% rename from python/packages/azure/tests/conftest.py rename to python/packages/main/tests/azure/conftest.py index 28349f15df..a9c03cd664 100644 --- a/python/packages/azure/tests/conftest.py +++ b/python/packages/main/tests/azure/conftest.py @@ -1,9 +1,10 @@ # Copyright (c) Microsoft. All rights reserved. from typing import Any -from agent_framework import ChatMessage from pytest import fixture +from agent_framework import ChatMessage + # region: Connector Settings fixtures @fixture diff --git a/python/packages/azure/tests/test_azure_assistants_client.py b/python/packages/main/tests/azure/test_azure_assistants_client.py similarity index 88% rename from python/packages/azure/tests/test_azure_assistants_client.py rename to python/packages/main/tests/azure/test_azure_assistants_client.py index 7bd2f6571b..bdab12970f 100644 --- a/python/packages/azure/tests/test_azure_assistants_client.py +++ b/python/packages/main/tests/azure/test_azure_assistants_client.py @@ -5,6 +5,9 @@ from typing import Annotated from unittest.mock import AsyncMock, MagicMock, patch import pytest +from azure.identity import AzureCliCredential +from pydantic import Field + from agent_framework import ( AgentRunResponse, AgentRunResponseUpdate, @@ -17,11 +20,8 @@ from agent_framework import ( HostedCodeInterpreterTool, TextContent, ) +from agent_framework.azure import AzureOpenAIAssistantsClient from agent_framework.exceptions import ServiceInitializationError -from azure.identity import AzureCliCredential -from pydantic import Field - -from agent_framework_azure import AzureAssistantsClient skip_if_azure_integration_tests_disabled = pytest.mark.skipif( os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true" @@ -39,9 +39,9 @@ def create_test_azure_assistants_client( assistant_name: str | None = None, thread_id: str | None = None, should_delete_assistant: bool = False, -) -> AzureAssistantsClient: - """Helper function to create AzureAssistantsClient instances for testing, bypassing Pydantic validation.""" - return AzureAssistantsClient.model_construct( +) -> AzureOpenAIAssistantsClient: + """Helper function to create AzureOpenAIAssistantsClient instances for testing, bypassing Pydantic validation.""" + return AzureOpenAIAssistantsClient.model_construct( ai_model_id=deployment_name or "test_chat_deployment", assistant_id=assistant_id, assistant_name=assistant_name, @@ -79,7 +79,7 @@ def mock_async_azure_openai() -> MagicMock: def test_azure_assistants_client_init_with_client(mock_async_azure_openai: MagicMock) -> None: - """Test AzureAssistantsClient initialization with existing client.""" + """Test AzureOpenAIAssistantsClient initialization with existing client.""" chat_client = create_test_azure_assistants_client( mock_async_azure_openai, deployment_name="test_chat_deployment", @@ -99,8 +99,8 @@ def test_azure_assistants_client_init_auto_create_client( azure_openai_unit_test_env: dict[str, str], mock_async_azure_openai: MagicMock, ) -> None: - """Test AzureAssistantsClient initialization with auto-created client.""" - chat_client = AzureAssistantsClient.model_construct( + """Test AzureOpenAIAssistantsClient initialization with auto-created client.""" + chat_client = AzureOpenAIAssistantsClient.model_construct( ai_model_id=azure_openai_unit_test_env["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"], assistant_id=None, assistant_name="TestAssistant", @@ -119,26 +119,26 @@ def test_azure_assistants_client_init_auto_create_client( def test_azure_assistants_client_init_validation_fail() -> None: - """Test AzureAssistantsClient initialization with validation failure.""" + """Test AzureOpenAIAssistantsClient initialization with validation failure.""" with pytest.raises(ServiceInitializationError): # Force failure by providing invalid deployment name type - this should cause validation to fail - AzureAssistantsClient(deployment_name=123, api_key="valid-key") # type: ignore + AzureOpenAIAssistantsClient(deployment_name=123, api_key="valid-key") # type: ignore @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True) def test_azure_assistants_client_init_missing_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None: - """Test AzureAssistantsClient initialization with missing deployment name.""" + """Test AzureOpenAIAssistantsClient initialization with missing deployment name.""" with pytest.raises(ServiceInitializationError): - AzureAssistantsClient( + AzureOpenAIAssistantsClient( api_key=azure_openai_unit_test_env.get("AZURE_OPENAI_API_KEY", "test-key"), env_file_path="nonexistent.env" ) def test_azure_assistants_client_init_with_default_headers(azure_openai_unit_test_env: dict[str, str]) -> None: - """Test AzureAssistantsClient initialization with default headers.""" + """Test AzureOpenAIAssistantsClient initialization with default headers.""" default_headers = {"X-Unit-Test": "test-guid"} - chat_client = AzureAssistantsClient( + chat_client = AzureOpenAIAssistantsClient( deployment_name="test_chat_deployment", api_key=azure_openai_unit_test_env["AZURE_OPENAI_API_KEY"], endpoint=azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"], @@ -225,11 +225,11 @@ async def test_azure_assistants_client_async_context_manager(mock_async_azure_op def test_azure_assistants_client_serialize(azure_openai_unit_test_env: dict[str, str]) -> None: - """Test serialization of AzureAssistantsClient.""" + """Test serialization of AzureOpenAIAssistantsClient.""" default_headers = {"X-Unit-Test": "test-guid"} # Test basic initialization and to_dict - chat_client = AzureAssistantsClient( + chat_client = AzureOpenAIAssistantsClient( deployment_name="test_chat_deployment", assistant_id="test-assistant-id", assistant_name="TestAssistant", @@ -265,7 +265,7 @@ def get_weather( @skip_if_azure_integration_tests_disabled async def test_azure_assistants_client_get_response() -> None: """Test Azure Assistants Client response.""" - async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: assert isinstance(azure_assistants_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -289,7 +289,7 @@ async def test_azure_assistants_client_get_response() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_assistants_client_get_response_tools() -> None: """Test Azure Assistants Client response with tools.""" - async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: assert isinstance(azure_assistants_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -310,7 +310,7 @@ async def test_azure_assistants_client_get_response_tools() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_assistants_client_streaming() -> None: """Test Azure Assistants Client streaming response.""" - async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: assert isinstance(azure_assistants_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -340,7 +340,7 @@ async def test_azure_assistants_client_streaming() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_assistants_client_streaming_tools() -> None: """Test Azure Assistants Client streaming response with tools.""" - async with AzureAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as azure_assistants_client: assert isinstance(azure_assistants_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -367,14 +367,14 @@ async def test_azure_assistants_client_streaming_tools() -> None: async def test_azure_assistants_client_with_existing_assistant() -> None: """Test Azure Assistants Client with existing assistant ID.""" # First create an assistant to use in the test - async with AzureAssistantsClient(credential=AzureCliCredential()) as temp_client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as temp_client: # Get the assistant ID by triggering assistant creation messages = [ChatMessage(role="user", text="Hello")] await temp_client.get_response(messages=messages) assistant_id = temp_client.assistant_id # Now test using the existing assistant - async with AzureAssistantsClient( + async with AzureOpenAIAssistantsClient( assistant_id=assistant_id, credential=AzureCliCredential() ) as azure_assistants_client: assert isinstance(azure_assistants_client, ChatClientProtocol) @@ -392,9 +392,9 @@ async def test_azure_assistants_client_with_existing_assistant() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_assistants_agent_basic_run(): - """Test ChatAgent basic run functionality with AzureAssistantsClient.""" + """Test ChatAgent basic run functionality with AzureOpenAIAssistantsClient.""" async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), ) as agent: # Run a simple query response = await agent.run("Hello! Please respond with 'Hello World' exactly.") @@ -408,9 +408,9 @@ async def test_azure_assistants_agent_basic_run(): @skip_if_azure_integration_tests_disabled async def test_azure_assistants_agent_basic_run_streaming(): - """Test ChatAgent basic streaming functionality with AzureAssistantsClient.""" + """Test ChatAgent basic streaming functionality with AzureOpenAIAssistantsClient.""" async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), ) as agent: # Run streaming query full_message: str = "" @@ -427,9 +427,9 @@ async def test_azure_assistants_agent_basic_run_streaming(): @skip_if_azure_integration_tests_disabled async def test_azure_assistants_agent_thread_persistence(): - """Test ChatAgent thread persistence across runs with AzureAssistantsClient.""" + """Test ChatAgent thread persistence across runs with AzureOpenAIAssistantsClient.""" async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as agent: # Create a new thread that will be reused @@ -460,7 +460,7 @@ async def test_azure_assistants_agent_existing_thread_id(): existing_thread_id = None async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=[get_weather], ) as agent: @@ -480,7 +480,7 @@ async def test_azure_assistants_agent_existing_thread_id(): # Now continue with the same thread ID in a new agent instance async with ChatAgent( - chat_client=AzureAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=[get_weather], ) as agent: @@ -499,10 +499,10 @@ async def test_azure_assistants_agent_existing_thread_id(): @skip_if_azure_integration_tests_disabled async def test_azure_assistants_agent_code_interpreter(): - """Test ChatAgent with code interpreter through AzureAssistantsClient.""" + """Test ChatAgent with code interpreter through AzureOpenAIAssistantsClient.""" async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can write and execute Python code.", tools=[HostedCodeInterpreterTool()], ) as agent: @@ -521,7 +521,7 @@ async def test_azure_assistants_client_agent_level_tool_persistence(): """Test that agent-level tools persist across multiple runs with Azure Assistants Client.""" async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that uses available tools.", tools=[get_weather], # Agent-level tool ) as agent: @@ -547,8 +547,8 @@ def test_azure_assistants_client_entra_id_authentication() -> None: mock_credential = MagicMock() with ( - patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class, - patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, + patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class, + patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None), ): mock_settings = MagicMock() @@ -561,7 +561,7 @@ def test_azure_assistants_client_entra_id_authentication() -> None: mock_settings.base_url = None mock_settings_class.return_value = mock_settings - client = AzureAssistantsClient( + client = AzureOpenAIAssistantsClient( deployment_name="test-deployment", api_key="placeholder-key", endpoint="https://test-endpoint.openai.azure.com", @@ -578,12 +578,12 @@ def test_azure_assistants_client_entra_id_authentication() -> None: assert call_args["azure_ad_token"] == "entra-token-12345" assert client is not None - assert isinstance(client, AzureAssistantsClient) + assert isinstance(client, AzureOpenAIAssistantsClient) def test_azure_assistants_client_no_authentication_error() -> None: """Test authentication validation error when no auth provided.""" - with patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class: + with patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class: mock_settings = MagicMock() mock_settings.chat_deployment_name = "test-deployment" mock_settings.api_key = None # No API key @@ -592,7 +592,7 @@ def test_azure_assistants_client_no_authentication_error() -> None: # Test missing authentication raises error with pytest.raises(ServiceInitializationError, match="API key, ad_token, or ad_token_provider is required"): - AzureAssistantsClient( + AzureOpenAIAssistantsClient( deployment_name="test-deployment", endpoint="https://test-endpoint.openai.azure.com", # No authentication provided at all @@ -602,8 +602,8 @@ def test_azure_assistants_client_no_authentication_error() -> None: def test_azure_assistants_client_ad_token_authentication() -> None: """Test ad_token authentication client parameter path.""" with ( - patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class, - patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, + patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class, + patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None), ): mock_settings = MagicMock() @@ -614,7 +614,7 @@ def test_azure_assistants_client_ad_token_authentication() -> None: mock_settings.base_url = None mock_settings_class.return_value = mock_settings - client = AzureAssistantsClient( + client = AzureOpenAIAssistantsClient( deployment_name="test-deployment", endpoint="https://test-endpoint.openai.azure.com", ad_token="test-ad-token-12345", @@ -626,7 +626,7 @@ def test_azure_assistants_client_ad_token_authentication() -> None: assert call_args["azure_ad_token"] == "test-ad-token-12345" assert client is not None - assert isinstance(client, AzureAssistantsClient) + assert isinstance(client, AzureOpenAIAssistantsClient) def test_azure_assistants_client_ad_token_provider_authentication() -> None: @@ -636,8 +636,8 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None: mock_token_provider = MagicMock(spec=AsyncAzureADTokenProvider) with ( - patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class, - patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, + patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class, + patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None), ): mock_settings = MagicMock() @@ -648,7 +648,7 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None: mock_settings.base_url = None mock_settings_class.return_value = mock_settings - client = AzureAssistantsClient( + client = AzureOpenAIAssistantsClient( deployment_name="test-deployment", endpoint="https://test-endpoint.openai.azure.com", ad_token_provider=mock_token_provider, @@ -660,14 +660,14 @@ def test_azure_assistants_client_ad_token_provider_authentication() -> None: assert call_args["azure_ad_token_provider"] is mock_token_provider assert client is not None - assert isinstance(client, AzureAssistantsClient) + assert isinstance(client, AzureOpenAIAssistantsClient) def test_azure_assistants_client_base_url_configuration() -> None: """Test base_url client parameter path.""" with ( - patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class, - patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, + patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class, + patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None), ): mock_settings = MagicMock() @@ -678,7 +678,7 @@ def test_azure_assistants_client_base_url_configuration() -> None: mock_settings.api_version = "2024-05-01-preview" mock_settings_class.return_value = mock_settings - client = AzureAssistantsClient( + client = AzureOpenAIAssistantsClient( deployment_name="test-deployment", api_key="test-api-key", base_url="https://custom-base-url.com" ) @@ -689,14 +689,14 @@ def test_azure_assistants_client_base_url_configuration() -> None: assert "azure_endpoint" not in call_args assert client is not None - assert isinstance(client, AzureAssistantsClient) + assert isinstance(client, AzureOpenAIAssistantsClient) def test_azure_assistants_client_azure_endpoint_configuration() -> None: """Test azure_endpoint client parameter path.""" with ( - patch("agent_framework_azure._assistants_client.AzureOpenAISettings") as mock_settings_class, - patch("agent_framework_azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, + patch("agent_framework.azure._assistants_client.AzureOpenAISettings") as mock_settings_class, + patch("agent_framework.azure._assistants_client.AsyncAzureOpenAI") as mock_azure_client, patch("agent_framework.openai.OpenAIAssistantsClient.__init__", return_value=None), ): mock_settings = MagicMock() @@ -707,7 +707,7 @@ def test_azure_assistants_client_azure_endpoint_configuration() -> None: mock_settings.api_version = "2024-05-01-preview" mock_settings_class.return_value = mock_settings - client = AzureAssistantsClient( + client = AzureOpenAIAssistantsClient( deployment_name="test-deployment", api_key="test-api-key", endpoint="https://test-endpoint.openai.azure.com", @@ -720,4 +720,4 @@ def test_azure_assistants_client_azure_endpoint_configuration() -> None: assert "base_url" not in call_args assert client is not None - assert isinstance(client, AzureAssistantsClient) + assert isinstance(client, AzureOpenAIAssistantsClient) diff --git a/python/packages/azure/tests/test_azure_chat_client.py b/python/packages/main/tests/azure/test_azure_chat_client.py similarity index 94% rename from python/packages/azure/tests/test_azure_chat_client.py rename to python/packages/main/tests/azure/test_azure_chat_client.py index 626bb5e9f3..5b4af83f29 100644 --- a/python/packages/azure/tests/test_azure_chat_client.py +++ b/python/packages/main/tests/azure/test_azure_chat_client.py @@ -6,6 +6,16 @@ from unittest.mock import AsyncMock, MagicMock, patch import openai import pytest +from azure.identity import AzureCliCredential +from httpx import Request, Response +from openai import AsyncAzureOpenAI, AsyncStream +from openai.resources.chat.completions import AsyncCompletions as AsyncChatCompletions +from openai.types.chat import ChatCompletion, ChatCompletionChunk +from openai.types.chat.chat_completion import Choice +from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice +from openai.types.chat.chat_completion_chunk import ChoiceDelta as ChunkChoiceDelta +from openai.types.chat.chat_completion_message import ChatCompletionMessage + from agent_framework import ( AgentRunResponse, AgentRunResponseUpdate, @@ -19,22 +29,12 @@ from agent_framework import ( ai_function, ) from agent_framework._telemetry import USER_AGENT_KEY +from agent_framework.azure import AzureOpenAIChatClient from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException from agent_framework.openai import ( ContentFilterResultSeverity, OpenAIContentFilterException, ) -from azure.identity import AzureCliCredential -from httpx import Request, Response -from openai import AsyncAzureOpenAI, AsyncStream -from openai.resources.chat.completions import AsyncCompletions as AsyncChatCompletions -from openai.types.chat import ChatCompletion, ChatCompletionChunk -from openai.types.chat.chat_completion import Choice -from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice -from openai.types.chat.chat_completion_chunk import ChoiceDelta as ChunkChoiceDelta -from openai.types.chat.chat_completion_message import ChatCompletionMessage - -from agent_framework_azure import AzureChatClient # region Service Setup @@ -49,7 +49,7 @@ skip_if_azure_integration_tests_disabled = pytest.mark.skipif( def test_init(azure_openai_unit_test_env: dict[str, str]) -> None: # Test successful initialization - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() assert azure_chat_client.client is not None assert isinstance(azure_chat_client.client, AsyncAzureOpenAI) @@ -60,7 +60,7 @@ def test_init(azure_openai_unit_test_env: dict[str, str]) -> None: def test_init_client(azure_openai_unit_test_env: dict[str, str]) -> None: # Test successful initialization with client client = MagicMock(spec=AsyncAzureOpenAI) - azure_chat_client = AzureChatClient(async_client=client) + azure_chat_client = AzureOpenAIChatClient(async_client=client) assert azure_chat_client.client is not None assert isinstance(azure_chat_client.client, AsyncAzureOpenAI) @@ -70,7 +70,7 @@ def test_init_base_url(azure_openai_unit_test_env: dict[str, str]) -> None: # Custom header for testing default_headers = {"X-Unit-Test": "test-guid"} - azure_chat_client = AzureChatClient( + azure_chat_client = AzureOpenAIChatClient( default_headers=default_headers, ) @@ -85,7 +85,7 @@ def test_init_base_url(azure_openai_unit_test_env: dict[str, str]) -> None: @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_BASE_URL"]], indirect=True) def test_init_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None: - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() assert azure_chat_client.client is not None assert isinstance(azure_chat_client.client, AsyncAzureOpenAI) @@ -96,7 +96,7 @@ def test_init_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None: @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True) def test_init_with_empty_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None: with pytest.raises(ServiceInitializationError): - AzureChatClient( + AzureOpenAIChatClient( env_file_path="test.env", ) @@ -104,7 +104,7 @@ def test_init_with_empty_deployment_name(azure_openai_unit_test_env: dict[str, s @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_BASE_URL"]], indirect=True) def test_init_with_empty_endpoint_and_base_url(azure_openai_unit_test_env: dict[str, str]) -> None: with pytest.raises(ServiceInitializationError): - AzureChatClient( + AzureOpenAIChatClient( env_file_path="test.env", ) @@ -112,7 +112,7 @@ def test_init_with_empty_endpoint_and_base_url(azure_openai_unit_test_env: dict[ @pytest.mark.parametrize("override_env_param_dict", [{"AZURE_OPENAI_ENDPOINT": "http://test.com"}], indirect=True) def test_init_with_invalid_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None: with pytest.raises(ServiceInitializationError): - AzureChatClient() + AzureOpenAIChatClient() @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_BASE_URL"]], indirect=True) @@ -128,7 +128,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None: "env_file_path": "test.env", } - azure_chat_client = AzureChatClient.from_dict(settings) + 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"]) @@ -186,7 +186,7 @@ async def test_cmc( mock_create.return_value = mock_chat_completion_response chat_history.append(ChatMessage(text="hello world", role="user")) - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() await azure_chat_client.get_response( messages=chat_history, ) @@ -210,7 +210,7 @@ async def test_cmc_with_logit_bias( token_bias: dict[str | int, float] = {"1": -100} - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() await azure_chat_client.get_response(messages=chat_history, logit_bias=token_bias) @@ -235,7 +235,7 @@ async def test_cmc_with_stop( stop = ["!"] - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() await azure_chat_client.get_response(messages=chat_history, stop=stop) @@ -296,7 +296,7 @@ async def test_azure_on_your_data( ] } - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() content = await azure_chat_client.get_response( messages=messages_in, @@ -366,7 +366,7 @@ async def test_azure_on_your_data_string( ] } - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() content = await azure_chat_client.get_response( messages=messages_in, @@ -425,7 +425,7 @@ async def test_azure_on_your_data_fail( ] } - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() content = await azure_chat_client.get_response( messages=messages_in, @@ -489,7 +489,7 @@ async def test_content_filtering_raises_correct_exception( }, ) - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() with pytest.raises(OpenAIContentFilterException, match="service encountered a content error") as exc_info: await azure_chat_client.get_response( @@ -533,7 +533,7 @@ async def test_content_filtering_without_response_code_raises_with_default_code( }, ) - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() with pytest.raises(OpenAIContentFilterException, match="service encountered a content error"): await azure_chat_client.get_response( @@ -556,7 +556,7 @@ async def test_bad_request_non_content_filter( "The request was bad.", response=Response(400, request=Request("POST", test_endpoint)), body={} ) - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() with pytest.raises(ServiceResponseException, match="service failed to complete the prompt"): await azure_chat_client.get_response( @@ -574,7 +574,7 @@ async def test_get_streaming( mock_create.return_value = mock_streaming_chat_completion_response chat_history.append(ChatMessage(text="hello world", role="user")) - azure_chat_client = AzureChatClient() + azure_chat_client = AzureOpenAIChatClient() async for msg in azure_chat_client.get_streaming_response( messages=chat_history, ): @@ -612,7 +612,7 @@ def get_weather(location: str) -> str: @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_response() -> None: """Test Azure OpenAI chat completion responses.""" - azure_chat_client = AzureChatClient(credential=AzureCliCredential()) + azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) assert isinstance(azure_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -641,7 +641,7 @@ async def test_azure_openai_chat_client_response() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_response_tools() -> None: """Test AzureOpenAI chat completion responses.""" - azure_chat_client = AzureChatClient(credential=AzureCliCredential()) + azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) assert isinstance(azure_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -662,7 +662,7 @@ async def test_azure_openai_chat_client_response_tools() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_streaming() -> None: """Test Azure OpenAI chat completion responses.""" - azure_chat_client = AzureChatClient(credential=AzureCliCredential()) + azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) assert isinstance(azure_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -696,7 +696,7 @@ async def test_azure_openai_chat_client_streaming() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_streaming_tools() -> None: """Test AzureOpenAI chat completion responses.""" - azure_chat_client = AzureChatClient(credential=AzureCliCredential()) + azure_chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) assert isinstance(azure_chat_client, ChatClientProtocol) messages: list[ChatMessage] = [] @@ -721,9 +721,9 @@ async def test_azure_openai_chat_client_streaming_tools() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_agent_basic_run(): - """Test Azure OpenAI chat client agent basic run functionality with AzureChatClient.""" + """Test Azure OpenAI chat client agent basic run functionality with AzureOpenAIChatClient.""" async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), ) as agent: # Test basic run response = await agent.run("Hello! Please respond with 'Hello World' exactly.") @@ -736,9 +736,9 @@ async def test_azure_openai_chat_client_agent_basic_run(): @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_agent_basic_run_streaming(): - """Test Azure OpenAI chat client agent basic streaming functionality with AzureChatClient.""" + """Test Azure OpenAI chat client agent basic streaming functionality with AzureOpenAIChatClient.""" async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), ) as agent: # Test streaming run full_text = "" @@ -753,9 +753,9 @@ async def test_azure_openai_chat_client_agent_basic_run_streaming(): @skip_if_azure_integration_tests_disabled async def test_azure_openai_chat_client_agent_thread_persistence(): - """Test Azure OpenAI chat client agent thread persistence across runs with AzureChatClient.""" + """Test Azure OpenAI chat client agent thread persistence across runs with AzureOpenAIChatClient.""" async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as agent: # Create a new thread that will be reused @@ -782,7 +782,7 @@ async def test_azure_openai_chat_client_agent_existing_thread(): preserved_thread = None async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as first_agent: # Start a conversation and capture the thread @@ -798,7 +798,7 @@ async def test_azure_openai_chat_client_agent_existing_thread(): # Second conversation - reuse the thread in a new agent instance if preserved_thread: async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as second_agent: # Reuse the preserved thread @@ -814,7 +814,7 @@ async def test_azure_chat_client_agent_level_tool_persistence(): """Test that agent-level tools persist across multiple runs with Azure Chat Client.""" async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that uses available tools.", tools=[get_weather], # Agent-level tool ) as agent: diff --git a/python/packages/azure/tests/test_azure_responses_client.py b/python/packages/main/tests/azure/test_azure_responses_client.py similarity index 91% rename from python/packages/azure/tests/test_azure_responses_client.py rename to python/packages/main/tests/azure/test_azure_responses_client.py index 7efde01314..5e51fc8bcf 100644 --- a/python/packages/azure/tests/test_azure_responses_client.py +++ b/python/packages/main/tests/azure/test_azure_responses_client.py @@ -4,6 +4,9 @@ import os from typing import Annotated import pytest +from azure.identity import AzureCliCredential +from pydantic import BaseModel + from agent_framework import ( AgentRunResponse, AgentRunResponseUpdate, @@ -20,10 +23,8 @@ from agent_framework import ( TextContent, ai_function, ) -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from agent_framework.exceptions import ServiceInitializationError -from azure.identity import AzureCliCredential -from pydantic import BaseModel skip_if_azure_integration_tests_disabled = pytest.mark.skipif( os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true" @@ -48,7 +49,7 @@ async def get_weather(location: Annotated[str, "The location as a city name"]) - return f"The weather in {location} is sunny and 72°F." -async def create_vector_store(client: AzureResponsesClient) -> tuple[str, HostedVectorStoreContent]: +async def create_vector_store(client: AzureOpenAIResponsesClient) -> tuple[str, HostedVectorStoreContent]: """Create a vector store with sample documents for testing.""" file = await client.client.files.create( file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants" @@ -64,7 +65,7 @@ async def create_vector_store(client: AzureResponsesClient) -> tuple[str, Hosted return file.id, HostedVectorStoreContent(vector_store_id=vector_store.id) -async def delete_vector_store(client: AzureResponsesClient, file_id: str, vector_store_id: str) -> None: +async def delete_vector_store(client: AzureOpenAIResponsesClient, file_id: str, vector_store_id: str) -> None: """Delete the vector store after tests.""" await client.client.vector_stores.delete(vector_store_id=vector_store_id) @@ -73,7 +74,7 @@ async def delete_vector_store(client: AzureResponsesClient, file_id: str, vector def test_init(azure_openai_unit_test_env: dict[str, str]) -> None: # Test successful initialization - azure_responses_client = AzureResponsesClient() + azure_responses_client = AzureOpenAIResponsesClient() assert azure_responses_client.ai_model_id == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"] assert isinstance(azure_responses_client, ChatClientProtocol) @@ -82,13 +83,13 @@ def test_init(azure_openai_unit_test_env: dict[str, str]) -> None: def test_init_validation_fail() -> None: # Test successful initialization with pytest.raises(ServiceInitializationError): - AzureResponsesClient(api_key="34523", deployment_name={"test": "dict"}) # type: ignore + AzureOpenAIResponsesClient(api_key="34523", deployment_name={"test": "dict"}) # type: ignore def test_init_ai_model_id_constructor(azure_openai_unit_test_env: dict[str, str]) -> None: # Test successful initialization ai_model_id = "test_model_id" - azure_responses_client = AzureResponsesClient(deployment_name=ai_model_id) + azure_responses_client = AzureOpenAIResponsesClient(deployment_name=ai_model_id) assert azure_responses_client.ai_model_id == ai_model_id assert isinstance(azure_responses_client, ChatClientProtocol) @@ -98,7 +99,7 @@ def test_init_with_default_header(azure_openai_unit_test_env: dict[str, str]) -> default_headers = {"X-Unit-Test": "test-guid"} # Test successful initialization - azure_responses_client = AzureResponsesClient( + azure_responses_client = AzureOpenAIResponsesClient( default_headers=default_headers, ) @@ -114,7 +115,7 @@ def test_init_with_default_header(azure_openai_unit_test_env: dict[str, str]) -> @pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"]], indirect=True) def test_init_with_empty_model_id(azure_openai_unit_test_env: dict[str, str]) -> None: with pytest.raises(ServiceInitializationError): - AzureResponsesClient( + AzureOpenAIResponsesClient( env_file_path="test.env", ) @@ -128,7 +129,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None: "default_headers": default_headers, } - azure_responses_client = AzureResponsesClient.from_dict(settings) + azure_responses_client = AzureOpenAIResponsesClient.from_dict(settings) dumped_settings = azure_responses_client.to_dict() assert dumped_settings["ai_model_id"] == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"] assert dumped_settings["api_key"] == azure_openai_unit_test_env["AZURE_OPENAI_API_KEY"] @@ -143,7 +144,7 @@ def test_serialize(azure_openai_unit_test_env: dict[str, str]) -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_response() -> None: """Test azure responses client responses.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) @@ -186,7 +187,7 @@ async def test_azure_responses_client_response() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_response_tools() -> None: """Test azure responses client tools.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) @@ -225,7 +226,7 @@ async def test_azure_responses_client_response_tools() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_streaming() -> None: """Test Azure azure responses client streaming responses.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) @@ -275,7 +276,7 @@ async def test_azure_responses_client_streaming() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_streaming_tools() -> None: """Test azure responses client streaming tools.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) @@ -321,8 +322,8 @@ async def test_azure_responses_client_streaming_tools() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_agent_basic_run(): - """Test Azure Responses Client agent basic run functionality with AzureResponsesClient.""" - agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent( + """Test Azure Responses Client agent basic run functionality with AzureOpenAIResponsesClient.""" + agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful assistant.", ) @@ -337,9 +338,9 @@ async def test_azure_responses_client_agent_basic_run(): @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_agent_basic_run_streaming(): - """Test Azure Responses Client agent basic streaming functionality with AzureResponsesClient.""" + """Test Azure Responses Client agent basic streaming functionality with AzureOpenAIResponsesClient.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), ) as agent: # Test streaming run full_text = "" @@ -354,9 +355,9 @@ async def test_azure_responses_client_agent_basic_run_streaming(): @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_agent_thread_persistence(): - """Test Azure Responses Client agent thread persistence across runs with AzureResponsesClient.""" + """Test Azure Responses Client agent thread persistence across runs with AzureOpenAIResponsesClient.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as agent: # Create a new thread that will be reused @@ -379,7 +380,7 @@ async def test_azure_responses_client_agent_thread_persistence(): async def test_azure_responses_client_agent_thread_storage_with_store_true(): """Test Azure Responses Client agent with store=True to verify service_thread_id is returned.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", ) as agent: # Create a new thread @@ -413,7 +414,7 @@ async def test_azure_responses_client_agent_existing_thread(): preserved_thread = None async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as first_agent: # Start a conversation and capture the thread @@ -429,7 +430,7 @@ async def test_azure_responses_client_agent_existing_thread(): # Second conversation - reuse the thread in a new agent instance if preserved_thread: async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant with good memory.", ) as second_agent: # Reuse the preserved thread @@ -442,9 +443,9 @@ async def test_azure_responses_client_agent_existing_thread(): @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_agent_hosted_code_interpreter_tool(): - """Test Azure Responses Client agent with HostedCodeInterpreterTool through AzureResponsesClient.""" + """Test Azure Responses Client agent with HostedCodeInterpreterTool through AzureOpenAIResponsesClient.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can execute Python code.", tools=[HostedCodeInterpreterTool()], ) as agent: @@ -466,7 +467,7 @@ async def test_azure_responses_client_agent_level_tool_persistence(): """Test that agent-level tools persist across multiple runs with Azure Responses Client.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that uses available tools.", tools=[get_weather], # Agent-level tool ) as agent: @@ -491,7 +492,7 @@ async def test_azure_responses_client_agent_level_tool_persistence(): async def test_azure_responses_client_agent_chat_options_run_level() -> None: """Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", ) as agent: response = await agent.run( @@ -514,7 +515,7 @@ async def test_azure_responses_client_agent_chat_options_run_level() -> None: async def test_azure_responses_client_agent_chat_options_agent_level() -> None: """Integration test for comprehensive ChatOptions parameter coverage with Azure Response Agent.""" async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", max_tokens=100, temperature=0.7, @@ -536,7 +537,7 @@ async def test_azure_responses_client_agent_chat_options_agent_level() -> None: @skip_if_azure_integration_tests_disabled async def test_azure_responses_client_agent_hosted_mcp_tool() -> None: """Integration test for HostedMCPTool with Azure Response Agent using Microsoft Learn MCP.""" - # Use the same MCP server as the Foundry example + mcp_tool = HostedMCPTool( name="Microsoft Learn MCP", url="https://learn.microsoft.com/api/mcp", @@ -545,11 +546,10 @@ async def test_azure_responses_client_agent_hosted_mcp_tool() -> None: ) async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can help with microsoft documentation questions.", tools=[mcp_tool], ) as agent: - # Use the same query as the Foundry example response = await agent.run( "How to create an Azure storage account using az cli?", max_tokens=200, @@ -566,7 +566,7 @@ async def test_azure_responses_client_agent_hosted_mcp_tool() -> None: @pytest.mark.skip(reason="File search requires API key auth, subscription only allows token auth") async def test_azure_responses_client_file_search() -> None: """Test Azure responses client with file search tool.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) @@ -592,7 +592,7 @@ async def test_azure_responses_client_file_search() -> None: @pytest.mark.skip(reason="File search requires API key auth, subscription only allows token auth") async def test_azure_responses_client_file_search_streaming() -> None: """Test Azure responses client with file search tool and streaming.""" - azure_responses_client = AzureResponsesClient(credential=AzureCliCredential()) + azure_responses_client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) assert isinstance(azure_responses_client, ChatClientProtocol) diff --git a/python/packages/azure/tests/test_entra_id_authentication.py b/python/packages/main/tests/azure/test_entra_id_authentication.py similarity index 99% rename from python/packages/azure/tests/test_entra_id_authentication.py rename to python/packages/main/tests/azure/test_entra_id_authentication.py index 949e2a70be..b8e8c543b9 100644 --- a/python/packages/azure/tests/test_entra_id_authentication.py +++ b/python/packages/main/tests/azure/test_entra_id_authentication.py @@ -3,13 +3,13 @@ from unittest.mock import AsyncMock, MagicMock import pytest -from agent_framework.exceptions import ServiceInvalidAuthError from azure.core.exceptions import ClientAuthenticationError -from agent_framework_azure._entra_id_authentication import ( +from agent_framework.azure._entra_id_authentication import ( get_entra_auth_token, get_entra_auth_token_async, ) +from agent_framework.exceptions import ServiceInvalidAuthError @pytest.fixture diff --git a/python/packages/main/tests/openai/test_openai_responses_client.py b/python/packages/main/tests/openai/test_openai_responses_client.py index 61c0117eef..31cc68e5b4 100644 --- a/python/packages/main/tests/openai/test_openai_responses_client.py +++ b/python/packages/main/tests/openai/test_openai_responses_client.py @@ -1560,7 +1560,7 @@ async def test_openai_responses_client_agent_chat_options_agent_level() -> None: @skip_if_openai_integration_tests_disabled async def test_openai_responses_client_agent_hosted_mcp_tool() -> None: """Integration test for HostedMCPTool with OpenAI Response Agent using Microsoft Learn MCP.""" - # Use the same MCP server as the Foundry example + mcp_tool = HostedMCPTool( name="Microsoft Learn MCP", url="https://learn.microsoft.com/api/mcp", @@ -1573,7 +1573,6 @@ async def test_openai_responses_client_agent_hosted_mcp_tool() -> None: instructions="You are a helpful assistant that can help with microsoft documentation questions.", tools=[mcp_tool], ) as agent: - # Use the same query as the Foundry example response = await agent.run( "How to create an Azure storage account using az cli?", max_tokens=200, diff --git a/python/packages/mem0/pyproject.toml b/python/packages/mem0/pyproject.toml index 5e95378c60..89356a9a55 100644 --- a/python/packages/mem0/pyproject.toml +++ b/python/packages/mem0/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-mem0" description = "Mem0 integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" diff --git a/python/packages/mem0/tests/test_cross_package.py b/python/packages/mem0/tests/test_cross_package.py deleted file mode 100644 index 7d6e156702..0000000000 --- a/python/packages/mem0/tests/test_cross_package.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - - -def test_self_through_main() -> None: - try: - from agent_framework.mem0 import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_self() -> None: - try: - from agent_framework_mem0 import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_agent_framework() -> None: - try: - from agent_framework import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None diff --git a/python/packages/mem0/tests/test_provider.py b/python/packages/mem0/tests/test_mem0_context_provider.py similarity index 100% rename from python/packages/mem0/tests/test_provider.py rename to python/packages/mem0/tests/test_mem0_context_provider.py diff --git a/python/packages/redis/pyproject.toml b/python/packages/redis/pyproject.toml index 4fe7b0b36e..f7ad9ecf27 100644 --- a/python/packages/redis/pyproject.toml +++ b/python/packages/redis/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-redis" description = "Redis integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.0.0b1" diff --git a/python/packages/redis/tests/test_redis_cross_package.py b/python/packages/redis/tests/test_redis_cross_package.py deleted file mode 100644 index 8d522a57c2..0000000000 --- a/python/packages/redis/tests/test_redis_cross_package.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - - -def test_self_through_main() -> None: - try: - from agent_framework.redis import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_self() -> None: - try: - from agent_framework_redis import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None - - -def test_agent_framework() -> None: - try: - from agent_framework import __version__ - except ImportError: - __version__ = None - - assert __version__ is not None diff --git a/python/packages/runtime/pyproject.toml b/python/packages/runtime/pyproject.toml index a22c1ba683..53fe754fe8 100644 --- a/python/packages/runtime/pyproject.toml +++ b/python/packages/runtime/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "agent-framework-runtime" description = "Runtime integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] readme = "README.md" requires-python = ">=3.10" version = "0.1.0b1" diff --git a/python/pyproject.toml b/python/pyproject.toml index 3c4e0f8936..6837629887 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -5,9 +5,8 @@ version = "0.0.0" requires-python = ">=3.10" dependencies = [ "agent-framework", - "agent-framework-azure", + "agent-framework-azure-ai", "agent-framework-copilotstudio", - "agent-framework-foundry", "agent-framework-mem0", "agent-framework-redis", "agent-framework-devui", @@ -55,9 +54,8 @@ exclude = [ "packages/agent_framework_project.egg-info", "packages/lab", "packag [tool.uv.sources] agent-framework = { workspace = true } -agent-framework-azure = { workspace = true } +agent-framework-azure-ai = { workspace = true } agent-framework-copilotstudio = { workspace = true } -agent-framework-foundry = { workspace = true } agent-framework-lab-gaia = { workspace = true } agent-framework-mem0 = { workspace = true } agent-framework-redis = { workspace = true } @@ -70,6 +68,9 @@ target-version = "py310" fix = true include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"] exclude = ["docs/*", "run_tasks_in_packages_if_exists.py", "check_md_code_blocks.py"] +extend-exclude = [ + "[{][{]cookiecutter.package_name[}][}]", +] preview = true [tool.ruff.lint] @@ -131,7 +132,7 @@ filterwarnings = [] timeout = 120 markers = [ "azure: marks tests as Azure provider specific", - "foundry: marks tests as Foundry provider specific", + "azure-ai: marks tests as Azure AI provider specific", "openai: marks tests as OpenAI provider specific", ] @@ -187,8 +188,30 @@ build = "python run_tasks_in_packages_if_exists.py build" # combined checks check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint", "samples-code-check"] pre-commit-check = ["fmt", "lint", "pyright", "markdown-code-lint", "samples-code-check"] -all-tests = "pytest --import-mode=importlib --cov=agent_framework --cov=agent_framework_azure --cov=agent_framework_copilotstudio --cov=agent_framework_foundry --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered packages/azure/tests packages/copilotstudio/tests packages/foundry/tests packages/main/tests packages/mem0/tests packages/redis/tests" +[tool.poe.tasks.all-tests-cov] +cmd = """ +pytest --import-mode=importlib +--cov=agent_framework +--cov=agent_framework_azure_ai +--cov=agent_framework_copilotstudio +--cov=agent_framework_mem0 +--cov=agent_framework_redis +--cov-report=term-missing:skip-covered +--ignore-glob=packages/lab/** +--ignore-glob=packages/devui/** +-n logical --dist loadfile --dist worksteal +packages/**/tests +""" + +[tool.poe.tasks.all-tests] +cmd = """ +pytest --import-mode=importlib +--ignore-glob=packages/lab/** +--ignore-glob=packages/devui/** +-n logical --dist loadfile --dist worksteal +packages/**/tests +""" [tool.poe.tasks.venv] cmd = "uv venv --clear --python $python" @@ -227,4 +250,4 @@ sequence = [ [tool.setuptools.packages.find] where = ["packages"] include = ["agent_framework**"] -namespaces = true \ No newline at end of file +namespaces = true diff --git a/python/samples/getting_started/agents/README.md b/python/samples/getting_started/agents/README.md index 80aa37a533..15dfee08da 100644 --- a/python/samples/getting_started/agents/README.md +++ b/python/samples/getting_started/agents/README.md @@ -8,7 +8,7 @@ This folder contains examples demonstrating how to create and use agents with di | Folder | Description | |--------|-------------| -| **[`foundry/`](foundry/)** | Create agents using Azure AI Foundry | +| **[`azure_ai/`](azure_ai/)** | Create agents using Azure AI Foundry Agent Service | ### Microsoft Copilot Studio Examples @@ -20,14 +20,17 @@ This folder contains examples demonstrating how to create and use agents with di | Folder | Description | |--------|-------------| -| **[`azure_assistants_client/`](azure_assistants_client/)** | Create agents using Azure OpenAI Assistants API | -| **[`azure_chat_client/`](azure_chat_client/)** | Create agents using Azure OpenAI Chat Completions API | -| **[`azure_responses_client/`](azure_responses_client/)** | Create agents using Azure OpenAI Responses API | +| **[`azure_openai/`](azure_openai/)** | Create agents using Azure OpenAI APIs | ### OpenAI Examples | Folder | Description | |--------|-------------| -| **[`openai_assistants_client/`](openai_assistants_client/)** | Create agents using OpenAI Assistants API | -| **[`openai_chat_client/`](openai_chat_client/)** | Create agents using OpenAI Chat Completions API | -| **[`openai_responses_client/`](openai_responses_client/)** | Create agents using OpenAI Responses API | +| **[`openai/`](openai/)** | Create agents using OpenAI APIs | + +### Custom Client Examples + +| Folder | Description | +|--------|-------------| +| **[`custom_client/`](custom_client/)** | Create agents using a custom chat client or a custom agent | +| **[`anthropic/`](anthropic/)** | Create agents using Anthropic APIs | diff --git a/python/samples/getting_started/agents/azure_ai/README.md b/python/samples/getting_started/agents/azure_ai/README.md new file mode 100644 index 0000000000..57e465fa91 --- /dev/null +++ b/python/samples/getting_started/agents/azure_ai/README.md @@ -0,0 +1,25 @@ +# Azure AI Agent Examples + +This folder contains examples demonstrating different ways to create and use agents with the Azure AI chat client from the `agent_framework.azure` package. + +## Examples + +| File | Description | +|------|-------------| +| [`azure_ai_basic.py`](azure_ai_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAIAgentClient`. It automatically handles all configuration using environment variables. | +| [`azure_ai_with_explicit_settings.py`](azure_ai_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `AzureAIAgentClient` settings, including project endpoint, model deployment, credentials, and agent name. | +| [`azure_ai_with_existing_agent.py`](azure_ai_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Azure AI chat client. This example also demonstrates proper cleanup of manually created agents. | +| [`azure_ai_with_function_tools.py`](azure_ai_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`azure_ai_with_code_interpreter.py`](azure_ai_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure AI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`azure_ai_with_local_mcp.py`](azure_ai_with_local_mcp.py) | Shows how to integrate Azure AI agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. | +| [`azure_ai_with_thread.py`](azure_ai_with_thread.py) | Demonstrates thread management with Azure AI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | + +## Environment Variables + +Make sure to set the following environment variables before running the examples: + +- `AZURE_AZURE_FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI project endpoint +- `AZURE_AZURE_FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment + +Optionally, you can set: +- `AZURE_AZURE_FOUNDRY_AGENT_NAME`: The name of your agent, this can also be set programmatically when creating the agent. diff --git a/python/samples/getting_started/agents/foundry/foundry_basic.py b/python/samples/getting_started/agents/azure_ai/azure_ai_basic.py similarity index 90% rename from python/samples/getting_started/agents/foundry/foundry_basic.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_basic.py index cc396379a0..b05dcf92a3 100644 --- a/python/samples/getting_started/agents/foundry/foundry_basic.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_basic.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -27,7 +27,7 @@ async def non_streaming_example() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather agent.", tools=get_weather, @@ -49,7 +49,7 @@ async def streaming_example() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather agent.", tools=get_weather, @@ -65,7 +65,7 @@ async def streaming_example() -> None: async def main() -> None: - print("=== Basic Foundry Chat Client Agent Example ===") + print("=== Basic Azure AI Chat Client Agent Example ===") await non_streaming_example() await streaming_example() diff --git a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter.py similarity index 79% rename from python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter.py index faf63b73e4..1e5e8bb155 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_code_interpreter.py @@ -2,20 +2,16 @@ import asyncio -from agent_framework import ( - AgentRunResponse, - HostedCodeInterpreterTool, +from agent_framework import AgentRunResponse, ChatResponseUpdate, HostedCodeInterpreterTool +from agent_framework.azure import AzureAIAgentClient +from azure.ai.agents.models import ( + RunStepDeltaCodeInterpreterDetailItemObject, ) -from agent_framework.foundry import FoundryChatClient from azure.identity.aio import AzureCliCredential def print_code_interpreter_inputs(response: AgentRunResponse) -> None: """Helper method to access code interpreter data.""" - from agent_framework import ChatResponseUpdate - from azure.ai.agents.models import ( - RunStepDeltaCodeInterpreterDetailItemObject, - ) print("\nCode Interpreter Inputs during the run:") if response.raw_representation is None: @@ -29,14 +25,14 @@ def print_code_interpreter_inputs(response: AgentRunResponse) -> None: async def main() -> None: - """Example showing how to use the HostedCodeInterpreterTool with Foundry.""" - print("=== Foundry Agent with Code Interpreter Example ===") + """Example showing how to use the HostedCodeInterpreterTool with Azure AI.""" + print("=== Azure AI Agent with Code Interpreter Example ===") # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential) as chat_client, + AzureAIAgentClient(async_credential=credential) as chat_client, ): agent = chat_client.create_agent( name="CodingAgent", diff --git a/python/samples/getting_started/agents/foundry/foundry_with_existing_agent.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_existing_agent.py similarity index 79% rename from python/samples/getting_started/agents/foundry/foundry_with_existing_agent.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_existing_agent.py index 2d90316c28..7dd2f81801 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_existing_agent.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_existing_agent.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.ai.projects.aio import AIProjectClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -21,23 +21,23 @@ def get_weather( async def main() -> None: - print("=== Foundry Chat Client with Existing Agent ===") + print("=== Azure AI Chat Client with Existing Agent ===") # Create the client async with ( AzureCliCredential() as credential, - AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as client, + AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as client, ): # Create an agent that will persist created_agent = await client.agents.create_agent( - model=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent" + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], name="WeatherAgent" ) try: async with ChatAgent( # passing in the client is optional here, so if you take the agent_id from the portal # you can use it directly without the two lines above. - chat_client=FoundryChatClient(client=client, agent_id=created_agent.id), + chat_client=AzureAIAgentClient(client=client, agent_id=created_agent.id), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: diff --git a/python/samples/getting_started/agents/foundry/foundry_with_explicit_settings.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_explicit_settings.py similarity index 80% rename from python/samples/getting_started/agents/foundry/foundry_with_explicit_settings.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_explicit_settings.py index c89e78c6c8..890b42face 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_explicit_settings.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_explicit_settings.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -20,7 +20,7 @@ def get_weather( async def main() -> None: - print("=== Foundry Chat Client with Explicit Settings ===") + print("=== Azure AI Chat Client with Explicit Settings ===") # Since no Agent ID is provided, the agent will be automatically created # and deleted after getting a response @@ -29,9 +29,9 @@ async def main() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient( - project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], - model_deployment_name=os.environ["FOUNDRY_MODEL_DEPLOYMENT_NAME"], + chat_client=AzureAIAgentClient( + project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], + model_deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], async_credential=credential, agent_name="WeatherAgent", ), diff --git a/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_function_tools.py similarity index 93% rename from python/samples/getting_started/agents/foundry/foundry_with_function_tools.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_function_tools.py index 65cd3d1da9..c22fbd431c 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_function_tools.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -36,7 +36,7 @@ async def tools_on_agent_level() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a helpful assistant that can provide weather and time information.", tools=[get_weather, get_time], # Tools defined at agent creation ) as agent, @@ -70,7 +70,7 @@ async def tools_on_run_level() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a helpful assistant.", # No tools defined here ) as agent, @@ -104,7 +104,7 @@ async def mixed_tools_example() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a comprehensive assistant that can help with various information requests.", tools=[get_weather], # Base tool available for all queries ) as agent, @@ -122,7 +122,7 @@ async def mixed_tools_example() -> None: async def main() -> None: - print("=== Foundry Chat Client Agent with Function Tools Examples ===\n") + print("=== Azure AI Chat Client Agent with Function Tools Examples ===\n") await tools_on_agent_level() await tools_on_run_level() diff --git a/python/samples/getting_started/agents/foundry/foundry_with_hosted_mcp.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py similarity index 89% rename from python/samples/getting_started/agents/foundry/foundry_with_hosted_mcp.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py index a44d790137..3a96a7f2cc 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_hosted_mcp.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_hosted_mcp.py @@ -4,7 +4,7 @@ import asyncio from typing import Any from agent_framework import AgentProtocol, AgentThread, HostedMCPTool -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential @@ -32,13 +32,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa async def main() -> None: - """Example showing Hosted MCP tools for a Foundry Agent.""" + """Example showing Hosted MCP tools for a Azure AI Agent.""" async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential) as chat_client, + AzureAIAgentClient(async_credential=credential) as chat_client, ): - # enable foundry observability - await chat_client.setup_foundry_observability() + # enable azure-ai observability + await chat_client.setup_observability() agent = chat_client.create_agent( name="DocsAgent", instructions="You are a helpful assistant that can help with microsoft documentation questions.", diff --git a/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_local_mcp.py similarity index 91% rename from python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_local_mcp.py index 401a62f2a7..71a50708ac 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_local_mcp.py @@ -3,7 +3,7 @@ import asyncio from agent_framework import ChatAgent, MCPStreamableHTTPTool -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential @@ -21,7 +21,7 @@ async def mcp_tools_on_run_level() -> None: url="https://learn.microsoft.com/api/mcp", ) as mcp_server, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), name="DocsAgent", instructions="You are a helpful assistant that can help with microsoft documentation questions.", ) as agent, @@ -48,7 +48,7 @@ async def mcp_tools_on_agent_level() -> None: # The agent will connect to the MCP server through its context manager. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="DocsAgent", instructions="You are a helpful assistant that can help with microsoft documentation questions.", tools=MCPStreamableHTTPTool( # Tools defined at agent creation @@ -71,7 +71,7 @@ async def mcp_tools_on_agent_level() -> None: async def main() -> None: - print("=== Foundry Chat Client Agent with MCP Tools Examples ===\n") + print("=== Azure AI Chat Client Agent with MCP Tools Examples ===\n") await mcp_tools_on_agent_level() await mcp_tools_on_run_level() diff --git a/python/samples/getting_started/agents/foundry/foundry_with_multiple_tools.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_multiple_tools.py similarity index 90% rename from python/samples/getting_started/agents/foundry/foundry_with_multiple_tools.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_multiple_tools.py index 52d510b52f..89eccdc0df 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_multiple_tools.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_multiple_tools.py @@ -10,7 +10,7 @@ from agent_framework import ( HostedMCPTool, HostedWebSearchTool, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential @@ -44,13 +44,13 @@ async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", threa async def main() -> None: - """Example showing Hosted MCP tools for a Foundry Agent.""" + """Example showing Hosted MCP tools for a Azure AI Agent.""" async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential) as chat_client, + AzureAIAgentClient(async_credential=credential) as chat_client, ): - # enable foundry observability - await chat_client.setup_foundry_observability() + # enable azure-ai observability + await chat_client.setup_observability() agent = chat_client.create_agent( name="DocsAgent", instructions="You are a helpful assistant that can help with microsoft documentation questions.", diff --git a/python/samples/getting_started/agents/foundry/foundry_with_thread.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_thread.py similarity index 92% rename from python/samples/getting_started/agents/foundry/foundry_with_thread.py rename to python/samples/getting_started/agents/azure_ai/azure_ai_with_thread.py index 3118ce5498..9eb564c240 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_thread.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_thread.py @@ -5,7 +5,7 @@ from random import randint from typing import Annotated from agent_framework import AgentThread, ChatAgent -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -27,7 +27,7 @@ async def example_with_automatic_thread_creation() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent, @@ -56,7 +56,7 @@ async def example_with_thread_persistence() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent, @@ -97,7 +97,7 @@ async def example_with_existing_thread_id() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(async_credential=credential), + chat_client=AzureAIAgentClient(async_credential=credential), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent, @@ -120,7 +120,7 @@ async def example_with_existing_thread_id() -> None: async with ( AzureCliCredential() as credential, ChatAgent( - chat_client=FoundryChatClient(thread_id=existing_thread_id, async_credential=credential), + chat_client=AzureAIAgentClient(thread_id=existing_thread_id, async_credential=credential), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent, @@ -136,7 +136,7 @@ async def example_with_existing_thread_id() -> None: async def main() -> None: - print("=== Foundry Chat Client Agent Thread Management Examples ===\n") + print("=== Azure AI Chat Client Agent Thread Management Examples ===\n") await example_with_automatic_thread_creation() await example_with_thread_persistence() diff --git a/python/samples/getting_started/agents/azure_assistants_client/README.md b/python/samples/getting_started/agents/azure_assistants_client/README.md deleted file mode 100644 index 2bd80bb27f..0000000000 --- a/python/samples/getting_started/agents/azure_assistants_client/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Azure Assistants Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the Azure Assistants client from the `agent_framework.azure` package. - -## Examples - -| File | Description | -|------|-------------| -| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. | -| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. | -| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. | -| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | -| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint -- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment - -## Authentication - -All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method. - -## Required role-based access control (RBAC) roles - -To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource: - -- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples. -- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models. - -For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section. - -For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control) diff --git a/python/samples/getting_started/agents/azure_chat_client/README.md b/python/samples/getting_started/agents/azure_chat_client/README.md deleted file mode 100644 index ee923c50d9..0000000000 --- a/python/samples/getting_started/agents/azure_chat_client/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Azure Chat Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the Azure Chat client from the `agent_framework.azure` package. - -## Examples - -| File | Description | -|------|-------------| -| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. | -| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. | -| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint -- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment - -## Authentication - -All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method. - -## Required role-based access control (RBAC) roles - -To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource: - -- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples. -- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models. - -For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section. - -For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control) diff --git a/python/samples/getting_started/agents/azure_openai/README.md b/python/samples/getting_started/agents/azure_openai/README.md new file mode 100644 index 0000000000..1c93c1dcb9 --- /dev/null +++ b/python/samples/getting_started/agents/azure_openai/README.md @@ -0,0 +1,51 @@ +# Azure OpenAI Agent Examples + +This folder contains examples demonstrating different ways to create and use agents with the different Azure OpenAI chat client from the `agent_framework.azure` package. + +## Examples + +| File | Description | +|------|-------------| +| [`azure_assistants_basic.py`](azure_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. | +| [`azure_assistants_with_existing_assistant.py`](azure_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the Azure Assistants client. Demonstrates proper cleanup of manually created assistants. | +| [`azure_assistants_with_explicit_settings.py`](azure_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including endpoint and deployment name. | +| [`azure_assistants_with_function_tools.py`](azure_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`azure_assistants_with_code_interpreter.py`](azure_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`azure_assistants_with_thread.py`](azure_assistants_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | +| [`azure_chat_client_basic.py`](azure_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with Azure OpenAI models. | +| [`azure_chat_client_with_explicit_settings.py`](azure_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including endpoint and deployment name. | +| [`azure_chat_client_with_function_tools.py`](azure_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`azure_chat_client_with_thread.py`](azure_chat_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | +| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureOpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. | +| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. | +| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | + +## Environment Variables + +Make sure to set the following environment variables before running the examples: + +- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint +- `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat model deployment +- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI Responses deployment + +Optionally, you can set: +- `AZURE_OPENAI_API_VERSION`: The API version to use (default is `2024-02-15-preview`) +- `AZURE_OPENAI_API_KEY`: Your Azure OpenAI API key (if not using `AzureCliCredential`) +- `AZURE_OPENAI_BASE_URL`: Your Azure OpenAI base URL (if different from the endpoint) + +## Authentication + +All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method. + +## Required role-based access control (RBAC) roles + +To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource: + +- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples. +- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models. + +For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section. + +For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control) diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_basic.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_basic.py similarity index 89% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_basic.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_basic.py index b1b3f1591f..7d75933d54 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_basic.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_basic.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from pydantic import Field @@ -25,7 +25,7 @@ async def non_streaming_example() -> None: # and deleted after getting a response # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent( + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: @@ -41,7 +41,7 @@ async def streaming_example() -> None: # Since no assistant ID is provided, the assistant will be automatically created # and deleted after getting a response - async with AzureAssistantsClient(credential=AzureCliCredential()).create_agent( + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_code_interpreter.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_code_interpreter.py similarity index 94% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_code_interpreter.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_with_code_interpreter.py index 6dfce9a885..10a0a211b8 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_code_interpreter.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_code_interpreter.py @@ -3,7 +3,7 @@ import asyncio from agent_framework import AgentRunResponseUpdate, ChatAgent, ChatResponseUpdate, HostedCodeInterpreterTool -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from openai.types.beta.threads.runs import ( CodeInterpreterToolCallDelta, @@ -40,7 +40,7 @@ async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can write and execute Python code to solve problems.", tools=HostedCodeInterpreterTool(), ) as agent: diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_existing_assistant.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_existing_assistant.py similarity index 90% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_existing_assistant.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_with_existing_assistant.py index ebd3fc883a..45d1523a0a 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_existing_assistant.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_existing_assistant.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential, get_bearer_token_provider from openai import AsyncAzureOpenAI from pydantic import Field @@ -38,7 +38,7 @@ async def main() -> None: try: async with ChatAgent( - chat_client=AzureAssistantsClient(async_client=client, assistant_id=created_assistant.id), + chat_client=AzureOpenAIAssistantsClient(async_client=client, assistant_id=created_assistant.id), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_explicit_settings.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_explicit_settings.py similarity index 91% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_explicit_settings.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_with_explicit_settings.py index 4bb1474a4c..e412b1ef06 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_explicit_settings.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_explicit_settings.py @@ -5,7 +5,7 @@ import os from random import randint from typing import Annotated -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from pydantic import Field @@ -23,7 +23,7 @@ async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - async with AzureAssistantsClient( + async with AzureOpenAIAssistantsClient( endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"], credential=AzureCliCredential(), diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_function_tools.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_function_tools.py similarity index 93% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_function_tools.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_with_function_tools.py index 3986ea921a..7e5e4757d2 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_function_tools.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_function_tools.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from pydantic import Field @@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can provide weather and time information.", tools=[get_weather, get_time], # Tools defined at agent creation ) as agent: @@ -65,7 +65,7 @@ async def tools_on_run_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", # No tools defined here ) as agent: @@ -96,7 +96,7 @@ async def mixed_tools_example() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a comprehensive assistant that can help with various information requests.", tools=[get_weather], # Base tool available for all queries ) as agent: diff --git a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_thread.py b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_thread.py similarity index 92% rename from python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_thread.py rename to python/samples/getting_started/agents/azure_openai/azure_assistants_with_thread.py index ecc8537704..758771a8e1 100644 --- a/python/samples/getting_started/agents/azure_assistants_client/azure_assistants_with_thread.py +++ b/python/samples/getting_started/agents/azure_openai/azure_assistants_with_thread.py @@ -5,7 +5,7 @@ from random import randint from typing import Annotated from agent_framework import AgentThread, ChatAgent -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from pydantic import Field @@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: @@ -51,7 +51,7 @@ async def example_with_thread_persistence() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: @@ -89,7 +89,7 @@ async def example_with_existing_thread_id() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: @@ -109,7 +109,7 @@ async def example_with_existing_thread_id() -> None: # Create a new agent instance but use the existing thread ID async with ChatAgent( - chat_client=AzureAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()), + chat_client=AzureOpenAIAssistantsClient(thread_id=existing_thread_id, credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: diff --git a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_basic.py b/python/samples/getting_started/agents/azure_openai/azure_chat_client_basic.py similarity index 90% rename from python/samples/getting_started/agents/azure_chat_client/azure_chat_client_basic.py rename to python/samples/getting_started/agents/azure_openai/azure_chat_client_basic.py index fbdea95016..a7efcf9c6a 100644 --- a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_basic.py +++ b/python/samples/getting_started/agents/azure_openai/azure_chat_client_basic.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import Field @@ -24,7 +24,7 @@ async def non_streaming_example() -> None: # Create agent with Azure Chat Client # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureChatClient(credential=AzureCliCredential()).create_agent( + agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -42,7 +42,7 @@ async def streaming_example() -> None: # Create agent with Azure Chat Client # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureChatClient(credential=AzureCliCredential()).create_agent( + agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) diff --git a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_explicit_settings.py b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_explicit_settings.py similarity index 92% rename from python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_explicit_settings.py rename to python/samples/getting_started/agents/azure_openai/azure_chat_client_with_explicit_settings.py index c640cc4fd2..411c8fa03d 100644 --- a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_explicit_settings.py +++ b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_explicit_settings.py @@ -5,7 +5,7 @@ import os from random import randint from typing import Annotated -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import Field @@ -23,7 +23,7 @@ async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureChatClient( + agent = AzureOpenAIChatClient( deployment_name=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"], endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], credential=AzureCliCredential(), diff --git a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_function_tools.py b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_function_tools.py similarity index 93% rename from python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_function_tools.py rename to python/samples/getting_started/agents/azure_openai/azure_chat_client_with_function_tools.py index 7aa7f9b0b3..a18c3eef62 100644 --- a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_function_tools.py +++ b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_function_tools.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import Field @@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can provide weather and time information.", tools=[get_weather, get_time], # Tools defined at agent creation ) @@ -66,7 +66,7 @@ async def tools_on_run_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", # No tools defined here ) @@ -98,7 +98,7 @@ async def mixed_tools_example() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a comprehensive assistant that can help with various information requests.", tools=[get_weather], # Base tool available for all queries ) diff --git a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_thread.py similarity index 93% rename from python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py rename to python/samples/getting_started/agents/azure_openai/azure_chat_client_with_thread.py index 3dffbdf863..45533fcb3d 100644 --- a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py +++ b/python/samples/getting_started/agents/azure_openai/azure_chat_client_with_thread.py @@ -5,7 +5,7 @@ from random import randint from typing import Annotated from agent_framework import AgentThread, ChatAgent, ChatMessageList -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import Field @@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -52,7 +52,7 @@ async def example_with_thread_persistence() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -87,7 +87,7 @@ async def example_with_existing_thread_messages() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -109,7 +109,7 @@ async def example_with_existing_thread_messages() -> None: # Create a new agent instance but use the existing thread with its message history new_agent = ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) diff --git a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_basic.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_basic.py similarity index 89% rename from python/samples/getting_started/agents/azure_responses_client/azure_responses_client_basic.py rename to python/samples/getting_started/agents/azure_openai/azure_responses_client_basic.py index 40a4e41e5b..0f80ac47d5 100644 --- a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_basic.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_basic.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from pydantic import Field @@ -23,7 +23,7 @@ async def non_streaming_example() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent( + agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -40,7 +40,7 @@ async def streaming_example() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureResponsesClient(credential=AzureCliCredential()).create_agent( + agent = AzureOpenAIResponsesClient(credential=AzureCliCredential()).create_agent( instructions="You are a helpful weather agent.", tools=get_weather, ) diff --git a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_code_interpreter.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_code_interpreter.py similarity index 91% rename from python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_code_interpreter.py rename to python/samples/getting_started/agents/azure_openai/azure_responses_client_with_code_interpreter.py index edfa533dcb..60164b6c83 100644 --- a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_code_interpreter.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_code_interpreter.py @@ -3,7 +3,7 @@ import asyncio from agent_framework import ChatAgent, ChatResponse, HostedCodeInterpreterTool -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from openai.types.responses.response import Response as OpenAIResponse from openai.types.responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall @@ -16,7 +16,7 @@ async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can write and execute Python code to solve problems.", tools=HostedCodeInterpreterTool(), ) diff --git a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_explicit_settings.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_explicit_settings.py similarity index 92% rename from python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_explicit_settings.py rename to python/samples/getting_started/agents/azure_openai/azure_responses_client_with_explicit_settings.py index 9efaf6214e..4f06828643 100644 --- a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_explicit_settings.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_explicit_settings.py @@ -5,7 +5,7 @@ import os from random import randint from typing import Annotated -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from pydantic import Field @@ -23,7 +23,7 @@ async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - agent = AzureResponsesClient( + agent = AzureOpenAIResponsesClient( deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"], endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], credential=AzureCliCredential(), diff --git a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_function_tools.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_function_tools.py similarity index 93% rename from python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_function_tools.py rename to python/samples/getting_started/agents/azure_openai/azure_responses_client_with_function_tools.py index c5ff02d69d..41a5c9dde5 100644 --- a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_function_tools.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_function_tools.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from pydantic import Field @@ -34,7 +34,7 @@ async def tools_on_agent_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant that can provide weather and time information.", tools=[get_weather, get_time], # Tools defined at agent creation ) @@ -66,7 +66,7 @@ async def tools_on_run_level() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful assistant.", # No tools defined here ) @@ -98,7 +98,7 @@ async def mixed_tools_example() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a comprehensive assistant that can help with various information requests.", tools=[get_weather], # Base tool available for all queries ) diff --git a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_thread.py b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_thread.py similarity index 92% rename from python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_thread.py rename to python/samples/getting_started/agents/azure_openai/azure_responses_client_with_thread.py index 96483debb7..2c78cf675a 100644 --- a/python/samples/getting_started/agents/azure_responses_client/azure_responses_client_with_thread.py +++ b/python/samples/getting_started/agents/azure_openai/azure_responses_client_with_thread.py @@ -5,7 +5,7 @@ from random import randint from typing import Annotated from agent_framework import AgentThread, ChatAgent -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from pydantic import Field @@ -25,7 +25,7 @@ async def example_with_automatic_thread_creation() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -54,7 +54,7 @@ async def example_with_thread_persistence_in_memory() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -95,7 +95,7 @@ async def example_with_existing_thread_id() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) @@ -117,7 +117,7 @@ async def example_with_existing_thread_id() -> None: print("\n--- Continuing with the same thread ID in a new agent instance ---") agent = ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), + chat_client=AzureOpenAIResponsesClient(credential=AzureCliCredential()), instructions="You are a helpful weather agent.", tools=get_weather, ) diff --git a/python/samples/getting_started/agents/azure_responses_client/README.md b/python/samples/getting_started/agents/azure_responses_client/README.md deleted file mode 100644 index 67edc30942..0000000000 --- a/python/samples/getting_started/agents/azure_responses_client/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Azure Responses Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the Azure Responses client from the `agent_framework.azure` package. - -## Examples - -| File | Description | -|------|-------------| -| [`azure_responses_client_basic.py`](azure_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `AzureResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with Azure OpenAI models. | -| [`azure_responses_client_with_explicit_settings.py`](azure_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including endpoint and deployment name. | -| [`azure_responses_client_with_function_tools.py`](azure_responses_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`azure_responses_client_with_code_interpreter.py`](azure_responses_client_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Azure agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | -| [`azure_responses_client_with_thread.py`](azure_responses_client_with_thread.py) | Demonstrates thread management with Azure agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint -- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI deployment - -## Authentication - -All examples use `AzureCliCredential` for authentication. Run `az login` in your terminal before running the examples, or replace `AzureCliCredential` with your preferred authentication method. - -## Required role-based access control (RBAC) roles - -To access the Azure OpenAI API, your Azure account or service principal needs one of the following RBAC roles assigned to the Azure OpenAI resource: - -- **Cognitive Services OpenAI User**: Provides read access to Azure OpenAI resources and the ability to call the inference APIs. This is the minimum role required for running these examples. -- **Cognitive Services OpenAI Contributor**: Provides full access to Azure OpenAI resources, including the ability to create, update, and delete deployments and models. - -For most scenarios, the **Cognitive Services OpenAI User** role is sufficient. You can assign this role through the Azure portal under the Azure OpenAI resource's "Access control (IAM)" section. - -For more detailed information about Azure OpenAI RBAC roles, see: [Role-based access control for Azure OpenAI Service](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/role-based-access-control) diff --git a/python/samples/getting_started/agents/copilotstudio/README.md b/python/samples/getting_started/agents/copilotstudio/README.md index 10c308e690..43796de378 100644 --- a/python/samples/getting_started/agents/copilotstudio/README.md +++ b/python/samples/getting_started/agents/copilotstudio/README.md @@ -46,7 +46,7 @@ Your Azure AD App Registration should have: ```python import asyncio -from agent_framework.copilotstudio import CopilotStudioAgent +from agent_framework.microsoft import CopilotStudioAgent # Uses environment variables for configuration async def main(): @@ -63,7 +63,7 @@ asyncio.run(main()) ### Explicit Configuration ```python -from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token +from agent_framework.microsoft import CopilotStudioAgent, acquire_token from microsoft_agents.copilotstudio.client import ConnectionSettings, CopilotClient, PowerPlatformCloud, AgentType # Acquire token manually diff --git a/python/samples/getting_started/agents/copilotstudio/copilotstudio_basic.py b/python/samples/getting_started/agents/copilotstudio/copilotstudio_basic.py index 169f9957cd..ddb993c332 100644 --- a/python/samples/getting_started/agents/copilotstudio/copilotstudio_basic.py +++ b/python/samples/getting_started/agents/copilotstudio/copilotstudio_basic.py @@ -2,7 +2,7 @@ import asyncio -from agent_framework.copilotstudio import CopilotStudioAgent +from agent_framework.microsoft import CopilotStudioAgent # Environment variables needed: # COPILOTSTUDIOAGENT__ENVIRONMENTID - Environment ID where your copilot is deployed diff --git a/python/samples/getting_started/agents/copilotstudio/copilotstudio_with_explicit_settings.py b/python/samples/getting_started/agents/copilotstudio/copilotstudio_with_explicit_settings.py index 46566dfab7..c1b85d69f6 100644 --- a/python/samples/getting_started/agents/copilotstudio/copilotstudio_with_explicit_settings.py +++ b/python/samples/getting_started/agents/copilotstudio/copilotstudio_with_explicit_settings.py @@ -3,7 +3,7 @@ import asyncio import os -from agent_framework.copilotstudio import CopilotStudioAgent, acquire_token +from agent_framework.microsoft import CopilotStudioAgent, acquire_token from microsoft_agents.copilotstudio.client import AgentType, ConnectionSettings, CopilotClient, PowerPlatformCloud # Environment variables needed: diff --git a/python/samples/getting_started/agents/foundry/README.md b/python/samples/getting_started/agents/foundry/README.md deleted file mode 100644 index 39d8c9aa6e..0000000000 --- a/python/samples/getting_started/agents/foundry/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Foundry Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the Foundry chat client from the `agent_framework.foundry` package. - -## Examples - -| File | Description | -|------|-------------| -| [`foundry_basic.py`](foundry_basic.py) | The simplest way to create an agent using `ChatAgent` with `FoundryChatClient`. It automatically handles all configuration using environment variables. | -| [`foundry_with_explicit_settings.py`](foundry_with_explicit_settings.py) | Shows how to create an agent with explicitly configured `FoundryChatClient` settings, including project endpoint, model deployment, credentials, and agent name. | -| [`foundry_with_existing_agent.py`](foundry_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Foundry chat client. This example also demonstrates proper cleanup of manually created agents. | -| [`foundry_with_function_tools.py`](foundry_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`foundry_with_code_interpreter.py`](foundry_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Foundry agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | -| [`foundry_with_local_mcp.py`](foundry_with_local_mcp.py) | Shows how to integrate Foundry agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. | -| [`foundry_with_thread.py`](foundry_with_thread.py) | Demonstrates thread management with Foundry agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint -- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment diff --git a/python/samples/getting_started/agents/openai_responses_client/README.md b/python/samples/getting_started/agents/openai/README.md similarity index 51% rename from python/samples/getting_started/agents/openai_responses_client/README.md rename to python/samples/getting_started/agents/openai/README.md index fe588e0a62..84b1ca404a 100644 --- a/python/samples/getting_started/agents/openai_responses_client/README.md +++ b/python/samples/getting_started/agents/openai/README.md @@ -1,11 +1,24 @@ -# OpenAI Responses Agent Examples +# OpenAI Assistants Agent Examples -This folder contains examples demonstrating different ways to create and use agents with the OpenAI Responses client from the `agent_framework.openai` package. +This folder contains examples demonstrating different ways to create and use agents with the OpenAI Assistants client from the `agent_framework.openai` package. ## Examples | File | Description | |------|-------------| +| [`openai_assistants_basic.py`](openai_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. | +| [`openai_assistants_with_existing_assistant.py`](openai_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the OpenAI Assistants client. Demonstrates proper cleanup of manually created assistants. | +| [`openai_assistants_with_explicit_settings.py`](openai_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including API key and model ID. | +| [`openai_assistants_with_function_tools.py`](openai_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`openai_assistants_with_file_search.py`](openai_assistants_with_file_search.py) | Demonstrates how to use file search capabilities with OpenAI agents, allowing the agent to search through uploaded files to answer questions. | +| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | +| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. | +| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. | +| [`openai_chat_client_with_function_tools.py`](openai_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | +| [`openai_chat_client_with_local_mcp.py`](openai_chat_client_with_local_mcp.py) | Shows how to integrate OpenAI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. | +| [`openai_chat_client_with_thread.py`](openai_chat_client_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | +| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. | | [`openai_responses_client_basic.py`](openai_responses_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIResponsesClient`. Shows both streaming and non-streaming responses for structured response generation with OpenAI models. | | [`openai_responses_client_reasoning.py`](openai_responses_client_reasoning.py) | Demonstrates how to use reasoning capabilities with OpenAI agents, showing how the agent can provide detailed reasoning for its responses. | | [`openai_responses_client_with_explicit_settings.py`](openai_responses_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific responses client, configuring settings explicitly including API key and model ID. | @@ -25,9 +38,14 @@ This folder contains examples demonstrating different ways to create and use age Make sure to set the following environment variables before running the examples: - `OPENAI_API_KEY`: Your OpenAI API key +- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`) - `OPENAI_RESPONSES_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`) - For image processing examples, use a vision-capable model like `gpt-4o` or `gpt-4o-mini` +Optionally, you can set: +- `OPENAI_ORG_ID`: Your OpenAI organization ID (if applicable) +- `OPENAI_API_BASE_URL`: Your OpenAI base URL (if using a different base URL) + ## Optional Dependencies Some examples require additional dependencies: @@ -36,7 +54,7 @@ Some examples require additional dependencies: ```bash # Using uv uv add pillow - + # Or using pip pip install pillow ``` diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_basic.py b/python/samples/getting_started/agents/openai/openai_assistants_basic.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_basic.py rename to python/samples/getting_started/agents/openai/openai_assistants_basic.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_code_interpreter.py b/python/samples/getting_started/agents/openai/openai_assistants_with_code_interpreter.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_code_interpreter.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_code_interpreter.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_existing_assistant.py b/python/samples/getting_started/agents/openai/openai_assistants_with_existing_assistant.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_existing_assistant.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_existing_assistant.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_explicit_settings.py b/python/samples/getting_started/agents/openai/openai_assistants_with_explicit_settings.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_explicit_settings.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_explicit_settings.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_file_search.py b/python/samples/getting_started/agents/openai/openai_assistants_with_file_search.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_file_search.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_file_search.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_function_tools.py b/python/samples/getting_started/agents/openai/openai_assistants_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_function_tools.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_function_tools.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_thread.py b/python/samples/getting_started/agents/openai/openai_assistants_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/openai_assistants_client/openai_assistants_with_thread.py rename to python/samples/getting_started/agents/openai/openai_assistants_with_thread.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_basic.py b/python/samples/getting_started/agents/openai/openai_chat_client_basic.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_basic.py rename to python/samples/getting_started/agents/openai/openai_chat_client_basic.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_explicit_settings.py b/python/samples/getting_started/agents/openai/openai_chat_client_with_explicit_settings.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_explicit_settings.py rename to python/samples/getting_started/agents/openai/openai_chat_client_with_explicit_settings.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_function_tools.py b/python/samples/getting_started/agents/openai/openai_chat_client_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_function_tools.py rename to python/samples/getting_started/agents/openai/openai_chat_client_with_function_tools.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_local_mcp.py b/python/samples/getting_started/agents/openai/openai_chat_client_with_local_mcp.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_local_mcp.py rename to python/samples/getting_started/agents/openai/openai_chat_client_with_local_mcp.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_thread.py b/python/samples/getting_started/agents/openai/openai_chat_client_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_thread.py rename to python/samples/getting_started/agents/openai/openai_chat_client_with_thread.py diff --git a/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_web_search.py b/python/samples/getting_started/agents/openai/openai_chat_client_with_web_search.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_web_search.py rename to python/samples/getting_started/agents/openai/openai_chat_client_with_web_search.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_basic.py b/python/samples/getting_started/agents/openai/openai_responses_client_basic.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_basic.py rename to python/samples/getting_started/agents/openai/openai_responses_client_basic.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_image_analysis.py b/python/samples/getting_started/agents/openai/openai_responses_client_image_analysis.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_image_analysis.py rename to python/samples/getting_started/agents/openai/openai_responses_client_image_analysis.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_image_generation.py b/python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_image_generation.py rename to python/samples/getting_started/agents/openai/openai_responses_client_image_generation.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_reasoning.py b/python/samples/getting_started/agents/openai/openai_responses_client_reasoning.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_reasoning.py rename to python/samples/getting_started/agents/openai/openai_responses_client_reasoning.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_code_interpreter.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_code_interpreter.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_code_interpreter.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_explicit_settings.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_explicit_settings.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_explicit_settings.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_explicit_settings.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_file_search.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_file_search.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_file_search.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_file_search.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_function_tools.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_function_tools.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_function_tools.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_hosted_mcp.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_local_mcp.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_local_mcp.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_structured_output.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_structured_output.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_structured_output.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_structured_output.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_thread.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_thread.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_thread.py diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_web_search.py b/python/samples/getting_started/agents/openai/openai_responses_client_with_web_search.py similarity index 100% rename from python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_web_search.py rename to python/samples/getting_started/agents/openai/openai_responses_client_with_web_search.py diff --git a/python/samples/getting_started/agents/openai_assistants_client/README.md b/python/samples/getting_started/agents/openai_assistants_client/README.md deleted file mode 100644 index 1770c1d3ce..0000000000 --- a/python/samples/getting_started/agents/openai_assistants_client/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# OpenAI Assistants Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the OpenAI Assistants client from the `agent_framework.openai` package. - -## Examples - -| File | Description | -|------|-------------| -| [`openai_assistants_basic.py`](openai_assistants_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIAssistantsClient`. Shows both streaming and non-streaming responses with automatic assistant creation and cleanup. | -| [`openai_assistants_with_existing_assistant.py`](openai_assistants_with_existing_assistant.py) | Shows how to work with a pre-existing assistant by providing the assistant ID to the OpenAI Assistants client. Demonstrates proper cleanup of manually created assistants. | -| [`openai_assistants_with_explicit_settings.py`](openai_assistants_with_explicit_settings.py) | Shows how to initialize an agent with a specific assistants client, configuring settings explicitly including API key and model ID. | -| [`openai_assistants_with_function_tools.py`](openai_assistants_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`openai_assistants_with_code_interpreter.py`](openai_assistants_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with OpenAI agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | -| [`openai_assistants_with_file_search.py`](openai_assistants_with_file_search.py) | Demonstrates how to use file search capabilities with OpenAI agents, allowing the agent to search through uploaded files to answer questions. | -| [`openai_assistants_with_thread.py`](openai_assistants_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `OPENAI_API_KEY`: Your OpenAI API key -- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`) diff --git a/python/samples/getting_started/agents/openai_chat_client/README.md b/python/samples/getting_started/agents/openai_chat_client/README.md deleted file mode 100644 index 922fc4636c..0000000000 --- a/python/samples/getting_started/agents/openai_chat_client/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# OpenAI Chat Agent Examples - -This folder contains examples demonstrating different ways to create and use agents with the OpenAI Chat client from the `agent_framework.openai` package. - -## Examples - -| File | Description | -|------|-------------| -| [`openai_chat_client_basic.py`](openai_chat_client_basic.py) | The simplest way to create an agent using `ChatAgent` with `OpenAIChatClient`. Shows both streaming and non-streaming responses for chat-based interactions with OpenAI models. | -| [`openai_chat_client_with_explicit_settings.py`](openai_chat_client_with_explicit_settings.py) | Shows how to initialize an agent with a specific chat client, configuring settings explicitly including API key and model ID. | -| [`openai_chat_client_with_function_tools.py`](openai_chat_client_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | -| [`openai_chat_client_with_local_mcp.py`](openai_chat_client_with_local_mcp.py) | Shows how to integrate OpenAI agents with local Model Context Protocol (MCP) servers for enhanced functionality and tool integration. | -| [`openai_chat_client_with_thread.py`](openai_chat_client_with_thread.py) | Demonstrates thread management with OpenAI agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | -| [`openai_chat_client_with_web_search.py`](openai_chat_client_with_web_search.py) | Shows how to use web search capabilities with OpenAI agents to retrieve and use information from the internet in responses. | - -## Environment Variables - -Make sure to set the following environment variables before running the examples: - -- `OPENAI_API_KEY`: Your OpenAI API key -- `OPENAI_CHAT_MODEL_ID`: The OpenAI model to use (e.g., `gpt-4o`, `gpt-4o-mini`, `gpt-3.5-turbo`) diff --git a/python/samples/getting_started/chat_client/README.md b/python/samples/getting_started/chat_client/README.md index 48d96fa0de..a0951c3864 100644 --- a/python/samples/getting_started/chat_client/README.md +++ b/python/samples/getting_started/chat_client/README.md @@ -10,7 +10,7 @@ This folder contains simple examples demonstrating direct usage of various chat | [`azure_chat_client.py`](azure_chat_client.py) | Direct usage of Azure Chat Client for chat interactions with Azure OpenAI models. | | [`azure_responses_client.py`](azure_responses_client.py) | Direct usage of Azure Responses Client for structured response generation with Azure OpenAI models. | | [`chat_response_cancellation.py`](chat_response_cancellation.py) | Demonstrates how to cancel chat responses during streaming, showing proper cancellation handling and cleanup. | -| [`foundry_chat_client.py`](foundry_chat_client.py) | Direct usage of Foundry Chat Client for chat interactions with Azure AI Foundry models. | +| [`azure_ai_chat_client.py`](azure_ai_chat_client.py) | Direct usage of Azure AI Chat Client for chat interactions with Azure AI models. | | [`openai_assistants_client.py`](openai_assistants_client.py) | Direct usage of OpenAI Assistants Client for basic chat interactions with OpenAI assistants. | | [`openai_chat_client.py`](openai_chat_client.py) | Direct usage of OpenAI Chat Client for chat interactions with OpenAI models. | | [`openai_responses_client.py`](openai_responses_client.py) | Direct usage of OpenAI Responses Client for structured response generation with OpenAI models. | @@ -24,9 +24,9 @@ Depending on which client you're using, set the appropriate environment variable - `AZURE_OPENAI_CHAT_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat deployment - `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your Azure OpenAI responses deployment -**For Foundry client:** -- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint -- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment +**For Azure AI client:** +- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI project endpoint +- `AZURE_AI_MODEL_DEPLOYMENT_NAME`: The name of your model deployment **For OpenAI clients:** - `OPENAI_API_KEY`: Your OpenAI API key diff --git a/python/samples/getting_started/chat_client/foundry_chat_client.py b/python/samples/getting_started/chat_client/azure_ai_chat_client.py similarity index 90% rename from python/samples/getting_started/chat_client/foundry_chat_client.py rename to python/samples/getting_started/chat_client/azure_ai_chat_client.py index 72d1f04c42..e0ee2258bf 100644 --- a/python/samples/getting_started/chat_client/foundry_chat_client.py +++ b/python/samples/getting_started/chat_client/azure_ai_chat_client.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -20,7 +20,7 @@ def get_weather( async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - async with FoundryChatClient(async_credential=AzureCliCredential()) as client: + async with AzureAIAgentClient(async_credential=AzureCliCredential()) as client: message = "What's the weather in Amsterdam and in Paris?" stream = False print(f"User: {message}") diff --git a/python/samples/getting_started/chat_client/azure_assistants_client.py b/python/samples/getting_started/chat_client/azure_assistants_client.py index 96aef39bea..35bcf6fb41 100644 --- a/python/samples/getting_started/chat_client/azure_assistants_client.py +++ b/python/samples/getting_started/chat_client/azure_assistants_client.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.azure import AzureAssistantsClient +from agent_framework.azure import AzureOpenAIAssistantsClient from azure.identity import AzureCliCredential from pydantic import Field @@ -20,7 +20,7 @@ def get_weather( async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - async with AzureAssistantsClient(credential=AzureCliCredential()) as client: + async with AzureOpenAIAssistantsClient(credential=AzureCliCredential()) as client: message = "What's the weather in Amsterdam and in Paris?" stream = False print(f"User: {message}") diff --git a/python/samples/getting_started/chat_client/azure_chat_client.py b/python/samples/getting_started/chat_client/azure_chat_client.py index 919c3cea14..c720dfb536 100644 --- a/python/samples/getting_started/chat_client/azure_chat_client.py +++ b/python/samples/getting_started/chat_client/azure_chat_client.py @@ -4,7 +4,7 @@ import asyncio from random import randint from typing import Annotated -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import Field @@ -20,7 +20,7 @@ def get_weather( async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - client = AzureChatClient(credential=AzureCliCredential()) + client = AzureOpenAIChatClient(credential=AzureCliCredential()) message = "What's the weather in Amsterdam and in Paris?" stream = False print(f"User: {message}") diff --git a/python/samples/getting_started/chat_client/azure_responses_client.py b/python/samples/getting_started/chat_client/azure_responses_client.py index 46413a1ba1..f2cc806605 100644 --- a/python/samples/getting_started/chat_client/azure_responses_client.py +++ b/python/samples/getting_started/chat_client/azure_responses_client.py @@ -5,7 +5,7 @@ from random import randint from typing import Annotated from agent_framework import ChatResponse -from agent_framework.azure import AzureResponsesClient +from agent_framework.azure import AzureOpenAIResponsesClient from azure.identity import AzureCliCredential from pydantic import BaseModel, Field @@ -28,7 +28,7 @@ class OutputStruct(BaseModel): async def main() -> None: # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred # authentication option. - client = AzureResponsesClient(credential=AzureCliCredential()) + client = AzureOpenAIResponsesClient(credential=AzureCliCredential()) message = "What's the weather in Amsterdam and in Paris?" stream = True print(f"User: {message}") diff --git a/python/samples/getting_started/context_providers/mem0/README.md b/python/samples/getting_started/context_providers/mem0/README.md index dcf68e1527..44409dbe79 100644 --- a/python/samples/getting_started/context_providers/mem0/README.md +++ b/python/samples/getting_started/context_providers/mem0/README.md @@ -16,7 +16,7 @@ This folder contains examples demonstrating how to use the Mem0 context provider ### Required Resources 1. [Mem0 API Key](https://app.mem0.ai/) - Sign up for a Mem0 account and get your API key -2. Azure AI Foundry project endpoint (used in these examples) +2. Azure AI project endpoint (used in these examples) 3. Azure CLI authentication (run `az login`) ## Configuration @@ -28,9 +28,9 @@ Set the following environment variables: **For Mem0:** - `MEM0_API_KEY`: Your Mem0 API key (alternatively, pass it as `api_key` parameter to `Mem0Provider`) -**For Azure AI Foundry:** -- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint -- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment +**For Azure AI:** +- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI project endpoint +- `AZURE_AI_MODEL_DEPLOYMENT_NAME`: The name of your model deployment ## Key Concepts diff --git a/python/samples/getting_started/context_providers/mem0/mem0_basic.py b/python/samples/getting_started/context_providers/mem0/mem0_basic.py index 008542c7b4..70a70a4c35 100644 --- a/python/samples/getting_started/context_providers/mem0/mem0_basic.py +++ b/python/samples/getting_started/context_providers/mem0/mem0_basic.py @@ -3,7 +3,7 @@ import asyncio import uuid -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from agent_framework.mem0 import Mem0Provider from azure.identity.aio import AzureCliCredential @@ -32,7 +32,7 @@ async def main() -> None: # For Mem0 authentication, set Mem0 API key via "api_key" parameter or MEM0_API_KEY environment variable. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="FriendlyAssistant", instructions="You are a friendly assistant.", tools=retrieve_company_report, diff --git a/python/samples/getting_started/context_providers/mem0/mem0_threads.py b/python/samples/getting_started/context_providers/mem0/mem0_threads.py index 7c7c87c1a8..0cbc7b59be 100644 --- a/python/samples/getting_started/context_providers/mem0/mem0_threads.py +++ b/python/samples/getting_started/context_providers/mem0/mem0_threads.py @@ -3,7 +3,7 @@ import asyncio import uuid -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from agent_framework.mem0 import Mem0Provider from azure.identity.aio import AzureCliCredential @@ -27,7 +27,7 @@ async def example_global_thread_scope() -> None: async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="GlobalMemoryAssistant", instructions="You are an assistant that remembers user preferences across conversations.", tools=get_user_preferences, @@ -65,7 +65,7 @@ async def example_per_operation_thread_scope() -> None: async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="ScopedMemoryAssistant", instructions="You are an assistant with thread-scoped memory.", tools=get_user_preferences, @@ -113,14 +113,14 @@ async def example_multiple_agents() -> None: async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="PersonalAssistant", instructions="You are a personal assistant that helps with personal tasks.", context_providers=Mem0Provider( agent_id=agent_id_1, ), ) as personal_agent, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WorkAssistant", instructions="You are a work assistant that helps with professional tasks.", context_providers=Mem0Provider( diff --git a/python/samples/getting_started/middleware/agent_and_run_level_middleware.py b/python/samples/getting_started/middleware/agent_and_run_level_middleware.py index 0b962d8af8..c0ba169680 100644 --- a/python/samples/getting_started/middleware/agent_and_run_level_middleware.py +++ b/python/samples/getting_started/middleware/agent_and_run_level_middleware.py @@ -12,7 +12,7 @@ from agent_framework import ( AgentRunResponse, FunctionInvocationContext, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -166,7 +166,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant.", tools=get_weather, diff --git a/python/samples/getting_started/middleware/class_based_middleware.py b/python/samples/getting_started/middleware/class_based_middleware.py index 4958e3d094..d446bf29c2 100644 --- a/python/samples/getting_started/middleware/class_based_middleware.py +++ b/python/samples/getting_started/middleware/class_based_middleware.py @@ -15,7 +15,7 @@ from agent_framework import ( FunctionMiddleware, Role, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -99,7 +99,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant.", tools=get_weather, diff --git a/python/samples/getting_started/middleware/decorator_middleware.py b/python/samples/getting_started/middleware/decorator_middleware.py index 672f4c7741..5d9a979176 100644 --- a/python/samples/getting_started/middleware/decorator_middleware.py +++ b/python/samples/getting_started/middleware/decorator_middleware.py @@ -7,7 +7,7 @@ from agent_framework import ( agent_middleware, function_middleware, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential """ @@ -70,7 +70,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="TimeAgent", instructions="You are a helpful time assistant. Call get_current_time when asked about time.", tools=get_current_time, diff --git a/python/samples/getting_started/middleware/exception_handling_with_middleware.py b/python/samples/getting_started/middleware/exception_handling_with_middleware.py index ca37a75bd1..4644ae888c 100644 --- a/python/samples/getting_started/middleware/exception_handling_with_middleware.py +++ b/python/samples/getting_started/middleware/exception_handling_with_middleware.py @@ -5,7 +5,7 @@ from collections.abc import Awaitable, Callable from typing import Annotated from agent_framework import FunctionInvocationContext -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -58,7 +58,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="DataAgent", instructions="You are a helpful data assistant. Use the data service tool to fetch information for users.", tools=unstable_data_service, diff --git a/python/samples/getting_started/middleware/function_based_middleware.py b/python/samples/getting_started/middleware/function_based_middleware.py index e6b92494b3..de962a12ad 100644 --- a/python/samples/getting_started/middleware/function_based_middleware.py +++ b/python/samples/getting_started/middleware/function_based_middleware.py @@ -10,7 +10,7 @@ from agent_framework import ( AgentRunContext, FunctionInvocationContext, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -83,7 +83,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant.", tools=get_weather, diff --git a/python/samples/getting_started/middleware/middleware_termination.py b/python/samples/getting_started/middleware/middleware_termination.py index 991368a0f5..e13bcdd54a 100644 --- a/python/samples/getting_started/middleware/middleware_termination.py +++ b/python/samples/getting_started/middleware/middleware_termination.py @@ -12,7 +12,7 @@ from agent_framework import ( ChatMessage, Role, ) -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -110,7 +110,7 @@ async def pre_termination_middleware() -> None: print("\n--- Example 1: Pre-termination Middleware ---") async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant.", tools=get_weather, @@ -137,7 +137,7 @@ async def post_termination_middleware() -> None: print("\n--- Example 2: Post-termination Middleware ---") async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant.", tools=get_weather, diff --git a/python/samples/getting_started/middleware/override_result_with_middleware.py b/python/samples/getting_started/middleware/override_result_with_middleware.py index b56cb6c78b..1b030193fa 100644 --- a/python/samples/getting_started/middleware/override_result_with_middleware.py +++ b/python/samples/getting_started/middleware/override_result_with_middleware.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import FunctionInvocationContext -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential from pydantic import Field @@ -67,7 +67,7 @@ async def main() -> None: # authentication option. async with ( AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( + AzureAIAgentClient(async_credential=credential).create_agent( name="WeatherAgent", instructions="You are a helpful weather assistant. Use the weather tool to get current conditions.", tools=get_weather, diff --git a/python/samples/getting_started/multimodal_input/azure_chat_multimodal.py b/python/samples/getting_started/multimodal_input/azure_chat_multimodal.py index 4a21b36d24..cbea8657c1 100644 --- a/python/samples/getting_started/multimodal_input/azure_chat_multimodal.py +++ b/python/samples/getting_started/multimodal_input/azure_chat_multimodal.py @@ -2,13 +2,15 @@ import asyncio import base64 + import requests from agent_framework import ChatMessage, DataContent, Role, TextContent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient + async def test_image(): """Test image analysis with Azure.""" - client = AzureChatClient() + client = AzureOpenAIChatClient() # Fetch image from httpbin image_url = "https://httpbin.org/image/jpeg" @@ -18,10 +20,7 @@ async def test_image(): message = ChatMessage( role=Role.USER, - contents=[ - TextContent(text="What's in this image?"), - DataContent(uri=image_uri, media_type="image/jpeg") - ] + contents=[TextContent(text="What's in this image?"), DataContent(uri=image_uri, media_type="image/jpeg")], ) response = await client.get_response(message) @@ -30,7 +29,8 @@ async def test_image(): async def main(): print("=== Testing Azure Multimodal ===") - await test_image() + await test_image() + if __name__ == "__main__": asyncio.run(main()) diff --git a/python/samples/getting_started/observability/.env.example b/python/samples/getting_started/observability/.env.example index d8f9222f57..3d7c49d47e 100644 --- a/python/samples/getting_started/observability/.env.example +++ b/python/samples/getting_started/observability/.env.example @@ -1,5 +1,5 @@ # Connector environment variables -# Foundry +# Azure AI # see ../../../env.example for details # OpenAI # see ../../../env.example for details diff --git a/python/samples/getting_started/observability/02b-foundry_chat_client.py b/python/samples/getting_started/observability/02b-azure_ai_chat_client.py similarity index 76% rename from python/samples/getting_started/observability/02b-foundry_chat_client.py rename to python/samples/getting_started/observability/02b-azure_ai_chat_client.py index 30e1655876..974f103ebc 100644 --- a/python/samples/getting_started/observability/02b-foundry_chat_client.py +++ b/python/samples/getting_started/observability/02b-azure_ai_chat_client.py @@ -6,7 +6,7 @@ from random import randint from typing import Annotated from agent_framework import HostedCodeInterpreterTool -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from agent_framework.observability import get_tracer, setup_observability from azure.ai.projects.aio import AIProjectClient from azure.identity.aio import AzureCliCredential @@ -15,12 +15,12 @@ from opentelemetry.trace.span import format_trace_id from pydantic import Field """ -This sample, shows you can leverage the built-in telemetry in Foundry. -It uses the Foundry client to setup the telemetry, this calls -out to Foundry for a telemetry connection strings, +This sample, shows you can leverage the built-in telemetry in Azure AI. +It uses the Azure AI client to setup the telemetry, this calls +out to Azure AI for a telemetry connection strings, and then call the setup_observability function in the agent framework. If you want to compare with the trace sent to a generic OTLP endpoint, -switch the `use_foundry_telemetry` variable to False. +switch the `use_azure_ai_telemetry` variable to False. """ @@ -47,10 +47,10 @@ async def main() -> None: The telemetry will include information about the AI service execution. - In foundry you will also see specific operations happening that are called by the Foundry implementation, + In azure_ai you will also see specific operations happening that are called by the Azure AI implementation, such as `create_agent`. """ - use_foundry_obs = True + use_azure_ai_obs = True questions = [ "What's the weather in Amsterdam and in Paris?", "Why is the sky blue?", @@ -59,16 +59,16 @@ async def main() -> None: ] async with ( AzureCliCredential() as credential, - AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project, - FoundryChatClient(client=project, setup_tracing=False) as client, + AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project, + AzureAIAgentClient(client=project, setup_tracing=False) as client, ): - if use_foundry_obs: - await client.setup_foundry_observability(enable_live_metrics=True) + if use_azure_ai_obs: + await client.setup_observability(enable_live_metrics=True) else: setup_observability() with get_tracer().start_as_current_span( - name="Foundry Telemetry from Agent Framework", kind=SpanKind.CLIENT + name="Azure AI Telemetry from Agent Framework", kind=SpanKind.CLIENT ) as span: for question in questions: print(f"{BLUE}User: {question}{RESET}") diff --git a/python/samples/getting_started/observability/03b-foundry_agent.py b/python/samples/getting_started/observability/03b-azure_ai_agent.py similarity index 80% rename from python/samples/getting_started/observability/03b-foundry_agent.py rename to python/samples/getting_started/observability/03b-azure_ai_agent.py index 67d65e2c8c..fc050ccf09 100644 --- a/python/samples/getting_started/observability/03b-foundry_agent.py +++ b/python/samples/getting_started/observability/03b-azure_ai_agent.py @@ -6,16 +6,16 @@ from random import randint from typing import Annotated from agent_framework import ChatAgent +from agent_framework.azure import AzureAIAgentClient from agent_framework.observability import get_tracer -from agent_framework_foundry import FoundryChatClient from azure.ai.projects.aio import AIProjectClient from azure.identity.aio import AzureCliCredential from opentelemetry.trace import SpanKind from pydantic import Field """ -This sample shows you can can setup telemetry with a agent from Foundry. -We once again call the `setup_foundry_observability` method to set up telemetry in order to include the overall spans. +This sample shows you can can setup telemetry with a agent from Azure AI. +We once again call the `setup_observability` method to set up telemetry in order to include the overall spans. """ @@ -34,11 +34,11 @@ async def main(): questions = ["What's the weather in Amsterdam?", "and in Paris, and which is better?", "Why is the sky blue?"] async with ( AzureCliCredential() as credential, - AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project, - # this calls `setup_foundry_observability` through the context manager - FoundryChatClient(client=project) as client, + AIProjectClient(endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], credential=credential) as project, + # this calls `setup_observability` through the context manager + AzureAIAgentClient(client=project) as client, ): - await client.setup_foundry_observability(enable_live_metrics=True) + await client.setup_observability(enable_live_metrics=True) with get_tracer().start_as_current_span("Single Agent Chat", kind=SpanKind.CLIENT): print("Running Single Agent Chat") print("Welcome to the chat, type 'exit' to quit.") diff --git a/python/samples/getting_started/observability/README.md b/python/samples/getting_started/observability/README.md index fb9061d6ac..5c5c973d92 100644 --- a/python/samples/getting_started/observability/README.md +++ b/python/samples/getting_started/observability/README.md @@ -26,7 +26,7 @@ The Agent Framework Python SDK is designed to efficiently generate comprehensive ### Required resources 2. OpenAI or [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource?pivots=web-portal) -2. [Foundry project](https://ai.azure.com/doc/azure/ai-foundry/what-is-azure-ai-foundry) +2. [Azure AI project](https://ai.azure.com/doc/azure/ai-foundry/what-is-azure-ai-foundry) ### Optional resources @@ -55,12 +55,12 @@ This folder contains different samples demonstrating how to use telemetry in var ### [01 - zero_code](./01-zero_code.py): A simple example showing how to enable telemetry in a zero-touch scenario. When the above environment variables are set, telemetry will be automatically enabled, however since you do not define any overarching tracer, you will only see the spans for the specific calls to the chat client and tools. -### [02a](./02a-generic_chat_client.py) and [02b](./02b-foundry_chat_client.py) Chat Clients: -These two samples show how to first setup the telemetry by manually importing the `setup_observability` function from the `agent_framework.observability` module and calling it. After this is done, the trace that get's created will live in the same context as the chat client calls, allowing you to see the end-to-end flow of your application. For Foundry, there is a method in the Foundry project client to get the azure monitor connection string for your project, the `.setup_foundry_observability()` method in the `FoundryChatClient` class will use this url to configure telemetry and you then do not have to import and call `setup_observability()` manually. +### [02a](./02a-generic_chat_client.py) and [02b](./02b-azure_ai_chat_client.py) Chat Clients: +These two samples show how to first setup the telemetry by manually importing the `setup_observability` function from the `agent_framework.observability` module and calling it. After this is done, the trace that get's created will live in the same context as the chat client calls, allowing you to see the end-to-end flow of your application. For Azure AI, there is a method in the Azure AI project client to get the azure monitor connection string for your project, the `.setup_observability()` method in the `AzureAIAgentClient` class will use this url to configure telemetry and you then do not have to import and call `setup_observability()` manually. If you or some other process already configure global tracer_providers or metrics_providers, the `setup_observability()` function will not override them, but instead use the existing tracer_provider, if possible. Metrics cannot be setup this way, so if you want to use metrics, you will have to call `setup_observability()` manually, before another process. -### [03a](./03a-generic_agent.py) and [03b](./03b-foundry_agent.py) Agents: -These two samples show how to setup telemetry when using the Agent Framework's agent abstraction layer. They are similar to the chat client samples, but also show how to create an agent and invoke it. The same rules apply for setting up telemetry, you can either call `setup_observability()` manually, or use the `setup_foundry_observability()` method in the `FoundryChatClient` class. +### [03a](./03a-generic_agent.py) and [03b](./03b-azure_ai_agent.py) Agents: +These two samples show how to setup telemetry when using the Agent Framework's agent abstraction layer. They are similar to the chat client samples, but also show how to create an agent and invoke it. The same rules apply for setting up telemetry, you can either call `setup_observability()` manually, or use the `setup_observability()` method in the `AzureAIAgentClient` class. ### [04 - workflow](./04-workflow.py) Workflow: This sample shows how to setup telemetry when using the Agent Framework's workflow execution engine. It demonstrates a simple workflow scenario with telemetry. diff --git a/python/samples/getting_started/workflow/README.md b/python/samples/getting_started/workflow/README.md index 7eba2c95a9..14ed675c01 100644 --- a/python/samples/getting_started/workflow/README.md +++ b/python/samples/getting_started/workflow/README.md @@ -35,7 +35,7 @@ Once comfortable with these, explore the rest of the samples below. |---|---|---| | Azure Chat Agents (Streaming) | [agents/azure_chat_agents_streaming.py](./agents/azure_chat_agents_streaming.py) | Add Azure agents as edges and handle streaming events | | Custom Agent Executors | [agents/custom_agent_executors.py](./agents/custom_agent_executors.py) | Create executors to handle agent run methods | -| Foundry Chat Agents (Streaming) | [agents/foundry_chat_agents_streaming.py](./agents/foundry_chat_agents_streaming.py) | Add Foundry agents as edges and handle streaming events | +| Azure AI Chat Agents (Streaming) | [agents/azure_ai_chat_agents_streaming.py](./agents/azure_ai_chat_agents_streaming.py) | Add Azure AI agents as edges and handle streaming events | | Workflow as Agent (Reflection Pattern) | [agents/workflow_as_agent_reflection_pattern.py](./agents/workflow_as_agent_reflection_pattern.py) | Wrap a workflow so it can behave like an agent (reflection pattern) | | Workflow as Agent + HITL | [agents/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability | @@ -119,9 +119,9 @@ concurrent’s dispatcher and aggregator and can be ignored if you only care abo ### Environment Variables -- **AzureChatClient**: Set Azure OpenAI environment variables as documented [here](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/chat_client/README.md#environment-variables). - These variables are required for samples that construct `AzureChatClient` +- **AzureOpenAIChatClient**: Set Azure OpenAI environment variables as documented [here](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/chat_client/README.md#environment-variables). + These variables are required for samples that construct `AzureOpenAIChatClient` -- **OpenAI** (used in orchestration samples): - - [OpenAIChatClient env vars](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/agents/openai_chat_client/README.md) +- **OpenAI** (used in orchestration samples): + - [OpenAIChatClient env vars](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/agents/openai_chat_client/README.md) - [OpenAIResponsesClient env vars](https://github.com/microsoft/agent-framework/blob/main/python/samples/getting_started/agents/openai_responses_client/README.md) diff --git a/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py b/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py index 303e85aed4..5391e59e66 100644 --- a/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py +++ b/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py @@ -3,7 +3,7 @@ import asyncio from agent_framework import AgentRunEvent, WorkflowBuilder -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -13,12 +13,12 @@ This sample uses two custom executors. A Writer agent creates or edits content, then hands the conversation to a Reviewer agent which evaluates and finalizes the result. Purpose: -Show how to wrap chat agents created by AzureChatClient inside workflow executors. Demonstrate how agents +Show how to wrap chat agents created by AzureOpenAIChatClient inside workflow executors. Demonstrate how agents automatically yield outputs when they complete, removing the need for explicit completion events. The workflow completes when it becomes idle. Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, executors, edges, events, and streaming or non streaming runs. """ @@ -27,7 +27,7 @@ Prerequisites: async def main(): """Build and run a simple two node agent workflow: Writer then Reviewer.""" # Create the Azure chat client. AzureCliCredential uses your current az login. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) writer_agent = chat_client.create_agent( instructions=( "You are an excellent content writer. You create new content and edit contents based on the feedback." diff --git a/python/samples/getting_started/workflow/_start-here/step3_streaming.py b/python/samples/getting_started/workflow/_start-here/step3_streaming.py index a09c3bd1c8..ee9b72ecc5 100644 --- a/python/samples/getting_started/workflow/_start-here/step3_streaming.py +++ b/python/samples/getting_started/workflow/_start-here/step3_streaming.py @@ -2,8 +2,6 @@ import asyncio -from typing_extensions import Never - from agent_framework import ( ChatAgent, ChatMessage, @@ -17,8 +15,9 @@ from agent_framework import ( handler, ) from agent_framework._workflow._events import WorkflowOutputEvent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential +from typing_extensions import Never """ Step 3: Agents in a workflow with streaming @@ -28,14 +27,14 @@ then passes the conversation to a Reviewer agent that finalizes the result. The workflow is invoked with run_stream so you can observe events as they occur. Purpose: -Show how to wrap chat agents created by AzureChatClient inside workflow executors, wire them with WorkflowBuilder, +Show how to wrap chat agents created by AzureOpenAIChatClient inside workflow executors, wire them with WorkflowBuilder, and consume streaming events from the workflow. Demonstrate the @handler pattern with typed inputs and typed WorkflowContext[T_Out, T_W_Out] outputs. Agents automatically yield outputs when they complete. The streaming loop also surfaces WorkflowEvent.origin so you can distinguish runner-generated lifecycle events from executor-generated data-plane events. Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, executors, edges, events, and streaming runs. """ @@ -51,8 +50,8 @@ class Writer(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "writer"): - # Create a domain specific agent using your configured AzureChatClient. + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "writer"): + # Create a domain specific agent using your configured AzureOpenAIChatClient. agent = chat_client.create_agent( instructions=( "You are an excellent content writer. You create new content and edit contents based on the feedback." @@ -88,7 +87,7 @@ class Reviewer(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "reviewer"): + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "reviewer"): # Create a domain specific agent that evaluates and refines content. agent = chat_client.create_agent( instructions=( @@ -111,7 +110,7 @@ class Reviewer(Executor): async def main(): """Build the two node workflow and run it with streaming to observe events.""" # Create the Azure chat client. AzureCliCredential uses your current az login. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) # Instantiate the two agent backed executors. writer = Writer(chat_client) reviewer = Reviewer(chat_client) diff --git a/python/samples/getting_started/workflow/agents/foundry_chat_agents_streaming.py b/python/samples/getting_started/workflow/agents/azure_ai_agents_streaming.py similarity index 86% rename from python/samples/getting_started/workflow/agents/foundry_chat_agents_streaming.py rename to python/samples/getting_started/workflow/agents/azure_ai_agents_streaming.py index ab600dea0b..6706be4a78 100644 --- a/python/samples/getting_started/workflow/agents/foundry_chat_agents_streaming.py +++ b/python/samples/getting_started/workflow/agents/azure_ai_agents_streaming.py @@ -6,7 +6,7 @@ from contextlib import AsyncExitStack from typing import Any from agent_framework import AgentRunUpdateEvent, WorkflowBuilder, WorkflowOutputEvent -from agent_framework.foundry import FoundryChatClient +from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import AzureCliCredential """ @@ -24,21 +24,21 @@ Demonstrate: - The workflow completes when idle and outputs are available in events.get_outputs(). Prerequisites: -- Foundry Agent Service configured, along with the required environment variables. +- Azure AI Agent Service configured, along with the required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, edges, events, and streaming runs. """ -async def create_foundry_agent() -> tuple[Callable[..., Awaitable[Any]], Callable[[], Awaitable[None]]]: - """Helper method to create a Foundry agent factory and a close function. +async def create_azure_ai_agent() -> tuple[Callable[..., Awaitable[Any]], Callable[[], Awaitable[None]]]: + """Helper method to create a Azure AI agent factory and a close function. This makes sure the async context managers are properly handled. """ stack = AsyncExitStack() cred = await stack.enter_async_context(AzureCliCredential()) - client = await stack.enter_async_context(FoundryChatClient(async_credential=cred)) + client = await stack.enter_async_context(AzureAIAgentClient(async_credential=cred)) async def agent(**kwargs: Any) -> Any: return await stack.enter_async_context(client.create_agent(**kwargs)) @@ -50,7 +50,7 @@ async def create_foundry_agent() -> tuple[Callable[..., Awaitable[Any]], Callabl async def main() -> None: - agent, close = await create_foundry_agent() + agent, close = await create_azure_ai_agent() try: writer = await agent( name="Writer", diff --git a/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py b/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py index ba8410bb53..6e707220ed 100644 --- a/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py +++ b/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py @@ -3,7 +3,7 @@ import asyncio from agent_framework import AgentRunUpdateEvent, WorkflowBuilder, WorkflowOutputEvent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -21,7 +21,7 @@ Demonstrate: - The workflow completes when idle and outputs are available in events.get_outputs(). Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, edges, events, and streaming runs. """ @@ -30,7 +30,7 @@ Prerequisites: async def main(): """Build and run a simple two node agent workflow: Writer then Reviewer.""" # Create the Azure chat client. AzureCliCredential uses your current az login. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) # Define two domain specific chat agents. The builder will wrap these as executors. writer_agent = chat_client.create_agent( diff --git a/python/samples/getting_started/workflow/agents/custom_agent_executors.py b/python/samples/getting_started/workflow/agents/custom_agent_executors.py index 80f9871d6e..8de1c5d4a3 100644 --- a/python/samples/getting_started/workflow/agents/custom_agent_executors.py +++ b/python/samples/getting_started/workflow/agents/custom_agent_executors.py @@ -10,7 +10,7 @@ from agent_framework import ( WorkflowContext, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -20,12 +20,12 @@ This sample uses two custom executors. A Writer agent creates or edits content, then hands the conversation to a Reviewer agent which evaluates and finalizes the result. Purpose: -Show how to wrap chat agents created by AzureChatClient inside workflow executors. Demonstrate the @handler pattern +Show how to wrap chat agents created by AzureOpenAIChatClient inside workflow executors. Demonstrate the @handler pattern with typed inputs and typed WorkflowContext[T] outputs, connect executors with the fluent WorkflowBuilder, and finish by yielding outputs from the terminal node. Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, executors, edges, events, and streaming or non streaming runs. """ @@ -41,8 +41,8 @@ class Writer(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "writer"): - # Create a domain specific agent using your configured AzureChatClient. + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "writer"): + # Create a domain specific agent using your configured AzureOpenAIChatClient. agent = chat_client.create_agent( instructions=( "You are an excellent content writer. You create new content and edit contents based on the feedback." @@ -83,7 +83,7 @@ class Reviewer(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "reviewer"): + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "reviewer"): # Create a domain specific agent that evaluates and refines content. agent = chat_client.create_agent( instructions=( @@ -106,7 +106,7 @@ class Reviewer(Executor): async def main(): """Build and run a simple two node agent workflow: Writer then Reviewer.""" # Create the Azure chat client. AzureCliCredential uses your current az login. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) # Instantiate the two agent backed executors. writer = Writer(chat_client) diff --git a/python/samples/getting_started/workflow/checkpoint/checkpoint_with_human_in_the_loop.py b/python/samples/getting_started/workflow/checkpoint/checkpoint_with_human_in_the_loop.py index 528c986d6c..5a48029a0e 100644 --- a/python/samples/getting_started/workflow/checkpoint/checkpoint_with_human_in_the_loop.py +++ b/python/samples/getting_started/workflow/checkpoint/checkpoint_with_human_in_the_loop.py @@ -25,7 +25,7 @@ from agent_framework import ( WorkflowStatusEvent, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential # NOTE: the Azure client imports above are real dependencies. When running this @@ -203,7 +203,7 @@ def create_workflow(*, checkpoint_storage: FileCheckpointStorage | None = None) # The Azure client is created once so our agent executor can issue calls to # the hosted model. The agent id is stable across runs which keeps # checkpoints deterministic. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) writer = AgentExecutor( chat_client.create_agent( instructions="Write concise, warm release notes that sound human and helpful.", diff --git a/python/samples/getting_started/workflow/checkpoint/checkpoint_with_resume.py b/python/samples/getting_started/workflow/checkpoint/checkpoint_with_resume.py index db441dcb6c..62881a925b 100644 --- a/python/samples/getting_started/workflow/checkpoint/checkpoint_with_resume.py +++ b/python/samples/getting_started/workflow/checkpoint/checkpoint_with_resume.py @@ -18,7 +18,7 @@ from agent_framework import ( WorkflowContext, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential if TYPE_CHECKING: @@ -51,7 +51,7 @@ What you learn: - How workflows complete by yielding outputs when idle, not via explicit completion events. Prerequisites: -- Azure AI or Azure OpenAI available for AzureChatClient. +- Azure AI or Azure OpenAI available for AzureOpenAIChatClient. - Authentication with azure-identity via AzureCliCredential. Run az login locally. - Filesystem access for writing JSON checkpoint files in a temp directory. """ @@ -161,7 +161,7 @@ def create_workflow(checkpoint_storage: FileCheckpointStorage) -> "Workflow": reverse_text_executor = ReverseTextExecutor(id="reverse-text") # Configure the agent stage that lowercases the text. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) lower_agent = AgentExecutor( chat_client.create_agent( instructions=("You transform text to lowercase. Reply with ONLY the transformed text.") diff --git a/python/samples/getting_started/workflow/control-flow/edge_condition.py b/python/samples/getting_started/workflow/control-flow/edge_condition.py index 3b051148ad..5ef0956733 100644 --- a/python/samples/getting_started/workflow/control-flow/edge_condition.py +++ b/python/samples/getting_started/workflow/control-flow/edge_condition.py @@ -16,7 +16,7 @@ from agent_framework import ( # Core chat primitives used to build requests WorkflowContext, # Per-run context and event bus executor, # Decorator to declare a Python function as a workflow executor ) -from agent_framework.azure import AzureChatClient # Thin client wrapper for Azure OpenAI chat models +from agent_framework.azure import AzureOpenAIChatClient # Thin client wrapper for Azure OpenAI chat models from azure.identity import AzureCliCredential # Uses your az CLI login for credentials from pydantic import BaseModel # Structured outputs for safer parsing @@ -35,7 +35,7 @@ Purpose: Prerequisites: - You understand the basics of WorkflowBuilder, executors, and events in this framework. - You know the concept of edge conditions and how they gate routes using a predicate function. -- Azure OpenAI access is configured for AzureChatClient. You should be logged in with Azure CLI (AzureCliCredential) +- Azure OpenAI access is configured for AzureOpenAIChatClient. You should be logged in with Azure CLI (AzureCliCredential) and have the Azure OpenAI environment variables set as documented in the getting started chat client README. - The sample email resource file exists at workflow/resources/email.txt. @@ -132,7 +132,7 @@ async def to_email_assistant_request( async def main() -> None: # Create agents # AzureCliCredential uses your current az login. This avoids embedding secrets in code. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) # Agent 1. Classifies spam and returns a DetectionResult object. # response_format enforces that the LLM returns parsable JSON for the Pydantic model. diff --git a/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py index ec8265bd55..3d55ad2a0f 100644 --- a/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py @@ -22,7 +22,7 @@ from agent_framework import ( WorkflowOutputEvent, executor, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import BaseModel @@ -184,7 +184,7 @@ async def database_access(analysis: AnalysisResult, ctx: WorkflowContext[Never, async def main() -> None: # Agents - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) email_analysis_agent = AgentExecutor( chat_client.create_agent( diff --git a/python/samples/getting_started/workflow/control-flow/simple_loop.py b/python/samples/getting_started/workflow/control-flow/simple_loop.py index 8d48274830..53d65331ae 100644 --- a/python/samples/getting_started/workflow/control-flow/simple_loop.py +++ b/python/samples/getting_started/workflow/control-flow/simple_loop.py @@ -16,7 +16,7 @@ from agent_framework import ( WorkflowOutputEvent, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -28,7 +28,7 @@ What it does: - The workflow completes when the correct number is guessed. Prerequisites: -- Azure AI/ Azure OpenAI for `AzureChatClient` agent. +- Azure AI/ Azure OpenAI for `AzureOpenAIChatClient` agent. - Authentication via `azure-identity` — uses `AzureCliCredential()` (run `az login`). """ @@ -122,7 +122,7 @@ async def main(): guess_number_executor = GuessNumberExecutor((1, 100)) # Agent judge setup - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) judge_agent = AgentExecutor( chat_client.create_agent( instructions=( diff --git a/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py index cc2193baeb..385e595b86 100644 --- a/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py @@ -20,7 +20,7 @@ from agent_framework import ( # Core chat primitives used to form LLM requests WorkflowContext, # Per-run context and event bus executor, # Decorator to turn a function into a workflow executor ) -from agent_framework.azure import AzureChatClient # Thin client for Azure OpenAI chat models +from agent_framework.azure import AzureOpenAIChatClient # Thin client for Azure OpenAI chat models from azure.identity import AzureCliCredential # Uses your az CLI login for credentials from pydantic import BaseModel # Structured outputs with validation @@ -42,7 +42,7 @@ on that type. Prerequisites: - Familiarity with WorkflowBuilder, executors, edges, and events. - Understanding of switch-case edge groups and how Case and Default are evaluated in order. -- Working Azure OpenAI configuration for AzureChatClient, with Azure CLI login and required environment variables. +- Working Azure OpenAI configuration for AzureOpenAIChatClient, with Azure CLI login and required environment variables. - Access to workflow/resources/ambiguous_email.txt, or accept the inline fallback string. """ @@ -155,7 +155,7 @@ async def handle_uncertain(detection: DetectionResult, ctx: WorkflowContext[Neve async def main(): """Main function to run the workflow.""" - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) # Agents. response_format enforces that the LLM returns JSON that Pydantic can validate. spam_detection_agent = AgentExecutor( diff --git a/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py b/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py index 4eec70ebb4..02aa758bea 100644 --- a/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py +++ b/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py @@ -21,7 +21,7 @@ from agent_framework import ( WorkflowStatusEvent, # Event emitted on run state changes handler, # Decorator to expose an Executor method as a step ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import BaseModel @@ -42,7 +42,7 @@ Demonstrate: - Driving the loop in application code with run_stream and send_responses_streaming. Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Basic familiarity with WorkflowBuilder, executors, edges, events, and streaming runs. """ @@ -158,7 +158,7 @@ class TurnManager(Executor): async def main() -> None: # Create the chat agent and wrap it in an AgentExecutor. # response_format enforces that the model produces JSON compatible with GuessOutput. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) agent = chat_client.create_agent( instructions=( "You guess a number between 1 and 10. " diff --git a/python/samples/getting_started/workflow/orchestration/concurrent_agents.py b/python/samples/getting_started/workflow/orchestration/concurrent_agents.py index bee9ef3029..de46afe386 100644 --- a/python/samples/getting_started/workflow/orchestration/concurrent_agents.py +++ b/python/samples/getting_started/workflow/orchestration/concurrent_agents.py @@ -4,7 +4,7 @@ import asyncio from typing import Any from agent_framework import ChatMessage, ConcurrentBuilder -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -21,14 +21,14 @@ Demonstrates: - Workflow completion when idle with no pending work Prerequisites: -- Azure OpenAI access configured for AzureChatClient (use az login + env vars) +- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) - Familiarity with Workflow events (AgentRunEvent, WorkflowOutputEvent) """ async def main() -> None: - # 1) Create three domain agents using AzureChatClient - chat_client = AzureChatClient(credential=AzureCliCredential()) + # 1) Create three domain agents using AzureOpenAIChatClient + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) researcher = chat_client.create_agent( instructions=( diff --git a/python/samples/getting_started/workflow/orchestration/concurrent_custom_agent_executors.py b/python/samples/getting_started/workflow/orchestration/concurrent_custom_agent_executors.py index 6a25a8a922..7a33d55ed4 100644 --- a/python/samples/getting_started/workflow/orchestration/concurrent_custom_agent_executors.py +++ b/python/samples/getting_started/workflow/orchestration/concurrent_custom_agent_executors.py @@ -13,7 +13,7 @@ from agent_framework import ( WorkflowContext, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -25,21 +25,21 @@ and emit AgentExecutorResponse outputs, which allows reuse of the high-level ConcurrentBuilder API and the default aggregator. Demonstrates: -- Executors that create their ChatAgent in __init__ (via AzureChatClient) +- Executors that create their ChatAgent in __init__ (via AzureOpenAIChatClient) - A @handler that converts AgentExecutorRequest -> AgentExecutorResponse - ConcurrentBuilder().participants([...]) to build fan-out/fan-in - Default aggregator returning list[ChatMessage] (one user + one assistant per agent) - Workflow completion when all participants become idle Prerequisites: -- Azure OpenAI configured for AzureChatClient (az login + required env vars) +- Azure OpenAI configured for AzureOpenAIChatClient (az login + required env vars) """ class ResearcherExec(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "researcher"): + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "researcher"): agent = chat_client.create_agent( instructions=( "You're an expert market and product researcher. Given a prompt, provide concise, factual insights," @@ -59,7 +59,7 @@ class ResearcherExec(Executor): class MarketerExec(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "marketer"): + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "marketer"): agent = chat_client.create_agent( instructions=( "You're a creative marketing strategist. Craft compelling value propositions and target messaging" @@ -79,7 +79,7 @@ class MarketerExec(Executor): class LegalExec(Executor): agent: ChatAgent - def __init__(self, chat_client: AzureChatClient, id: str = "legal"): + def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "legal"): agent = chat_client.create_agent( instructions=( "You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns" @@ -97,7 +97,7 @@ class LegalExec(Executor): async def main() -> None: - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) researcher = ResearcherExec(chat_client) marketer = MarketerExec(chat_client) diff --git a/python/samples/getting_started/workflow/orchestration/concurrent_custom_aggregator.py b/python/samples/getting_started/workflow/orchestration/concurrent_custom_aggregator.py index 15fe455464..4ad8c9fcb3 100644 --- a/python/samples/getting_started/workflow/orchestration/concurrent_custom_aggregator.py +++ b/python/samples/getting_started/workflow/orchestration/concurrent_custom_aggregator.py @@ -4,7 +4,7 @@ import asyncio from typing import Any from agent_framework import ChatMessage, ConcurrentBuilder, Role -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -12,7 +12,7 @@ Sample: Concurrent Orchestration with Custom Aggregator Build a concurrent workflow with ConcurrentBuilder that fans out one prompt to multiple domain agents and fans in their responses. Override the default -aggregator with a custom async callback that uses AzureChatClient.get_response() +aggregator with a custom async callback that uses AzureOpenAIChatClient.get_response() to synthesize a concise, consolidated summary from the experts' outputs. The workflow completes when all participants become idle. @@ -23,12 +23,12 @@ Demonstrates: - Workflow output yielded with the synthesized summary string Prerequisites: -- Azure OpenAI configured for AzureChatClient (az login + required env vars) +- Azure OpenAI configured for AzureOpenAIChatClient (az login + required env vars) """ async def main() -> None: - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) researcher = chat_client.create_agent( instructions=( diff --git a/python/samples/getting_started/workflow/orchestration/sequential_agents.py b/python/samples/getting_started/workflow/orchestration/sequential_agents.py index a444514b99..f50d191104 100644 --- a/python/samples/getting_started/workflow/orchestration/sequential_agents.py +++ b/python/samples/getting_started/workflow/orchestration/sequential_agents.py @@ -4,7 +4,7 @@ import asyncio from typing import cast from agent_framework import ChatMessage, Role, SequentialBuilder, WorkflowOutputEvent -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -23,13 +23,13 @@ Note on internal adapters: You can safely ignore them when focusing on agent progress. Prerequisites: -- Azure OpenAI access configured for AzureChatClient (use az login + env vars) +- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) """ async def main() -> None: # 1) Create agents - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) writer = chat_client.create_agent( instructions=("You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt."), diff --git a/python/samples/getting_started/workflow/orchestration/sequential_custom_executors.py b/python/samples/getting_started/workflow/orchestration/sequential_custom_executors.py index 452234bb42..071a668d6d 100644 --- a/python/samples/getting_started/workflow/orchestration/sequential_custom_executors.py +++ b/python/samples/getting_started/workflow/orchestration/sequential_custom_executors.py @@ -13,7 +13,7 @@ from agent_framework import ( WorkflowContext, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -35,7 +35,7 @@ Note on internal adapters: for completion—similar to concurrent's dispatcher/aggregator. Prerequisites: -- Azure OpenAI access configured for AzureChatClient (use az login + env vars) +- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars) """ @@ -53,7 +53,7 @@ class Summarizer(Executor): async def main() -> None: # 1) Create a content agent - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) content = chat_client.create_agent( instructions="Produce a concise paragraph answering the user's request.", name="content", diff --git a/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py b/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py index ff1cc1a73c..6340836ab4 100644 --- a/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py +++ b/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py @@ -18,7 +18,7 @@ from agent_framework import ( # Core chat primitives to build LLM requests WorkflowOutputEvent, # Event emitted when workflow yields output handler, # Decorator to mark an Executor method as invokable ) -from agent_framework.azure import AzureChatClient # Client wrapper for Azure OpenAI chat models +from agent_framework.azure import AzureOpenAIChatClient # Client wrapper for Azure OpenAI chat models from azure.identity import AzureCliCredential # Uses your az CLI login for credentials """ @@ -35,7 +35,7 @@ Show how to construct a parallel branch pattern in workflows. Demonstrate: Prerequisites: - Familiarity with WorkflowBuilder, executors, edges, events, and streaming runs. -- Azure OpenAI access configured for AzureChatClient. Log in with Azure CLI and set any required environment variables. +- Azure OpenAI access configured for AzureOpenAIChatClient. Log in with Azure CLI and set any required environment variables. - Comfort reading AgentExecutorResponse.agent_run_response.text for assistant output aggregation. """ @@ -107,7 +107,7 @@ class AggregateInsights(Executor): async def main() -> None: # 1) Create agent executors for domain experts - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) researcher = AgentExecutor( chat_client.create_agent( diff --git a/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py index b329049c27..a560994346 100644 --- a/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py +++ b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py @@ -17,7 +17,7 @@ from agent_framework import ( WorkflowContext, executor, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential from pydantic import BaseModel @@ -35,7 +35,7 @@ Show how to: - Compose agent backed executors with function style executors and yield the final output when the workflow completes. Prerequisites: -- Azure OpenAI configured for AzureChatClient with required environment variables. +- Azure OpenAI configured for AzureOpenAIChatClient with required environment variables. - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample. - Familiarity with WorkflowBuilder, executors, conditional edges, and streaming runs. """ @@ -157,7 +157,7 @@ async def handle_spam(detection: DetectionResult, ctx: WorkflowContext[Never, st async def main() -> None: # Create chat client and agents. response_format enforces structured JSON from each agent. - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) spam_detection_agent = chat_client.create_agent( instructions=( diff --git a/python/samples/getting_started/workflow/visualization/concurrent_with_visualization.py b/python/samples/getting_started/workflow/visualization/concurrent_with_visualization.py index a07b390051..f38a94353f 100644 --- a/python/samples/getting_started/workflow/visualization/concurrent_with_visualization.py +++ b/python/samples/getting_started/workflow/visualization/concurrent_with_visualization.py @@ -19,7 +19,7 @@ from agent_framework import ( WorkflowViz, handler, ) -from agent_framework.azure import AzureChatClient +from agent_framework.azure import AzureOpenAIChatClient from azure.identity import AzureCliCredential """ @@ -31,7 +31,7 @@ What it does: - Visualization: generate Mermaid and GraphViz representations via `WorkflowViz` and optionally export SVG. Prerequisites: -- Azure AI/ Azure OpenAI for `AzureChatClient` agents. +- Azure AI/ Azure OpenAI for `AzureOpenAIChatClient` agents. - Authentication via `azure-identity` — uses `AzureCliCredential()` (run `az login`). - For visualization export: `pip install agent-framework[viz]` and install GraphViz binaries. """ @@ -103,7 +103,7 @@ class AggregateInsights(Executor): async def main() -> None: # 1) Create agent executors for domain experts - chat_client = AzureChatClient(credential=AzureCliCredential()) + chat_client = AzureOpenAIChatClient(credential=AzureCliCredential()) researcher = AgentExecutor( chat_client.create_agent( diff --git a/python/tests/samples/getting_started/test_agents.py b/python/tests/samples/getting_started/test_agents.py index 32ae175d4a..73e90a2330 100644 --- a/python/tests/samples/getting_started/test_agents.py +++ b/python/tests/samples/getting_started/test_agents.py @@ -8,142 +8,142 @@ from typing import Any import pytest from pytest import MonkeyPatch, mark, param -from samples.getting_started.agents.azure_assistants_client.azure_assistants_basic import ( +from samples.getting_started.agents.azure_ai.azure_ai_basic import ( + main as azure_ai_basic, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_code_interpreter import ( + main as azure_ai_with_code_interpreter, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_existing_agent import ( + main as azure_ai_with_existing_agent, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_explicit_settings import ( + main as azure_ai_with_explicit_settings, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( + mixed_tools_example as azure_ai_with_function_tools_mixed, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( + tools_on_agent_level as azure_ai_with_function_tools_agent, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_function_tools import ( + tools_on_run_level as azure_ai_with_function_tools_run, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_local_mcp import ( + main as azure_ai_with_local_mcp, +) +from samples.getting_started.agents.azure_ai.azure_ai_with_thread import ( + main as azure_ai_with_thread, +) +from samples.getting_started.agents.azure_openai.azure_assistants_basic import ( main as azure_assistants_basic, ) -from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_code_interpreter import ( +from samples.getting_started.agents.azure_openai.azure_assistants_with_code_interpreter import ( main as azure_assistants_with_code_interpreter, ) -from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_existing_assistant import ( +from samples.getting_started.agents.azure_openai.azure_assistants_with_existing_assistant import ( main as azure_assistants_with_existing_assistant, ) -from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_explicit_settings import ( +from samples.getting_started.agents.azure_openai.azure_assistants_with_explicit_settings import ( main as azure_assistants_with_explicit_settings, ) -from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_function_tools import ( +from samples.getting_started.agents.azure_openai.azure_assistants_with_function_tools import ( main as azure_assistants_with_function_tools, ) -from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_thread import ( +from samples.getting_started.agents.azure_openai.azure_assistants_with_thread import ( main as azure_assistants_with_thread, ) -from samples.getting_started.agents.azure_chat_client.azure_chat_client_basic import ( +from samples.getting_started.agents.azure_openai.azure_chat_client_basic import ( main as azure_chat_client_basic, ) -from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_explicit_settings import ( +from samples.getting_started.agents.azure_openai.azure_chat_client_with_explicit_settings import ( main as azure_chat_client_with_explicit_settings, ) -from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_function_tools import ( +from samples.getting_started.agents.azure_openai.azure_chat_client_with_function_tools import ( main as azure_chat_client_with_function_tools, ) -from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_thread import ( +from samples.getting_started.agents.azure_openai.azure_chat_client_with_thread import ( main as azure_chat_client_with_thread, ) -from samples.getting_started.agents.azure_responses_client.azure_responses_client_basic import ( +from samples.getting_started.agents.azure_openai.azure_responses_client_basic import ( main as azure_responses_client_basic, ) -from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_code_interpreter import ( +from samples.getting_started.agents.azure_openai.azure_responses_client_with_code_interpreter import ( main as azure_responses_client_with_code_interpreter, ) -from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_explicit_settings import ( +from samples.getting_started.agents.azure_openai.azure_responses_client_with_explicit_settings import ( main as azure_responses_client_with_explicit_settings, ) -from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_function_tools import ( +from samples.getting_started.agents.azure_openai.azure_responses_client_with_function_tools import ( main as azure_responses_client_with_function_tools, ) -from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_thread import ( +from samples.getting_started.agents.azure_openai.azure_responses_client_with_thread import ( main as azure_responses_client_with_thread, ) -from samples.getting_started.agents.foundry.foundry_basic import ( - main as foundry_basic, -) -from samples.getting_started.agents.foundry.foundry_with_code_interpreter import ( - main as foundry_with_code_interpreter, -) -from samples.getting_started.agents.foundry.foundry_with_existing_agent import ( - main as foundry_with_existing_agent, -) -from samples.getting_started.agents.foundry.foundry_with_explicit_settings import ( - main as foundry_with_explicit_settings, -) -from samples.getting_started.agents.foundry.foundry_with_function_tools import ( - mixed_tools_example as foundry_with_function_tools_mixed, -) -from samples.getting_started.agents.foundry.foundry_with_function_tools import ( - tools_on_agent_level as foundry_with_function_tools_agent, -) -from samples.getting_started.agents.foundry.foundry_with_function_tools import ( - tools_on_run_level as foundry_with_function_tools_run, -) -from samples.getting_started.agents.foundry.foundry_with_local_mcp import ( - main as foundry_with_local_mcp, -) -from samples.getting_started.agents.foundry.foundry_with_thread import ( - main as foundry_with_thread, -) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_basic import ( +from samples.getting_started.agents.openai.openai_assistants_basic import ( main as openai_assistants_basic, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_code_interpreter import ( +from samples.getting_started.agents.openai.openai_assistants_with_code_interpreter import ( main as openai_assistants_with_code_interpreter, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_existing_assistant import ( +from samples.getting_started.agents.openai.openai_assistants_with_existing_assistant import ( main as openai_assistants_with_existing_assistant, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_explicit_settings import ( +from samples.getting_started.agents.openai.openai_assistants_with_explicit_settings import ( main as openai_assistants_with_explicit_settings, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_file_search import ( +from samples.getting_started.agents.openai.openai_assistants_with_file_search import ( main as openai_assistants_with_file_search, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_function_tools import ( +from samples.getting_started.agents.openai.openai_assistants_with_function_tools import ( main as openai_assistants_with_function_tools, ) -from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_thread import ( +from samples.getting_started.agents.openai.openai_assistants_with_thread import ( main as openai_assistants_with_thread, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_basic import ( +from samples.getting_started.agents.openai.openai_chat_client_basic import ( main as openai_chat_client_basic, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_explicit_settings import ( +from samples.getting_started.agents.openai.openai_chat_client_with_explicit_settings import ( main as openai_chat_client_with_explicit_settings, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_function_tools import ( +from samples.getting_started.agents.openai.openai_chat_client_with_function_tools import ( main as openai_chat_client_with_function_tools, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_local_mcp import ( +from samples.getting_started.agents.openai.openai_chat_client_with_local_mcp import ( main as openai_chat_client_with_local_mcp, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_thread import ( +from samples.getting_started.agents.openai.openai_chat_client_with_thread import ( main as openai_chat_client_with_thread, ) -from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_web_search import ( +from samples.getting_started.agents.openai.openai_chat_client_with_web_search import ( main as openai_chat_client_with_web_search, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_basic import ( +from samples.getting_started.agents.openai.openai_responses_client_basic import ( main as openai_responses_client_basic, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_reasoning import ( +from samples.getting_started.agents.openai.openai_responses_client_reasoning import ( main as openai_responses_client_reasoning, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_code_interpreter import ( +from samples.getting_started.agents.openai.openai_responses_client_with_code_interpreter import ( main as openai_responses_client_with_code_interpreter, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_explicit_settings import ( +from samples.getting_started.agents.openai.openai_responses_client_with_explicit_settings import ( main as openai_responses_client_with_explicit_settings, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_file_search import ( +from samples.getting_started.agents.openai.openai_responses_client_with_file_search import ( main as openai_responses_client_with_file_search, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_function_tools import ( +from samples.getting_started.agents.openai.openai_responses_client_with_function_tools import ( main as openai_responses_client_with_function_tools, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_local_mcp import ( +from samples.getting_started.agents.openai.openai_responses_client_with_local_mcp import ( main as openai_responses_client_with_local_mcp, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_thread import ( +from samples.getting_started.agents.openai.openai_responses_client_with_thread import ( main as openai_responses_client_with_thread, ) -from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_web_search import ( +from samples.getting_started.agents.openai.openai_responses_client_with_web_search import ( main as openai_responses_client_with_web_search, ) from tests.sample_utils import retry @@ -291,85 +291,85 @@ agent_samples = [ pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), - # Foundry Agent samples + # Azure AI Agent samples param( - foundry_basic, + azure_ai_basic, [], # Non-interactive sample - id="foundry_basic", + id="azure_ai_basic", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_code_interpreter, + azure_ai_with_code_interpreter, [], # Non-interactive sample - id="foundry_with_code_interpreter", + id="azure_ai_with_code_interpreter", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_existing_agent, + azure_ai_with_existing_agent, [], # Non-interactive sample - id="foundry_with_existing_agent", + id="azure_ai_with_existing_agent", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_explicit_settings, + azure_ai_with_explicit_settings, [], # Non-interactive sample - id="foundry_with_explicit_settings", + id="azure_ai_with_explicit_settings", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_function_tools_agent, + azure_ai_with_function_tools_agent, [], # Non-interactive sample - id="foundry_with_function_tools", + id="azure_ai_with_function_tools", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_function_tools_run, + azure_ai_with_function_tools_run, [], # Non-interactive sample - id="foundry_with_function_tools", + id="azure_ai_with_function_tools", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_function_tools_mixed, + azure_ai_with_function_tools_mixed, [], # Non-interactive sample - id="foundry_with_function_tools", + id="azure_ai_with_function_tools", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_thread, + azure_ai_with_thread, [], # Non-interactive sample - id="foundry_with_thread", + id="azure_ai_with_thread", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), param( - foundry_with_local_mcp, + azure_ai_with_local_mcp, [], # Non-interactive sample - id="foundry_with_local_mcp", + id="azure_ai_with_local_mcp", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), diff --git a/python/tests/samples/getting_started/test_chat_client.py b/python/tests/samples/getting_started/test_chat_client.py index 2c93ee0804..740ea919b4 100644 --- a/python/tests/samples/getting_started/test_chat_client.py +++ b/python/tests/samples/getting_started/test_chat_client.py @@ -8,6 +8,9 @@ from typing import Any import pytest from pytest import MonkeyPatch, mark, param +from samples.getting_started.chat_client.azure_ai_chat_client import ( + main as azure_ai_chat_client, +) from samples.getting_started.chat_client.azure_assistants_client import ( main as azure_assistants_client, ) @@ -20,9 +23,6 @@ from samples.getting_started.chat_client.azure_responses_client import ( from samples.getting_started.chat_client.chat_response_cancellation import ( main as chat_response_cancellation, ) -from samples.getting_started.chat_client.foundry_chat_client import ( - main as foundry_chat_client, -) from samples.getting_started.chat_client.openai_assistants_client import ( main as openai_assistants_client, ) @@ -67,13 +67,13 @@ chat_client_samples = [ pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), - # Foundry Chat Client samples + # Azure AI Chat Client samples param( - foundry_chat_client, + azure_ai_chat_client, [], # Non-interactive sample - id="foundry_chat_client", + id="azure_ai_chat_client", marks=[ - pytest.mark.foundry, + pytest.mark.azure_ai, pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), diff --git a/python/uv.lock b/python/uv.lock index f7d3ddd404..0d2c099c92 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -24,10 +24,9 @@ supported-markers = [ [manifest] members = [ "agent-framework", - "agent-framework-azure", + "agent-framework-azure-ai", "agent-framework-copilotstudio", "agent-framework-devui", - "agent-framework-foundry", "agent-framework-lab-gaia", "agent-framework-lab-lightning", "agent-framework-lab-tau2", @@ -52,6 +51,7 @@ version = "0.1.0b1" source = { editable = "packages/main" } dependencies = [ { name = "aiofiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "azure-monitor-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "azure-monitor-opentelemetry-exporter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "mcp", extra = ["ws"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -67,25 +67,32 @@ dependencies = [ [package.optional-dependencies] all = [ - { name = "agent-framework-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-azure-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-devui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-runtime", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "graphviz", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] azure = [ - { name = "agent-framework-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-azure-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +azure-ai = [ + { name = "agent-framework-azure-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] devui = [ { name = "agent-framework-devui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -foundry = [ - { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, -] mem0 = [ { name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] +microsoft = [ + { name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +microsoft-copilotstudio = [ + { name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] redis = [ { name = "agent-framework-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] @@ -98,12 +105,14 @@ viz = [ [package.metadata] requires-dist = [ - { name = "agent-framework-azure", marker = "extra == 'all'", editable = "packages/azure" }, - { name = "agent-framework-azure", marker = "extra == 'azure'", editable = "packages/azure" }, + { name = "agent-framework-azure-ai", marker = "extra == 'all'", editable = "packages/azure-ai" }, + { name = "agent-framework-azure-ai", marker = "extra == 'azure'", editable = "packages/azure-ai" }, + { name = "agent-framework-azure-ai", marker = "extra == 'azure-ai'", editable = "packages/azure-ai" }, + { name = "agent-framework-copilotstudio", marker = "extra == 'all'", editable = "packages/copilotstudio" }, + { name = "agent-framework-copilotstudio", marker = "extra == 'microsoft'", editable = "packages/copilotstudio" }, + { name = "agent-framework-copilotstudio", marker = "extra == 'microsoft-copilotstudio'", editable = "packages/copilotstudio" }, { name = "agent-framework-devui", marker = "extra == 'all'", editable = "packages/devui" }, { name = "agent-framework-devui", marker = "extra == 'devui'", editable = "packages/devui" }, - { name = "agent-framework-foundry", marker = "extra == 'all'", editable = "packages/foundry" }, - { name = "agent-framework-foundry", marker = "extra == 'foundry'", editable = "packages/foundry" }, { name = "agent-framework-mem0", marker = "extra == 'all'", editable = "packages/mem0" }, { name = "agent-framework-mem0", marker = "extra == 'mem0'", editable = "packages/mem0" }, { name = "agent-framework-redis", marker = "extra == 'all'", editable = "packages/redis" }, @@ -111,34 +120,40 @@ requires-dist = [ { name = "agent-framework-runtime", marker = "extra == 'all'", editable = "packages/runtime" }, { name = "agent-framework-runtime", marker = "extra == 'runtime'", editable = "packages/runtime" }, { name = "aiofiles", specifier = ">=24.1.0" }, + { name = "azure-identity", specifier = ">=1,<2" }, { name = "azure-monitor-opentelemetry", specifier = ">=1.7.0" }, { name = "azure-monitor-opentelemetry-exporter", specifier = ">=1.0.0b41" }, + { name = "graphviz", marker = "extra == 'all'", specifier = ">=0.20.0" }, { name = "graphviz", marker = "extra == 'viz'", specifier = ">=0.20.0" }, { name = "mcp", extras = ["ws"], specifier = ">=1.13" }, { name = "openai", specifier = ">=1.99.0" }, - { name = "opentelemetry-api", specifier = "~=1.24" }, + { name = "opentelemetry-api", specifier = ">=1.24" }, { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.36.0" }, - { name = "opentelemetry-sdk", specifier = "~=1.24" }, + { name = "opentelemetry-sdk", specifier = ">=1.24" }, { name = "opentelemetry-semantic-conventions-ai", specifier = ">=0.4.13" }, - { name = "pydantic", specifier = ">=2.11.7" }, - { name = "pydantic-settings", specifier = ">=2.10.1" }, - { name = "typing-extensions", specifier = ">=4.14.0" }, + { name = "pydantic", specifier = ">=2,<3" }, + { name = "pydantic-settings", specifier = ">=2,<3" }, + { name = "typing-extensions" }, ] -provides-extras = ["azure", "foundry", "redis", "viz", "runtime", "mem0", "devui", "all"] +provides-extras = ["azure-ai", "azure", "microsoft-copilotstudio", "microsoft", "redis", "viz", "runtime", "mem0", "devui", "all"] [[package]] -name = "agent-framework-azure" +name = "agent-framework-azure-ai" version = "0.1.0b1" -source = { editable = "packages/azure" } +source = { editable = "packages/azure-ai" } dependencies = [ { name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-ai-agents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] [package.metadata] requires-dist = [ { name = "agent-framework", editable = "packages/main" }, - { name = "azure-identity", specifier = ">=1.13" }, + { name = "aiohttp", specifier = "~=3.8" }, + { name = "azure-ai-agents", specifier = ">=1.2.0b4" }, + { name = "azure-ai-projects", specifier = ">=1.0.0b11" }, ] [[package]] @@ -190,25 +205,6 @@ requires-dist = [ ] provides-extras = ["dev", "all"] -[[package]] -name = "agent-framework-foundry" -version = "0.1.0b1" -source = { editable = "packages/foundry" } -dependencies = [ - { name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-agents", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "azure-ai-projects", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, -] - -[package.metadata] -requires-dist = [ - { name = "agent-framework", editable = "packages/main" }, - { name = "aiohttp", specifier = "~=3.8" }, - { name = "azure-ai-agents", specifier = ">=1.2.0b4" }, - { name = "azure-ai-projects", specifier = ">=1.0.0b11" }, -] - [[package]] name = "agent-framework-lab-gaia" version = "0.1.0b1" @@ -289,10 +285,9 @@ version = "0.0.0" source = { virtual = "." } dependencies = [ { name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-azure-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-copilotstudio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-devui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-lab-gaia", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-redis", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -325,10 +320,9 @@ docs = [ [package.metadata] requires-dist = [ { name = "agent-framework", editable = "packages/main" }, - { name = "agent-framework-azure", editable = "packages/azure" }, + { name = "agent-framework-azure-ai", editable = "packages/azure-ai" }, { name = "agent-framework-copilotstudio", editable = "packages/copilotstudio" }, { name = "agent-framework-devui", editable = "packages/devui" }, - { name = "agent-framework-foundry", editable = "packages/foundry" }, { name = "agent-framework-lab-gaia", editable = "packages/lab/gaia" }, { name = "agent-framework-mem0", editable = "packages/mem0" }, { name = "agent-framework-redis", editable = "packages/redis" }, @@ -526,7 +520,7 @@ wheels = [ [[package]] name = "anyio" -version = "4.10.0" +version = "4.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, @@ -534,9 +528,9 @@ dependencies = [ { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] [[package]] @@ -1839,7 +1833,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.35.0" +version = "0.35.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -1851,9 +1845,9 @@ dependencies = [ { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/79/d71d40efa058e8c4a075158f8855bc2998037b5ff1c84f249f34435c1df7/huggingface_hub-0.35.0.tar.gz", hash = "sha256:ccadd2a78eef75effff184ad89401413629fabc52cefd76f6bbacb9b1c0676ac", size = 461486, upload-time = "2025-09-16T13:49:33.282Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/42/0e7be334a6851cd7d51cc11717cb95e89333ebf0064431c0255c56957526/huggingface_hub-0.35.1.tar.gz", hash = "sha256:3585b88c5169c64b7e4214d0e88163d4a709de6d1a502e0cd0459e9ee2c9c572", size = 461374, upload-time = "2025-09-23T13:43:47.074Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/85/a18508becfa01f1e4351b5e18651b06d210dbd96debccd48a452acccb901/huggingface_hub-0.35.0-py3-none-any.whl", hash = "sha256:f2e2f693bca9a26530b1c0b9bcd4c1495644dad698e6a0060f90e22e772c31e9", size = 563436, upload-time = "2025-09-16T13:49:30.627Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/4acf0c8a3925d9ff491dc08fe84d37e09cfca9c3b885e0db3d4dedb98cea/huggingface_hub-0.35.1-py3-none-any.whl", hash = "sha256:2f0e2709c711e3040e31d3e0418341f7092910f1462dd00350c4e97af47280a8", size = 563340, upload-time = "2025-09-23T13:43:45.343Z" }, ] [[package]] @@ -2509,16 +2503,16 @@ wheels = [ [[package]] name = "msal" -version = "1.33.0" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pyjwt", extra = ["crypto"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510", size = 153801, upload-time = "2025-07-22T19:36:33.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/0e/c857c46d653e104019a84f22d4494f2119b4fe9f896c92b4b864b3b045cc/msal-1.34.0.tar.gz", hash = "sha256:76ba83b716ea5a6d75b0279c0ac353a0e05b820ca1f6682c0eb7f45190c43c2f", size = 153961, upload-time = "2025-09-22T23:05:48.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273", size = 116853, upload-time = "2025-07-22T19:36:32.403Z" }, + { url = "https://files.pythonhosted.org/packages/c2/dc/18d48843499e278538890dc709e9ee3dea8375f8be8e82682851df1b48b5/msal-1.34.0-py3-none-any.whl", hash = "sha256:f669b1644e4950115da7a176441b0e13ec2975c29528d8b9e81316023676d6e1", size = 116987, upload-time = "2025-09-22T23:05:47.294Z" }, ] [[package]] @@ -3066,21 +3060,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/7f/88079bc3e4aa188d78692328453f906dca35fa9f286623af13df0b0a1ead/opentelemetry_instrumentation_flask-0.58b0-py3-none-any.whl", hash = "sha256:b0d57ad4db7bd0177ddf8c7ae3adf8bd90e2ebfa2dd30884c6a97c97197e4ac5", size = 14685, upload-time = "2025-09-11T11:41:30.02Z" }, ] -[[package]] -name = "opentelemetry-instrumentation-openai" -version = "0.47.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "opentelemetry-semantic-conventions-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/46/3e59891b433869c731131b5ececdaa3ee1d4a45d19169299b52d1397c8e5/opentelemetry_instrumentation_openai-0.47.1.tar.gz", hash = "sha256:a9ad8d898f8f03581fff1764b605d2c277381ced3083a66e7bddb951b95afb2a", size = 25411, upload-time = "2025-09-14T12:09:19.202Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/4e/ee98a8e9b9d58425ff84aff63d06fdf004727cdac2cfe3a2916a1869a2e2/opentelemetry_instrumentation_openai-0.47.1-py3-none-any.whl", hash = "sha256:2bc426c1324f7e9babee8d2a02d7966562ec993da5d280c597bd29a92997e2ed", size = 35274, upload-time = "2025-09-14T12:08:48.987Z" }, -] - [[package]] name = "opentelemetry-instrumentation-psycopg2" version = "0.58b0" @@ -3730,7 +3709,7 @@ wheels = [ [[package]] name = "py2docfx" -version = "0.1.20" +version = "0.1.21" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -3738,7 +3717,7 @@ dependencies = [ { name = "wheel", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/89/c3/77b954fb853ddd3857a814e2a723b5ce2e8c0e59fbed7b83a962db714e66/py2docfx-0.1.20-py3-none-any.whl", hash = "sha256:d450e62591e8b96c5c8be2001f909ca5a1bb2e4dde8c0c8dc7ffb1aec45e4369", size = 11400026, upload-time = "2025-08-08T02:57:00.075Z" }, + { url = "https://files.pythonhosted.org/packages/bc/bc/056b4751229c92b58296f8eccd262a448f799b3044510f2357742eeeae98/py2docfx-0.1.21-py3-none-any.whl", hash = "sha256:ce782d503593c79fc49324d3b102fe38b1cf5de9e13dfe582abe6d1387a67999", size = 11410660, upload-time = "2025-09-23T03:44:18.458Z" }, ] [[package]] @@ -5121,42 +5100,42 @@ wheels = [ [[package]] name = "uv" -version = "0.8.19" +version = "0.8.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/97/fe818d33597b69746da89c7fecf1d5047f44eaadc464ad35157a546a4a77/uv-0.8.19.tar.gz", hash = "sha256:8250d61be8ad7da952dff476d9855b2d0b3b5737819cc12ecd94ae6665aab322", size = 3658322, upload-time = "2025-09-19T20:08:10.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/5c/285626e2fa4f89d3111aba48dd1a3b940a7fc21b3e0b0595c90fd15bc216/uv-0.8.20.tar.gz", hash = "sha256:9560b87a25b05a2487a2a97ba944df22e565d7c0c8586f78152b867970fa1329", size = 3659652, upload-time = "2025-09-22T23:02:24.601Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/ca/2b295412fe26d54745ffbd67b01c7c8edae753b38e188a99726a8cadb9a4/uv-0.8.19-py3-none-linux_armv6l.whl", hash = "sha256:d2c19109b13245094222adac41665d6a785c7d495981e5c41ffc7814bb5c9e0a", size = 20250724, upload-time = "2025-09-19T20:07:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/b0/a2/b524d7dd5fc7a81b98005805c3daa9512477b29fe37759c90ecfd4c8e65f/uv-0.8.19-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c9c4aeb322faa28d8d97339b0ed75f29e470dcaed08300de049b8f03a8074386", size = 19260875, upload-time = "2025-09-19T20:07:18.187Z" }, - { url = "https://files.pythonhosted.org/packages/01/53/3a965df32ba40377850b8fd22a8e7a0bdbb2df12d4881d7ca43ba38729d3/uv-0.8.19-py3-none-macosx_11_0_arm64.whl", hash = "sha256:78aeab7fd2b398c517bbff02096d79df71f4b0ec1fca65af8627f10a95eaa393", size = 17881146, upload-time = "2025-09-19T20:07:21.161Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3d/bd58b35c831163519dfdbedfd61685fa5e666db422436097af8c8fa1b094/uv-0.8.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:4d8abd84fec2f4e476da1a5b59d246e63cabcb5c92432ea126cc3096cd87d43b", size = 19670532, upload-time = "2025-09-19T20:07:23.867Z" }, - { url = "https://files.pythonhosted.org/packages/9c/43/9c2b3b812acfee547b88467765353a504a3742f0b826c417587aa7a22a6d/uv-0.8.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9dad4d57496b827742388fba09861bec731b859c911c24e0c0b70e47bd168bd9", size = 19832944, upload-time = "2025-09-19T20:07:26.719Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9d/5a5c1911ef45393b0116f9d9050fa6293814a8174f0faa22b3a0f323c60f/uv-0.8.19-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c151785f8f158767279204d2e08b4f8a1b0efdee8d336c933887700208cc6ae", size = 20795887, upload-time = "2025-09-19T20:07:29.664Z" }, - { url = "https://files.pythonhosted.org/packages/74/b3/a2a419733df18084e993db5f4e159b4af53948260b6b22941f4680be049d/uv-0.8.19-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:474d62c59258462c7e88cdc2bae2914476643156d80a21cb2abc4b19646e4520", size = 22250360, upload-time = "2025-09-19T20:07:32.49Z" }, - { url = "https://files.pythonhosted.org/packages/0b/90/84d9afcf9a2e88a191d58d7cb4d9d78204e0fc3baac51905a27e7dd03034/uv-0.8.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29f6e9e137edad619e3e1b39b5acbd2ebe85e55529187ec4426cbd187fe6a0e9", size = 21872749, upload-time = "2025-09-19T20:07:35.471Z" }, - { url = "https://files.pythonhosted.org/packages/75/10/50a809dba72a300eb1b75f8101195686fbecbc479345571fcd0ee2835fc7/uv-0.8.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64de980f2e8802e0e9937e037d0c890c5eb3f033beeb83ca46710ccf253df4db", size = 21046237, upload-time = "2025-09-19T20:07:38.277Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f9/1c95510b46d94ea6a580ed923b1a06cff454c3bf2e6d9eababcea8d7f70d/uv-0.8.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0097b67a8eddf3ed48a66b86a04754350a21a9c025eca5311732775da4d2e2b2", size = 20924288, upload-time = "2025-09-19T20:07:41.168Z" }, - { url = "https://files.pythonhosted.org/packages/53/0e/9244460c17692805f55a7d490ecc13b7e877601e1504d29771604850b9cf/uv-0.8.19-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:698ca5bc205456bd933b56332283621c8f3c93a315f3dcc20500c21ead951c07", size = 19805261, upload-time = "2025-09-19T20:07:43.899Z" }, - { url = "https://files.pythonhosted.org/packages/4b/44/3afa5f2cc136b30884f570d638239bea1c3242acaa9a0c83d9f1f982badb/uv-0.8.19-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:497f674ea7ee5c442c7c75c49e543770f1505b5a1c1749fc271671983d2a1fc5", size = 20916088, upload-time = "2025-09-19T20:07:46.911Z" }, - { url = "https://files.pythonhosted.org/packages/61/7d/7f61affd0bcba863326926a4ea983ecf08179c284d8251b1e05fbb2bab25/uv-0.8.19-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:bd65e706cb30e8d006caf42a91326cb9f96b49a332771766dd3f5992d7ecca9f", size = 19835747, upload-time = "2025-09-19T20:07:49.674Z" }, - { url = "https://files.pythonhosted.org/packages/79/d6/d3419ac38fb7817fb2bf61b3c712f8a9b1433875d730008f26a42fffa282/uv-0.8.19-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8222edcc388fc9ff41bc095dfcc0cfd861f3133b0118cc67347f15daf5fc96e9", size = 20239438, upload-time = "2025-09-19T20:07:53.834Z" }, - { url = "https://files.pythonhosted.org/packages/fa/30/efd06512b13f05d41c253ce4f5c7941cc02e99b22500b25de80c898218c9/uv-0.8.19-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d1fe68ad66e46106c987e7647e863d06d5e052b08b2da6e30664f5f630b90fc0", size = 21135323, upload-time = "2025-09-19T20:07:56.743Z" }, - { url = "https://files.pythonhosted.org/packages/68/84/094c2bcd65f873041949b1c58978005f0ae43ceb1adc222ba1ecc46d35de/uv-0.8.19-py3-none-win32.whl", hash = "sha256:2d63c86fc027769bd904d5a3ce8814ab5cfaf66f633ced552dbd9fe83fffbff0", size = 19023637, upload-time = "2025-09-19T20:07:59.547Z" }, - { url = "https://files.pythonhosted.org/packages/2c/75/1808130fec1298d1be68f0f66f0deb3f201246236957eb014db2888a03b9/uv-0.8.19-py3-none-win_amd64.whl", hash = "sha256:6d7304c5485ef091740460118840e660c2aa24edbb8267684c3380a5a40a209c", size = 21083463, upload-time = "2025-09-19T20:08:02.436Z" }, - { url = "https://files.pythonhosted.org/packages/1a/29/8bbe90380f94f8eaadedf62030316341770faf27d8713b00507970cdc552/uv-0.8.19-py3-none-win_arm64.whl", hash = "sha256:3029aadc6d19cae617f7d7b1f69a7fd2d5444ad4845e3018a736759043ef23cb", size = 19548735, upload-time = "2025-09-19T20:08:05.552Z" }, + { url = "https://files.pythonhosted.org/packages/31/19/061b2f23ad3ac4a2bebd1c934623821c62ccfb454385a052c2f8889a3592/uv-0.8.20-py3-none-linux_armv6l.whl", hash = "sha256:46798fad22dfcc1ba66c4e98faddbf15e3878255345049f5b599879caf52b01a", size = 20270849, upload-time = "2025-09-22T23:01:12.315Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c5/80b0c091c56da5a709c01bebf9fd2f8d1fd553289e5ec0ba1783cd2a7d94/uv-0.8.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4f9941c625c12abaeb619c693b37007082e975455d149f32bb12068535af043e", size = 19282587, upload-time = "2025-09-22T23:01:18.162Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/5f5407a0bdd9309ad9abb96633055c721c5faa1cb11d21a2568b8dc79626/uv-0.8.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:94f30a025cbdd82f6212b0297c47e63e56cc57a5fb6f4e74853554f19ac9f6fa", size = 17908187, upload-time = "2025-09-22T23:01:21.907Z" }, + { url = "https://files.pythonhosted.org/packages/65/5b/40b874db599c016586a5becee6064b6502aeb07f7150a685ea2b4f0e1cac/uv-0.8.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:be6a69919241d4e953cfd7f601a64012649cd69cf66d499474bc10fc4f4dabb0", size = 19697193, upload-time = "2025-09-22T23:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/b8ff6f97d649f5b74183b6a0ca9d59744086ee8f873034969110da4c4cbf/uv-0.8.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:342ff059c4866c2dcefa829acf75b47cf0a03201769941afab5c774424a902ab", size = 19840725, upload-time = "2025-09-22T23:01:29.703Z" }, + { url = "https://files.pythonhosted.org/packages/07/0d/7fc078cead61c472ede432f931b6010be70daf8682b04f0c86911f76908b/uv-0.8.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edb79a6948712285220f21120dc740648e9ee640f7782be811b620895a4af7fc", size = 20820489, upload-time = "2025-09-22T23:01:33.446Z" }, + { url = "https://files.pythonhosted.org/packages/7f/df/572035306469de778365f25ae6926fa55ebfb87bc93144c6635877fda2e8/uv-0.8.20-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d3a872a87fdd69b46f865f9743dfdc51850f7b5d64cda48b4eb8a9ebb792df26", size = 22250316, upload-time = "2025-09-22T23:01:37.183Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c8/9b475b7fe01f2ef0375ce94e0643ed9606fde63a621b96bddca22a4eabff/uv-0.8.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0024e6c5fdc633ee2659a3bcd01db4bb5e828c87322af2f96a6cd9dbd8fbb202", size = 21896353, upload-time = "2025-09-22T23:01:41.085Z" }, + { url = "https://files.pythonhosted.org/packages/04/f4/6165da453076a18caa2d2f892e661071225566248df6c5a20428273d2282/uv-0.8.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2eab535373ef1d81cd5181e267b14bfc53ad28e39de6d222dcb118c163386902", size = 21073526, upload-time = "2025-09-22T23:01:44.663Z" }, + { url = "https://files.pythonhosted.org/packages/bb/74/5a4996aa94346ffa4930af06e8c36117880d1e19c8695f820f279f3280eb/uv-0.8.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f70877df868b56e408883bad8e5bfa68ffc27865af181e5ee499074b364f571", size = 20967130, upload-time = "2025-09-22T23:01:48.337Z" }, + { url = "https://files.pythonhosted.org/packages/a9/7f/199a1a9f41eb0dba2f34f2f69008f3939d591747c90ad23406a551d61a1d/uv-0.8.20-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:59d70332b42a7c8dd5b8ecce54c05ffa126bd4eafebd7ededf347b915f2e4263", size = 19813405, upload-time = "2025-09-22T23:01:52.454Z" }, + { url = "https://files.pythonhosted.org/packages/4f/da/20e559b743f961590319435874e32267280795e112657b7edbfaf9ed3662/uv-0.8.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:40d749a856867bc7e0fa9314c06cc8e91584122e33e90c594f298d777c9acd51", size = 20887613, upload-time = "2025-09-22T23:01:56.688Z" }, + { url = "https://files.pythonhosted.org/packages/47/dc/52b4ad48e812cade9122e742e8a4249ccf0c9071f1b5ea26e3ad5d0bb7b6/uv-0.8.20-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a39a90083cd7d5639affe77f969f80d93153ba7b82962f62ea30a944634f90dc", size = 19848883, upload-time = "2025-09-22T23:02:00.395Z" }, + { url = "https://files.pythonhosted.org/packages/db/fd/b61612e4bb87a3b583dfd7906dcebb9f3b3f3ca27a4b462301774d0dd503/uv-0.8.20-py3-none-musllinux_1_1_i686.whl", hash = "sha256:d5994d5cb4a6b327de657511825cb9e14d8fc1c7518d2cb01eeec70000f575ed", size = 20234284, upload-time = "2025-09-22T23:02:03.886Z" }, + { url = "https://files.pythonhosted.org/packages/68/55/e011ede284e0a1f1e38e27549c0a80f2bf44b1321003c1ddffc2d76110b3/uv-0.8.20-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:7d01cbe0a856f72a2505c6ac3ca4b9fafc304df664a00f27c9d3e4ddb085a844", size = 21139219, upload-time = "2025-09-22T23:02:09.099Z" }, + { url = "https://files.pythonhosted.org/packages/21/ab/aba7b572bf4066f3046f9143c28c58a833fafd3b0a16951da11739e3c6c2/uv-0.8.20-py3-none-win32.whl", hash = "sha256:86ce315cce8be58e24a88448f42649760aa29cc429691638f281337b68fe20b3", size = 19033345, upload-time = "2025-09-22T23:02:12.67Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9c/50ad29d3697bb85ba09e47d2d3ebf7ef694320c8054c2513f641782da96e/uv-0.8.20-py3-none-win_amd64.whl", hash = "sha256:e356e987b14779c957bc402abf5d917b0ff6c53b4153ad0271a00eef46a9b65a", size = 21082152, upload-time = "2025-09-22T23:02:17.534Z" }, + { url = "https://files.pythonhosted.org/packages/7a/01/4d44aacb9b02561fdbd53948ffc278b78c80e929debba4945809c4cf1295/uv-0.8.20-py3-none-win_arm64.whl", hash = "sha256:23222fd90d843d8c5650f2b3e297dbed4d05a4d28a5e99d017d73aebaa98bea4", size = 19560961, upload-time = "2025-09-22T23:02:21.791Z" }, ] [[package]] name = "uvicorn" -version = "0.36.0" +version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/5e/f0cd46063a02fd8515f0e880c37d2657845b7306c16ce6c4ffc44afd9036/uvicorn-0.36.0.tar.gz", hash = "sha256:527dc68d77819919d90a6b267be55f0e76704dca829d34aea9480be831a9b9d9", size = 80032, upload-time = "2025-09-20T01:07:14.418Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/06/5cc0542b47c0338c1cb676b348e24a1c29acabc81000bced518231dded6f/uvicorn-0.36.0-py3-none-any.whl", hash = "sha256:6bb4ba67f16024883af8adf13aba3a9919e415358604ce46780d3f9bdc36d731", size = 67675, upload-time = "2025-09-20T01:07:12.984Z" }, + { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, ] [package.optional-dependencies]