diff --git a/.github/ISSUE_TEMPLATE/python-issue.yml b/.github/ISSUE_TEMPLATE/python-issue.yml index 3a506c66fe..4c4d94a953 100644 --- a/.github/ISSUE_TEMPLATE/python-issue.yml +++ b/.github/ISSUE_TEMPLATE/python-issue.yml @@ -47,7 +47,7 @@ body: attributes: label: Package Versions description: List the agent-framework-* packages and versions you are using - placeholder: "e.g., agent-framework-core: 1.0.0, agent-framework-azure-ai: 1.0.0" + placeholder: "e.g., agent-framework-core: 1.0.0, agent-framework-foundry: 1.0.0" validations: required: true diff --git a/.github/workflows/python-check-coverage.py b/.github/workflows/python-check-coverage.py index e40c497c86..c9694aa35e 100644 --- a/.github/workflows/python-check-coverage.py +++ b/.github/workflows/python-check-coverage.py @@ -37,7 +37,6 @@ ENFORCED_TARGETS: set[str] = { # Packages (sorted alphabetically) "packages.anthropic.agent_framework_anthropic", "packages.azure-ai-search.agent_framework_azure_ai_search", - "packages.azure-ai.agent_framework_azure_ai", "packages.core.agent_framework", "packages.core.agent_framework._workflows", "packages.foundry.agent_framework_foundry", diff --git a/.github/workflows/python-integration-tests.yml b/.github/workflows/python-integration-tests.yml index 06720cc7dd..523a763b62 100644 --- a/.github/workflows/python-integration-tests.yml +++ b/.github/workflows/python-integration-tests.yml @@ -60,8 +60,8 @@ jobs: environment: integration timeout-minutes: 60 env: - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }} OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} @@ -95,8 +95,8 @@ jobs: environment: integration timeout-minutes: 60 env: - AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} - AZURE_OPENAI_RESPONSES_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} AZURE_OPENAI_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} AZURE_OPENAI_EMBEDDING_MODEL: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }} AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} @@ -201,14 +201,15 @@ jobs: timeout-minutes: 60 env: UV_PYTHON: "3.11" - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }} OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} AZURE_OPENAI_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} - AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }} FUNCTIONS_WORKER_RUNTIME: "python" @@ -255,12 +256,14 @@ jobs: environment: integration timeout-minutes: 60 env: - AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }} - AZURE_AI_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }} FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }} FOUNDRY_AGENT_NAME: ${{ vars.FOUNDRY_AGENT_NAME }} FOUNDRY_AGENT_VERSION: ${{ vars.FOUNDRY_AGENT_VERSION }} + FOUNDRY_MODELS_ENDPOINT: ${{ vars.FOUNDRY_MODELS_ENDPOINT || '' }} + FOUNDRY_MODELS_API_KEY: ${{ secrets.FOUNDRY_MODELS_API_KEY || '' }} + FOUNDRY_EMBEDDING_MODEL: ${{ vars.FOUNDRY_EMBEDDING_MODEL || '' }} + FOUNDRY_IMAGE_EMBEDDING_MODEL: ${{ vars.FOUNDRY_IMAGE_EMBEDDING_MODEL || '' }} LOCAL_MCP_URL: ${{ vars.LOCAL_MCP__URL }} defaults: run: diff --git a/.github/workflows/python-merge-tests.yml b/.github/workflows/python-merge-tests.yml index 12b602d0eb..4417165a24 100644 --- a/.github/workflows/python-merge-tests.yml +++ b/.github/workflows/python-merge-tests.yml @@ -37,7 +37,7 @@ jobs: azureChanged: ${{ steps.filter.outputs.azure }} miscChanged: ${{ steps.filter.outputs.misc }} functionsChanged: ${{ steps.filter.outputs.functions }} - azureAiChanged: ${{ steps.filter.outputs.azure-ai }} + foundryChanged: ${{ steps.filter.outputs.foundry }} cosmosChanged: ${{ steps.filter.outputs.cosmos }} steps: - uses: actions/checkout@v6 @@ -75,10 +75,10 @@ jobs: functions: - 'python/packages/azurefunctions/**' - 'python/packages/durabletask/**' - azure-ai: - - 'python/packages/azure-ai/**' + foundry: - 'python/packages/foundry/**' - 'python/samples/**/providers/foundry/**' + - 'python/samples/02-agents/embeddings/foundry_embeddings.py' cosmos: - 'python/packages/azure-cosmos/**' # run only if 'python' files were changed @@ -139,8 +139,8 @@ jobs: runs-on: ubuntu-latest environment: integration env: - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }} OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} @@ -192,8 +192,8 @@ jobs: runs-on: ubuntu-latest environment: integration env: - AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} - AZURE_OPENAI_RESPONSES_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} AZURE_OPENAI_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} AZURE_OPENAI_EMBEDDING_MODEL: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME }} AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} @@ -330,14 +330,15 @@ jobs: environment: integration env: UV_PYTHON: "3.11" - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_EMBEDDING_MODEL: ${{ vars.OPENAI_EMBEDDING_MODEL_ID }} OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} AZURE_OPENAI_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} - AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }} FUNCTIONS_WORKER_RUNTIME: "python" @@ -392,13 +393,11 @@ jobs: github.event_name != 'pull_request' && needs.paths-filter.outputs.pythonChanges == 'true' && (github.event_name != 'merge_group' || - needs.paths-filter.outputs.azureAiChanged == 'true' || + needs.paths-filter.outputs.foundryChanged == 'true' || needs.paths-filter.outputs.coreChanged == 'true') runs-on: ubuntu-latest environment: integration env: - AZURE_AI_PROJECT_ENDPOINT: ${{ secrets.AZUREAI__ENDPOINT }} - AZURE_AI_MODEL: ${{ vars.AZUREAI__DEPLOYMENTNAME }} FOUNDRY_PROJECT_ENDPOINT: ${{ vars.FOUNDRY_PROJECT_ENDPOINT }} FOUNDRY_MODEL: ${{ vars.FOUNDRY_MODEL }} FOUNDRY_AGENT_NAME: ${{ vars.FOUNDRY_AGENT_NAME }} @@ -432,11 +431,6 @@ jobs: --timeout=120 --session-timeout=900 --timeout_method thread --retries 2 --retry-delay 5 working-directory: ./python - - name: Test Azure AI samples - timeout-minutes: 10 - if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "azure-ai" - working-directory: ./python - name: Surface failing tests if: always() uses: pmeier/pytest-results-action@v0.7.2 diff --git a/.github/workflows/python-sample-validation.yml b/.github/workflows/python-sample-validation.yml index 598da770fb..8b72df3b74 100644 --- a/.github/workflows/python-sample-validation.yml +++ b/.github/workflows/python-sample-validation.yml @@ -66,12 +66,13 @@ jobs: # Azure OpenAI configuration AZURE_OPENAI_ENDPOINT: ${{ vars.AZUREOPENAI__ENDPOINT }} AZURE_OPENAI_MODEL: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME || vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} - AZURE_OPENAI_RESPONSES_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.AZUREOPENAI__CHATDEPLOYMENTNAME }} + AZURE_OPENAI_CHAT_MODEL: ${{ vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} AZURE_OPENAI_EMBEDDING_MODEL: ${{ vars.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME || vars.AZUREOPENAI__EMBEDDINGDEPLOYMENTNAME }} # OpenAI configuration OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} # GitHub MCP GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} @@ -97,11 +98,12 @@ jobs: echo "FOUNDRY_MODEL=$FOUNDRY_MODEL" >> .env echo "AZURE_OPENAI_ENDPOINT=$AZURE_OPENAI_ENDPOINT" >> .env echo "AZURE_OPENAI_MODEL=$AZURE_OPENAI_MODEL" >> .env - echo "AZURE_OPENAI_RESPONSES_MODEL=$AZURE_OPENAI_RESPONSES_MODEL" >> .env + echo "AZURE_OPENAI_CHAT_COMPLETION_MODEL=$AZURE_OPENAI_CHAT_COMPLETION_MODEL" >> .env + echo "AZURE_OPENAI_CHAT_MODEL=$AZURE_OPENAI_CHAT_MODEL" >> .env echo "AZURE_OPENAI_EMBEDDING_MODEL=$AZURE_OPENAI_EMBEDDING_MODEL" >> .env echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env + echo "OPENAI_CHAT_COMPLETION_MODEL=$OPENAI_CHAT_COMPLETION_MODEL" >> .env echo "OPENAI_CHAT_MODEL=$OPENAI_CHAT_MODEL" >> .env - echo "OPENAI_RESPONSES_MODEL=$OPENAI_RESPONSES_MODEL" >> .env echo "GITHUB_PAT=$GITHUB_PAT" >> .env - name: Run sample validation @@ -122,8 +124,8 @@ jobs: env: OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} OPENAI_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} defaults: run: working-directory: python @@ -142,8 +144,8 @@ jobs: run: | echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env echo "OPENAI_MODEL=$OPENAI_MODEL" >> .env + echo "OPENAI_CHAT_COMPLETION_MODEL=$OPENAI_CHAT_COMPLETION_MODEL" >> .env echo "OPENAI_CHAT_MODEL=$OPENAI_CHAT_MODEL" >> .env - echo "OPENAI_RESPONSES_MODEL=$OPENAI_RESPONSES_MODEL" >> .env - name: Run sample validation run: | @@ -565,8 +567,8 @@ jobs: AZURE_OPENAI_MODEL: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME || vars.AZUREOPENAI__RESPONSESDEPLOYMENTNAME }} # OpenAI configuration OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} defaults: run: @@ -589,8 +591,8 @@ jobs: echo "AZURE_OPENAI_ENDPOINT=$AZURE_OPENAI_ENDPOINT" >> .env echo "AZURE_OPENAI_MODEL=$AZURE_OPENAI_MODEL" >> .env echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env + echo "OPENAI_CHAT_COMPLETION_MODEL=$OPENAI_CHAT_COMPLETION_MODEL" >> .env echo "OPENAI_CHAT_MODEL=$OPENAI_CHAT_MODEL" >> .env - echo "OPENAI_RESPONSES_MODEL=$OPENAI_RESPONSES_MODEL" >> .env - name: Run sample validation run: | @@ -617,9 +619,8 @@ jobs: AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_DEPLOYMENT_NAME }} # OpenAI key OPENAI_API_KEY: ${{ secrets.OPENAI__APIKEY }} - # OpenAI configuration for AF - OPENAI_CHAT_MODEL: ${{ vars.OPENAI__CHATMODELID }} - OPENAI_RESPONSES_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} + OPENAI_CHAT_COMPLETION_MODEL: ${{ vars.OPENAI__CHATMODELID }} + OPENAI_CHAT_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} OPENAI_MODEL: ${{ vars.OPENAI__RESPONSESMODELID }} # OpenAI configuration for SK OPENAI_CHAT_MODEL_ID: ${{ vars.OPENAI__CHATMODELID }} @@ -649,8 +650,8 @@ jobs: echo "AZURE_OPENAI_ENDPOINT=$AZURE_OPENAI_ENDPOINT" >> .env echo "AZURE_OPENAI_MODEL=$AZURE_OPENAI_MODEL" >> .env echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env + echo "OPENAI_CHAT_COMPLETION_MODEL=$OPENAI_CHAT_COMPLETION_MODEL" >> .env echo "OPENAI_CHAT_MODEL=$OPENAI_CHAT_MODEL" >> .env - echo "OPENAI_RESPONSES_MODEL=$OPENAI_RESPONSES_MODEL" >> .env echo "COPILOTSTUDIOAGENT__ENVIRONMENTID=$COPILOTSTUDIOAGENT__ENVIRONMENTID" >> .env echo "COPILOTSTUDIOAGENT__SCHEMANAME=$COPILOTSTUDIOAGENT__SCHEMANAME" >> .env echo "COPILOTSTUDIOAGENT__TENANTID=$COPILOTSTUDIOAGENT__TENANTID" >> .env diff --git a/docs/decisions/0021-provider-leading-clients.md b/docs/decisions/0021-provider-leading-clients.md index 1dcc334209..7f95802161 100644 --- a/docs/decisions/0021-provider-leading-clients.md +++ b/docs/decisions/0021-provider-leading-clients.md @@ -37,7 +37,7 @@ Key changes: 4. **New `FoundryChatClient`** in azure-ai for Azure AI Foundry Responses API access, built on `RawFoundryChatClient(RawOpenAIChatClient)`. 5. **All deprecated `AzureOpenAI*` classes** consolidated into a single file (`_deprecated_azure_openai.py`) in the azure-ai package for clean future deletion. 6. **Core's `agent_framework.openai` and `agent_framework.azure` namespaces** become lazy-loading gateways, preserving backward-compatible import paths while removing hard dependencies. -7. **Unified `model` parameter** replaces `model_id` (OpenAI), `deployment_name` (Azure OpenAI), and `model_deployment_name` (Azure AI) across all client constructors. The term `model` is intentionally generic: it naturally maps to an OpenAI model name *and* to an Azure OpenAI deployment name, making it straightforward to use `OpenAIChatClient` with either OpenAI or Azure OpenAI backends (via `AsyncAzureOpenAI`). Environment variables are similarly unified (e.g., `OPENAI_MODEL` instead of separate `OPENAI_RESPONSES_MODEL_ID` / `OPENAI_CHAT_MODEL_ID`). +7. **Unified `model` parameter** replaces `model_id` (OpenAI), `deployment_name` (Azure OpenAI), and `model_deployment_name` (Azure AI) across all client constructors. The term `model` is intentionally generic: it naturally maps to an OpenAI model name *and* to an Azure OpenAI deployment name, making it straightforward to use `OpenAIChatClient` with either OpenAI or Azure OpenAI backends (via `AsyncAzureOpenAI`). Environment variables are similarly unified (e.g., `OPENAI_MODEL` instead of separate `OPENAI_CHAT_MODEL_ID` / `OPENAI_CHAT_COMPLETION_MODEL_ID`). 8. **`FoundryAgent`** replaces the pattern of `Agent(client=AzureAIClient(...))` for connecting to pre-configured agents in Azure AI Foundry (PromptAgents and HostedAgents). The underlying `RawFoundryAgentChatClient` is an implementation detail — most users interact only with `FoundryAgent`. `AzureAIAgentClient` is separately deprecated as it refers to the V1 Agents Service API. See below for design rationale. ### Foundry Agent Design: `FoundryAgentClient` vs `FoundryAgent` diff --git a/docs/features/vector-stores-and-embeddings/README.md b/docs/features/vector-stores-and-embeddings/README.md index 560fdd86d6..9f820ad7c7 100644 --- a/docs/features/vector-stores-and-embeddings/README.md +++ b/docs/features/vector-stores-and-embeddings/README.md @@ -177,7 +177,7 @@ This feature ports the vector store abstractions, embedding generator abstractio **Goal:** Add embedding generators to all existing AF provider packages that have chat clients. **Mergeable:** Yes — each is independent, added to existing provider packages. -#### 2.1 — Azure AI Inference embedding (in `packages/azure-ai/`) +#### 2.1 — Foundry inference embedding (in `packages/foundry/`) #### 2.2 — Ollama embedding (in `packages/ollama/`) #### 2.3 — Anthropic embedding (in `packages/anthropic/`) #### 2.4 — Bedrock embedding (in `packages/bedrock/`) diff --git a/python/.env.example b/python/.env.example index e5ae5f09d7..e8644ea003 100644 --- a/python/.env.example +++ b/python/.env.example @@ -1,6 +1,15 @@ -# Azure AI +# Microsoft Foundry FOUNDRY_PROJECT_ENDPOINT="" +# Model used for FoundryChatClient FOUNDRY_MODEL="" +# Foundry Agents (prompt or hosted agents) +FOUNDRY_AGENT_NAME="" +FOUNDRY_AGENT_VERSION="" +# Microsoft Foundry Models endpoint, used by embeddings +FOUNDRY_MODELS_ENDPOINT="" +FOUNDRY_MODELS_API_KEY="" +FOUNDRY_EMBEDDING_MODEL="" +FOUNDRY_IMAGE_EMBEDDING_MODEL="" # Bing connection for web search (optional, used by samples with web search) BING_CONNECTION_ID="" # Azure AI Search (optional, used by AzureAISearchContextProvider samples) @@ -13,12 +22,12 @@ AZURE_SEARCH_KNOWLEDGE_BASE_NAME="" # (different from AZURE_AI_PROJECT_ENDPOINT - Knowledge Base needs OpenAI endpoint for model calls) # OpenAI OPENAI_API_KEY="" +OPENAI_CHAT_COMPLETION_MODEL="" OPENAI_CHAT_MODEL="" -OPENAI_RESPONSES_MODEL="" # Azure OpenAI AZURE_OPENAI_ENDPOINT="" +AZURE_OPENAI_CHAT_COMPLETION_MODEL="" AZURE_OPENAI_CHAT_MODEL="" -AZURE_OPENAI_RESPONSES_MODEL="" # Mem0 MEM0_API_KEY="" # Copilot Studio diff --git a/python/.github/skills/python-package-management/SKILL.md b/python/.github/skills/python-package-management/SKILL.md index baee776828..cd870c747d 100644 --- a/python/.github/skills/python-package-management/SKILL.md +++ b/python/.github/skills/python-package-management/SKILL.md @@ -15,7 +15,7 @@ python/ ├── pyproject.toml # Root package (agent-framework) ├── packages/ │ ├── core/ # agent-framework-core (main package) -│ ├── azure-ai/ # agent-framework-azure-ai +│ ├── foundry/ # agent-framework-foundry │ ├── anthropic/ # agent-framework-anthropic │ └── ... # Other connector packages ``` @@ -76,9 +76,9 @@ uv run poe add-dependency-and-validate-bounds --package core --dependency " Any: diff --git a/python/.github/skills/python-testing/SKILL.md b/python/.github/skills/python-testing/SKILL.md index b9c874a694..1d8fb50cf1 100644 --- a/python/.github/skills/python-testing/SKILL.md +++ b/python/.github/skills/python-testing/SKILL.md @@ -124,7 +124,7 @@ The merge CI workflow (`python-merge-tests.yml`) splits integration tests into p - **Azure OpenAI integration** — runs when `packages/core/agent_framework/azure/` or core changes - **Misc integration** — Anthropic, Ollama, MCP tests; runs when their packages or core change - **Functions integration** — Azure Functions + Durable Task; runs when their packages or core change -- **Azure AI integration** — runs when `packages/azure-ai/` or core changes +- **Foundry integration** — runs when `packages/foundry/` or core changes Core infrastructure changes (e.g., `_agents.py`, `_types.py`) trigger all integration test jobs. Scheduled and manual runs always execute all jobs. diff --git a/python/AGENTS.md b/python/AGENTS.md index eeab275a4e..e4697e18d5 100644 --- a/python/AGENTS.md +++ b/python/AGENTS.md @@ -40,7 +40,7 @@ python/ │ ├── core/ # agent-framework-core (main package) │ │ ├── agent_framework/ # Public API exports │ │ └── tests/ -│ ├── azure-ai/ # agent-framework-azure-ai +│ ├── foundry/ # agent-framework-foundry │ ├── anthropic/ # agent-framework-anthropic │ ├── ollama/ # agent-framework-ollama │ └── ... # Other provider packages @@ -52,7 +52,7 @@ python/ ### Package Relationships - `agent-framework-core` contains core abstractions and OpenAI/Azure OpenAI built-in -- Provider packages (`azure-ai`, `anthropic`, etc.) extend core with specific integrations +- Provider packages (`foundry`, `anthropic`, etc.) extend core with specific integrations - Core uses lazy loading via `__getattr__` in provider folders (e.g., `agent_framework/azure/`) ## Package Documentation @@ -68,8 +68,9 @@ python/ - [ollama](packages/ollama/AGENTS.md) - Local Ollama inference ### Azure Integrations -- [azure-ai](packages/azure-ai/AGENTS.md) - Azure AI Foundry agents +- [foundry](packages/foundry/README.md) - Microsoft Foundry chat, agent, memory, and embedding integrations - [azure-ai-search](packages/azure-ai-search/AGENTS.md) - Azure AI Search RAG +- [azure-cosmos](packages/azure-cosmos/AGENTS.md) - Azure Cosmos DB-backed history provider - [azurefunctions](packages/azurefunctions/AGENTS.md) - Azure Functions hosting ### Protocols & UI diff --git a/python/CODING_STANDARD.md b/python/CODING_STANDARD.md index 3ffeb3562d..8c73414f3f 100644 --- a/python/CODING_STANDARD.md +++ b/python/CODING_STANDARD.md @@ -325,14 +325,15 @@ python/ │ │ ├── mem0/ # Lazy loads from agent-framework-mem0 │ │ └── redis/ # Lazy loads from agent-framework-redis │ │ -│ ├── azure-ai/ # agent-framework-azure-ai +│ ├── foundry/ # agent-framework-foundry │ │ ├── pyproject.toml │ │ ├── tests/ -│ │ └── agent_framework_azure_ai/ +│ │ └── agent_framework_foundry/ │ │ ├── __init__.py # Public exports -│ │ ├── _chat_client.py # AzureAIClient implementation -│ │ ├── _client.py # AzureAIAgentClient implementation -│ │ ├── _shared.py # AzureAISettings and shared utilities +│ │ ├── _chat_client.py # FoundryChatClient implementation +│ │ ├── _agent.py # FoundryAgent implementation +│ │ ├── _embedding_client.py # FoundryEmbeddingClient implementation +│ │ ├── _memory_provider.py # Foundry memory implementation │ │ └── py.typed # PEP 561 marker │ ├── anthropic/ # agent-framework-anthropic │ ├── bedrock/ # agent-framework-bedrock @@ -345,9 +346,9 @@ python/ Provider folders in the core package use `__getattr__` to lazy load classes from their respective connector packages. This allows users to import from a consistent location while only loading dependencies when needed: ```python -# In agent_framework/azure/__init__.py +# In agent_framework/foundry/__init__.py _IMPORTS: dict[str, tuple[str, str]] = { - "AzureAIAgentClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"), + "FoundryChatClient": ("agent_framework_foundry", "agent-framework-foundry"), # ... } @@ -419,7 +420,7 @@ pip install agent-framework-core[all] pip install agent-framework # Install specific connector (pulls in core as dependency) -pip install agent-framework-azure-ai +pip install agent-framework-foundry ``` ## Documentation diff --git a/python/README.md b/python/README.md index d9d65247ab..55af41d399 100644 --- a/python/README.md +++ b/python/README.md @@ -249,7 +249,7 @@ For more advanced orchestration patterns including Sequential, Concurrent, Group - [Getting Started with Agents](samples/02-agents): Basic agent creation and tool usage - [Chat Client Examples](samples/02-agents/chat_client): Direct chat client usage patterns -- [Azure AI Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure-ai): Azure AI integration +- [Foundry Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/foundry): Microsoft Foundry integration - [Workflow Samples](samples/03-workflows): Advanced multi-agent patterns ## Agent Framework Documentation diff --git a/python/packages/azure-ai/AGENTS.md b/python/packages/azure-ai/AGENTS.md deleted file mode 100644 index b8244fd480..0000000000 --- a/python/packages/azure-ai/AGENTS.md +++ /dev/null @@ -1,30 +0,0 @@ -# Azure AI Package (agent-framework-azure-ai) - -Integration with Azure AI inference embeddings plus shared Azure authentication helpers. - -## Main Classes - -- **`AzureAIInferenceEmbeddingClient`** - Full-featured Azure AI inference embeddings client -- **`RawAzureAIInferenceEmbeddingClient`** - Raw embeddings client without middleware layers -- **`AzureAIInferenceEmbeddingOptions`** / **`AzureAIInferenceEmbeddingSettings`** - Embedding options and settings -- **`AzureAISettings`** - Shared Azure AI project settings TypedDict -- **`AzureCredentialTypes`** / **`AzureTokenProvider`** - Shared Azure authentication helpers - -## Usage - -```python -from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient - -client = AzureAIInferenceEmbeddingClient( - endpoint="https://.inference.ai.azure.com", - api_key="...", - model="text-embedding-3-large", -) -result = await client.get_embeddings(["Hello"]) -``` - -## Import Path - -```python -from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient -``` diff --git a/python/packages/azure-ai/LICENSE b/python/packages/azure-ai/LICENSE deleted file mode 100644 index 9e841e7a26..0000000000 --- a/python/packages/azure-ai/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/azure-ai/README.md b/python/packages/azure-ai/README.md deleted file mode 100644 index 34dedd5500..0000000000 --- a/python/packages/azure-ai/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Get Started with Microsoft Agent Framework Azure AI - -Please install this package via pip: - -```bash -pip install agent-framework-azure-ai --pre -``` - -## Foundry Memory Context Provider - -The Foundry Memory context provider enables semantic memory capabilities for your agents using Azure AI Foundry Memory Store. It automatically: -- Retrieves static (user profile) memories on first run -- Searches for contextual memories based on conversation -- Updates the memory store with new conversation messages - -### Basic Usage Example - -See the [Foundry Memory example](../../samples/02-agents/context_providers/azure_ai_foundry_memory.py) which demonstrates: - -- Creating a memory store using Azure AI Projects client -- Setting up an agent with FoundryMemoryProvider -- Teaching the agent user preferences -- Retrieving information using remembered context across conversations -- Automatic memory updates with configurable delays - -and see the [README](https://github.com/microsoft/agent-framework/tree/main/python/README.md) for more information. diff --git a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py deleted file mode 100644 index 5e72be8852..0000000000 --- a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import importlib.metadata - -from ._embedding_client import ( - AzureAIInferenceEmbeddingClient, - AzureAIInferenceEmbeddingOptions, - AzureAIInferenceEmbeddingSettings, - RawAzureAIInferenceEmbeddingClient, -) -from ._entra_id_authentication import AzureCredentialTypes, AzureTokenProvider -from ._shared import AzureAISettings - -try: - __version__ = importlib.metadata.version(__name__) -except importlib.metadata.PackageNotFoundError: - __version__ = "0.0.0" - -__all__ = [ - "AzureAIInferenceEmbeddingClient", - "AzureAIInferenceEmbeddingOptions", - "AzureAIInferenceEmbeddingSettings", - "AzureAISettings", - "AzureCredentialTypes", - "AzureTokenProvider", - "RawAzureAIInferenceEmbeddingClient", - "__version__", -] diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_entra_id_authentication.py b/python/packages/azure-ai/agent_framework_azure_ai/_entra_id_authentication.py deleted file mode 100644 index b1ae8a4739..0000000000 --- a/python/packages/azure-ai/agent_framework_azure_ai/_entra_id_authentication.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -from __future__ import annotations - -import logging -from collections.abc import Awaitable, Callable -from typing import Union - -from agent_framework.exceptions import ChatClientInvalidAuthException -from azure.core.credentials import TokenCredential -from azure.core.credentials_async import AsyncTokenCredential - -logger: logging.Logger = logging.getLogger(__name__) - -AzureTokenProvider = Callable[[], Union[str, Awaitable[str]]] -"""A callable that returns a bearer token string, either synchronously or asynchronously.""" - -AzureCredentialTypes = Union[TokenCredential, AsyncTokenCredential] -"""Union of Azure credential types. - -Accepts: -- ``TokenCredential`` — synchronous Azure credential (e.g. ``DefaultAzureCredential()``) -- ``AsyncTokenCredential`` — asynchronous Azure credential (e.g. ``azure.identity.aio.DefaultAzureCredential()``) -""" - - -def resolve_credential_to_token_provider( - credential: AzureCredentialTypes | AzureTokenProvider, - token_endpoint: str | None, -) -> AzureTokenProvider: - """Convert an Azure credential or token provider into an ``ad_token_provider`` callable. - - If the credential is already a callable token provider, it is returned as-is - (``token_endpoint`` is not required in this case). - If it is a ``TokenCredential`` or ``AsyncTokenCredential``, it is wrapped using - ``azure.identity.get_bearer_token_provider`` (sync or async variant) which - handles token caching and automatic refresh. - - Args: - credential: An Azure credential or token provider callable. - token_endpoint: The token scope/endpoint - (e.g. ``"https://cognitiveservices.azure.com/.default"``). - Required when ``credential`` is a ``TokenCredential`` or ``AsyncTokenCredential``. - - Returns: - A callable that returns a bearer token string (sync or async). - - Raises: - ServiceInvalidAuthError: If the token endpoint is empty when needed for credential wrapping. - """ - # Already a token provider callable (not a credential object) — use directly - if callable(credential) and not isinstance(credential, (TokenCredential, AsyncTokenCredential)): - return credential - - if not token_endpoint: - raise ChatClientInvalidAuthException( - "A token endpoint must be provided either in settings, as an environment variable, or as an argument." - ) - - if isinstance(credential, AsyncTokenCredential): - from azure.identity.aio import get_bearer_token_provider as get_async_bearer_token_provider - - return get_async_bearer_token_provider(credential, token_endpoint) - - from azure.identity import get_bearer_token_provider - - return get_bearer_token_provider(credential, token_endpoint) # type: ignore[arg-type] diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_shared.py b/python/packages/azure-ai/agent_framework_azure_ai/_shared.py deleted file mode 100644 index ce167a1f82..0000000000 --- a/python/packages/azure-ai/agent_framework_azure_ai/_shared.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -from __future__ import annotations - -import sys - -if sys.version_info >= (3, 11): - from typing import TypedDict # pragma: no cover -else: - from typing_extensions import TypedDict # type: ignore # pragma: no cover - - -class AzureAISettings(TypedDict, total=False): - """Azure AI Project settings. - - Settings are resolved in this order: explicit keyword arguments, values from an - explicitly provided .env file, then environment variables with the prefix - 'AZURE_AI_'. If settings are missing after resolution, validation will fail. - - Keyword Args: - project_endpoint: The Azure AI Project endpoint URL. - Can be set via environment variable AZURE_AI_PROJECT_ENDPOINT. - model: The name of the model to use. - Can be set via environment variable AZURE_AI_MODEL. - 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'. - - Examples: - .. code-block:: python - - from agent_framework.azure import AzureAISettings - - # Using environment variables - # Set AZURE_AI_PROJECT_ENDPOINT=https://your-project.cognitiveservices.azure.com - # Set AZURE_AI_MODEL=gpt-4 - settings = AzureAISettings() - - # Or passing parameters directly - settings = AzureAISettings( - project_endpoint="https://your-project.cognitiveservices.azure.com", model="gpt-4" - ) - - # Or loading from a .env file - settings = AzureAISettings(env_file_path="path/to/.env") - """ - - project_endpoint: str | None - model: str | None diff --git a/python/packages/azure-ai/agent_framework_azure_ai/py.typed b/python/packages/azure-ai/agent_framework_azure_ai/py.typed deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python/packages/azure-ai/pyproject.toml b/python/packages/azure-ai/pyproject.toml deleted file mode 100644 index 0a4e0cb66e..0000000000 --- a/python/packages/azure-ai/pyproject.toml +++ /dev/null @@ -1,109 +0,0 @@ -[project] -name = "agent-framework-azure-ai" -description = "Azure AI Foundry integration for Microsoft Agent Framework." -authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}] -readme = "README.md" -requires-python = ">=3.10" -version = "1.0.0rc6" -license-files = ["LICENSE"] -urls.homepage = "https://aka.ms/agent-framework" -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 :: 4 - Beta", - "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", - "Programming Language :: Python :: 3.14", - "Typing :: Typed", -] -dependencies = [ - "agent-framework-core>=1.0.0rc6", - "agent-framework-openai>=1.0.0rc6", - "azure-ai-projects>=2.0.0,<3.0", - "azure-ai-agents>=1.2.0b5,<1.2.0b6", - "azure-ai-inference>=1.0.0b9,<1.0.0b10", - "azure-identity>=1,<2", - "aiohttp>=3.7.0,<4", -] - -[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 -markers = [ - "integration: marks tests as integration tests that require external services", -] - -[tool.ruff] -extend = "../../pyproject.toml" - -[tool.coverage.run] -omit = [ - "**/__init__.py" -] - -[tool.pyright] -extends = "../../pyproject.toml" -include = ["agent_framework_azure_ai"] - -[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_ai"] -exclude_dirs = ["tests"] - -[tool.poe] -executor.type = "uv" -include = "../../shared_tasks.toml" - -[tool.poe.tasks.mypy] -help = "Run MyPy for this package." -cmd = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_azure_ai" - -[tool.poe.tasks.test] -help = "Run the default unit test suite for this package." -cmd = 'pytest -m "not integration" --cov=agent_framework_azure_ai --cov-report=term-missing:skip-covered tests' - -[tool.poe.tasks.integration-tests] -help = "Run the package integration test suite." -cmd = """ -pytest --import-mode=importlib --n logical --dist worksteal -tests -""" - -[build-system] -requires = ["flit-core >= 3.11,<4.0"] -build-backend = "flit_core.buildapi" diff --git a/python/packages/azure-ai/tests/assets/sample_image.jpg b/python/packages/azure-ai/tests/assets/sample_image.jpg deleted file mode 100644 index ea6486656f..0000000000 Binary files a/python/packages/azure-ai/tests/assets/sample_image.jpg and /dev/null differ diff --git a/python/packages/azure-ai/tests/azure_openai/test_entra_id_authentication.py b/python/packages/azure-ai/tests/azure_openai/test_entra_id_authentication.py deleted file mode 100644 index a741459fd6..0000000000 --- a/python/packages/azure-ai/tests/azure_openai/test_entra_id_authentication.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -from unittest.mock import MagicMock, patch - -import pytest -from agent_framework.exceptions import ChatClientInvalidAuthException -from azure.core.credentials import TokenCredential -from azure.core.credentials_async import AsyncTokenCredential - -from agent_framework_azure_ai._entra_id_authentication import ( - resolve_credential_to_token_provider, -) - -TOKEN_ENDPOINT = "https://cognitiveservices.azure.com/.default" - - -def test_resolve_sync_credential_returns_provider() -> None: - """Test that a sync TokenCredential is resolved via azure.identity.get_bearer_token_provider.""" - mock_credential = MagicMock(spec=TokenCredential) - mock_provider = MagicMock(return_value="token-string") - - with patch("azure.identity.get_bearer_token_provider", return_value=mock_provider) as mock_gbtp: - result = resolve_credential_to_token_provider(mock_credential, TOKEN_ENDPOINT) - - mock_gbtp.assert_called_once_with(mock_credential, TOKEN_ENDPOINT) - assert result is mock_provider - - -def test_resolve_async_credential_returns_provider() -> None: - """Test that an AsyncTokenCredential is resolved via azure.identity.aio.get_bearer_token_provider.""" - mock_credential = MagicMock(spec=AsyncTokenCredential) - mock_provider = MagicMock(return_value="token-string") - - with patch("azure.identity.aio.get_bearer_token_provider", return_value=mock_provider) as mock_gbtp: - result = resolve_credential_to_token_provider(mock_credential, TOKEN_ENDPOINT) - - mock_gbtp.assert_called_once_with(mock_credential, TOKEN_ENDPOINT) - assert result is mock_provider - - -def test_resolve_callable_provider_passthrough() -> None: - """Test that a callable token provider is returned as-is, without needing token_endpoint.""" - my_provider = lambda: "my-token" # noqa: E731 - - # Works with token_endpoint - assert resolve_credential_to_token_provider(my_provider, TOKEN_ENDPOINT) is my_provider - - # Also works without token_endpoint - assert resolve_credential_to_token_provider(my_provider, None) is my_provider - assert resolve_credential_to_token_provider(my_provider, "") is my_provider - - -def test_resolve_missing_endpoint_raises() -> None: - """Test that missing token endpoint raises ChatClientInvalidAuthException.""" - mock_credential = MagicMock(spec=TokenCredential) - - with pytest.raises(ChatClientInvalidAuthException, match="A token endpoint must be provided"): - resolve_credential_to_token_provider(mock_credential, "") - - with pytest.raises(ChatClientInvalidAuthException, match="A token endpoint must be provided"): - resolve_credential_to_token_provider(mock_credential, None) # type: ignore[arg-type] diff --git a/python/packages/azure-ai/tests/conftest.py b/python/packages/azure-ai/tests/conftest.py deleted file mode 100644 index 62c09226c4..0000000000 --- a/python/packages/azure-ai/tests/conftest.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. -from typing import Any -from unittest.mock import AsyncMock, MagicMock - -from pytest import fixture - - -@fixture -def exclude_list(request: Any) -> list[str]: - """Fixture that returns a list of environment variables to exclude.""" - return request.param if hasattr(request, "param") else [] - - -@fixture -def override_env_param_dict(request: Any) -> dict[str, str]: - """Fixture that returns a dict of environment variables to override.""" - return request.param if hasattr(request, "param") else {} - - -@fixture() -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 = [] - - if override_env_param_dict is None: - override_env_param_dict = {} - - env_vars = { - "AZURE_AI_PROJECT_ENDPOINT": "https://test-project.cognitiveservices.azure.com/", - "AZURE_AI_MODEL": "test-gpt-4o", - } - - env_vars.update(override_env_param_dict) # type: ignore - - for key, value in env_vars.items(): - if key in exclude_list: - monkeypatch.delenv(key, raising=False) # type: ignore - continue - monkeypatch.setenv(key, value) # type: ignore - - return env_vars - - -@fixture -def mock_agents_client() -> MagicMock: - """Fixture that provides a mock AgentsClient.""" - mock_client = MagicMock() - - # Mock agents property - mock_client.create_agent = AsyncMock() - mock_client.delete_agent = AsyncMock() - - # Mock agent creation response - mock_agent = MagicMock() - mock_agent.id = "test-agent-id" - mock_client.create_agent.return_value = mock_agent - - # Mock threads property - mock_client.threads = MagicMock() - mock_client.threads.create = AsyncMock() - mock_client.messages.create = AsyncMock() - - # Mock runs property - mock_client.runs = MagicMock() - mock_client.runs.list = AsyncMock() - mock_client.runs.cancel = AsyncMock() - mock_client.runs.stream = AsyncMock() - mock_client.runs.submit_tool_outputs_stream = AsyncMock() - - return mock_client - - -@fixture -def mock_azure_credential() -> MagicMock: - """Fixture that provides a mock AsyncTokenCredential.""" - return MagicMock() diff --git a/python/packages/azure-ai/tests/resources/employees.pdf b/python/packages/azure-ai/tests/resources/employees.pdf deleted file mode 100644 index 9590e9d30f..0000000000 --- a/python/packages/azure-ai/tests/resources/employees.pdf +++ /dev/null @@ -1,76 +0,0 @@ -%PDF-1.7 -%���� -1 0 obj -<>/Metadata 132 0 R/ViewerPreferences 133 0 R>> -endobj -2 0 obj -<> -endobj -3 0 obj -<> -endobj -4 0 obj -<>>>/Contents 6 0 R>> -endobj -5 0 obj -<> -endobj -6 0 obj -<> -stream -BT -/F1 12 Tf -50 750 Td -(Employee Directory) Tj -0 -30 Td -(Name: John Smith) Tj -0 -15 Td -(Department: Engineering) Tj -0 -15 Td -(Age: 28) Tj -0 -30 Td -(Name: Alice Johnson) Tj -0 -15 Td -(Department: Sales) Tj -0 -15 Td -(Age: 24) Tj -0 -30 Td -(Name: Bob Wilson) Tj -0 -15 Td -(Department: Marketing) Tj -0 -15 Td -(Age: 35) Tj -ET -endstream -endobj -22 0 obj -<> -endobj -132 0 obj -<> -endobj -133 0 obj -<> -endobj -xref -0 10 -0000000000 65535 f -0000000015 00000 n -0000000152 00000 n -0000000209 00000 n -0000000300 00000 n -0000000420 00000 n -0000000490 00000 n -0000000000 65535 f -0000000000 65535 f -0000000000 65535 f -22 1 -0000000740 00000 n -132 2 -0000000780 00000 n -0000000820 00000 n -trailer -<> -startxref -860 -%%EOF \ No newline at end of file diff --git a/python/packages/azure-cosmos/AGENTS.md b/python/packages/azure-cosmos/AGENTS.md index 7cb0c2c717..9bb7f76da9 100644 --- a/python/packages/azure-cosmos/AGENTS.md +++ b/python/packages/azure-cosmos/AGENTS.md @@ -9,7 +9,7 @@ Azure Cosmos DB history provider integration for Agent Framework. ## Usage ```python -from agent_framework_azure_cosmos import CosmosHistoryProvider +from agent_framework.azure import CosmosHistoryProvider provider = CosmosHistoryProvider( endpoint="https://.documents.azure.com:443/", @@ -24,5 +24,7 @@ Container name is configured on the provider. `session_id` is used as the partit ## Import Path ```python +from agent_framework.azure import CosmosHistoryProvider +# or directly: from agent_framework_azure_cosmos import CosmosHistoryProvider ``` diff --git a/python/packages/azure-cosmos/README.md b/python/packages/azure-cosmos/README.md index 198376bcbb..d2868c78b7 100644 --- a/python/packages/azure-cosmos/README.md +++ b/python/packages/azure-cosmos/README.md @@ -14,7 +14,7 @@ The Azure Cosmos DB integration provides `CosmosHistoryProvider` for persistent ```python from azure.identity.aio import DefaultAzureCredential -from agent_framework_azure_cosmos import CosmosHistoryProvider +from agent_framework.azure import CosmosHistoryProvider provider = CosmosHistoryProvider( endpoint="https://.documents.azure.com:443/", @@ -35,4 +35,13 @@ Container naming behavior: - Container name is configured on the provider (`container_name` or `AZURE_COSMOS_CONTAINER_NAME`) - `session_id` is used as the Cosmos partition key for reads/writes -See `samples/cosmos_history_provider.py` for a runnable package-local example. +See the [conversation samples](../../samples/02-agents/conversations/) for runnable examples, including +[`cosmos_history_provider.py`](../../samples/02-agents/conversations/cosmos_history_provider.py). + +## Import Paths + +```python +from agent_framework.azure import CosmosHistoryProvider +# or directly: +from agent_framework_azure_cosmos import CosmosHistoryProvider +``` diff --git a/python/packages/azure-cosmos/samples/README.md b/python/packages/azure-cosmos/samples/README.md deleted file mode 100644 index e3714139e4..0000000000 --- a/python/packages/azure-cosmos/samples/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Azure Cosmos DB Package Samples - -This folder contains samples for `agent-framework-azure-cosmos`. - -| File | Description | -| --- | --- | -| [`cosmos_history_provider.py`](cosmos_history_provider.py) | Demonstrates an Agent using `CosmosHistoryProvider` with `FoundryChatClient` (configured against an Azure AI Foundry project endpoint), provider-configured container name, and `session_id` partitioning. | - -## Prerequisites - -- `AZURE_COSMOS_ENDPOINT` -- `AZURE_COSMOS_DATABASE_NAME` -- `AZURE_COSMOS_CONTAINER_NAME` -- `AZURE_COSMOS_KEY` (or equivalent credential flow) - -## Run - -```bash -uv run --directory packages/azure-cosmos python samples/cosmos_history_provider.py -``` diff --git a/python/packages/azure-cosmos/samples/__init__.py b/python/packages/azure-cosmos/samples/__init__.py deleted file mode 100644 index 516b9492f6..0000000000 --- a/python/packages/azure-cosmos/samples/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -"""Samples for the Azure Cosmos history provider package.""" diff --git a/python/packages/core/README.md b/python/packages/core/README.md index d817b3b3be..86b6f44a72 100644 --- a/python/packages/core/README.md +++ b/python/packages/core/README.md @@ -34,8 +34,8 @@ FOUNDRY_PROJECT_ENDPOINT=... FOUNDRY_MODEL=... ... OPENAI_API_KEY=sk-... +OPENAI_CHAT_COMPLETION_MODEL=... OPENAI_CHAT_MODEL=... -OPENAI_RESPONSES_MODEL=... ... AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=... diff --git a/python/packages/core/agent_framework/azure/__init__.py b/python/packages/core/agent_framework/azure/__init__.py index 27a9dc7e3a..7cff0150f1 100644 --- a/python/packages/core/agent_framework/azure/__init__.py +++ b/python/packages/core/agent_framework/azure/__init__.py @@ -14,13 +14,7 @@ _IMPORTS: dict[str, tuple[str, str]] = { "AgentResponseCallbackProtocol": ("agent_framework_durabletask", "agent-framework-durabletask"), "AzureAISearchContextProvider": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"), "AzureAISearchSettings": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"), - "AzureAISettings": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "AzureAIInferenceEmbeddingClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "AzureAIInferenceEmbeddingOptions": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "AzureAIInferenceEmbeddingSettings": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "RawAzureAIInferenceEmbeddingClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "AzureCredentialTypes": ("agent_framework_azure_ai", "agent-framework-azure-ai"), - "AzureTokenProvider": ("agent_framework_azure_ai", "agent-framework-azure-ai"), + "CosmosHistoryProvider": ("agent_framework_azure_cosmos", "agent-framework-azure-cosmos"), "DurableAIAgent": ("agent_framework_durabletask", "agent-framework-durabletask"), "DurableAIAgentClient": ("agent_framework_durabletask", "agent-framework-durabletask"), "DurableAIAgentOrchestrationContext": ("agent_framework_durabletask", "agent-framework-durabletask"), diff --git a/python/packages/core/agent_framework/azure/__init__.pyi b/python/packages/core/agent_framework/azure/__init__.pyi index 52d32bb146..12527b6db3 100644 --- a/python/packages/core/agent_framework/azure/__init__.pyi +++ b/python/packages/core/agent_framework/azure/__init__.pyi @@ -3,19 +3,11 @@ # Type stubs for the agent_framework.azure lazy-loading namespace. # Install the relevant packages for full type support. -from agent_framework_azure_ai import ( - AzureAIInferenceEmbeddingClient, - AzureAIInferenceEmbeddingOptions, - AzureAIInferenceEmbeddingSettings, - AzureAISettings, - AzureCredentialTypes, - AzureTokenProvider, - RawAzureAIInferenceEmbeddingClient, -) from agent_framework_azure_ai_search import ( AzureAISearchContextProvider, AzureAISearchSettings, ) +from agent_framework_azure_cosmos import CosmosHistoryProvider from agent_framework_azurefunctions import AgentFunctionApp from agent_framework_durabletask import ( AgentCallbackContext, @@ -30,17 +22,11 @@ __all__ = [ "AgentCallbackContext", "AgentFunctionApp", "AgentResponseCallbackProtocol", - "AzureAIInferenceEmbeddingClient", - "AzureAIInferenceEmbeddingOptions", - "AzureAIInferenceEmbeddingSettings", "AzureAISearchContextProvider", "AzureAISearchSettings", - "AzureAISettings", - "AzureCredentialTypes", - "AzureTokenProvider", + "CosmosHistoryProvider", "DurableAIAgent", "DurableAIAgentClient", "DurableAIAgentOrchestrationContext", "DurableAIAgentWorker", - "RawAzureAIInferenceEmbeddingClient", ] diff --git a/python/packages/core/agent_framework/foundry/__init__.py b/python/packages/core/agent_framework/foundry/__init__.py index 6e922f4b3a..b1d2b88450 100644 --- a/python/packages/core/agent_framework/foundry/__init__.py +++ b/python/packages/core/agent_framework/foundry/__init__.py @@ -16,6 +16,9 @@ _IMPORTS: dict[str, tuple[str, str]] = { "FoundryAgent": ("agent_framework_foundry", "agent-framework-foundry"), "FoundryChatClient": ("agent_framework_foundry", "agent-framework-foundry"), "FoundryChatOptions": ("agent_framework_foundry", "agent-framework-foundry"), + "FoundryEmbeddingClient": ("agent_framework_foundry", "agent-framework-foundry"), + "FoundryEmbeddingOptions": ("agent_framework_foundry", "agent-framework-foundry"), + "FoundryEmbeddingSettings": ("agent_framework_foundry", "agent-framework-foundry"), "FoundryEvals": ("agent_framework_foundry", "agent-framework-foundry"), "FoundryMemoryProvider": ("agent_framework_foundry", "agent-framework-foundry"), "FoundryLocalChatOptions": ("agent_framework_foundry_local", "agent-framework-foundry-local"), @@ -25,6 +28,7 @@ _IMPORTS: dict[str, tuple[str, str]] = { "RawFoundryAgent": ("agent_framework_foundry", "agent-framework-foundry"), "RawFoundryAgentChatClient": ("agent_framework_foundry", "agent-framework-foundry"), "RawFoundryChatClient": ("agent_framework_foundry", "agent-framework-foundry"), + "RawFoundryEmbeddingClient": ("agent_framework_foundry", "agent-framework-foundry"), "evaluate_foundry_target": ("agent_framework_foundry", "agent-framework-foundry"), "evaluate_traces": ("agent_framework_foundry", "agent-framework-foundry"), } diff --git a/python/packages/core/agent_framework/foundry/__init__.pyi b/python/packages/core/agent_framework/foundry/__init__.pyi index 0d37d3a7d2..47eb92b3af 100644 --- a/python/packages/core/agent_framework/foundry/__init__.pyi +++ b/python/packages/core/agent_framework/foundry/__init__.pyi @@ -8,11 +8,15 @@ from agent_framework_foundry import ( FoundryAgent, FoundryChatClient, FoundryChatOptions, + FoundryEmbeddingClient, + FoundryEmbeddingOptions, + FoundryEmbeddingSettings, FoundryEvals, FoundryMemoryProvider, RawFoundryAgent, RawFoundryAgentChatClient, RawFoundryChatClient, + RawFoundryEmbeddingClient, evaluate_foundry_target, evaluate_traces, ) @@ -27,6 +31,9 @@ __all__ = [ "FoundryAgent", "FoundryChatClient", "FoundryChatOptions", + "FoundryEmbeddingClient", + "FoundryEmbeddingOptions", + "FoundryEmbeddingSettings", "FoundryEvals", "FoundryLocalChatOptions", "FoundryLocalClient", @@ -36,6 +43,7 @@ __all__ = [ "RawFoundryAgent", "RawFoundryAgentChatClient", "RawFoundryChatClient", + "RawFoundryEmbeddingClient", "evaluate_foundry_target", "evaluate_traces", ] diff --git a/python/packages/core/agent_framework/microsoft/__init__.py b/python/packages/core/agent_framework/microsoft/__init__.py index cf61e518d9..c7239a0c3b 100644 --- a/python/packages/core/agent_framework/microsoft/__init__.py +++ b/python/packages/core/agent_framework/microsoft/__init__.py @@ -5,7 +5,6 @@ This module lazily re-exports objects from: - ``agent-framework-copilotstudio`` - ``agent-framework-purview`` -- ``agent-framework-foundry-local`` Supported classes: - CopilotStudioAgent @@ -20,9 +19,6 @@ Supported classes: - PurviewRequestError - PurviewServiceError - CacheProvider -- FoundryLocalChatOptions -- FoundryLocalClient -- FoundryLocalSettings """ @@ -43,9 +39,6 @@ _IMPORTS: dict[str, tuple[str, str]] = { "PurviewRequestError": ("agent_framework_purview", "agent-framework-purview"), "PurviewServiceError": ("agent_framework_purview", "agent-framework-purview"), "CacheProvider": ("agent_framework_purview", "agent-framework-purview"), - "FoundryLocalChatOptions": ("agent_framework_foundry_local", "agent-framework-foundry-local"), - "FoundryLocalClient": ("agent_framework_foundry_local", "agent-framework-foundry-local"), - "FoundryLocalSettings": ("agent_framework_foundry_local", "agent-framework-foundry-local"), } diff --git a/python/packages/core/agent_framework/microsoft/__init__.pyi b/python/packages/core/agent_framework/microsoft/__init__.pyi index be3abe523c..e70502ad3a 100644 --- a/python/packages/core/agent_framework/microsoft/__init__.pyi +++ b/python/packages/core/agent_framework/microsoft/__init__.pyi @@ -4,11 +4,6 @@ from agent_framework_copilotstudio import ( CopilotStudioAgent, acquire_token, ) -from agent_framework_foundry_local import ( - FoundryLocalChatOptions, - FoundryLocalClient, - FoundryLocalSettings, -) from agent_framework_purview import ( CacheProvider, PurviewAppLocation, @@ -26,9 +21,6 @@ from agent_framework_purview import ( __all__ = [ "CacheProvider", "CopilotStudioAgent", - "FoundryLocalChatOptions", - "FoundryLocalClient", - "FoundryLocalSettings", "PurviewAppLocation", "PurviewAuthenticationError", "PurviewChatPolicyMiddleware", diff --git a/python/packages/core/pyproject.toml b/python/packages/core/pyproject.toml index f002567381..a9f011435c 100644 --- a/python/packages/core/pyproject.toml +++ b/python/packages/core/pyproject.toml @@ -35,10 +35,10 @@ all = [ "agent-framework-a2a", "agent-framework-ag-ui", "agent-framework-azure-ai-search", + "agent-framework-azure-cosmos", "agent-framework-anthropic", "agent-framework-openai", "agent-framework-claude", - "agent-framework-azure-ai", "agent-framework-azurefunctions", "agent-framework-bedrock", "agent-framework-chatkit", diff --git a/python/packages/core/tests/core/test_azure_namespace.py b/python/packages/core/tests/core/test_azure_namespace.py new file mode 100644 index 0000000000..9960b77ac9 --- /dev/null +++ b/python/packages/core/tests/core/test_azure_namespace.py @@ -0,0 +1,10 @@ +# Copyright (c) Microsoft. All rights reserved. + +from agent_framework_azure_cosmos import CosmosHistoryProvider + +import agent_framework.azure as azure + + +def test_azure_namespace_exposes_cosmos_history_provider() -> None: + assert azure.CosmosHistoryProvider is CosmosHistoryProvider + assert "CosmosHistoryProvider" in dir(azure) diff --git a/python/packages/devui/agent_framework_devui/ui/assets/index.js b/python/packages/devui/agent_framework_devui/ui/assets/index.js index d4721e6db5..fe3c02b6ee 100644 --- a/python/packages/devui/agent_framework_devui/ui/assets/index.js +++ b/python/packages/devui/agent_framework_devui/ui/assets/index.js @@ -481,7 +481,7 @@ services: environment: # OpenAI - OPENAI_API_KEY=\${OPENAI_API_KEY} - - OPENAI_CHAT_MODEL=\${OPENAI_CHAT_MODEL:-gpt-4o-mini} + - OPENAI_CHAT_COMPLETION_MODEL=\${OPENAI_CHAT_COMPLETION_MODEL:-gpt-4o-mini} # Or Azure OpenAI - AZURE_OPENAI_API_KEY=\${AZURE_OPENAI_API_KEY} - AZURE_OPENAI_ENDPOINT=\${AZURE_OPENAI_ENDPOINT} @@ -514,7 +514,7 @@ az acr build --registry myregistry \\ --target-port 8080 \\ --ingress 'external' \\ --registry-server myregistry.azurecr.io \\ - --env-vars OPENAI_API_KEY=secretref:openai-key OPENAI_CHAT_MODEL=gpt-4o-mini`})] + --env-vars OPENAI_API_KEY=secretref:openai-key OPENAI_CHAT_COMPLETION_MODEL=gpt-4o-mini`})] }), o.jsxs("div", { className: "border-l-2 border-primary pl-3", children: [o.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [o.jsx("div", { className: "w-5 h-5 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-xs font-bold", children: "5" }), o.jsx("h5", { className: "font-medium text-sm", children: "Get Application URL" })] }), o.jsx("pre", { className: "bg-muted p-2 rounded text-xs overflow-x-auto border mt-2", children: `az containerapp show --name ${r.toLowerCase()}-app \\ diff --git a/python/packages/devui/dev.md b/python/packages/devui/dev.md index 7c5e544c8e..d537c22ca7 100644 --- a/python/packages/devui/dev.md +++ b/python/packages/devui/dev.md @@ -33,7 +33,7 @@ Then edit `.env` and add your API keys: ```bash # For OpenAI (minimum required) OPENAI_API_KEY="your-api-key-here" -OPENAI_CHAT_MODEL="gpt-4o-mini" +OPENAI_CHAT_COMPLETION_MODEL="gpt-4o-mini" # Or for Azure OpenAI AZURE_OPENAI_ENDPOINT="your-endpoint" diff --git a/python/packages/devui/frontend/src/components/layout/deployment-modal.tsx b/python/packages/devui/frontend/src/components/layout/deployment-modal.tsx index ae1eddc235..14c684bec2 100644 --- a/python/packages/devui/frontend/src/components/layout/deployment-modal.tsx +++ b/python/packages/devui/frontend/src/components/layout/deployment-modal.tsx @@ -243,7 +243,7 @@ services: environment: # OpenAI - OPENAI_API_KEY=\${OPENAI_API_KEY} - - OPENAI_CHAT_MODEL=\${OPENAI_CHAT_MODEL:-gpt-4o-mini} + - OPENAI_CHAT_COMPLETION_MODEL=\${OPENAI_CHAT_COMPLETION_MODEL:-gpt-4o-mini} # Or Azure OpenAI - AZURE_OPENAI_API_KEY=\${AZURE_OPENAI_API_KEY} - AZURE_OPENAI_ENDPOINT=\${AZURE_OPENAI_ENDPOINT} @@ -802,7 +802,7 @@ az acr build --registry myregistry \\ --target-port 8080 \\ --ingress 'external' \\ --registry-server myregistry.azurecr.io \\ - --env-vars OPENAI_API_KEY=secretref:openai-key OPENAI_CHAT_MODEL=gpt-4o-mini`} + --env-vars OPENAI_API_KEY=secretref:openai-key OPENAI_CHAT_COMPLETION_MODEL=gpt-4o-mini`} diff --git a/python/packages/foundry/README.md b/python/packages/foundry/README.md index abaa0cb9c3..e22fb523a5 100644 --- a/python/packages/foundry/README.md +++ b/python/packages/foundry/README.md @@ -1,3 +1,3 @@ # Agent Framework Foundry -This package contains the cloud Azure AI Foundry integrations for Microsoft Agent Framework, including Foundry chat clients, preconfigured Foundry agents, and Foundry memory providers. +This package contains the Microsoft Foundry integrations for Microsoft Agent Framework, including Foundry chat clients, preconfigured Foundry agents, Foundry embedding clients, and Foundry memory providers. diff --git a/python/packages/foundry/agent_framework_foundry/__init__.py b/python/packages/foundry/agent_framework_foundry/__init__.py index a67b5df801..fbd1376735 100644 --- a/python/packages/foundry/agent_framework_foundry/__init__.py +++ b/python/packages/foundry/agent_framework_foundry/__init__.py @@ -4,6 +4,12 @@ import importlib.metadata from ._agent import FoundryAgent, RawFoundryAgent, RawFoundryAgentChatClient from ._chat_client import FoundryChatClient, FoundryChatOptions, RawFoundryChatClient +from ._embedding_client import ( + FoundryEmbeddingClient, + FoundryEmbeddingOptions, + FoundryEmbeddingSettings, + RawFoundryEmbeddingClient, +) from ._foundry_evals import ( FoundryEvals, evaluate_foundry_target, @@ -20,11 +26,15 @@ __all__ = [ "FoundryAgent", "FoundryChatClient", "FoundryChatOptions", + "FoundryEmbeddingClient", + "FoundryEmbeddingOptions", + "FoundryEmbeddingSettings", "FoundryEvals", "FoundryMemoryProvider", "RawFoundryAgent", "RawFoundryAgentChatClient", "RawFoundryChatClient", + "RawFoundryEmbeddingClient", "__version__", "evaluate_foundry_target", "evaluate_traces", diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py b/python/packages/foundry/agent_framework_foundry/_embedding_client.py similarity index 79% rename from python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py rename to python/packages/foundry/agent_framework_foundry/_embedding_client.py index ee8709d410..2e63bf7f56 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_embedding_client.py +++ b/python/packages/foundry/agent_framework_foundry/_embedding_client.py @@ -28,22 +28,22 @@ else: from typing_extensions import TypeVar # type: ignore # pragma: no cover -logger = logging.getLogger("agent_framework.azure_ai") +logger = logging.getLogger("agent_framework.foundry") _IMAGE_MEDIA_PREFIXES = ("image/",) -class AzureAIInferenceEmbeddingOptions(EmbeddingGenerationOptions, total=False): - """Azure AI Inference-specific embedding options. +class FoundryEmbeddingOptions(EmbeddingGenerationOptions, total=False): + """Foundry inference-specific embedding options. - Extends EmbeddingGenerationOptions with Azure AI Inference-specific fields. + Extends ``EmbeddingGenerationOptions`` with Foundry inference-specific fields. Examples: .. code-block:: python - from agent_framework_azure_ai import AzureAIInferenceEmbeddingOptions + from agent_framework_foundry import FoundryEmbeddingOptions - options: AzureAIInferenceEmbeddingOptions = { + options: FoundryEmbeddingOptions = { "model": "text-embedding-3-small", "dimensions": 1536, "input_type": "document", @@ -68,28 +68,28 @@ class AzureAIInferenceEmbeddingOptions(EmbeddingGenerationOptions, total=False): """Additional model-specific parameters passed directly to the API.""" -AzureAIInferenceEmbeddingOptionsT = TypeVar( - "AzureAIInferenceEmbeddingOptionsT", +FoundryEmbeddingOptionsT = TypeVar( + "FoundryEmbeddingOptionsT", bound=TypedDict, # type: ignore[valid-type] - default="AzureAIInferenceEmbeddingOptions", + default="FoundryEmbeddingOptions", covariant=True, ) -class AzureAIInferenceEmbeddingSettings(TypedDict, total=False): - """Azure AI Inference embedding settings.""" +class FoundryEmbeddingSettings(TypedDict, total=False): + """Foundry inference embedding settings.""" - endpoint: str | None - api_key: str | None + models_endpoint: str | None + models_api_key: str | None embedding_model: str | None image_embedding_model: str | None -class RawAzureAIInferenceEmbeddingClient( - BaseEmbeddingClient[Content | str, list[float], AzureAIInferenceEmbeddingOptionsT], - Generic[AzureAIInferenceEmbeddingOptionsT], +class RawFoundryEmbeddingClient( + BaseEmbeddingClient[Content | str, list[float], FoundryEmbeddingOptionsT], + Generic[FoundryEmbeddingOptionsT], ): - """Raw Azure AI Inference embedding client without telemetry. + """Raw Foundry embedding client without telemetry. Accepts both text (``str``) and image (``Content``) inputs. Text and image inputs within a single batch are separated and dispatched to @@ -98,14 +98,14 @@ class RawAzureAIInferenceEmbeddingClient( Keyword Args: model: The text embedding model (e.g. "text-embedding-3-small"). - Can also be set via environment variable AZURE_AI_INFERENCE_EMBEDDING_MODEL. + Can also be set via environment variable FOUNDRY_EMBEDDING_MODEL. image_model: The image embedding model (e.g. "Cohere-embed-v3-english"). - Can also be set via environment variable AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL. + Can also be set via environment variable FOUNDRY_IMAGE_EMBEDDING_MODEL. Falls back to ``model`` if not provided. - endpoint: The Azure AI Inference endpoint URL. - Can also be set via environment variable AZURE_AI_INFERENCE_ENDPOINT. + endpoint: The Foundry inference endpoint URL. + Can also be set via environment variable FOUNDRY_MODELS_ENDPOINT. api_key: API key for authentication. - Can also be set via environment variable AZURE_AI_INFERENCE_API_KEY. + Can also be set via environment variable FOUNDRY_MODELS_API_KEY. text_client: Optional pre-configured ``EmbeddingsClient``. image_client: Optional pre-configured ``ImageEmbeddingsClient``. credential: Optional ``AzureKeyCredential`` or token credential. If not provided, @@ -128,13 +128,13 @@ class RawAzureAIInferenceEmbeddingClient( env_file_path: str | None = None, env_file_encoding: str | None = None, ) -> None: - """Initialize a raw Azure AI Inference embedding client.""" + """Initialize a raw Foundry embedding client.""" settings = load_settings( - AzureAIInferenceEmbeddingSettings, - env_prefix="AZURE_AI_INFERENCE_", - required_fields=["endpoint", "embedding_model"], - endpoint=endpoint, - api_key=api_key, + FoundryEmbeddingSettings, + env_prefix="FOUNDRY_", + required_fields=["models_endpoint", "embedding_model"], + models_endpoint=endpoint, + models_api_key=api_key, embedding_model=model, image_embedding_model=image_model, env_file_path=env_file_path, @@ -143,10 +143,10 @@ class RawAzureAIInferenceEmbeddingClient( self.model = settings["embedding_model"] # type: ignore[reportTypedDictNotRequiredAccess] self.image_model: str = settings.get("image_embedding_model") or self.model # type: ignore[assignment] - resolved_endpoint = settings["endpoint"] # type: ignore[reportTypedDictNotRequiredAccess] + resolved_endpoint = settings["models_endpoint"] # type: ignore[reportTypedDictNotRequiredAccess] - if credential is None and settings.get("api_key"): - credential = AzureKeyCredential(settings["api_key"]) # type: ignore[arg-type] + if credential is None and settings.get("models_api_key"): + credential = AzureKeyCredential(settings["models_api_key"]) # type: ignore[arg-type] if credential is None and text_client is None and image_client is None: raise ValueError("Either 'api_key', 'credential', or pre-configured client(s) must be provided.") @@ -169,7 +169,7 @@ class RawAzureAIInferenceEmbeddingClient( with suppress(Exception): await self._image_client.close() - async def __aenter__(self) -> RawAzureAIInferenceEmbeddingClient[AzureAIInferenceEmbeddingOptionsT]: + async def __aenter__(self) -> RawFoundryEmbeddingClient[FoundryEmbeddingOptionsT]: """Enter the async context manager.""" return self @@ -185,8 +185,8 @@ class RawAzureAIInferenceEmbeddingClient( self, values: Sequence[Content | str], *, - options: AzureAIInferenceEmbeddingOptionsT | None = None, - ) -> GeneratedEmbeddings[list[float], AzureAIInferenceEmbeddingOptionsT]: + options: FoundryEmbeddingOptionsT | None = None, + ) -> GeneratedEmbeddings[list[float], FoundryEmbeddingOptionsT]: """Generate embeddings for text and/or image inputs. Text inputs (``str`` or ``Content`` with ``type="text"``) are sent to the @@ -310,12 +310,12 @@ class RawAzureAIInferenceEmbeddingClient( ) # type: ignore[reportReturnType] -class AzureAIInferenceEmbeddingClient( - EmbeddingTelemetryLayer[Content | str, list[float], AzureAIInferenceEmbeddingOptionsT], - RawAzureAIInferenceEmbeddingClient[AzureAIInferenceEmbeddingOptionsT], - Generic[AzureAIInferenceEmbeddingOptionsT], +class FoundryEmbeddingClient( + EmbeddingTelemetryLayer[Content | str, list[float], FoundryEmbeddingOptionsT], + RawFoundryEmbeddingClient[FoundryEmbeddingOptionsT], + Generic[FoundryEmbeddingOptionsT], ): - """Azure AI Inference embedding client with telemetry support. + """Foundry embedding client with telemetry support. Supports both text and image inputs in a single client. Pass plain strings or ``Content`` instances created with ``Content.from_text()`` or @@ -323,14 +323,14 @@ class AzureAIInferenceEmbeddingClient( Keyword Args: model: The text embedding model (e.g. "text-embedding-3-small"). - Can also be set via environment variable AZURE_AI_INFERENCE_EMBEDDING_MODEL. + Can also be set via environment variable FOUNDRY_EMBEDDING_MODEL. image_model: The image embedding model (e.g. "Cohere-embed-v3-english"). Can also be set via environment variable - AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL. Falls back to ``model``. - endpoint: The Azure AI Inference endpoint URL. - Can also be set via environment variable AZURE_AI_INFERENCE_ENDPOINT. + FOUNDRY_IMAGE_EMBEDDING_MODEL. Falls back to ``model``. + endpoint: The Foundry inference endpoint URL. + Can also be set via environment variable FOUNDRY_MODELS_ENDPOINT. api_key: API key for authentication. - Can also be set via environment variable AZURE_AI_INFERENCE_API_KEY. + Can also be set via environment variable FOUNDRY_MODELS_API_KEY. text_client: Optional pre-configured ``EmbeddingsClient``. image_client: Optional pre-configured ``ImageEmbeddingsClient``. credential: Optional ``AzureKeyCredential`` or token credential. @@ -341,14 +341,14 @@ class AzureAIInferenceEmbeddingClient( Examples: .. code-block:: python - from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient + from agent_framework_foundry import FoundryEmbeddingClient # Using environment variables - # Set AZURE_AI_INFERENCE_ENDPOINT=https://your-endpoint.inference.ai.azure.com - # Set AZURE_AI_INFERENCE_API_KEY=your-key - # Set AZURE_AI_INFERENCE_EMBEDDING_MODEL=text-embedding-3-small - # Set AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL=Cohere-embed-v3-english - client = AzureAIInferenceEmbeddingClient() + # Set FOUNDRY_MODELS_ENDPOINT=https://your-endpoint.inference.ai.azure.com + # Set FOUNDRY_MODELS_API_KEY=your-key + # Set FOUNDRY_EMBEDDING_MODEL=text-embedding-3-small + # Set FOUNDRY_IMAGE_EMBEDDING_MODEL=Cohere-embed-v3-english + client = FoundryEmbeddingClient() # Text embeddings result = await client.get_embeddings(["Hello, world!"]) @@ -380,7 +380,7 @@ class AzureAIInferenceEmbeddingClient( env_file_path: str | None = None, env_file_encoding: str | None = None, ) -> None: - """Initialize an Azure AI Inference embedding client.""" + """Initialize a Foundry embedding client.""" super().__init__( model=model, image_model=image_model, diff --git a/python/packages/foundry/pyproject.toml b/python/packages/foundry/pyproject.toml index a81e199693..752a86b993 100644 --- a/python/packages/foundry/pyproject.toml +++ b/python/packages/foundry/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ dependencies = [ "agent-framework-core>=1.0.0rc6", "agent-framework-openai>=1.0.0rc6", + "azure-ai-inference>=1.0.0b9,<1.0.0b10", "azure-ai-projects>=2.0.0,<3.0", ] diff --git a/python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py b/python/packages/foundry/tests/foundry/test_foundry_embedding_client.py similarity index 70% rename from python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py rename to python/packages/foundry/tests/foundry/test_foundry_embedding_client.py index 812c2eaf29..e9e342d675 100644 --- a/python/packages/azure-ai/tests/azure_ai/test_azure_ai_inference_embedding_client.py +++ b/python/packages/foundry/tests/foundry/test_foundry_embedding_client.py @@ -10,10 +10,10 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest from agent_framework import Content -from agent_framework_azure_ai import ( - AzureAIInferenceEmbeddingClient, - AzureAIInferenceEmbeddingOptions, - RawAzureAIInferenceEmbeddingClient, +from agent_framework_foundry import ( + FoundryEmbeddingClient, + FoundryEmbeddingOptions, + RawFoundryEmbeddingClient, ) @@ -57,9 +57,9 @@ def mock_image_client() -> AsyncMock: @pytest.fixture -def raw_client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> RawAzureAIInferenceEmbeddingClient[Any]: - """Create a RawAzureAIInferenceEmbeddingClient with mocked SDK clients.""" - return RawAzureAIInferenceEmbeddingClient( +def raw_client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> RawFoundryEmbeddingClient[Any]: + """Create a RawFoundryEmbeddingClient with mocked SDK clients.""" + return RawFoundryEmbeddingClient( model="test-model", endpoint="https://test.inference.ai.azure.com", api_key="test-key", @@ -69,9 +69,9 @@ def raw_client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> Raw @pytest.fixture -def client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> AzureAIInferenceEmbeddingClient[Any]: - """Create an AzureAIInferenceEmbeddingClient with mocked SDK clients.""" - return AzureAIInferenceEmbeddingClient( +def client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> FoundryEmbeddingClient[Any]: + """Create a FoundryEmbeddingClient with mocked SDK clients.""" + return FoundryEmbeddingClient( model="test-model", endpoint="https://test.inference.ai.azure.com", api_key="test-key", @@ -80,11 +80,11 @@ def client(mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> AzureAI ) -class TestRawAzureAIInferenceEmbeddingClient: - """Tests for the raw Azure AI Inference embedding client.""" +class TestRawFoundryEmbeddingClient: + """Tests for the raw Foundry embedding client.""" async def test_text_embeddings( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock ) -> None: """Text inputs are dispatched to the text client.""" result = await raw_client.get_embeddings(["hello", "world"]) @@ -94,7 +94,7 @@ class TestRawAzureAIInferenceEmbeddingClient: assert call_kwargs.kwargs["model"] == "test-model" async def test_text_content_embeddings( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock ) -> None: """Content.from_text() inputs are dispatched to the text client.""" text_content = Content.from_text("hello") @@ -105,7 +105,7 @@ class TestRawAzureAIInferenceEmbeddingClient: assert call_kwargs.kwargs["input"] == ["hello"] async def test_image_content_embeddings( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_image_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_image_client: AsyncMock ) -> None: """Image Content inputs are dispatched to the image client.""" image_content = Content.from_data(data=b"\x89PNG", media_type="image/png") @@ -119,7 +119,7 @@ class TestRawAzureAIInferenceEmbeddingClient: async def test_mixed_text_and_image( self, - raw_client: RawAzureAIInferenceEmbeddingClient[Any], + raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock, mock_image_client: AsyncMock, ) -> None: @@ -138,16 +138,16 @@ class TestRawAzureAIInferenceEmbeddingClient: image_call = mock_image_client.embed.call_args assert len(image_call.kwargs["input"]) == 1 - async def test_empty_input(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + async def test_empty_input(self, raw_client: RawFoundryEmbeddingClient[Any]) -> None: """Empty input returns empty result.""" result = await raw_client.get_embeddings([]) assert len(result) == 0 async def test_options_passed_through( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock ) -> None: """Options are passed through to the SDK.""" - options: AzureAIInferenceEmbeddingOptions = { + options: FoundryEmbeddingOptions = { "dimensions": 512, "input_type": "document", "encoding_format": "float", @@ -160,23 +160,23 @@ class TestRawAzureAIInferenceEmbeddingClient: assert call_kwargs.kwargs["encoding_format"] == "float" async def test_model_override_in_options( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock ) -> None: """model in options overrides the default.""" - options: AzureAIInferenceEmbeddingOptions = {"model": "custom-model"} + options: FoundryEmbeddingOptions = {"model": "custom-model"} await raw_client.get_embeddings(["hello"], options=options) call_kwargs = mock_text_client.embed.call_args assert call_kwargs.kwargs["model"] == "custom-model" - async def test_unsupported_content_type_raises(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + async def test_unsupported_content_type_raises(self, raw_client: RawFoundryEmbeddingClient[Any]) -> None: """Non-text, non-image Content raises ValueError.""" error_content = Content("error", message="fail") with pytest.raises(ValueError, match="Unsupported Content type"): await raw_client.get_embeddings([error_content]) async def test_usage_metadata( - self, raw_client: RawAzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock + self, raw_client: RawFoundryEmbeddingClient[Any], mock_text_client: AsyncMock ) -> None: """Usage metadata is populated from the response.""" mock_text_client.embed.return_value = _make_embed_response([[0.1, 0.2]], prompt_tokens=42) @@ -184,7 +184,7 @@ class TestRawAzureAIInferenceEmbeddingClient: assert result.usage is not None assert result.usage["input_token_count"] == 42 - def test_service_url(self, raw_client: RawAzureAIInferenceEmbeddingClient[Any]) -> None: + def test_service_url(self, raw_client: RawFoundryEmbeddingClient[Any]) -> None: """service_url returns the configured endpoint.""" assert raw_client.service_url() == "https://test.inference.ai.azure.com" @@ -194,15 +194,15 @@ class TestRawAzureAIInferenceEmbeddingClient: patch.dict( os.environ, { - "AZURE_AI_INFERENCE_ENDPOINT": "https://env.inference.ai.azure.com", - "AZURE_AI_INFERENCE_API_KEY": "env-key", - "AZURE_AI_INFERENCE_EMBEDDING_MODEL": "env-model", + "FOUNDRY_MODELS_ENDPOINT": "https://env.inference.ai.azure.com", + "FOUNDRY_MODELS_API_KEY": "env-key", + "FOUNDRY_EMBEDDING_MODEL": "env-model", }, ), - patch("agent_framework_azure_ai._embedding_client.EmbeddingsClient"), - patch("agent_framework_azure_ai._embedding_client.ImageEmbeddingsClient"), + patch("agent_framework_foundry._embedding_client.EmbeddingsClient"), + patch("agent_framework_foundry._embedding_client.ImageEmbeddingsClient"), ): - client = RawAzureAIInferenceEmbeddingClient() + client = RawFoundryEmbeddingClient() assert client.model == "env-model" assert client.image_model == "env-model" # falls back to model @@ -212,22 +212,22 @@ class TestRawAzureAIInferenceEmbeddingClient: patch.dict( os.environ, { - "AZURE_AI_INFERENCE_ENDPOINT": "https://env.inference.ai.azure.com", - "AZURE_AI_INFERENCE_API_KEY": "env-key", - "AZURE_AI_INFERENCE_EMBEDDING_MODEL": "text-model", - "AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL": "image-model", + "FOUNDRY_MODELS_ENDPOINT": "https://env.inference.ai.azure.com", + "FOUNDRY_MODELS_API_KEY": "env-key", + "FOUNDRY_EMBEDDING_MODEL": "text-model", + "FOUNDRY_IMAGE_EMBEDDING_MODEL": "image-model", }, ), - patch("agent_framework_azure_ai._embedding_client.EmbeddingsClient"), - patch("agent_framework_azure_ai._embedding_client.ImageEmbeddingsClient"), + patch("agent_framework_foundry._embedding_client.EmbeddingsClient"), + patch("agent_framework_foundry._embedding_client.ImageEmbeddingsClient"), ): - client = RawAzureAIInferenceEmbeddingClient() + client = RawFoundryEmbeddingClient() assert client.model == "text-model" assert client.image_model == "image-model" def test_image_model_explicit(self, mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> None: """image_model can be set explicitly.""" - client = RawAzureAIInferenceEmbeddingClient( + client = RawFoundryEmbeddingClient( model="text-model", image_model="image-model", endpoint="https://test.inference.ai.azure.com", @@ -242,7 +242,7 @@ class TestRawAzureAIInferenceEmbeddingClient: self, mock_text_client: AsyncMock, mock_image_client: AsyncMock ) -> None: """image_model is passed to the image client embed call.""" - client = RawAzureAIInferenceEmbeddingClient( + client = RawFoundryEmbeddingClient( model="text-model", image_model="image-model", endpoint="https://test.inference.ai.azure.com", @@ -256,12 +256,10 @@ class TestRawAzureAIInferenceEmbeddingClient: assert call_kwargs.kwargs["model"] == "image-model" -class TestAzureAIInferenceEmbeddingClient: - """Tests for the telemetry-enabled Azure AI Inference embedding client.""" +class TestFoundryEmbeddingClient: + """Tests for the telemetry-enabled Foundry embedding client.""" - async def test_text_embeddings( - self, client: AzureAIInferenceEmbeddingClient[Any], mock_text_client: AsyncMock - ) -> None: + async def test_text_embeddings(self, client: FoundryEmbeddingClient[Any], mock_text_client: AsyncMock) -> None: """Text embeddings work through the telemetry layer.""" result = await client.get_embeddings(["hello"]) assert len(result) == 1 @@ -269,11 +267,11 @@ class TestAzureAIInferenceEmbeddingClient: async def test_otel_provider_name_default(self) -> None: """Default OTEL provider name is azure.ai.inference.""" - assert AzureAIInferenceEmbeddingClient.OTEL_PROVIDER_NAME == "azure.ai.inference" + assert FoundryEmbeddingClient.OTEL_PROVIDER_NAME == "azure.ai.inference" async def test_otel_provider_name_override(self, mock_text_client: AsyncMock, mock_image_client: AsyncMock) -> None: """OTEL provider name can be overridden.""" - client = AzureAIInferenceEmbeddingClient( + client = FoundryEmbeddingClient( model="test-model", endpoint="https://test.inference.ai.azure.com", api_key="test-key", @@ -284,32 +282,32 @@ class TestAzureAIInferenceEmbeddingClient: assert client.otel_provider_name == "custom-provider" -_SKIP_REASON = "Azure AI Inference integration tests disabled" +_SKIP_REASON = "Foundry inference integration tests disabled" -def _integration_tests_enabled() -> bool: +def _foundry_integration_tests_enabled() -> bool: return bool( - os.environ.get("AZURE_AI_INFERENCE_ENDPOINT") - and os.environ.get("AZURE_AI_INFERENCE_API_KEY") - and os.environ.get("AZURE_AI_INFERENCE_EMBEDDING_MODEL") + os.environ.get("FOUNDRY_MODELS_ENDPOINT") + and os.environ.get("FOUNDRY_MODELS_API_KEY") + and os.environ.get("FOUNDRY_EMBEDDING_MODEL") ) -skip_if_azure_ai_inference_integration_tests_disabled = pytest.mark.skipif( - not _integration_tests_enabled(), +skip_if_foundry_inference_integration_tests_disabled = pytest.mark.skipif( + not _foundry_integration_tests_enabled(), reason=_SKIP_REASON, ) -class TestAzureAIInferenceEmbeddingIntegration: - """Integration tests requiring a live Azure AI Inference endpoint.""" +class TestFoundryEmbeddingIntegration: + """Integration tests requiring a live Foundry inference endpoint.""" @pytest.mark.flaky @pytest.mark.integration - @skip_if_azure_ai_inference_integration_tests_disabled + @skip_if_foundry_inference_integration_tests_disabled async def test_text_embedding_live(self) -> None: """Generate text embeddings against a live endpoint.""" - client = AzureAIInferenceEmbeddingClient() + client = FoundryEmbeddingClient() result = await client.get_embeddings(["Hello, world!"]) assert len(result) == 1 assert len(result[0].vector) > 0 diff --git a/python/packages/lab/gaia/samples/openai_agent.py b/python/packages/lab/gaia/samples/openai_agent.py index ba0ef4ab49..8075d8c298 100644 --- a/python/packages/lab/gaia/samples/openai_agent.py +++ b/python/packages/lab/gaia/samples/openai_agent.py @@ -7,7 +7,7 @@ configured for GAIA benchmark tasks using the OpenAI Responses API. Required Environment Variables: OPENAI_API_KEY: Your OpenAI API key - OPENAI_RESPONSES_MODEL: Model to use with Responses API (e.g., gpt-4o, gpt-4o-mini) + OPENAI_CHAT_MODEL: Model to use with Responses API (e.g., gpt-4o, gpt-4o-mini) Optional Environment Variables: OPENAI_BASE_URL: Custom API base URL if using a proxy or compatible service @@ -19,7 +19,7 @@ Authentication: Example: export OPENAI_API_KEY="sk-..." - export OPENAI_RESPONSES_MODEL="gpt-4o" + export OPENAI_CHAT_MODEL="gpt-4o" """ from collections.abc import AsyncIterator diff --git a/python/packages/openai/README.md b/python/packages/openai/README.md index 17f1ee9597..185e528d8b 100644 --- a/python/packages/openai/README.md +++ b/python/packages/openai/README.md @@ -36,16 +36,20 @@ These variables are used when the client is configured for OpenAI: | `OPENAI_ORG_ID` | OpenAI organization ID | | `OPENAI_BASE_URL` | Custom OpenAI-compatible base URL | | `OPENAI_MODEL` | Generic fallback model | -| `OPENAI_RESPONSES_MODEL` | Preferred model for `OpenAIChatClient` | -| `OPENAI_CHAT_MODEL` | Preferred model for `OpenAIChatCompletionClient` | +| `OPENAI_CHAT_MODEL` | Preferred model for `OpenAIChatClient` | +| `OPENAI_CHAT_COMPLETION_MODEL` | Preferred model for `OpenAIChatCompletionClient` | | `OPENAI_EMBEDDING_MODEL` | Preferred model for `OpenAIEmbeddingClient` | Model lookup order: -- `OpenAIChatClient`: `OPENAI_RESPONSES_MODEL` -> `OPENAI_MODEL` -- `OpenAIChatCompletionClient`: `OPENAI_CHAT_MODEL` -> `OPENAI_MODEL` +- `OpenAIChatClient`: `OPENAI_CHAT_MODEL` -> `OPENAI_MODEL` +- `OpenAIChatCompletionClient`: `OPENAI_CHAT_COMPLETION_MODEL` -> `OPENAI_MODEL` - `OpenAIEmbeddingClient`: `OPENAI_EMBEDDING_MODEL` -> `OPENAI_MODEL` +These model variables are only consulted when you do not pass `model=` directly. In other words, +`OpenAIChatClient(model="...")` ignores `OPENAI_CHAT_MODEL`, and +`OpenAIChatCompletionClient(model="...")` ignores `OPENAI_CHAT_COMPLETION_MODEL`. + ### Azure OpenAI These variables are used when the client is configured for Azure OpenAI: @@ -57,16 +61,19 @@ These variables are used when the client is configured for Azure OpenAI: | `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | | `AZURE_OPENAI_API_VERSION` | Azure OpenAI API version | | `AZURE_OPENAI_MODEL` | Generic fallback deployment | -| `AZURE_OPENAI_RESPONSES_MODEL` | Preferred deployment for `OpenAIChatClient` | -| `AZURE_OPENAI_CHAT_MODEL` | Preferred deployment for `OpenAIChatCompletionClient` | +| `AZURE_OPENAI_CHAT_MODEL` | Preferred deployment for `OpenAIChatClient` | +| `AZURE_OPENAI_CHAT_COMPLETION_MODEL` | Preferred deployment for `OpenAIChatCompletionClient` | | `AZURE_OPENAI_EMBEDDING_MODEL` | Preferred deployment for `OpenAIEmbeddingClient` | Deployment lookup order: -- `OpenAIChatClient`: `AZURE_OPENAI_RESPONSES_MODEL` -> `AZURE_OPENAI_MODEL` -- `OpenAIChatCompletionClient`: `AZURE_OPENAI_CHAT_MODEL` -> `AZURE_OPENAI_MODEL` +- `OpenAIChatClient`: `AZURE_OPENAI_CHAT_MODEL` -> `AZURE_OPENAI_MODEL` +- `OpenAIChatCompletionClient`: `AZURE_OPENAI_CHAT_COMPLETION_MODEL` -> `AZURE_OPENAI_MODEL` - `OpenAIEmbeddingClient`: `AZURE_OPENAI_EMBEDDING_MODEL` -> `AZURE_OPENAI_MODEL` +For Azure routing, the same rule applies: the client-specific deployment variable is checked first, +then the generic `AZURE_OPENAI_MODEL` fallback. Passing `model=` overrides both environment variables. + When both OpenAI and Azure environment variables are present, the generic clients prefer OpenAI when `OPENAI_API_KEY` is configured. To use Azure explicitly, pass `azure_endpoint` or `credential`. diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index ec5bfa09b2..963888e45a 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -289,7 +289,7 @@ class RawOpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_RESPONSES_MODEL`` and then ``OPENAI_MODEL``. + reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL``. api_key: API key. When not provided explicitly, the constructor reads ``OPENAI_API_KEY``. A callable API key is also supported. org_id: OpenAI organization ID. When not provided explicitly, the constructor reads @@ -331,7 +331,7 @@ class RawOpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``AZURE_OPENAI_RESPONSES_MODEL`` and then + reads ``AZURE_OPENAI_CHAT_MODEL`` and then ``AZURE_OPENAI_MODEL``. azure_endpoint: Azure resource endpoint. When not provided explicitly, the constructor reads ``AZURE_OPENAI_ENDPOINT``. @@ -380,8 +380,8 @@ class RawOpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_RESPONSES_MODEL`` and then ``OPENAI_MODEL`` for OpenAI, - or ``AZURE_OPENAI_RESPONSES_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure. + reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL`` for OpenAI, + or ``AZURE_OPENAI_CHAT_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure. api_key: API key override. For OpenAI this maps to ``OPENAI_API_KEY``. For Azure this can be used instead of ``AZURE_OPENAI_API_KEY`` for key auth. A callable token provider is also accepted for backwards compatibility, @@ -418,10 +418,10 @@ class RawOpenAIChatClient( # type: ignore[misc] 2. Explicit OpenAI API key or ``OPENAI_API_KEY`` 3. Azure environment fallback - OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_RESPONSES_MODEL``, + OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_MODEL``, ``OPENAI_MODEL``, ``OPENAI_ORG_ID``, and ``OPENAI_BASE_URL``. Azure routing reads ``AZURE_OPENAI_ENDPOINT``, ``AZURE_OPENAI_BASE_URL``, - ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_RESPONSES_MODEL``, + ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_MODEL``, ``AZURE_OPENAI_MODEL``, and ``AZURE_OPENAI_API_VERSION``. """ settings, client, use_azure_client = load_openai_service_settings( @@ -437,8 +437,8 @@ class RawOpenAIChatClient( # type: ignore[misc] client=async_client, env_file_path=env_file_path, env_file_encoding=env_file_encoding, - openai_model_fields=("responses_model", "model"), - azure_model_fields=("responses_model", "model"), + openai_model_fields=("chat_model", "model"), + azure_model_fields=("chat_model", "model"), responses_mode=True, ) @@ -2501,7 +2501,7 @@ class OpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_RESPONSES_MODEL`` and then ``OPENAI_MODEL``. + reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL``. api_key: API key. When not provided explicitly, the constructor reads ``OPENAI_API_KEY``. A callable API key is also supported. org_id: OpenAI organization ID. When not provided explicitly, the constructor reads @@ -2547,7 +2547,7 @@ class OpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``AZURE_OPENAI_RESPONSES_MODEL`` and then + reads ``AZURE_OPENAI_CHAT_MODEL`` and then ``AZURE_OPENAI_MODEL``. azure_endpoint: Azure resource endpoint. When not provided explicitly, the constructor reads ``AZURE_OPENAI_ENDPOINT``. @@ -2600,8 +2600,8 @@ class OpenAIChatClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_RESPONSES_MODEL`` and then ``OPENAI_MODEL`` for OpenAI - routing, or ``AZURE_OPENAI_RESPONSES_MODEL`` and then + reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL`` for OpenAI + routing, or ``AZURE_OPENAI_CHAT_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure routing. api_key: API key override. For OpenAI routing this maps to ``OPENAI_API_KEY``. For Azure routing this can be used instead of ``AZURE_OPENAI_API_KEY`` for key @@ -2641,10 +2641,10 @@ class OpenAIChatClient( # type: ignore[misc] 2. Explicit OpenAI API key or ``OPENAI_API_KEY`` 3. Azure environment fallback - OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_RESPONSES_MODEL``, + OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_MODEL``, ``OPENAI_MODEL``, ``OPENAI_ORG_ID``, and ``OPENAI_BASE_URL``. Azure routing reads ``AZURE_OPENAI_ENDPOINT``, ``AZURE_OPENAI_BASE_URL``, - ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_RESPONSES_MODEL``, + ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_MODEL``, ``AZURE_OPENAI_MODEL``, and ``AZURE_OPENAI_API_VERSION``. Examples: diff --git a/python/packages/openai/agent_framework_openai/_chat_completion_client.py b/python/packages/openai/agent_framework_openai/_chat_completion_client.py index 0c70ea83a0..2da59e031e 100644 --- a/python/packages/openai/agent_framework_openai/_chat_completion_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_completion_client.py @@ -203,7 +203,7 @@ class RawOpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL``. + reads ``OPENAI_CHAT_COMPLETION_MODEL`` and then ``OPENAI_MODEL``. api_key: API key. When not provided explicitly, the constructor reads ``OPENAI_API_KEY``. A callable API key is also supported. org_id: OpenAI organization ID. When not provided explicitly, the constructor reads @@ -245,7 +245,7 @@ class RawOpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``AZURE_OPENAI_CHAT_MODEL`` and then + reads ``AZURE_OPENAI_CHAT_COMPLETION_MODEL`` and then ``AZURE_OPENAI_MODEL``. azure_endpoint: Azure resource endpoint. When not provided explicitly, the constructor reads ``AZURE_OPENAI_ENDPOINT``. @@ -294,8 +294,8 @@ class RawOpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL`` for OpenAI routing, - or ``AZURE_OPENAI_CHAT_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure routing. + reads ``OPENAI_CHAT_COMPLETION_MODEL`` and then ``OPENAI_MODEL`` for OpenAI routing, + or ``AZURE_OPENAI_CHAT_COMPLETION_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure routing. api_key: API key override. For OpenAI routing this maps to ``OPENAI_API_KEY``. For Azure routing this can be used instead of ``AZURE_OPENAI_API_KEY`` for key auth. A callable token provider is also accepted for backwards compatibility, @@ -332,10 +332,10 @@ class RawOpenAIChatCompletionClient( # type: ignore[misc] 2. Explicit OpenAI API key or ``OPENAI_API_KEY`` 3. Azure environment fallback - OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_MODEL``, + OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_COMPLETION_MODEL``, ``OPENAI_MODEL``, ``OPENAI_ORG_ID``, and ``OPENAI_BASE_URL``. Azure routing reads ``AZURE_OPENAI_ENDPOINT``, ``AZURE_OPENAI_BASE_URL``, - ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_MODEL``, + ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_COMPLETION_MODEL``, ``AZURE_OPENAI_MODEL``, and ``AZURE_OPENAI_API_VERSION``. """ settings, client, use_azure_client = load_openai_service_settings( @@ -351,8 +351,8 @@ class RawOpenAIChatCompletionClient( # type: ignore[misc] client=async_client, env_file_path=env_file_path, env_file_encoding=env_file_encoding, - openai_model_fields=("chat_model", "model"), - azure_model_fields=("chat_model", "model"), + openai_model_fields=("chat_completion_model", "model"), + azure_model_fields=("chat_completion_model", "model"), ) self.client = client @@ -1048,7 +1048,7 @@ class OpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL``. + reads ``OPENAI_CHAT_COMPLETION_MODEL`` and then ``OPENAI_MODEL``. api_key: API key. When not provided explicitly, the constructor reads ``OPENAI_API_KEY``. A callable API key is also supported. org_id: OpenAI organization ID. When not provided explicitly, the constructor reads @@ -1088,7 +1088,7 @@ class OpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``AZURE_OPENAI_CHAT_MODEL`` and then + reads ``AZURE_OPENAI_CHAT_COMPLETION_MODEL`` and then ``AZURE_OPENAI_MODEL``. azure_endpoint: Azure resource endpoint. When not provided explicitly, the constructor reads ``AZURE_OPENAI_ENDPOINT``. @@ -1135,8 +1135,8 @@ class OpenAIChatCompletionClient( # type: ignore[misc] Keyword Args: model: Model identifier to use for the request. When not provided, the constructor - reads ``OPENAI_CHAT_MODEL`` and then ``OPENAI_MODEL`` for OpenAI routing, - or ``AZURE_OPENAI_CHAT_MODEL`` and then + reads ``OPENAI_CHAT_COMPLETION_MODEL`` and then ``OPENAI_MODEL`` for OpenAI routing, + or ``AZURE_OPENAI_CHAT_COMPLETION_MODEL`` and then ``AZURE_OPENAI_MODEL`` for Azure routing. api_key: API key override. For OpenAI routing this maps to ``OPENAI_API_KEY``. For Azure routing this can be used instead of ``AZURE_OPENAI_API_KEY`` for key @@ -1173,10 +1173,10 @@ class OpenAIChatCompletionClient( # type: ignore[misc] 2. Explicit OpenAI API key or ``OPENAI_API_KEY`` 3. Azure environment fallback - OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_MODEL``, + OpenAI routing reads ``OPENAI_API_KEY``, ``OPENAI_CHAT_COMPLETION_MODEL``, ``OPENAI_MODEL``, ``OPENAI_ORG_ID``, and ``OPENAI_BASE_URL``. Azure routing reads ``AZURE_OPENAI_ENDPOINT``, ``AZURE_OPENAI_BASE_URL``, - ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_MODEL``, + ``AZURE_OPENAI_API_KEY``, ``AZURE_OPENAI_CHAT_COMPLETION_MODEL``, ``AZURE_OPENAI_MODEL``, and ``AZURE_OPENAI_API_VERSION``. Examples: diff --git a/python/packages/openai/agent_framework_openai/_shared.py b/python/packages/openai/agent_framework_openai/_shared.py index 7afc4f2d25..f1d0728f61 100644 --- a/python/packages/openai/agent_framework_openai/_shared.py +++ b/python/packages/openai/agent_framework_openai/_shared.py @@ -67,10 +67,10 @@ class OpenAISettings(TypedDict, total=False): Can be set via environment variable OPENAI_MODEL. embedding_model: The OpenAI embedding model to use, for example, text-embedding-3-small. Can be set via environment variable OPENAI_EMBEDDING_MODEL. - chat_model: The OpenAI chat-completions model to prefer before OPENAI_MODEL. + chat_model: The OpenAIChatClient model to prefer before OPENAI_MODEL. Can be set via environment variable OPENAI_CHAT_MODEL. - responses_model: The OpenAI responses model to prefer before OPENAI_MODEL. - Can be set via environment variable OPENAI_RESPONSES_MODEL. + chat_completion_model: The OpenAIChatCompletionClient model to prefer before OPENAI_MODEL. + Can be set via environment variable OPENAI_CHAT_COMPLETION_MODEL. Examples: .. code-block:: python @@ -95,7 +95,7 @@ class OpenAISettings(TypedDict, total=False): model: str | None embedding_model: str | None chat_model: str | None - responses_model: str | None + chat_completion_model: str | None class AzureOpenAISettings(TypedDict, total=False): @@ -107,24 +107,24 @@ class AzureOpenAISettings(TypedDict, total=False): model: str | None embedding_model: str | None chat_model: str | None - responses_model: str | None + chat_completion_model: str | None api_version: str | None -OpenAIModelSettingName = Literal["model", "embedding_model", "chat_model", "responses_model"] +OpenAIModelSettingName = Literal["model", "embedding_model", "chat_model", "chat_completion_model"] OPENAI_MODEL_ENV_VARS: dict[OpenAIModelSettingName, str] = { "model": "OPENAI_MODEL", "embedding_model": "OPENAI_EMBEDDING_MODEL", "chat_model": "OPENAI_CHAT_MODEL", - "responses_model": "OPENAI_RESPONSES_MODEL", + "chat_completion_model": "OPENAI_CHAT_COMPLETION_MODEL", } AZURE_MODEL_ENV_VARS: dict[OpenAIModelSettingName, str] = { "model": "AZURE_OPENAI_MODEL", "embedding_model": "AZURE_OPENAI_EMBEDDING_MODEL", "chat_model": "AZURE_OPENAI_CHAT_MODEL", - "responses_model": "AZURE_OPENAI_RESPONSES_MODEL", + "chat_completion_model": "AZURE_OPENAI_CHAT_COMPLETION_MODEL", } diff --git a/python/packages/openai/tests/openai/conftest.py b/python/packages/openai/tests/openai/conftest.py index 9e11067213..34c81952e3 100644 --- a/python/packages/openai/tests/openai/conftest.py +++ b/python/packages/openai/tests/openai/conftest.py @@ -43,15 +43,15 @@ def openai_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # "OPENAI_ORG_ID", "OPENAI_MODEL", "OPENAI_EMBEDDING_MODEL", + "OPENAI_CHAT_COMPLETION_MODEL", "OPENAI_CHAT_MODEL", - "OPENAI_RESPONSES_MODEL", "OPENAI_API_VERSION", "OPENAI_BASE_URL", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_API_KEY", + "AZURE_OPENAI_CHAT_COMPLETION_MODEL", "AZURE_OPENAI_CHAT_MODEL", - "AZURE_OPENAI_RESPONSES_MODEL", "AZURE_OPENAI_EMBEDDING_MODEL", "AZURE_OPENAI_MODEL", "AZURE_OPENAI_API_VERSION", @@ -92,15 +92,15 @@ def azure_openai_unit_test_env(monkeypatch, exclude_list, override_env_param_dic "OPENAI_ORG_ID", "OPENAI_MODEL", "OPENAI_EMBEDDING_MODEL", + "OPENAI_CHAT_COMPLETION_MODEL", "OPENAI_CHAT_MODEL", - "OPENAI_RESPONSES_MODEL", "OPENAI_API_VERSION", "OPENAI_BASE_URL", "AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_API_KEY", + "AZURE_OPENAI_CHAT_COMPLETION_MODEL", "AZURE_OPENAI_CHAT_MODEL", - "AZURE_OPENAI_RESPONSES_MODEL", "AZURE_OPENAI_EMBEDDING_MODEL", "AZURE_OPENAI_MODEL", "AZURE_OPENAI_API_VERSION", @@ -109,8 +109,8 @@ def azure_openai_unit_test_env(monkeypatch, exclude_list, override_env_param_dic env_vars = { "AZURE_OPENAI_ENDPOINT": "https://test-endpoint.openai.azure.com", - "AZURE_OPENAI_CHAT_MODEL": "test_chat_deployment", - "AZURE_OPENAI_RESPONSES_MODEL": "test_responses_deployment", + "AZURE_OPENAI_CHAT_COMPLETION_MODEL": "test_chat_deployment", + "AZURE_OPENAI_CHAT_MODEL": "test_responses_deployment", "AZURE_OPENAI_EMBEDDING_MODEL": "test_embedding_deployment", "AZURE_OPENAI_MODEL": "test_deployment", "AZURE_OPENAI_API_KEY": "test_api_key", diff --git a/python/packages/openai/tests/openai/test_openai_chat_client.py b/python/packages/openai/tests/openai/test_openai_chat_client.py index dd1da5851a..b1ed7973f2 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_client.py @@ -150,12 +150,12 @@ def test_openai_chat_client_tool_methods_return_dict() -> None: assert web_tool.get("type") == "web_search" -def test_init_prefers_openai_responses_model(monkeypatch, openai_unit_test_env: dict[str, str]) -> None: - monkeypatch.setenv("OPENAI_RESPONSES_MODEL", "test_responses_model") +def test_init_prefers_openai_chat_model(monkeypatch, openai_unit_test_env: dict[str, str]) -> None: + monkeypatch.setenv("OPENAI_CHAT_MODEL", "test_chat_model") openai_responses_client = OpenAIChatClient() - assert openai_responses_client.model == "test_responses_model" + assert openai_responses_client.model == "test_chat_model" def test_init_validation_fail() -> None: diff --git a/python/packages/openai/tests/openai/test_openai_chat_client_azure.py b/python/packages/openai/tests/openai/test_openai_chat_client_azure.py index 044477bc72..b101ed8df4 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_client_azure.py +++ b/python/packages/openai/tests/openai/test_openai_chat_client_azure.py @@ -23,7 +23,7 @@ pytestmark = pytest.mark.azure skip_if_azure_openai_integration_tests_disabled = pytest.mark.skipif( os.getenv("AZURE_OPENAI_ENDPOINT", "") in ("", "https://test-endpoint.openai.azure.com") - or (os.getenv("AZURE_OPENAI_RESPONSES_MODEL", "") == "" and os.getenv("AZURE_OPENAI_MODEL", "") == ""), + or (os.getenv("AZURE_OPENAI_CHAT_MODEL", "") == "" and os.getenv("AZURE_OPENAI_MODEL", "") == ""), reason="No real Azure OpenAI endpoint or responses deployment provided; skipping integration tests.", ) @@ -35,7 +35,7 @@ def _with_azure_openai_debug() -> Any: try: return await func(*args, **kwargs) except Exception as exc: - model = os.getenv("AZURE_OPENAI_RESPONSES_MODEL") or os.getenv("AZURE_OPENAI_MODEL", "") + model = os.getenv("AZURE_OPENAI_CHAT_MODEL") or os.getenv("AZURE_OPENAI_MODEL", "") api_version = os.getenv("AZURE_OPENAI_API_VERSION") or "preview" endpoint = os.getenv("AZURE_OPENAI_ENDPOINT", "") debug_message = f"Azure OpenAI debug: endpoint={endpoint}, model={model}, api_version={api_version}" @@ -96,7 +96,7 @@ async def get_weather(location: str) -> str: def test_init_with_azure_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None: client = OpenAIChatClient(credential=AzureCliCredential()) - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] assert isinstance(client, SupportsChatGetResponse) assert isinstance(client.client, AsyncAzureOpenAI) assert client.OTEL_PROVIDER_NAME == "azure.ai.openai" @@ -106,7 +106,7 @@ def test_init_with_azure_endpoint(azure_openai_unit_test_env: dict[str, str]) -> def test_init_auto_detects_azure_env(azure_openai_unit_test_env: dict[str, str]) -> None: client = OpenAIChatClient() - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] assert isinstance(client.client, AsyncAzureOpenAI) assert client.azure_endpoint == azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"] @@ -141,7 +141,7 @@ def test_explicit_credential_wins_over_openai_api_key(monkeypatch, azure_openai_ client = OpenAIChatClient(credential=lambda: "token") - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] assert isinstance(client.client, AsyncAzureOpenAI) assert client.azure_endpoint == azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"] @@ -149,7 +149,7 @@ def test_explicit_credential_wins_over_openai_api_key(monkeypatch, azure_openai_ def test_init_falls_back_to_generic_azure_deployment_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_RESPONSES_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) client = OpenAIChatClient() @@ -160,9 +160,9 @@ def test_init_falls_back_to_generic_azure_deployment_env( def test_init_does_not_fall_back_to_openai_responses_model_for_azure_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_RESPONSES_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) monkeypatch.delenv("AZURE_OPENAI_MODEL", raising=False) - monkeypatch.setenv("OPENAI_RESPONSES_MODEL", "test_responses_model") + monkeypatch.setenv("OPENAI_CHAT_MODEL", "test_responses_model") with pytest.raises(SettingNotFoundError, match="Azure OpenAI client requires a model"): OpenAIChatClient() @@ -171,9 +171,9 @@ def test_init_does_not_fall_back_to_openai_responses_model_for_azure_env( def test_init_does_not_fall_back_to_openai_model_for_azure_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_RESPONSES_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) monkeypatch.delenv("AZURE_OPENAI_MODEL", raising=False) - monkeypatch.delenv("OPENAI_RESPONSES_MODEL", raising=False) + monkeypatch.delenv("OPENAI_CHAT_MODEL", raising=False) monkeypatch.setenv("OPENAI_MODEL", "gpt-5") with pytest.raises(SettingNotFoundError, match="Azure OpenAI client requires a model"): @@ -203,7 +203,7 @@ def test_init_with_credential_wraps_async_token_credential( def test_init_uses_default_azure_api_version(azure_openai_unit_test_env: dict[str, str]) -> None: client = OpenAIChatClient(credential=AzureCliCredential()) - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_RESPONSES_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] assert client.api_version is not None diff --git a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py index fb5e7c471e..053682eb32 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_completion_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_completion_client.py @@ -79,7 +79,7 @@ def test_supports_web_search_only() -> None: def test_init_prefers_openai_chat_model(monkeypatch, openai_unit_test_env: dict[str, str]) -> None: - monkeypatch.setenv("OPENAI_CHAT_MODEL", "test_chat_model") + monkeypatch.setenv("OPENAI_CHAT_COMPLETION_MODEL", "test_chat_model") open_ai_chat_completion = OpenAIChatCompletionClient() diff --git a/python/packages/openai/tests/openai/test_openai_chat_completion_client_azure.py b/python/packages/openai/tests/openai/test_openai_chat_completion_client_azure.py index 3a5e930d1f..a343ed80dd 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_completion_client_azure.py +++ b/python/packages/openai/tests/openai/test_openai_chat_completion_client_azure.py @@ -29,7 +29,7 @@ pytestmark = pytest.mark.azure skip_if_azure_openai_integration_tests_disabled = pytest.mark.skipif( os.getenv("AZURE_OPENAI_ENDPOINT", "") in ("", "https://test-endpoint.openai.azure.com") - or (os.getenv("AZURE_OPENAI_CHAT_MODEL", "") == "" and os.getenv("AZURE_OPENAI_MODEL", "") == ""), + or (os.getenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", "") == "" and os.getenv("AZURE_OPENAI_MODEL", "") == ""), reason="No real Azure OpenAI endpoint or chat deployment provided; skipping integration tests.", ) @@ -41,7 +41,7 @@ def _with_azure_openai_debug() -> Any: try: return await func(*args, **kwargs) except Exception as exc: - model = os.getenv("AZURE_OPENAI_CHAT_MODEL") or os.getenv("AZURE_OPENAI_MODEL", "") + model = os.getenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL") or os.getenv("AZURE_OPENAI_MODEL", "") api_version = os.getenv("AZURE_OPENAI_API_VERSION", "") endpoint = os.getenv("AZURE_OPENAI_ENDPOINT", "") debug_message = f"Azure OpenAI debug: endpoint={endpoint}, model={model}, api_version={api_version}" @@ -78,7 +78,7 @@ async def get_weather(location: str) -> str: def test_init_with_azure_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None: client = OpenAIChatCompletionClient(azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")) - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_COMPLETION_MODEL"] assert isinstance(client, SupportsChatGetResponse) assert isinstance(client.client, AsyncAzureOpenAI) assert client.OTEL_PROVIDER_NAME == "azure.ai.openai" @@ -89,7 +89,7 @@ def test_init_with_azure_endpoint(azure_openai_unit_test_env: dict[str, str]) -> def test_init_auto_detects_azure_env(azure_openai_unit_test_env: dict[str, str]) -> None: client = OpenAIChatCompletionClient() - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_COMPLETION_MODEL"] assert isinstance(client.client, AsyncAzureOpenAI) assert client.azure_endpoint == azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"] @@ -111,7 +111,7 @@ def test_explicit_credential_wins_over_openai_api_key(monkeypatch, azure_openai_ client = OpenAIChatCompletionClient(credential=lambda: "token") - assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_MODEL"] + assert client.model == azure_openai_unit_test_env["AZURE_OPENAI_CHAT_COMPLETION_MODEL"] assert isinstance(client.client, AsyncAzureOpenAI) assert client.azure_endpoint == azure_openai_unit_test_env["AZURE_OPENAI_ENDPOINT"] @@ -119,7 +119,7 @@ def test_explicit_credential_wins_over_openai_api_key(monkeypatch, azure_openai_ def test_init_falls_back_to_generic_azure_deployment_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", raising=False) client = OpenAIChatCompletionClient() @@ -130,9 +130,9 @@ def test_init_falls_back_to_generic_azure_deployment_env( def test_init_does_not_fall_back_to_openai_chat_model_for_azure_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", raising=False) monkeypatch.delenv("AZURE_OPENAI_MODEL", raising=False) - monkeypatch.setenv("OPENAI_CHAT_MODEL", "test_chat_model") + monkeypatch.setenv("OPENAI_CHAT_COMPLETION_MODEL", "test_chat_model") with pytest.raises(SettingNotFoundError, match="Azure OpenAI client requires a model"): OpenAIChatCompletionClient() @@ -141,9 +141,9 @@ def test_init_does_not_fall_back_to_openai_chat_model_for_azure_env( def test_init_does_not_fall_back_to_openai_model_for_azure_env( monkeypatch, azure_openai_unit_test_env: dict[str, str] ) -> None: - monkeypatch.delenv("AZURE_OPENAI_CHAT_MODEL", raising=False) + monkeypatch.delenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", raising=False) monkeypatch.delenv("AZURE_OPENAI_MODEL", raising=False) - monkeypatch.delenv("OPENAI_CHAT_MODEL", raising=False) + monkeypatch.delenv("OPENAI_CHAT_COMPLETION_MODEL", raising=False) monkeypatch.setenv("OPENAI_MODEL", "gpt-5") with pytest.raises(SettingNotFoundError, match="Azure OpenAI client requires a model"): diff --git a/python/pyproject.toml b/python/pyproject.toml index a554ce97f1..2782b98230 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -70,7 +70,6 @@ agent-framework-ag-ui = { workspace = true } agent-framework-azure-ai-search = { workspace = true } agent-framework-azure-cosmos = { workspace = true } agent-framework-anthropic = { workspace = true } -agent-framework-azure-ai = { workspace = true } agent-framework-azurefunctions = { workspace = true } agent-framework-bedrock = { workspace = true } agent-framework-chatkit = { workspace = true } @@ -187,7 +186,6 @@ executionEnvironments = [ { root = "packages/ag-ui/tests", reportPrivateUsage = "none" }, { root = "packages/anthropic/tests", reportPrivateUsage = "none" }, { root = "packages/azure-ai-search/tests", reportPrivateUsage = "none" }, - { root = "packages/azure-ai/tests", reportPrivateUsage = "none" }, { root = "packages/azure-cosmos/tests", reportPrivateUsage = "none" }, { root = "packages/azurefunctions/tests", reportPrivateUsage = "none" }, { root = "packages/bedrock/tests", reportPrivateUsage = "none" }, diff --git a/python/samples/02-agents/chat_client/README.md b/python/samples/02-agents/chat_client/README.md index 0a67230d51..b7abb24e55 100644 --- a/python/samples/02-agents/chat_client/README.md +++ b/python/samples/02-agents/chat_client/README.md @@ -61,8 +61,8 @@ Depending on the selected client, set the appropriate environment variables: **For OpenAI clients:** - `OPENAI_API_KEY`: Your OpenAI API key -- `OPENAI_CHAT_MODEL`: The OpenAI model for `openai_chat_completion` -- `OPENAI_RESPONSES_MODEL`: The OpenAI model for `openai_responses` +- `OPENAI_CHAT_COMPLETION_MODEL`: The OpenAI model for `openai_chat_completion` +- `OPENAI_CHAT_MODEL`: The OpenAI model for `openai_responses` **For Anthropic client (`anthropic`):** - `ANTHROPIC_API_KEY`: Your Anthropic API key diff --git a/python/samples/02-agents/conversations/README.md b/python/samples/02-agents/conversations/README.md index ab527890fa..c516d7d639 100644 --- a/python/samples/02-agents/conversations/README.md +++ b/python/samples/02-agents/conversations/README.md @@ -8,6 +8,7 @@ These samples demonstrate different approaches to managing conversation history |------|-------------| | [`suspend_resume_session.py`](suspend_resume_session.py) | Suspend and resume conversation sessions, comparing service-managed sessions (Azure AI Foundry) with in-memory sessions (OpenAI). | | [`custom_history_provider.py`](custom_history_provider.py) | Implement a custom history provider by extending `BaseHistoryProvider`, enabling conversation persistence in your preferred storage backend. | +| [`cosmos_history_provider.py`](cosmos_history_provider.py) | Use Azure Cosmos DB as a history provider for durable conversation storage with `CosmosHistoryProvider`. | | [`redis_history_provider.py`](redis_history_provider.py) | Use Redis as a history provider for persistent conversation history storage across sessions. | ## Prerequisites @@ -21,6 +22,14 @@ These samples demonstrate different approaches to managing conversation history **For `custom_history_provider.py`:** - `OPENAI_API_KEY`: Your OpenAI API key +**For `cosmos_history_provider.py`:** +- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint +- `FOUNDRY_MODEL`: The Foundry model deployment name +- `AZURE_COSMOS_ENDPOINT`: Your Azure Cosmos DB account endpoint +- `AZURE_COSMOS_DATABASE_NAME`: The database that stores conversation history +- `AZURE_COSMOS_CONTAINER_NAME`: The container that stores conversation history +- Either `AZURE_COSMOS_KEY` or Azure CLI authentication (`az login`) + **For `redis_history_provider.py`:** - `OPENAI_API_KEY`: Your OpenAI API key - A running Redis server — default URL is `redis://localhost:6379` diff --git a/python/packages/azure-cosmos/samples/cosmos_history_provider.py b/python/samples/02-agents/conversations/cosmos_history_provider.py similarity index 50% rename from python/packages/azure-cosmos/samples/cosmos_history_provider.py rename to python/samples/02-agents/conversations/cosmos_history_provider.py index 67235cbf14..5dc5c54b65 100644 --- a/python/packages/azure-cosmos/samples/cosmos_history_provider.py +++ b/python/samples/02-agents/conversations/cosmos_history_provider.py @@ -1,20 +1,19 @@ # Copyright (c) Microsoft. All rights reserved. -# ruff: noqa: T201 import asyncio import os +from agent_framework import Agent +from agent_framework.azure import CosmosHistoryProvider from agent_framework.foundry import FoundryChatClient from azure.identity.aio import AzureCliCredential from dotenv import load_dotenv -from agent_framework_azure_cosmos import CosmosHistoryProvider - # Load environment variables from .env file. load_dotenv() """ -This sample demonstrates CosmosHistoryProvider as an agent context provider. +This sample demonstrates CosmosHistoryProvider as an agent history provider. Key components: - FoundryChatClient configured with an Azure AI project endpoint @@ -54,39 +53,38 @@ async def main() -> None: ) return - # 1. Create an Azure credential and Foundry chat client using project endpoint auth. - async with AzureCliCredential() as credential: - client = FoundryChatClient( - project_endpoint=project_endpoint, - model=model, - credential=credential, - ) + # 1. Create an Azure credential and a CosmosHistoryProvider for agent context + async with ( + AzureCliCredential() as credential, + CosmosHistoryProvider( + endpoint=cosmos_endpoint, + database_name=cosmos_database_name, + container_name=cosmos_container_name, + credential=cosmos_key or credential, + ) as history_provider, + # 2. Create an agent that uses Cosmos for persisted conversation history. + Agent( + client=FoundryChatClient( + project_endpoint=project_endpoint, + model=model, + credential=credential, + ), + name="CosmosHistoryAgent", + instructions="You are a helpful assistant that remembers prior turns.", + context_providers=[history_provider], + default_options={"store": False}, + ) as agent, + ): + # 3. Create a session (session_id is used as the partition key). + session = agent.create_session() - # 2. Create an agent that uses the history provider as a context provider. - async with ( - CosmosHistoryProvider( - endpoint=cosmos_endpoint, - database_name=cosmos_database_name, - container_name=cosmos_container_name, - credential=cosmos_key or credential, - ) as history_provider, - client.as_agent( - name="CosmosHistoryAgent", - instructions="You are a helpful assistant that remembers prior turns.", - context_providers=[history_provider], - default_options={"store": False}, - ) as agent, - ): - # 3. Create a session (session_id is used as the partition key). - session = agent.create_session() + # 4. Run a multi-turn conversation; history is persisted by CosmosHistoryProvider. + response1 = await agent.run("My name is Ada and I enjoy distributed systems.", session=session) + print(f"Assistant: {response1.text}") - # 4. Run a multi-turn conversation; history is persisted by CosmosHistoryProvider. - response1 = await agent.run("My name is Ada and I enjoy distributed systems.", session=session) - print(f"Assistant: {response1.text}") - - response2 = await agent.run("What do you remember about me?", session=session) - print(f"Assistant: {response2.text}") - print(f"Container: {history_provider.container_name}") + response2 = await agent.run("What do you remember about me?", session=session) + print(f"Assistant: {response2.text}") + print(f"Container: {history_provider.container_name}") if __name__ == "__main__": diff --git a/python/samples/02-agents/devui/.env.example b/python/samples/02-agents/devui/.env.example index 712a1f7a30..b52c21a4e8 100644 --- a/python/samples/02-agents/devui/.env.example +++ b/python/samples/02-agents/devui/.env.example @@ -8,7 +8,7 @@ FOUNDRY_MODEL=gpt-4o # Azure OpenAI workflow sample AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com -AZURE_OPENAI_RESPONSES_MODEL=gpt-4o +AZURE_OPENAI_CHAT_MODEL=gpt-4o # Optional fallback env name also supported by workflow_with_agents/workflow.py: AZURE_OPENAI_MODEL=gpt-4o # Optional if you need to override the default API version: diff --git a/python/samples/02-agents/devui/README.md b/python/samples/02-agents/devui/README.md index 8498a0cab5..970116c61a 100644 --- a/python/samples/02-agents/devui/README.md +++ b/python/samples/02-agents/devui/README.md @@ -94,7 +94,7 @@ workflow_name/ | Sample | What it demonstrates | Required keys / auth | | ------ | -------------------- | -------------------- | | [**workflow_declarative/**](workflow_declarative/) | A YAML-defined workflow loaded through `WorkflowFactory`, with nested age-based branching and no model client code. | None | -| [**workflow_with_agents/**](workflow_with_agents/) | A content review workflow that uses agents as executors and routes based on structured review output (`Writer -> Reviewer -> Editor/Publisher -> Summarizer`). | `AZURE_OPENAI_ENDPOINT`, plus `AZURE_OPENAI_RESPONSES_MODEL` or `AZURE_OPENAI_MODEL`; Azure CLI auth via `az login`; `AZURE_OPENAI_API_VERSION` is optional | +| [**workflow_with_agents/**](workflow_with_agents/) | A content review workflow that uses agents as executors and routes based on structured review output (`Writer -> Reviewer -> Editor/Publisher -> Summarizer`). | `AZURE_OPENAI_ENDPOINT`, plus `AZURE_OPENAI_CHAT_MODEL` or `AZURE_OPENAI_MODEL`; Azure CLI auth via `az login`; `AZURE_OPENAI_API_VERSION` is optional | | [**workflow_spam/**](workflow_spam/) | A multi-step spam detection workflow with human-in-the-loop approval, branching for spam vs. legitimate messages, and a final reporting step. | None | | [**workflow_fanout/**](workflow_fanout/) | A larger fan-out/fan-in data processing workflow with parallel validation, multiple transformations, QA, aggregation, and demo failure toggles. | None | @@ -130,7 +130,7 @@ export FOUNDRY_MODEL="gpt-4o" # Azure OpenAI workflow_with_agents sample export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com" -export AZURE_OPENAI_RESPONSES_MODEL="gpt-4o" +export AZURE_OPENAI_CHAT_MODEL="gpt-4o" export AZURE_OPENAI_MODEL="gpt-4o" az login diff --git a/python/samples/02-agents/devui/workflow_with_agents/.env.example b/python/samples/02-agents/devui/workflow_with_agents/.env.example index ebbf5ccc9e..54eb5d179a 100644 --- a/python/samples/02-agents/devui/workflow_with_agents/.env.example +++ b/python/samples/02-agents/devui/workflow_with_agents/.env.example @@ -2,7 +2,7 @@ # This sample uses Azure CLI auth, so run `az login` before starting DevUI. AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com -AZURE_OPENAI_RESPONSES_MODEL=gpt-4o +AZURE_OPENAI_CHAT_MODEL=gpt-4o # Optional fallback env name also supported by the client: # AZURE_OPENAI_MODEL=gpt-4o # Optional if you need to override the default API version: diff --git a/python/samples/02-agents/devui/workflow_with_agents/workflow.py b/python/samples/02-agents/devui/workflow_with_agents/workflow.py index 598e7201f7..708c85a63c 100644 --- a/python/samples/02-agents/devui/workflow_with_agents/workflow.py +++ b/python/samples/02-agents/devui/workflow_with_agents/workflow.py @@ -65,7 +65,7 @@ def is_approved(message: Any) -> bool: # Create Azure OpenAI Responses chat client client = OpenAIChatClient( - model=os.environ.get("AZURE_OPENAI_RESPONSES_MODEL") or os.environ.get("AZURE_OPENAI_MODEL"), + model=os.environ.get("AZURE_OPENAI_CHAT_MODEL") or os.environ.get("AZURE_OPENAI_MODEL"), azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"), api_version=os.environ.get("AZURE_OPENAI_API_VERSION"), credential=AzureCliCredential(), diff --git a/python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py b/python/samples/02-agents/embeddings/foundry_embeddings.py similarity index 78% rename from python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py rename to python/samples/02-agents/embeddings/foundry_embeddings.py index 931d8a8759..b52443249c 100644 --- a/python/samples/02-agents/embeddings/azure_ai_inference_embeddings.py +++ b/python/samples/02-agents/embeddings/foundry_embeddings.py @@ -1,10 +1,10 @@ # /// script # requires-python = ">=3.10" # dependencies = [ -# "agent-framework-azure-ai", +# "agent-framework-foundry", # ] # /// -# Run with: uv run samples/02-agents/embeddings/azure_ai_inference_embeddings.py +# Run with: uv run samples/02-agents/embeddings/foundry_embeddings.py # Copyright (c) Microsoft. All rights reserved. @@ -12,28 +12,29 @@ import asyncio import pathlib from agent_framework import Content -from agent_framework.azure import AzureAIInferenceEmbeddingClient +from agent_framework.foundry import FoundryEmbeddingClient from dotenv import load_dotenv load_dotenv() -"""Azure AI Inference Image Embedding Example +"""Microsoft Foundry Image Embedding Example This sample demonstrates how to generate image embeddings using the -Azure AI Inference embedding client with the Cohere-embed-v3-english model. +Foundry embedding client with the Cohere-embed-v3-english model. Images are passed as ``Content`` objects created with ``Content.from_data()``. Prerequisites: - Deploy an embedding model in Azure AI Inference that supports image inputs, such as Cohere-embed-v3-english. + Deploy an embedding model to a Foundry-hosted inference endpoint that supports image inputs, + such as Cohere-embed-v3-english. The details page for that model, has a target URI and a Key, which should be set in environment variables or a .env file as follows, the target URI should append the `/models` path: - - AZURE_AI_INFERENCE_ENDPOINT: Your Azure AI model inference endpoint URL, for instance: + - FOUNDRY_MODELS_ENDPOINT: Your Foundry models endpoint URL, for instance: https://.azure-api.net//models - - AZURE_AI_INFERENCE_API_KEY: Your API key - - AZURE_AI_INFERENCE_EMBEDDING_MODEL: The text embedding model name + - FOUNDRY_MODELS_API_KEY: Your API key + - FOUNDRY_EMBEDDING_MODEL: The text embedding model name (e.g. "text-embedding-3-small") - - AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL: The image embedding model name + - FOUNDRY_IMAGE_EMBEDDING_MODEL: The image embedding model name (e.g. "Cohere-embed-v3-english") """ @@ -41,8 +42,8 @@ SAMPLE_IMAGE_PATH = pathlib.Path(__file__).parent.parent.parent / "shared" / "sa async def main() -> None: - """Generate image embeddings with Azure AI Inference.""" - async with AzureAIInferenceEmbeddingClient() as client: + """Generate image embeddings with Foundry.""" + async with FoundryEmbeddingClient() as client: # 1. Generate an image embedding. image_bytes = SAMPLE_IMAGE_PATH.read_bytes() image_content = Content.from_data(data=image_bytes, media_type="image/jpeg") diff --git a/python/samples/02-agents/embeddings/azure_openai_embeddings.py b/python/samples/02-agents/embeddings/openai_embeddings_on_azure.py similarity index 100% rename from python/samples/02-agents/embeddings/azure_openai_embeddings.py rename to python/samples/02-agents/embeddings/openai_embeddings_on_azure.py diff --git a/python/samples/02-agents/mcp/README.md b/python/samples/02-agents/mcp/README.md index 149ba19c0c..f433b349dc 100644 --- a/python/samples/02-agents/mcp/README.md +++ b/python/samples/02-agents/mcp/README.md @@ -17,7 +17,7 @@ The Model Context Protocol (MCP) is an open standard for connecting AI agents to ## Prerequisites - `OPENAI_API_KEY` environment variable -- `OPENAI_RESPONSES_MODEL` environment variable +- `OPENAI_CHAT_MODEL` environment variable Run `mcp_api_key_auth.py` with the MCP API key as the first command-line argument. diff --git a/python/samples/02-agents/middleware/README.md b/python/samples/02-agents/middleware/README.md index 755a705eea..1ec5a1abab 100644 --- a/python/samples/02-agents/middleware/README.md +++ b/python/samples/02-agents/middleware/README.md @@ -25,7 +25,7 @@ The new usage tracking sample uses `OpenAIChatClient`, so set the usual OpenAI r ```bash export OPENAI_API_KEY="your-openai-api-key" -export OPENAI_RESPONSES_MODEL="gpt-4.1-mini" +export OPENAI_CHAT_MODEL="gpt-4.1-mini" ``` Then run: diff --git a/python/samples/02-agents/observability/.env.example b/python/samples/02-agents/observability/.env.example index bbcf8c1301..f3dd329bac 100644 --- a/python/samples/02-agents/observability/.env.example +++ b/python/samples/02-agents/observability/.env.example @@ -40,8 +40,8 @@ ENABLE_SENSITIVE_DATA=true # OpenAI specific variables # ========================== OPENAI_API_KEY="..." -OPENAI_RESPONSES_MODEL="gpt-4o-2024-08-06" OPENAI_CHAT_MODEL="gpt-4o-2024-08-06" +OPENAI_CHAT_COMPLETION_MODEL="gpt-4o-2024-08-06" # Azure AI Foundry specific variables # ==================================== diff --git a/python/samples/03-workflows/README.md b/python/samples/03-workflows/README.md index 8d95c90a59..1fd5ab01fc 100644 --- a/python/samples/03-workflows/README.md +++ b/python/samples/03-workflows/README.md @@ -115,7 +115,9 @@ Orchestration-focused samples (Sequential, Concurrent, Handoff, GroupChat, Magen | Sample | File | Concepts | | -------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | | State with Agents | [state-management/state_with_agents.py](./state-management/state_with_agents.py) | Store in state once and later reuse across agents | -| Workflow Kwargs (Custom Context) | [state-management/workflow_kwargs.py](./state-management/workflow_kwargs.py) | Pass custom context (data, user tokens) via kwargs to `@tool` tools | +| Workflow Kwargs - Global Context | [state-management/workflow_kwargs_global.py](./state-management/workflow_kwargs_global.py) | Pass custom context (data, user tokens) via kwargs to `@tool` tools in all agents | +| Workflow Kwargs - Per Agent | [state-management/workflow_kwargs_per_agent.py](./state-management/workflow_kwargs_per_agent.py) | Pass custom context (data, user tokens) via kwargs to `@tool` tools in individual agents | + ### visualization diff --git a/python/samples/05-end-to-end/m365-agent/.env.example b/python/samples/05-end-to-end/m365-agent/.env.example index 100c2bf69d..82a7bb4575 100644 --- a/python/samples/05-end-to-end/m365-agent/.env.example +++ b/python/samples/05-end-to-end/m365-agent/.env.example @@ -1,6 +1,6 @@ # OpenAI Configuration OPENAI_API_KEY= -OPENAI_CHAT_MODEL= +OPENAI_CHAT_COMPLETION_MODEL= # Agent 365 Agentic Authentication Configuration USE_ANONYMOUS_MODE= diff --git a/python/samples/05-end-to-end/m365-agent/README.md b/python/samples/05-end-to-end/m365-agent/README.md index ded1c63f09..ec9da47331 100644 --- a/python/samples/05-end-to-end/m365-agent/README.md +++ b/python/samples/05-end-to-end/m365-agent/README.md @@ -21,7 +21,7 @@ export USE_ANONYMOUS_MODE=True # set to false if using auth # OpenAI export OPENAI_API_KEY="..." -export OPENAI_CHAT_MODEL="..." +export OPENAI_CHAT_COMPLETION_MODEL="..." ``` ## Installing Dependencies diff --git a/python/samples/README.md b/python/samples/README.md index fc55a4157e..fb9cec8992 100644 --- a/python/samples/README.md +++ b/python/samples/README.md @@ -92,10 +92,10 @@ variable. | --- | --- | --- | --- | | `agent-framework-anthropic` | `AnthropicClient` | `ANTHROPIC_API_KEY` | `sk-ant-api03-...` | | `agent-framework-anthropic` | `AnthropicClient` | `ANTHROPIC_CHAT_MODEL` | `claude-sonnet-4-5-20250929` | -| `agent-framework-azure-ai` | `AzureAIInferenceEmbeddingClient` | `AZURE_AI_INFERENCE_ENDPOINT` | `https://my-endpoint.inference.ai.azure.com` | -| `agent-framework-azure-ai` | `AzureAIInferenceEmbeddingClient` | `AZURE_AI_INFERENCE_API_KEY` | `env-key` | -| `agent-framework-azure-ai` | `AzureAIInferenceEmbeddingClient` | `AZURE_AI_INFERENCE_EMBEDDING_MODEL` | `text-embedding-3-small` | -| `agent-framework-azure-ai` | `AzureAIInferenceEmbeddingClient` | `AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL` | `Cohere-embed-v3-english` | +| `agent-framework-foundry` | `FoundryEmbeddingClient` | `FOUNDRY_MODELS_ENDPOINT` | `https://my-endpoint.inference.ai.azure.com` | +| `agent-framework-foundry` | `FoundryEmbeddingClient` | `FOUNDRY_MODELS_API_KEY` | `env-key` | +| `agent-framework-foundry` | `FoundryEmbeddingClient` | `FOUNDRY_EMBEDDING_MODEL` | `text-embedding-3-small` | +| `agent-framework-foundry` | `FoundryEmbeddingClient` | `FOUNDRY_IMAGE_EMBEDDING_MODEL` | `Cohere-embed-v3-english` | | `agent-framework-azure-ai-search` | `AzureAISearchContextProvider` | `AZURE_SEARCH_ENDPOINT` | `https://my-search.search.windows.net` | | `agent-framework-azure-ai-search` | `AzureAISearchContextProvider` | `AZURE_SEARCH_API_KEY` | `search-key` | | `agent-framework-azure-ai-search` | `AzureAISearchContextProvider` | `AZURE_SEARCH_INDEX_NAME` | `hotels-index` | @@ -144,8 +144,8 @@ variable. | `agent-framework-ollama` | `OllamaChatClient` | `OLLAMA_MODEL` | `llama3.1:8b` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `OPENAI_API_KEY` | `sk-proj-...` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `OPENAI_MODEL` | `gpt-4o-mini` | -| `agent-framework-openai` | `OpenAIChatClient` | `OPENAI_RESPONSES_MODEL` | `gpt-4.1-mini` | -| `agent-framework-openai` | `OpenAIChatCompletionClient` | `OPENAI_CHAT_MODEL` | `gpt-4o` | +| `agent-framework-openai` | `OpenAIChatClient` | `OPENAI_CHAT_MODEL` | `gpt-4.1-mini` | +| `agent-framework-openai` | `OpenAIChatCompletionClient` | `OPENAI_CHAT_COMPLETION_MODEL` | `gpt-4o` | | `agent-framework-openai` | `OpenAIEmbeddingClient` | `OPENAI_EMBEDDING_MODEL` | `text-embedding-3-small` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `OPENAI_BASE_URL` | `https://api.openai.com/v1/` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `OPENAI_ORG_ID` | `org_123456789` | @@ -154,8 +154,8 @@ variable. | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `AZURE_OPENAI_API_VERSION` | `2024-10-21` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `AZURE_OPENAI_BASE_URL` | `https://my-resource.openai.azure.com/openai/v1/` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `AZURE_OPENAI_MODEL` | `gpt-4o` | -| `agent-framework-openai` | `OpenAIChatClient` | `AZURE_OPENAI_RESPONSES_MODEL` | `gpt-4.1` | -| `agent-framework-openai` | `OpenAIChatCompletionClient` | `AZURE_OPENAI_CHAT_MODEL` | `gpt-4o-mini` | +| `agent-framework-openai` | `OpenAIChatClient` | `AZURE_OPENAI_CHAT_MODEL` | `gpt-4.1` | +| `agent-framework-openai` | `OpenAIChatCompletionClient` | `AZURE_OPENAI_CHAT_COMPLETION_MODEL` | `gpt-4o-mini` | | `agent-framework-openai` | `OpenAIEmbeddingClient` | `AZURE_OPENAI_EMBEDDING_MODEL` | `text-embedding-3-large` | | `agent-framework-openai` | `OpenAIChatClient / OpenAIChatCompletionClient / OpenAIEmbeddingClient` | `AZURE_OPENAI_RESOURCE_URL` | `https://cognitiveservices.azure.com/` | diff --git a/python/uv.lock b/python/uv.lock index 23d3297009..944792ff24 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -30,7 +30,6 @@ members = [ "agent-framework-a2a", "agent-framework-ag-ui", "agent-framework-anthropic", - "agent-framework-azure-ai", "agent-framework-azure-ai-search", "agent-framework-azure-cosmos", "agent-framework-azurefunctions", @@ -203,31 +202,6 @@ requires-dist = [ { name = "anthropic", specifier = ">=0.80.0,<0.80.1" }, ] -[[package]] -name = "agent-framework-azure-ai" -version = "1.0.0rc6" -source = { editable = "packages/azure-ai" } -dependencies = [ - { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "agent-framework-openai", 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-inference", 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'" }, - { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, -] - -[package.metadata] -requires-dist = [ - { name = "agent-framework-core", editable = "packages/core" }, - { name = "agent-framework-openai", editable = "packages/openai" }, - { name = "aiohttp", specifier = ">=3.7.0,<4" }, - { name = "azure-ai-agents", specifier = ">=1.2.0b5,<1.2.0b6" }, - { name = "azure-ai-inference", specifier = ">=1.0.0b9,<1.0.0b10" }, - { name = "azure-ai-projects", specifier = ">=2.0.0,<3.0" }, - { name = "azure-identity", specifier = ">=1,<2" }, -] - [[package]] name = "agent-framework-azure-ai-search" version = "1.0.0b260330" @@ -358,8 +332,8 @@ all = [ { name = "agent-framework-a2a", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-ag-ui", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-anthropic", 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-azure-ai-search", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-azure-cosmos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-azurefunctions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-bedrock", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-chatkit", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -386,8 +360,8 @@ requires-dist = [ { name = "agent-framework-a2a", marker = "extra == 'all'", editable = "packages/a2a" }, { name = "agent-framework-ag-ui", marker = "extra == 'all'", editable = "packages/ag-ui" }, { name = "agent-framework-anthropic", marker = "extra == 'all'", editable = "packages/anthropic" }, - { name = "agent-framework-azure-ai", marker = "extra == 'all'", editable = "packages/azure-ai" }, { name = "agent-framework-azure-ai-search", marker = "extra == 'all'", editable = "packages/azure-ai-search" }, + { name = "agent-framework-azure-cosmos", marker = "extra == 'all'", editable = "packages/azure-cosmos" }, { name = "agent-framework-azurefunctions", marker = "extra == 'all'", editable = "packages/azurefunctions" }, { name = "agent-framework-bedrock", marker = "extra == 'all'", editable = "packages/bedrock" }, { name = "agent-framework-chatkit", marker = "extra == 'all'", editable = "packages/chatkit" }, @@ -511,6 +485,7 @@ source = { editable = "packages/foundry" } dependencies = [ { name = "agent-framework-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-ai-inference", 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'" }, ] @@ -518,6 +493,7 @@ dependencies = [ requires-dist = [ { name = "agent-framework-core", editable = "packages/core" }, { name = "agent-framework-openai", editable = "packages/openai" }, + { name = "azure-ai-inference", specifier = ">=1.0.0b9,<1.0.0b10" }, { name = "azure-ai-projects", specifier = ">=2.0.0,<3.0" }, ] @@ -1020,20 +996,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] -[[package]] -name = "azure-ai-agents" -version = "1.2.0b5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, - { name = "isodate", 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/ed/57/8adeed578fa8984856c67b4229e93a58e3f6024417d448d0037aafa4ee9b/azure_ai_agents-1.2.0b5.tar.gz", hash = "sha256:1a16ef3f305898aac552269f01536c34a00473dedee0bca731a21fdb739ff9d5", size = 394876, upload-time = "2025-09-30T01:55:02.328Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/6d/15070d23d7a94833a210da09d5d7ed3c24838bb84f0463895e5d159f1695/azure_ai_agents-1.2.0b5-py3-none-any.whl", hash = "sha256:257d0d24a6bf13eed4819cfa5c12fb222e5908deafb3cbfd5711d3a511cc4e88", size = 217948, upload-time = "2025-09-30T01:55:04.155Z" }, -] - [[package]] name = "azure-ai-inference" version = "1.0.0b9"