From d3ec77fad4200675d7e537f0f04a523b6467a4e6 Mon Sep 17 00:00:00 2001 From: Harsh-Microsoft Date: Fri, 13 Mar 2026 11:15:26 +0530 Subject: [PATCH 01/10] add and update virtual machine size parameter and update documentation for VM sizing --- docs/CustomizingAzdParameters.md | 1 + docs/TroubleShootingSteps.md | 4 +-- infra/main.bicep | 4 ++- infra/main.json | 44 +++++++++++++++++------------- infra/main.waf.parameters.json | 3 ++ infra/main_custom.bicep | 4 ++- infra/modules/virtualNetwork.bicep | 4 +-- 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index 3438096ca..c88dedad3 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -29,6 +29,7 @@ By default this template will use the environment name as the prefix to prevent | `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Set this if you want to reuse an existing Log Analytics Workspace instead of creating a new one. | | `AZURE_ENV_VM_ADMIN_USERNAME` | string | `take(newGuid(), 20)` | The administrator username for the virtual machine. | | `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `newGuid()` | The administrator password for the virtual machine. | +| `AZURE_ENV_VM_SIZE` | string | `Standard_D2s_v5` | The size of the virtual machine deployed with private networking. | | `AZURE_ENV_CONTAINER_REGISTRY_ENDPOINT` | string | `` | Sets container registry used by backend, frontend and Mcp containers. | --- diff --git a/docs/TroubleShootingSteps.md b/docs/TroubleShootingSteps.md index 99c9172d0..0f6e754d4 100644 --- a/docs/TroubleShootingSteps.md +++ b/docs/TroubleShootingSteps.md @@ -61,7 +61,7 @@ Use these as quick reference guides to unblock your deployments. | **ServiceQuotaExceeded** | Free tier service quota limit reached for Azure AI Search | This error occurs when you attempt to deploy an Azure AI Search service but have already reached the **free tier quota limit** for your subscription. Each Azure subscription is limited to **one free tier Search service**.

**Example error message:**
`ServiceQuotaExceeded: Operation would exceed 'free' tier service quota. You are using 1 out of 1 'free' tier service quota.`

**Common causes:**

**Resolution:**

**Reference:**
| | **InsufficientQuota** | Not enough quota available in subscription | | | **MaxNumberOfRegionalEnvironmentsInSubExceeded** | Maximum Container App Environments limit reached for region |This error occurs when you attempt to create more **Azure Container App Environments** than the regional quota limit allows for your subscription. Each Azure region has a specific limit on the number of Container App Environments that can be created per subscription.

**Common Causes:**

**Resolution:**

**Reference:**
| -| **SkuNotAvailable** | Requested SKU not available in selected location or zone | You receive this error in the following scenarios:
| +| **SkuNotAvailable** | Requested SKU not available in selected location or zone | This error occurs when the resource SKU you've selected isn't available in the target location or availability zone.

**For this deployment:**
This solution uses **`Standard_D2s_v5`** VM size for the jumpbox when `enablePrivateNetworking=true`. This VM size is widely available, but occasional capacity constraints may occur.

**Common causes:**

**Resolution:**

**Reference:**
| | **Conflict - No available instances to satisfy this request** | Azure App Service has insufficient capacity in the region | This error occurs when Azure App Service doesn't have enough available compute instances in the selected region to provision or scale your app.

**Common Causes:**

**Resolution:**

**Reference:** [Azure App Service Plans](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans) | -------------------------------- @@ -121,7 +121,7 @@ Use these as quick reference guides to unblock your deployments. |-----------------|-------------|------------------| | **NetcfgSubnetRangeOutsideVnet** | Subnet IP range outside virtual network address space | | | **DisableExport_PublicNetworkAccessMustBeDisabled** | Public network access must be disabled when export is disabled | | -| **VMSizeIsNotPermittedToEnableAcceleratedNetworking** | VM size does not support accelerated networking | This error occurs when you attempt to enable accelerated networking on a VM size that does not support it.

**How to reproduce:**

**Resolution:**
| +| **VMSizeIsNotPermittedToEnableAcceleratedNetworking** | VM size does not support accelerated networking | This error occurs when you attempt to enable accelerated networking on a VM size that does not support it.

**Note:** This solution uses `Standard_D2s_v5` which **fully supports accelerated networking**, so this error should not occur with the default configuration.

**How to reproduce:**

**Resolution:**
| **NetworkSecurityGroupNotCompliantForAzureBastionSubnet** / **SecurityRuleParameterContainsUnsupportedValue** | NSG rules blocking required Azure Bastion ports | This error occurs when the Network Security Group (NSG) attached to `AzureBastionSubnet` explicitly denies inbound TCP ports 443 and/or 4443, which Azure Bastion requires for management and tunneling.

**How to reproduce:**

**Resolution:**
| | **RouteTableCannotBeAttachedForAzureBastionSubnet** | Route table attached to Azure Bastion subnet | This error occurs because Azure Bastion subnet (`AzureBastionSubnet`) has a platform restriction that prevents route tables from being attached.

**How to reproduce:**

**Resolution:**
| diff --git a/infra/main.bicep b/infra/main.bicep index 3e48d4742..0f5dfc7cd 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -132,6 +132,9 @@ param virtualMachineAdminUsername string? @secure() param virtualMachineAdminPassword string? +@description('Optional. The size of the virtual machine. Defaults to Standard_D2s_v5.') +param virtualMachineSize string = 'Standard_D2s_v5' + // These parameters are changed for testing - please reset as part of publication @description('Optional. The Container Registry hostname where the docker images for the backend are located.') @@ -604,7 +607,6 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr var virtualMachineResourceName = 'vm-${solutionSuffix}' var virtualMachineAvailabilityZone = 1 -var virtualMachineSize = 'Standard_D2s_v4' module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (enablePrivateNetworking) { name: take('avm.res.compute.virtual-machine.${virtualMachineResourceName}', 64) params: { diff --git a/infra/main.json b/infra/main.json index 7c6043215..6f5d8eff3 100644 --- a/infra/main.json +++ b/infra/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "17476534152468179054" + "version": "0.41.2.15936", + "templateHash": "576514245908514889" }, "name": "Multi-Agent Custom Automation Engine", "description": "This module contains the resources required to deploy the [Multi-Agent Custom Automation Engine solution accelerator](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) for both Sandbox environments and WAF aligned environments.\n\n> **Note:** This module is not intended for broad, generic use, as it was designed by the Commercial Solution Areas CTO team, as a Microsoft Solution Accelerator. Feature requests and bug fix requests are welcome if they support the needs of this organization but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. This module will likely be updated to leverage AVM resource modules in the future. This may result in breaking changes in upcoming versions when these features are implemented.\n" @@ -240,6 +240,13 @@ "description": "Optional. The password for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true." } }, + "virtualMachineSize": { + "type": "string", + "defaultValue": "Standard_D2s_v5", + "metadata": { + "description": "Optional. The size of the virtual machine. Defaults to Standard_D2s_v5." + } + }, "backendContainerRegistryHostname": { "type": "string", "defaultValue": "biabcontainerreg.azurecr.io", @@ -415,7 +422,6 @@ "proximityPlacementGroupResourceName": "[format('ppg-{0}', variables('solutionSuffix'))]", "virtualMachineResourceName": "[format('vm-{0}', variables('solutionSuffix'))]", "virtualMachineAvailabilityZone": 1, - "virtualMachineSize": "Standard_D2s_v4", "keyVaultPrivateDNSZone": "[format('privatelink.{0}', if(equals(toLower(environment().name), 'azureusgovernment'), 'vaultcore.usgovcloudapi.net', 'vaultcore.azure.net'))]", "privateDnsZones": [ "privatelink.cognitiveservices.azure.com", @@ -4921,8 +4927,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16969845928384020185" + "version": "0.41.2.15936", + "templateHash": "8667922205584012198" } }, "definitions": { @@ -10446,7 +10452,7 @@ "intent": { "value": { "vmSizes": [ - "[variables('virtualMachineSize')]" + "[parameters('virtualMachineSize')]" ] } } @@ -10804,7 +10810,7 @@ "value": "Windows" }, "vmSize": { - "value": "[variables('virtualMachineSize')]" + "value": "[parameters('virtualMachineSize')]" }, "adminUsername": { "value": "[coalesce(parameters('virtualMachineAdminUsername'), 'JumpboxAdminUser')]" @@ -22453,8 +22459,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "8742987061721021759" + "version": "0.41.2.15936", + "templateHash": "8365054813170845685" } }, "definitions": { @@ -25440,9 +25446,9 @@ } }, "dependsOn": [ - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "logAnalyticsWorkspace", "userAssignedIdentity", "virtualNetwork" @@ -25481,8 +25487,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7507285802464480889" + "version": "0.41.2.15936", + "templateHash": "5789718034225488560" } }, "parameters": { @@ -34461,8 +34467,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "8640881069237947782" + "version": "0.41.2.15936", + "templateHash": "14525082674956141939" } }, "definitions": { @@ -35474,8 +35480,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "10706743168754451638" + "version": "0.41.2.15936", + "templateHash": "1185169597469996118" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting." @@ -44644,8 +44650,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15348022841521786626" + "version": "0.41.2.15936", + "templateHash": "8488390916703184584" } }, "parameters": { diff --git a/infra/main.waf.parameters.json b/infra/main.waf.parameters.json index b784dae71..dcdfd1e23 100644 --- a/infra/main.waf.parameters.json +++ b/infra/main.waf.parameters.json @@ -74,6 +74,9 @@ "virtualMachineAdminPassword": { "value": "${AZURE_ENV_VM_ADMIN_PASSWORD}" }, + "virtualMachineSize": { + "value": "${AZURE_ENV_VM_SIZE}" + }, "existingLogAnalyticsWorkspaceId": { "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}" }, diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 1aeebeea4..d5fdef0ad 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -131,6 +131,9 @@ param virtualMachineAdminUsername string? @description('Optional. The password for the administrator account of the virtual machine. Allows to customize credentials if `enablePrivateNetworking` is set to true.') @secure() param virtualMachineAdminPassword string? + +@description('Optional. The size of the virtual machine. Defaults to Standard_D2s_v5.') +param virtualMachineSize string = 'Standard_D2s_v5' // These parameters are changed for testing - please reset as part of publication @description('Optional. The Container Registry hostname where the docker images for the backend are located.') @@ -603,7 +606,6 @@ module proximityPlacementGroup 'br/public:avm/res/compute/proximity-placement-gr var virtualMachineResourceName = 'vm-${solutionSuffix}' var virtualMachineAvailabilityZone = 1 -var virtualMachineSize = 'Standard_D2s_v4' module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.17.0' = if (enablePrivateNetworking) { name: take('avm.res.compute.virtual-machine.${virtualMachineResourceName}', 64) params: { diff --git a/infra/modules/virtualNetwork.bicep b/infra/modules/virtualNetwork.bicep index 42d2aad5d..6e54dd333 100644 --- a/infra/modules/virtualNetwork.bicep +++ b/infra/modules/virtualNetwork.bicep @@ -197,8 +197,8 @@ param resourceSuffix string // 1 B-series VMs (like Standard_B2ms) do not support accelerated networking. // 2 Pick a VM size that does support accelerated networking (the usual jump-box candidates): // Standard_DS2_v2 (2 vCPU, 7 GiB RAM, Premium SSD) // The most broadly available (it’s a legacy SKU supported in virtually every region). -// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // next most common -// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // Newest, so fewer regions availabl +// Standard_D2s_v5 (2 vCPU, 8 GiB RAM, Premium SSD) // Current generation with better price-to-performance and wide availability +// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // Previous generation // Subnet Classless Inter-Doman Routing (CIDR) Sizing Reference Table (Best Practices) // | CIDR | # of Addresses | # of /24s | Notes | From d46364fdda9861571b5ba401fabcfadf60499a9e Mon Sep 17 00:00:00 2001 From: Harsh-Microsoft Date: Fri, 13 Mar 2026 12:34:42 +0530 Subject: [PATCH 02/10] docs: update troubleshooting steps for SKU availability and service quota errors --- docs/TroubleShootingSteps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TroubleShootingSteps.md b/docs/TroubleShootingSteps.md index 0f6e754d4..3b83d504d 100644 --- a/docs/TroubleShootingSteps.md +++ b/docs/TroubleShootingSteps.md @@ -61,7 +61,7 @@ Use these as quick reference guides to unblock your deployments. | **ServiceQuotaExceeded** | Free tier service quota limit reached for Azure AI Search | This error occurs when you attempt to deploy an Azure AI Search service but have already reached the **free tier quota limit** for your subscription. Each Azure subscription is limited to **one free tier Search service**.

**Example error message:**
`ServiceQuotaExceeded: Operation would exceed 'free' tier service quota. You are using 1 out of 1 'free' tier service quota.`

**Common causes:**
  • Already have a free tier Azure AI Search service in the subscription
  • Previous deployment created a free tier Search service that wasn't deleted
  • Attempting to deploy multiple environments with free tier Search services

**Resolution:**
  • **Option 1: Delete existing free tier Search service:**
    `az search service list --query "[?sku.name=='free']" -o table`
    `az search service delete --name --resource-group --yes`
  • **Option 2: Upgrade to a paid SKU:**
    Modify your Bicep/ARM template to use `basic`, `standard`, or higher SKU instead of `free`
  • **Option 3: Use existing Search service:**
    Reference the existing free tier Search service in your deployment instead of creating a new one
  • **Request quota increase:**
    Submit a support request with issue type 'Service and subscription limits (quota)' and quota type 'Search' via [Azure Quota Request](https://aka.ms/AddQuotaSubscription)

**Reference:**
  • [Azure AI Search service limits](https://learn.microsoft.com/en-us/azure/search/search-limits-quotas-capacity)
  • [Azure AI Search pricing tiers](https://learn.microsoft.com/en-us/azure/search/search-sku-tier)
| | **InsufficientQuota** | Not enough quota available in subscription |
  • Check if you have sufficient quota available in your subscription before deployment
  • To verify, refer to the [quota_check](../docs/quota_check.md) file for details
| | **MaxNumberOfRegionalEnvironmentsInSubExceeded** | Maximum Container App Environments limit reached for region |This error occurs when you attempt to create more **Azure Container App Environments** than the regional quota limit allows for your subscription. Each Azure region has a specific limit on the number of Container App Environments that can be created per subscription.

**Common Causes:**
  • Deploying to regions with low quota limits (e.g., Sweden Central allows only 1 environment)
  • Multiple deployments without cleaning up previous environments
  • Exceeding the standard limit of 15 environments in most major regions

**Resolution:**
  • **Delete unused environments** in the target region, OR
  • **Deploy to a different region** with available capacity, OR
  • **Request quota increase** via [Azure Support](https://go.microsoft.com/fwlink/?linkid=2208872)

**Reference:**
  • [Azure Container Apps quotas](https://learn.microsoft.com/en-us/azure/container-apps/quotas)
  • [Azure subscription and service limits](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits)
| -| **SkuNotAvailable** | Requested SKU not available in selected location or zone | This error occurs when the resource SKU you've selected isn't available in the target location or availability zone.

**For this deployment:**
This solution uses **`Standard_D2s_v5`** VM size for the jumpbox when `enablePrivateNetworking=true`. This VM size is widely available, but occasional capacity constraints may occur.

**Common causes:**
  • VM size not available in selected region or availability zone
  • Temporary capacity constraints in the region
  • Deploying Azure Spot VMs with insufficient spot capacity

**Resolution:**
  • **Check VM size availability in your region:**
    `az vm list-skus --location --size Standard_D2s_v5 --output table`
  • **Try alternative VM sizes** (modify `virtualMachineSize` in `main.bicep`):
    - `Standard_D2s_v5` (current - 2 vCPU, 8 GiB RAM, Premium SSD)
    - `Standard_D2s_v4` (previous gen - 2 vCPU, 8 GiB RAM, Premium SSD)
    - `Standard_DS2_v2` (older but most widely available - 2 vCPU, 7 GiB RAM)
    - `Standard_D4s_v5` (if more capacity needed - 4 vCPU, 16 GiB RAM)
  • **Deploy to a different region** with better availability
  • **Remove availability zone constraint** if acceptable (modify `virtualMachineAvailabilityZone` in `main.bicep`)
  • **For Spot VMs:** Check spot pricing/availability: `az vm list-skus --location --all --output table`

**Reference:**
  • [Azure VM sizes documentation](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes)
| +| **SkuNotAvailable** | Requested SKU not available in selected location or zone | This error occurs when the resource SKU you've selected (such as VM size) isn't available for the target location or availability zone.

**In this deployment**, the jumpbox VM defaults to `Standard_D2s_v5`. While this size is available in most regions, certain regions or zones may not support it.

**Resolution:**
  • **Check SKU availability** for your target region:
    `az vm list-skus --location --size Standard_D2s --output table`
  • **Override the VM size** if the default isn't available in your region:
    `azd env set AZURE_ENV_VM_SIZE Standard_D2s_v4`
  • **Recommended alternatives** (all support accelerated networking + Premium SSD):
    - `Standard_D2s_v4` — previous gen, identical pricing
    - `Standard_D2as_v5` — AMD-based, similar pricing
    - `Standard_D2s_v3` — older gen, widely available
  • **Avoid A-series VMs** (e.g., `Standard_A2m_v2`) — they do not support accelerated networking or Premium SSD, which are required by this deployment

**Reference:**
  • [Resolve errors for SKU not available](https://learn.microsoft.com/en-us/azure/azure-resource-manager/troubleshooting/error-sku-not-available)
  • [Azure VM sizes - Dsv5 series](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dsv5-series)
| | **Conflict - No available instances to satisfy this request** | Azure App Service has insufficient capacity in the region | This error occurs when Azure App Service doesn't have enough available compute instances in the selected region to provision or scale your app.

**Common Causes:**
  • High demand in the selected region (e.g., East US, West Europe)
  • Specific SKUs experiencing capacity constraints (Free, Shared, or certain Premium tiers)
  • Multiple rapid deployments in the same region

**Resolution:**
  • **Wait and Retry** (15-30 minutes): `azd up`
  • **Deploy to a New Resource Group** (Recommended for urgent cases):
    ```
    azd down --force --purge
    azd up
    ```
  • **Try a Different Region:**
    Update region in `main.bicep` or `azure.yaml` to a less congested region (e.g., `westus2`, `centralus`, `northeurope`)
  • **Use a Different SKU/Tier:**
    If using Free/Shared tier, upgrade to Basic or Standard
    Check SKU availability: `az appservice list-locations --sku `

**Reference:** [Azure App Service Plans](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans) | -------------------------------- From 40b8ccadee9a49aefa955a2f8bacb5649294d4db Mon Sep 17 00:00:00 2001 From: Dhruvkumar-Microsoft Date: Tue, 17 Mar 2026 16:20:31 +0530 Subject: [PATCH 03/10] updated the unit testcases --- src/backend/requirements.txt | 5 + src/tests/backend/conftest.py | 47 ++++- .../v4/callbacks/test_response_handlers.py | 22 ++- .../magentic_agents/common/test_lifecycle.py | 6 +- .../v4/magentic_agents/test_foundry_agent.py | 68 +++---- .../test_human_approval_manager.py | 13 +- .../test_orchestration_manager.py | 173 +++++++++++------- 7 files changed, 216 insertions(+), 118 deletions(-) diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index b7c42b455..0bae19a54 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -26,6 +26,11 @@ opentelemetry-exporter-otlp-proto-grpc # Date and internationalization babel>=2.9.0 +# Agent Framework +agent-framework-core==1.0.0rc4 +agent-framework-azure-ai==1.0.0rc4 +agent-framework-orchestrations==1.0.0b260311 + # Testing tools pytest>=8.2,<9 # Compatible version for pytest-asyncio pytest-asyncio==0.24.0 diff --git a/src/tests/backend/conftest.py b/src/tests/backend/conftest.py index d5001295f..2a43301b4 100644 --- a/src/tests/backend/conftest.py +++ b/src/tests/backend/conftest.py @@ -51,18 +51,26 @@ def _setup_agent_framework_mock(): # Names used as base classes or in Union type hints MUST be real classes # to avoid SyntaxError from typing module's forward reference evaluation. _class_names = [ - 'AgentResponse', 'AgentResponseUpdate', 'AgentRunUpdateEvent', - 'AgentThread', 'BaseAgent', 'ChatAgent', 'ChatMessage', + 'Agent', 'AgentResponse', 'AgentResponseUpdate', 'AgentRunUpdateEvent', + 'AgentSession', 'AgentThread', 'BaseAgent', 'ChatAgent', 'ChatMessage', 'ChatOptions', 'Content', 'ExecutorCompletedEvent', 'GroupChatRequestSentEvent', 'GroupChatResponseReceivedEvent', 'HostedCodeInterpreterTool', 'HostedMCPTool', 'InMemoryCheckpointStorage', 'MCPStreamableHTTPTool', 'MagenticBuilder', 'MagenticOrchestratorEvent', - 'MagenticProgressLedger', 'Role', 'UsageDetails', + 'MagenticProgressLedger', 'Message', 'Role', 'UsageDetails', 'WorkflowOutputEvent', ] for name in _class_names: - setattr(mock_af, name, type(name, (), {})) + setattr(mock_af, name, type(name, (), { + '__init__': lambda self, *args, **kwargs: None, + })) + + # Sub-module: agent_framework._types + mock_af_types = ModuleType('agent_framework._types') + mock_af_types.ResponseStream = type('ResponseStream', (), {}) + mock_af._types = mock_af_types + sys.modules['agent_framework._types'] = mock_af_types # Sub-module: agent_framework.azure mock_af_azure = ModuleType('agent_framework.azure') @@ -91,6 +99,37 @@ def _setup_agent_framework_mock(): sys.modules['agent_framework._workflows'] = mock_af_workflows sys.modules['agent_framework._workflows._magentic'] = mock_af_magentic + if 'agent_framework_orchestrations' not in sys.modules: + mock_af_orch = ModuleType('agent_framework_orchestrations') + mock_af_orch.MagenticBuilder = type('MagenticBuilder', (), { + '__init__': lambda self, *args, **kwargs: None, + 'build': lambda self: Mock(), + }) + sys.modules['agent_framework_orchestrations'] = mock_af_orch + + mock_af_orch_base = ModuleType('agent_framework_orchestrations._base_group_chat_orchestrator') + for name in ['GroupChatRequestSentEvent', 'GroupChatResponseReceivedEvent']: + setattr(mock_af_orch_base, name, type(name, (), {})) + sys.modules['agent_framework_orchestrations._base_group_chat_orchestrator'] = mock_af_orch_base + + mock_af_orch_mag = ModuleType('agent_framework_orchestrations._magentic') + for name in ['MagenticContext', 'MagenticProgressLedger']: + setattr(mock_af_orch_mag, name, type(name, (), {})) + # StandardMagenticManager needs a proper __init__ that accepts args/kwargs + # because HumanApprovalMagenticManager calls super().__init__(agent, *args, **kwargs) + setattr(mock_af_orch_mag, 'StandardMagenticManager', + type('StandardMagenticManager', (), { + '__init__': lambda self, *args, **kwargs: None + })) + for name in [ + 'ORCHESTRATOR_FINAL_ANSWER_PROMPT', + 'ORCHESTRATOR_PROGRESS_LEDGER_PROMPT', + 'ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT', + 'ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT', + ]: + setattr(mock_af_orch_mag, name, 'mock_prompt_string') + sys.modules['agent_framework_orchestrations._magentic'] = mock_af_orch_mag + if 'agent_framework_azure_ai' not in sys.modules: mock_af_ai = ModuleType('agent_framework_azure_ai') mock_af_ai.AzureAIClient = type('AzureAIClient', (), {}) diff --git a/src/tests/backend/v4/callbacks/test_response_handlers.py b/src/tests/backend/v4/callbacks/test_response_handlers.py index 85a1137f9..d57747348 100644 --- a/src/tests/backend/v4/callbacks/test_response_handlers.py +++ b/src/tests/backend/v4/callbacks/test_response_handlers.py @@ -61,12 +61,19 @@ def __init__(self): self.author_name = "TestAgent" self.role = "assistant" +class MockMessage: + """Mock Message class for isinstance checks.""" + def __init__(self, text="", role="assistant", author_name=""): + self.text = text + self.author_name = author_name + self.role = role + mock_chat_message = MockChatMessage mock_agent_response_update = Mock() mock_agent_response_update.text = "Sample update text" mock_agent_response_update.contents = [] -sys.modules['agent_framework'] = Mock(ChatMessage=mock_chat_message) +sys.modules['agent_framework'] = Mock(ChatMessage=mock_chat_message, Message=MockMessage) sys.modules['agent_framework._workflows'] = Mock() sys.modules['agent_framework._workflows._magentic'] = Mock(AgentRunResponseUpdate=mock_agent_response_update) sys.modules['agent_framework.azure'] = Mock(AzureOpenAIChatClient=Mock()) @@ -388,14 +395,15 @@ def test_agent_response_callback_no_user_id(self): @patch('backend.v4.callbacks.response_handlers.asyncio.create_task') @patch('backend.v4.callbacks.response_handlers.time.time') def test_agent_response_callback_with_chat_message(self, mock_time, mock_create_task): - """Test agent_response_callback with ChatMessage object.""" + """Test agent_response_callback with Message object.""" mock_time.return_value = 1234567890.0 - # Create an instance of our MockChatMessage - mock_message = MockChatMessage() - mock_message.text = "Test message with citations [1:2|source]" - mock_message.author_name = "TestAgent" - mock_message.role = "assistant" + # Create an instance of our MockMessage (source checks isinstance(message, Message)) + mock_message = MockMessage( + text="Test message with citations [1:2|source]", + author_name="TestAgent", + role="assistant", + ) with patch('backend.v4.callbacks.response_handlers.AgentMessage') as mock_agent_message: mock_agent_msg = Mock() diff --git a/src/tests/backend/v4/magentic_agents/common/test_lifecycle.py b/src/tests/backend/v4/magentic_agents/common/test_lifecycle.py index 00c261cbd..eadb96042 100644 --- a/src/tests/backend/v4/magentic_agents/common/test_lifecycle.py +++ b/src/tests/backend/v4/magentic_agents/common/test_lifecycle.py @@ -15,6 +15,8 @@ sys.modules['azure.identity'] = Mock() sys.modules['azure.identity.aio'] = Mock() sys.modules['common'] = Mock() +sys.modules['common.config'] = Mock() +sys.modules['common.config.app_config'] = Mock(config=Mock()) sys.modules['common.database'] = Mock() sys.modules['common.database.database_base'] = Mock() sys.modules['common.models'] = Mock() @@ -327,7 +329,7 @@ def test_get_chat_client_with_existing_client(self): base = MCPEnabledBase() mock_agent = Mock() mock_chat_client = Mock() - mock_agent.chat_client = mock_chat_client + mock_agent.client = mock_chat_client base._agent = mock_agent result = base.get_chat_client() @@ -339,7 +341,7 @@ def test_get_chat_client_from_agent(self): base = MCPEnabledBase() mock_agent = Mock() mock_chat_client = Mock() - mock_agent.chat_client = mock_chat_client + mock_agent.client = mock_chat_client base._agent = mock_agent result = base.get_chat_client() diff --git a/src/tests/backend/v4/magentic_agents/test_foundry_agent.py b/src/tests/backend/v4/magentic_agents/test_foundry_agent.py index 4d2cf9dec..48593a602 100644 --- a/src/tests/backend/v4/magentic_agents/test_foundry_agent.py +++ b/src/tests/backend/v4/magentic_agents/test_foundry_agent.py @@ -45,8 +45,9 @@ sys.modules['azure.core'] = Mock() sys.modules['azure.core.exceptions'] = Mock() sys.modules['azure.identity'] = Mock() +sys.modules['azure.identity.aio'] = Mock() sys.modules['azure.cosmos'] = Mock(CosmosClient=Mock) -sys.modules['agent_framework'] = Mock(ChatAgent=Mock, ChatMessage=Mock, HostedCodeInterpreterTool=Mock, Role=Mock) +sys.modules['agent_framework'] = Mock(Agent=Mock, Message=Mock, ChatOptions=Mock, ChatMessage=Mock, Role=Mock) sys.modules['agent_framework_azure_ai'] = Mock(AzureAIClient=Mock) # Mock additional Azure modules that may be needed @@ -303,17 +304,13 @@ def test_is_azure_search_requested_no_index_name(self, mock_get_logger, mock_con assert result is False @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.HostedCodeInterpreterTool') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') - async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_config, mock_code_tool_class): - """Test _collect_tools with code interpreter enabled.""" + async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_config): + """Test _collect_tools with code interpreter enabled - now handled server-side.""" mock_logger = Mock() mock_get_logger.return_value = mock_logger - mock_code_tool = Mock() - mock_code_tool_class.return_value = mock_code_tool - agent = FoundryAgentTemplate( agent_name="TestAgent", agent_description="Test Description", @@ -329,23 +326,19 @@ async def test_collect_tools_with_code_interpreter(self, mock_get_logger, mock_c tools = await agent._collect_tools() - assert len(tools) == 1 - assert tools[0] == mock_code_tool - mock_code_tool_class.assert_called_once() - mock_logger.info.assert_any_call("Added Code Interpreter tool.") - mock_logger.info.assert_any_call("Total tools collected (MCP path): %d", 1) + # HostedCodeInterpreterTool was removed in rc4; code interpreter is now server-side + assert len(tools) == 0 + mock_logger.info.assert_any_call("Code Interpreter requested \u2014 handled server-side by AzureAIClient.") + mock_logger.info.assert_any_call("Total tools collected (MCP path): %d", 0) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.HostedCodeInterpreterTool') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') - async def test_collect_tools_code_interpreter_exception(self, mock_get_logger, mock_config, mock_code_tool_class): - """Test _collect_tools when code interpreter creation fails.""" + async def test_collect_tools_code_interpreter_server_side(self, mock_get_logger, mock_config): + """Test _collect_tools when code interpreter is enabled - handled server-side in rc4.""" mock_logger = Mock() mock_get_logger.return_value = mock_logger - mock_code_tool_class.side_effect = Exception("Code interpreter failed") - agent = FoundryAgentTemplate( agent_name="TestAgent", agent_description="Test Description", @@ -361,8 +354,9 @@ async def test_collect_tools_code_interpreter_exception(self, mock_get_logger, m tools = await agent._collect_tools() + # No tools created locally; code interpreter is handled server-side assert len(tools) == 0 - mock_logger.error.assert_called_with("Code Interpreter tool creation failed: %s", mock_code_tool_class.side_effect) + mock_logger.info.assert_any_call("Code Interpreter requested \u2014 handled server-side by AzureAIClient.") @pytest.mark.asyncio @patch('backend.v4.magentic_agents.foundry_agent.config') @@ -639,7 +633,7 @@ class SimpleCreds: # Verify error was logged (removed specific assertion due to mock corruption issues) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatAgent') + @patch('backend.v4.magentic_agents.foundry_agent.Agent') @patch('backend.v4.magentic_agents.foundry_agent.agent_registry') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') @@ -676,11 +670,11 @@ async def test_after_open_reasoning_mode_azure_search(self, mock_get_logger, moc "TestAgent", "test-index" ) - mock_logger.info.assert_any_call("Initialized ChatAgent '%s'", "TestAgent") + mock_logger.info.assert_any_call("Initialized Agent '%s'", "TestAgent") mock_registry.register_agent.assert_called_once_with(agent) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatAgent') + @patch('backend.v4.magentic_agents.foundry_agent.Agent') @patch('backend.v4.magentic_agents.foundry_agent.agent_registry') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') @@ -712,11 +706,11 @@ async def test_after_open_foundry_mode_mcp(self, mock_get_logger, mock_config, m mock_logger.info.assert_any_call("Initializing agent in Foundry mode.") mock_logger.info.assert_any_call("Initializing agent in MCP mode.") - mock_logger.info.assert_any_call("Initialized ChatAgent '%s'", "TestAgent") + mock_logger.info.assert_any_call("Initialized Agent '%s'", "TestAgent") mock_registry.register_agent.assert_called_once_with(agent) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatAgent') + @patch('backend.v4.magentic_agents.foundry_agent.Agent') @patch('backend.v4.magentic_agents.foundry_agent.agent_registry') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') @@ -745,16 +739,16 @@ async def test_after_open_azure_search_setup_failure(self, mock_get_logger, mock assert "Azure AI Search mode requested but setup failed." in str(exc_info.value) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatAgent') + @patch('backend.v4.magentic_agents.foundry_agent.Agent') @patch('backend.v4.magentic_agents.foundry_agent.agent_registry') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') async def test_after_open_chat_agent_creation_error(self, mock_get_logger, mock_config, mock_registry, mock_chat_agent_class): - """Test _after_open when ChatAgent creation fails.""" + """Test _after_open when Agent creation fails.""" mock_logger = Mock() mock_get_logger.return_value = mock_logger - mock_chat_agent_class.side_effect = Exception("ChatAgent creation failed") + mock_chat_agent_class.side_effect = Exception("Agent creation failed") agent = FoundryAgentTemplate( agent_name="TestAgent", @@ -774,11 +768,11 @@ async def test_after_open_chat_agent_creation_error(self, mock_get_logger, mock_ with pytest.raises(Exception) as exc_info: await agent._after_open() - assert "ChatAgent creation failed" in str(exc_info.value) - mock_logger.error.assert_called_with("Failed to initialize ChatAgent: %s", mock_chat_agent_class.side_effect) + assert "Agent creation failed" in str(exc_info.value) + mock_logger.error.assert_called_with("Failed to initialize Agent: %s", mock_chat_agent_class.side_effect) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatAgent') + @patch('backend.v4.magentic_agents.foundry_agent.Agent') @patch('backend.v4.magentic_agents.foundry_agent.agent_registry') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') @@ -817,11 +811,10 @@ async def test_after_open_registry_failure(self, mock_get_logger, mock_config, m ) @pytest.mark.asyncio - @patch('backend.v4.magentic_agents.foundry_agent.ChatMessage') - @patch('backend.v4.magentic_agents.foundry_agent.Role') + @patch('backend.v4.magentic_agents.foundry_agent.Message') @patch('backend.v4.magentic_agents.foundry_agent.config') @patch('backend.v4.magentic_agents.foundry_agent.logging.getLogger') - async def test_invoke_success(self, mock_get_logger, mock_config, mock_role, mock_chat_message_class): + async def test_invoke_success(self, mock_get_logger, mock_config, mock_message_class): """Test invoke method successfully streams responses.""" mock_logger = Mock() mock_get_logger.return_value = mock_logger @@ -830,15 +823,14 @@ async def test_invoke_success(self, mock_get_logger, mock_config, mock_role, moc mock_update1 = Mock() mock_update2 = Mock() - # Mock run_stream to return an async iterator - async def mock_run_stream(messages): + # Mock run to return an async iterator (source uses self._agent.run, not run_stream) + async def mock_run(messages, stream=True): yield mock_update1 yield mock_update2 - mock_agent.run_stream = mock_run_stream + mock_agent.run = mock_run mock_message = Mock() - mock_chat_message_class.return_value = mock_message - mock_role.USER = "user" + mock_message_class.return_value = mock_message agent = FoundryAgentTemplate( agent_name="TestAgent", @@ -857,7 +849,7 @@ async def mock_run_stream(messages): updates.append(update) assert updates == [mock_update1, mock_update2] - mock_chat_message_class.assert_called_once_with(role=mock_role.USER, text="Test prompt") + mock_message_class.assert_called_once_with(role="user", text="Test prompt") @pytest.mark.asyncio @patch('backend.v4.magentic_agents.foundry_agent.config') diff --git a/src/tests/backend/v4/orchestration/test_human_approval_manager.py b/src/tests/backend/v4/orchestration/test_human_approval_manager.py index 056393237..454cf98d2 100644 --- a/src/tests/backend/v4/orchestration/test_human_approval_manager.py +++ b/src/tests/backend/v4/orchestration/test_human_approval_manager.py @@ -113,7 +113,9 @@ async def prepare_final_answer(self, magentic_context): ORCHESTRATOR_PROGRESS_LEDGER_PROMPT = "Progress ledger prompt" sys.modules['agent_framework'] = Mock( - ChatMessage=MockChatMessage + ChatMessage=MockChatMessage, + AgentResponse=Mock, + Message=MockChatMessage, ) sys.modules['agent_framework._workflows'] = Mock() sys.modules['agent_framework._workflows._magentic'] = Mock( @@ -124,6 +126,15 @@ async def prepare_final_answer(self, magentic_context): ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT=ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT, ORCHESTRATOR_PROGRESS_LEDGER_PROMPT=ORCHESTRATOR_PROGRESS_LEDGER_PROMPT, ) +sys.modules['agent_framework_orchestrations'] = Mock() +sys.modules['agent_framework_orchestrations._magentic'] = Mock( + MagenticContext=MockMagenticContext, + StandardMagenticManager=MockStandardMagenticManager, + ORCHESTRATOR_FINAL_ANSWER_PROMPT=ORCHESTRATOR_FINAL_ANSWER_PROMPT, + ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT=ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT, + ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT=ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT, + ORCHESTRATOR_PROGRESS_LEDGER_PROMPT=ORCHESTRATOR_PROGRESS_LEDGER_PROMPT, +) # Mock v4.models.messages class MockWebsocketMessageType: diff --git a/src/tests/backend/v4/orchestration/test_orchestration_manager.py b/src/tests/backend/v4/orchestration/test_orchestration_manager.py index 26a81a9e9..14f0fa748 100644 --- a/src/tests/backend/v4/orchestration/test_orchestration_manager.py +++ b/src/tests/backend/v4/orchestration/test_orchestration_manager.py @@ -130,10 +130,15 @@ def assert_called_once_with(self, *args, **kwargs): class MockMagenticBuilder: """Mock MagenticBuilder.""" - def __init__(self): + def __init__(self, participants=None, manager=None, checkpoint_storage=None, + max_round_count=10, max_stall_count=0, intermediate_outputs=False, **kwargs): self._participants = {} - self._manager = None - self._storage = None + self._manager = manager + self._storage = checkpoint_storage + if participants: + for p in participants: + name = getattr(p, 'name', None) or getattr(p, 'agent_name', None) or str(p) + self._participants[name] = p def participants(self, participants_dict=None, **kwargs): if participants_dict: @@ -166,8 +171,8 @@ def build(self): _chat_history=[] ) } - # Mock async generator for run_stream - workflow.run_stream = AsyncGeneratorMock([]) + # Mock async generator for run (source uses workflow.run(task, stream=True)) + workflow.run = AsyncGeneratorMock([]) return workflow class MockInMemoryCheckpointStorage: @@ -211,11 +216,13 @@ def __init__(self): # Set up agent_framework mocks sys.modules['agent_framework_azure_ai'] = Mock(AzureAIAgentClient=Mock(), AzureAIClient=Mock()) sys.modules['agent_framework'] = Mock( - ChatAgent=Mock(return_value=Mock()), + Agent=Mock(return_value=Mock()), + AgentResponseUpdate=MockAgentRunUpdateEvent, + ChatOptions=Mock(return_value=Mock()), ChatMessage=MockChatMessage, - WorkflowOutputEvent=MockWorkflowOutputEvent, - MagenticBuilder=MockMagenticBuilder, + Message=MockChatMessage, InMemoryCheckpointStorage=MockInMemoryCheckpointStorage, + WorkflowOutputEvent=MockWorkflowOutputEvent, MagenticOrchestratorMessageEvent=MockMagenticOrchestratorMessageEvent, MagenticAgentDeltaEvent=MockMagenticAgentDeltaEvent, MagenticAgentMessageEvent=MockMagenticAgentMessageEvent, @@ -227,6 +234,23 @@ def __init__(self): ExecutorCompletedEvent=MockExecutorCompletedEvent, MagenticProgressLedger=MockMagenticProgressLedger, ) +# agent_framework_orchestrations mocks (source imports from these paths) +sys.modules['agent_framework_orchestrations'] = Mock( + MagenticBuilder=MockMagenticBuilder, +) +sys.modules['agent_framework_orchestrations._base_group_chat_orchestrator'] = Mock( + GroupChatRequestSentEvent=MockGroupChatRequestSentEvent, + GroupChatResponseReceivedEvent=MockGroupChatResponseReceivedEvent, +) +sys.modules['agent_framework_orchestrations._magentic'] = Mock( + MagenticProgressLedger=MockMagenticProgressLedger, + StandardMagenticManager=Mock(), + MagenticContext=Mock(), + ORCHESTRATOR_FINAL_ANSWER_PROMPT="Final answer prompt", + ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT="Task ledger plan prompt", + ORCHESTRATOR_TASK_LEDGER_PLAN_UPDATE_PROMPT="Task ledger plan update prompt", + ORCHESTRATOR_PROGRESS_LEDGER_PROMPT="Progress ledger prompt", +) # Mock common modules mock_config = Mock() @@ -561,30 +585,40 @@ async def test_run_orchestration_success(self): # Set up mock workflow with events mock_workflow = Mock() - # Create events matching production code isinstance() checks - mock_orchestrator_event = MockMagenticOrchestratorEvent() - mock_orchestrator_event.event_type = Mock() - mock_orchestrator_event.event_type.name = "PLAN" + # Source dispatches on event.type (string), not isinstance() for event classes + mock_orchestrator_event = Mock() + mock_orchestrator_event.type = "magentic_orchestrator" mock_orchestrator_event.data = MockChatMessage("Plan message") - mock_agent_update = MockAgentRunUpdateEvent() - mock_agent_update.executor_id = "agent_1" - mock_agent_update.data = Mock() - mock_agent_update.data.message_id = "msg_123" - mock_agent_update.data.text = "Agent streaming update" - - mock_response_event = MockGroupChatResponseReceivedEvent() - mock_response_event.round_index = 1 - mock_response_event.participant_name = "agent_1" - mock_response_event.data = MockChatMessage("Agent response") + # Output event with AgentResponseUpdate data triggers streaming callback + mock_agent_update_data = MockAgentRunUpdateEvent() + mock_agent_update_data.text = "Agent streaming update" + mock_output_event = Mock() + mock_output_event.type = "output" + mock_output_event.executor_id = "agent_1" + mock_output_event.data = mock_agent_update_data + + mock_response_data = MockGroupChatResponseReceivedEvent() + mock_response_data.round_index = 1 + mock_response_data.participant_name = "agent_1" + mock_response_data.data = MockChatMessage("Agent response") + mock_response_event = Mock() + mock_response_event.type = "group_chat" + mock_response_event.data = mock_response_data + + # Final output event + mock_final_output = Mock() + mock_final_output.type = "output" + mock_final_output.executor_id = None + mock_final_output.data = MockChatMessage("Final result") mock_events = [ mock_orchestrator_event, - mock_agent_update, + mock_output_event, mock_response_event, - MockWorkflowOutputEvent(MockChatMessage("Final result")) + mock_final_output, ] - mock_workflow.run_stream = AsyncGeneratorMock(mock_events) + mock_workflow.run = AsyncGeneratorMock(mock_events) mock_workflow.executors = { "magentic_orchestrator": Mock(_conversation=[]), "agent_1": Mock(_chat_history=[]) @@ -627,8 +661,8 @@ async def test_run_orchestration_workflow_execution_error(self): """Test run_orchestration when workflow execution fails.""" # Set up mock workflow that raises exception mock_workflow = Mock() - mock_workflow.run_stream = AsyncGeneratorMock([]) - mock_workflow.run_stream = Mock(side_effect=Exception("Workflow execution failed")) + mock_workflow.run = AsyncGeneratorMock([]) + mock_workflow.run = Mock(side_effect=Exception("Workflow execution failed")) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -662,7 +696,7 @@ async def test_run_orchestration_conversation_clearing(self): "magentic_orchestrator": mock_orchestrator_executor, "agent_1": mock_agent_executor } - mock_workflow.run_stream = AsyncGeneratorMock([]) + mock_workflow.run = AsyncGeneratorMock([]) orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -691,7 +725,7 @@ async def test_run_orchestration_clearing_with_custom_containers(self): mock_workflow.executors = { "magentic_orchestrator": mock_executor } - mock_workflow.run_stream = AsyncGeneratorMock([]) + mock_workflow.run = AsyncGeneratorMock([]) orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -718,7 +752,7 @@ async def test_run_orchestration_clearing_failure_handling(self): mock_workflow.executors = { "magentic_orchestrator": mock_executor } - mock_workflow.run_stream = AsyncGeneratorMock([]) + mock_workflow.run = AsyncGeneratorMock([]) orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -732,14 +766,14 @@ async def test_run_orchestration_clearing_failure_handling(self): ) # Verify workflow still executed - mock_workflow.run_stream.assert_called_once() + mock_workflow.run.assert_called_once() async def test_run_orchestration_event_processing_error(self): """Test handling of errors during event processing.""" # Set up workflow with events that cause processing errors mock_workflow = Mock() mock_events = [MockMagenticAgentDeltaEvent()] - mock_workflow.run_stream = AsyncGeneratorMock(mock_events) + mock_workflow.run = AsyncGeneratorMock(mock_events) mock_workflow.executors = {} # Make streaming callback raise exception @@ -781,7 +815,7 @@ def test_run_orchestration_job_id_generation(self): async def test_run_orchestration_string_input_task(self): """Test run_orchestration with string input task.""" mock_workflow = Mock() - mock_workflow.run_stream = AsyncGeneratorMock([]) + mock_workflow.run = AsyncGeneratorMock([]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -795,12 +829,12 @@ async def test_run_orchestration_string_input_task(self): ) # Verify workflow was called with the string - mock_workflow.run_stream.assert_called_once_with("Simple string task") + mock_workflow.run.assert_called_once_with("Simple string task", stream=True) async def test_run_orchestration_websocket_error_handling(self): """Test handling of WebSocket sending errors.""" mock_workflow = Mock() - mock_workflow.run_stream = AsyncGeneratorMock([]) + mock_workflow.run = AsyncGeneratorMock([]) mock_workflow.executors = {} # Make WebSocket sending fail @@ -830,42 +864,49 @@ async def test_run_orchestration_all_event_types(self): """Test processing of all event types.""" mock_workflow = Mock() - # Create events matching production code isinstance() checks - mock_orchestrator_event = MockMagenticOrchestratorEvent() - mock_orchestrator_event.event_type = Mock() - mock_orchestrator_event.event_type.name = "PLAN" + # Source dispatches on event.type (string), not isinstance() for event classes + mock_orchestrator_event = Mock() + mock_orchestrator_event.type = "magentic_orchestrator" mock_orchestrator_event.data = MockChatMessage("Plan message") - mock_agent_update = MockAgentRunUpdateEvent() - mock_agent_update.executor_id = "agent_1" - mock_agent_update.data = Mock() - mock_agent_update.data.message_id = "msg_123" - mock_agent_update.data.text = "Agent streaming update" - - mock_request_event = MockGroupChatRequestSentEvent() - mock_request_event.participant_name = "agent_1" - mock_request_event.round_index = 1 - - mock_response_event = MockGroupChatResponseReceivedEvent() - mock_response_event.round_index = 1 - mock_response_event.participant_name = "agent_1" - mock_response_event.data = MockChatMessage("Agent response") - - mock_executor_completed = MockExecutorCompletedEvent() - mock_executor_completed.executor_name = "agent_1" + # Output event with AgentResponseUpdate triggers streaming callback + mock_agent_update_data = MockAgentRunUpdateEvent() + mock_agent_update_data.text = "Agent streaming update" + mock_output_event = Mock() + mock_output_event.type = "output" + mock_output_event.executor_id = "agent_1" + mock_output_event.data = mock_agent_update_data + + mock_request_data = MockGroupChatRequestSentEvent() + mock_request_data.participant_name = "agent_1" + mock_request_data.round_index = 1 + mock_request_event = Mock() + mock_request_event.type = "group_chat" + mock_request_event.data = mock_request_data + + mock_response_data = MockGroupChatResponseReceivedEvent() + mock_response_data.round_index = 1 + mock_response_data.participant_name = "agent_1" + mock_response_data.data = MockChatMessage("Agent response") + mock_response_event = Mock() + mock_response_event.type = "group_chat" + mock_response_event.data = mock_response_data + + mock_executor_completed = Mock() + mock_executor_completed.type = "executor_completed" + mock_executor_completed.executor_id = "agent_1" # Create all possible event types events = [ mock_orchestrator_event, - mock_agent_update, + mock_output_event, mock_request_event, mock_response_event, mock_executor_completed, - MockWorkflowOutputEvent(), Mock() # Unknown event type - should be safely ignored ] - mock_workflow.run_stream = AsyncGeneratorMock(events) + mock_workflow.run = AsyncGeneratorMock(events) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -879,7 +920,7 @@ async def test_run_orchestration_all_event_types(self): input_task=input_task ) - # Verify streaming callback was called (for AgentRunUpdateEvent) + # Verify streaming callback was called (for output event with AgentResponseUpdate data) streaming_agent_response_callback.assert_called() @@ -1041,7 +1082,7 @@ async def test_workflow_output_with_list_of_chat_messages(self): ] output_event = MockWorkflowOutputEvent(messages) - mock_workflow.run_stream = AsyncGeneratorMock([output_event]) + mock_workflow.run = AsyncGeneratorMock([output_event]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -1070,7 +1111,7 @@ async def test_workflow_output_with_mixed_list(self): ] output_event = MockWorkflowOutputEvent(messages) - mock_workflow.run_stream = AsyncGeneratorMock([output_event]) + mock_workflow.run = AsyncGeneratorMock([output_event]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -1095,7 +1136,7 @@ async def test_workflow_output_with_object_with_text(self): obj_with_text.text = "Object response" output_event = MockWorkflowOutputEvent(obj_with_text) - mock_workflow.run_stream = AsyncGeneratorMock([output_event]) + mock_workflow.run = AsyncGeneratorMock([output_event]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -1117,7 +1158,7 @@ async def test_workflow_output_with_unknown_type(self): # Create object without text attribute that will be str() converted output_event = MockWorkflowOutputEvent(12345) - mock_workflow.run_stream = AsyncGeneratorMock([output_event]) + mock_workflow.run = AsyncGeneratorMock([output_event]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -1138,7 +1179,7 @@ async def test_workflow_output_with_empty_list(self): output_event = MockWorkflowOutputEvent([]) - mock_workflow.run_stream = AsyncGeneratorMock([output_event]) + mock_workflow.run = AsyncGeneratorMock([output_event]) mock_workflow.executors = {} orchestration_config.get_current_orchestration.return_value = mock_workflow @@ -1156,4 +1197,4 @@ async def test_workflow_output_with_empty_list(self): if __name__ == '__main__': - main() \ No newline at end of file + main() From 8b9615bfc2bc2fda26885fd878973b184064c926 Mon Sep 17 00:00:00 2001 From: Dhruvkumar-Microsoft Date: Tue, 17 Mar 2026 16:24:07 +0530 Subject: [PATCH 04/10] removed the version --- src/backend/requirements.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index 0bae19a54..6e4326510 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -26,10 +26,6 @@ opentelemetry-exporter-otlp-proto-grpc # Date and internationalization babel>=2.9.0 -# Agent Framework -agent-framework-core==1.0.0rc4 -agent-framework-azure-ai==1.0.0rc4 -agent-framework-orchestrations==1.0.0b260311 # Testing tools pytest>=8.2,<9 # Compatible version for pytest-asyncio From aae6a2a7b5b60f4c6723fc783da48c55004c1618 Mon Sep 17 00:00:00 2001 From: Dhruvkumar-Microsoft Date: Tue, 17 Mar 2026 16:43:53 +0530 Subject: [PATCH 05/10] updated the version in the requirements.txt --- src/backend/requirements.txt | 60 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt index 6e4326510..77a0bf5c7 100644 --- a/src/backend/requirements.txt +++ b/src/backend/requirements.txt @@ -1,34 +1,42 @@ -fastapi -uvicorn -autogen-agentchat==0.7.5 -azure-cosmos -azure-monitor-opentelemetry -azure-monitor-events-extension -azure-identity -python-dotenv -python-multipart -opentelemetry-api -opentelemetry-sdk -opentelemetry-exporter-otlp-proto-grpc -opentelemetry-instrumentation-fastapi -opentelemetry-instrumentation-openai -opentelemetry-exporter-otlp-proto-http +fastapi==0.116.1 +uvicorn==0.35.0 +azure-cosmos==4.9.0 +azure-monitor-opentelemetry==1.8.5 +azure-monitor-events-extension==0.1.0 +azure-identity==1.24.0 +python-dotenv==1.1.1 +python-multipart==0.0.22 +opentelemetry-api==1.39.0 +opentelemetry-sdk==1.39.0 +opentelemetry-exporter-otlp-proto-grpc==1.39.0 +opentelemetry-exporter-otlp-proto-http==1.39.0 +opentelemetry-instrumentation-fastapi==0.60b0 +opentelemetry-instrumentation-openai==0.46.2 -semantic-kernel[azure]==1.39.4 -azure-ai-projects==1.0.0 -openai==1.105.0 -azure-ai-inference==1.0.0b9 -azure-search-documents -azure-ai-evaluation +azure-ai-projects==2.0.0 +openai==2.16.0 +azure-ai-inference==1.0.0b9 +azure-search-documents==11.5.3 +azure-ai-evaluation==1.11.0 +azure-core==1.38.0 -opentelemetry-exporter-otlp-proto-grpc - -# Date and internationalization -babel>=2.9.0 +agent-framework-azure-ai==1.0.0rc4 +agent-framework-core==1.0.0rc4 +agent-framework-orchestrations==1.0.0b260311 +mcp==1.26.0 +werkzeug==3.1.5 +pylint-pydantic==0.3.5 +pexpect==4.9.0 +urllib3==2.6.3 +protobuf==5.29.6 +cryptography==46.0.5 +aiohttp==3.13.3 +pyasn1==0.6.2 +nltk==3.9.3 # Testing tools -pytest>=8.2,<9 # Compatible version for pytest-asyncio +pytest==8.4.1 pytest-asyncio==0.24.0 pytest-cov==5.0.0 From 52473be04f70a26578632932f7d2371d9a091000 Mon Sep 17 00:00:00 2001 From: Harsh-Microsoft Date: Fri, 20 Mar 2026 18:54:14 +0530 Subject: [PATCH 06/10] Refactor private endpoint configuration to deploy separately via aiFoundryPrivateEndpoint module --- infra/main.bicep | 64 ++-- infra/main.json | 786 +++++++++++++++++++++++++++++++++++++++- infra/main_custom.bicep | 63 ++-- 3 files changed, 857 insertions(+), 56 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index e06d90894..ee6a471e7 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -966,34 +966,48 @@ module aiFoundryAiServices 'br:mcr.microsoft.com/bicep/avm/res/cognitive-service // WAF aligned configuration for Monitoring diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' - privateEndpoints: (enablePrivateNetworking) - ? ([ - { - name: 'pep-${aiFoundryAiServicesResourceName}' - customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' - subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId - privateDnsZoneGroup: { - privateDnsZoneGroupConfigs: [ - { - name: 'ai-services-dns-zone-cognitiveservices' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId - } - { - name: 'ai-services-dns-zone-openai' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId - } - { - name: 'ai-services-dns-zone-aiservices' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId - } - ] - } - } - ]) - : [] + // Private endpoints are deployed separately via the aiFoundryPrivateEndpoint module below + privateEndpoints: [] } } +module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8.1' = if (enablePrivateNetworking && !useExistingAiFoundryAiProject) { + name: take('pep-${aiFoundryAiServicesResourceName}-deployment', 64) + params: { + name: 'pep-${aiFoundryAiServicesResourceName}' + customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' + location: azureAiServiceLocation + tags: tags + privateLinkServiceConnections: [ + { + name: 'pep-${aiFoundryAiServicesResourceName}-connection' + properties: { + privateLinkServiceId: aiFoundryAiServices!.outputs.resourceId + groupIds: ['account'] + } + } + ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'ai-services-dns-zone-cognitiveservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-openai' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-aiservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId + } + ] + } + subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId + } +} + + resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/accounts/projects@2025-06-01' existing = if (useExistingAiFoundryAiProject) { name: aiFoundryAiProjectResourceName parent: existingAiFoundryAiServices diff --git a/infra/main.json b/infra/main.json index e74942e83..6f0b87042 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.41.2.15936", - "templateHash": "7604025969554104872" + "templateHash": "4258449159176431849" }, "name": "Multi-Agent Custom Automation Engine", "description": "This module contains the resources required to deploy the [Multi-Agent Custom Automation Engine solution accelerator](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) for both Sandbox environments and WAF aligned environments.\n\n> **Note:** This module is not intended for broad, generic use, as it was designed by the Commercial Solution Areas CTO team, as a Microsoft Solution Accelerator. Feature requests and bug fix requests are welcome if they support the needs of this organization but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. This module will likely be updated to leverage AVM resource modules in the future. This may result in breaking changes in upcoming versions when these features are implemented.\n" @@ -22911,7 +22911,9 @@ }, "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]", "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", - "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-{0}', variables('aiFoundryAiServicesResourceName')), 'customNetworkInterfaceName', format('nic-{0}', variables('aiFoundryAiServicesResourceName')), 'subnetResourceId', reference('virtualNetwork').outputs.backendSubnetResourceId.value, 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'ai-services-dns-zone-cognitiveservices', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)).outputs.resourceId.value), createObject('name', 'ai-services-dns-zone-openai', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)).outputs.resourceId.value), createObject('name', 'ai-services-dns-zone-aiservices', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)).outputs.resourceId.value)))))), createObject('value', createArray()))]" + "privateEndpoints": { + "value": [] + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -25445,11 +25447,783 @@ } }, "dependsOn": [ - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "logAnalyticsWorkspace", - "userAssignedIdentity", + "userAssignedIdentity" + ] + }, + "aiFoundryPrivateEndpoint": { + "condition": "[and(parameters('enablePrivateNetworking'), not(variables('useExistingAiFoundryAiProject')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2025-04-01", + "name": "[take(format('pep-{0}-deployment', variables('aiFoundryAiServicesResourceName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('pep-{0}', variables('aiFoundryAiServicesResourceName'))]" + }, + "customNetworkInterfaceName": { + "value": "[format('nic-{0}', variables('aiFoundryAiServicesResourceName'))]" + }, + "location": { + "value": "[parameters('azureAiServiceLocation')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "privateLinkServiceConnections": { + "value": [ + { + "name": "[format('pep-{0}-connection', variables('aiFoundryAiServicesResourceName'))]", + "properties": { + "privateLinkServiceId": "[reference('aiFoundryAiServices').outputs.resourceId.value]", + "groupIds": [ + "account" + ] + } + } + ] + }, + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "name": "ai-services-dns-zone-cognitiveservices", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)).outputs.resourceId.value]" + }, + { + "name": "ai-services-dns-zone-openai", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)).outputs.resourceId.value]" + }, + { + "name": "ai-services-dns-zone-aiservices", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)).outputs.resourceId.value]" + } + ] + } + }, + "subnetResourceId": { + "value": "[reference('virtualNetwork').outputs.backendSubnetResourceId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "2541425927059591098" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.8.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "12329174801198479603" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceIds": { + "type": "array", + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + }, + "value": "[reference('privateEndpoint').networkInterfaces]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "aiFoundryAiServices", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "virtualNetwork" ] }, diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 1748394cf..0d97b2c2c 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -965,31 +965,44 @@ module aiFoundryAiServices 'br:mcr.microsoft.com/bicep/avm/res/cognitive-service // WAF aligned configuration for Monitoring diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' - privateEndpoints: (enablePrivateNetworking) - ? ([ - { - name: 'pep-${aiFoundryAiServicesResourceName}' - customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' - subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId - privateDnsZoneGroup: { - privateDnsZoneGroupConfigs: [ - { - name: 'ai-services-dns-zone-cognitiveservices' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId - } - { - name: 'ai-services-dns-zone-openai' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId - } - { - name: 'ai-services-dns-zone-aiservices' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId - } - ] - } - } - ]) - : [] + // Private endpoints are deployed separately via the aiFoundryPrivateEndpoint module below + privateEndpoints: [] + } +} + +module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8.1' = if (enablePrivateNetworking && !useExistingAiFoundryAiProject) { + name: take('pep-${aiFoundryAiServicesResourceName}-deployment', 64) + params: { + name: 'pep-${aiFoundryAiServicesResourceName}' + customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' + location: azureAiServiceLocation + tags: tags + privateLinkServiceConnections: [ + { + name: 'pep-${aiFoundryAiServicesResourceName}-connection' + properties: { + privateLinkServiceId: aiFoundryAiServices!.outputs.resourceId + groupIds: ['account'] + } + } + ] + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'ai-services-dns-zone-cognitiveservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-openai' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-aiservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId + } + ] + } + subnetResourceId: virtualNetwork!.outputs.backendSubnetResourceId } } From c49ed5d7dfd780db482c1d98c94520f96e6bf437 Mon Sep 17 00:00:00 2001 From: Harsh-Microsoft Date: Mon, 23 Mar 2026 10:53:25 +0530 Subject: [PATCH 07/10] Update location parameter in aiFoundryPrivateEndpoint module to use 'location' instead of 'azureAiServiceLocation' --- infra/main.bicep | 2 +- infra/main.json | 4 ++-- infra/main_custom.bicep | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index ee6a471e7..f67dcaa9e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -976,7 +976,7 @@ module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8. params: { name: 'pep-${aiFoundryAiServicesResourceName}' customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' - location: azureAiServiceLocation + location: location tags: tags privateLinkServiceConnections: [ { diff --git a/infra/main.json b/infra/main.json index 6f0b87042..d691bae19 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.41.2.15936", - "templateHash": "4258449159176431849" + "templateHash": "13724980897210103236" }, "name": "Multi-Agent Custom Automation Engine", "description": "This module contains the resources required to deploy the [Multi-Agent Custom Automation Engine solution accelerator](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) for both Sandbox environments and WAF aligned environments.\n\n> **Note:** This module is not intended for broad, generic use, as it was designed by the Commercial Solution Areas CTO team, as a Microsoft Solution Accelerator. Feature requests and bug fix requests are welcome if they support the needs of this organization but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. This module will likely be updated to leverage AVM resource modules in the future. This may result in breaking changes in upcoming versions when these features are implemented.\n" @@ -25469,7 +25469,7 @@ "value": "[format('nic-{0}', variables('aiFoundryAiServicesResourceName'))]" }, "location": { - "value": "[parameters('azureAiServiceLocation')]" + "value": "[parameters('location')]" }, "tags": { "value": "[parameters('tags')]" diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 0d97b2c2c..632423442 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -975,7 +975,7 @@ module aiFoundryPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.8. params: { name: 'pep-${aiFoundryAiServicesResourceName}' customNetworkInterfaceName: 'nic-${aiFoundryAiServicesResourceName}' - location: azureAiServiceLocation + location: location tags: tags privateLinkServiceConnections: [ { From df4c141dff7f5f3b8471f5c28cb339d73d00fa36 Mon Sep 17 00:00:00 2001 From: Harsh-Microsoft Date: Tue, 24 Mar 2026 14:33:15 +0530 Subject: [PATCH 08/10] Add dependency on aiFoundryPrivateEndpoint for aiFoundryAiServicesProject module --- infra/main.bicep | 1 + infra/main.json | 5 +++-- infra/main_custom.bicep | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index f67dcaa9e..998a90a5e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1015,6 +1015,7 @@ resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/account module aiFoundryAiServicesProject 'modules/ai-project.bicep' = if (!useExistingAiFoundryAiProject) { name: take('module.ai-project.${aiFoundryAiProjectResourceName}', 64) + dependsOn: enablePrivateNetworking ? [ aiFoundryPrivateEndpoint ] : [] params: { name: aiFoundryAiProjectResourceName location: azureAiServiceLocation diff --git a/infra/main.json b/infra/main.json index d691bae19..773e456d8 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.41.2.15936", - "templateHash": "13724980897210103236" + "templateHash": "13556415974563619107" }, "name": "Multi-Agent Custom Automation Engine", "description": "This module contains the resources required to deploy the [Multi-Agent Custom Automation Engine solution accelerator](https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator) for both Sandbox environments and WAF aligned environments.\n\n> **Note:** This module is not intended for broad, generic use, as it was designed by the Commercial Solution Areas CTO team, as a Microsoft Solution Accelerator. Feature requests and bug fix requests are welcome if they support the needs of this organization but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. This module will likely be updated to leverage AVM resource modules in the future. This may result in breaking changes in upcoming versions when these features are implemented.\n" @@ -26348,7 +26348,8 @@ } }, "dependsOn": [ - "aiFoundryAiServices" + "aiFoundryAiServices", + "aiFoundryPrivateEndpoint" ] }, "cosmosDb": { diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 632423442..db6a6394e 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -1013,6 +1013,7 @@ resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/account module aiFoundryAiServicesProject 'modules/ai-project.bicep' = if (!useExistingAiFoundryAiProject) { name: take('module.ai-project.${aiFoundryAiProjectResourceName}', 64) + dependsOn: enablePrivateNetworking ? [ aiFoundryPrivateEndpoint ] : [] params: { name: aiFoundryAiProjectResourceName location: azureAiServiceLocation From 40bb9aa183289cc3e451e487c27eb0d8ba4af3c2 Mon Sep 17 00:00:00 2001 From: Ajit Padhi Date: Thu, 26 Mar 2026 15:37:26 +0530 Subject: [PATCH 09/10] Updated azure.yaml file to exclude the 1.23.9 azd version --- azure.yaml | 2 +- azure_custom.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azure.yaml b/azure.yaml index 2df09f7d4..af9c81738 100644 --- a/azure.yaml +++ b/azure.yaml @@ -3,7 +3,7 @@ name: multi-agent-custom-automation-engine-solution-accelerator metadata: template: multi-agent-custom-automation-engine-solution-accelerator@1.0 requiredVersions: - azd: '>= 1.18.0' + azd: '>= 1.18.0 != 1.23.9' hooks: postdeploy: windows: diff --git a/azure_custom.yaml b/azure_custom.yaml index 9663e8f22..5e5ecc03f 100644 --- a/azure_custom.yaml +++ b/azure_custom.yaml @@ -3,7 +3,7 @@ name: multi-agent-custom-automation-engine-solution-accelerator metadata: template: multi-agent-custom-automation-engine-solution-accelerator@1.0 requiredVersions: - azd: ">=1.15.0 !=1.17.1" + azd: '>= 1.18.0 != 1.23.9' services: backend: From 76aedcab443b730a8d2f4e7990c156e8324b8314 Mon Sep 17 00:00:00 2001 From: Ayaz-Microsoft Date: Thu, 26 Mar 2026 17:34:25 +0530 Subject: [PATCH 10/10] dependabot vulnerability fix --- src/backend/pyproject.toml | 6 +- src/backend/uv.lock | 47 ++++--- src/frontend/package-lock.json | 250 ++++++++++++++++++++------------- src/frontend/package.json | 5 + src/mcp_server/pyproject.toml | 2 +- src/mcp_server/uv.lock | 23 +-- 6 files changed, 207 insertions(+), 126 deletions(-) diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index 61e34175a..6e9088c49 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "azure-monitor-events-extension==0.1.0", "azure-monitor-opentelemetry==1.8.5", "azure-search-documents==11.5.3", - "fastapi==0.116.1", + "fastapi==0.135.2", "openai==2.16.0", "opentelemetry-api==1.39.0", "opentelemetry-exporter-otlp-proto-grpc==1.39.0", @@ -39,6 +39,6 @@ dependencies = [ "protobuf==5.29.6", "cryptography==46.0.5", "aiohttp==3.13.3", - "pyasn1==0.6.2", - "nltk==3.9.3", + "pyasn1==0.6.3", + "nltk==3.9.4", ] \ No newline at end of file diff --git a/src/backend/uv.lock b/src/backend/uv.lock index e68f3e097..0ff4aab7b 100644 --- a/src/backend/uv.lock +++ b/src/backend/uv.lock @@ -180,6 +180,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -504,9 +513,9 @@ requires-dist = [ { name = "azure-monitor-opentelemetry", specifier = "==1.8.5" }, { name = "azure-search-documents", specifier = "==11.5.3" }, { name = "cryptography", specifier = "==46.0.5" }, - { name = "fastapi", specifier = "==0.116.1" }, + { name = "fastapi", specifier = "==0.135.2" }, { name = "mcp", specifier = "==1.26.0" }, - { name = "nltk", specifier = "==3.9.3" }, + { name = "nltk", specifier = "==3.9.4" }, { name = "openai", specifier = "==2.16.0" }, { name = "opentelemetry-api", specifier = "==1.39.0" }, { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = "==1.39.0" }, @@ -516,7 +525,7 @@ requires-dist = [ { name = "opentelemetry-sdk", specifier = "==1.39.0" }, { name = "pexpect", specifier = "==4.9.0" }, { name = "protobuf", specifier = "==5.29.6" }, - { name = "pyasn1", specifier = "==0.6.2" }, + { name = "pyasn1", specifier = "==0.6.3" }, { name = "pylint-pydantic", specifier = "==0.3.5" }, { name = "pytest", specifier = "==8.4.1" }, { name = "pytest-asyncio", specifier = "==0.24.0" }, @@ -872,16 +881,18 @@ wheels = [ [[package]] name = "fastapi" -version = "0.116.1" +version = "0.135.2" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/73/5903c4b13beae98618d64eb9870c3fac4f605523dd0312ca5c80dadbd5b9/fastapi-0.135.2.tar.gz", hash = "sha256:88a832095359755527b7f63bb4c6bc9edb8329a026189eed83d6c1afcf419d56", size = 395833, upload-time = "2026-03-23T14:12:41.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ea/18f6d0457f9efb2fc6fa594857f92810cadb03024975726db6546b3d6fcf/fastapi-0.135.2-py3-none-any.whl", hash = "sha256:0af0447d541867e8db2a6a25c23a8c4bd80e2394ac5529bd87501bbb9e240ca5", size = 117407, upload-time = "2026-03-23T14:12:43.284Z" }, ] [[package]] @@ -1553,7 +1564,7 @@ wheels = [ [[package]] name = "nltk" -version = "3.9.3" +version = "3.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -1561,9 +1572,9 @@ dependencies = [ { name = "regex" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/8f/915e1c12df07c70ed779d18ab83d065718a926e70d3ea33eb0cd66ffb7c0/nltk-3.9.3.tar.gz", hash = "sha256:cb5945d6424a98d694c2b9a0264519fab4363711065a46aa0ae7a2195b92e71f", size = 2923673, upload-time = "2026-02-24T12:05:53.833Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/a1/b3b4adf15585a5bc4c357adde150c01ebeeb642173ded4d871e89468767c/nltk-3.9.4.tar.gz", hash = "sha256:ed03bc098a40481310320808b2db712d95d13ca65b27372f8a403949c8b523d0", size = 2946864, upload-time = "2026-03-24T06:13:40.641Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/7e/9af5a710a1236e4772de8dfcc6af942a561327bb9f42b5b4a24d0cf100fd/nltk-3.9.3-py3-none-any.whl", hash = "sha256:60b3db6e9995b3dd976b1f0fa7dec22069b2677e759c28eb69b62ddd44870522", size = 1525385, upload-time = "2026-02-24T12:05:46.54Z" }, + { url = "https://files.pythonhosted.org/packages/9d/91/04e965f8e717ba0ab4bdca5c112deeab11c9e750d94c4d4602f050295d39/nltk-3.9.4-py3-none-any.whl", hash = "sha256:f2fa301c3a12718ce4a0e9305c5675299da5ad9e26068218b69d692fda84828f", size = 1552087, upload-time = "2026-03-24T06:13:38.47Z" }, ] [[package]] @@ -2233,11 +2244,11 @@ wheels = [ [[package]] name = "pyasn1" -version = "0.6.2" +version = "0.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, ] [[package]] @@ -2354,11 +2365,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.10.1" +version = "2.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, ] [package.optional-dependencies] @@ -2838,15 +2849,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.47.3" +version = "0.49.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/3f/507c21db33b66fb027a332f2cb3abbbe924cc3a79ced12f01ed8645955c9/starlette-0.49.1.tar.gz", hash = "sha256:481a43b71e24ed8c43b11ea02f5353d77840e01480881b8cb5a26b8cae64a8cb", size = 2654703, upload-time = "2025-10-28T17:34:10.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" }, + { url = "https://files.pythonhosted.org/packages/51/da/545b75d420bb23b5d494b0517757b351963e974e79933f01e05c929f20a6/starlette-0.49.1-py3-none-any.whl", hash = "sha256:d92ce9f07e4a3caa3ac13a79523bd18e3bc0042bb8ff2d759a8e7dd0e1859875", size = 74175, upload-time = "2025-10-28T17:34:09.13Z" }, ] [[package]] diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index ab014b2d5..e252664aa 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -36,7 +36,9 @@ "@vitest/ui": "^3.2.4", "eslint": "^8.57.1", "eslint-plugin-react": "^7.37.5", + "flatted": "^3.4.2", "jsdom": "^26.1.0", + "rollup": "^4.59.0", "typescript": "^5.8.3", "vite": "^7.1.2", "vitest": "^3.2.4" @@ -2781,9 +2783,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", - "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], @@ -2795,9 +2797,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", - "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], @@ -2809,9 +2811,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", - "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -2823,9 +2825,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", - "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], @@ -2837,9 +2839,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", - "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], @@ -2851,9 +2853,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", - "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], @@ -2865,9 +2867,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", - "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], @@ -2879,9 +2881,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", - "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], @@ -2893,9 +2895,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", - "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], @@ -2907,9 +2909,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", - "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], @@ -2920,10 +2922,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", - "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], @@ -2935,9 +2951,23 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", - "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], @@ -2949,9 +2979,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", - "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], @@ -2963,9 +2993,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", - "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], @@ -2977,9 +3007,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", - "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], @@ -3004,9 +3034,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", - "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], @@ -3017,10 +3047,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", - "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], @@ -3032,9 +3076,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", - "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], @@ -3046,9 +3090,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", - "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], @@ -3059,10 +3103,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", - "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], @@ -5324,9 +5382,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -7679,9 +7737,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", + "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { @@ -8575,9 +8633,9 @@ } }, "node_modules/rollup": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", - "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", "dependencies": { @@ -8591,34 +8649,38 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.1", - "@rollup/rollup-android-arm64": "4.50.1", - "@rollup/rollup-darwin-arm64": "4.50.1", - "@rollup/rollup-darwin-x64": "4.50.1", - "@rollup/rollup-freebsd-arm64": "4.50.1", - "@rollup/rollup-freebsd-x64": "4.50.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", - "@rollup/rollup-linux-arm-musleabihf": "4.50.1", - "@rollup/rollup-linux-arm64-gnu": "4.50.1", - "@rollup/rollup-linux-arm64-musl": "4.50.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", - "@rollup/rollup-linux-ppc64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-gnu": "4.50.1", - "@rollup/rollup-linux-riscv64-musl": "4.50.1", - "@rollup/rollup-linux-s390x-gnu": "4.50.1", - "@rollup/rollup-linux-x64-gnu": "4.50.1", - "@rollup/rollup-linux-x64-musl": "4.50.1", - "@rollup/rollup-openharmony-arm64": "4.50.1", - "@rollup/rollup-win32-arm64-msvc": "4.50.1", - "@rollup/rollup-win32-ia32-msvc": "4.50.1", - "@rollup/rollup-win32-x64-msvc": "4.50.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", - "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], diff --git a/src/frontend/package.json b/src/frontend/package.json index 58c047424..e1b252e8e 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -63,9 +63,14 @@ "@vitest/ui": "^3.2.4", "eslint": "^8.57.1", "eslint-plugin-react": "^7.37.5", + "flatted": "^3.4.2", "jsdom": "^26.1.0", + "rollup": "^4.59.0", "typescript": "^5.8.3", "vite": "^7.1.2", "vitest": "^3.2.4" + }, + "overrides": { + "minimatch": "3.1.3" } } diff --git a/src/mcp_server/pyproject.toml b/src/mcp_server/pyproject.toml index f04ef6db1..f603e4cfc 100644 --- a/src/mcp_server/pyproject.toml +++ b/src/mcp_server/pyproject.toml @@ -15,7 +15,7 @@ dynamic = ["version"] # Core runtime dependencies (kept in sync with requirements.txt) dependencies = [ - "fastmcp==2.14.0", + "fastmcp==2.14.2", "uvicorn[standard]==0.38.0", "python-dotenv==1.1.1", "azure-identity==1.19.0", diff --git a/src/mcp_server/uv.lock b/src/mcp_server/uv.lock index 9ee3540d0..35ab86120 100644 --- a/src/mcp_server/uv.lock +++ b/src/mcp_server/uv.lock @@ -54,14 +54,14 @@ wheels = [ [[package]] name = "authlib" -version = "1.6.6" +version = "1.6.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, + { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, ] [[package]] @@ -502,7 +502,7 @@ lua = [ [[package]] name = "fastmcp" -version = "2.14.0" +version = "2.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "authlib" }, @@ -522,9 +522,9 @@ dependencies = [ { name = "uvicorn" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/50/9bb042a2d290ccadb35db3580ac507f192e1a39c489eb8faa167cd5e3b57/fastmcp-2.14.0.tar.gz", hash = "sha256:c1f487b36a3e4b043dbf3330e588830047df2e06f8ef0920d62dfb34d0905727", size = 8232562, upload-time = "2025-12-11T23:04:27.134Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/1e/e3528227688c248283f6d86869b1e900563ffc223eff00f4f923d2750365/fastmcp-2.14.2.tar.gz", hash = "sha256:bd23d1b808b6f446444f10114dac468b11bfb9153ed78628f5619763d0cf573e", size = 8272966, upload-time = "2025-12-31T15:26:13.433Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/73/b5656172a6beb2eacec95f04403ddea1928e4b22066700fd14780f8f45d1/fastmcp-2.14.0-py3-none-any.whl", hash = "sha256:7b374c0bcaf1ef1ef46b9255ea84c607f354291eaf647ff56a47c69f5ec0c204", size = 398965, upload-time = "2025-12-11T23:04:25.587Z" }, + { url = "https://files.pythonhosted.org/packages/0d/67/8456d39484fcb7afd0defed21918e773ed59a98b39e5b633328527c88367/fastmcp-2.14.2-py3-none-any.whl", hash = "sha256:e33cd622e1ebd5110af6a981804525b6cd41072e3c7d68268ed69ef3be651aca", size = 413279, upload-time = "2025-12-31T15:26:11.178Z" }, ] [[package]] @@ -854,7 +854,7 @@ requires-dist = [ { name = "azure-core", specifier = "==1.38.0" }, { name = "azure-identity", specifier = "==1.19.0" }, { name = "cryptography", specifier = "==46.0.5" }, - { name = "fastmcp", specifier = "==2.14.0" }, + { name = "fastmcp", specifier = "==2.14.2" }, { name = "httpx", specifier = "==0.28.1" }, { name = "pydantic", specifier = "==2.11.7" }, { name = "pydantic-settings", specifier = "==2.6.1" }, @@ -1318,11 +1318,14 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.10.1" +version = "2.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, ] [package.optional-dependencies]