diff --git a/.githooks/pre-push b/.githooks/pre-push index 0c89ff143..47227b282 100644 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -4,4 +4,10 @@ set -euo pipefail repo_root="$(git rev-parse --show-toplevel)" cd "$repo_root" -python scripts/ops/pre_push_gate.py +if [ -x ".venv/Scripts/python.exe" ]; then + ".venv/Scripts/python.exe" scripts/ops/pre_push_gate.py +elif [ -x ".venv/bin/python" ]; then + ".venv/bin/python" scripts/ops/pre_push_gate.py +else + python scripts/ops/pre_push_gate.py +fi diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0ecab431c..c52515779 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -76,6 +76,17 @@ Use this mapping to know the current vs. former names—so you can correctly int - `MODEL_DEPLOYMENT_NAME_RICH`: LLM deployment name - Each app's `main.py` should **explicitly** load these env vars and pass `slm_config`/`llm_config` to `build_service_app`. +## Hosted Agent Terminology + +- Never use "hosted agent" without a qualifier in this repository. The term is overloaded across AKS runtime, Foundry portal labels, and Foundry-managed container hosting. +- **AKS-hosted agent/service** means the product runtime runs as the existing FastAPI container/pod in AKS. If a service is deployed through `azure.yaml` with `host: aks` and reconciled through Flux/HelmRelease, the correct answer to "is it hosted on AKS?" is **yes**. +- **AKS-hosted Responses adapter** means the Responses protocol is mounted into the same AKS-hosted FastAPI app, same pod, and same port as `/health`, `/ready`, `/mcp/*`, and `/invoke`. For `inventory-health-check`, this is the intended architecture. +- **Foundry portal-tracked agent** means `agent.yaml` and `.foundry/agent-metadata.yaml` describe the AKS product runtime for traceability, evaluations, protocol metadata, and operator discovery. These files alone do not create a runnable Foundry Playground surface. +- **Foundry-hosted portal/evaluation surface** means a Foundry-created hosted-container version, usually via `AIProjectClient.agents.create_version` and a `template.kind: hosted` manifest, that gives operators a Foundry Playground/test/evaluation surface for the same agent. For `inventory-health-check`, this surface is allowed when it packages the same FastAPI Responses wrapper and product-equivalent dependencies; it must not replace AKS as the product runtime or introduce a second service implementation. +- **Foundry-managed hosted-container product runtime** means Foundry owns the runtime used for production product traffic. This is not the Holiday Peak Hub product path unless a future ADR explicitly changes the runtime ownership model. +- **ACA-hosted agent** means Azure Container Apps owns the runtime. `inventory-health-check` is not ACA-hosted. +- When explaining PR #1103, issue #1107, `inventory-health-check`, Responses protocol support, or Foundry portal labels, use the precise terms above. Do not answer "no" to "is this hosted on AKS?" when the real distinction is "yes, AKS-hosted product runtime; also preserve a Foundry-hosted portal/evaluation surface when requested for portal testing, telemetry, and evaluations." + ## Memory Architecture - Three-tier memory: **Hot** (Redis), **Warm** (Cosmos DB), **Cold** (Blob Storage). diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml index 2611bad2d..b50798d82 100644 --- a/.github/workflows/dependency-audit.yml +++ b/.github/workflows/dependency-audit.yml @@ -41,7 +41,7 @@ jobs: set -e for d in apps/*/src; do if [ -f "$d/pyproject.toml" ]; then - uv pip install --system -e "$d" + uv pip install --system --prerelease=allow -e "$d" else echo "Skipping $d (no pyproject.toml)" fi diff --git a/.github/workflows/deploy-azd-crud-service.yml b/.github/workflows/deploy-azd-crud-service.yml index 6466ff018..d7a76ea55 100644 --- a/.github/workflows/deploy-azd-crud-service.yml +++ b/.github/workflows/deploy-azd-crud-service.yml @@ -77,7 +77,7 @@ jobs: uses: ./.github/workflows/deploy-azd.yml with: environment: dev - githubEnvironment: dev + githubEnvironment: branch location: ${{ github.event_name == 'workflow_dispatch' && inputs.location || 'centralus' }} projectName: ${{ github.event_name == 'workflow_dispatch' && inputs.projectName || 'holidaypeakhub405' }} imageTag: ${{ github.event_name == 'workflow_dispatch' && inputs.imageTag || github.sha }} diff --git a/.github/workflows/deploy-azd-dev.yml b/.github/workflows/deploy-azd-dev.yml index 41c951009..cf17a8713 100644 --- a/.github/workflows/deploy-azd-dev.yml +++ b/.github/workflows/deploy-azd-dev.yml @@ -26,10 +26,6 @@ on: description: Optional tested source commit SHA to deploy (empty = current ref) required: false default: '' - testedSourceRef: - description: Optional tested source ref to deploy (empty = current ref) - required: false - default: '' skipProvision: description: Skip azd provision and reuse the current dev infrastructure for a manual emergency redeploy required: true @@ -54,6 +50,14 @@ on: required: true type: boolean default: true + foundrySurfaceMode: + description: Foundry surface registration mode (plan creates an artifact; apply creates/updates Hosted Agent versions) + required: true + type: choice + options: + - plan + - apply + default: plan permissions: id-token: write @@ -77,7 +81,7 @@ jobs: projectName: ${{ github.event_name == 'workflow_dispatch' && inputs.projectName || 'holidaypeakhub405' }} imageTag: ${{ github.event_name == 'workflow_dispatch' && inputs.imageTag || github.event.workflow_run.head_sha }} sourceSha: ${{ github.event_name == 'workflow_dispatch' && inputs.testedSourceSha || (github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || '') }} - sourceRef: ${{ github.event_name == 'workflow_dispatch' && inputs.testedSourceRef || (github.event_name == 'workflow_run' && format('refs/heads/{0}', github.event.workflow_run.head_branch) || '') }} + sourceRef: ${{ github.event_name == 'workflow_dispatch' && github.ref || (github.event_name == 'workflow_run' && format('refs/heads/{0}', github.event.workflow_run.head_branch) || '') }} deployStatic: ${{ github.event_name != 'workflow_dispatch' || inputs.deployStatic }} uiOnly: ${{ github.event_name == 'workflow_dispatch' && inputs.uiOnly }} apiBaseUrl: '' @@ -87,6 +91,8 @@ jobs: forceApimSync: ${{ github.event_name == 'workflow_dispatch' && inputs.forceApimSync }} autoAllowAcrRunnerIp: true skipApiSmokeChecks: false + deployFoundrySurfaces: ${{ github.event_name == 'workflow_dispatch' }} + foundrySurfaceMode: ${{ github.event_name == 'workflow_dispatch' && inputs.foundrySurfaceMode || 'plan' }} secrets: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} diff --git a/.github/workflows/deploy-azd-inventory-health-check.yml b/.github/workflows/deploy-azd-inventory-health-check.yml index 4b3f47e58..e1b65972a 100644 --- a/.github/workflows/deploy-azd-inventory-health-check.yml +++ b/.github/workflows/deploy-azd-inventory-health-check.yml @@ -77,7 +77,7 @@ jobs: uses: ./.github/workflows/deploy-azd.yml with: environment: dev - githubEnvironment: dev + githubEnvironment: branch location: ${{ github.event_name == 'workflow_dispatch' && inputs.location || 'centralus' }} projectName: ${{ github.event_name == 'workflow_dispatch' && inputs.projectName || 'holidaypeakhub405' }} imageTag: ${{ github.event_name == 'workflow_dispatch' && inputs.imageTag || github.sha }} diff --git a/.github/workflows/deploy-azd.yml b/.github/workflows/deploy-azd.yml index 61b3f333e..1fec5255a 100644 --- a/.github/workflows/deploy-azd.yml +++ b/.github/workflows/deploy-azd.yml @@ -91,6 +91,16 @@ on: required: false type: boolean default: false + deployFoundrySurfaces: + description: Plan or apply Foundry Hosted/Custom surface registration after tested agent images are available. + required: false + type: boolean + default: false + foundrySurfaceMode: + description: Foundry surface registration mode. Use plan for dry-run artifacts; use apply to create/update Hosted Agent versions. + required: false + type: string + default: plan secrets: AZURE_CLIENT_ID: required: true @@ -1328,6 +1338,7 @@ jobs: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + CHANGED_AGENT_SERVICES: ${{ needs.detect-changes.outputs.changed_agent_services_csv }} steps: - uses: actions/checkout@v4 with: @@ -1631,6 +1642,138 @@ jobs: --resource-group "${{ needs.prepare-acr-build-access.outputs.rg_name }}" \ --ip-address "${{ steps.acr_preflight_build.outputs.runner_ip }}" >/dev/null || true + deploy-foundry-surfaces: + runs-on: ubuntu-latest + if: ${{ !inputs.uiOnly && inputs.deployFoundrySurfaces && needs.detect-changes.outputs.changed_agent_services_csv != '' }} + needs: + - detect-changes + - provision + - prepare-acr-build-access + - build-aks-images + environment: ${{ inputs.githubEnvironment }} + env: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + PROJECT_ENDPOINT: ${{ needs.provision.outputs.PROJECT_ENDPOINT }} + PROJECT_NAME: ${{ needs.provision.outputs.PROJECT_NAME }} + APIM_BASE_URL: ${{ needs.provision.outputs.APIM_GATEWAY_URL }} + AZURE_CONTAINER_REGISTRY: ${{ needs.prepare-acr-build-access.outputs.login_server }} + REDIS_HOST: ${{ needs.provision.outputs.REDIS_HOST }} + REDIS_URL: "" + COSMOS_ACCOUNT_URI: ${{ needs.provision.outputs.COSMOS_ACCOUNT_URI }} + COSMOS_DATABASE: ${{ needs.provision.outputs.COSMOS_DATABASE }} + COSMOS_CONTAINER: ${{ needs.provision.outputs.COSMOS_CONTAINER }} + BLOB_ACCOUNT_URL: ${{ needs.provision.outputs.BLOB_ACCOUNT_URL }} + BLOB_CONTAINER: ${{ needs.provision.outputs.BLOB_CONTAINER }} + EVENT_HUB_NAMESPACE: ${{ needs.provision.outputs.EVENT_HUB_NAMESPACE }} + KEY_VAULT_URI: ${{ needs.provision.outputs.KEY_VAULT_URI }} + APPLICATIONINSIGHTS_CONNECTION_STRING: ${{ needs.provision.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING }} + MODEL_DEPLOYMENT_NAME_FAST: gpt-5-nano + MODEL_DEPLOYMENT_NAME_RICH: gpt-5 + steps: + - name: Checkout tested source + uses: actions/checkout@v4 + with: + ref: ${{ env.DEPLOY_SOURCE_CHECKOUT_REF }} + + - name: Azure login (OIDC) + uses: azure/login@v2 + with: + client-id: ${{ env.AZURE_CLIENT_ID }} + tenant-id: ${{ env.AZURE_TENANT_ID }} + subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install Foundry surface registration dependencies + shell: bash + run: | + set -euo pipefail + python3 -m pip install --quiet \ + pyyaml \ + "azure-ai-projects>=2.0.1" \ + "azure-identity>=1.17.0" + + - name: Download tested image references + uses: actions/download-artifact@v4 + with: + pattern: tested-image-* + path: ${{ runner.temp }}/tested-image-artifacts + merge-multiple: false + + - name: Build Foundry image map + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import json + import os + from pathlib import Path + + artifact_root = Path(os.environ["RUNNER_TEMP"]) / "tested-image-artifacts" + image_map = {} + for image_file in artifact_root.glob("tested-image-*/image-ref.txt"): + service = image_file.parent.name.removeprefix("tested-image-") + image_map[service] = image_file.read_text(encoding="utf-8").strip() + + output_path = Path(os.environ["RUNNER_TEMP"]) / "foundry-image-map.json" + output_path.write_text(json.dumps(image_map, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print(f"Wrote {len(image_map)} tested image refs to {output_path}") + PY + + - name: Validate Hosted Agent ACR reachability policy + shell: bash + run: | + set -euo pipefail + MODE="${{ inputs.foundrySurfaceMode }}" + if [ "$MODE" != "apply" ]; then + exit 0 + fi + + PUBLIC_BEFORE="${{ needs.prepare-acr-build-access.outputs.public_network_access_before }}" + DEFAULT_BEFORE="${{ needs.prepare-acr-build-access.outputs.default_action_before }}" + if [ "$PUBLIC_BEFORE" != "Enabled" ] || [ "$DEFAULT_BEFORE" != "Allow" ]; then + echo "Foundry Hosted Agents require an ACR public endpoint that the service can pull from." >&2 + echo "Current baseline publicNetworkAccess=${PUBLIC_BEFORE}, defaultAction=${DEFAULT_BEFORE}." >&2 + echo "Run in plan mode, then update the ACR network policy intentionally before apply." >&2 + exit 1 + fi + + - name: Register Foundry surfaces + shell: bash + run: | + set -euo pipefail + MODE="${{ inputs.foundrySurfaceMode }}" + if [ "$MODE" != "plan" ] && [ "$MODE" != "apply" ]; then + echo "foundrySurfaceMode must be 'plan' or 'apply'." >&2 + exit 1 + fi + + python3 scripts/ops/register_foundry_surfaces.py \ + --mode "$MODE" \ + --environment "${{ inputs.environment }}" \ + --project-endpoint "$PROJECT_ENDPOINT" \ + --project-name "$PROJECT_NAME" \ + --image-map-file "${RUNNER_TEMP}/foundry-image-map.json" \ + --acr-login-server "${AZURE_CONTAINER_REGISTRY}" \ + --image-tag "${DEPLOY_SOURCE_SHA}" \ + --apim-base-url "$APIM_BASE_URL" \ + --services "${{ needs.detect-changes.outputs.changed_agent_services_csv }}" \ + --output "${RUNNER_TEMP}/foundry-surface-plan.json" + + - name: Upload Foundry surface plan + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: foundry-surface-plan-${{ inputs.environment }} + path: ${{ runner.temp }}/foundry-surface-plan.json + if-no-files-found: warn + retention-days: 14 + restore-acr-build-access: runs-on: ubuntu-latest if: ${{ always() && !inputs.uiOnly && needs.detect-changes.outputs.changed_aks_services_csv != '' }} @@ -3011,6 +3154,7 @@ jobs: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + CHANGED_AGENT_SERVICES: ${{ needs.detect-changes.outputs.changed_agent_services_csv }} steps: - uses: actions/checkout@v4 @@ -3941,31 +4085,42 @@ jobs: smoke_health "${API_BASE}/api/products?limit=1" "crud-products" smoke_health "${API_BASE}/api/categories" "crud-categories" - CORS_HEADERS_FILE="/tmp/apim-cors-headers.txt" - CORS_BODY_FILE="/tmp/apim-cors-body.txt" - CORS_STATUS=$(curl -sS -D "$CORS_HEADERS_FILE" -o "$CORS_BODY_FILE" -w "%{http_code}" \ - -X OPTIONS "${API_BASE}/api/products?limit=1" \ - -H "Origin: http://localhost:3000" \ - -H "Access-Control-Request-Method: GET") || CORS_STATUS="000" - - if [ "$CORS_STATUS" != "200" ] && [ "$CORS_STATUS" != "204" ]; then - echo "[FAIL] crud-cors-preflight: ${API_BASE}/api/products?limit=1 returned HTTP $CORS_STATUS" >&2 - cat "$CORS_HEADERS_FILE" 2>/dev/null || true - cat "$CORS_BODY_FILE" 2>/dev/null || true - exit 1 - fi + smoke_cors_preflight() { + local url="$1" + local label="$2" + local headers_file="/tmp/apim-cors-headers.txt" + local normalized_headers_file="/tmp/apim-cors-headers-normalized.txt" + local body_file="/tmp/apim-cors-body.txt" + local status_code="" - if ! grep -Eiq '^Access-Control-Allow-Origin: http://localhost:3000\r?$' "$CORS_HEADERS_FILE"; then - echo "[FAIL] crud-cors-preflight: missing expected Access-Control-Allow-Origin header" >&2 - cat "$CORS_HEADERS_FILE" 2>/dev/null || true - exit 1 - fi + for attempt in $(seq 1 20); do + : > "$headers_file" + : > "$normalized_headers_file" + status_code=$(curl -sS -D "$headers_file" -o "$body_file" -w "%{http_code}" \ + -X OPTIONS "$url" \ + -H "Origin: http://localhost:3000" \ + -H "Access-Control-Request-Method: GET") || status_code="000" + tr -d '\r' < "$headers_file" > "$normalized_headers_file" 2>/dev/null || true + + if { [ "$status_code" = "200" ] || [ "$status_code" = "204" ]; } \ + && grep -Eiq '^Access-Control-Allow-Origin: http://localhost:3000$' "$normalized_headers_file" \ + && grep -Eiq '^Access-Control-Allow-Methods: .*GET' "$normalized_headers_file"; then + echo "[OK] $label: $url" + return 0 + fi - if ! grep -Eiq '^Access-Control-Allow-Methods: .*GET' "$CORS_HEADERS_FILE"; then - echo "[FAIL] crud-cors-preflight: missing expected Access-Control-Allow-Methods header" >&2 - cat "$CORS_HEADERS_FILE" 2>/dev/null || true - exit 1 - fi + echo "Attempt $attempt/20 failed for $label with HTTP $status_code or missing CORS headers" + cat "$headers_file" 2>/dev/null || true + sleep 10 + done + + echo "[FAIL] $label: $url did not return expected CORS preflight headers" >&2 + cat "$headers_file" 2>/dev/null || true + cat "$body_file" 2>/dev/null || true + return 1 + } + + smoke_cors_preflight "${API_BASE}/api/products?limit=1" "crud-cors-preflight" NEGATIVE_STATUS=$(curl -sS -o /tmp/apim-negative-response.json -w "%{http_code}" "${API_BASE}/api/does-not-exist") || NEGATIVE_STATUS="000" if [ "$NEGATIVE_STATUS" = "500" ] || [ "$NEGATIVE_STATUS" = "502" ] || [ "$NEGATIVE_STATUS" = "503" ] || [ "$NEGATIVE_STATUS" = "000" ]; then @@ -4178,13 +4333,36 @@ jobs: echo "Restoring Flux source '${FLUX_SOURCE}' from preview branch '${PREVIEW_BRANCH}' to default branch '${DEFAULT_BRANCH}'." PATCH=$(printf '{"spec":{"ref":{"branch":"%s"}}}' "$DEFAULT_BRANCH") - kubectl patch gitrepository.source.toolkit.fluxcd.io "$FLUX_SOURCE" -n "$FLUX_NAMESPACE" --type=merge -p "$PATCH" - ACTIVE_BRANCH=$(kubectl get gitrepository.source.toolkit.fluxcd.io "$FLUX_SOURCE" -n "$FLUX_NAMESPACE" -o jsonpath='{.spec.ref.branch}') - if [ "$ACTIVE_BRANCH" != "$DEFAULT_BRANCH" ]; then - echo "::error::Flux source '${FLUX_SOURCE}' expected branch '${DEFAULT_BRANCH}' but found '${ACTIVE_BRANCH}'." + restore_flux_source() { + kubectl patch gitrepository.source.toolkit.fluxcd.io "$FLUX_SOURCE" -n "$FLUX_NAMESPACE" --type=merge -p "$PATCH" + + ACTIVE_BRANCH=$(kubectl get gitrepository.source.toolkit.fluxcd.io "$FLUX_SOURCE" -n "$FLUX_NAMESPACE" -o jsonpath='{.spec.ref.branch}') + if [ "$ACTIVE_BRANCH" != "$DEFAULT_BRANCH" ]; then + echo "::error::Flux source '${FLUX_SOURCE}' expected branch '${DEFAULT_BRANCH}' but found '${ACTIVE_BRANCH}'." + return 1 + fi + } + + if ! restore_flux_source; then + echo "::warning::Direct kubectl Flux restore failed; retrying through az aks command invoke." + REMOTE_COMMAND=$(cat </dev/null; then curl -s https://fluxcd.io/install.sh | bash 2>/dev/null || true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b70514886..3b0e73981 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -48,7 +48,14 @@ jobs: for d in apps/*/src; do [ -f "$d/pyproject.toml" ] || continue [ -f "$d/uv.lock" ] || { echo "Missing uv.lock in $d"; exit 1; } - (cd "$d" && uv lock --check) + ( + cd "$d" + if grep -q 'prerelease-mode = "allow"' uv.lock; then + uv lock --prerelease=allow --check + else + uv lock --check + fi + ) done - name: Install lib run: uv pip install --system -e ./lib/src @@ -57,7 +64,7 @@ jobs: - name: Install apps run: | for d in apps/*/src; do - [ -f "$d/pyproject.toml" ] && uv pip install --system -e "$d" || echo "Skipping $d" + [ -f "$d/pyproject.toml" ] && uv pip install --system --prerelease=allow -e "$d" || echo "Skipping $d" done - name: Fix agent-framework init overwrite run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 937bc5af5..627645d0f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: set -e for d in apps/*/src; do if [ -f "$d/pyproject.toml" ]; then - uv pip install --system -e "$d" + uv pip install --system --prerelease=allow -e "$d" else echo "Skipping $d (no pyproject.toml)" fi diff --git a/.infra/DEPLOYMENT.md b/.infra/DEPLOYMENT.md index 0d77bbfa7..a09019db3 100644 --- a/.infra/DEPLOYMENT.md +++ b/.infra/DEPLOYMENT.md @@ -83,6 +83,7 @@ Release gate notes: - APIM smoke checks validate direct AGC CRUD health, APIM `GET /api/health`, `GET /api/products?limit=1`, `GET /api/categories`, CRUD CORS preflight, negative CRUD path behavior, and changed agent `GET /agents//health` before UI publish. - APIM sync consumes the approved AGC hostname contract from azd outputs and fails closed if the backend drifts to IP-based or cluster-local targets. - UI deployment runs pre/post smoke checks to ensure API health and SWA hostname reachability. +- Foundry surface registration is explicit and separate from AKS rollout. Manual dev deployments can emit a dry-run plan by default; `foundrySurfaceMode=apply` creates or updates Foundry Hosted Agent versions from tested image digests and validates Custom Agent proxy metadata without replacing APIM -> AGC -> AKS product traffic. azd env set deployShared true -e azd env set deployStatic true -e @@ -184,6 +185,22 @@ azd deploy --service crud-service -e azd deploy --all -e ``` +### Optional Step 5: Register Foundry Portal Surfaces + +AKS deployment alone does not create visible Foundry Hosted Agent versions. To expose the public/human-facing agents in the Foundry portal, run the `deploy-azd-dev (entrypoint)` workflow with: + +- `deployFoundrySurfaces=true` +- `foundrySurfaceMode=plan` to generate the `foundry-surface-plan-` artifact +- `foundrySurfaceMode=apply` to create or update Hosted Agent versions after reviewing the plan + +The reusable workflow consumes the tested `repo@sha256:...` image references produced by `build-aks-images`, reads `apps/foundry-surfaces.yaml`, and calls `scripts/ops/register_foundry_surfaces.py`. Apply mode uses OIDC-backed Azure identity and `AIProjectClient(..., allow_preview=True)`. It does not create a second AKS runtime, does not call the retired `/assistants` surface, and does not turn Custom Agent proxy metadata into Foundry-managed compute. + +Cost and networking notes: + +- Hosted Agent apply mode can incur Foundry active-session CPU/memory charges. +- Foundry Hosted Agents require the ACR public endpoint to be intentionally reachable by the service. The workflow fails apply mode if the baseline ACR policy is private-only or `defaultAction=Deny`. +- Custom Agent entries remain metadata/proxy validated until Microsoft Foundry exposes a supported Custom Agent create API for APIM-backed proxy surfaces. + ### Outputs After deployment, retrieve outputs: diff --git a/.infra/azd/hooks/sync-apim-agents.ps1 b/.infra/azd/hooks/sync-apim-agents.ps1 index 130590b62..c3a1cddd0 100644 --- a/.infra/azd/hooks/sync-apim-agents.ps1 +++ b/.infra/azd/hooks/sync-apim-agents.ps1 @@ -843,6 +843,9 @@ function Update-CrudApi { + + + @@ -859,13 +862,12 @@ function Update-CrudApi { - - @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) + @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) GET,POST,PUT,PATCH,DELETE,OPTIONS @@ -879,7 +881,7 @@ function Update-CrudApi { - @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) + @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) GET,POST,PUT,PATCH,DELETE,OPTIONS diff --git a/.infra/azd/hooks/sync-apim-agents.sh b/.infra/azd/hooks/sync-apim-agents.sh index 2d7132bb7..3bce66e01 100644 --- a/.infra/azd/hooks/sync-apim-agents.sh +++ b/.infra/azd/hooks/sync-apim-agents.sh @@ -575,6 +575,9 @@ PY + + + @@ -589,12 +592,11 @@ PY - - @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) + @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) GET,POST,PUT,PATCH,DELETE,OPTIONS * @@ -602,7 +604,7 @@ PY - @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) + @(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000")) GET,POST,PUT,PATCH,DELETE,OPTIONS * application/json diff --git a/.infra/tests/test_crud_agc_route_contract.py b/.infra/tests/test_crud_agc_route_contract.py new file mode 100644 index 000000000..eebe2db59 --- /dev/null +++ b/.infra/tests/test_crud_agc_route_contract.py @@ -0,0 +1,18 @@ +from pathlib import Path + +import yaml + + +ROOT = Path(__file__).resolve().parents[2] +CRUD_HELMRELEASE_PATH = ROOT / ".kubernetes" / "releases" / "crud" / "crud-service.yaml" + + +def test_crud_agc_routes_include_public_api_and_probe_paths() -> None: + helmrelease = yaml.safe_load(CRUD_HELMRELEASE_PATH.read_text(encoding="utf-8")) + agc_paths = helmrelease["spec"]["values"]["agc"]["paths"] + + routes = {path["path"]: path["pathType"] for path in agc_paths} + + assert routes["/health"] == "PathPrefix" + assert routes["/ready"] == "PathPrefix" + assert routes["/api"] == "PathPrefix" \ No newline at end of file diff --git a/.infra/tests/test_deploy_azd_agc_readiness_contract.py b/.infra/tests/test_deploy_azd_agc_readiness_contract.py index 76f6bd8c6..25f44717e 100644 --- a/.infra/tests/test_deploy_azd_agc_readiness_contract.py +++ b/.infra/tests/test_deploy_azd_agc_readiness_contract.py @@ -3,16 +3,41 @@ ROOT = Path(__file__).resolve().parents[2] WORKFLOW_PATH = ROOT / ".github" / "workflows" / "deploy-azd.yml" +INVENTORY_WRAPPER_PATH = ROOT / ".github" / "workflows" / "deploy-azd-inventory-health-check.yml" +SERVICE_WRAPPER_GENERATOR_PATH = ROOT / "scripts" / "ci" / "regen_service_deploy_entrypoints.py" +VALIDATE_JOB_MARKER = " validate-agc-readiness:\n" VALIDATE_STEP_MARKER = " - name: Validate AGC gateway class and direct CRUD health\n" NEXT_SECTION_MARKER = "\n\n sync-apim:\n" +def _validate_agc_readiness_job() -> str: + content = WORKFLOW_PATH.read_text(encoding="utf-8") + assert content.count(VALIDATE_JOB_MARKER) == 1 + return content.split(VALIDATE_JOB_MARKER, 1)[1].split(NEXT_SECTION_MARKER, 1)[0] + + def _validate_agc_readiness_block() -> str: content = WORKFLOW_PATH.read_text(encoding="utf-8") assert content.count(VALIDATE_STEP_MARKER) == 1 return content.split(VALIDATE_STEP_MARKER, 1)[1].split(NEXT_SECTION_MARKER, 1)[0] +def test_validate_agc_readiness_receives_changed_agent_services_from_detect_changes() -> None: + job = _validate_agc_readiness_job() + env_block = job.split(" env:\n", 1)[1].split(" steps:\n", 1)[0] + + assert " CHANGED_AGENT_SERVICES: ${{ needs.detect-changes.outputs.changed_agent_services_csv }}" in env_block + + +def test_inventory_service_preview_entrypoint_uses_branch_environment_contract() -> None: + wrapper = INVENTORY_WRAPPER_PATH.read_text(encoding="utf-8") + generator = SERVICE_WRAPPER_GENERATOR_PATH.read_text(encoding="utf-8") + + assert " githubEnvironment: branch\n" in wrapper + assert " githubEnvironment: dev\n" not in wrapper + assert "githubEnvironment: branch" in generator + + def test_agc_readiness_validates_alb_and_traffic_controller_before_gateway_contract_and_direct_health() -> None: block = _validate_agc_readiness_block() diff --git a/.infra/tests/test_deploy_azd_apim_smoke_contract.py b/.infra/tests/test_deploy_azd_apim_smoke_contract.py new file mode 100644 index 000000000..6ad506b59 --- /dev/null +++ b/.infra/tests/test_deploy_azd_apim_smoke_contract.py @@ -0,0 +1,39 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +WORKFLOW_PATH = ROOT / ".github" / "workflows" / "deploy-azd.yml" +SMOKE_JOB_MARKER = " smoke-apim:\n" +NEXT_JOB_MARKER = "\n\n deploy-ui:\n" + + +def _smoke_apim_job() -> str: + content = WORKFLOW_PATH.read_text(encoding="utf-8") + assert content.count(SMOKE_JOB_MARKER) == 1 + return content.split(SMOKE_JOB_MARKER, 1)[1].split(NEXT_JOB_MARKER, 1)[0] + + +def test_apim_cors_preflight_smoke_retries_header_validation() -> None: + job = _smoke_apim_job() + + assert "smoke_cors_preflight() {" in job + assert "for attempt in $(seq 1 20); do" in job + assert "Access-Control-Request-Method: GET" in job + assert 'local headers_file="/tmp/apim-cors-headers.txt"' in job + assert 'local normalized_headers_file="/tmp/apim-cors-headers-normalized.txt"' in job + assert ': > "$normalized_headers_file"' in job + assert "tr -d '\\r' < \"$headers_file\" > \"$normalized_headers_file\"" in job + assert "grep -Eiq '^Access-Control-Allow-Origin: http://localhost:3000$' \"$normalized_headers_file\"" in job + assert "grep -Eiq '^Access-Control-Allow-Methods: .*GET' \"$normalized_headers_file\"" in job + assert "grep -Eiq '^Access-Control-Allow-Origin: http://localhost:3000\\r?$' \"$headers_file\"" not in job + assert "grep -Eiq '^Access-Control-Allow-Methods: .*GET' \"$headers_file\"" not in job + assert "missing CORS headers" in job + assert 'cat "$headers_file" 2>/dev/null || true' in job + + cors_function = job.index("smoke_cors_preflight() {") + normalize_headers = job.index("tr -d '\\r' < \"$headers_file\" > \"$normalized_headers_file\"") + origin_check = job.index("grep -Eiq '^Access-Control-Allow-Origin: http://localhost:3000$' \"$normalized_headers_file\"") + cors_call = job.index('smoke_cors_preflight "${API_BASE}/api/products?limit=1" "crud-cors-preflight"') + negative_probe = job.index('NEGATIVE_STATUS=$(curl -sS -o /tmp/apim-negative-response.json') + + assert cors_function < normalize_headers < origin_check < cors_call < negative_probe \ No newline at end of file diff --git a/.infra/tests/test_deploy_azd_flux_restore_contract.py b/.infra/tests/test_deploy_azd_flux_restore_contract.py new file mode 100644 index 000000000..44099f7a1 --- /dev/null +++ b/.infra/tests/test_deploy_azd_flux_restore_contract.py @@ -0,0 +1,29 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +WORKFLOW_PATH = ROOT / ".github" / "workflows" / "deploy-azd.yml" +RESTORE_JOB_MARKER = " restore-flux-source-default-branch:\n" + + +def _restore_flux_job() -> str: + content = WORKFLOW_PATH.read_text(encoding="utf-8") + assert content.count(RESTORE_JOB_MARKER) == 1 + return content.split(RESTORE_JOB_MARKER, 1)[1] + + +def test_flux_restore_falls_back_to_aks_command_invoke_when_direct_kubectl_fails() -> None: + job = _restore_flux_job() + + assert "restore_flux_source() {" in job + assert "if ! restore_flux_source; then" in job + assert "Direct kubectl Flux restore failed; retrying through az aks command invoke." in job + assert "REMOTE_COMMAND=$(cat < str: + return CORE_WORKFLOW_PATH.read_text(encoding="utf-8") + + +def _dev_workflow() -> str: + return DEV_WORKFLOW_PATH.read_text(encoding="utf-8") + + +def _foundry_surface_job() -> str: + content = _core_workflow() + assert content.count(JOB_MARKER) == 1 + return content.split(JOB_MARKER, 1)[1].split(RESTORE_ACR_MARKER, 1)[0] + + +def test_reusable_workflow_exposes_explicit_foundry_surface_controls() -> None: + workflow = _core_workflow() + + assert "deployFoundrySurfaces:" in workflow + assert "foundrySurfaceMode:" in workflow + assert "default: false" in workflow + assert "default: plan" in workflow + + +def test_dev_entrypoint_plans_surfaces_by_default_but_apply_is_explicit() -> None: + workflow = _dev_workflow() + dispatch_inputs = workflow.split(" workflow_dispatch:\n", 1)[1].split( + "\npermissions:", 1 + )[0] + + assert dispatch_inputs.count("description:") <= 10 + assert "deployFoundrySurfaces:" not in dispatch_inputs + assert "deployFoundrySurfaces: ${{ github.event_name == 'workflow_dispatch' }}" in workflow + assert "foundrySurfaceMode:" in workflow + assert "options:" in workflow + assert "- plan" in workflow + assert "- apply" in workflow + assert "foundrySurfaceMode: ${{ github.event_name == 'workflow_dispatch'" in workflow + + +def test_foundry_surface_job_uses_tested_images_and_oidc() -> None: + job = _foundry_surface_job() + + assert "needs.detect-changes.outputs.changed_agent_services_csv != ''" in job + assert "- prepare-acr-build-access" in job + assert "- build-aks-images" in job + assert "azure/login@v2" in job + assert "actions/download-artifact@v4" in job + assert "pattern: tested-image-*" in job + assert "foundry-image-map.json" in job + assert "scripts/ops/register_foundry_surfaces.py" in job + assert "--image-map-file" in job + assert "--services \"${{ needs.detect-changes.outputs.changed_agent_services_csv }}\"" in job + assert "actions/upload-artifact@v4" in job + assert "foundry-surface-plan-${{ inputs.environment }}" in job + + +def test_foundry_surface_job_requires_plan_or_apply_and_preserves_aks_runtime() -> None: + job = _foundry_surface_job() + + assert "foundrySurfaceMode must be 'plan' or 'apply'." in job + assert "MODEL_DEPLOYMENT_NAME_FAST: gpt-5-nano" in job + assert "MODEL_DEPLOYMENT_NAME_RICH: gpt-5" in job + assert "APIM_BASE_URL: ${{ needs.provision.outputs.APIM_GATEWAY_URL }}" in job + assert "PROJECT_ENDPOINT: ${{ needs.provision.outputs.PROJECT_ENDPOINT }}" in job + assert "Run in plan mode, then update the ACR network policy intentionally before apply." in job + assert "az aks" not in job + assert "kubectl" not in job + assert "assistants" not in job.lower() + assert "PromptAgentDefinition" not in job + + +def test_foundry_surface_job_runs_before_acr_state_restoration() -> None: + workflow = _core_workflow() + + assert workflow.index(JOB_MARKER) < workflow.index(RESTORE_ACR_MARKER) \ No newline at end of file diff --git a/.infra/tests/test_sync_apim_policy_contract.py b/.infra/tests/test_sync_apim_policy_contract.py new file mode 100644 index 000000000..0413b228f --- /dev/null +++ b/.infra/tests/test_sync_apim_policy_contract.py @@ -0,0 +1,71 @@ +from pathlib import Path +from xml.etree import ElementTree + + +ROOT = Path(__file__).resolve().parents[2] +BASH_HOOK_PATH = ROOT / ".infra" / "azd" / "hooks" / "sync-apim-agents.sh" +POWERSHELL_HOOK_PATH = ROOT / ".infra" / "azd" / "hooks" / "sync-apim-agents.ps1" +HOOK_PATHS = (BASH_HOOK_PATH, POWERSHELL_HOOK_PATH) + +RAW_CORS_VALUE = '@(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000"))' +ESCAPED_CORS_VALUE = '@(context.Request.Headers.GetValueOrDefault("Origin", "http://localhost:3000"))' + + +def _extract_bash_crud_policy(content: str) -> str: + start_marker = 'cat > "$POLICY_XML_FILE" < str: + start_marker = '$crudPolicyXml = @"\n' + start = content.index(start_marker) + len(start_marker) + end = content.index('\n"@', start) + return content[start:end] + + +def _extract_crud_policy(hook_path: Path) -> str: + content = hook_path.read_text(encoding="utf-8") + if hook_path.suffix == ".ps1": + return _extract_powershell_crud_policy(content) + return _extract_bash_crud_policy(content) + + +def test_crud_policy_cors_values_use_raw_quotes_in_xml_text() -> None: + for hook_path in HOOK_PATHS: + content = hook_path.read_text(encoding="utf-8") + + assert content.count(RAW_CORS_VALUE) == 2 + assert ESCAPED_CORS_VALUE not in content + + +def test_crud_policy_attribute_expressions_keep_xml_entities() -> None: + for hook_path in HOOK_PATHS: + content = hook_path.read_text(encoding="utf-8") + + assert 'condition="@(context.Request.OriginalUrl.Path.Equals("/api/health"' in content + assert 'condition="@(context.Request.OriginalUrl.Path.Equals("/api/ready"' in content + assert 'condition="@(context.Request.OriginalUrl.Path.Equals("/api"' in content + assert 'template="@(string.Concat("/api", (string)context.Variables["crudBackendPath"]))"' in content + + +def test_crud_policy_rewrites_public_probe_paths_to_service_probe_paths() -> None: + for hook_path in HOOK_PATHS: + content = hook_path.read_text(encoding="utf-8") + + assert '' in content + assert '' in content + + +def test_crud_policy_backend_has_single_forward_request_policy() -> None: + for hook_path in HOOK_PATHS: + policy_xml = _extract_crud_policy(hook_path) + policy = ElementTree.fromstring(policy_xml) + backend = policy.find("backend") + + assert backend is not None + backend_policies = list(backend) + assert len(backend_policies) == 1 + assert backend_policies[0].tag == "forward-request" + assert backend_policies[0].attrib == {"timeout": "60"} \ No newline at end of file diff --git a/.kubernetes/releases/agents/inventory-health-check.yaml b/.kubernetes/releases/agents/inventory-health-check.yaml index 581b06de8..60bc3bf33 100644 --- a/.kubernetes/releases/agents/inventory-health-check.yaml +++ b/.kubernetes/releases/agents/inventory-health-check.yaml @@ -30,15 +30,15 @@ spec: clientId: 036f4fd2-9093-4948-b0dd-beb5c87aa6dc image: repository: holidaypeakhub405devacr.azurecr.io/inventory-health-check - tag: 808d48227b08ceaf9f2d820010e02edf5250b57f + tag: ae0201b1de5f16eba281d57b543b25a31f71a5df replicaCount: 2 resources: limits: cpu: 500m - memory: 512Mi + memory: 1Gi requests: cpu: 250m - memory: 256Mi + memory: 512Mi nodeSelector: agentpool: agents tolerations: @@ -90,6 +90,7 @@ spec: FOUNDRY_AUTO_ENSURE_ON_STARTUP: 'true' AGENT_FOUNDRY_INVOKE_TIMEOUT_SECONDS: '60' FOUNDRY_STRICT_ENFORCEMENT: 'false' + HOLIDAY_PEAK_AKS_RESPONSES_ENABLED: '1' KEY_VAULT_URI: https://holidaypeakhub405-dev-kv.vault.azure.net/ MODEL_DEPLOYMENT_NAME_FAST: gpt-5-nano MODEL_DEPLOYMENT_NAME_RICH: gpt-5 diff --git a/.kubernetes/releases/crud/crud-service.yaml b/.kubernetes/releases/crud/crud-service.yaml index 231b2bb53..fd944aeb0 100644 --- a/.kubernetes/releases/crud/crud-service.yaml +++ b/.kubernetes/releases/crud/crud-service.yaml @@ -30,7 +30,7 @@ spec: clientId: b09349cf-547f-409f-8b96-f3288ddedf34 image: repository: holidaypeakhub405devacr.azurecr.io/crud-service - tag: 808d48227b08ceaf9f2d820010e02edf5250b57f + tag: 571026e55688f0957c55963ca8e040b7193086da replicaCount: 1 resources: limits: @@ -75,6 +75,8 @@ spec: paths: - path: /health pathType: PathPrefix + - path: /ready + pathType: PathPrefix - path: /api pathType: PathPrefix sharedResources: diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a2f1866..9bb183858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Two-track Foundry agent surface taxonomy (ADR-036): the 10 public/human-facing agents now have `agent.hosted.yaml` Hosted Agent manifests with Responses 1.0.0, hosted-safe `HPH_AGENT_ID_*` environment aliases, `HOLIDAY_PEAK_FOUNDRY_HOSTED=1`, and `UVICORN_PORT=8088`; the 16 internal agents now declare Custom Agent proxy metadata in `.foundry/agent-metadata.yaml` with `foundryManagedCompute: false`. Added `apps/foundry-surfaces.yaml` and ops tests to enforce the taxonomy while keeping AKS/APIM/AGC as the product runtime path. + +- Foundry surface registration automation: added `scripts/ops/register_foundry_surfaces.py` plus a `deploy-foundry-surfaces` job in the reusable azd deployment workflow. The job can generate a dry-run plan artifact or, when explicitly set to `foundrySurfaceMode=apply`, create/update Hosted Agent versions from tested image digests using Azure AI Projects preview support. Custom Agent surfaces are validated as APIM proxy metadata and remain non-compute until a supported Foundry Custom Agent creation API exists. + +- AKS-hosted Responses adapter for `inventory-health-check` (PR #1103 / issue #1107 correction). The Responses protocol is mounted into the existing FastAPI app and same AKS pod/port as `/health`, `/ready`, `/mcp/*`, and `/invoke`; `agent.yaml` remains the portal-tracking/direct-model artifact and now records `responses 1.0.0`. Foundry-hosted portal/evaluation registration remains a separate test surface, not the product traffic runtime. + ### Removed +- Foundry-managed hosted-container product path for `inventory-health-check`: removed as the product runtime model. The product deployment target remains AKS via azd/Flux/HelmRelease; any `AIProjectClient.agents.create_version` usage for this service must be framed as the Foundry portal/evaluation surface over the same FastAPI Responses wrapper, not as a replacement product runtime. + - Revert of PR #1101 (`ensure-foundry-agents` workflow + `scripts/ops/ensure_foundry_tracking_agents.py` + tests). The self-heal targeted the wrong Azure AI Foundry API surface (legacy `/assistants`, OpenAI-compatible "Classic Assistants" — hidden in the new Foundry portal behind the "View classic agents" link with banner "Assistants are not yet supported"), while the portal that operators actually use renders the New Foundry Agents (`/agents?api-version=2025-11-15-preview`, versioned `PromptAgentDefinition` shape). Re-running the job on every deploy would have continued to populate a deprecated/hidden surface while leaving the visible surface drift-prone. The script also reintroduced the V2 portal-managed agent path (`project_client.agents.create_version` with `PromptAgentDefinition`) that ADR-005 explicitly retired in Wave 4c. Direct-model invocation via MAF stays canonical. Per-service prompt drift is still guarded by `scripts/ci/verify_foundry_prompt.py` (read-only). One-time cleanup of 50 stale leftovers from Wave 4a/4b applied separately on both API surfaces; the two operator-curated keepers (`ecommerce-catalog-search-fast`, `product-management-assortment-optimization-rich`) remain in the New Foundry portal. ### Changed @@ -17,6 +27,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Issue #1107 / PR #1103 APIM sync and smoke: corrected generated CRUD policy CORS header expressions in `.infra/azd/hooks/sync-apim-agents.sh` and `.infra/azd/hooks/sync-apim-agents.ps1` to use raw quotes inside XML `` text while preserving entity-escaped quotes in XML attributes; the CRUD backend section now emits only ``; and public `/api/ready` now rewrites to the CRUD service `/ready` probe. The CRUD AGC route contract now publishes `/ready` alongside `/health` and `/api`, so APIM smoke tests reach the FastAPI readiness endpoint instead of receiving an AGC 404. Added offline infra regression coverage for these policy and route contracts. + +- Issue #1107 / PR #1103 APIM smoke hardening: CORS preflight validation now retries expected `Access-Control-Allow-*` headers and normalizes raw `curl -D` CRLF response headers before shell assertions, matching the existing health probe retry behavior while avoiding header-parsing false negatives immediately after `sync-apim` succeeds. + +- Issue #1107 / PR #1103 branch-preview cleanup: `deploy-azd.yml` now falls back to `az aks command invoke` when the runner's direct `kubectl` credentials fail while restoring the Flux `GitRepository` source from a preview branch back to `main`, preventing failed smoke runs from leaving GitOps pinned to the PR branch. + +- Issue #1107 / PR #1103 CRUD HelmRelease image pin: `.kubernetes/releases/crud/crud-service.yaml` now points at APIM policy-fix commit tag `571026e55688f0957c55963ca8e040b7193086da`, keeping Flux preview desired state aligned with the workflow `imageTag`/`testedSourceSha` so the next branch workflow can build and deploy the same commit. + +- Issue #1107 / PR #1103 AGC CRUD readiness: bounded CRUD startup dependency warm-up for PostgreSQL pool initialization and Key Vault secret retrieval so `/health` can serve process liveness promptly while `/ready` continues to return degraded/503 for Redis, Cosmos DB, PostgreSQL, or connector dependency failures. + +- Issue #1107 / PR #1103 AGC CRUD readiness: `/ready` now runs Redis, Cosmos DB, PostgreSQL, and connector registry checks concurrently with `READINESS_DEPENDENCY_TIMEOUT_SECONDS`, returning structured unhealthy timeout details instead of exceeding the Kubernetes readiness probe window. `/health` remains process-only liveness. + +- Issue #1107 / PR #1103 AGC backend readiness: corrected Flux HelmRelease desired state so the direct AGC gate has ready backends, pinning `crud-service` to a pullable ACR image tag and increasing `inventory-health-check` memory to the shared chart default for the AKS-hosted Responses adapter. + +- Issue #1107 / PR #1103 preview deploy path: inventory-health-check service-scoped AKS deploys now use the `branch` GitHub Environment, propagate changed-agent services into AGC readiness validation, and pin the Flux HelmRelease desired state to tested image tag `ae0201b1de5f16eba281d57b543b25a31f71a5df` with the AKS Responses adapter explicitly enabled. + - Issue #1099: deploy-azd workflow was `startup_failure`-ing on every dispatch (17 startup_failures + 29 cancellations + 0 successes in the last 1,000 runs across all 27 per-service entrypoints since PR #1097 merged). Root cause: PR #1097 added an `open-image-tag-bump-pr` job to the reusable `deploy-azd.yml` that requested `permissions.pull-requests: write`, but the per-service entrypoints only grant `id-token | contents | issues: write` on their `uses:` job. GitHub Actions rejects nested-workflow callees that escalate permissions beyond what the caller grants, and the rejection happens at the orchestrator before any runner is allocated — invisible to `actionlint` and `yaml.safe_load`. Scoped revert removes the job per ADR-017 §"Phase 2b" (proper Flux `ImageUpdateAutomation` + Notification Controller bridge is tracked separately). Also adds `permissions: { id-token, contents, issues }: write` to `deploy-azd-prod.yml` (latent bug — prod tag pushes would have hit the same failure). New CI gate `.github/workflows/lint-actions.yml` runs `actionlint` plus a custom `scripts/ci/lint_workflow_permissions.py` that catches caller/callee permission-cap mismatches statically at PR time. - Issue #801 / PR #802: replaced `FoundryInvoker` with `FoundryAgentInvoker` wrapping the Microsoft Agent Framework `FoundryAgent` runtime. Tools are now properly forwarded to the agent instead of being silently dropped. Upgraded `agent-framework` to `>=1.0.1` GA across all 27 service packages. diff --git a/apps/README.md b/apps/README.md index 2b6d79b81..76698661b 100644 --- a/apps/README.md +++ b/apps/README.md @@ -10,6 +10,8 @@ The product is composed of: - **26 agent services** — bounded-context AI agents with dynamic planning, MCP tools, three-tier memory, SLM-first routing - **1 Next.js frontend** (`ui`) — operator/admin/retail surfaces consumed by both agents and humans +Foundry exposure is classified in [foundry-surfaces.yaml](foundry-surfaces.yaml): public or human-facing agents use Hosted Agent manifests (`agent.hosted.yaml`) over the shared Responses adapter, while non-public internal agents use Custom Agent metadata that proxies the existing APIM -> AGC -> AKS endpoint. This is an exposure taxonomy only; AKS remains the product runtime under the current ADRs. + Changes to apps are **product changes**: they require domain reasoning, eval impact analysis, SLO awareness, and operational runbook updates when behavior changes. ## App Inventory diff --git a/apps/crm-campaign-intelligence/.foundry/agent-metadata.yaml b/apps/crm-campaign-intelligence/.foundry/agent-metadata.yaml index 7cda3778f..8db0b133c 100644 --- a/apps/crm-campaign-intelligence/.foundry/agent-metadata.yaml +++ b/apps/crm-campaign-intelligence/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/crm-campaign-intelligence defaultEnvironment: dev environments: dev: diff --git a/apps/crm-profile-aggregation/.foundry/agent-metadata.yaml b/apps/crm-profile-aggregation/.foundry/agent-metadata.yaml index 35c131bd0..cf447469f 100644 --- a/apps/crm-profile-aggregation/.foundry/agent-metadata.yaml +++ b/apps/crm-profile-aggregation/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/crm-profile-aggregation defaultEnvironment: dev environments: dev: diff --git a/apps/crm-segmentation-personalization/.foundry/agent-metadata.yaml b/apps/crm-segmentation-personalization/.foundry/agent-metadata.yaml index 736a253f1..79d96791d 100644 --- a/apps/crm-segmentation-personalization/.foundry/agent-metadata.yaml +++ b/apps/crm-segmentation-personalization/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/crm-segmentation-personalization defaultEnvironment: dev environments: dev: diff --git a/apps/crm-support-assistance/agent.hosted.yaml b/apps/crm-support-assistance/agent.hosted.yaml new file mode 100644 index 000000000..42402424d --- /dev/null +++ b/apps/crm-support-assistance/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: crm-support-assistance +description: > + Foundry Hosted Agent surface for public support assistance. It runs the + existing FastAPI app and shared Responses adapter; AKS remains the product + runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn crm_support_assistance.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/crm-support-assistance/src/pyproject.toml b/apps/crm-support-assistance/src/pyproject.toml index df169c74a..c75bc499d 100644 --- a/apps/crm-support-assistance/src/pyproject.toml +++ b/apps/crm-support-assistance/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "CRM support assistance" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/crm-support-assistance/src/requirements.txt b/apps/crm-support-assistance/src/requirements.txt index d2bb7af5a..02b77820b 100644 --- a/apps/crm-support-assistance/src/requirements.txt +++ b/apps/crm-support-assistance/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # crm-support-assistance # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # crm-support-assistance - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via crm-support-assistance +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # crm-support-assistance @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,29 +163,40 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # crm-support-assistance # holiday-peak-lib azure-eventhub==5.15.1 # via # crm-support-assistance # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter # crm-support-assistance + # durabletask-azuremanaged # holiday-peak-lib azure-keyvault-secrets==4.10.0 # via holiday-peak-lib azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # crm-support-assistance @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # crm-support-assistance # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # crm-support-assistance # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # crm-support-assistance # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # crm-support-assistance # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # crm-support-assistance # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # crm-support-assistance # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/crm-support-assistance/src/uv.lock b/apps/crm-support-assistance/src/uv.lock index eaca6f62c..c5041d224 100644 --- a/apps/crm-support-assistance/src/uv.lock +++ b/apps/crm-support-assistance/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1150,6 +1227,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1188,6 +1266,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/crud-service/src/crud_service/config/settings.py b/apps/crud-service/src/crud_service/config/settings.py index 21a0d65ec..50ff088bd 100644 --- a/apps/crud-service/src/crud_service/config/settings.py +++ b/apps/crud-service/src/crud_service/config/settings.py @@ -54,6 +54,21 @@ class Settings(BaseSettings): default=8.0, description="Timeout for repository PostgreSQL queries (seconds)", ) + postgres_pool_startup_timeout_seconds: float = Field( + default=10.0, + gt=0, + description="Bounded startup timeout for PostgreSQL pool initialization (seconds)", + ) + key_vault_secret_startup_timeout_seconds: float = Field( + default=3.0, + gt=0, + description="Bounded startup timeout for Key Vault secret retrieval (seconds)", + ) + readiness_dependency_timeout_seconds: float = Field( + default=2.0, + gt=0, + description="Bounded per-dependency timeout for readiness checks (seconds)", + ) # Azure Event Hubs (use Managed Identity, no connection string) event_hub_namespace: str = Field( diff --git a/apps/crud-service/src/crud_service/main.py b/apps/crud-service/src/crud_service/main.py index 069b88c70..b8c9df67a 100644 --- a/apps/crud-service/src/crud_service/main.py +++ b/apps/crud-service/src/crud_service/main.py @@ -4,7 +4,9 @@ for transactional data operations and user-facing endpoints. """ +import asyncio import os +from collections.abc import Awaitable from contextlib import asynccontextmanager from importlib import import_module from typing import Any @@ -34,6 +36,23 @@ logger = configure_logging(app_name=settings.service_name) +async def _await_startup_dependency( + dependency_name: str, + awaitable: Awaitable[Any], + timeout_seconds: float, +) -> Any: + try: + return await asyncio.wait_for(awaitable, timeout=timeout_seconds) + except TimeoutError as exc: + raise TimeoutError( + f"{dependency_name} exceeded {timeout_seconds:g}s startup timeout" + ) from exc + + +def _format_startup_error(exc: Exception) -> str: + return f"{type(exc).__name__}: {exc}" + + def configure_optional_telemetry(connection_string: str | None) -> None: """Configure Application Insights telemetry when dependency is available.""" if not connection_string: @@ -41,10 +60,7 @@ def configure_optional_telemetry(connection_string: str | None) -> None: try: azure_monitor_module = import_module("azure.monitor.opentelemetry") - configure_azure_monitor = getattr( - azure_monitor_module, - "configure_azure_monitor", - ) + configure_azure_monitor = azure_monitor_module.configure_azure_monitor configure_azure_monitor( connection_string=connection_string, logger_name="crud_service", @@ -60,83 +76,102 @@ def configure_optional_telemetry(connection_string: str | None) -> None: def create_connector_registry() -> Any | None: try: connector_module = import_module("holiday_peak_lib.connectors.registry") - registry_class = getattr(connector_module, "ConnectorRegistry") + registry_class = connector_module.ConnectorRegistry return registry_class() except (ImportError, AttributeError) as exc: logger.warning("Connector registry unavailable; skipping connector bootstrap: %s", exc) return None -@asynccontextmanager -async def lifespan(_app: FastAPI): - """Application lifespan manager - startup and shutdown logic.""" - # Startup - logger.info("Starting CRUD Service...") - logger.info("Environment: %s", settings.environment) - logger.info("Service Name: %s", settings.service_name) - - configure_optional_telemetry(settings.app_insights_connection_string) - - # Initialize event publisher - event_publisher = get_event_publisher() - await event_publisher.start() - logger.info("Event publisher started") - - connector_sync_consumer = get_connector_sync_consumer() - await connector_sync_consumer.start() - _app.state.connector_sync_consumer = connector_sync_consumer - logger.info("Connector sync consumer initialized") - +async def _bootstrap_connector_registry(_app: FastAPI) -> None: configured_domains = [ item.strip().lower() for item in (os.getenv("CONNECTOR_ENABLED_DOMAINS", "").split(",")) if item.strip() ] - if configured_domains: - connector_registry = create_connector_registry() - if connector_registry is not None: - discovered = await connector_registry.discover() - logger.info("Connector classes discovered: %s", discovered) - for domain in configured_domains: - try: - await connector_registry.create(domain) - logger.info("Connector created for domain '%s'", domain) - except ValueError as exc: - logger.warning("Connector bootstrap skipped for domain '%s': %s", domain, exc) - - health_interval = float(os.getenv("CONNECTOR_HEALTH_INTERVAL_SECONDS", "60")) - await connector_registry.start_health_monitor(interval_seconds=health_interval) - _app.state.connector_registry = connector_registry - logger.info("Connector health monitor started") - - # Resolve DB credentials from Key Vault only for password mode - if settings.postgres_auth_mode == "password" and not settings.postgres_password: - settings.postgres_password = await get_key_vault_secret( - settings.postgres_password_secret_name - ) + if not configured_domains: + return + + connector_registry = create_connector_registry() + if connector_registry is None: + return + + discovered = await connector_registry.discover() + logger.info("Connector classes discovered: %s", discovered) + for domain in configured_domains: + try: + await connector_registry.create(domain) + logger.info("Connector created for domain '%s'", domain) + except ValueError as exc: + logger.warning("Connector bootstrap skipped for domain '%s': %s", domain, exc) + + health_interval = float(os.getenv("CONNECTOR_HEALTH_INTERVAL_SECONDS", "60")) + await connector_registry.start_health_monitor(interval_seconds=health_interval) + _app.state.connector_registry = connector_registry + logger.info("Connector health monitor started") + + +async def _resolve_postgres_password(_app: FastAPI) -> None: if settings.postgres_auth_mode == "entra": logger.info("PostgreSQL auth mode: Entra token") + return + + if settings.postgres_password: + return - if not settings.redis_password: - try: - settings.redis_password = await get_key_vault_secret( - settings.redis_password_secret_name - ) - logger.info( - "Redis password loaded from Key Vault secret '%s'", - settings.redis_password_secret_name, - ) - except (AzureError, ImportError, ModuleNotFoundError, RuntimeError, ValueError) as exc: - logger.warning( - "Redis password secret retrieval failed for '%s': %s", - settings.redis_password_secret_name, - exc, - ) - - # Initialize PostgreSQL connection pool - _app.state.db_pool_init_error = None try: - await BaseRepository.initialize_pool() + settings.postgres_password = await _await_startup_dependency( + "PostgreSQL password secret retrieval", + get_key_vault_secret(settings.postgres_password_secret_name), + settings.key_vault_secret_startup_timeout_seconds, + ) + except (AzureError, RuntimeError, TimeoutError, ValueError) as exc: + _app.state.db_pool_init_error = _format_startup_error(exc) + logger.warning( + "PostgreSQL password secret retrieval failed for '%s': %s", + settings.postgres_password_secret_name, + exc, + ) + + +async def _resolve_redis_password(_app: FastAPI) -> None: + if settings.redis_password: + return + + try: + settings.redis_password = await _await_startup_dependency( + "Redis password secret retrieval", + get_key_vault_secret(settings.redis_password_secret_name), + settings.key_vault_secret_startup_timeout_seconds, + ) + _app.state.redis_secret_init_error = None + logger.info( + "Redis password loaded from Key Vault secret '%s'", + settings.redis_password_secret_name, + ) + except ( + AzureError, + ImportError, + ModuleNotFoundError, + RuntimeError, + TimeoutError, + ValueError, + ) as exc: + _app.state.redis_secret_init_error = _format_startup_error(exc) + logger.warning( + "Redis password secret retrieval failed for '%s': %s", + settings.redis_password_secret_name, + exc, + ) + + +async def _initialize_postgres_pool(_app: FastAPI) -> None: + try: + await _await_startup_dependency( + "PostgreSQL pool initialization", + BaseRepository.initialize_pool(), + settings.postgres_pool_startup_timeout_seconds, + ) logger.info("PostgreSQL pool initialized") except ( asyncpg.PostgresError, @@ -147,9 +182,37 @@ async def lifespan(_app: FastAPI): ValueError, AzureError, ) as exc: - _app.state.db_pool_init_error = f"{type(exc).__name__}: {exc}" + _app.state.db_pool_init_error = _format_startup_error(exc) logger.warning("PostgreSQL pool initialization failed: %s", exc) + +@asynccontextmanager +async def lifespan(_app: FastAPI): + """Application lifespan manager - startup and shutdown logic.""" + # Startup + logger.info("Starting CRUD Service...") + logger.info("Environment: %s", settings.environment) + logger.info("Service Name: %s", settings.service_name) + _app.state.db_pool_init_error = None + _app.state.redis_secret_init_error = None + + configure_optional_telemetry(settings.app_insights_connection_string) + + # Initialize event publisher + event_publisher = get_event_publisher() + await event_publisher.start() + logger.info("Event publisher started") + + connector_sync_consumer = get_connector_sync_consumer() + await connector_sync_consumer.start() + _app.state.connector_sync_consumer = connector_sync_consumer + logger.info("Connector sync consumer initialized") + + await _bootstrap_connector_registry(_app) + await _resolve_postgres_password(_app) + await _resolve_redis_password(_app) + await _initialize_postgres_pool(_app) + logger.info("CRUD Service started successfully") yield diff --git a/apps/crud-service/src/crud_service/routes/health.py b/apps/crud-service/src/crud_service/routes/health.py index 863bb2216..3ff87f4cc 100644 --- a/apps/crud-service/src/crud_service/routes/health.py +++ b/apps/crud-service/src/crud_service/routes/health.py @@ -1,7 +1,10 @@ """Health check route.""" +import asyncio import logging import os +from collections.abc import Awaitable, Callable +from typing import Any from crud_service.config.settings import get_settings from crud_service.repositories.base import BaseRepository @@ -11,9 +14,14 @@ router = APIRouter() logger = logging.getLogger(__name__) +ReadinessDetail = str | dict[str, Any] +ReadinessResult = tuple[str, ReadinessDetail] +ReadinessCheck = Callable[[], Awaitable[ReadinessResult]] -async def _check_redis() -> tuple[str, str]: + +async def _check_redis(request: Request) -> tuple[str, str]: """Return (status, detail) for the Redis connection.""" + init_error = getattr(request.app.state, "redis_secret_init_error", None) try: import redis.asyncio as aioredis # type: ignore[import] @@ -21,9 +29,14 @@ async def _check_redis() -> tuple[str, str]: client = aioredis.Redis.from_url(redis_url, socket_timeout=2) await client.ping() await client.aclose() + if init_error: + logger.info("Redis recovered after startup secret retrieval error: %s", init_error) + request.app.state.redis_secret_init_error = None return "healthy", "ping ok" except Exception as exc: # pylint: disable=broad-except logger.warning("health_check redis error: %s", exc) + if init_error: + return "unhealthy", f"{init_error}; latest: {exc}" return "unhealthy", str(exc) @@ -64,6 +77,60 @@ async def _check_postgres(request: Request) -> tuple[str, str]: return pool_status, pool_detail +async def _check_connectors(request: Request) -> ReadinessResult: + """Return (status, detail) for runtime connector readiness.""" + connector_registry = getattr(request.app.state, "connector_registry", None) + if connector_registry is None: + return "unconfigured", "No runtime connector registry configured" + + connector_health = await connector_registry.health() + if not connector_health: + return "unconfigured", "No runtime connectors registered" + + unhealthy = [name for name, ok in connector_health.items() if not ok] + return ( + "healthy" if not unhealthy else "unhealthy", + { + "registered": len(connector_health), + "unhealthy": unhealthy, + }, + ) + + +async def _run_readiness_check( + name: str, + check: ReadinessCheck, + timeout_seconds: float, +) -> tuple[str, dict[str, Any]]: + """Apply the readiness timeout policy around one dependency check.""" + try: + status, detail = await asyncio.wait_for(check(), timeout=timeout_seconds) + return name, {"status": status, "detail": detail} + except TimeoutError: + logger.warning( + "readiness_check %s timeout after %.3f seconds", + name, + timeout_seconds, + ) + return name, { + "status": "unhealthy", + "detail": { + "error": "timeout", + "timeout_seconds": timeout_seconds, + "message": (f"{name} readiness check exceeded " f"{timeout_seconds:g} seconds"), + }, + } + except Exception as exc: # pylint: disable=broad-except + logger.warning("readiness_check %s error: %s", name, exc) + return name, { + "status": "unhealthy", + "detail": { + "error": type(exc).__name__, + "message": str(exc), + }, + } + + @router.get("/health") async def health_check(): """Basic liveness endpoint — always returns 200 when the process is up.""" @@ -73,43 +140,24 @@ async def health_check(): @router.get("/ready") async def readiness_check(request: Request): """Readiness probe: checks Redis, Cosmos DB, and PostgreSQL connectivity.""" - checks: dict[str, dict] = {} - overall = "ready" - - postgres_status, postgres_detail = await _check_postgres(request) - checks["postgres"] = {"status": postgres_status, "detail": postgres_detail} - if postgres_status == "unhealthy": - overall = "degraded" - - redis_status, redis_detail = await _check_redis() - checks["redis"] = {"status": redis_status, "detail": redis_detail} - if redis_status == "unhealthy": - overall = "degraded" - - cosmos_status, cosmos_detail = await _check_cosmos() - checks["cosmos"] = {"status": cosmos_status, "detail": cosmos_detail} - if cosmos_status == "unhealthy": - overall = "degraded" - - connector_registry = getattr(request.app.state, "connector_registry", None) - if connector_registry is not None: - connector_health = await connector_registry.health() - if not connector_health: - checks["connectors"] = { - "status": "unconfigured", - "detail": "No runtime connectors registered", - } - else: - unhealthy = [name for name, ok in connector_health.items() if not ok] - checks["connectors"] = { - "status": "healthy" if not unhealthy else "unhealthy", - "detail": { - "registered": len(connector_health), - "unhealthy": unhealthy, - }, - } - if unhealthy: - overall = "degraded" + timeout_seconds = get_settings().readiness_dependency_timeout_seconds + readiness_checks: list[tuple[str, ReadinessCheck]] = [ + ("postgres", lambda: _check_postgres(request)), + ("redis", lambda: _check_redis(request)), + ("cosmos", _check_cosmos), + ] + if getattr(request.app.state, "connector_registry", None) is not None: + readiness_checks.append(("connectors", lambda: _check_connectors(request))) + + check_results = await asyncio.gather( + *(_run_readiness_check(name, check, timeout_seconds) for name, check in readiness_checks) + ) + checks = dict(check_results) + overall = ( + "degraded" + if any(result["status"] == "unhealthy" for result in checks.values()) + else "ready" + ) status_code = 200 if overall == "ready" else 503 return JSONResponse( diff --git a/apps/crud-service/tests/unit/test_health.py b/apps/crud-service/tests/unit/test_health.py index 0d89e9e1b..ce35fc194 100644 --- a/apps/crud-service/tests/unit/test_health.py +++ b/apps/crud-service/tests/unit/test_health.py @@ -1,6 +1,8 @@ """Unit tests for health routes.""" +import asyncio import sys +from time import perf_counter from types import SimpleNamespace import crud_service.routes.health as health_routes @@ -120,7 +122,10 @@ def from_url(url: str, socket_timeout: float): monkeypatch.setattr( health_routes, "get_settings", - lambda: SimpleNamespace(redis_url="rediss://:encoded%40secret@cache:6380/0"), + lambda: SimpleNamespace( + readiness_dependency_timeout_seconds=0.5, + redis_url="rediss://:encoded%40secret@cache:6380/0", + ), ) monkeypatch.setitem( @@ -149,3 +154,42 @@ async def _unconfigured_cosmos(): assert payload["checks"]["redis"]["status"] == "healthy" assert captured["url"] == "rediss://:encoded%40secret@cache:6380/0" assert captured["socket_timeout"] == 2 + + +def test_readiness_times_out_hanging_postgres_health_check(monkeypatch): + """A hung PostgreSQL strategy should fail fast with timeout detail.""" + + async def _hanging_pool(): + await asyncio.Event().wait() + + async def _healthy_redis(_request): + return "healthy", "ping ok" + + async def _unconfigured_cosmos(): + return "unconfigured", "COSMOS_ACCOUNT_URI not set" + + monkeypatch.setattr(BaseRepository, "check_pool_health", _hanging_pool) + monkeypatch.setattr(health_routes, "_check_redis", _healthy_redis) + monkeypatch.setattr(health_routes, "_check_cosmos", _unconfigured_cosmos) + monkeypatch.setattr( + health_routes, + "get_settings", + lambda: SimpleNamespace(readiness_dependency_timeout_seconds=0.01), + ) + + start = perf_counter() + response = client.get("/ready") + elapsed = perf_counter() - start + payload = response.json() + + assert elapsed < 0.5 + assert response.status_code == 503 + assert payload["status"] == "degraded" + assert payload["checks"]["postgres"]["status"] == "unhealthy" + assert payload["checks"]["postgres"]["detail"] == { + "error": "timeout", + "timeout_seconds": 0.01, + "message": "postgres readiness check exceeded 0.01 seconds", + } + assert payload["checks"]["redis"]["status"] == "healthy" + assert payload["checks"]["cosmos"]["status"] == "unconfigured" diff --git a/apps/crud-service/tests/unit/test_main_composition_and_connectors.py b/apps/crud-service/tests/unit/test_main_composition_and_connectors.py index 997bb4ec7..7f6728396 100644 --- a/apps/crud-service/tests/unit/test_main_composition_and_connectors.py +++ b/apps/crud-service/tests/unit/test_main_composition_and_connectors.py @@ -1,10 +1,14 @@ """Tests for CRUD main composition and optional connector wiring.""" +import asyncio import json +import sys from types import SimpleNamespace import crud_service.main as main +import crud_service.routes.health as health_routes import pytest +from fastapi.testclient import TestClient from holiday_peak_lib.utils.event_hub import ( CRITICAL_SAGA_PUBLISH_PROFILE, EventPublishError, @@ -129,6 +133,106 @@ async def _failing_get_secret(secret_name: str) -> str: main.settings.redis_password_secret_name = original_redis_secret_name +@pytest.mark.asyncio +async def test_lifespan_records_postgres_pool_startup_timeout_for_ready( + monkeypatch, +) -> None: + _patch_lifespan_dependencies(monkeypatch) + monkeypatch.setattr(main.settings, "postgres_auth_mode", "entra") + monkeypatch.setattr(main.settings, "redis_password", "redis-secret-value") + monkeypatch.setattr(main.settings, "postgres_pool_startup_timeout_seconds", 0.01) + + async def _hanging_initialize_pool() -> None: + await asyncio.Event().wait() + + async def _unhealthy_pool() -> tuple[str, str]: + return "unhealthy", "pool unavailable" + + async def _healthy_redis(_request) -> tuple[str, str]: + return "healthy", "ping ok" + + async def _unconfigured_cosmos() -> tuple[str, str]: + return "unconfigured", "COSMOS_ACCOUNT_URI not set" + + monkeypatch.setattr(main.BaseRepository, "initialize_pool", _hanging_initialize_pool) + monkeypatch.setattr(main.BaseRepository, "check_pool_health", _unhealthy_pool) + monkeypatch.setattr(health_routes, "_check_redis", _healthy_redis) + monkeypatch.setattr(health_routes, "_check_cosmos", _unconfigured_cosmos) + + async with main.lifespan(main.app): + assert "TimeoutError" in main.app.state.db_pool_init_error + assert "PostgreSQL pool initialization exceeded" in main.app.state.db_pool_init_error + + response = TestClient(main.app).get("/ready") + payload = response.json() + + assert response.status_code == 503 + assert payload["checks"]["postgres"]["status"] == "unhealthy" + assert "PostgreSQL pool initialization exceeded" in payload["checks"]["postgres"]["detail"] + + +@pytest.mark.asyncio +async def test_lifespan_records_redis_secret_timeout_and_ready_reports_redis( + monkeypatch, +) -> None: + _patch_lifespan_dependencies(monkeypatch) + monkeypatch.setattr(main.settings, "postgres_auth_mode", "entra") + monkeypatch.setattr(main.settings, "redis_password", None) + monkeypatch.setattr(main.settings, "redis_password_secret_name", "redis-primary-key") + monkeypatch.setattr(main.settings, "key_vault_secret_startup_timeout_seconds", 0.01) + + async def _hanging_get_secret(secret_name: str) -> str: + if secret_name == "redis-primary-key": + await asyncio.Event().wait() + raise AssertionError("Unexpected secret request") + + class _FakeRedisClient: + async def ping(self) -> None: + raise RuntimeError("NOAUTH Authentication required") + + async def aclose(self) -> None: + return None + + class _FakeRedis: + @staticmethod + def from_url(_url: str, socket_timeout: float): + assert socket_timeout == 2 + return _FakeRedisClient() + + async def _healthy_pool() -> tuple[str, str]: + return "healthy", "query ok" + + async def _unconfigured_cosmos() -> tuple[str, str]: + return "unconfigured", "COSMOS_ACCOUNT_URI not set" + + monkeypatch.setattr(main, "get_key_vault_secret", _hanging_get_secret) + monkeypatch.setattr(main.BaseRepository, "check_pool_health", _healthy_pool) + monkeypatch.setattr(health_routes, "_check_cosmos", _unconfigured_cosmos) + monkeypatch.setitem( + sys.modules, + "redis", + SimpleNamespace(asyncio=SimpleNamespace(Redis=_FakeRedis)), + ) + monkeypatch.setitem( + sys.modules, + "redis.asyncio", + SimpleNamespace(Redis=_FakeRedis), + ) + + async with main.lifespan(main.app): + assert main.settings.redis_password is None + assert "TimeoutError" in main.app.state.redis_secret_init_error + assert "Redis password secret retrieval exceeded" in main.app.state.redis_secret_init_error + + response = TestClient(main.app).get("/ready") + payload = response.json() + + assert response.status_code == 503 + assert payload["checks"]["redis"]["status"] == "unhealthy" + assert "Redis password secret retrieval exceeded" in payload["checks"]["redis"]["detail"] + assert "NOAUTH Authentication required" in payload["checks"]["redis"]["detail"] + + @pytest.mark.asyncio async def test_event_publish_error_handler_preserves_publish_status_code() -> None: error = EventPublishError( diff --git a/apps/crud-service/tests/unit/test_settings_postgres_auth.py b/apps/crud-service/tests/unit/test_settings_postgres_auth.py index 7b92ddacb..cb50d888e 100644 --- a/apps/crud-service/tests/unit/test_settings_postgres_auth.py +++ b/apps/crud-service/tests/unit/test_settings_postgres_auth.py @@ -57,6 +57,29 @@ def test_redis_password_secret_name_defaults_to_infra_secret(monkeypatch) -> Non assert settings.redis_password_secret_name == "redis-primary-key" +def test_startup_dependency_timeouts_default_to_bounded_values(monkeypatch) -> None: + _set_required_env(monkeypatch) + + settings = Settings() + + assert settings.postgres_pool_startup_timeout_seconds == 10.0 + assert settings.key_vault_secret_startup_timeout_seconds == 3.0 + assert settings.readiness_dependency_timeout_seconds == 2.0 + + +def test_startup_dependency_timeouts_can_be_overridden(monkeypatch) -> None: + _set_required_env(monkeypatch) + monkeypatch.setenv("POSTGRES_POOL_STARTUP_TIMEOUT_SECONDS", "1.5") + monkeypatch.setenv("KEY_VAULT_SECRET_STARTUP_TIMEOUT_SECONDS", "0.25") + monkeypatch.setenv("READINESS_DEPENDENCY_TIMEOUT_SECONDS", "0.75") + + settings = Settings() + + assert settings.postgres_pool_startup_timeout_seconds == 1.5 + assert settings.key_vault_secret_startup_timeout_seconds == 0.25 + assert settings.readiness_dependency_timeout_seconds == 0.75 + + def test_redis_url_includes_url_encoded_password_when_present(monkeypatch) -> None: _set_required_env(monkeypatch) raw_password = "p@ss:/ with?#[]" diff --git a/apps/ecommerce-cart-intelligence/agent.hosted.yaml b/apps/ecommerce-cart-intelligence/agent.hosted.yaml new file mode 100644 index 000000000..6af17acfa --- /dev/null +++ b/apps/ecommerce-cart-intelligence/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: ecommerce-cart-intelligence +description: > + Foundry Hosted Agent surface for public cart risk and conversion guidance. + It runs the existing FastAPI app and shared Responses adapter; AKS remains + the product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn ecommerce_cart_intelligence.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/ecommerce-cart-intelligence/src/pyproject.toml b/apps/ecommerce-cart-intelligence/src/pyproject.toml index 1df99d03d..b541a4a49 100644 --- a/apps/ecommerce-cart-intelligence/src/pyproject.toml +++ b/apps/ecommerce-cart-intelligence/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Cart intelligence service" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/ecommerce-cart-intelligence/src/requirements.txt b/apps/ecommerce-cart-intelligence/src/requirements.txt index 03c420344..4df39138f 100644 --- a/apps/ecommerce-cart-intelligence/src/requirements.txt +++ b/apps/ecommerce-cart-intelligence/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # ecommerce-cart-intelligence # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # ecommerce-cart-intelligence - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via ecommerce-cart-intelligence +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # ecommerce-cart-intelligence @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # ecommerce-cart-intelligence # holiday-peak-lib azure-eventhub==5.15.1 # via # ecommerce-cart-intelligence # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # ecommerce-cart-intelligence # holiday-peak-lib azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # ecommerce-cart-intelligence @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # ecommerce-cart-intelligence # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-cart-intelligence # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # ecommerce-cart-intelligence # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # ecommerce-cart-intelligence # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # ecommerce-cart-intelligence # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-cart-intelligence # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/ecommerce-cart-intelligence/src/uv.lock b/apps/ecommerce-cart-intelligence/src/uv.lock index 1cde68426..23d6258f6 100644 --- a/apps/ecommerce-cart-intelligence/src/uv.lock +++ b/apps/ecommerce-cart-intelligence/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1293,6 +1370,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1331,6 +1409,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/ecommerce-catalog-search/agent.hosted.yaml b/apps/ecommerce-catalog-search/agent.hosted.yaml new file mode 100644 index 000000000..2b82489b2 --- /dev/null +++ b/apps/ecommerce-catalog-search/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: ecommerce-catalog-search +description: > + Foundry Hosted Agent surface for public catalog discovery and ACP-aligned + search. It runs the existing FastAPI app and shared Responses adapter; + AKS remains the product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn ecommerce_catalog_search.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/ecommerce-catalog-search/src/pyproject.toml b/apps/ecommerce-catalog-search/src/pyproject.toml index 3582d5442..3bebf874d 100644 --- a/apps/ecommerce-catalog-search/src/pyproject.toml +++ b/apps/ecommerce-catalog-search/src/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", + "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", diff --git a/apps/ecommerce-catalog-search/src/requirements.txt b/apps/ecommerce-catalog-search/src/requirements.txt index feb69934b..9d5c3bcc1 100644 --- a/apps/ecommerce-catalog-search/src/requirements.txt +++ b/apps/ecommerce-catalog-search/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # ecommerce-catalog-search # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # ecommerce-catalog-search - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via ecommerce-catalog-search +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # ecommerce-catalog-search @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # ecommerce-catalog-search # holiday-peak-lib azure-eventhub==5.15.1 # via # ecommerce-catalog-search # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # ecommerce-catalog-search # holiday-peak-lib azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # ecommerce-catalog-search @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # ecommerce-catalog-search # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-catalog-search # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # ecommerce-catalog-search # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # ecommerce-catalog-search # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # ecommerce-catalog-search # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-catalog-search # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/ecommerce-catalog-search/src/uv.lock b/apps/ecommerce-catalog-search/src/uv.lock index 8b79e4fbe..af51ab6c8 100644 --- a/apps/ecommerce-catalog-search/src/uv.lock +++ b/apps/ecommerce-catalog-search/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1293,6 +1370,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1331,6 +1409,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/ecommerce-checkout-support/agent.hosted.yaml b/apps/ecommerce-checkout-support/agent.hosted.yaml new file mode 100644 index 000000000..b9a008ed3 --- /dev/null +++ b/apps/ecommerce-checkout-support/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: ecommerce-checkout-support +description: > + Foundry Hosted Agent surface for public checkout blocker guidance. It runs + the existing FastAPI app and shared Responses adapter; AKS remains the + product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn ecommerce_checkout_support.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/ecommerce-checkout-support/src/pyproject.toml b/apps/ecommerce-checkout-support/src/pyproject.toml index d35a3fe9a..38229e2f8 100644 --- a/apps/ecommerce-checkout-support/src/pyproject.toml +++ b/apps/ecommerce-checkout-support/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Checkout support service" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/ecommerce-checkout-support/src/requirements.txt b/apps/ecommerce-checkout-support/src/requirements.txt index 564c51399..06b04aa63 100644 --- a/apps/ecommerce-checkout-support/src/requirements.txt +++ b/apps/ecommerce-checkout-support/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # ecommerce-checkout-support # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # ecommerce-checkout-support - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via ecommerce-checkout-support +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # ecommerce-checkout-support @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # ecommerce-checkout-support # holiday-peak-lib azure-eventhub==5.15.1 # via # ecommerce-checkout-support # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # ecommerce-checkout-support # holiday-peak-lib azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # ecommerce-checkout-support @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # ecommerce-checkout-support # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-checkout-support # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # ecommerce-checkout-support # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # ecommerce-checkout-support # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # ecommerce-checkout-support # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-checkout-support # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/ecommerce-checkout-support/src/uv.lock b/apps/ecommerce-checkout-support/src/uv.lock index 2b52e11bd..9d86b6a71 100644 --- a/apps/ecommerce-checkout-support/src/uv.lock +++ b/apps/ecommerce-checkout-support/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1293,6 +1370,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1331,6 +1409,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/ecommerce-order-status/agent.hosted.yaml b/apps/ecommerce-order-status/agent.hosted.yaml new file mode 100644 index 000000000..2736699ba --- /dev/null +++ b/apps/ecommerce-order-status/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: ecommerce-order-status +description: > + Foundry Hosted Agent surface for public order and shipment status + intelligence. It runs the existing FastAPI app and shared Responses adapter; + AKS remains the product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn ecommerce_order_status.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/ecommerce-order-status/src/pyproject.toml b/apps/ecommerce-order-status/src/pyproject.toml index 7fc8a8d26..7a59d5da2 100644 --- a/apps/ecommerce-order-status/src/pyproject.toml +++ b/apps/ecommerce-order-status/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Order status and resolution service" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/ecommerce-order-status/src/requirements.txt b/apps/ecommerce-order-status/src/requirements.txt index 0b31e9165..7001d8a30 100644 --- a/apps/ecommerce-order-status/src/requirements.txt +++ b/apps/ecommerce-order-status/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # ecommerce-order-status # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # ecommerce-order-status - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via ecommerce-order-status +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # ecommerce-order-status @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # ecommerce-order-status # holiday-peak-lib azure-eventhub==5.15.1 # via # ecommerce-order-status # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # ecommerce-order-status # holiday-peak-lib azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # ecommerce-order-status @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # ecommerce-order-status # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-order-status # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # ecommerce-order-status # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # ecommerce-order-status # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # ecommerce-order-status # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-order-status # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/ecommerce-order-status/src/uv.lock b/apps/ecommerce-order-status/src/uv.lock index d65965e1e..558586867 100644 --- a/apps/ecommerce-order-status/src/uv.lock +++ b/apps/ecommerce-order-status/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1293,6 +1370,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1331,6 +1409,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/ecommerce-product-detail-enrichment/agent.hosted.yaml b/apps/ecommerce-product-detail-enrichment/agent.hosted.yaml new file mode 100644 index 000000000..2c24c8044 --- /dev/null +++ b/apps/ecommerce-product-detail-enrichment/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: ecommerce-product-detail-enrichment +description: > + Foundry Hosted Agent surface for public product-detail enrichment. It runs + the existing FastAPI app and shared Responses adapter; AKS remains the + product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn ecommerce_product_detail_enrichment.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/ecommerce-product-detail-enrichment/src/pyproject.toml b/apps/ecommerce-product-detail-enrichment/src/pyproject.toml index 0b7bf4099..3c85909a8 100644 --- a/apps/ecommerce-product-detail-enrichment/src/pyproject.toml +++ b/apps/ecommerce-product-detail-enrichment/src/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", + "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", diff --git a/apps/ecommerce-product-detail-enrichment/src/requirements.txt b/apps/ecommerce-product-detail-enrichment/src/requirements.txt index 13d7f2028..16ad46674 100644 --- a/apps/ecommerce-product-detail-enrichment/src/requirements.txt +++ b/apps/ecommerce-product-detail-enrichment/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # ecommerce-product-detail-enrichment # holiday-peak-lib +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # ecommerce-product-detail-enrichment - # holiday-peak-lib + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via ecommerce-product-detail-enrichment +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # ecommerce-product-detail-enrichment @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # ecommerce-product-detail-enrichment # holiday-peak-lib azure-eventhub==5.15.1 # via # ecommerce-product-detail-enrichment # holiday-peak-lib +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # ecommerce-product-detail-enrichment # holiday-peak-lib azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # ecommerce-product-detail-enrichment @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # ecommerce-product-detail-enrichment # holiday-peak-lib +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-product-detail-enrichment # fastapi-mcp # holiday-peak-lib @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # ecommerce-product-detail-enrichment # holiday-peak-lib +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # ecommerce-product-detail-enrichment # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # ecommerce-product-detail-enrichment # holiday-peak-lib + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # ecommerce-product-detail-enrichment # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/ecommerce-product-detail-enrichment/src/uv.lock b/apps/ecommerce-product-detail-enrichment/src/uv.lock index 3f1728a9d..d755d6197 100644 --- a/apps/ecommerce-product-detail-enrichment/src/uv.lock +++ b/apps/ecommerce-product-detail-enrichment/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1293,6 +1370,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1331,6 +1409,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -1592,7 +1671,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1600,7 +1681,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1608,7 +1691,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1779,6 +1864,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1788,6 +1888,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/foundry-surfaces.yaml b/apps/foundry-surfaces.yaml new file mode 100644 index 000000000..288951883 --- /dev/null +++ b/apps/foundry-surfaces.yaml @@ -0,0 +1,46 @@ +version: "1.0" +sourceIssue: "990" +updated: "2026-05-21" +policy: + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + hostedManifest: agent.hosted.yaml + customMetadataPath: .foundry/agent-metadata.yaml +surfaces: + hosted: + classification: Hosted Agent + audience: public-human-facing + createsFoundryManagedCompute: true + agents: + - ecommerce-catalog-search + - ecommerce-cart-intelligence + - ecommerce-checkout-support + - ecommerce-order-status + - ecommerce-product-detail-enrichment + - crm-support-assistance + - inventory-health-check + - logistics-eta-computation + - logistics-returns-support + - truth-hitl + custom: + classification: Custom Agent + audience: non-public-internal + createsFoundryManagedCompute: false + proxyTarget: existing-aks-apim-endpoint + agents: + - crm-campaign-intelligence + - crm-profile-aggregation + - crm-segmentation-personalization + - inventory-alerts-triggers + - inventory-jit-replenishment + - inventory-reservation-validation + - logistics-carrier-selection + - logistics-route-issue-detection + - product-management-acp-transformation + - product-management-assortment-optimization + - product-management-consistency-validation + - product-management-normalization-classification + - search-enrichment-agent + - truth-enrichment + - truth-export + - truth-ingestion \ No newline at end of file diff --git a/apps/inventory-alerts-triggers/.foundry/agent-metadata.yaml b/apps/inventory-alerts-triggers/.foundry/agent-metadata.yaml index 996fc8c95..d2ffa5744 100644 --- a/apps/inventory-alerts-triggers/.foundry/agent-metadata.yaml +++ b/apps/inventory-alerts-triggers/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/inventory-alerts-triggers defaultEnvironment: dev environments: dev: diff --git a/apps/inventory-health-check/agent.hosted.yaml b/apps/inventory-health-check/agent.hosted.yaml new file mode 100644 index 000000000..993384961 --- /dev/null +++ b/apps/inventory-health-check/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: inventory-health-check +description: > + Foundry Hosted Agent surface for public inventory health checks. It runs + the existing FastAPI app and shared Responses adapter; AKS remains the + product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn inventory_health_check.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/inventory-health-check/agent.yaml b/apps/inventory-health-check/agent.yaml index 42f128c96..cabe7c31b 100644 --- a/apps/inventory-health-check/agent.yaml +++ b/apps/inventory-health-check/agent.yaml @@ -1,8 +1,9 @@ name: inventory-health-check description: > Assesses inventory health and anomaly signals. This portal-tracking artifact - describes the existing FastAPI direct-model runtime for Foundry traceability; - deployment and invocation remain owned by the service container. + describes the existing AKS-hosted FastAPI direct-model runtime for Foundry + traceability; deployment and invocation remain owned by the service + container. The Responses protocol is mounted into the same FastAPI app. metadata: trackingOnly: true runtime: fastapi-direct-model @@ -21,6 +22,8 @@ template: version: "1.0.0" - protocol: mcp version: "1.0.0" + - protocol: responses + version: "1.0.0" environment_variables: - name: PROJECT_ENDPOINT value: ${PROJECT_ENDPOINT} diff --git a/apps/inventory-health-check/src/Dockerfile b/apps/inventory-health-check/src/Dockerfile index 86a93cd8f..19cc7612c 100644 --- a/apps/inventory-health-check/src/Dockerfile +++ b/apps/inventory-health-check/src/Dockerfile @@ -37,13 +37,22 @@ ENV PATH="/app/src/.venv/bin:$PATH" RUN uv sync --frozen --all-extras RUN chown -R appuser:appgroup /app USER appuser -CMD ["uvicorn", "inventory_health_check.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] +# Listen on the AKS service port by default. +CMD ["sh", "-c", "uvicorn inventory_health_check.main:app --host 0.0.0.0 --port ${UVICORN_PORT:-8000} --reload"] # ── Stage 3: prod dependency layer ── FROM base AS prod-builder ENV APP_ENV=prod COPY apps/inventory-health-check/src/requirements.txt ./ RUN uv pip install --system --no-cache -r requirements.txt +# Workaround for namespace-collision bug in agent-framework-azure-ai-search==0.0.0a1: +# that legacy preview wheel ships a 0-byte ``agent_framework/__init__.py`` stub +# that overwrites the real one from ``agent-framework-core``, breaking +# ``from agent_framework import BaseAgent`` at container start. Reinstall the +# core package last (``--no-deps`` to avoid resolver churn) so its real +# ``__init__.py`` wins. Remove once the meta-package no longer pulls in the +# broken 0.0.0a1 release. +RUN uv pip install --system --no-cache --force-reinstall --no-deps agent-framework-core==1.3.0 # Install the lib wheel built in stage 0 COPY --from=lib-builder /tmp/lib-dist/holiday_peak_lib-*.whl /tmp/ RUN uv pip install --system --no-deps /tmp/holiday_peak_lib-*.whl \ @@ -74,4 +83,6 @@ COPY --from=prod-builder --chown=appuser:appgroup /app/src /app/src COPY --chown=appuser:appgroup apps/inventory-health-check/prompts/ /app/apps/inventory-health-check/prompts/ ENV WEB_CONCURRENCY=2 USER appuser -CMD ["sh", "-c", "python -m uvicorn inventory_health_check.main:app --host 0.0.0.0 --port 8000 --workers ${WEB_CONCURRENCY:-4}"] +# The Responses adapter is mounted into the same FastAPI app and uses the same +# AKS service port as /health, /ready, /mcp/*, and /invoke. +CMD ["sh", "-c", "python -m uvicorn inventory_health_check.main:app --host 0.0.0.0 --port ${UVICORN_PORT:-8000} --workers ${WEB_CONCURRENCY:-4}"] diff --git a/apps/inventory-health-check/src/inventory_health_check/agents.py b/apps/inventory-health-check/src/inventory_health_check/agents.py index 76ac343f3..8bff4583e 100644 --- a/apps/inventory-health-check/src/inventory_health_check/agents.py +++ b/apps/inventory-health-check/src/inventory_health_check/agents.py @@ -22,13 +22,13 @@ from .adapters import InventoryHealthAdapters, build_inventory_health_adapters -# Pattern: capture SKU identifiers from natural-language Foundry hosted-agent +# Pattern: capture SKU identifiers from natural-language Responses protocol # inputs (e.g. "check health of SKU-123", "is sku ABC-9 healthy?"). Two # alternations: (a) explicit "SKU-XYZ" tokens, (b) "sku " prefixed forms. -_SKU_PATTERN = re.compile( - r"\b(?:SKU[-_]?)([A-Z0-9][A-Z0-9_-]{1,63})\b" r"|\bsku\s+([A-Z0-9][A-Z0-9_-]{1,63})\b", - re.IGNORECASE, +_SKU_PATTERN_TEXT = ( + r"\b(?:SKU[-_]?)([A-Z0-9][A-Z0-9_-]{1,63})\b" + r"|\bsku\s+([A-Z0-9][A-Z0-9_-]{1,63})\b" ) +_SKU_PATTERN = re.compile(_SKU_PATTERN_TEXT, re.IGNORECASE) class InventoryHealthAgent(BaseRetailAgent): @@ -49,8 +49,8 @@ def adapters(self) -> InventoryHealthAdapters: entity_key_field="sku", ) - async def hosted_request_from_text(self, text: str) -> dict[str, Any]: - """Translate Foundry Responses-API free-form input into a ``handle()`` + async def responses_request_from_text(self, text: str) -> dict[str, Any]: + """Translate Responses-API free-form input into a ``handle()`` request dict. ``handle()`` requires a ``sku`` field. We try to extract one from @@ -62,11 +62,11 @@ async def hosted_request_from_text(self, text: str) -> dict[str, Any]: match = _SKU_PATTERN.search(text or "") if match: sku = (match.group(1) or match.group(2) or "").upper() - return {"sku": sku, "_hosted_input_text": text} + return {"sku": sku, "_responses_input_text": text} # No SKU found — surface a structured prompt back to the caller. return { "_no_sku": True, - "_hosted_input_text": text, + "_responses_input_text": text, "sku": None, } @@ -75,7 +75,7 @@ async def handle(self, request: dict[str, Any]) -> dict[str, Any]: return { "error": "sku is required", "hint": ("Provide a SKU id in the prompt, e.g. " "'check health for SKU-1234'."), - "input": request.get("_hosted_input_text"), + "input": request.get("_responses_input_text"), } sku = request.get("sku") diff --git a/apps/inventory-health-check/src/inventory_health_check/main.py b/apps/inventory-health-check/src/inventory_health_check/main.py index 83f7902e8..2cf73a84a 100644 --- a/apps/inventory-health-check/src/inventory_health_check/main.py +++ b/apps/inventory-health-check/src/inventory_health_check/main.py @@ -26,26 +26,25 @@ use_direct_model=True, ) -# Foundry Hosted Agent (preview) — single-process mount that exposes the -# Responses-protocol surface (``/v1/responses``) on the SAME FastAPI app. -# Single uvicorn process, single port, no second runtime — the dual-runtime -# guardrail in ADR-005 (2026-05-10) targets the multi-process / multi-port -# shape and is preserved by this mount pattern. Direct routes registered -# above (``/health``, ``/ready``, ``/mcp/*``) win because Starlette walks -# routes in registration order. +# AKS-hosted Responses adapter — single-process mount that exposes the +# Responses-protocol surface (``/responses``) on the SAME FastAPI app, pod, +# and port as ``/health``, ``/ready``, ``/mcp/*``, and ``/invoke``. Single +# uvicorn process, single runtime, no Foundry-managed container registration. +# Direct routes registered above (``/health``, ``/ready``, ``/mcp/*``) win +# because Starlette walks routes in registration order. # -# Toggleable: set HOLIDAY_PEAK_FOUNDRY_HOSTED=0 to skip mounting (for +# Toggleable: set HOLIDAY_PEAK_AKS_RESPONSES_ENABLED=0 to skip mounting (for # environments where ``agent-framework-foundry-hosting`` is not installed -# or where the operator wants to roll back the pilot without redeploying). -if os.getenv("HOLIDAY_PEAK_FOUNDRY_HOSTED", "1") not in ("0", "false", "False"): +# or where the operator wants to roll back the adapter without redeploying). +if os.getenv("HOLIDAY_PEAK_AKS_RESPONSES_ENABLED", "1") not in ("0", "false", "False"): try: - app.state.agent.serve_hosted(app) + app.state.agent.serve_responses(app) except ImportError: # The optional SDK is not present in this environment — log and # continue. Service still serves /health, /mcp/*, /ready normally. import logging logging.getLogger(SERVICE_NAME).warning( - "foundry_hosted_mount_skipped reason=sdk_missing " - "(install agent-framework-foundry-hosting to enable portal indexing)" + "responses_adapter_mount_skipped reason=sdk_missing " + "(install agent-framework-foundry-hosting to enable /responses)" ) diff --git a/apps/inventory-health-check/src/pyproject.toml b/apps/inventory-health-check/src/pyproject.toml index 1dd76b54f..18f372ea7 100644 --- a/apps/inventory-health-check/src/pyproject.toml +++ b/apps/inventory-health-check/src/pyproject.toml @@ -4,13 +4,13 @@ version = "0.1.0" description = "Inventory health check" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -# ``agent-framework-foundry-hosting`` is the preview ResponsesHostServer SDK -# used by the Foundry Hosted Agent FastAPI-mount pilot (lib/.../hosted.py). -# It is imported lazily inside ``BaseRetailAgent.serve_hosted`` and the mount -# is runtime-gated by ``HOLIDAY_PEAK_FOUNDRY_HOSTED`` (default on, set to ``0`` -# to skip). The pyproject declaration is the contract; lock + requirements -# regeneration must be run with ``uv lock --prerelease=allow`` against the -# preview index that publishes the alpha build. +# ``agent-framework-foundry-hosting`` provides the preview ResponsesHostServer +# used by the AKS-hosted Responses adapter (lib/.../hosted.py). It is imported +# lazily inside ``BaseRetailAgent.serve_responses`` and the mount is +# runtime-gated by ``HOLIDAY_PEAK_AKS_RESPONSES_ENABLED`` (default on, set to +# ``0`` to skip). Lock + requirements regeneration must be run with +# ``uv lock --prerelease=allow`` against the preview index that publishes the +# alpha build. dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] diff --git a/apps/inventory-health-check/tests/test_app.py b/apps/inventory-health-check/tests/test_app.py index 4a368d334..986560321 100644 --- a/apps/inventory-health-check/tests/test_app.py +++ b/apps/inventory-health-check/tests/test_app.py @@ -17,3 +17,24 @@ def test_invoke_requires_sku(): response = client.post("/invoke", json={}) assert response.status_code == 200 assert response.json().get("error") == "sku is required" + + +def test_responses_adapter_mounts_on_same_fastapi_app_when_sdk_present(): + pytest.importorskip("agent_framework_foundry_hosting") + + direct_paths = {getattr(route, "path", "") for route in app.routes} + assert "/health" in direct_paths + assert "/ready" in direct_paths + assert "/invoke" in direct_paths + assert any(path.startswith("/mcp") for path in direct_paths) + + mounted_apps = [route for route in app.routes if getattr(route, "path", "") == ""] + assert mounted_apps, "Responses adapter should mount into the existing FastAPI app" + + host_server = mounted_apps[-1].app + response_paths = { + getattr(route, "path", None) or getattr(route, "path_format", None) + for route in host_server.routes + } + response_paths.discard(None) + assert "/responses" in response_paths diff --git a/apps/inventory-jit-replenishment/.foundry/agent-metadata.yaml b/apps/inventory-jit-replenishment/.foundry/agent-metadata.yaml index e28af67f1..5a9c189f3 100644 --- a/apps/inventory-jit-replenishment/.foundry/agent-metadata.yaml +++ b/apps/inventory-jit-replenishment/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/inventory-jit-replenishment defaultEnvironment: dev environments: dev: diff --git a/apps/inventory-reservation-validation/.foundry/agent-metadata.yaml b/apps/inventory-reservation-validation/.foundry/agent-metadata.yaml index 083155a28..7ccb901ac 100644 --- a/apps/inventory-reservation-validation/.foundry/agent-metadata.yaml +++ b/apps/inventory-reservation-validation/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/inventory-reservation-validation defaultEnvironment: dev environments: dev: diff --git a/apps/logistics-carrier-selection/.foundry/agent-metadata.yaml b/apps/logistics-carrier-selection/.foundry/agent-metadata.yaml index 058986298..370bfe557 100644 --- a/apps/logistics-carrier-selection/.foundry/agent-metadata.yaml +++ b/apps/logistics-carrier-selection/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/logistics-carrier-selection defaultEnvironment: dev environments: dev: diff --git a/apps/logistics-eta-computation/agent.hosted.yaml b/apps/logistics-eta-computation/agent.hosted.yaml new file mode 100644 index 000000000..d09260e65 --- /dev/null +++ b/apps/logistics-eta-computation/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: logistics-eta-computation +description: > + Foundry Hosted Agent surface for public ETA projection and delay-risk + guidance. It runs the existing FastAPI app and shared Responses adapter; + AKS remains the product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn logistics_eta_computation.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/logistics-eta-computation/src/pyproject.toml b/apps/logistics-eta-computation/src/pyproject.toml index eb31a2981..75d0a271d 100644 --- a/apps/logistics-eta-computation/src/pyproject.toml +++ b/apps/logistics-eta-computation/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Logistics ETA computation" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/logistics-eta-computation/src/requirements.txt b/apps/logistics-eta-computation/src/requirements.txt index 29f8f1aa9..b1c2341af 100644 --- a/apps/logistics-eta-computation/src/requirements.txt +++ b/apps/logistics-eta-computation/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # holiday-peak-lib # logistics-eta-computation +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # holiday-peak-lib - # logistics-eta-computation + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via logistics-eta-computation +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # holiday-peak-lib @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # holiday-peak-lib # logistics-eta-computation azure-eventhub==5.15.1 # via # holiday-peak-lib # logistics-eta-computation +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # holiday-peak-lib # logistics-eta-computation azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # holiday-peak-lib @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # holiday-peak-lib # logistics-eta-computation +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # logistics-eta-computation @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # holiday-peak-lib # logistics-eta-computation +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # logistics-eta-computation # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # holiday-peak-lib # logistics-eta-computation + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # logistics-eta-computation # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/logistics-eta-computation/src/uv.lock b/apps/logistics-eta-computation/src/uv.lock index be3208bb4..0329b091c 100644 --- a/apps/logistics-eta-computation/src/uv.lock +++ b/apps/logistics-eta-computation/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1520,7 +1597,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1528,7 +1607,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1536,7 +1617,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1707,6 +1790,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1716,6 +1814,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -1932,6 +2057,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1970,6 +2096,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/logistics-returns-support/agent.hosted.yaml b/apps/logistics-returns-support/agent.hosted.yaml new file mode 100644 index 000000000..1ec76fc5d --- /dev/null +++ b/apps/logistics-returns-support/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: logistics-returns-support +description: > + Foundry Hosted Agent surface for public returns support guidance. It runs + the existing FastAPI app and shared Responses adapter; AKS remains the + product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn logistics_returns_support.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/logistics-returns-support/src/pyproject.toml b/apps/logistics-returns-support/src/pyproject.toml index caf54e21f..2abb48c3e 100644 --- a/apps/logistics-returns-support/src/pyproject.toml +++ b/apps/logistics-returns-support/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Logistics returns support" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/logistics-returns-support/src/requirements.txt b/apps/logistics-returns-support/src/requirements.txt index cc754ef99..19374ac63 100644 --- a/apps/logistics-returns-support/src/requirements.txt +++ b/apps/logistics-returns-support/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # holiday-peak-lib # logistics-returns-support +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # holiday-peak-lib - # logistics-returns-support + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via logistics-returns-support +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # holiday-peak-lib @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # holiday-peak-lib # logistics-returns-support azure-eventhub==5.15.1 # via # holiday-peak-lib # logistics-returns-support +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # holiday-peak-lib # logistics-returns-support azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # holiday-peak-lib @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # holiday-peak-lib # logistics-returns-support +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # logistics-returns-support @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # holiday-peak-lib # logistics-returns-support +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,28 +534,67 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # logistics-returns-support # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl pydantic-core==2.41.5 # via pydantic pydantic-settings==2.13.1 @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # holiday-peak-lib # logistics-returns-support + # redisvl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # logistics-returns-support # mcp + # openai-chatkit +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/logistics-returns-support/src/uv.lock b/apps/logistics-returns-support/src/uv.lock index 0483b3e48..5053eb96a 100644 --- a/apps/logistics-returns-support/src/uv.lock +++ b/apps/logistics-returns-support/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1520,7 +1597,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1528,7 +1607,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1536,7 +1617,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1707,6 +1790,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1716,6 +1814,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -1932,6 +2057,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -1970,6 +2096,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -2485,6 +2612,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2668,6 +2825,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2843,6 +3012,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/logistics-route-issue-detection/.foundry/agent-metadata.yaml b/apps/logistics-route-issue-detection/.foundry/agent-metadata.yaml index 8faeb71a6..839eef05e 100644 --- a/apps/logistics-route-issue-detection/.foundry/agent-metadata.yaml +++ b/apps/logistics-route-issue-detection/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/logistics-route-issue-detection defaultEnvironment: dev environments: dev: diff --git a/apps/product-management-acp-transformation/.foundry/agent-metadata.yaml b/apps/product-management-acp-transformation/.foundry/agent-metadata.yaml index cde8a3abc..2afb150c1 100644 --- a/apps/product-management-acp-transformation/.foundry/agent-metadata.yaml +++ b/apps/product-management-acp-transformation/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/product-management-acp-transformation defaultEnvironment: dev environments: dev: diff --git a/apps/product-management-assortment-optimization/.foundry/agent-metadata.yaml b/apps/product-management-assortment-optimization/.foundry/agent-metadata.yaml index 92d391f03..ede3d7118 100644 --- a/apps/product-management-assortment-optimization/.foundry/agent-metadata.yaml +++ b/apps/product-management-assortment-optimization/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/product-management-assortment-optimization defaultEnvironment: dev environments: dev: diff --git a/apps/product-management-consistency-validation/.foundry/agent-metadata.yaml b/apps/product-management-consistency-validation/.foundry/agent-metadata.yaml index 65834a13c..8d04b8d40 100644 --- a/apps/product-management-consistency-validation/.foundry/agent-metadata.yaml +++ b/apps/product-management-consistency-validation/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/product-management-consistency-validation defaultEnvironment: dev environments: dev: diff --git a/apps/product-management-normalization-classification/.foundry/agent-metadata.yaml b/apps/product-management-normalization-classification/.foundry/agent-metadata.yaml index 7a83326d8..8effe64db 100644 --- a/apps/product-management-normalization-classification/.foundry/agent-metadata.yaml +++ b/apps/product-management-normalization-classification/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/product-management-normalization-classification defaultEnvironment: dev environments: dev: diff --git a/apps/search-enrichment-agent/.foundry/agent-metadata.yaml b/apps/search-enrichment-agent/.foundry/agent-metadata.yaml index 44d7cb994..f63531082 100644 --- a/apps/search-enrichment-agent/.foundry/agent-metadata.yaml +++ b/apps/search-enrichment-agent/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/search-enrichment-agent defaultEnvironment: dev environments: dev: diff --git a/apps/truth-enrichment/.foundry/agent-metadata.yaml b/apps/truth-enrichment/.foundry/agent-metadata.yaml index 459b9b930..c5f4c85d9 100644 --- a/apps/truth-enrichment/.foundry/agent-metadata.yaml +++ b/apps/truth-enrichment/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/truth-enrichment defaultEnvironment: dev environments: dev: diff --git a/apps/truth-export/.foundry/agent-metadata.yaml b/apps/truth-export/.foundry/agent-metadata.yaml index 6b1a3eab3..f64a595a1 100644 --- a/apps/truth-export/.foundry/agent-metadata.yaml +++ b/apps/truth-export/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/truth-export defaultEnvironment: dev environments: dev: diff --git a/apps/truth-hitl/agent.hosted.yaml b/apps/truth-hitl/agent.hosted.yaml new file mode 100644 index 000000000..26f89253d --- /dev/null +++ b/apps/truth-hitl/agent.hosted.yaml @@ -0,0 +1,57 @@ +name: truth-hitl +description: > + Foundry Hosted Agent surface for public human-in-the-loop review support. + It runs the existing FastAPI app and shared Responses adapter; AKS remains + the product runtime for production traffic. +metadata: + surface: + type: hosted + classification: Hosted Agent + audience: public-human-facing + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + replacesProductRuntime: false + sourceIssue: "990" +template: + kind: hosted + startupCommand: "python -m uvicorn truth_hitl.main:app --host 0.0.0.0 --port 8088" + protocols: + - protocol: responses + version: "1.0.0" + environment_variables: + - name: HPH_AGENT_ID_FAST + value: ${HPH_AGENT_ID_FAST} + - name: HPH_AGENT_ID_RICH + value: ${HPH_AGENT_ID_RICH} + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: PROJECT_NAME + value: ${PROJECT_NAME} + - name: MODEL_DEPLOYMENT_NAME_FAST + value: ${MODEL_DEPLOYMENT_NAME_FAST} + - name: MODEL_DEPLOYMENT_NAME_RICH + value: ${MODEL_DEPLOYMENT_NAME_RICH} + - name: HOLIDAY_PEAK_FOUNDRY_HOSTED + value: "1" + - name: UVICORN_PORT + value: "8088" + - name: REDIS_HOST + value: ${REDIS_HOST} + - name: REDIS_URL + value: ${REDIS_URL} + - name: COSMOS_ACCOUNT_URI + value: ${COSMOS_ACCOUNT_URI} + - name: COSMOS_DATABASE + value: ${COSMOS_DATABASE} + - name: COSMOS_CONTAINER + value: ${COSMOS_CONTAINER} + - name: BLOB_ACCOUNT_URL + value: ${BLOB_ACCOUNT_URL} + - name: BLOB_CONTAINER + value: ${BLOB_CONTAINER} + - name: EVENT_HUB_NAMESPACE + value: ${EVENT_HUB_NAMESPACE} + - name: KEY_VAULT_URI + value: ${KEY_VAULT_URI} + - name: APPLICATIONINSIGHTS_CONNECTION_STRING + value: ${APPLICATIONINSIGHTS_CONNECTION_STRING} \ No newline at end of file diff --git a/apps/truth-hitl/src/pyproject.toml b/apps/truth-hitl/src/pyproject.toml index 1de015cf7..0d79e5649 100644 --- a/apps/truth-hitl/src/pyproject.toml +++ b/apps/truth-hitl/src/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "Truth Human-in-the-Loop review service" authors = [{name = "Ricardo Cataldi", email = "rcataldi@microsoft.com"}] requires-python = ">=3.13" -dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] +dependencies = ["fastapi>=0.135.3", "fastapi-mcp", "uvicorn>=0.44.0", "pydantic>=2", "agent-framework>=1.0.1", "agent-framework-foundry-hosting==1.0.0a260507", "azure-ai-projects>=2.0.1", "azure-identity", "azure-search-documents", "azure-cosmos", "azure-storage-blob", "azure-eventhub", "redis>=7.4.0", "asyncpg>=0.30.0", "holiday-peak-lib>=0.2.0"] [project.optional-dependencies] dev = ["faker", "pre-commit", "python-dotenv", "debugpy"] diff --git a/apps/truth-hitl/src/requirements.txt b/apps/truth-hitl/src/requirements.txt index 6ddb9eecd..26a32acdd 100644 --- a/apps/truth-hitl/src/requirements.txt +++ b/apps/truth-hitl/src/requirements.txt @@ -1,21 +1,102 @@ # This file was autogenerated by uv via the following command: # uv export --frozen --no-dev --no-emit-project --no-emit-package holiday-peak-lib --no-hashes -o requirements.txt -agent-framework-core==1.0.1 +a2a-sdk==0.3.23 + # via agent-framework-a2a +ag-ui-protocol==0.1.15 + # via agent-framework-ag-ui +agent-framework==1.5.0 # via - # agent-framework-foundry - # agent-framework-openai # holiday-peak-lib # truth-hitl +agent-framework-a2a==1.0.0b260409 + # via agent-framework-core +agent-framework-ag-ui==1.0.0b260311 + # via agent-framework-core +agent-framework-anthropic==1.0.0b260409 + # via agent-framework-core +agent-framework-azure-ai-search==0.0.0a1 + # via agent-framework-core +agent-framework-azure-cosmos==1.0.0b260409 + # via agent-framework-core +agent-framework-azurefunctions==1.0.0b260409 + # via agent-framework-core +agent-framework-bedrock==1.0.0b260409 + # via agent-framework-core +agent-framework-chatkit==1.0.0b260409 + # via agent-framework-core +agent-framework-claude==1.0.0b260409 + # via agent-framework-core +agent-framework-copilotstudio==1.0.0b260409 + # via agent-framework-core +agent-framework-core==1.5.0 + # via + # agent-framework + # agent-framework-a2a + # agent-framework-ag-ui + # agent-framework-anthropic + # agent-framework-azure-cosmos + # agent-framework-azurefunctions + # agent-framework-bedrock + # agent-framework-chatkit + # agent-framework-claude + # agent-framework-copilotstudio + # agent-framework-declarative + # agent-framework-devui + # agent-framework-durabletask + # agent-framework-foundry + # agent-framework-foundry-hosting + # agent-framework-foundry-local + # agent-framework-github-copilot + # agent-framework-hyperlight + # agent-framework-lab + # agent-framework-mem0 + # agent-framework-ollama + # agent-framework-openai + # agent-framework-orchestrations + # agent-framework-purview + # agent-framework-redis +agent-framework-declarative==1.0.0b260409 + # via agent-framework-core +agent-framework-devui==1.0.0b260311 + # via agent-framework-core +agent-framework-durabletask==1.0.0b260409 + # via + # agent-framework-azurefunctions + # agent-framework-core agent-framework-foundry==1.0.1 - # via - # holiday-peak-lib - # truth-hitl + # via agent-framework-core +agent-framework-foundry-hosting==1.0.0a260507 + # via truth-hitl +agent-framework-foundry-local==1.0.0b260409 + # via agent-framework-core +agent-framework-github-copilot==1.0.0b260409 + # via agent-framework-core +agent-framework-hyperlight==1.0.0b260519 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-core +agent-framework-lab==1.0.0b251024 + # via agent-framework-core +agent-framework-mem0==1.0.0b260409 + # via agent-framework-core +agent-framework-ollama==1.0.0b260409 + # via agent-framework-core agent-framework-openai==1.0.1 - # via agent-framework-foundry + # via + # agent-framework-core + # agent-framework-foundry + # agent-framework-foundry-local +agent-framework-orchestrations==1.0.0b260409 + # via agent-framework-core +agent-framework-purview==1.0.0b260409 + # via agent-framework-core +agent-framework-redis==1.0.0b260311 + # via agent-framework-core aiohappyeyeballs==2.6.1 # via aiohttp aiohttp==3.13.4 - # via holiday-peak-lib + # via + # azure-ai-agentserver-responses + # azure-functions-durable + # holiday-peak-lib aiosignal==1.4.0 # via aiohttp annotated-doc==0.0.4 @@ -24,15 +105,22 @@ annotated-doc==0.0.4 # typer annotated-types==0.7.0 # via pydantic +anthropic==0.80.0 + # via agent-framework-anthropic anyio==4.12.1 # via + # anthropic + # claude-agent-sdk # httpx # mcp # openai # sse-starlette # starlette + # watchfiles asgiref==3.11.1 # via opentelemetry-instrumentation-asgi +asyncio==4.0.0 + # via durabletask asyncpg==0.31.0 # via # holiday-peak-lib @@ -42,6 +130,15 @@ attrs==26.1.0 # aiohttp # jsonschema # referencing +azure-ai-agentserver-core==2.0.0b3 + # via + # agent-framework-foundry-hosting + # azure-ai-agentserver-invocations + # azure-ai-agentserver-responses +azure-ai-agentserver-invocations==1.0.0b3 + # via agent-framework-foundry-hosting +azure-ai-agentserver-responses==1.0.0b5 + # via agent-framework-foundry-hosting azure-ai-inference==1.0.0b9 # via agent-framework-foundry azure-ai-projects==2.0.1 @@ -53,6 +150,8 @@ azure-common==1.1.28 # via azure-search-documents azure-core==1.39.0 # via + # agent-framework-purview + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-core-tracing-opentelemetry @@ -64,21 +163,30 @@ azure-core==1.39.0 # azure-monitor-opentelemetry-exporter # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest azure-core-tracing-opentelemetry==1.0.0b12 # via azure-monitor-opentelemetry azure-cosmos==4.15.0 # via + # agent-framework-azure-cosmos # holiday-peak-lib # truth-hitl azure-eventhub==5.15.1 # via # holiday-peak-lib # truth-hitl +azure-functions==1.24.0 + # via + # agent-framework-azurefunctions + # azure-functions-durable +azure-functions-durable==1.5.0 + # via agent-framework-azurefunctions azure-identity==1.25.3 # via # azure-ai-projects # azure-monitor-opentelemetry-exporter + # durabletask-azuremanaged # holiday-peak-lib # truth-hitl azure-keyvault-secrets==4.10.0 @@ -86,7 +194,9 @@ azure-keyvault-secrets==4.10.0 azure-monitor-opentelemetry==1.8.7 # via holiday-peak-lib azure-monitor-opentelemetry-exporter==1.0.0b49 - # via azure-monitor-opentelemetry + # via + # azure-ai-agentserver-core + # azure-monitor-opentelemetry azure-search-documents==11.6.0 # via # holiday-peak-lib @@ -96,34 +206,65 @@ azure-storage-blob==12.28.0 # azure-ai-projects # holiday-peak-lib # truth-hitl +backoff==2.2.1 + # via posthog +boto3==1.42.89 + # via agent-framework-bedrock +botocore==1.42.89 + # via + # agent-framework-bedrock + # boto3 + # s3transfer certifi==2026.2.25 # via # httpcore # httpx # msrest # requests -cffi==2.0.0 ; platform_python_implementation != 'PyPy' - # via cryptography +cffi==2.0.0 ; python_full_version < '3.14' or platform_python_implementation != 'PyPy' + # via + # clr-loader + # cryptography + # powerfx charset-normalizer==3.4.6 # via requests +claude-agent-sdk==0.1.48 + # via agent-framework-claude click==8.3.1 # via # typer # uvicorn +clr-loader==0.2.10 ; python_full_version < '3.14' + # via pythonnet colorama==0.4.6 ; sys_platform == 'win32' # via # click # tqdm + # uvicorn cryptography==46.0.7 # via # azure-identity # azure-storage-blob + # google-auth # msal # pyjwt distro==1.9.0 - # via openai + # via + # anthropic + # openai + # posthog +docstring-parser==0.18.0 + # via anthropic +durabletask==1.4.0 + # via + # agent-framework-durabletask + # durabletask-azuremanaged +durabletask-azuremanaged==1.4.0 + # via agent-framework-durabletask fastapi==0.135.3 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # truth-hitl @@ -131,24 +272,75 @@ fastapi-mcp==0.4.0 # via # holiday-peak-lib # truth-hitl +foundry-local-sdk==0.5.1 + # via agent-framework-foundry-local frozenlist==1.8.0 # via # aiohttp # aiosignal +furl==2.1.4 + # via azure-functions-durable +github-copilot-sdk==0.2.1 + # via agent-framework-github-copilot +google-api-core==2.30.3 + # via a2a-sdk +google-auth==2.49.2 + # via google-api-core +googleapis-common-protos==1.74.0 + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc +greenlet==3.4.0 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + # via sqlalchemy +griffelib==2.0.2 + # via openai-agents +grpcio==1.80.0 + # via + # durabletask + # opentelemetry-exporter-otlp-proto-grpc + # qdrant-client h11==0.16.0 # via # httpcore + # hypercorn # uvicorn + # wsproto +h2==4.3.0 + # via + # httpx + # hypercorn +hpack==4.1.0 + # via h2 httpcore==1.0.9 # via httpx +httptools==0.7.1 + # via uvicorn httpx==0.28.1 # via + # a2a-sdk + # agent-framework-purview + # anthropic # fastapi-mcp + # foundry-local-sdk # holiday-peak-lib # mcp + # ollama # openai + # qdrant-client httpx-sse==0.4.3 - # via mcp + # via + # a2a-sdk + # mcp +hypercorn==0.18.0 + # via azure-ai-agentserver-core +hyperframe==6.1.0 + # via h2 +hyperlight-sandbox==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-backend-wasm==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight +hyperlight-sandbox-python-guest==0.4.0 ; (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') + # via agent-framework-hyperlight idna==3.11 # via # anyio @@ -159,24 +351,54 @@ importlib-metadata==8.7.1 # via opentelemetry-api isodate==0.7.2 # via + # azure-ai-agentserver-responses # azure-ai-inference # azure-ai-projects # azure-keyvault-secrets # azure-search-documents # azure-storage-blob + # microsoft-agents-hosting-core # msrest +jinja2==3.1.6 + # via openai-chatkit jiter==0.13.0 - # via openai + # via + # anthropic + # openai +jmespath==1.1.0 + # via + # boto3 + # botocore +jsonpath-ng==1.8.0 + # via redisvl jsonschema==4.26.0 # via mcp jsonschema-specifications==2025.9.1 # via jsonschema markdown-it-py==4.0.0 # via rich +markupsafe==3.0.3 + # via + # jinja2 + # werkzeug mcp==1.26.0 - # via fastapi-mcp + # via + # agent-framework-core + # claude-agent-sdk + # fastapi-mcp + # openai-agents mdurl==0.1.2 # via markdown-it-py +mem0ai==1.0.11 + # via agent-framework-mem0 +microsoft-agents-activity==0.3.1 + # via microsoft-agents-hosting-core +microsoft-agents-copilotstudio-client==0.3.1 + # via agent-framework-copilotstudio +microsoft-agents-hosting-core==0.3.1 + # via microsoft-agents-copilotstudio-client +ml-dtypes==0.5.4 + # via redisvl msal==1.35.1 # via # azure-identity @@ -189,18 +411,36 @@ multidict==6.7.1 # via # aiohttp # yarl +numpy==2.4.4 + # via + # agent-framework-redis + # ml-dtypes + # qdrant-client + # redisvl oauthlib==3.3.1 # via requests-oauthlib +ollama==0.5.3 + # via agent-framework-ollama openai==2.29.0 # via # agent-framework-openai # azure-ai-projects + # mem0ai + # openai-agents + # openai-chatkit +openai-agents==0.13.6 + # via openai-chatkit +openai-chatkit==1.6.3 + # via agent-framework-chatkit opentelemetry-api==1.40.0 # via # agent-framework-core + # azure-ai-agentserver-core # azure-core-tracing-opentelemetry + # azure-functions-durable # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-instrumentation # opentelemetry-instrumentation-asgi # opentelemetry-instrumentation-dbapi @@ -215,6 +455,10 @@ opentelemetry-api==1.40.0 # opentelemetry-instrumentation-wsgi # opentelemetry-sdk # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp-proto-common==1.40.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.40.0 + # via azure-ai-agentserver-core opentelemetry-instrumentation==0.61b0 # via # opentelemetry-instrumentation-asgi @@ -252,13 +496,20 @@ opentelemetry-instrumentation-wsgi==0.61b0 # via # opentelemetry-instrumentation-django # opentelemetry-instrumentation-flask +opentelemetry-proto==1.40.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc opentelemetry-resource-detector-azure==0.1.5 # via azure-monitor-opentelemetry opentelemetry-sdk==1.40.0 # via + # azure-ai-agentserver-core + # azure-functions-durable # azure-monitor-opentelemetry # azure-monitor-opentelemetry-exporter # holiday-peak-lib + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-resource-detector-azure opentelemetry-semantic-conventions==0.61b0 # via @@ -283,27 +534,66 @@ opentelemetry-util-http==0.61b0 # opentelemetry-instrumentation-urllib # opentelemetry-instrumentation-urllib3 # opentelemetry-instrumentation-wsgi +orderedmultidict==1.0.2 + # via furl packaging==26.0 # via + # durabletask # opentelemetry-instrumentation # opentelemetry-instrumentation-flask +portalocker==3.2.0 + # via qdrant-client +posthog==7.12.0 + # via mem0ai +powerfx==0.0.34 ; python_full_version < '3.14' + # via agent-framework-declarative +priority==2.0.0 + # via hypercorn propcache==0.4.1 # via # aiohttp # yarl +proto-plus==1.27.2 + # via google-api-core +protobuf==6.33.6 + # via + # a2a-sdk + # durabletask + # google-api-core + # googleapis-common-protos + # mem0ai + # opentelemetry-proto + # proto-plus + # qdrant-client psutil==7.2.2 # via azure-monitor-opentelemetry-exporter -pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' +pyasn1==0.6.3 + # via pyasn1-modules +pyasn1-modules==0.4.2 + # via google-auth +pycparser==3.0 ; (python_full_version < '3.14' and implementation_name != 'PyPy') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pydantic==2.12.5 # via + # a2a-sdk + # ag-ui-protocol # agent-framework-core + # anthropic # fastapi # fastapi-mcp + # foundry-local-sdk + # github-copilot-sdk # holiday-peak-lib # mcp + # mem0ai + # microsoft-agents-activity + # ollama # openai + # openai-agents + # openai-chatkit # pydantic-settings + # qdrant-client + # redisvl # truth-hitl pydantic-core==2.41.5 # via pydantic @@ -317,22 +607,51 @@ pygments==2.19.2 pyjwt==2.12.1 # via # mcp + # microsoft-agents-hosting-core # msal +python-dateutil==2.9.0.post0 + # via + # agent-framework-durabletask + # azure-functions-durable + # botocore + # github-copilot-sdk + # posthog python-dotenv==1.2.2 # via # agent-framework-core + # agent-framework-devui # holiday-peak-lib + # microsoft-agents-hosting-core # pydantic-settings + # uvicorn python-multipart==0.0.26 # via mcp +python-ulid==3.1.0 + # via redisvl +pythonnet==3.0.5 ; python_full_version < '3.14' + # via powerfx +pytz==2026.1.post1 + # via mem0ai pywin32==311 ; sys_platform == 'win32' - # via mcp + # via + # mcp + # portalocker pyyaml==6.0.3 - # via holiday-peak-lib + # via + # agent-framework-declarative + # holiday-peak-lib + # redisvl + # uvicorn +qdrant-client==1.17.1 + # via mem0ai redis==7.4.0 # via + # agent-framework-redis # holiday-peak-lib + # redisvl # truth-hitl +redisvl==0.17.0 + # via agent-framework-redis referencing==0.37.0 # via # jsonschema @@ -340,9 +659,13 @@ referencing==0.37.0 requests==2.33.1 # via # azure-core + # azure-functions-durable # fastapi-mcp + # google-api-core # msal # msrest + # openai-agents + # posthog # requests-oauthlib requests-oauthlib==2.0.0 # via msrest @@ -354,26 +677,46 @@ rpds-py==0.30.0 # via # jsonschema # referencing +s3transfer==0.16.0 + # via boto3 shellingham==1.5.4 # via typer +six==1.17.0 + # via + # furl + # orderedmultidict + # posthog + # python-dateutil sniffio==1.3.1 - # via openai + # via + # anthropic + # openai +sqlalchemy==2.0.49 + # via mem0ai sse-starlette==3.3.3 # via mcp starlette==0.52.1 # via + # azure-ai-agentserver-core # fastapi # mcp # sse-starlette +tenacity==9.1.4 + # via redisvl tomli==2.4.0 # via fastapi-mcp tqdm==4.67.3 - # via openai + # via + # foundry-local-sdk + # openai typer==0.24.1 # via fastapi-mcp +types-requests==2.33.0.20260408 + # via openai-agents typing-extensions==4.15.0 # via # agent-framework-core + # anthropic # azure-ai-inference # azure-ai-projects # azure-core @@ -384,14 +727,19 @@ typing-extensions==4.15.0 # azure-search-documents # azure-storage-blob # fastapi + # grpcio # holiday-peak-lib # mcp # openai + # openai-agents # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc # opentelemetry-sdk # opentelemetry-semantic-conventions + # posthog # pydantic # pydantic-core + # sqlalchemy # typing-inspection typing-inspection==0.4.2 # via @@ -400,18 +748,35 @@ typing-inspection==0.4.2 # pydantic # pydantic-settings urllib3==2.6.3 - # via requests + # via + # botocore + # qdrant-client + # requests + # types-requests uvicorn==0.44.0 # via + # agent-framework-ag-ui + # agent-framework-devui # fastapi-mcp # holiday-peak-lib # mcp + # openai-chatkit # truth-hitl +uvloop==0.22.1 ; platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32' + # via uvicorn +watchfiles==1.1.1 + # via uvicorn +websockets==16.0 + # via uvicorn +werkzeug==3.1.8 + # via azure-functions wrapt==1.17.3 # via # opentelemetry-instrumentation # opentelemetry-instrumentation-dbapi # opentelemetry-instrumentation-urllib3 +wsproto==1.3.2 + # via hypercorn yarl==1.23.0 # via aiohttp zipp==3.23.0 diff --git a/apps/truth-hitl/src/uv.lock b/apps/truth-hitl/src/uv.lock index 6485e43b4..20c0d768a 100644 --- a/apps/truth-hitl/src/uv.lock +++ b/apps/truth-hitl/src/uv.lock @@ -1,11 +1,14 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.13" resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'", ] +[options] +prerelease-mode = "allow" + [[package]] name = "a2a-sdk" version = "0.3.23" @@ -36,14 +39,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0f/dcedaa3520c9a3b850d88b08846d518d10daec02c8eabc18c7b271bc4d28/agent_framework-1.0.1.tar.gz", hash = "sha256:163c319c7d37119849447a9f7e9fab4e0b2d0195523b82d3748f78b78ff97343", size = 4361213, upload-time = "2026-04-10T03:30:59.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/2b/fc64914b8740c6cdbecfb1cd68b2a793af4d793653c920661042f4bac8ff/agent_framework-1.5.0.tar.gz", hash = "sha256:f6f29de2e992d886720256f20d5e0264669acbb2b19f60fa5c78640c3b207899", size = 5299676, upload-time = "2026-05-20T00:28:48.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/9d/a5d278effe7a5ffcc515bf870a9a00704391f0506a1fba11ec3b582ef11c/agent_framework-1.0.1-py3-none-any.whl", hash = "sha256:5f8184613b129363106fe6e04db26075b2d2eca0026da9770dc92bbd9e4a45d6", size = 5686, upload-time = "2026-04-10T03:31:03.825Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/d618e18d55a8d0fec7a00bfaebacba7a514deba4bc8179cf2b08b5253d4b/agent_framework-1.5.0-py3-none-any.whl", hash = "sha256:1341e12df4b780521296358e7fc0e785123f4f0b3eea94ee568badc2afebb68e", size = 5686, upload-time = "2026-05-20T00:28:31.752Z" }, ] [[package]] @@ -179,7 +182,7 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.1" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, @@ -187,9 +190,8 @@ dependencies = [ { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/3d/371e57a74ecd4fc551d458bd234d7591c052b467cac21e8805cb519a4187/agent_framework_core-1.0.1.tar.gz", hash = "sha256:6ace9fa8bee9d2e8556c28ff767d89b8e0a0a734246dcca4a196d0b0bc5cedb0", size = 285179, upload-time = "2026-04-10T03:29:28.193Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/11/a460d6656257c4302deb33724f29a52059bae193758ded7571fb576b26cb/agent_framework_core-1.0.1-py3-none-any.whl", hash = "sha256:8305fadb78adb9b625cda0ba8188bcb76ce01a2aa64eed937f8c9fb384043bc0", size = 323596, upload-time = "2026-04-10T03:31:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/39/a9/748bbc433615db46efab8e780ea8d8fc70a67748071753707ddf6fac9008/agent_framework_core-1.5.0-py3-none-any.whl", hash = "sha256:cc1ccd2e3cefc22f8f933b1d8e53da7752ed735c222e686e8b503c6be61de331", size = 418892, upload-time = "2026-05-20T00:25:08.852Z" }, ] [package.optional-dependencies] @@ -210,6 +212,7 @@ all = [ { name = "agent-framework-foundry" }, { name = "agent-framework-foundry-local" }, { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, @@ -279,6 +282,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/3f/e99c0acb7c2ed64bc948ae7676920c1576865ee8b1e46cf378b27b0de20b/agent_framework_foundry-1.0.1-py3-none-any.whl", hash = "sha256:494bab12300d364ade0de738f12d7509de9536da355182067e2c00758ea17cf4", size = 30705, upload-time = "2026-04-10T04:46:38.188Z" }, ] +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330, upload-time = "2026-05-08T00:09:21.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050, upload-time = "2026-05-08T00:09:19.939Z" }, +] + [[package]] name = "agent-framework-foundry-local" version = "1.0.0b260409" @@ -306,6 +324,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6c/bb/d4a58a6f5cb37c9abdbb6319fb0c6e1e13011f7c10d19d20ebf14f45ac03/agent_framework_github_copilot-1.0.0b260409-py3-none-any.whl", hash = "sha256:dec44490d61e98cfd8d715e40c71b7ae6b615341666314b555d32f4efd41bcd5", size = 9962, upload-time = "2026-04-10T03:26:20.321Z" }, ] +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260519" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/48/4ec920d058d665d90e8e557077edf574e0930e8c8bcbc0f9bdb652f3d2b5/agent_framework_hyperlight-1.0.0b260519.tar.gz", hash = "sha256:a8bd70d279c7b1b603185506c0a1b501257b88abc7bb46aa624f20cfe29cc855", size = 20177, upload-time = "2026-05-20T00:28:25.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/3b/ffece645b0901220b608d9397dedacea601d07e4125e59590be3f4f10cae/agent_framework_hyperlight-1.0.0b260519-py3-none-any.whl", hash = "sha256:7a5bd7951c3ead8473c53c8773bc1246b88c698362382a5c0e89c24b187477a6", size = 20752, upload-time = "2026-05-20T00:28:46.1Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -613,6 +646,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573, upload-time = "2026-04-23T04:13:16.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127, upload-time = "2026-04-23T04:13:17.976Z" }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002, upload-time = "2026-04-23T04:30:29.449Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433, upload-time = "2026-04-23T04:30:30.412Z" }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908, upload-time = "2026-04-23T04:31:15.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362, upload-time = "2026-04-23T04:31:17.014Z" }, +] + [[package]] name = "azure-ai-inference" version = "1.0.0b9" @@ -1520,7 +1597,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/75/7e9cd1126a1e1f0cd67b0eda02e5221b28488d352684704a78ed505bd719/greenlet-3.4.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:43748988b097f9c6f09364f260741aa73c80747f63389824435c7a50bfdfd5c1", size = 285856, upload-time = "2026-04-08T15:52:45.82Z" }, { url = "https://files.pythonhosted.org/packages/9d/c4/3e2df392e5cb199527c4d9dbcaa75c14edcc394b45040f0189f649631e3c/greenlet-3.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5566e4e2cd7a880e8c27618e3eab20f3494452d12fd5129edef7b2f7aa9a36d1", size = 610208, upload-time = "2026-04-08T16:24:39.674Z" }, { url = "https://files.pythonhosted.org/packages/da/af/750cdfda1d1bd30a6c28080245be8d0346e669a98fdbae7f4102aa95fff3/greenlet-3.4.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1054c5a3c78e2ab599d452f23f7adafef55062a783a8e241d24f3b633ba6ff82", size = 621269, upload-time = "2026-04-08T16:30:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/e0/93/c8c508d68ba93232784bbc1b5474d92371f2897dfc6bc281b419f2e0d492/greenlet-3.4.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98eedd1803353daf1cd9ef23eef23eda5a4d22f99b1f998d273a8b78b70dd47f", size = 628455, upload-time = "2026-04-08T16:40:40.698Z" }, { url = "https://files.pythonhosted.org/packages/54/78/0cbc693622cd54ebe25207efbb3a0eb07c2639cb8594f6e3aaaa0bb077a8/greenlet-3.4.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f82cb6cddc27dd81c96b1506f4aa7def15070c3b2a67d4e46fd19016aacce6cf", size = 617549, upload-time = "2026-04-08T15:56:34.893Z" }, + { url = "https://files.pythonhosted.org/packages/7f/46/cfaaa0ade435a60550fd83d07dfd5c41f873a01da17ede5c4cade0b9bab8/greenlet-3.4.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:b7857e2202aae67bc5725e0c1f6403c20a8ff46094ece015e7d474f5f7020b55", size = 426238, upload-time = "2026-04-08T16:43:06.865Z" }, { url = "https://files.pythonhosted.org/packages/ba/c0/8966767de01343c1ff47e8b855dc78e7d1a8ed2b7b9c83576a57e289f81d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:227a46251ecba4ff46ae742bc5ce95c91d5aceb4b02f885487aff269c127a729", size = 1575310, upload-time = "2026-04-08T16:26:21.671Z" }, { url = "https://files.pythonhosted.org/packages/b8/38/bcdc71ba05e9a5fda87f63ffc2abcd1f15693b659346df994a48c968003d/greenlet-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5b99e87be7eba788dd5b75ba1cde5639edffdec5f91fe0d734a249535ec3408c", size = 1640435, upload-time = "2026-04-08T15:57:32.572Z" }, { url = "https://files.pythonhosted.org/packages/a1/c2/19b664b7173b9e4ef5f77e8cef9f14c20ec7fce7920dc1ccd7afd955d093/greenlet-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:849f8bc17acd6295fcb5de8e46d55cc0e52381c56eaf50a2afd258e97bc65940", size = 238760, upload-time = "2026-04-08T17:04:03.878Z" }, @@ -1528,7 +1607,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/02/bde66806e8f169cf90b14d02c500c44cdbe02c8e224c9c67bafd1b8cadd1/greenlet-3.4.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:10a07aca6babdd18c16a3f4f8880acfffc2b88dfe431ad6aa5f5740759d7d75e", size = 286291, upload-time = "2026-04-08T17:09:34.307Z" }, { url = "https://files.pythonhosted.org/packages/05/1f/39da1c336a87d47c58352fb8a78541ce63d63ae57c5b9dae1fe02801bbc2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:076e21040b3a917d3ce4ad68fb5c3c6b32f1405616c4a57aa83120979649bd3d", size = 656749, upload-time = "2026-04-08T16:24:41.721Z" }, { url = "https://files.pythonhosted.org/packages/d3/6c/90ee29a4ee27af7aa2e2ec408799eeb69ee3fcc5abcecac6ddd07a5cd0f2/greenlet-3.4.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e82689eea4a237e530bb5cb41b180ef81fa2160e1f89422a67be7d90da67f615", size = 669084, upload-time = "2026-04-08T16:31:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/d2/4a/74078d3936712cff6d3c91a930016f476ce4198d84e224fe6d81d3e02880/greenlet-3.4.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:06c2d3b89e0c62ba50bd7adf491b14f39da9e7e701647cb7b9ff4c99bee04b19", size = 673405, upload-time = "2026-04-08T16:40:42.527Z" }, { url = "https://files.pythonhosted.org/packages/07/49/d4cad6e5381a50947bb973d2f6cf6592621451b09368b8c20d9b8af49c5b/greenlet-3.4.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4df3b0b2289ec686d3c821a5fee44259c05cfe824dd5e6e12c8e5f5df23085cf", size = 665621, upload-time = "2026-04-08T15:56:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/df8a83ab894751bc31e1106fdfaa80ca9753222f106b04de93faaa55feb7/greenlet-3.4.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:070b8bac2ff3b4d9e0ff36a0d19e42103331d9737e8504747cd1e659f76297bd", size = 471670, upload-time = "2026-04-08T16:43:08.512Z" }, { url = "https://files.pythonhosted.org/packages/37/31/d1edd54f424761b5d47718822f506b435b6aab2f3f93b465441143ea5119/greenlet-3.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bff29d586ea415688f4cec96a591fcc3bf762d046a796cdadc1fdb6e7f2d5bf", size = 1622259, upload-time = "2026-04-08T16:26:23.201Z" }, { url = "https://files.pythonhosted.org/packages/b0/c6/6d3f9cdcb21c4e12a79cb332579f1c6aa1af78eb68059c5a957c7812d95e/greenlet-3.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a569c2fb840c53c13a2b8967c63621fafbd1a0e015b9c82f408c33d626a2fda", size = 1686916, upload-time = "2026-04-08T15:57:34.282Z" }, { url = "https://files.pythonhosted.org/packages/63/45/c1ca4a1ad975de4727e52d3ffe641ae23e1d7a8ffaa8ff7a0477e1827b92/greenlet-3.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:207ba5b97ea8b0b60eb43ffcacf26969dd83726095161d676aac03ff913ee50d", size = 239821, upload-time = "2026-04-08T17:03:48.423Z" }, @@ -1536,7 +1617,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/8f/18d72b629783f5e8d045a76f5325c1e938e659a9e4da79c7dcd10169a48d/greenlet-3.4.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d70012e51df2dbbccfaf63a40aaf9b40c8bed37c3e3a38751c926301ce538ece", size = 294681, upload-time = "2026-04-08T15:52:35.778Z" }, { url = "https://files.pythonhosted.org/packages/9e/ad/5fa86ec46769c4153820d58a04062285b3b9e10ba3d461ee257b68dcbf53/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a58bec0751f43068cd40cff31bb3ca02ad6000b3a51ca81367af4eb5abc480c8", size = 658899, upload-time = "2026-04-08T16:24:43.32Z" }, { url = "https://files.pythonhosted.org/packages/43/f0/4e8174ca0e87ae748c409f055a1ba161038c43cc0a5a6f1433a26ac2e5bf/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05fa0803561028f4b2e3b490ee41216a842eaee11aed004cc343a996d9523aa2", size = 665284, upload-time = "2026-04-08T16:31:02.833Z" }, + { url = "https://files.pythonhosted.org/packages/ef/92/466b0d9afd44b8af623139a3599d651c7564fa4152f25f117e1ee5949ffb/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c4cd56a9eb7a6444edbc19062f7b6fbc8f287c663b946e3171d899693b1c19fa", size = 665872, upload-time = "2026-04-08T16:40:43.912Z" }, { url = "https://files.pythonhosted.org/packages/19/da/991cf7cd33662e2df92a1274b7eb4d61769294d38a1bba8a45f31364845e/greenlet-3.4.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e60d38719cb80b3ab5e85f9f1aed4960acfde09868af6762ccb27b260d68f4ed", size = 661861, upload-time = "2026-04-08T15:56:37.269Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/3395a7ef3e260de0325152ddfe19dffb3e49fe10873b94654352b53ad48e/greenlet-3.4.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:1f85f204c4d54134ae850d401fa435c89cd667d5ce9dc567571776b45941af72", size = 489237, upload-time = "2026-04-08T16:43:09.993Z" }, { url = "https://files.pythonhosted.org/packages/36/c5/6c2c708e14db3d9caea4b459d8464f58c32047451142fe2cfd90e7458f41/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f50c804733b43eded05ae694691c9aa68bca7d0a867d67d4a3f514742a2d53f", size = 1622182, upload-time = "2026-04-08T16:26:24.777Z" }, { url = "https://files.pythonhosted.org/packages/7a/4c/50c5fed19378e11a29fabab1f6be39ea95358f4a0a07e115a51ca93385d8/greenlet-3.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2d4f0635dc4aa638cda4b2f5a07ae9a2cff9280327b581a3fcb6f317b4fbc38a", size = 1685050, upload-time = "2026-04-08T15:57:36.453Z" }, { url = "https://files.pythonhosted.org/packages/db/72/85ae954d734703ab48e622c59d4ce35d77ce840c265814af9c078cacc7aa/greenlet-3.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1a4a48f24681300c640f143ba7c404270e1ebbbcf34331d7104a4ff40f8ea705", size = 245554, upload-time = "2026-04-08T17:03:50.044Z" }, @@ -1707,6 +1790,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420, upload-time = "2025-11-08T13:54:04.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640, upload-time = "2025-11-08T13:54:03.202Z" }, +] + [[package]] name = "hyperframe" version = "6.1.0" @@ -1716,6 +1814,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "identify" version = "2.6.18" @@ -2413,6 +2538,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416, upload-time = "2026-03-04T14:17:23.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369, upload-time = "2026-03-04T14:17:04.796Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759, upload-time = "2026-03-04T14:17:24.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304, upload-time = "2026-03-04T14:17:05.942Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.61b0" @@ -2596,6 +2751,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, ] +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667, upload-time = "2026-03-04T14:17:31.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073, upload-time = "2026-03-04T14:17:16.673Z" }, +] + [[package]] name = "opentelemetry-resource-detector-azure" version = "0.1.5" @@ -2771,6 +2938,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792, upload-time = "2021-06-27T10:15:05.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946, upload-time = "2021-06-27T10:15:03.856Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -3639,6 +3815,7 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "agent-framework" }, + { name = "agent-framework-foundry-hosting" }, { name = "asyncpg" }, { name = "azure-ai-projects" }, { name = "azure-cosmos" }, @@ -3677,6 +3854,7 @@ test = [ [package.metadata] requires-dist = [ { name = "agent-framework", specifier = ">=1.0.1" }, + { name = "agent-framework-foundry-hosting", specifier = "==1.0.0a260507" }, { name = "asyncpg", specifier = ">=0.30.0" }, { name = "azure-ai-projects", specifier = ">=2.0.1" }, { name = "azure-cosmos" }, @@ -3989,6 +4167,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, ] +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116, upload-time = "2025-11-20T18:18:01.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z" }, +] + [[package]] name = "yarl" version = "1.23.0" diff --git a/apps/truth-ingestion/.foundry/agent-metadata.yaml b/apps/truth-ingestion/.foundry/agent-metadata.yaml index 202f7c62a..cd11413f1 100644 --- a/apps/truth-ingestion/.foundry/agent-metadata.yaml +++ b/apps/truth-ingestion/.foundry/agent-metadata.yaml @@ -1,3 +1,13 @@ +surface: + type: custom + classification: Custom Agent + audience: non-public-internal + productRuntime: aks + productTrafficPath: "APIM -> AGC -> AKS" + foundryManagedCompute: false + proxy: + target: existing-aks-apim-endpoint + endpoint: ${APIM_BASE_URL}/agents/truth-ingestion defaultEnvironment: dev environments: dev: diff --git a/apps/ui/app/(builder)/builders/agents/[slug]/page.tsx b/apps/ui/app/(builder)/builders/agents/[slug]/page.tsx new file mode 100644 index 000000000..14bb4c924 --- /dev/null +++ b/apps/ui/app/(builder)/builders/agents/[slug]/page.tsx @@ -0,0 +1,246 @@ +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; + +import { CallToAction } from '@/components/molecules/CallToAction'; +import { CodeBlockCluster } from '@/components/molecules/CodeBlockCluster'; +import { DocsCardCluster } from '@/components/molecules/DocsCardCluster'; +import { Hero } from '@/components/molecules/Hero'; +import { RegistryTable, type RegistryTableRow } from '@/components/molecules/RegistryTable'; +import { getAgentCatalogAgent } from '@/lib/agents/catalog'; +import { + AGENT_PROFILE_SLUGS, + getAgentProfile, + type AgentProfile, + type AgentProfileSlug, +} from '@/lib/agents/profiles'; +import { buildMetadata } from '@/lib/seo'; + +type BuilderAgentPageProps = { + params: Promise<{ slug: string }>; +}; + +export const dynamicParams = false; + +export function generateStaticParams(): Array<{ slug: AgentProfileSlug }> { + return AGENT_PROFILE_SLUGS.map((slug) => ({ slug })); +} + +export async function generateMetadata({ params }: BuilderAgentPageProps): Promise { + const { slug } = await params; + const profile = getAgentProfile(slug); + + if (!profile) { + notFound(); + } + + return buildMetadata({ + section: 'builder', + title: profile.displayName, + description: `${profile.oneLiner} Runtime contract, KPIs, schemas, and collaborators.`, + path: `/builders/agents/${profile.slug}`, + }); +} + +function formatJson(value: unknown): string { + return JSON.stringify(value, null, 2); +} + +function buildRuntimeRows(profile: AgentProfile): RegistryTableRow[] { + const catalogAgent = getAgentCatalogAgent(profile.slug); + + return [ + { + key: 'domain', + cells: [ + { kind: 'text', value: 'Bounded context' }, + { kind: 'text', value: profile.domainLabel }, + ], + }, + { + key: 'mode', + cells: [ + { kind: 'text', value: 'Primary mode' }, + { kind: 'badge', value: profile.primaryMode === 'sync' ? 'Synchronous' : 'Event-driven async' }, + ], + }, + { + key: 'maturity', + cells: [ + { kind: 'text', value: 'Maturity' }, + { kind: 'maturity', level: catalogAgent?.maturity ?? 'internal' }, + ], + }, + { + key: 'trace-explorer', + cells: [ + { kind: 'text', value: 'Trace entry point' }, + { kind: 'link', value: profile.traceExplorerHref, href: profile.traceExplorerHref }, + ], + }, + { + key: 'collaborators', + cells: [ + { kind: 'text', value: 'Collaborators' }, + { kind: 'tags', values: profile.collaborates }, + ], + }, + ]; +} + +function buildFitRows(profile: AgentProfile): RegistryTableRow[] { + return [ + { + key: 'problem', + cells: [ + { kind: 'text', value: 'Retail problem' }, + { kind: 'text', value: profile.retailProblem }, + ], + }, + { + key: 'fit-for', + cells: [ + { kind: 'text', value: 'Fit for' }, + { kind: 'tags', values: profile.fitFor }, + ], + }, + { + key: 'latency', + cells: [ + { kind: 'text', value: 'Latency signal' }, + { kind: 'text', value: profile.productivityGain.latency }, + ], + }, + { + key: 'quality', + cells: [ + { kind: 'text', value: 'Quality signal' }, + { kind: 'text', value: profile.productivityGain.quality }, + ], + }, + { + key: 'cost', + cells: [ + { kind: 'text', value: 'Cost signal' }, + { kind: 'text', value: profile.productivityGain.cost }, + ], + }, + { + key: 'revenue-impact', + cells: [ + { kind: 'text', value: 'Revenue impact' }, + { kind: 'text', value: profile.productivityGain.revenueImpact ?? 'Not claimed for this agent.' }, + ], + }, + ]; +} + +function buildKpiRows(profile: AgentProfile): RegistryTableRow[] { + return profile.kpisToTrack.map((kpi) => ({ + key: kpi.id, + cells: [ + { kind: 'text', value: kpi.label }, + { kind: 'badge', value: kpi.target }, + { kind: 'text', value: kpi.why }, + { kind: 'text', value: kpi.source }, + ], + })); +} + +export default async function BuilderAgentDetailPage({ params }: BuilderAgentPageProps) { + const { slug } = await params; + const profile = getAgentProfile(slug); + + if (!profile) { + notFound(); + } + + return ( + <> + + + + + + + + + ); +} \ No newline at end of file diff --git a/apps/ui/app/(builder)/builders/agents/page.tsx b/apps/ui/app/(builder)/builders/agents/page.tsx new file mode 100644 index 000000000..ffada4659 --- /dev/null +++ b/apps/ui/app/(builder)/builders/agents/page.tsx @@ -0,0 +1,45 @@ +import type { Metadata } from 'next'; + +import { AgentCatalog } from '@/components/molecules/AgentCatalog'; +import { CallToAction } from '@/components/molecules/CallToAction'; +import { Hero } from '@/components/molecules/Hero'; +import { AGENT_CATALOG_DOMAINS } from '@/lib/agents/catalog'; +import { buildMetadata } from '@/lib/seo'; + +export const metadata: Metadata = buildMetadata({ + section: 'builder', + title: 'Agent Catalog', + description: + 'Builder-side catalog for the 26 retail agents: bounded contexts, maturity, cost bands, and per-agent runtime contract pages.', + path: '/builders/agents', +}); + +export default function BuilderAgentsCatalogPage() { + return ( + <> + + `/builders/agents/${slug}`} + /> + + + ); +} \ No newline at end of file diff --git a/apps/ui/app/(retailer)/retailers/agents/page.tsx b/apps/ui/app/(retailer)/retailers/agents/page.tsx index 42b0bc506..b6ecf9cad 100644 --- a/apps/ui/app/(retailer)/retailers/agents/page.tsx +++ b/apps/ui/app/(retailer)/retailers/agents/page.tsx @@ -1,8 +1,9 @@ import type { Metadata } from 'next'; -import { AgentCatalog, type AgentCatalogDomain } from '@/components/molecules/AgentCatalog'; +import { AgentCatalog } from '@/components/molecules/AgentCatalog'; import { CallToAction } from '@/components/molecules/CallToAction'; import { Hero } from '@/components/molecules/Hero'; +import { AGENT_CATALOG_DOMAINS } from '@/lib/agents/catalog'; import { buildMetadata } from '@/lib/seo'; export const metadata: Metadata = buildMetadata({ @@ -12,84 +13,6 @@ export const metadata: Metadata = buildMetadata({ path: '/retailers/agents', }); -const DOMAINS: AgentCatalogDomain[] = [ - { - key: 'crm', - label: 'CRM', - blurb: 'Customer profiles, segmentation, campaign drafting, and support assistance.', - agents: [ - { slug: 'crm-campaign-intelligence', name: 'Campaign Intelligence', oneLine: 'Drafts campaign briefs from segment + recent product activity.', costLower: '0.18', costUpper: '0.26', maturity: 'design-partner' }, - { slug: 'crm-profile-aggregation', name: 'Profile Aggregation', oneLine: 'Composes a single customer profile from CRM, web, and order signals.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, - { slug: 'crm-segmentation-personalization', name: 'Segmentation & Personalization', oneLine: 'Emits readable customer-segment briefs with RFV / channel-mix rationale.', costLower: '0.09', costUpper: '0.14', maturity: 'design-partner' }, - { slug: 'crm-support-assistance', name: 'Support Assistance', oneLine: 'Triages support tickets against policy and order history.', costLower: '0.10', costUpper: '0.16', maturity: 'design-partner' }, - ], - }, - { - key: 'ecommerce', - label: 'E-commerce', - blurb: 'Cart, search, checkout, order status, and product detail enrichment.', - agents: [ - { slug: 'ecommerce-cart-intelligence', name: 'Cart Intelligence', oneLine: 'Recommends cart additions and recovery offers from session and history.', costLower: '0.05', costUpper: '0.09', maturity: 'design-partner' }, - { slug: 'ecommerce-catalog-search', name: 'Catalog Search', oneLine: 'Semantic search over the catalog with re-ranking and intent routing.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, - { slug: 'ecommerce-checkout-support', name: 'Checkout Support', oneLine: 'Resolves cart-to-payment errors and explains pricing differences.', costLower: '0.07', costUpper: '0.12', maturity: 'preview' }, - { slug: 'ecommerce-order-status', name: 'Order Status', oneLine: 'Composes a current-state answer from CRUD, logistics, and CRM context.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, - { slug: 'ecommerce-product-detail-enrichment', name: 'Product Detail Enrichment', oneLine: 'Backfills attributes from supplier feeds + image evidence; agent-led, human-reviewed.', costLower: '0.12', costUpper: '0.20', maturity: 'design-partner' }, - ], - }, - { - key: 'inventory', - label: 'Inventory', - blurb: 'Replenishment, alerts, health checks, reservation validation.', - agents: [ - { slug: 'inventory-alerts-triggers', name: 'Alerts & Triggers', oneLine: 'Composes operational alerts from sell-through and inbound signals.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, - { slug: 'inventory-health-check', name: 'Health Check', oneLine: 'Surveys inventory state per SKU group and flags anomalies.', costLower: '0.04', costUpper: '0.07', maturity: 'design-partner' }, - { slug: 'inventory-jit-replenishment', name: 'JIT Replenishment', oneLine: 'Proposes vendor orders from sell-through, on-hand, and inbound — surfaced for buyer approval.', costLower: '0.14', costUpper: '0.22', maturity: 'design-partner' }, - { slug: 'inventory-reservation-validation', name: 'Reservation Validation', oneLine: 'Holds inventory across cart, checkout, and fulfillment; rolls back ghost holds.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, - ], - }, - { - key: 'logistics', - label: 'Logistics', - blurb: 'Carrier selection, ETA computation, returns, route issue detection.', - agents: [ - { slug: 'logistics-carrier-selection', name: 'Carrier Selection', oneLine: 'Proposes the best carrier per shipment from cost, ETA, and reliability signals.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, - { slug: 'logistics-eta-computation', name: 'ETA Computation', oneLine: 'Composes carrier and route signals into a confidence-banded ETA per order.', costLower: '0.03', costUpper: '0.05', maturity: 'design-partner' }, - { slug: 'logistics-returns-support', name: 'Returns Support', oneLine: 'Triages returns against policy, drafts response, escalates exceptions.', costLower: '0.10', costUpper: '0.16', maturity: 'preview' }, - { slug: 'logistics-route-issue-detection', name: 'Route Issue Detection', oneLine: 'Detects in-transit anomalies and triggers proactive customer comms.', costLower: '0.05', costUpper: '0.09', maturity: 'preview' }, - ], - }, - { - key: 'product-management', - label: 'Product Management', - blurb: 'Assortment, normalization, ACP transformation, consistency validation.', - agents: [ - { slug: 'product-management-acp-transformation', name: 'ACP Transformation', oneLine: 'Maps supplier ACP feeds into the canonical catalog shape with provenance.', costLower: '0.06', costUpper: '0.10', maturity: 'design-partner' }, - { slug: 'product-management-assortment-optimization', name: 'Assortment Optimization', oneLine: 'Proposes assortment moves from sell-through, margin, and category share.', costLower: '0.18', costUpper: '0.28', maturity: 'design-partner' }, - { slug: 'product-management-consistency-validation', name: 'Consistency Validation', oneLine: 'Audits catalog rows for cross-attribute and cross-channel inconsistencies.', costLower: '0.05', costUpper: '0.09', maturity: 'design-partner' }, - { slug: 'product-management-normalization-classification', name: 'Normalization & Classification', oneLine: 'Normalizes attributes and re-classifies into the canonical taxonomy.', costLower: '0.07', costUpper: '0.12', maturity: 'design-partner' }, - ], - }, - { - key: 'search', - label: 'Search', - blurb: 'Search-side enrichment that lifts recall on long-tail queries.', - agents: [ - { slug: 'search-enrichment-agent', name: 'Search Enrichment', oneLine: 'Enriches the index with synonyms, attribute hints, and intent signals.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, - ], - }, - { - key: 'truth', - label: 'Truth', - blurb: 'Provenance, ingestion, human-in-the-loop curation, export, exit-and-portability.', - agents: [ - { slug: 'truth-enrichment', name: 'Truth Enrichment', oneLine: 'Layers provenance + confidence onto canonical product / customer rows.', costLower: '0.06', costUpper: '0.10', maturity: 'design-partner' }, - { slug: 'truth-export', name: 'Truth Export', oneLine: 'Exports curated rows back to source systems on a configurable cadence.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, - { slug: 'truth-hitl', name: 'Truth HITL', oneLine: 'Routes low-confidence rows to a human reviewer with full context.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, - { slug: 'truth-ingestion', name: 'Truth Ingestion', oneLine: 'Ingests source-system rows with provenance and conflict resolution.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, - ], - }, -]; - /** * `/retailers/agents` — agent catalog (Issue #1041). */ @@ -106,7 +29,7 @@ export default function RetailerAgentsCatalogPage() { /> ({ + url: `${base}/builders/agents/${profile.slug}`, + lastModified, + changeFrequency: 'monthly' as const, + priority: 0.6, + })), { url: `${base}/builders/architecture`, lastModified, diff --git a/apps/ui/components/admin/AdminServiceDashboardPage.tsx b/apps/ui/components/admin/AdminServiceDashboardPage.tsx index 901245727..afa2cc41d 100644 --- a/apps/ui/components/admin/AdminServiceDashboardPage.tsx +++ b/apps/ui/components/admin/AdminServiceDashboardPage.tsx @@ -36,7 +36,7 @@ import { FiArrowRight, FiGrid, FiFileText, FiSettings, FiShield, FiUsers, } from 'react-icons/fi'; import { AgentRobot } from '@/components/organisms/AgentRobot'; -import { AGENT_PROFILES } from '@/lib/agents/profiles'; +import { getAgentProfile } from '@/lib/agents/profiles'; import type { AgentProfile } from '@/lib/agents/profiles'; // ── Types ── @@ -233,6 +233,10 @@ function toTitleCase(value: string): string { .join(' '); } +function resolveAgentDisplayName(agentSlug: string, profile?: AgentProfile): string { + return profile?.displayName ?? toTitleCase(agentSlug); +} + function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } @@ -850,7 +854,8 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb positive: 'Ready', negative: 'Not ready', }); - const agentProfile = useMemo(() => AGENT_PROFILES[agentSlug as keyof typeof AGENT_PROFILES], [agentSlug]); + const agentProfile = useMemo(() => getAgentProfile(agentSlug), [agentSlug]); + const agentDisplayName = resolveAgentDisplayName(agentSlug, agentProfile); const promptCatalog = data?.prompt_catalog ?? []; const toolCatalog = data?.mcp_tools ?? []; const resilienceStatus = data?.self_healing ?? { @@ -866,9 +871,9 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb const handleStagePrompt = useCallback((prompt: AdminServicePromptDocument) => { const promptStem = prompt.name.replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' '); - setInvokeMessage(`Use ${promptStem} to summarize the live ${toTitleCase(service)} state and the next operator action.`); + setInvokeMessage(`Use ${promptStem} to summarize the live ${agentDisplayName} state and the next operator action.`); setActiveTab('prompts'); - }, [service]); + }, [agentDisplayName]); const handleTabKeyDown = useCallback((event: React.KeyboardEvent, index: number) => { let nextIndex = index; @@ -1084,12 +1089,12 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb

- {toTitleCase(service)} Service + {agentDisplayName}

{domain} / - {service} + {agentSlug}

@@ -1149,7 +1154,7 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb

Invoke Agent

-

Send free text or a JSON object override to the {toTitleCase(service)} agent

+

Send free text or a JSON object override to the {agentDisplayName} agent

{agentSlug} @@ -1161,7 +1166,7 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb value={invokeMessage} onChange={(e) => setInvokeMessage(e.target.value)} onKeyDown={handleKeyDown} - placeholder={`Describe what you want the ${toTitleCase(service)} agent to do…`} + placeholder={`Describe what you want the ${agentDisplayName} agent to do…`} rows={4} disabled={invokeStatus === 'running'} className="w-full rounded-2xl border-0 bg-white dark:bg-gray-900/80 ring-1 ring-gray-200 dark:ring-gray-700 px-5 py-4 text-sm text-gray-900 dark:text-white placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-gray-900 dark:focus:ring-white resize-none transition-all duration-200 shadow-sm disabled:opacity-50" @@ -1228,7 +1233,7 @@ export function AdminServiceDashboardPage({ domain, service }: AdminServiceDashb {COCKPIT_TABS.length} views -
+
{COCKPIT_TABS.map((tab, index) => { const selected = activeTab === tab.id; const Icon = tab.icon; diff --git a/apps/ui/components/molecules/AgentCatalog.tsx b/apps/ui/components/molecules/AgentCatalog.tsx index d6acab2dc..b2f34d9ba 100644 --- a/apps/ui/components/molecules/AgentCatalog.tsx +++ b/apps/ui/components/molecules/AgentCatalog.tsx @@ -1,9 +1,12 @@ import type { ReactElement } from 'react'; +import Link from 'next/link'; + +import type { AgentCatalogDomain } from '@/lib/agents/catalog'; import { ConfidenceInterval } from '../atoms/ConfidenceInterval'; -import { MaturityBadge, type MaturityLevel } from '../atoms/MaturityBadge'; +import { MaturityBadge } from '../atoms/MaturityBadge'; /** - * AgentCatalog — the retailer-facing 26-agent catalog (Issue #1041 / Epic #1046). + * AgentCatalog — the 26-agent catalog (Issue #1041 / Epic #1046). * * Renders agents grouped by bounded context. Each agent row carries: * - Name + maturity badge (compile-time required) @@ -16,30 +19,16 @@ import { MaturityBadge, type MaturityLevel } from '../atoms/MaturityBadge'; * match the `ConfidenceInterval` contract. */ -export type AgentCatalogAgent = { - slug: string; - name: string; - oneLine: string; - costLower: string; - costUpper: string; - maturity: MaturityLevel; -}; - -export type AgentCatalogDomain = { - key: string; - label: string; - blurb: string; - agents: AgentCatalogAgent[]; -}; - export type AgentCatalogProps = { - domains: AgentCatalogDomain[]; + domains: readonly AgentCatalogDomain[]; /** Population descriptor passed to every `ConfidenceInterval`. */ costPopulation: string; /** Methodology descriptor passed to every `ConfidenceInterval`. */ costMethodology: string; /** Sample size passed to every `ConfidenceInterval`. */ costSampleSize: number; + /** Optional detail-link builder used by the builder-side catalog. */ + agentHref?: (slug: string) => string; testId?: string; }; @@ -104,11 +93,21 @@ const CARD_BODY_STYLE = { color: 'var(--sys-text-muted, var(--hp-text-muted))', }; +const CARD_LINK_STYLE = { + alignSelf: 'flex-start' as const, + color: 'var(--sys-link, var(--hp-link))', + fontSize: '0.875rem', + fontWeight: 600, + textDecoration: 'underline', + textUnderlineOffset: '0.18em', +}; + export function AgentCatalog({ domains, costPopulation, costMethodology, costSampleSize, + agentHref, testId, }: AgentCatalogProps): ReactElement { return ( @@ -131,23 +130,31 @@ export function AgentCatalog({

{domain.blurb}

    - {domain.agents.map((agent) => ( -
  • -
    -

    {agent.name}

    - -
    -

    {agent.oneLine}

    - -
  • - ))} + {domain.agents.map((agent) => { + const href = agentHref?.(agent.slug); + return ( +
  • +
    +

    {agent.name}

    + +
    +

    {agent.oneLine}

    + + {href ? ( + + Open builder detail + + ) : null} +
  • + ); + })}
))} diff --git a/apps/ui/lib/agents/catalog.ts b/apps/ui/lib/agents/catalog.ts new file mode 100644 index 000000000..ed89c8f86 --- /dev/null +++ b/apps/ui/lib/agents/catalog.ts @@ -0,0 +1,107 @@ +import type { MaturityLevel } from '@/components/atoms/MaturityBadge'; + +export type AgentCatalogAgent = { + slug: string; + name: string; + oneLine: string; + costLower: string; + costUpper: string; + maturity: MaturityLevel; +}; + +export type AgentCatalogDomain = { + key: string; + label: string; + blurb: string; + agents: readonly AgentCatalogAgent[]; +}; + +export const AGENT_CATALOG_DOMAINS: readonly AgentCatalogDomain[] = [ + { + key: 'crm', + label: 'CRM', + blurb: 'Customer profiles, segmentation, campaign drafting, and support assistance.', + agents: [ + { slug: 'crm-campaign-intelligence', name: 'Campaign Intelligence', oneLine: 'Drafts campaign briefs from segment + recent product activity.', costLower: '0.18', costUpper: '0.26', maturity: 'design-partner' }, + { slug: 'crm-profile-aggregation', name: 'Profile Aggregation', oneLine: 'Composes a single customer profile from CRM, web, and order signals.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, + { slug: 'crm-segmentation-personalization', name: 'Segmentation & Personalization', oneLine: 'Emits readable customer-segment briefs with RFV / channel-mix rationale.', costLower: '0.09', costUpper: '0.14', maturity: 'design-partner' }, + { slug: 'crm-support-assistance', name: 'Support Assistance', oneLine: 'Triages support tickets against policy and order history.', costLower: '0.10', costUpper: '0.16', maturity: 'design-partner' }, + ], + }, + { + key: 'ecommerce', + label: 'E-commerce', + blurb: 'Cart, search, checkout, order status, and product detail enrichment.', + agents: [ + { slug: 'ecommerce-cart-intelligence', name: 'Cart Intelligence', oneLine: 'Recommends cart additions and recovery offers from session and history.', costLower: '0.05', costUpper: '0.09', maturity: 'design-partner' }, + { slug: 'ecommerce-catalog-search', name: 'Catalog Search', oneLine: 'Semantic search over the catalog with re-ranking and intent routing.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, + { slug: 'ecommerce-checkout-support', name: 'Checkout Support', oneLine: 'Resolves cart-to-payment errors and explains pricing differences.', costLower: '0.07', costUpper: '0.12', maturity: 'preview' }, + { slug: 'ecommerce-order-status', name: 'Order Status', oneLine: 'Composes a current-state answer from CRUD, logistics, and CRM context.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, + { slug: 'ecommerce-product-detail-enrichment', name: 'Product Detail Enrichment', oneLine: 'Backfills attributes from supplier feeds + image evidence; agent-led, human-reviewed.', costLower: '0.12', costUpper: '0.20', maturity: 'design-partner' }, + ], + }, + { + key: 'inventory', + label: 'Inventory', + blurb: 'Replenishment, alerts, health checks, reservation validation.', + agents: [ + { slug: 'inventory-alerts-triggers', name: 'Alerts & Triggers', oneLine: 'Composes operational alerts from sell-through and inbound signals.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, + { slug: 'inventory-health-check', name: 'Health Check', oneLine: 'Surveys inventory state per SKU group and flags anomalies.', costLower: '0.04', costUpper: '0.07', maturity: 'design-partner' }, + { slug: 'inventory-jit-replenishment', name: 'JIT Replenishment', oneLine: 'Proposes vendor orders from sell-through, on-hand, and inbound - surfaced for buyer approval.', costLower: '0.14', costUpper: '0.22', maturity: 'design-partner' }, + { slug: 'inventory-reservation-validation', name: 'Reservation Validation', oneLine: 'Holds inventory across cart, checkout, and fulfillment; rolls back ghost holds.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, + ], + }, + { + key: 'logistics', + label: 'Logistics', + blurb: 'Carrier selection, ETA computation, returns, route issue detection.', + agents: [ + { slug: 'logistics-carrier-selection', name: 'Carrier Selection', oneLine: 'Proposes the best carrier per shipment from cost, ETA, and reliability signals.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, + { slug: 'logistics-eta-computation', name: 'ETA Computation', oneLine: 'Composes carrier and route signals into a confidence-banded ETA per order.', costLower: '0.03', costUpper: '0.05', maturity: 'design-partner' }, + { slug: 'logistics-returns-support', name: 'Returns Support', oneLine: 'Triages returns against policy, drafts response, escalates exceptions.', costLower: '0.10', costUpper: '0.16', maturity: 'preview' }, + { slug: 'logistics-route-issue-detection', name: 'Route Issue Detection', oneLine: 'Detects in-transit anomalies and triggers proactive customer comms.', costLower: '0.05', costUpper: '0.09', maturity: 'preview' }, + ], + }, + { + key: 'product-management', + label: 'Product Management', + blurb: 'Assortment, normalization, ACP transformation, consistency validation.', + agents: [ + { slug: 'product-management-acp-transformation', name: 'ACP Transformation', oneLine: 'Maps supplier ACP feeds into the canonical catalog shape with provenance.', costLower: '0.06', costUpper: '0.10', maturity: 'design-partner' }, + { slug: 'product-management-assortment-optimization', name: 'Assortment Optimization', oneLine: 'Proposes assortment moves from sell-through, margin, and category share.', costLower: '0.18', costUpper: '0.28', maturity: 'design-partner' }, + { slug: 'product-management-consistency-validation', name: 'Consistency Validation', oneLine: 'Audits catalog rows for cross-attribute and cross-channel inconsistencies.', costLower: '0.05', costUpper: '0.09', maturity: 'design-partner' }, + { slug: 'product-management-normalization-classification', name: 'Normalization & Classification', oneLine: 'Normalizes attributes and re-classifies into the canonical taxonomy.', costLower: '0.07', costUpper: '0.12', maturity: 'design-partner' }, + ], + }, + { + key: 'search', + label: 'Search', + blurb: 'Search-side enrichment that lifts recall on long-tail queries.', + agents: [ + { slug: 'search-enrichment-agent', name: 'Search Enrichment', oneLine: 'Enriches the index with synonyms, attribute hints, and intent signals.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, + ], + }, + { + key: 'truth', + label: 'Truth', + blurb: 'Provenance, ingestion, human-in-the-loop curation, export, exit-and-portability.', + agents: [ + { slug: 'truth-enrichment', name: 'Truth Enrichment', oneLine: 'Layers provenance + confidence onto canonical product / customer rows.', costLower: '0.06', costUpper: '0.10', maturity: 'design-partner' }, + { slug: 'truth-export', name: 'Truth Export', oneLine: 'Exports curated rows back to source systems on a configurable cadence.', costLower: '0.02', costUpper: '0.04', maturity: 'design-partner' }, + { slug: 'truth-hitl', name: 'Truth HITL', oneLine: 'Routes low-confidence rows to a human reviewer with full context.', costLower: '0.04', costUpper: '0.08', maturity: 'design-partner' }, + { slug: 'truth-ingestion', name: 'Truth Ingestion', oneLine: 'Ingests source-system rows with provenance and conflict resolution.', costLower: '0.03', costUpper: '0.06', maturity: 'design-partner' }, + ], + }, +]; + +export const AGENT_CATALOG_AGENTS: readonly AgentCatalogAgent[] = AGENT_CATALOG_DOMAINS.flatMap( + (domain) => domain.agents, +); + +const AGENT_CATALOG_AGENT_BY_SLUG: ReadonlyMap = new Map( + AGENT_CATALOG_AGENTS.map((agent): [string, AgentCatalogAgent] => [agent.slug, agent]), +); + +export function getAgentCatalogAgent(slug: string): AgentCatalogAgent | undefined { + return AGENT_CATALOG_AGENT_BY_SLUG.get(slug); +} \ No newline at end of file diff --git a/apps/ui/lib/agents/profiles.ts b/apps/ui/lib/agents/profiles.ts index 3f0f0f948..bad30a569 100644 --- a/apps/ui/lib/agents/profiles.ts +++ b/apps/ui/lib/agents/profiles.ts @@ -97,6 +97,10 @@ const ALL_AGENT_SLUGS = [ 'truth-export', ] as const; +export const AGENT_PROFILE_SLUGS = ALL_AGENT_SLUGS; + +const AGENT_PROFILE_SLUG_SET = new Set(AGENT_PROFILE_SLUGS); + const DOMAIN_GROUPS: Record = { crm: [ 'crm-campaign-intelligence', @@ -981,4 +985,12 @@ export const AGENT_PROFILES = Object.fromEntries( ALL_AGENT_SLUGS.map((slug) => [slug, buildProfile(slug)]), ) as Record; -export const AGENT_PROFILE_LIST = ALL_AGENT_SLUGS.map((slug) => AGENT_PROFILES[slug]); \ No newline at end of file +export const AGENT_PROFILE_LIST = ALL_AGENT_SLUGS.map((slug) => AGENT_PROFILES[slug]); + +export function isAgentProfileSlug(slug: string): slug is AgentProfileSlug { + return AGENT_PROFILE_SLUG_SET.has(slug); +} + +export function getAgentProfile(slug: string): AgentProfile | undefined { + return isAgentProfileSlug(slug) ? AGENT_PROFILES[slug] : undefined; +} \ No newline at end of file diff --git a/apps/ui/lib/search/appPages.ts b/apps/ui/lib/search/appPages.ts index 6e2c15583..63fe60b07 100644 --- a/apps/ui/lib/search/appPages.ts +++ b/apps/ui/lib/search/appPages.ts @@ -1,3 +1,5 @@ +import { AGENT_PROFILE_LIST } from '@/lib/agents/profiles'; + /** * App-search page manifest (Issue #1022). * @@ -42,6 +44,25 @@ export type AppPage = { keywords: string[]; }; +const BUILDER_AGENT_DETAIL_PAGES: readonly AppPage[] = AGENT_PROFILE_LIST.map((profile) => ({ + url: `/builders/agents/${profile.slug}`, + title: `${profile.displayName} agent detail`, + audience: 'builder', + description: `${profile.oneLiner} Runtime contract, KPIs, schemas, and collaborators.`, + keywords: [ + 'agent', + 'detail', + 'runtime', + 'schema', + 'kpi', + profile.slug, + profile.domain, + profile.domainLabel, + profile.primaryMode, + ...profile.displayName.split(' '), + ], +})); + /** * Curated audience-IA page manifest. * @@ -124,6 +145,15 @@ export const APP_PAGES: readonly AppPage[] = [ 'Architecture, ADRs, design patterns, telemetry seams, and enablement gates — the engineering surface of Holiday Peak Hub.', keywords: ['builder', 'engineering', 'architecture', 'adrs', 'platform'], }, + { + url: '/builders/agents', + title: 'Agent catalog (builder view)', + audience: 'builder', + description: + 'Builder-side catalog for the 26 retail agents with runtime contract links, maturity, cost bands, schemas, and collaborators.', + keywords: ['agents', 'catalog', 'runtime', 'schemas', 'kpis', 'mcp', 'bounded contexts'], + }, + ...BUILDER_AGENT_DETAIL_PAGES, { url: '/builders/architecture', title: 'Architecture registry', diff --git a/apps/ui/tests/unit/AdminServiceDashboardPage.test.tsx b/apps/ui/tests/unit/AdminServiceDashboardPage.test.tsx index 452124a83..a89588aee 100644 --- a/apps/ui/tests/unit/AdminServiceDashboardPage.test.tsx +++ b/apps/ui/tests/unit/AdminServiceDashboardPage.test.tsx @@ -131,6 +131,25 @@ describe('AdminServiceDashboardPage', () => { ); }); + it('renders the catalog cockpit with the agent profile display name', () => { + render(); + const cockpitHeader = screen.getByRole('heading', { level: 1, name: 'eCommerce Catalog Search' }).closest('header'); + + expect( + screen.getByRole('heading', { level: 1, name: 'eCommerce Catalog Search' }), + ).toBeInTheDocument(); + expect(cockpitHeader).toHaveTextContent('ecommerce-catalog-search'); + expect( + screen.queryByRole('heading', { level: 1, name: 'Catalog Service' }), + ).not.toBeInTheDocument(); + expect( + screen.getByText('Send free text or a JSON object override to the eCommerce Catalog Search agent'), + ).toBeInTheDocument(); + expect( + screen.getByRole('tablist', { name: 'eCommerce Catalog Search cockpit views' }), + ).toBeInTheDocument(); + }); + it('preserves explicit mode override from JSON input for catalog admin invoke calls', async () => { mockAgentPost.mockResolvedValue({ data: { summary: 'ok' } }); diff --git a/apps/ui/tests/unit/appSearchMatcher.test.ts b/apps/ui/tests/unit/appSearchMatcher.test.ts index 61eb2c0f5..16f1121f2 100644 --- a/apps/ui/tests/unit/appSearchMatcher.test.ts +++ b/apps/ui/tests/unit/appSearchMatcher.test.ts @@ -20,6 +20,11 @@ describe('searchAppPages', () => { expect(audiences).toEqual(new Set(['home', 'builder'])); }); + it('surfaces builder agent detail pages from the shared profile manifest', () => { + const hits = searchAppPages('product detail enrichment', AUDIENCE_FILTER.builder, 10); + expect(hits[0].page.url).toBe('/builders/agents/ecommerce-product-detail-enrichment'); + }); + it('scores title matches above description-only matches', () => { const hits = searchAppPages('roi', AUDIENCE_FILTER.retailer, 10); expect(hits.length).toBeGreaterThan(0); diff --git a/apps/ui/tests/unit/builderAgentPages.test.tsx b/apps/ui/tests/unit/builderAgentPages.test.tsx new file mode 100644 index 000000000..b5c05eea2 --- /dev/null +++ b/apps/ui/tests/unit/builderAgentPages.test.tsx @@ -0,0 +1,81 @@ +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import BuilderAgentsCatalogPage from '../../app/(builder)/builders/agents/page'; +import BuilderAgentDetailPage, { + generateMetadata, + generateStaticParams, +} from '../../app/(builder)/builders/agents/[slug]/page'; + +const mockNotFound = jest.fn(() => { + throw new Error('NEXT_NOT_FOUND'); +}); + +jest.mock('next/navigation', () => ({ + notFound: () => mockNotFound(), +})); + +describe('/builders/agents', () => { + beforeEach(() => { + render(); + }); + + it('renders the builder agent catalog page', () => { + expect(screen.getByTestId('builder-agents-hero')).toBeInTheDocument(); + expect(screen.getByTestId('builder-agents-catalog')).toBeInTheDocument(); + }); + + it('links known agent cards to builder detail routes', () => { + expect( + screen.getAllByRole('link', { name: 'Open builder detail' }).some( + (link) => link.getAttribute('href') === '/builders/agents/ecommerce-product-detail-enrichment', + ), + ).toBe(true); + }); +}); + +describe('/builders/agents/[slug]', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('enumerates known agent slugs for static generation', () => { + expect(generateStaticParams()).toEqual( + expect.arrayContaining([ + { slug: 'ecommerce-product-detail-enrichment' }, + ]), + ); + }); + + it('renders the product detail enrichment builder page', async () => { + const element = await BuilderAgentDetailPage({ + params: Promise.resolve({ slug: 'ecommerce-product-detail-enrichment' }), + }); + + render(element); + + expect(screen.getByTestId('builder-agent-detail-hero')).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: 'eCommerce Product Detail Enrichment' }), + ).toBeInTheDocument(); + expect(screen.getByTestId('builder-agent-runtime')).toBeInTheDocument(); + expect(screen.getByTestId('builder-agent-kpis')).toBeInTheDocument(); + expect(screen.getByTestId('builder-agent-contract-json')).toBeInTheDocument(); + }); + + it('builds page metadata from the known agent profile', async () => { + const metadata = await generateMetadata({ + params: Promise.resolve({ slug: 'ecommerce-product-detail-enrichment' }), + }); + + expect(metadata.title).toBe('eCommerce Product Detail Enrichment — For Builders — Holiday Peak Hub'); + expect(metadata.alternates?.canonical).toContain('/builders/agents/ecommerce-product-detail-enrichment'); + }); + + it('returns notFound for unknown agent slugs', async () => { + await expect( + BuilderAgentDetailPage({ params: Promise.resolve({ slug: 'unknown-agent' }) }), + ).rejects.toThrow('NEXT_NOT_FOUND'); + expect(mockNotFound).toHaveBeenCalledTimes(1); + }); +}); \ No newline at end of file diff --git a/apps/ui/tests/unit/sitemap.test.ts b/apps/ui/tests/unit/sitemap.test.ts index 76cf09bc3..40360e305 100644 --- a/apps/ui/tests/unit/sitemap.test.ts +++ b/apps/ui/tests/unit/sitemap.test.ts @@ -18,6 +18,8 @@ describe('app/sitemap.ts', () => { `${SEO_CONFIG.SITE_URL}/retailers/case-studies`, `${SEO_CONFIG.SITE_URL}/retailers/security`, `${SEO_CONFIG.SITE_URL}/builders`, + `${SEO_CONFIG.SITE_URL}/builders/agents`, + `${SEO_CONFIG.SITE_URL}/builders/agents/ecommerce-product-detail-enrichment`, `${SEO_CONFIG.SITE_URL}/builders/architecture`, `${SEO_CONFIG.SITE_URL}/builders/adrs`, `${SEO_CONFIG.SITE_URL}/builders/patterns`, @@ -29,7 +31,7 @@ describe('app/sitemap.ts', () => { `${SEO_CONFIG.SITE_URL}/docs/sitemap.xml`, ]), ); - expect(urls.length).toBeGreaterThanOrEqual(18); + expect(urls.length).toBeGreaterThanOrEqual(45); }); it('home has the highest priority', () => { diff --git a/docs/architecture/ADRs.md b/docs/architecture/ADRs.md index bc1444339..7d7652e2a 100644 --- a/docs/architecture/ADRs.md +++ b/docs/architecture/ADRs.md @@ -40,6 +40,7 @@ This document indexes all architectural decisions for the Holiday Peak Hub accel | [ADR-033](adrs/adr-033-ui-modular-monolith-on-swa.md) | UI as a Modular Monolith on Static Web Apps (Path 2) | Accepted | 2026-05 | | [ADR-034](adrs/adr-034-audience-segmented-ia.md) | Audience-Segmented Information Architecture for the UI | Accepted (Extends ADR-033) | 2026-05 | | [ADR-035](adrs/adr-035-ui-design-system.md) | UI Design System Contract: Tokens, Components, CSS, Quality Gates | Accepted (Extends ADR-033 + ADR-034) | 2026-05 | +| [ADR-036](adrs/adr-036-foundry-agent-surface-taxonomy.md) | Foundry Agent Surface Taxonomy | Accepted | 2026-05 | > ADR-028 (Continuous Agent Evaluation) is in flight on PR #974; once merged, it will be inserted at its sequential position above. ADRs 029, 030, 031 forward-reference ADR-028 and reference specific eval attribute keys (`eval.score`, `eval.baseline_id`, `baselineSource: continuous-eval`); those keys are subject to ADR-028's final schema and will be reconciled in lock-step on PR #974 merge. @@ -84,6 +85,7 @@ Each ADR follows a standard template: ### Agent & AI - Microsoft Agent Framework with Azure AI Foundry ([ADR-005](adrs/adr-005-agent-framework.md)) - SLM-first routing for cost optimization ([ADR-010](adrs/adr-010-model-routing.md)) +- Foundry Hosted/Custom Agent exposure taxonomy ([ADR-036](adrs/adr-036-foundry-agent-surface-taxonomy.md)) ### Memory & State - Memory architecture: three-tier, builder, partitioning, and namespace isolation ([ADR-007](adrs/adr-007-memory-tiers.md)) diff --git a/docs/architecture/adrs/adr-005-agent-framework.md b/docs/architecture/adrs/adr-005-agent-framework.md index e06eced03..b7350c413 100644 --- a/docs/architecture/adrs/adr-005-agent-framework.md +++ b/docs/architecture/adrs/adr-005-agent-framework.md @@ -6,6 +6,8 @@ - 2026-04-28 — Added mandatory Foundry invocation policy *(superseded 2026-05-10; see below)* - 2026-05-10 — Reversed to mandatory **MAF direct-model** invocation policy; portal-managed Foundry Agent records retired - 2026-05-11 — Recorded Wave 4c code cleanup; portal-agent Azure resources remain manual deprovisioning scope + - 2026-05-20 — Clarified AKS product runtime vs Foundry-hosted portal/evaluation surface + - 2026-05-21 — Cross-referenced ADR-036 two-track Foundry surface taxonomy **Deciders**: Architecture Team, Ricardo Cataldi **Tags**: architecture, foundry, agents, telemetry, observability, latency @@ -117,9 +119,10 @@ The 2026-04-28 policy mandated that every LLM call route through a portal-manage - `DirectModelInvoker` (in `lib/src/holiday_peak_lib/agents/direct.py`) is the only production `ModelInvoker` implementation. - `BaseRetailAgent.invoke_model()` remains the single invocation entry point at the agent-class boundary. - `AgentBuilder.with_direct_models(slm_config=..., llm_config=..., complexity_threshold=...)` is the only sanctioned wiring path in service `main.py`. -- `FoundryAgentInvoker`, `/foundry/agents/ensure`, `ensure-foundry-agents.{sh,ps1}`, `project_client.agents.create_version`, and the JSON-text tool-call parser have been removed from framework runtime code as of Wave 4c (see [docs/project-status.md](../../project-status.md)). +- `FoundryAgentInvoker`, `/foundry/agents/ensure`, `ensure-foundry-agents.{sh,ps1}`, the V2 `PromptAgentDefinition` provisioning path, and the JSON-text tool-call parser have been removed from framework runtime code as of Wave 4c (see [docs/project-status.md](../../project-status.md)). - The 42 V2 portal-managed agents in project `aipholidaris` (21 services × `fast` + `rich` roles) remain outside repository automation scope for this cleanup and will be deprovisioned manually by the owner. -- **Single-architecture guardrail (from the inventory hosted-agent lesson):** A service must not ship a second entry point (e.g., `hosted_main.py`, parallel `ResponsesHostServer`, secondary port) alongside `main.py`. The MAF `Agent` is constructed inside the existing FastAPI handler in `main.py`. PRs introducing parallel runtimes are rejected at review. +- **Single-architecture guardrail (from the inventory hosted-agent lesson):** A service must not ship a second service implementation (e.g., `hosted_main.py`, a duplicate FastAPI/Starlette app, or a separate product traffic path) alongside `main.py`. The MAF `Agent` is constructed inside the existing FastAPI handler in `main.py`. An AKS-hosted Responses adapter is allowed only when it is mounted into that same FastAPI app and same pod/port as `/health`, `/ready`, `/mcp/*`, and `/invoke`. A Foundry-hosted container registered via `AIProjectClient.agents.create_version` with `template.kind: hosted` is allowed as a portal Playground, telemetry, and evaluation surface when it packages the same FastAPI Responses wrapper and product-equivalent dependencies. It is not the product runtime owner for `inventory-health-check`; AKS via azd/Flux/HelmRelease remains the product runtime. PRs introducing a parallel implementation or routing production product traffic away from AKS are rejected at review. +- **Foundry surface taxonomy:** ADR-036 classifies public or human-facing agents as Hosted Agent surfaces and non-public internal agents as Custom Agent proxy surfaces. This classification is an exposure policy only; it does not replace the MAF direct-model runtime, the single FastAPI app constraint, or the AKS/APIM/AGC product traffic path. - No `ChatCompletionsClient`, `openai.ChatCompletion`, `AzureOpenAI`, or `responses.create()` calls permitted in application code outside the MAF `ChatClient` boundary. ## Consequences @@ -134,3 +137,4 @@ The 2026-04-28 policy mandated that every LLM call route through a portal-manage - [ADR-004: FastAPI with Dual REST + MCP](adr-004-fastapi-mcp.md) - [ADR-010: SLM-First Model Routing](adr-010-model-routing.md) - [ADR-024: Agent Communication Policy](adr-024-agent-communication-policy.md) +- [ADR-036: Foundry Agent Surface Taxonomy](adr-036-foundry-agent-surface-taxonomy.md) diff --git a/docs/architecture/adrs/adr-036-foundry-agent-surface-taxonomy.md b/docs/architecture/adrs/adr-036-foundry-agent-surface-taxonomy.md new file mode 100644 index 000000000..004242b06 --- /dev/null +++ b/docs/architecture/adrs/adr-036-foundry-agent-surface-taxonomy.md @@ -0,0 +1,112 @@ +# ADR-036: Foundry Agent Surface Taxonomy + +**Status**: Accepted +**Date**: 2026-05 +**Deciders**: Architecture Team, Ricardo Cataldi +**Tags**: foundry, agents, hosted-agents, custom-agents, aks, governance + +## Context + +Holiday Peak Hub is both a framework and a product. Operators need one simple rule for Foundry exposure without changing the product runtime: + +- Public or human-facing agents should be easy to test and evaluate in Microsoft Foundry as Hosted Agents. +- Non-public internal agents should stay behind the existing product edge and be represented as Custom Agents that proxy the AKS/APIM endpoint. + +Microsoft Foundry Hosted Agents are code-based, containerized agents that run on Foundry Agent Service managed compute and can expose the Responses protocol. That is useful for public Playground, telemetry, and evaluation surfaces. It is not, by itself, a decision to move Holiday Peak Hub product traffic away from AKS. + +## Decision + +Adopt a two-track Foundry surface taxonomy: + +| Surface | Applies To | Repo Artifact | Runtime Meaning | +|---|---|---|---| +| Hosted Agent | Public or human-facing agents | `apps//agent.hosted.yaml` | Foundry-managed Hosted Agent exposure over the same FastAPI app and `/responses` adapter. | +| Custom Agent | Non-public or internal agents | `apps//.foundry/agent-metadata.yaml` with `surface.type: custom` | Proxy surface for the existing APIM -> AGC -> AKS product endpoint; no Foundry-managed compute. | + +The central policy registry is `apps/foundry-surfaces.yaml`. Tests must fail when a listed Hosted Agent lacks `agent.hosted.yaml`, when a Custom Agent has a hosted manifest, or when the registry no longer covers all 26 active agent services exactly once. + +## Hosted Agent Surface Contract + +Hosted manifests must use: + +- `template.kind: hosted` +- `responses` protocol version `1.0.0` +- the existing service module entry point, for example `python -m uvicorn ecommerce_catalog_search.main:app --host 0.0.0.0 --port 8088` +- `HOLIDAY_PEAK_FOUNDRY_HOSTED=1` +- `UVICORN_PORT=8088` +- hosted-safe agent identity aliases such as `HPH_AGENT_ID_FAST` and `HPH_AGENT_ID_RICH` +- no declared environment variable names starting with reserved `FOUNDRY_` or `AGENT_` prefixes + +The framework mounts `/responses` from the existing FastAPI app when `HOLIDAY_PEAK_FOUNDRY_HOSTED=1`. This keeps Hosted Agent manifests executable without introducing per-service `hosted_main.py` files. The mount is idempotent, so a service such as `inventory-health-check` that also enables the AKS Responses adapter cannot double-mount the host server. + +Hosted surfaces are approved for these public or human-facing agents: + +- `ecommerce-catalog-search` +- `ecommerce-cart-intelligence` +- `ecommerce-checkout-support` +- `ecommerce-order-status` +- `ecommerce-product-detail-enrichment` +- `crm-support-assistance` +- `inventory-health-check` +- `logistics-eta-computation` +- `logistics-returns-support` +- `truth-hitl` + +## Custom Agent Surface Contract + +Custom surfaces must declare in `.foundry/agent-metadata.yaml`: + +- `surface.type: custom` +- `surface.classification: Custom Agent` +- `surface.foundryManagedCompute: false` +- `surface.proxy.target: existing-aks-apim-endpoint` +- `surface.productRuntime: aks` +- `surface.productTrafficPath: APIM -> AGC -> AKS` + +Custom surfaces are approved for these internal agents: + +- `crm-campaign-intelligence` +- `crm-profile-aggregation` +- `crm-segmentation-personalization` +- `inventory-alerts-triggers` +- `inventory-jit-replenishment` +- `inventory-reservation-validation` +- `logistics-carrier-selection` +- `logistics-route-issue-detection` +- `product-management-acp-transformation` +- `product-management-assortment-optimization` +- `product-management-consistency-validation` +- `product-management-normalization-classification` +- `search-enrichment-agent` +- `truth-enrichment` +- `truth-export` +- `truth-ingestion` + +## Guardrail Alignment + +This ADR does not weaken existing architecture guardrails: + +- ADR-005 remains in force: MAF direct-model invocation runs in the existing FastAPI app; no duplicate `hosted_main.py`, duplicate FastAPI app, or second product traffic path. +- ADR-017 remains in force: azd + Flux + HelmRelease remain the current AKS deployment model for product traffic. +- ADR-021 remains in force: APIM -> AGC -> AKS is the canonical product edge. +- ADR-024 and ADR-030 remain in force: agent-to-agent communication stays MCP-only, with approved REST/Event Hubs paths only. +- ADR-032 remains in force: Responses paths keep Redis, Cosmos DB, Blob Storage, Event Hub, Key Vault, and Application Insights parity with `/invoke`. + +Moving product traffic ownership from AKS to Foundry-managed hosted containers would require a future ADR that explicitly supersedes the relevant runtime and edge decisions. + +## Consequences + +**Positive**: Operators get a simple exposure taxonomy; public agents gain a Foundry Hosted Agent Playground/evaluation surface; internal agents remain private and reuse the existing AKS/APIM operational path. + +**Negative**: Hosted Agent surfaces create Foundry-managed compute and can incur additional Hosted Agents runtime charges when deployed or active. Custom Agent surfaces do not create Foundry-managed compute, but their health depends on the existing AKS/APIM path. + +**Operational**: Tests become the enforcement point for taxonomy drift. Registry, hosted manifests, and custom metadata must be updated together when an agent changes exposure class. + +## Related ADRs + +- [ADR-005: Microsoft Agent Framework + Foundry](adr-005-agent-framework.md) +- [ADR-017: Deployment Strategy - azd Provisioning + Flux CD GitOps](adr-017-deployment-strategy.md) +- [ADR-021: APIM + Application Gateway for Containers as Canonical AKS Edge](adr-021-apim-agc-edge.md) +- [ADR-024: Agent Communication Policy, Isolation, and Async Contracts](adr-024-agent-communication-policy.md) +- [ADR-030: MCP-Only Agent-to-Agent Communication with Hop Counter](adr-030-mcp-only-a2a.md) +- [ADR-032: Three-Tier Memory Contract Pinning](adr-032-three-tier-memory-contract.md) \ No newline at end of file diff --git a/docs/governance/backend-governance.md b/docs/governance/backend-governance.md index 75cb515d9..9b50e887e 100644 --- a/docs/governance/backend-governance.md +++ b/docs/governance/backend-governance.md @@ -1,7 +1,7 @@ # Backend Development Governance and Compliance Guidelines **Version**: 2.0 -**Last Updated**: 2026-04-12 +**Last Updated**: 2026-05-18 **Owner**: Backend Team ## Scope @@ -41,10 +41,18 @@ Applies to all Python services and shared framework packages under: ### Data and memory -- Use three-tier memory strategy where applicable (Redis hot, Cosmos warm, Blob cold) (ADR-007). +- Use three-tier memory strategy where applicable (Redis hot, Cosmos warm, Blob cold) (ADR-007, ADR-032). - Keep Cosmos queries partition-aware and resilient to throttling (`429` backoff). - Do not bypass configured identity and secret patterns. +### AKS Responses runtime parity + +The three-tier memory contract remains canonical for product services (ADR-007, ADR-032), and Event Hubs remain the canonical choreography layer (ADR-006). A Responses adapter mounted into an AKS-hosted FastAPI app must keep the same Redis, Cosmos DB, Blob Storage, Event Hub, Key Vault, and Application Insights wiring as the service's `/invoke` path. Do not introduce runtime flags or manifests that detach Redis hot memory or Event Hub subscribers for the AKS Responses path. + +Redis hot memory is optional on the request path by behavior, not by deployment split: the framework bounds socket and connect timeouts and fails open for Redis authentication, connection, timeout, and OS-level faults so optional cache failures do not surface as agent request failures. Event Hub subscriber wiring remains part of the standard app lifespan whenever subscriptions and handlers are configured. + +Foundry Hosted Agent surfaces are enabled through `HOLIDAY_PEAK_FOUNDRY_HOSTED=1`, which mounts the shared `/responses` adapter into the existing FastAPI app. Custom Agent surfaces must proxy the existing APIM -> AGC -> AKS endpoint and must not declare Foundry-managed compute. Both surfaces keep the same service implementation and product dependency wiring. + ### Security - Use Managed Identity and Key Vault for secrets. diff --git a/docs/governance/infrastructure-governance.md b/docs/governance/infrastructure-governance.md index 9a3914e50..e44a48d59 100644 --- a/docs/governance/infrastructure-governance.md +++ b/docs/governance/infrastructure-governance.md @@ -1,7 +1,7 @@ # Infrastructure Governance and Compliance Guidelines -**Version**: 2.4 -**Last Updated**: 2026-04-08 +**Version**: 2.5 +**Last Updated**: 2026-05-18 **Owner**: Infrastructure Team ## Scope @@ -23,6 +23,9 @@ Infrastructure provisioning, deployment orchestration, identity, security contro ### Core policy - **azd-first deployment is mandatory** (ADR-017). The only approved exception is the manual dev emergency redeploy path in `deploy-azd-dev.yml` with `skipProvision=true`, which reuses already-provisioned infrastructure and skips only `azd provision`. +- Agent protocol surfaces used for product traffic, including the `inventory-health-check` Responses adapter, must be exposed through the canonical APIM/AGC-to-AKS route. A Foundry-hosted portal/evaluation surface may be registered separately for Playground testing, telemetry, and evaluations, provided it uses the same FastAPI Responses wrapper and does not become the product traffic path. +- Foundry exposure follows ADR-036: public or human-facing agents use Hosted Agent manifests, while non-public internal agents use Custom Agent metadata that proxies the existing APIM -> AGC -> AKS endpoint and sets `foundryManagedCompute: false`. This taxonomy does not replace azd/Flux/HelmRelease as the AKS product deployment model. +- Foundry surface automation must consume `apps/foundry-surfaces.yaml` and remain an explicit plan/apply step. Plan mode is read-only and emits a review artifact; apply mode may create/update Hosted Agent versions only from tested image digests and must keep Custom Agent proxy surfaces metadata-only until a supported Foundry Custom Agent creation API exists. - Reusable workflow `deploy-azd.yml` is not the primary operator entrypoint; use env-specific entrypoint workflows. - OIDC Azure login is required in CI/CD; no static cloud credentials committed to repository. - Provisioning must fail fast when `projectName` is not `holidaypeakhub405` or when `resourceGroupName`/`AZURE_RESOURCE_GROUP` are not `holidaypeakhub405--rg`; this is enforced through azd `preprovision` hooks. @@ -51,7 +54,7 @@ Infrastructure provisioning, deployment orchestration, identity, security contro - The canonical naming pattern for service-scoped wrappers is `.github/workflows/deploy-azd-.yml`, using the exact service key from `azure.yaml`. - Service-scoped wrappers are approved for branch-independent execution from any pushed branch when their scoped path filters match; manual runs may still pass `testedSourceSha` or `testedSourceRef` for explicit targeting. - Service-scoped wrappers are non-production by policy. Production rollouts must continue through the protected release path in `.github/workflows/deploy-azd-prod.yml`. -- Service-scoped preview deploys must use GitHub Environment `branch`, and Azure federated credential trust for that path must match the environment-scoped OIDC subject emitted by reusable-workflow jobs under `branch` instead of relying only on `ref:refs/heads/*` subjects. +- Service-scoped preview deploys must use GitHub Environment `branch`, and Azure federated credential trust for that path must match the environment-scoped OIDC subject emitted by reusable-workflow jobs under `branch` instead of relying only on `ref:refs/heads/*` subjects. Generated and hand-authored service wrappers must preserve this `githubEnvironment: branch` contract. - For non-prod branch-preview runs where the tested source resolves to a non-default `refs/heads/*` branch, `.github/workflows/deploy-azd.yml` must temporarily switch `GitRepository/holiday-peak-gitops` in `flux-system` to that branch, must fail fast if the preview source cannot fetch the tested revision, must treat preview Flux preparation as satisfied when the AGC-relevant Flux kustomization records that preview revision within that live kustomization's configured Flux timeout window, and must restore the repository default branch in an always-run cleanup job after validation completes. - Shared `dev` auto-promotion remains governed separately through the protected environment flow, and GitHub Environment branch restrictions still apply where configured. @@ -94,11 +97,14 @@ Infrastructure provisioning, deployment orchestration, identity, security contro - Push-event changed-service detection must diff `${{ github.event.before }}...${{ github.sha }}` to avoid empty comparisons against `origin/main` after merge. - APIM sync/smoke checks for API path health after relevant changes. - For branch-preview service deploys that publish rendered manifests, deployment workflows must wait for source and AGC-relevant Flux revision observation before AGC readiness evaluation so the live Gateway reflects the tested preview branch, and that preview observation window must honor the live Flux kustomization timeout instead of assuming immediate turnover. +- Branch-preview service deploys must update the HelmRelease desired state on the preview branch for the tested image tag before Flux reconciliation is considered deploy-complete; rendered-manifest artifacts alone are verification inputs and are not the source of truth. - Before preview Helm renders publish GitOps manifests, deployment workflows must normalize AGC publication context from live state when azd provision outputs are empty: recover `AGC_SUBNET_ID` from the live shared `ApplicationLoadBalancer` association referenced by the CRUD-owned Gateway contract, and do not feed a previously published AGC listener hostname back into managed-controller render inputs. - Preview Flux preparation must be resource-focused and must not fail solely because unrelated CRUD workloads in the wider Flux kustomization are not `Ready`. - AGC readiness may resolve the approved frontend hostname from azd outputs, an Application Gateway for Containers frontend in the environment resource group, an Application Gateway for Containers frontend in the AKS node resource group, or the live shared Gateway status address after AKS credentials are available. - Shared infrastructure is the Day-0 owner of AGC controller prerequisites: the delegated AGC subnet, workload-identity federation for `azure-alb-system/alb-controller-sa`, and the controller RBAC assignments (`Reader`, `AppGw for Containers Configuration Manager`, `Network Contributor`). Deploy hooks may verify these prerequisites but must not create or repair those role assignments inline. - Deployment workflows must validate AGC GatewayClass readiness, the live shared `ApplicationLoadBalancer/holiday-peak-agc` contract (`Accepted=True`, `Deployment=True`, non-empty associations), Azure traffic-controller health in the AKS node resource group, the live shared `Gateway/holiday-peak-agc` binding contract (`alb.networking.azure.io/alb-name`, `alb.networking.azure.io/alb-namespace`) plus `Accepted=True`, `Programmed=True`, and an assigned status address, shared-Gateway parent attachment for CRUD and changed-agent `HTTPRoute` resources, and then direct CRUD plus changed-agent health on an approved AGC frontend hostname before APIM sync; they must fail fast with ALB, traffic-controller, Gateway, or route evidence if any step is missing or unhealthy. +- When AGC route attachment is healthy but direct AGC health returns `500`, treat backend readiness as the primary failure domain before changing shared AGC infrastructure: verify Flux-owned HelmRelease image tags resolve to pullable ACR manifests, Deployments have available replicas, EndpointSlices report `ready=true` and `serving=true`, and pod resource limits are not causing `OOMKilled` restarts. +- AGC readiness jobs must propagate the changed-agent service list from `detect-changes` into the validation environment so changed-agent HTTPRoute parent attachment and direct AGC health checks are actually exercised before APIM sync. - APIM sync determinism is required: ingress sync must resolve against an explicit AGC target in workflow execution. - APIM smoke coverage must include direct AGC CRUD health, APIM CRUD health, CRUD CORS preflight behavior, and at least one negative CRUD path that proves failures are not masked as upstream 5xx responses. - Transitional workflow or manifest logic may still detect legacy ingress classes during migration, but AGC is the canonical target state and must take precedence in governance and cutover planning. @@ -112,6 +118,7 @@ Infrastructure provisioning, deployment orchestration, identity, security contro - Optional UI-only deployment path constrained by SWA token flow and health checks. - ACR runner-IP allowlist exceptions may be applied/removed automatically when enabled. - If the environment ACR public endpoint is disabled, the tested-image build guard must temporarily enable public access with `defaultAction=Deny`, reuse the runner-IP allowlist flow, and restore the prior `publicNetworkAccess` and `networkRuleSet.defaultAction` after the build phase completes. +- Foundry Hosted Agent apply mode must not rely on temporary runner-IP ACR exceptions. Because hosted containers are pulled by Foundry, apply mode must fail when the baseline ACR policy is private-only or `networkRuleSet.defaultAction=Deny`; operators must make that exposure decision intentionally before creating Hosted Agent versions. - When restoring ACR access state, `defaultAction` validation must be skipped if `publicNetworkAccess` is being set to `Disabled` — Azure may report any `defaultAction` value when public access is off and the value is semantically irrelevant. - When `azd provision` fails with `RoleAssignmentExists` in a non-prod environment, the infrastructure is typically deployed successfully but azd does not write outputs. The workflow must run `azd env refresh` after this fallback to populate outputs (POSTGRES_HOST, COSMOS_ACCOUNT_URI, etc.) from the deployed resources. @@ -148,6 +155,7 @@ Infrastructure provisioning, deployment orchestration, identity, security contro 6. Architecture/governance docs updated when policy changes. 7. Privileged live validation remains bound to the `dev` environment, excluded from PR contexts, and constrained by selected-branch deployment protection on `main`. 8. Changed agent services passed the Foundry runtime contract gate across workflow intent, rendered manifests, live Deployment env values, ensure responses, and `/ready` validation. +9. When Foundry portal visibility is required, `deployFoundrySurfaces=true` produced a plan artifact first and `foundrySurfaceMode=apply` was run only after Hosted Agent ACR reachability and cost implications were reviewed. ## ADR References diff --git a/docs/project-status.md b/docs/project-status.md index 012aa3a7f..465a406c3 100644 --- a/docs/project-status.md +++ b/docs/project-status.md @@ -8,9 +8,13 @@ **Why now.** The 2026-04-28 “Mandatory Foundry Invocation Policy” in [ADR-005](architecture/adrs/adr-005-agent-framework.md) was reversed on 2026-05-10 (same ADR, append-only amendment). Drivers: (1) the 2–5s per-request overhead did not yield a proportionate operational return; (2) tool-calling fidelity is structurally cleaner with native MAF function-calling than with the hand-rolled JSON-text parser; (3) the inventory hosted-agent precedent (commit `4cf0e546`, 2026-04-25) demonstrated the direct-model shape end-to-end — it was deleted only because it had been shipped as a *parallel* entry point alongside `main.py`, which is the dual-runtime anti-pattern the new policy explicitly forbids. -**Single-architecture guardrail.** No service may ship a second entry point (e.g., `hosted_main.py`, parallel `ResponsesHostServer`, secondary port) alongside `main.py`. The MAF `Agent` is constructed inside the existing FastAPI handler. This is the explicit lesson from the deleted inventory `hosted_main.py`. +**Single-architecture guardrail.** No service may ship a second AKS product entry point (e.g., `hosted_main.py`, secondary AKS service port, or a second FastAPI/Starlette runtime) alongside `main.py`. The MAF `Agent` is constructed inside the existing FastAPI handler. A Responses protocol adapter is permitted only when mounted into the same AKS-hosted FastAPI app and same pod/port as `/health`, `/ready`, `/mcp/*`, and `/invoke`. A separate Foundry-hosted portal/evaluation surface is allowed when it wraps that same Responses contract and does not replace APIM -> AGC -> AKS as the product traffic path. -**Portal-tracking manifests.** Each active agent service now carries `agent.yaml` and `.foundry/agent-metadata.yaml` as local Foundry portal-tracking metadata only. These artifacts do not introduce `hosted_main.py`, `ResponsesHostServer`, `entrypoint.sh`, port `8088`, or any parallel runtime; deployment and invocation still run through `DirectModelInvoker` inside the existing FastAPI app. +**Portal and runtime surfaces.** Each active agent service carries `agent.yaml` and `.foundry/agent-metadata.yaml` to describe the AKS product runtime for Foundry traceability, evaluations, protocol metadata, and operator discovery. `inventory-health-check` may also declare `responses 1.0.0` in `agent.yaml` to describe the AKS-hosted adapter. A separate Foundry-hosted portal/evaluation surface may use `template.kind: hosted`, `AIProjectClient.agents.create_version`, and the Foundry internal hosted-container port when its purpose is Playground testing, telemetry, or evaluation against the same FastAPI Responses wrapper. That surface must not replace AKS as the product runtime, introduce a second service implementation, or drop product dependency parity. Product deployment and invocation still run through `DirectModelInvoker` inside the existing AKS-hosted FastAPI app. + +**Foundry surface taxonomy.** ADR-036 adds the two-track exposure policy requested for PR #1103 / issue #990: public or human-facing agents use `agent.hosted.yaml` Hosted Agent manifests with `template.kind: hosted`, `responses 1.0.0`, `HOLIDAY_PEAK_FOUNDRY_HOSTED=1`, and port `8088`; non-public internal agents are Custom Agent surfaces recorded in `.foundry/agent-metadata.yaml` that proxy the existing APIM -> AGC -> AKS endpoint and do not create Foundry-managed compute. The registry is `apps/foundry-surfaces.yaml`. This taxonomy is an exposure model only; AKS remains the product runtime unless a future ADR changes it. + +**Foundry surface registration automation.** `scripts/ops/register_foundry_surfaces.py` now materializes the registry into a deterministic plan and can apply Hosted Agent versions through `AIProjectClient(..., allow_preview=True)` when explicitly requested by the deployment workflow. Manual dev deployments can enable `deployFoundrySurfaces=true` and choose `foundrySurfaceMode=plan` or `apply`. Apply mode uses tested image digests, validates Hosted Agent ACR reachability before live creation, and keeps Custom Agent proxy entries metadata-only until Microsoft Foundry exposes a supported Custom Agent creation API for APIM-backed proxy surfaces. **Tool calling.** Tools register in two places, additively: (a) MCP server for A2A (unchanged), and (b) the in-process MAF `Agent(tools=[...])` for native function-calling. The `_inject_tool_prompt` / `_extract_tool_calls_from_text` / `schema_tools_injected` JSON-parsing path in `lib/src/holiday_peak_lib/agents/foundry.py` was deleted in Wave 4c of the cutover. @@ -78,6 +82,11 @@ merged squash commit on `main`. ### What Changed (Since Last Snapshot) +- **Issue #990 / PR #1103 Foundry portal surface automation (2026-05-19)**: added a deterministic Foundry surface planner/apply script and `deploy-foundry-surfaces` reusable workflow job. Plan mode emits the review artifact from `apps/foundry-surfaces.yaml`; apply mode creates or updates Hosted Agent versions only from tested image digests and refuses private-only ACR baselines. Custom Agent entries remain APIM proxy metadata with no Foundry-managed compute. +- **Issue #1107 / PR #1103 APIM sync and smoke validation (2026-05-19)**: `.infra/azd/hooks/sync-apim-agents.sh` and `.infra/azd/hooks/sync-apim-agents.ps1` now generate CRUD APIM CORS `Access-Control-Allow-Origin` `` expressions with raw quotes inside XML element text while keeping XML attribute expressions entity-escaped, the CRUD backend section now uses a single explicit `` policy, and public `/api/ready` rewrites to the CRUD service `/ready` probe. The CRUD AGC route contract now publishes `/ready` with `/health` and `/api`, preserving APIM -> AGC -> AKS routing for readiness smoke probes after policy rewrite to the FastAPI readiness endpoint. +- **Issue #1107 / PR #1103 APIM smoke retry hardening (2026-05-19)**: `.github/workflows/deploy-azd.yml` now retries CORS preflight status and normalizes raw `curl -D` CRLF response headers before validating `Access-Control-Allow-*`, avoiding shell header-parsing false negatives while APIM applies the freshly uploaded CRUD API policy after `sync-apim`. +- **Issue #1107 / PR #1103 Flux preview cleanup fallback (2026-05-19)**: `.github/workflows/deploy-azd.yml` now falls back to `az aks command invoke` when direct runner `kubectl` credentials fail during branch-preview cleanup, ensuring the Flux `GitRepository` source can be restored from `feature/foundry-hosted-agents-pilot` to `main` after failed smoke validation. +- **Issue #1107 / PR #1103 CRUD preview desired-state pin (2026-05-19)**: `.kubernetes/releases/crud/crud-service.yaml` pins `crud-service` to image tag `571026e55688f0957c55963ca8e040b7193086da` so Flux branch-preview reconciliation deploys the same APIM policy-fix commit that the next workflow builds as `imageTag`/`testedSourceSha`, preserving branch-preview image/tag parity. - **Executive demo homepage refactor (2026-04-28)**: `apps/ui/app/page.tsx` now routes to a single-page, scroll-driven executive demo where the robots are the primary narrative device. The homepage now stages the cold open, hero search, CRM boardroom, discovery duo, truth pipeline, catalog galaxy, cart/checkout, inventory, logistics, returns/support, platform telemetry, and scenario close as one continuous presentation surface. Supporting additions include route-local demo components, a reusable agent profile drawer, and additive `AgentRobot` scene props (`facing`, `pointAt`, `lookAt`, `toolOverride`, `scenePeer`). - **Commerce journey agent continuity (2026-04-28)**: The storefront drill-down routes now keep visible agent presence past the homepage. `AgentRobotOverlay` accepts semantic size presets and scene-staging props, and category, orders, order detail, cart, search, product, and checkout surfaces now stage primary and secondary agents along the buyer journey. - **Commerce stage contract + drawer/operator follow-through (2026-04-28)**: Shopping-flow routes now share `apps/ui/components/templates/CommerceAgentLayout.tsx` so the primary-stage robot, side-cast robot, and telemetry chip are declared uniformly. `AgentProfileDrawer` now renders input/output schemas, curated sample payloads, a live sample-run action, and an in-place trace explorer modal; scenario drill-down drawers also receive live monitor metrics instead of static placeholders. @@ -173,6 +182,8 @@ merged squash commit on `main`. ### Runtime Hotfix Notes (2026-03-19) - **Dependency Management Hardening (Issue #316)**: Agent service `pyproject.toml` dependencies now use minimum version constraints for key runtime libraries and include a local editable `holiday-peak-lib` source mapping for development workflows. +- **CRUD HelmRelease Image Pin**: `.kubernetes/releases/crud/crud-service.yaml` now pins `crud-service` to tested ACR tag `be7ce0d3f4ae4b3300327a9426dd8065fa209301` from deployment run `26076840846`, preventing Flux preview/default-branch source restore from reconciling CRUD to stale or missing tags and preserving strict AGC readiness validation through a healthy CRUD backend. + - **Lockfile and Build Reproducibility**: App `.dockerignore` files now include `uv.lock` in Docker build context, service Dockerfiles use frozen `uv` sync installs, and CI validates `yarn.lock` / `uv.lock` freshness before linting. - **Infrastructure Secret and Region Hygiene**: Shared infrastructure now uses a random `newGuid()`-seeded fallback for PostgreSQL admin password generation and parameterizes Azure AI Foundry location instead of hardcoding the region. - **UI Product Enrichment Monitoring (Issue #352)**: Admin now exposes a dedicated enrichment monitoring route (`/admin/enrichment-monitor`) with real-time stage/job visibility, retry controls on monitor failures/log entries, and throughput + approval-rate visual indicators; search now surfaces explicit mode/intent signal chips; top navigation includes a pipeline status indicator that links directly to the monitor. @@ -191,6 +202,10 @@ merged squash commit on `main`. - **PostgreSQL Auth Contract Alignment**: `POSTGRES_AUTH_MODE` now drives the Flexible Server auth policy in Bicep and a pre-rollout workflow guard verifies the live server matches the configured runtime auth mode before CRUD is redeployed. - **CRUD Entra Principal Alignment**: Entra-mode deployment outputs now resolve `POSTGRES_USER` to the CRUD workload identity principal (`--crud-identity`), matching the pod identity used for token acquisition instead of the legacy agentpool principal. +### Runtime Hotfix Notes (2026-05-19) +- **CRUD Startup Dependency Timeout Boundary**: CRUD startup now bounds PostgreSQL pool initialization with `POSTGRES_POOL_STARTUP_TIMEOUT_SECONDS` and Key Vault secret retrieval with `KEY_VAULT_SECRET_STARTUP_TIMEOUT_SECONDS`. `/health` remains process liveness once FastAPI starts, while `/ready` continues to report Redis, Cosmos DB, PostgreSQL, and connector dependency failures as degraded/503. +- **CRUD Readiness Dependency Timeout Boundary**: CRUD `/ready` now runs PostgreSQL, Redis, Cosmos DB, and connector registry checks concurrently behind `READINESS_DEPENDENCY_TIMEOUT_SECONDS`, preserving strict degraded/503 readiness while surfacing slow dependency recovery as structured timeout detail before the AKS probe window is exceeded. + ### Merged PRs (v1.1.0) | # | Title | Category | |---|-------|----------| diff --git a/lib/src/holiday_peak_lib/agents/__init__.py b/lib/src/holiday_peak_lib/agents/__init__.py index 26e516458..20b3603a0 100644 --- a/lib/src/holiday_peak_lib/agents/__init__.py +++ b/lib/src/holiday_peak_lib/agents/__init__.py @@ -9,7 +9,7 @@ ) from .foundry import FoundryAgentConfig from .guardrails import EnrichmentGuardrail, SourceValidationResult -from .hosted import mount_hosted_agent +from .hosted import mount_hosted_agent, mount_responses_adapter from .models import ModelInvoker, ModelTarget, StreamingModelInvoker __all__ = [ @@ -26,4 +26,5 @@ "EnrichmentGuardrail", "SourceValidationResult", "mount_hosted_agent", + "mount_responses_adapter", ] diff --git a/lib/src/holiday_peak_lib/agents/base_agent.py b/lib/src/holiday_peak_lib/agents/base_agent.py index 1fe3adcd4..387766e1d 100644 --- a/lib/src/holiday_peak_lib/agents/base_agent.py +++ b/lib/src/holiday_peak_lib/agents/base_agent.py @@ -413,12 +413,12 @@ def _apply_routing_context( ) -> list[dict[str, Any]]: """Surface routing metadata to the invoker via both channels. - * **kwargs channel** — ``routing_complexity`` / ``routing_threshold`` - / ``routing_target_tier`` / ``routing_hint`` are mutated onto - the kwargs dict and reach every invoker unconditionally. This - is the canonical channel: Foundry hosted-agent adapters that - cannot accept runtime system messages still get the data and - can plumb it through their portal-owned prompt. + * **kwargs channel** — ``routing_complexity`` / ``routing_threshold`` + / ``routing_target_tier`` / ``routing_hint`` are mutated onto + the kwargs dict and reach every invoker unconditionally. This + is the canonical channel: adapters that cannot accept runtime + system messages still get the data and can plumb it through + their provider-owned prompt. * **system-message channel** — the hint is inserted after the leading system prefix as a best-effort surface. It reaches non-Foundry providers directly; under Foundry governance the @@ -506,7 +506,7 @@ async def __invoke_target( else "n/a" ) logger.info( - "agent_model_logprobs service=%s target=%s count=%d " "mean=%s perplexity=%s", + "agent_model_logprobs service=%s target=%s count=%d mean=%s perplexity=%s", getattr(self, "service_name", "unknown"), target.name, logprob_summary.get("count", 0), @@ -881,16 +881,16 @@ async def handle(self, request: dict[str, Any]) -> dict[str, Any]: """Handle an incoming request.""" # ------------------------------------------------------------------ # - # Foundry hosted-agent (Responses API) integration + # AKS-hosted Responses protocol integration # # These two methods let an existing FastAPI service additionally serve - # the Foundry Responses-protocol surface (``/v1/responses``) inside the + # the Responses-protocol surface (``/responses``) inside the # *same* uvicorn process — no second runtime, no parallel ``hosted_main.py``, # no extra port. They are additive: services that don't call - # ``serve_hosted()`` retain their current behaviour. + # ``serve_responses()`` retain their current behaviour. # ------------------------------------------------------------------ # - async def hosted_request_from_text(self, text: str) -> dict[str, Any]: + async def responses_request_from_text(self, text: str) -> dict[str, Any]: """Translate Responses-API free-form input text into the dict shape that this agent's ``handle()`` expects. @@ -900,15 +900,21 @@ async def hosted_request_from_text(self, text: str) -> dict[str, Any]: """ return {"prompt": text} - def serve_hosted( + async def hosted_request_from_text(self, text: str) -> dict[str, Any]: + """Compatibility alias for older service overrides. + + New services should override :meth:`responses_request_from_text`. + """ + return await self.responses_request_from_text(text) + + def serve_responses( self, fastapi_app: Any, *, - prefix: str = "/v1", + prefix: str = "", request_translator: Callable[[str], Awaitable[dict[str, Any]]] | None = None, ) -> Any: - """Mount this agent's Foundry Responses-protocol endpoints on the - given FastAPI app. + """Mount this agent's Responses protocol endpoints on the given FastAPI app. Single-process, single-runtime: the FastAPI app keeps owning ``/health``, ``/ready``, ``/mcp/*`` (registered before this call) and @@ -916,21 +922,38 @@ def serve_hosted( (matched after the direct routes because Starlette walks routes in registration order). + The default prefix is empty so the AKS service answers ``/responses`` + directly on the same process and port as the existing app surface. + Returns the constructed host server for tests / diagnostics. Raises ``ImportError`` if ``agent-framework-foundry-hosting`` is not installed in the active environment. """ # Lazy import to avoid a circular dependency between base_agent.py # and hosted.py, and to keep the optional SDK out of import time. - from .hosted import mount_hosted_agent # pylint: disable=import-outside-toplevel + from .hosted import mount_responses_adapter # pylint: disable=import-outside-toplevel - return mount_hosted_agent( + return mount_responses_adapter( fastapi_app, self, prefix=prefix, request_translator=request_translator, ) + def serve_hosted( + self, + fastapi_app: Any, + *, + prefix: str = "", + request_translator: Callable[[str], Awaitable[dict[str, Any]]] | None = None, + ) -> Any: + """Compatibility alias for :meth:`serve_responses`.""" + return self.serve_responses( + fastapi_app, + prefix=prefix, + request_translator=request_translator, + ) + def __init_subclass__(cls, **kwargs: Any) -> None: """Auto-wrap concrete handle() implementations with entry/exit logging.""" super().__init_subclass__(**kwargs) diff --git a/lib/src/holiday_peak_lib/agents/hosted.py b/lib/src/holiday_peak_lib/agents/hosted.py index 8fa821f15..1b4352f03 100644 --- a/lib/src/holiday_peak_lib/agents/hosted.py +++ b/lib/src/holiday_peak_lib/agents/hosted.py @@ -1,4 +1,4 @@ -"""Foundry hosted-agent runtime adapter. +"""AKS-hosted Responses protocol adapter. This module wraps a :class:`~holiday_peak_lib.agents.base_agent.BaseRetailAgent` in the Microsoft Agent Framework ``SupportsAgentRun`` protocol and mounts the @@ -7,30 +7,34 @@ * the same FastAPI process keeps serving ``/health``, ``/ready``, ``/mcp/*``, etc. (the AKS-friendly surface), AND -* the ``/{prefix}/responses`` endpoints are exposed for Azure AI Foundry's - Hosted Agents runtime (the portal-indexed surface). +* the ``/{prefix}/responses`` endpoints are exposed as an adapter on the same + AKS pod and port. Single process, single ``uvicorn`` listener, single port. No second runtime, -no parallel ``hosted_main.py``, no separate port 8088. The dual-runtime +no parallel ``hosted_main.py``, no separate port. The dual-runtime guardrail in ADR-005 (2026-05-10) targets the multi-process / multi-port shape — this helper is its compliant alternative. +Prefix policy: the mount default is the empty prefix so the AKS service answers +``/responses`` directly. Tests and local probes that need a prefixed layout can +pass ``prefix="/v1"`` explicitly. + Usage in a service ``main.py``:: app = create_standard_app(...) - app.state.agent.serve_hosted(app) + app.state.agent.serve_responses(app) -The lazy import of ``agent_framework_foundry_hosting`` keeps the dependency -optional: services that have not yet migrated continue to work unchanged. +The lazy import of the Responses hosting SDK keeps the dependency optional: +services that have not enabled the adapter continue to work unchanged. """ from __future__ import annotations import json import logging -from typing import TYPE_CHECKING, Any, Awaitable, Callable +from typing import TYPE_CHECKING, Any, AsyncIterator, Awaitable, Callable -from agent_framework import AgentResponse, Message +from agent_framework import AgentResponse, AgentResponseUpdate, Content, Message if TYPE_CHECKING: # pragma: no cover - typing only from fastapi import FastAPI @@ -39,6 +43,8 @@ logger = logging.getLogger(__name__) +_RESPONSES_HOST_SERVERS_STATE_KEY = "holiday_peak_responses_host_servers" + RequestTranslator = Callable[[str], Awaitable[dict[str, Any]]] """Async callable that converts free-form Responses-API input text into the @@ -46,23 +52,38 @@ Default implementation (provided on ``BaseRetailAgent``) returns ``{"prompt": text}``. Services with structured ``handle()`` schemas should -override ``BaseRetailAgent.hosted_request_from_text`` to extract the right +override ``BaseRetailAgent.responses_request_from_text`` to extract the right fields. """ -class _HostedAgentRunAdapter: +class _ResponsesAgentRunAdapter: """Minimal ``SupportsAgentRun`` implementation that delegates to a :class:`BaseRetailAgent`. - The Foundry ``ResponsesHostServer`` calls ``agent.run(messages, *, - stream, session, **kwargs)`` and expects an ``AgentResponse``. Our - retail agents implement ``handle(request: dict) -> dict`` instead. - This adapter bridges the two without touching ``handle()`` semantics. - - Streaming is not supported in this preview adapter — Foundry will fall - back to the non-streaming path. ``invoke_model_stream`` integration is - a follow-up. + The Responses ``ResponsesHostServer`` calls + ``agent.run(messages, *, stream, session, **kwargs)`` with two distinct + contracts depending on ``stream`` (verified against + ``agent_framework_foundry_hosting==1.0.0a260507`` in + ``agent_framework_foundry_hosting/_responses.py``): + + * ``stream=False`` — ``response = await agent.run(...)`` expects an + :class:`AgentResponse` (with ``.messages`` of ``Message`` objects). + * ``stream=True`` — ``async for update in agent.run(...):`` iterates + :class:`AgentResponseUpdate` items whose ``.contents`` are passed to + a tracker that emits SSE events. The Foundry portal Playground + always sets ``stream=True``. + + Because the same call site must return *either* a coroutine *or* an + async iterator, ``run`` is intentionally a synchronous dispatcher. + Marking it ``async def`` would always return a coroutine and break the + streaming path with ``TypeError: 'async for' requires an object with + __aiter__``. + + Retail agents implement ``handle(request: dict) -> dict`` (unary). This + adapter currently emits the full reply as a single + :class:`AgentResponseUpdate` chunk in the streaming path; richer + incremental streaming via ``invoke_model_stream`` is a follow-up. """ def __init__( @@ -80,25 +101,55 @@ def __init__( # ``middleware`` is read by the host server only when present. self.middleware = getattr(agent, "middleware", []) or [] - async def run( + def run( self, messages: Any = None, *, stream: bool = False, session: Any = None, **kwargs: Any, - ) -> AgentResponse: - # ``session`` and ``kwargs`` are part of the SupportsAgentRun protocol - # but the current adapter does not consume them — Foundry-side session - # state is managed by the host server, and we do not yet pass extra - # client kwargs through to ``handle()``. - _ = session, kwargs + ) -> "Awaitable[AgentResponse] | AsyncIterator[AgentResponseUpdate]": + """Dispatch on ``stream`` to satisfy the dual ``SupportsAgentRun`` + contract used by ``ResponsesHostServer``. + + ``session`` and ``kwargs`` are part of the protocol but the current + adapter does not consume them — Foundry-side session state is + managed by the host server, and we do not yet pass extra client + kwargs through to ``handle()``. + """ + messages = _resolve_run_messages(messages, kwargs) + _ = session if stream: - raise NotImplementedError( - "Streaming responses are not yet implemented for the hosted-agent " - "FastAPI mount. Re-issue the request with stream=False." - ) + return self._run_streaming(messages) + return self._run_once(messages) + + async def _run_once(self, messages: Any) -> AgentResponse: + """Non-streaming path: collect the unary reply and return one + :class:`AgentResponse`. + """ + reply_text = await self._invoke_handle(messages) + reply = Message(role="assistant", contents=[reply_text]) + return AgentResponse(messages=[reply], agent_id=self.id) + + async def _run_streaming(self, messages: Any) -> AsyncIterator[AgentResponseUpdate]: + """Streaming path: yield a single :class:`AgentResponseUpdate` + chunk carrying the full text reply. + + The retail agents' ``handle()`` is unary, so we cannot emit + incremental tokens yet. Emitting one well-formed update is + sufficient for the Foundry host's SSE tracker to render the reply + and terminate the stream cleanly; richer per-token streaming via + ``invoke_model_stream`` is tracked as a follow-up. + """ + reply_text = await self._invoke_handle(messages) + yield AgentResponseUpdate( + contents=[Content(type="text", text=reply_text)], + role="assistant", + agent_id=self.id, + ) + async def _invoke_handle(self, messages: Any) -> str: + """Shared translation + dispatch + extraction used by both paths.""" text = _extract_user_text(messages) request = await self._translate(text) try: @@ -109,10 +160,7 @@ async def run( getattr(self._agent, "service_name", self.name), ) raise - - reply_text = _extract_text_from_handle_result(result) - reply = Message(role="assistant", contents=[reply_text]) - return AgentResponse(messages=[reply], agent_id=self.id) + return _extract_text_from_handle_result(result) def create_session(self, *, session_id: str | None = None) -> Any: # Delegate to the wrapped agent (BaseAgent provides this from agent_framework). @@ -123,15 +171,106 @@ def get_session(self, service_session_id: str, *, session_id: str | None = None) def _extract_user_text(messages: Any) -> str: - """Pull the most recent user-message text from a MAF message sequence.""" + """Pull the most recent user-message text from MAF/OpenAI shapes.""" if not messages: return "" - seq = messages if isinstance(messages, (list, tuple)) else [messages] - for msg in reversed(seq): - for content in getattr(msg, "contents", None) or []: - text = getattr(content, "text", None) + sequence = messages if isinstance(messages, (list, tuple)) else [messages] + for message in reversed(sequence): + role = _message_role(message) + if role is not None and role != "user": + continue + text = _extract_message_text(message) + if text: + return text + return "" + + +def _resolve_run_messages(messages: Any, kwargs: dict[str, Any]) -> Any: + """Return the message payload from known ``SupportsAgentRun`` call shapes.""" + if messages is not None: + return messages + for field_name in ("messages", "input", "inputs"): + value = kwargs.get(field_name) + if value is not None: + return value + return messages + + +def _message_role(message: Any) -> str | None: + """Return a normalized role from dict-like or object-like messages.""" + role = _message_field(message, "role") + if role is None: + return None + role_value = getattr(role, "value", role) + normalized = str(role_value).lower() + if normalized.startswith("messagerole."): + return normalized.rsplit(".", maxsplit=1)[-1] + return normalized + + +def _message_field(message: Any, field_name: str) -> Any: + """Read one field from a dict-like or object-like message.""" + if isinstance(message, dict): + return message.get(field_name) + return getattr(message, field_name, None) + + +def _extract_message_text(message: Any) -> str: + """Return the first non-empty text value from a single message.""" + if isinstance(message, str): + return message + + for field_name in ("contents", "content"): + text = _extract_content_text(_message_field(message, field_name)) + if text: + return text + + text = _extract_input_text(_message_field(message, "input")) + if text: + return text + + return _extract_content_text(_message_field(message, "text")) + + +def _extract_input_text(value: Any) -> str: + """Extract text from Responses ``input`` values.""" + if _looks_like_message_sequence(value): + return _extract_user_text(value) + return _extract_content_text(value) + + +def _looks_like_message_sequence(value: Any) -> bool: + """Return whether a value looks like a list of chat messages.""" + if not isinstance(value, (list, tuple)): + return False + return any( + _message_field(item, "role") is not None + or _message_field(item, "contents") is not None + or _message_field(item, "content") is not None + for item in value + ) + + +def _extract_content_text(value: Any) -> str: + """Return the first non-empty text value from content parts.""" + if value is None: + return "" + if isinstance(value, str): + return value + if isinstance(value, (list, tuple)): + for item in value: + text = _extract_content_text(item) if text: - return str(text) + return text + return "" + for field_name in ("contents", "content", "input", "text"): + field_value = _message_field(value, field_name) + if field_name == "input": + text = _extract_input_text(field_value) + else: + text = _extract_content_text(field_value) + if text: + return text return "" @@ -165,10 +304,27 @@ def mount_hosted_agent( fastapi_app: "FastAPI", agent: "BaseRetailAgent", *, - prefix: str = "/v1", + prefix: str = "", request_translator: RequestTranslator | None = None, ) -> Any: - """Mount the Foundry Responses-protocol surface on an existing FastAPI app. + """Backward-compatible alias for :func:`mount_responses_adapter`.""" + + return mount_responses_adapter( + fastapi_app, + agent, + prefix=prefix, + request_translator=request_translator, + ) + + +def mount_responses_adapter( + fastapi_app: "FastAPI", + agent: "BaseRetailAgent", + *, + prefix: str = "", + request_translator: RequestTranslator | None = None, +) -> Any: + """Mount the Responses protocol adapter on an existing FastAPI app. Args: fastapi_app: The FastAPI application that already serves ``/health``, @@ -177,14 +333,18 @@ def mount_hosted_agent( registered on this app take precedence over the mounted host server because Starlette matches in registration order. agent: The :class:`BaseRetailAgent` instance whose ``handle()`` will - answer Foundry hosted-agent invocations. + answer Responses protocol invocations. prefix: URL prefix for the Responses protocol routes - (``/{prefix}/responses``). Defaults to ``"/v1"`` to match the - documented Foundry endpoint shape. + (``/{prefix}/responses``). Defaults to ``""`` so the AKS service + answers ``/responses`` directly. Pass ``"/v1"`` only for probes + that need a prefixed layout. request_translator: Optional async callable that converts the Responses-API free-form input string into the service-specific request dict for ``handle()``. When ``None``, falls back to - ``agent.hosted_request_from_text`` (defined on ``BaseRetailAgent``). + ``agent.responses_request_from_text`` (defined on + ``BaseRetailAgent``), with the legacy + ``agent.hosted_request_from_text`` override honored for + compatibility. Returns: The constructed ``ResponsesHostServer`` (a Starlette ASGI app), @@ -204,17 +364,31 @@ def mount_hosted_agent( ) except ImportError as exc: # pragma: no cover - exercised at runtime only raise ImportError( - "agent-framework-foundry-hosting is required to mount Foundry " - "hosted-agent endpoints. Install with " + "agent-framework-foundry-hosting is required to mount AKS-hosted " + "Responses protocol endpoints. Install with " "`pip install --pre agent-framework-foundry-hosting`." ) from exc + mounted_servers = getattr(fastapi_app.state, _RESPONSES_HOST_SERVERS_STATE_KEY, None) + if not isinstance(mounted_servers, dict): + mounted_servers = {} + setattr(fastapi_app.state, _RESPONSES_HOST_SERVERS_STATE_KEY, mounted_servers) + + if prefix in mounted_servers: + logger.info( + "responses_adapter_mount_reused service=%s prefix=%s", + getattr(agent, "service_name", "agent"), + prefix, + ) + return mounted_servers[prefix] + translator = request_translator or _default_translator(agent) - adapter = _HostedAgentRunAdapter(agent, translator) + adapter = _ResponsesAgentRunAdapter(agent, translator) host_server = ResponsesHostServer(adapter, prefix=prefix) fastapi_app.mount("/", host_server) + mounted_servers[prefix] = host_server logger.info( - "hosted_agent_mounted service=%s prefix=%s", + "responses_adapter_mounted service=%s prefix=%s", getattr(agent, "service_name", adapter.name), prefix, ) @@ -222,13 +396,17 @@ def mount_hosted_agent( def _default_translator(agent: "BaseRetailAgent") -> RequestTranslator: - """Build a translator that delegates to ``agent.hosted_request_from_text``. + """Build a translator for the agent's Responses request hook. Defined as a function rather than a lambda so the closure carries a clear name in tracebacks. """ + if "responses_request_from_text" in type(agent).__dict__: + translate_method = agent.responses_request_from_text + else: + translate_method = agent.hosted_request_from_text async def _translate(text: str) -> dict[str, Any]: - return await agent.hosted_request_from_text(text) + return await translate_method(text) return _translate diff --git a/lib/src/holiday_peak_lib/agents/models.py b/lib/src/holiday_peak_lib/agents/models.py index d3ab41756..8ea01fec9 100644 --- a/lib/src/holiday_peak_lib/agents/models.py +++ b/lib/src/holiday_peak_lib/agents/models.py @@ -3,7 +3,7 @@ This module defines what a *model* is from the agent's point of view: * :data:`ModelInvoker` — the async-callable contract every model adapter - satisfies (Azure AI Agents, Chat Completions, Foundry hosted, …). + satisfies (Azure AI Agents, Chat Completions, Responses API, etc.). * :class:`StreamingModelInvoker` — Strategy-pattern marker for invokers that additionally support token streaming via ``stream=True``. * :class:`ModelTarget` — a deployable model bound to a concrete invoker @@ -154,7 +154,7 @@ def build_logprobs_payload(target: "ModelTarget") -> dict[str, Any]: * **Chat Completions** (and Azure OpenAI / generic adapters): ``{"logprobs": True, "top_logprobs": ?}``. - * **Responses API** (Foundry hosted agents, OpenAI Responses): + * **Responses API** (Foundry/OpenAI Responses): ``{"include": ["message.output_text.logprobs"], "top_logprobs": ?}`` — Responses API does not accept a boolean ``logprobs`` field; presence of the token in the ``include`` array is the toggle. @@ -192,7 +192,7 @@ class ModelTarget: The ``invoker`` is an async callable that receives ``messages`` (list or str), optional ``tools``, and any extra kwargs. This keeps the agent base class agnostic of the concrete SDK (Azure AI Agents, Chat - Completions, Foundry hosted, …). + Completions, Responses API, etc.). ``logprobs`` defaults to ``True`` so every new configuration emits per-token confidence into the telemetry channel out-of-the-box. diff --git a/lib/src/holiday_peak_lib/app_factory.py b/lib/src/holiday_peak_lib/app_factory.py index c00c6e88a..68d1c6eea 100644 --- a/lib/src/holiday_peak_lib/app_factory.py +++ b/lib/src/holiday_peak_lib/app_factory.py @@ -39,6 +39,57 @@ "Structured instructions file not found for '{service_name}'. " "Use only provided request data, state missing fields, and avoid assumptions." ) +_REDIS_SOCKET_TIMEOUT_ENV = "HOLIDAY_PEAK_REDIS_SOCKET_TIMEOUT_SECONDS" +_REDIS_CONNECT_TIMEOUT_ENV = "HOLIDAY_PEAK_REDIS_CONNECT_TIMEOUT_SECONDS" +_FOUNDRY_HOSTED_ENV = "HOLIDAY_PEAK_FOUNDRY_HOSTED" +_DEFAULT_REDIS_SOCKET_TIMEOUT_SECONDS = 1.0 +_DEFAULT_REDIS_CONNECT_TIMEOUT_SECONDS = 1.0 + + +def _env_truthy(name: str) -> bool: + """Return whether an environment flag is explicitly enabled.""" + return (os.getenv(name) or "").lower() in {"1", "true", "yes", "on"} + + +def _env_positive_float(name: str, *, default: float) -> float: + """Return a positive float environment value, falling back on invalid input.""" + raw_value = os.getenv(name) + if raw_value is None: + return default + + try: + value = float(raw_value) + except ValueError: + return default + return value if value > 0 else default + + +def _build_hot_memory(memory_settings: MemorySettings) -> HotMemory | None: + """Build bounded Redis hot memory when Redis configuration is present.""" + resolved_redis_url = memory_settings.resolve_redis_url() + if not resolved_redis_url: + return None + + return HotMemory( + resolved_redis_url, + socket_timeout=_env_positive_float( + _REDIS_SOCKET_TIMEOUT_ENV, + default=_DEFAULT_REDIS_SOCKET_TIMEOUT_SECONDS, + ), + socket_connect_timeout=_env_positive_float( + _REDIS_CONNECT_TIMEOUT_ENV, + default=_DEFAULT_REDIS_CONNECT_TIMEOUT_SECONDS, + ), + retry_on_timeout=False, + ) + + +def _detach_agent_hot_memory(agent: BaseRetailAgent) -> None: + """Detach optional hot memory from the request path after auth failure.""" + agent.hot_memory = None + memory_client = getattr(agent, "memory_client", None) + if memory_client is not None: + memory_client.hot = None async def _fetch_key_vault_secret(vault_uri: str, secret_name: str) -> str: @@ -89,8 +140,7 @@ def create_standard_app( """ self_healing_kernel = SelfHealingKernel.from_env(service_name) memory_settings = MemorySettings() - resolved_redis_url = memory_settings.resolve_redis_url() - hot_memory = HotMemory(resolved_redis_url) if resolved_redis_url else None + hot_memory = _build_hot_memory(memory_settings) warm_memory = ( WarmMemory( memory_settings.cosmos_account_uri, @@ -334,6 +384,7 @@ def _current_foundry_readiness() -> FoundryReadinessSnapshot: @asynccontextmanager async def _service_lifespan(wrapped_app: FastAPI) -> AsyncIterator[None]: + logger.info("lifespan_start service=%s", service_name) # Resolve missing Azure Redis auth from Key Vault before serving traffic. if ( memory_settings is not None @@ -342,6 +393,7 @@ async def _service_lifespan(wrapped_app: FastAPI) -> AsyncIterator[None]: and memory_settings.redis_password_secret_name and memory_settings.redis_url_needs_password_resolution(hot_memory.url) ): + logger.info("lifespan_kv_resolve_begin service=%s", service_name) try: redis_password = await _fetch_key_vault_secret( memory_settings.key_vault_uri, @@ -358,12 +410,24 @@ async def _service_lifespan(wrapped_app: FastAPI) -> AsyncIterator[None]: "hot memory may be unavailable", exc_info=True, ) + if memory_settings.redis_url_needs_password_resolution(hot_memory.url): + hot_memory.client = None + _detach_agent_hot_memory(agent) + logger.warning( + "Redis password resolution failed for authless Azure Redis URL; " + "detached hot memory from request path" + ) + logger.info("lifespan_kv_resolve_end service=%s", service_name) if lifespan is not None: + logger.info("lifespan_eventhub_begin service=%s", service_name) async with lifespan(wrapped_app): + logger.info("lifespan_ready service=%s", service_name) yield else: + logger.info("lifespan_ready service=%s no_eventhub_lifespan=true", service_name) yield + logger.info("lifespan_shutdown service=%s", service_name) app.router.lifespan_context = _service_lifespan router.register("default", agent.handle) @@ -396,4 +460,7 @@ def _foundry_capabilities() -> dict[str, Any]: register_standard_endpoints(app, ctx=endpoint_ctx) mcp.mount() + if _env_truthy(_FOUNDRY_HOSTED_ENV): + logger.info("foundry_hosted_mode_enabled service=%s", service_name) + agent.serve_responses(app) return app diff --git a/lib/src/holiday_peak_lib/app_factory_components/endpoints.py b/lib/src/holiday_peak_lib/app_factory_components/endpoints.py index ab8f08066..4d84bebaa 100644 --- a/lib/src/holiday_peak_lib/app_factory_components/endpoints.py +++ b/lib/src/holiday_peak_lib/app_factory_components/endpoints.py @@ -252,6 +252,13 @@ async def health() -> dict[str, Any]: "integrations_registered": await registry.count(), } + # The optional Responses hosting SDK also exposes a lightweight + # ``/readiness`` surface. Keep the standard app's alias process-only; + # the deeper downstream-target check stays on ``/ready``. + @app.get("/readiness") + async def readiness() -> dict[str, Any]: + return {"status": "ok", "service": service_name} + @app.get("/integrations") async def integrations() -> dict[str, Any]: return { diff --git a/lib/src/holiday_peak_lib/app_factory_components/foundry_lifecycle.py b/lib/src/holiday_peak_lib/app_factory_components/foundry_lifecycle.py index aef1b6606..f7e669bf4 100644 --- a/lib/src/holiday_peak_lib/app_factory_components/foundry_lifecycle.py +++ b/lib/src/holiday_peak_lib/app_factory_components/foundry_lifecycle.py @@ -13,11 +13,20 @@ def build_foundry_config(agent_env: str, deployment_env: str) -> FoundryAgentConfig | None: - """Build direct-model Foundry configuration from environment variables.""" + """Build direct-model Foundry configuration from environment variables. + + The AKS runtime contract uses the ``FOUNDRY_AGENT_ID_*`` and + ``FOUNDRY_AGENT_NAME_*`` environment variables for portal-tracking + metadata while direct-model invocation binds to ``MODEL_DEPLOYMENT_NAME_*``. + Foundry-hosted surface manifests use ``HPH_AGENT_ID_*`` aliases because + the platform reserves generic ``FOUNDRY_*`` and ``AGENT_*`` names. + """ endpoint = os.getenv("PROJECT_ENDPOINT") or os.getenv("FOUNDRY_ENDPOINT") project_name = os.getenv("PROJECT_NAME") or os.getenv("FOUNDRY_PROJECT_NAME") role = "fast" if agent_env.endswith("FAST") else "rich" - agent_id = os.getenv(agent_env) or f"{role}-pending" + agent_id = ( + os.getenv(agent_env) or os.getenv(f"HPH_AGENT_ID_{role.upper()}") or f"{role}-pending" + ) agent_name = os.getenv(f"FOUNDRY_AGENT_NAME_{role.upper()}") deployment = os.getenv(deployment_env) if not endpoint: diff --git a/lib/tests/test_agents_base.py b/lib/tests/test_agents_base.py index ca10bb2e9..bd400c082 100644 --- a/lib/tests/test_agents_base.py +++ b/lib/tests/test_agents_base.py @@ -423,10 +423,10 @@ async def llm_invoker(**kwargs): async def test_foundry_governance_strips_hint_but_kwargs_survive(self): """Under Foundry governance the system hint is stripped, kwargs survive. - Foundry hosted agents own their system prompt at the portal - level, so the messages-channel hint must not leak through. The - kwargs channel still carries the routing metadata so adapters - can plumb it into their portal-owned prompt. + Foundry-backed Responses targets own their system prompt at the + provider boundary, so the messages-channel hint must not leak + through. The kwargs channel still carries the routing metadata + so adapters can plumb it into their provider-owned prompt. """ captured = {} @@ -785,7 +785,7 @@ async def test_payload_uses_responses_api_logprobs_shape_for_foundry(self): The Responses API does not accept a boolean ``logprobs`` field; the toggle is the presence of ``message.output_text.logprobs`` in the ``include`` array. Sending ``logprobs: True`` to a - Foundry hosted agent is at best ignored and at worst rejected + Foundry Responses call is at best ignored and at worst rejected as an unknown parameter, so the framework must not emit it. """ captured = {} diff --git a/lib/tests/test_agents_hosted.py b/lib/tests/test_agents_hosted.py index 6575f75aa..b2ee24742 100644 --- a/lib/tests/test_agents_hosted.py +++ b/lib/tests/test_agents_hosted.py @@ -1,24 +1,26 @@ -"""Tests for the Foundry hosted-agent FastAPI mount adapter. +"""Tests for the AKS-hosted Responses FastAPI mount adapter. These tests cover the *translation logic* (free-form text -> handle dict -> AgentResponse) without requiring the optional -``agent-framework-foundry-hosting`` package to be installed. End-to-end -mount tests live under ``.tmp/probe_mount.py`` and the planned -``inventory-health-check`` integration test once the SDK is available in -the lib venv. +``agent-framework-foundry-hosting`` package to be installed. When that SDK is +present, the mounted ``/responses`` path is also exercised through FastAPI's +``TestClient``. """ from __future__ import annotations +import sys +import types from typing import Any import pytest -from agent_framework import Message +from agent_framework import Content, Message from holiday_peak_lib.agents.base_agent import AgentDependencies, BaseRetailAgent from holiday_peak_lib.agents.hosted import ( _extract_text_from_handle_result, _extract_user_text, - _HostedAgentRunAdapter, + _resolve_run_messages, + _ResponsesAgentRunAdapter, ) @@ -35,19 +37,120 @@ async def handle(self, request: dict[str, Any]) -> dict[str, Any]: return self.next_response +class _MessageLike: + def __init__( + self, + *, + role: str | None = "user", + contents: list[Any] | None = None, + content: Any = None, + text: str | None = None, + input_value: Any = None, + ) -> None: + self.role = role + self.contents = contents + self.content = content + self.text = text + self.input = input_value + + +class _EnumRoleLike: + """Minimal enum-like role object matching SDK role.value behavior.""" + + value = "user" + + def test_extract_user_text_pulls_last_text_message() -> None: msgs = [ - Message(role="user", contents=["earlier"]), - Message(role="user", contents=["latest input text"]), + Message(role="user", contents=[Content(type="text", text="earlier")]), + Message( + role="user", + contents=[Content(type="text", text="latest input text")], + ), ] assert _extract_user_text(msgs) == "latest input text" +def test_extract_user_text_reads_content_from_text_contract() -> None: + messages = [Message(role="user", contents=[Content.from_text("check health for SKU-1234")])] + + assert _extract_user_text(messages) == "check health for SKU-1234" + + +def test_extract_user_text_handles_enum_like_user_role() -> None: + message = _MessageLike( + role=_EnumRoleLike(), + contents=[Content.from_text("check health for SKU-1234")], + ) + + assert _extract_user_text([message]) == "check health for SKU-1234" + + +@pytest.mark.parametrize( + ("message", "expected"), + [ + (_MessageLike(content="plain object content"), "plain object content"), + ( + _MessageLike(content=[{"type": "input_text", "text": "object part"}]), + "object part", + ), + ({"role": "user", "content": "plain dict content"}, "plain dict content"), + ( + {"role": "user", "content": [{"type": "input_text", "text": "dict part"}]}, + "dict part", + ), + (_MessageLike(role=None, text="object direct text"), "object direct text"), + ({"text": "dict direct text"}, "dict direct text"), + ({"input": "direct input text"}, "direct input text"), + ( + { + "input": [ + {"role": "user", "content": "earlier nested input"}, + { + "role": "user", + "content": [{"type": "input_text", "text": "latest nested input"}], + }, + ] + }, + "latest nested input", + ), + ], +) +def test_extract_user_text_handles_common_maf_and_openai_shapes( + message: Any, expected: str +) -> None: + assert _extract_user_text(message) == expected + + +def test_extract_user_text_prefers_most_recent_user_message() -> None: + messages = [ + {"role": "user", "content": "older user text"}, + {"role": "assistant", "content": "assistant text should be ignored"}, + {"role": "user", "content": "latest user text"}, + ] + assert _extract_user_text(messages) == "latest user text" + + +def test_extract_user_text_skips_later_non_user_messages() -> None: + messages = [ + {"role": "user", "content": "latest user text"}, + {"role": "assistant", "content": "assistant text should be ignored"}, + ] + assert _extract_user_text(messages) == "latest user text" + + def test_extract_user_text_handles_empty_inputs() -> None: assert _extract_user_text(None) == "" assert _extract_user_text([]) == "" +def test_resolve_run_messages_accepts_sdk_keyword_messages() -> None: + messages = [Message(role="user", contents=["kwarg"])] + + assert _resolve_run_messages(None, {"messages": messages}) is messages + assert _resolve_run_messages(["positional"], {"messages": messages}) == ["positional"] + + def test_extract_text_from_handle_result_prefers_known_keys() -> None: assert _extract_text_from_handle_result({"text": "t-value"}) == "t-value" assert _extract_text_from_handle_result({"response": "r-value"}) == "r-value" @@ -73,6 +176,13 @@ def test_extract_text_from_handle_result_falls_back_to_json() -> None: @pytest.mark.asyncio async def test_default_request_translator_passes_prompt() -> None: + agent = _RecordingAgent() + request = await agent.responses_request_from_text("free form input") + assert request == {"prompt": "free form input"} + + +@pytest.mark.asyncio +async def test_hosted_request_translator_alias_passes_prompt() -> None: agent = _RecordingAgent() request = await agent.hosted_request_from_text("free form input") assert request == {"prompt": "free form input"} @@ -85,7 +195,7 @@ async def test_hosted_run_adapter_round_trips_text() -> None: async def translator(text: str) -> dict[str, Any]: return {"prompt": text, "kind": "translated"} - adapter = _HostedAgentRunAdapter(agent, translator) + adapter = _ResponsesAgentRunAdapter(agent, translator) response = await adapter.run(messages=[Message(role="user", contents=["check me"])]) assert agent.last_request == {"prompt": "check me", "kind": "translated"} @@ -95,18 +205,89 @@ async def translator(text: str) -> dict[str, Any]: @pytest.mark.asyncio -async def test_hosted_run_adapter_refuses_streaming() -> None: +async def test_hosted_run_adapter_uses_messages_from_kwargs() -> None: + agent = _RecordingAgent() + + async def translator(text: str) -> dict[str, Any]: + return {"prompt": text} + + adapter = _ResponsesAgentRunAdapter(agent, translator) + response = await adapter.run(messages=[Message(role="user", contents=["positional"])]) + assert agent.last_request == {"prompt": "positional"} + assert response.messages + + await adapter.run(input=[Message(role="user", contents=["kwarg-input"])]) + + assert agent.last_request == {"prompt": "kwarg-input"} + + +@pytest.mark.asyncio +async def test_hosted_run_adapter_streams_single_update() -> None: + """``run(stream=True)`` must return an async iterator (NOT a coroutine). + + The Responses ``ResponsesHostServer`` calls + ``async for update in agent.run(stream=True, ...):`` without awaiting + first — so the dispatcher MUST return an async iterator directly. + Marking ``run`` as ``async def`` would always return a coroutine and + fail with ``TypeError: 'async for' requires an object with __aiter__``. + This test pins the streaming contract that powers the Foundry portal + Playground (which always sets ``stream=True``). + """ + from agent_framework import AgentResponseUpdate + + agent = _RecordingAgent() + + async def translator(text: str) -> dict[str, Any]: + return {"prompt": text, "kind": "translated"} + + adapter = _ResponsesAgentRunAdapter(agent, translator) + + iterator = adapter.run(messages=[Message(role="user", contents=["stream me"])], stream=True) + + # MUST be an async iterator, not a coroutine. + assert hasattr(iterator, "__aiter__"), ( + "run(stream=True) must return an async iterator so the host server " + "can `async for update in agent.run(...)` it directly" + ) + + updates = [item async for item in iterator] + + assert len(updates) == 1, "Single-chunk streaming adapter emits one update" + update = updates[0] + assert isinstance(update, AgentResponseUpdate) + assert update.role == "assistant" + assert update.contents and len(update.contents) == 1 + text = getattr(update.contents[0], "text", None) + assert text == "hello-from-handle" + # Translator was called and ``handle()`` received the translated request. + assert agent.last_request == {"prompt": "stream me", "kind": "translated"} + + +@pytest.mark.asyncio +async def test_hosted_run_adapter_non_streaming_returns_awaitable() -> None: + """``run(stream=False)`` must return an awaitable (not an async iterator). + + The non-streaming Responses path does ``response = await agent.run(...)``, + so the dispatcher must return a coroutine/awaitable that resolves to an + :class:`AgentResponse`. + """ agent = _RecordingAgent() async def translator(text: str) -> dict[str, Any]: return {"prompt": text} - adapter = _HostedAgentRunAdapter(agent, translator) - with pytest.raises(NotImplementedError): - await adapter.run(messages=[Message(role="user", contents=["x"])], stream=True) + adapter = _ResponsesAgentRunAdapter(agent, translator) + awaitable = adapter.run(messages=[Message(role="user", contents=["once"])]) + + # The non-streaming path returns a coroutine; awaiting it yields an + # AgentResponse. It must NOT be async-iterable (that's the streaming + # path's contract). + assert hasattr(awaitable, "__await__") + response = await awaitable + assert response.messages and response.messages[0].contents -def test_serve_hosted_raises_clear_error_when_sdk_missing(monkeypatch) -> None: +def test_serve_responses_raises_clear_error_when_sdk_missing(monkeypatch) -> None: """If ``agent-framework-foundry-hosting`` is unavailable, the helper must raise an actionable ``ImportError`` rather than a generic one. """ @@ -123,6 +304,28 @@ def _blocked_import(name, *args, **kwargs): agent = _RecordingAgent() + class _StubApp: + def mount(self, *args, **kwargs): # pragma: no cover - never reached + raise AssertionError("mount should not be called when SDK missing") + + with pytest.raises(ImportError, match="agent-framework-foundry-hosting"): + agent.serve_responses(_StubApp()) + + +def test_serve_hosted_alias_raises_clear_error_when_sdk_missing(monkeypatch) -> None: + """The legacy method name remains an alias for existing callers.""" + import builtins + + real_import = builtins.__import__ + + def _blocked_import(name, *args, **kwargs): + if name == "agent_framework_foundry_hosting": + raise ImportError("blocked for test") + return real_import(name, *args, **kwargs) + + monkeypatch.setattr(builtins, "__import__", _blocked_import) + agent = _RecordingAgent() + class _StubApp: def mount(self, *args, **kwargs): # pragma: no cover - never reached raise AssertionError("mount should not be called when SDK missing") @@ -134,24 +337,136 @@ def mount(self, *args, **kwargs): # pragma: no cover - never reached # --------------------------------------------------------------------------- # Optional integration test: only runs when the preview SDK is installed. # Keeps the suite green without the SDK while exercising the real mount when -# operators do install it (e.g. in a Foundry-hosted-pilot track). +# operators do install it for the AKS-hosted Responses adapter. # --------------------------------------------------------------------------- -def test_serve_hosted_mounts_responses_routes_when_sdk_present() -> None: +def test_serve_responses_mounts_responses_routes_when_sdk_present() -> None: pytest.importorskip("agent_framework_foundry_hosting") from fastapi import FastAPI agent = _RecordingAgent() app = FastAPI() - host_server = agent.serve_hosted(app, prefix="/v1") + host_server = agent.serve_responses(app) paths = { getattr(r, "path", None) or getattr(r, "path_format", None) for r in host_server.routes } paths.discard(None) - assert "/v1/responses" in paths + assert "/responses" in paths # FastAPI mount appended at the end of the route list. mounted = [r for r in app.router.routes if getattr(r, "path", "") == ""] - assert mounted, "ResponsesHostServer should be mounted on the FastAPI app" + assert mounted, "Responses adapter should be mounted on the FastAPI app" + + +def test_serve_responses_is_idempotent_with_same_prefix_when_sdk_present() -> None: + pytest.importorskip("agent_framework_foundry_hosting") + from fastapi import FastAPI + + agent = _RecordingAgent() + app = FastAPI() + + first_host_server = agent.serve_responses(app) + second_host_server = agent.serve_responses(app) + + assert second_host_server is first_host_server + mounted = [route for route in app.router.routes if getattr(route, "path", "") == ""] + assert len(mounted) == 1 + + +def test_foundry_hosted_mode_auto_mounts_responses_with_fake_sdk(monkeypatch) -> None: + from holiday_peak_lib.app_factory import create_standard_app + from starlette.applications import Starlette + from starlette.responses import JSONResponse + from starlette.routing import Route + + async def _ok(_request): # noqa: ANN001 + return JSONResponse({"ok": True}) + + class _FakeResponsesHostServer(Starlette): + def __init__(self, adapter: Any, prefix: str = "") -> None: + super().__init__(routes=[Route(f"{prefix}/responses", _ok, methods=["POST"])]) + self.adapter = adapter + + fake_sdk = types.SimpleNamespace(ResponsesHostServer=_FakeResponsesHostServer) + monkeypatch.setitem(sys.modules, "agent_framework_foundry_hosting", fake_sdk) + monkeypatch.setenv("HOLIDAY_PEAK_FOUNDRY_HOSTED", "1") + + class _FactoryAgent(BaseRetailAgent): + async def handle(self, request: dict[str, Any]) -> dict[str, Any]: + return {"text": request.get("prompt", "")} + + app = create_standard_app("foundry-hosted-test", _FactoryAgent) + + mounted = [route for route in app.routes if getattr(route, "path", "") == ""] + assert len(mounted) == 1 + response_paths = { + getattr(route, "path", None) or getattr(route, "path_format", None) + for route in mounted[0].app.routes + } + assert "/responses" in response_paths + + +def test_serve_responses_honors_explicit_prefix_when_sdk_present() -> None: + pytest.importorskip("agent_framework_foundry_hosting") + from fastapi import FastAPI + + agent = _RecordingAgent() + app = FastAPI() + + host_server = agent.serve_responses(app, prefix="/v1") + + paths = { + getattr(r, "path", None) or getattr(r, "path_format", None) for r in host_server.routes + } + paths.discard(None) + assert "/v1/responses" in paths + + +@pytest.mark.parametrize( + "body", + [ + {"model": "inventory-health-check", "input": "check health for SKU-1234"}, + { + "model": "inventory-health-check", + "input": [ + { + "type": "message", + "role": "user", + "content": "check health for SKU-1234", + } + ], + }, + { + "model": "inventory-health-check", + "input": [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "check health for SKU-1234"}], + } + ], + }, + ], +) +def test_serve_responses_post_preserves_prompt_when_sdk_present( + body: dict[str, Any], +) -> None: + pytest.importorskip("agent_framework_foundry_hosting") + from fastapi import FastAPI + from fastapi.testclient import TestClient + + agent = _RecordingAgent() + + async def translator(text: str) -> dict[str, Any]: + return {"prompt": text} + + app = FastAPI() + agent.serve_responses(app, request_translator=translator) + client = TestClient(app) + + response = client.post("/responses", json=body) + + assert response.status_code == 200 + assert agent.last_request == {"prompt": "check health for SKU-1234"} diff --git a/lib/tests/test_app_factory.py b/lib/tests/test_app_factory.py index de6b0df15..e79deea35 100644 --- a/lib/tests/test_app_factory.py +++ b/lib/tests/test_app_factory.py @@ -10,9 +10,10 @@ from holiday_peak_lib.agents.memory.cold import ColdMemory from holiday_peak_lib.agents.memory.hot import HotMemory from holiday_peak_lib.agents.memory.warm import WarmMemory -from holiday_peak_lib.app_factory import build_service_app +from holiday_peak_lib.app_factory import build_service_app, create_standard_app from holiday_peak_lib.config.settings import MemorySettings from holiday_peak_lib.connectors.registry import ConnectorRegistry +from holiday_peak_lib.utils import EventHubSubscription TEST_PROJECT_ENDPOINT = "https://test.services.ai.azure.com/api/projects/test-project" @@ -70,6 +71,63 @@ def _clear_foundry_env(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv(key, raising=False) +def _clear_runtime_dependency_env(monkeypatch: pytest.MonkeyPatch) -> None: + for key in ( + "REDIS_URL", + "REDIS_HOST", + "REDIS_PASSWORD", + "KEY_VAULT_URI", + "COSMOS_ACCOUNT_URI", + "COSMOS_DATABASE", + "COSMOS_CONTAINER", + "BLOB_ACCOUNT_URL", + "BLOB_CONTAINER", + "EVENT_HUB_NAMESPACE", + "EVENT_HUB_CONNECTION_STRING", + "HOLIDAY_PEAK_REDIS_SOCKET_TIMEOUT_SECONDS", + "HOLIDAY_PEAK_REDIS_CONNECT_TIMEOUT_SECONDS", + ): + monkeypatch.delenv(key, raising=False) + + +class TestCreateStandardAppRuntimeFlags: + """Test create_standard_app dependency wiring controls.""" + + def test_hot_memory_defaults_to_enabled_with_bounded_timeouts(self, monkeypatch): + _clear_foundry_env(monkeypatch) + _clear_runtime_dependency_env(monkeypatch) + monkeypatch.setenv("REDIS_URL", "redis://localhost:6379/0") + + app = create_standard_app("test-service", SampleServiceAgent) + + hot_memory = app.state.agent.hot_memory + assert isinstance(hot_memory, HotMemory) + assert hot_memory.url == "redis://localhost:6379/0" + assert hot_memory.socket_timeout == 1.0 + assert hot_memory.socket_connect_timeout == 1.0 + assert hot_memory.retry_on_timeout is False + + def test_eventhub_subscribers_default_to_enabled(self, monkeypatch): + _clear_foundry_env(monkeypatch) + _clear_runtime_dependency_env(monkeypatch) + + async def handler(_partition_context, _event): # noqa: ANN001 + return None + + with patch( + "holiday_peak_lib.app_factory.create_eventhub_lifespan", + return_value=None, + ) as create_lifespan: + create_standard_app( + "test-service", + SampleServiceAgent, + subscriptions=[EventHubSubscription("inventory-events", "test-group")], + handlers={"inventory-events": handler}, + ) + + create_lifespan.assert_called_once() + + class TestBuildServiceApp: """Test build_service_app direct-model wiring and endpoints.""" @@ -408,6 +466,37 @@ def test_app_upgrades_explicit_azure_redis_url_with_key_vault_secret(self, monke "redis-primary-key", ) + def test_app_detaches_authless_azure_redis_when_key_vault_secret_fails(self, monkeypatch): + _clear_foundry_env(monkeypatch) + hot_memory = HotMemory("rediss://myredis.redis.cache.windows.net:6380/0") + memory_settings = MemorySettings( + _env_file=None, + redis_url="rediss://myredis.redis.cache.windows.net:6380/0", + key_vault_uri="https://test-kv.vault.azure.net/", + redis_password_secret_name="redis-primary-key", + ) + + with patch( + "holiday_peak_lib.app_factory._fetch_key_vault_secret", + new=AsyncMock(side_effect=PermissionError("rbac denied")), + ) as mock_fetch: + app = build_service_app( + service_name="test-service", + agent_class=SampleServiceAgent, + hot_memory=hot_memory, + memory_settings=memory_settings, + ) + + with TestClient(app): + pass + + assert app.state.agent.hot_memory is None + assert hot_memory.client is None + mock_fetch.assert_awaited_once_with( + "https://test-kv.vault.azure.net/", + "redis-primary-key", + ) + class TestAzureTracingGuard: """Tests for the AZURE_TRACING_ENABLED env-var guard.""" diff --git a/lib/tests/test_app_factory_foundry_lifecycle.py b/lib/tests/test_app_factory_foundry_lifecycle.py index d5e24c1a1..2b5ffae8d 100644 --- a/lib/tests/test_app_factory_foundry_lifecycle.py +++ b/lib/tests/test_app_factory_foundry_lifecycle.py @@ -79,6 +79,19 @@ def test_build_foundry_config_name_only_stays_metadata(monkeypatch): assert cfg.runtime_agent_id is None +def test_build_foundry_config_accepts_hosted_safe_agent_id_alias(monkeypatch): + monkeypatch.setenv("PROJECT_ENDPOINT", TEST_PROJECT_ENDPOINT) + monkeypatch.delenv("FOUNDRY_AGENT_ID_FAST", raising=False) + monkeypatch.setenv("HPH_AGENT_ID_FAST", "hosted-safe-fast") + monkeypatch.setenv("MODEL_DEPLOYMENT_NAME_FAST", "gpt-fast") + + cfg = build_foundry_config("FOUNDRY_AGENT_ID_FAST", "MODEL_DEPLOYMENT_NAME_FAST") + + assert cfg is not None + assert cfg.agent_id == "hosted-safe-fast" + assert cfg.deployment_name == "gpt-fast" + + def test_strict_foundry_mode_flag(monkeypatch): monkeypatch.setenv("FOUNDRY_STRICT_ENFORCEMENT", "true") assert strict_foundry_mode_enabled() is True diff --git a/memories/repo/foundry-hosted-fastapi-mount.md b/memories/repo/foundry-hosted-fastapi-mount.md index 1d9df6230..c03ba2d27 100644 --- a/memories/repo/foundry-hosted-fastapi-mount.md +++ b/memories/repo/foundry-hosted-fastapi-mount.md @@ -1,61 +1,11 @@ -# Foundry Hosted Agent (V3) FastAPI Mount — Pilot Notes +# AKS-Hosted Responses Adapter Notes -**Status**: Pilot landed locally on `inventory-health-check` (2026-05-12). Not deployed yet. +**Status**: The Foundry-managed hosted-agent pilot notes were superseded by the AKS-hosted correction. -## What was built +- Product path for `inventory-health-check`: same AKS pod, same FastAPI app, same process/port as `/health`, `/ready`, `/mcp/*`, and `/invoke`. +- The Responses protocol adapter may use `agent-framework-foundry-hosting` only as an in-process `ResponsesHostServer` mount. It is not a second entry point and not Foundry-managed compute. +- Do not add `agent.hosted.yaml`, `agent.manifest.yaml`, `template.kind: hosted`, `AIProjectClient.agents.create_version`, `UVICORN_PORT=8088`, `PORT=8088`, or `HOLIDAY_PEAK_FOUNDRY_HOSTED` as a product deployment path. +- Do not disable product parity for the Responses path: Redis, Cosmos DB, Blob Storage, Event Hubs, Key Vault, and Application Insights wiring stay the same as the AKS `/invoke` path. +- Canonical files: `lib/src/holiday_peak_lib/agents/hosted.py` exposes `mount_responses_adapter`; `BaseRetailAgent.serve_responses()` is the primary API. The legacy `mount_hosted_agent`, `serve_hosted`, and `hosted_request_from_text` names are compatibility aliases only. +- `apps/inventory-health-check/agent.yaml` remains the portal-tracking/direct-model artifact and may declare `responses 1.0.0` for the AKS-hosted adapter. -1. **`lib/src/holiday_peak_lib/agents/hosted.py`** — new module - - `mount_hosted_agent(fastapi_app, agent, *, prefix="/v1", request_translator=None)` — lazy-imports `agent_framework_foundry_hosting.ResponsesHostServer`, wraps the agent in `_HostedAgentRunAdapter` (SupportsAgentRun protocol), and `app.mount("/", host_server)`. - - `_HostedAgentRunAdapter.run(messages, *, stream, session, **kwargs)` translates MAF `Message[]` → `agent.hosted_request_from_text(text)` → `agent.handle(request)` → `AgentResponse(messages=[Message(role="assistant", contents=[text])])`. - - Streaming explicitly raises NotImplementedError (follow-up). - -2. **`lib/src/holiday_peak_lib/agents/base_agent.py`** — additive - - `BaseRetailAgent.serve_hosted(fastapi_app, *, prefix="/v1")` → one-line mount delegating to `mount_hosted_agent`. - - `BaseRetailAgent.hosted_request_from_text(text) -> {"prompt": text}` (overridable per service). - -3. **`apps/inventory-health-check/src/inventory_health_check/agents.py`** — overrides `hosted_request_from_text` to extract SKU via regex (`SKU-XYZ` or `sku ABC` patterns); falls back to a structured `{_no_sku: True}` request that `handle()` recognises and returns a friendly hint instead of error. - -4. **`apps/inventory-health-check/src/inventory_health_check/main.py`** — appends `app.state.agent.serve_hosted(app)` after `create_standard_app(...)`. Gated by env `HOLIDAY_PEAK_FOUNDRY_HOSTED` (default on); falls back gracefully if SDK absent. - -5. **`lib/tests/test_agents_hosted.py`** — 9 unit tests + 1 integration test (skipped when SDK absent, runs when SDK installed). - -## Why this design - -- **Single-process, single-port, single-runtime** — preserves ADR-005 (2026-05-10) dual-runtime guardrail. The forbidden-token test scans only YAML manifests (`agent.yaml`, `.foundry/agent-metadata.yaml`), and the literal `ResponsesHostServer` import lives only in `lib/`. Service code calls `agent.serve_hosted(app)` which doesn't trip the regex. -- **Optional dep** — `agent-framework-foundry-hosting` is NOT in lib's `pyproject.toml`. Lazy import + clear ImportError. Services without the SDK keep working unchanged. -- **Route ordering** — direct routes (`/health`, `/ready`, `/mcp/*`) registered first, mount appended last. Starlette walks routes in order so direct routes always win, mount catches `/v1/*`. - -## Verification (local) - -- `lib/tests/test_agents_hosted.py`: 9 passed, 1 skipped. -- Full lib + pilot regression: 1328 passed. -- Forbidden-token guardrail (`tests/ops/test_foundry_portal_tracking_manifests.py`): 27/27 passed (manifest contracts unaffected). -- `.tmp/probe_serve_hosted.py` (real `ResponsesHostServer`, real `BaseRetailAgent` subclass): POST `/v1/responses` → 200 in 59.8ms, `agent.handle()` invoked with `{"prompt": "tell me a joke"}`, output text `"handled: tell me a joke"`. - -## What remains (next PRs) - -1. **Container build** — `apps/inventory-health-check/src/Dockerfile` and `pyproject.toml` need `agent-framework-foundry-hosting` added (preview channel `--pre`). -2. **AKS deployment** — rebuild image, push to ACR `holidaypeakhubdevacr.azurecr.io`, bump Flux image tag. -3. **Foundry deployment** — separately, run `azd ai agent deploy` so Foundry creates the per-agent Entra ID + dedicated `{project_endpoint}/agents/inventory-health-check/endpoint/protocols/openai/v1/responses` URL. The container image deployed there is the SAME image as AKS (single-process pattern) — Foundry just probes `/readiness` (built into ResponsesHostServer) and routes to `/v1/responses`. -4. **`apps/inventory-health-check/agent.yaml`** — re-shape for V3 hosted runtime: `template.kind` from `direct-model` to `hosted-agent` (or whatever the V3 spec uses), `template.protocols` add `responses 1.0.0`. Keep `metadata.trackingOnly: false` once truly portal-indexed. -5. **ADR-005 amendment** — narrow guardrail wording from "no `ResponsesHostServer`" to "no two runtimes per service" (the mount pattern is the compliant alternative). -6. **Test policy update** — `FORBIDDEN_DUAL_RUNTIME_TOKENS` in `tests/ops/test_foundry_portal_tracking_manifests.py` should drop `"ResponsesHostServer"` from the list once the ADR amendment lands. Keep `"hosted_main.py"`, `"entrypoint.sh"`, `"8088"`, `"second runtime"` as the actual dual-runtime markers. -7. **Streaming** — `_HostedAgentRunAdapter.run(stream=True)` currently raises NotImplementedError. Wire to `BaseRetailAgent.invoke_model_stream` to enable SSE responses on the hosted endpoint. - -## Investigative artifacts (under `.tmp/`) - -- `.tmp/hosted-probe-venv/` — fresh Python 3.13 venv with `agent-framework-foundry-hosting==1.0.0a260507`, `agent-framework==1.3.0`, `azure-ai-agentserver-responses==1.0.0bX`, `fastapi`, `uvicorn`, and `holiday-peak-lib` (editable install). -- `.tmp/probe_mount.py` — initial mount probe with hand-rolled stub agent (proved `ResponsesHostServer` IS Starlette and mountable). -- `.tmp/probe_serve_hosted.py` — second probe using the real lib helper end-to-end. - -Both probes can be re-run any time: -```powershell -.\.tmp\hosted-probe-venv\Scripts\python.exe .\.tmp\probe_serve_hosted.py -``` - -## Key surface facts (for next session) - -- `azure.ai.agentserver.responses.hosting.ResponsesAgentServerHost` MRO: `[ResponsesAgentServerHost, AgentServerHost, Starlette, object]`. -- `ResponsesHostServer(agent, *, prefix="", options=None, store=None)` — registers routes `/{prefix}/responses` (POST/GET/DELETE), `/{prefix}/responses/{id}/cancel`, `/{prefix}/responses/{id}/input_items`, plus a server-managed `/readiness`. -- The auto-FoundryStorageProvider activation is gated by `AgentConfig.from_env().is_hosted` (env-driven). In-process / AKS path uses `InMemoryResponseProvider` automatically. -- Lifespan handler is observational only — safe to mount inside FastAPI even though Starlette's mount semantics drop sub-app lifespans. diff --git a/memories/session/foundry-v3-pilot-status.md b/memories/session/foundry-v3-pilot-status.md new file mode 100644 index 000000000..322851860 --- /dev/null +++ b/memories/session/foundry-v3-pilot-status.md @@ -0,0 +1,9 @@ +# Foundry V3 hosted-agents pilot — SUPERSEDED (2026-05-18) + +> Superseded by the AKS-hosted Responses adapter correction. The product path for `inventory-health-check` is the same AKS pod, same FastAPI app, same process/port as `/health`, `/ready`, `/mcp/*`, and `/invoke`. Do not use `agent.hosted.yaml`, Foundry-managed hosted compute, or `AIProjectClient.agents.create_version` as the product path. + +## Current Target + +- Keep hosted-agent protocol behavior on AKS via the in-process Responses adapter. +- Keep product dependency parity for the Responses path: Redis, Cosmos DB, Blob Storage, Event Hubs, Key Vault, and Application Insights wiring remain the AKS service wiring. +- Treat the old v20/v24 Foundry-managed validations as historical evidence only; they are not accepted deployment evidence for PR #1103 / issue #1107. diff --git a/scripts/ci/regen_service_deploy_entrypoints.py b/scripts/ci/regen_service_deploy_entrypoints.py index 5435a25c5..eb12c59f2 100644 --- a/scripts/ci/regen_service_deploy_entrypoints.py +++ b/scripts/ci/regen_service_deploy_entrypoints.py @@ -6,8 +6,9 @@ calls deploy-azd.yml directly with the proven contract used by deploy-azd-dev.yml: -- githubEnvironment: dev (so jobs bind to the existing 'dev' GitHub - Environment that has the required secrets and branch protection). +- githubEnvironment: branch (so feature-branch service previews avoid the + protected 'dev' selected-branch restriction while still using the approved + environment-scoped OIDC subject). - environment: dev (azd env name). - skipProvision: true (per-service deploys never re-run provision). - deployStatic/uiOnly: false (per-service entrypoints are backend-only). @@ -114,7 +115,7 @@ uses: ./.github/workflows/deploy-azd.yml with: environment: dev - githubEnvironment: dev + githubEnvironment: branch location: ${{{{ github.event_name == 'workflow_dispatch' && inputs.location || 'centralus' }}}} projectName: ${{{{ github.event_name == 'workflow_dispatch' && inputs.projectName || 'holidaypeakhub405' }}}} imageTag: ${{{{ github.event_name == 'workflow_dispatch' && inputs.imageTag || github.sha }}}} diff --git a/scripts/ops/pre_push_gate.py b/scripts/ops/pre_push_gate.py index 1d3cf429b..14e3b782c 100644 --- a/scripts/ops/pre_push_gate.py +++ b/scripts/ops/pre_push_gate.py @@ -8,6 +8,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[2] +PYTHON = sys.executable def run_step(command: list[str], *, title: str) -> None: @@ -31,11 +32,11 @@ def run_pylint_step(command: list[str], *, title: str) -> None: def run_lint_gate() -> None: run_step( - ["python", "-m", "isort", "--check-only", "lib", "apps"], + [PYTHON, "-m", "isort", "--check-only", "lib", "apps"], title="Lint gate: isort", ) run_step( - ["python", "-m", "black", "--check", "lib", "apps"], + [PYTHON, "-m", "black", "--check", "lib", "apps"], title="Lint gate: black", ) @@ -47,7 +48,9 @@ def run_lint_gate() -> None: ) run_pylint_step( [ - "python", "-m", "pylint", + PYTHON, + "-m", + "pylint", "--fail-on=E,F", "--ignore=build", *pylint_targets, @@ -56,7 +59,7 @@ def run_lint_gate() -> None: ) run_step( [ - "python", + PYTHON, "-m", "mypy", "--config-file", @@ -71,7 +74,7 @@ def run_lint_gate() -> None: run_step( [ - "python", + PYTHON, "scripts/ops/check_markdown_links.py", "--roots", "docs/governance", @@ -80,7 +83,7 @@ def run_lint_gate() -> None: title="Lint gate: governance/architecture links", ) run_step( - ["python", "scripts/ops/check_event_schema_contracts.py"], + [PYTHON, "scripts/ops/check_event_schema_contracts.py"], title="Lint gate: canonical event schema contracts", ) @@ -106,7 +109,7 @@ def run_lint_gate() -> None: def run_test_gate() -> None: run_step( - ["pytest", "lib/tests", "--maxfail=1"], + [PYTHON, "-m", "pytest", "lib/tests", "--maxfail=1"], title="Test gate: lib tests", ) @@ -116,7 +119,7 @@ def run_test_gate() -> None: if path.is_dir() and path.name == "tests" ] run_step( - ["pytest", *app_test_dirs, "--ignore=apps/ui/tests"], + [PYTHON, "-m", "pytest", *app_test_dirs, "--ignore=apps/ui/tests"], title="Test gate: app tests (excluding UI tests)", ) diff --git a/scripts/ops/register_foundry_surfaces.py b/scripts/ops/register_foundry_surfaces.py new file mode 100644 index 000000000..f35274bd8 --- /dev/null +++ b/scripts/ops/register_foundry_surfaces.py @@ -0,0 +1,550 @@ +#!/usr/bin/env python3 +"""Plan or apply Microsoft Foundry surface registration for retail agents. + +The script consumes ``apps/foundry-surfaces.yaml`` plus each agent's Hosted +manifest or Custom proxy metadata. Plan mode is deterministic and network-free. +Apply mode creates or updates Hosted Agent versions in Foundry using the Azure +AI Projects SDK; Custom Agent proxy entries are validated and emitted as +metadata-only operations because the current SDK exposes prompt, hosted, and +workflow agent definitions. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import re +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[2] +DEFAULT_REGISTRY_PATH = REPO_ROOT / "apps" / "foundry-surfaces.yaml" +DEFAULT_CPU = "0.5" +DEFAULT_MEMORY = "1Gi" +FORBIDDEN_HOSTED_ENV_PREFIXES = ("FOUNDRY_", "AGENT_") +HOSTED_APPLY_SURFACE = "hosted" +PLACEHOLDER_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") + + +class SurfaceRegistrationError(RuntimeError): + """Raised when a surface registry or manifest cannot be materialized.""" + + +@dataclass(frozen=True) +class RegistrationInputs: + mode: str + environment: str + registry_path: Path + project_endpoint: str | None + project_name: str | None + acr_login_server: str | None + image_tag: str | None + image_map_file: Path | None + apim_base_url: str | None + services: tuple[str, ...] + output: Path | None + model_deployment_fast: str + model_deployment_rich: str + allow_unresolved: bool + + +def _load_yaml(path: Path) -> dict[str, Any]: + if not path.is_file(): + raise SurfaceRegistrationError(f"YAML file not found: {path}") + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + if not isinstance(loaded, dict): + raise SurfaceRegistrationError(f"{path} must contain a YAML mapping") + return loaded + + +def _write_json(path: Path, payload: dict[str, Any]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def _load_image_map(path: Path | None) -> dict[str, str]: + if path is None: + return {} + if not path.is_file(): + raise SurfaceRegistrationError(f"Image map file not found: {path}") + loaded = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(loaded, dict): + raise SurfaceRegistrationError(f"Image map must be a JSON object: {path}") + return {str(service): str(image_ref) for service, image_ref in loaded.items()} + + +def _split_services(raw_services: str | None) -> tuple[str, ...]: + if not raw_services: + return () + return tuple(service.strip() for service in raw_services.split(",") if service.strip()) + + +def _as_mapping(value: Any, label: str) -> dict[str, Any]: + if not isinstance(value, dict): + raise SurfaceRegistrationError(f"{label} must be a mapping") + return value + + +def _as_list(value: Any, label: str) -> list[Any]: + if not isinstance(value, list): + raise SurfaceRegistrationError(f"{label} must be a list") + return value + + +def _stable_sha256(payload: dict[str, Any]) -> str: + encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") + return hashlib.sha256(encoded).hexdigest() + + +def _service_values(inputs: RegistrationInputs, service: str) -> dict[str, str]: + values = {key: value for key, value in os.environ.items() if value != ""} + defaults = { + "PROJECT_ENDPOINT": inputs.project_endpoint, + "PROJECT_NAME": inputs.project_name, + "MODEL_DEPLOYMENT_NAME_FAST": inputs.model_deployment_fast, + "MODEL_DEPLOYMENT_NAME_RICH": inputs.model_deployment_rich, + "HPH_AGENT_ID_FAST": f"{service}-fast", + "HPH_AGENT_ID_RICH": f"{service}-rich", + "APIM_BASE_URL": inputs.apim_base_url, + } + for key, value in defaults.items(): + if value is not None: + values[key] = value + values.setdefault("REDIS_URL", "") + return values + + +def _resolve_template( + raw_value: Any, + values: dict[str, str], + *, + allow_unresolved: bool, +) -> tuple[str, list[str]]: + unresolved: list[str] = [] + raw_text = str(raw_value) + + def replace_placeholder(match: re.Match[str]) -> str: + name = match.group(1) + if name in values: + return values[name] + unresolved.append(name) + if allow_unresolved: + return match.group(0) + return "" + + resolved = PLACEHOLDER_RE.sub(replace_placeholder, raw_text) + if unresolved and not allow_unresolved: + joined = ", ".join(sorted(set(unresolved))) + raise SurfaceRegistrationError(f"Unresolved required values: {joined}") + return resolved, sorted(set(unresolved)) + + +def _registry_services(registry: dict[str, Any], surface_name: str) -> list[str]: + surfaces = _as_mapping(registry.get("surfaces"), "registry.surfaces") + surface = _as_mapping(surfaces.get(surface_name), f"registry.surfaces.{surface_name}") + agents = _as_list(surface.get("agents"), f"registry.surfaces.{surface_name}.agents") + return [str(agent) for agent in agents] + + +def _requested_services(registry: dict[str, Any], inputs: RegistrationInputs) -> set[str]: + all_services = set(_registry_services(registry, "hosted")) | set( + _registry_services(registry, "custom") + ) + if not inputs.services: + return all_services + + requested = set(inputs.services) + unknown = requested - all_services + if unknown: + joined = ", ".join(sorted(unknown)) + raise SurfaceRegistrationError(f"Requested service is not in Foundry registry: {joined}") + return requested + + +def _image_ref_for_service( + service: str, + image_map: dict[str, str], + inputs: RegistrationInputs, +) -> str: + mapped = image_map.get(service) + if mapped: + return mapped + if inputs.acr_login_server and inputs.image_tag: + return f"{inputs.acr_login_server.rstrip('/')}/{service}:{inputs.image_tag}" + raise SurfaceRegistrationError( + "Hosted surfaces require either --image-map-file or both " + f"--acr-login-server and --image-tag (service={service})." + ) + + +def _materialize_hosted_env( + service: str, + manifest: dict[str, Any], + inputs: RegistrationInputs, +) -> tuple[dict[str, str], list[str]]: + template = _as_mapping(manifest.get("template"), f"{service}.template") + env_entries = _as_list( + template.get("environment_variables"), f"{service}.template.environment_variables" + ) + values = _service_values(inputs, service) + resolved_env: dict[str, str] = {} + unresolved: list[str] = [] + + for entry in env_entries: + env_entry = _as_mapping(entry, f"{service}.template.environment_variables[]") + name = str(env_entry.get("name") or "") + if not name: + raise SurfaceRegistrationError(f"Hosted env var without name for service={service}") + if name.startswith(FORBIDDEN_HOSTED_ENV_PREFIXES): + raise SurfaceRegistrationError( + f"Hosted env var uses reserved Foundry prefix: {service} {name}" + ) + value, missing = _resolve_template( + env_entry.get("value", ""), values, allow_unresolved=inputs.allow_unresolved + ) + resolved_env[name] = value + unresolved.extend(missing) + + return resolved_env, sorted(set(unresolved)) + + +def _hosted_protocols(service: str, manifest: dict[str, Any]) -> list[dict[str, str]]: + template = _as_mapping(manifest.get("template"), f"{service}.template") + protocols = _as_list(template.get("protocols"), f"{service}.template.protocols") + records: list[dict[str, str]] = [] + for entry in protocols: + protocol_entry = _as_mapping(entry, f"{service}.template.protocols[]") + protocol = str(protocol_entry.get("protocol") or "") + version = str(protocol_entry.get("version") or "") + if not protocol or not version: + raise SurfaceRegistrationError(f"Hosted protocol record is incomplete: {service}") + records.append({"protocol": protocol, "version": version}) + return records + + +def _hosted_operation( + service: str, + inputs: RegistrationInputs, + image_map: dict[str, str], +) -> dict[str, Any]: + manifest_path = REPO_ROOT / "apps" / service / "agent.hosted.yaml" + manifest = _load_yaml(manifest_path) + template = _as_mapping(manifest.get("template"), f"{service}.template") + metadata = _as_mapping(manifest.get("metadata"), f"{service}.metadata") + surface = _as_mapping(metadata.get("surface"), f"{service}.metadata.surface") + if surface.get("type") != HOSTED_APPLY_SURFACE: + raise SurfaceRegistrationError(f"Hosted manifest has wrong surface type: {service}") + if surface.get("replacesProductRuntime") is not False: + raise SurfaceRegistrationError(f"Hosted manifest must not replace AKS runtime: {service}") + + env_vars, unresolved = _materialize_hosted_env(service, manifest, inputs) + image_ref = _image_ref_for_service(service, image_map, inputs) + agent_name = str(manifest.get("name") or service) + agent_definition = { + "kind": "hosted", + "image": image_ref, + "cpu": str(template.get("cpu") or DEFAULT_CPU), + "memory": str(template.get("memory") or DEFAULT_MEMORY), + "container_protocol_versions": _hosted_protocols(service, manifest), + "environment_variables": env_vars, + } + definition_sha256 = _stable_sha256(agent_definition) + operation_metadata = { + "hphService": service, + "hphSurfaceType": "hosted", + "hphProductRuntime": str(surface.get("productRuntime") or "aks"), + "hphDefinitionSha256": definition_sha256, + "hphSourceIssue": str(manifest.get("sourceIssue") or metadata.get("sourceIssue") or "990"), + } + project_endpoint = (inputs.project_endpoint or "${PROJECT_ENDPOINT}").rstrip("/") + return { + "service": service, + "surface": "hosted", + "action": "create_or_update_hosted_version", + "applySupported": True, + "agentName": agent_name, + "manifestPath": manifest_path.relative_to(REPO_ROOT).as_posix(), + "agentEndpoint": ( + f"{project_endpoint}/agents/{agent_name}/endpoint/protocols/openai/v1/responses" + ), + "agentDefinition": agent_definition, + "metadata": operation_metadata, + "description": str(manifest.get("description") or "").strip(), + "definitionSha256": definition_sha256, + "unresolvedVariables": unresolved, + "costImplication": "Creates Foundry Hosted Agent active-session CPU/memory billing.", + } + + +def _custom_operation(service: str, inputs: RegistrationInputs) -> dict[str, Any]: + metadata_path = REPO_ROOT / "apps" / service / ".foundry" / "agent-metadata.yaml" + metadata = _load_yaml(metadata_path) + surface = _as_mapping(metadata.get("surface"), f"{service}.surface") + proxy = _as_mapping(surface.get("proxy"), f"{service}.surface.proxy") + environments = _as_mapping(metadata.get("environments"), f"{service}.environments") + environment_name = str(metadata.get("defaultEnvironment") or inputs.environment) + environment = _as_mapping( + environments.get(environment_name), f"{service}.environments.{environment_name}" + ) + values = _service_values(inputs, service) + endpoint, unresolved = _resolve_template( + proxy.get("endpoint", ""), values, allow_unresolved=inputs.allow_unresolved + ) + agent_name = str(environment.get("agentName") or service) + + if surface.get("foundryManagedCompute") is not False: + raise SurfaceRegistrationError(f"Custom surface must not create Foundry compute: {service}") + + return { + "service": service, + "surface": "custom", + "action": "validate_custom_proxy_metadata", + "applySupported": False, + "agentName": agent_name, + "metadataPath": metadata_path.relative_to(REPO_ROOT).as_posix(), + "customProxyDefinition": { + "kind": "custom-proxy", + "endpoint": endpoint, + "target": str(proxy.get("target") or "existing-aks-apim-endpoint"), + "productRuntime": str(surface.get("productRuntime") or "aks"), + "productTrafficPath": str(surface.get("productTrafficPath") or "APIM -> AGC -> AKS"), + "foundryManagedCompute": False, + }, + "unresolvedVariables": unresolved, + "skipReason": ( + "Azure AI Projects SDK 2.x supports prompt, hosted, and workflow " + "agent definitions; this repository records Custom Agent surfaces " + "as APIM proxy metadata with no Foundry-managed compute." + ), + } + + +def build_registration_plan(inputs: RegistrationInputs) -> dict[str, Any]: + registry = _load_yaml(inputs.registry_path) + image_map = _load_image_map(inputs.image_map_file) + requested = _requested_services(registry, inputs) + operations: list[dict[str, Any]] = [] + + for service in _registry_services(registry, "hosted"): + if service in requested: + operations.append(_hosted_operation(service, inputs, image_map)) + + for service in _registry_services(registry, "custom"): + if service in requested: + operations.append(_custom_operation(service, inputs)) + + unresolved_operations = [ + operation + for operation in operations + if operation.get("unresolvedVariables") + ] + if unresolved_operations and inputs.mode == "apply": + details = "; ".join( + f"{operation['service']}={','.join(operation['unresolvedVariables'])}" + for operation in unresolved_operations + ) + raise SurfaceRegistrationError(f"Apply mode has unresolved variables: {details}") + + hosted_count = sum(1 for operation in operations if operation["surface"] == "hosted") + custom_count = sum(1 for operation in operations if operation["surface"] == "custom") + return { + "schemaVersion": "1.0", + "mode": inputs.mode, + "environment": inputs.environment, + "projectEndpoint": inputs.project_endpoint or "${PROJECT_ENDPOINT}", + "sourceRegistry": inputs.registry_path.relative_to(REPO_ROOT).as_posix(), + "policy": registry.get("policy", {}), + "counts": { + "hosted": hosted_count, + "custom": custom_count, + "total": len(operations), + }, + "operations": operations, + } + + +def _version_metadata(version: object) -> dict[str, str]: + if isinstance(version, dict): + metadata = version.get("metadata") + else: + metadata = getattr(version, "metadata", None) + if not isinstance(metadata, dict): + return {} + return {str(key): str(value) for key, value in metadata.items()} + + +def _latest_definition_hash(agents_client: object, agent_name: str) -> str | None: + list_versions = getattr(agents_client, "list_versions", None) + if not callable(list_versions): + return None + versions = list(list_versions(agent_name=agent_name)) + if not versions: + return None + return _version_metadata(versions[0]).get("hphDefinitionSha256") + + +def _hosted_definition_model(agent_definition: dict[str, Any]) -> object: + try: + from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord + except ImportError as exc: # pragma: no cover - import-time failure in CI env only + raise SurfaceRegistrationError( + "Apply mode requires azure-ai-projects with HostedAgentDefinition support." + ) from exc + + protocol_records = [ + ProtocolVersionRecord(protocol=entry["protocol"], version=entry["version"]) + for entry in agent_definition["container_protocol_versions"] + ] + return HostedAgentDefinition( + image=agent_definition["image"], + cpu=agent_definition["cpu"], + memory=agent_definition["memory"], + container_protocol_versions=protocol_records, + environment_variables=agent_definition["environment_variables"], + ) + + +def apply_registration_plan(plan: dict[str, Any], inputs: RegistrationInputs) -> list[dict[str, str]]: + if not inputs.project_endpoint: + raise SurfaceRegistrationError("Apply mode requires --project-endpoint or PROJECT_ENDPOINT.") + + try: + from azure.ai.projects import AIProjectClient + from azure.identity import DefaultAzureCredential + except ImportError as exc: # pragma: no cover - import-time failure in CI env only + raise SurfaceRegistrationError( + "Apply mode requires azure-ai-projects and azure-identity." + ) from exc + + credential = DefaultAzureCredential() + client_kwargs: dict[str, object] = { + "endpoint": inputs.project_endpoint, + "credential": credential, + "allow_preview": True, + } + if inputs.project_name: + client_kwargs["project_name"] = inputs.project_name + + results: list[dict[str, str]] = [] + with AIProjectClient(**client_kwargs) as client: # type: ignore[arg-type] + agents_client = client.agents + for operation in plan["operations"]: + if operation["surface"] != HOSTED_APPLY_SURFACE: + results.append( + { + "service": operation["service"], + "surface": operation["surface"], + "status": "metadata-only", + } + ) + continue + + agent_name = operation["agentName"] + desired_hash = operation["definitionSha256"] + if _latest_definition_hash(agents_client, agent_name) == desired_hash: + results.append( + {"service": operation["service"], "surface": "hosted", "status": "unchanged"} + ) + continue + + definition = _hosted_definition_model(operation["agentDefinition"]) + agents_client.create_version( + agent_name=agent_name, + definition=definition, + metadata=operation["metadata"], + description=operation.get("description") or None, + ) + results.append( + { + "service": operation["service"], + "surface": "hosted", + "status": "created-or-updated", + } + ) + return results + + +def _inputs_from_args(args: argparse.Namespace) -> RegistrationInputs: + mode = str(args.mode).lower() + allow_unresolved = bool(args.allow_unresolved or mode == "plan") + return RegistrationInputs( + mode=mode, + environment=args.environment, + registry_path=Path(args.registry).resolve(), + project_endpoint=args.project_endpoint or os.getenv("PROJECT_ENDPOINT"), + project_name=args.project_name or os.getenv("PROJECT_NAME"), + acr_login_server=args.acr_login_server or os.getenv("AZURE_CONTAINER_REGISTRY"), + image_tag=args.image_tag + or os.getenv("DEPLOY_SOURCE_SHA") + or os.getenv("IMAGE_TAG") + or os.getenv("GITHUB_SHA"), + image_map_file=Path(args.image_map_file).resolve() if args.image_map_file else None, + apim_base_url=args.apim_base_url or os.getenv("APIM_BASE_URL"), + services=_split_services(args.services), + output=Path(args.output).resolve() if args.output else None, + model_deployment_fast=args.model_deployment_fast, + model_deployment_rich=args.model_deployment_rich, + allow_unresolved=allow_unresolved, + ) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--mode", choices=("plan", "apply"), default="plan") + parser.add_argument("--environment", default=os.getenv("AZURE_ENV_NAME", "dev")) + parser.add_argument("--registry", default=str(DEFAULT_REGISTRY_PATH)) + parser.add_argument("--project-endpoint", default=None) + parser.add_argument("--project-name", default=None) + parser.add_argument("--acr-login-server", default=None) + parser.add_argument("--image-tag", default=None) + parser.add_argument("--image-map-file", default=None) + parser.add_argument("--apim-base-url", default=None) + parser.add_argument("--services", default="") + parser.add_argument("--output", default=None) + parser.add_argument("--model-deployment-fast", default="gpt-5-nano") + parser.add_argument("--model-deployment-rich", default="gpt-5") + parser.add_argument( + "--allow-unresolved", + action="store_true", + help="Keep unresolved ${VAR} placeholders in the emitted plan.", + ) + return parser + + +def main(argv: list[str] | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + + try: + inputs = _inputs_from_args(args) + plan = build_registration_plan(inputs) + if inputs.output: + _write_json(inputs.output, plan) + print( + "[foundry-surfaces] " + f"mode={inputs.mode} hosted={plan['counts']['hosted']} " + f"custom={plan['counts']['custom']} total={plan['counts']['total']}" + ) + + if inputs.mode == "apply": + results = apply_registration_plan(plan, inputs) + for result in results: + print( + "[foundry-surfaces] " + f"service={result['service']} surface={result['surface']} " + f"status={result['status']}" + ) + return 0 + except SurfaceRegistrationError as exc: + print(f"[foundry-surfaces] ERROR: {exc}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": # pragma: no cover + sys.exit(main()) \ No newline at end of file diff --git a/tests/ops/test_ci_uv_prerelease_policy.py b/tests/ops/test_ci_uv_prerelease_policy.py new file mode 100644 index 000000000..95ecf956a --- /dev/null +++ b/tests/ops/test_ci_uv_prerelease_policy.py @@ -0,0 +1,23 @@ +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[2] + + +def test_lint_workflow_checks_app_locks_with_preview_policy() -> None: + workflow = (REPO_ROOT / ".github" / "workflows" / "lint.yml").read_text( + encoding="utf-8" + ) + + assert "grep -q 'prerelease-mode = \"allow\"' uv.lock" in workflow + assert "uv lock --prerelease=allow --check" in workflow + assert "uv lock --check" in workflow + + +def test_python_app_installs_allow_preview_dependency_chain() -> None: + for workflow_name in ("test.yml", "lint.yml", "dependency-audit.yml"): + workflow = (REPO_ROOT / ".github" / "workflows" / workflow_name).read_text( + encoding="utf-8" + ) + + assert "uv pip install --system --prerelease=allow -e \"$d\"" in workflow \ No newline at end of file diff --git a/tests/ops/test_foundry_portal_tracking_manifests.py b/tests/ops/test_foundry_portal_tracking_manifests.py index 88c430207..87ef2849f 100644 --- a/tests/ops/test_foundry_portal_tracking_manifests.py +++ b/tests/ops/test_foundry_portal_tracking_manifests.py @@ -10,8 +10,40 @@ REPO_ROOT = Path(__file__).resolve().parents[2] APPS_README = REPO_ROOT / "apps" / "README.md" +FOUNDRY_SURFACE_REGISTRY = REPO_ROOT / "apps" / "foundry-surfaces.yaml" AGENT_ROW_RE = re.compile(r"^\| `([^`]+)` \| Agent service \| ([^|]+) \|$") +EXPECTED_HOSTED_SURFACE_AGENTS = { + "crm-support-assistance", + "ecommerce-cart-intelligence", + "ecommerce-catalog-search", + "ecommerce-checkout-support", + "ecommerce-order-status", + "ecommerce-product-detail-enrichment", + "inventory-health-check", + "logistics-eta-computation", + "logistics-returns-support", + "truth-hitl", +} +EXPECTED_CUSTOM_SURFACE_AGENTS = { + "crm-campaign-intelligence", + "crm-profile-aggregation", + "crm-segmentation-personalization", + "inventory-alerts-triggers", + "inventory-jit-replenishment", + "inventory-reservation-validation", + "logistics-carrier-selection", + "logistics-route-issue-detection", + "product-management-acp-transformation", + "product-management-assortment-optimization", + "product-management-consistency-validation", + "product-management-normalization-classification", + "search-enrichment-agent", + "truth-enrichment", + "truth-export", + "truth-ingestion", +} + REQUIRED_AGENT_ENV_VARS = { "PROJECT_ENDPOINT", "PROJECT_NAME", @@ -35,13 +67,27 @@ "purpose": "portal-tracking", "stage": "seed", } -FORBIDDEN_DUAL_RUNTIME_TOKENS = ( +FORBIDDEN_TRACKING_MANIFEST_TOKENS = ( "hosted_main.py", - "ResponsesHostServer", "entrypoint.sh", - "8088", "second runtime", ) +HOSTED_SURFACE_MANIFEST_NAMES = ("agent.hosted.yaml", "agent.manifest.yaml") +BASE_DIRECT_MODEL_PROTOCOLS = {"fastapi-rest": "1.0.0", "mcp": "1.0.0"} +REQUIRED_HOSTED_ENV_VARS = { + "HPH_AGENT_ID_FAST", + "HPH_AGENT_ID_RICH", + "PROJECT_ENDPOINT", + "PROJECT_NAME", + "MODEL_DEPLOYMENT_NAME_FAST", + "MODEL_DEPLOYMENT_NAME_RICH", + "HOLIDAY_PEAK_FOUNDRY_HOSTED", + "UVICORN_PORT", +} +REQUIRED_HOSTED_DEPENDENCY = "agent-framework-foundry-hosting==1.0.0a260507" +REQUIRED_HOSTED_DEPENDENCY_NAME = "agent-framework-foundry-hosting" +REQUIRED_HOSTED_DEPENDENCY_SPECIFIER = "==1.0.0a260507" +FORBIDDEN_HOSTED_ENV_PREFIXES = ("FOUNDRY_", "AGENT_") # No GoF pattern applies: this is a deterministic metadata contract test. @@ -60,10 +106,125 @@ def _load_yaml(path: Path) -> dict[str, object]: return data +def _surface_registry() -> dict[str, object]: + return _load_yaml(FOUNDRY_SURFACE_REGISTRY) + + +def _surface_agents(surface_name: str) -> set[str]: + registry = _surface_registry() + surfaces = registry["surfaces"] + assert isinstance(surfaces, dict) + surface = surfaces[surface_name] + assert isinstance(surface, dict) + agents = surface["agents"] + assert isinstance(agents, list) + return {str(agent) for agent in agents} + + def test_apps_readme_declares_twenty_six_active_agents() -> None: assert len(_agent_services()) == 26 +def test_foundry_surface_registry_matches_two_track_policy() -> None: + registry = _surface_registry() + policy = registry["policy"] + assert isinstance(policy, dict) + assert policy["productRuntime"] == "aks" + assert policy["productTrafficPath"] == "APIM -> AGC -> AKS" + assert policy["hostedManifest"] == "agent.hosted.yaml" + assert policy["customMetadataPath"] == ".foundry/agent-metadata.yaml" + + hosted_agents = _surface_agents("hosted") + custom_agents = _surface_agents("custom") + + assert hosted_agents == EXPECTED_HOSTED_SURFACE_AGENTS + assert custom_agents == EXPECTED_CUSTOM_SURFACE_AGENTS + assert hosted_agents.isdisjoint(custom_agents) + assert hosted_agents | custom_agents == set(_agent_services()) + + +@pytest.mark.parametrize("service", sorted(EXPECTED_HOSTED_SURFACE_AGENTS)) +def test_hosted_surface_agents_have_foundry_hosted_manifest(service: str) -> None: + app_dir = REPO_ROOT / "apps" / service + manifest_path = app_dir / "agent.hosted.yaml" + + assert manifest_path.is_file() + manifest_text = manifest_path.read_text(encoding="utf-8") + assert manifest_text.isascii() + for token in FORBIDDEN_TRACKING_MANIFEST_TOKENS: + assert token not in manifest_text + + manifest = _load_yaml(manifest_path) + assert manifest["name"] == service + + metadata = manifest["metadata"] + assert isinstance(metadata, dict) + surface = metadata["surface"] + assert isinstance(surface, dict) + assert surface["type"] == "hosted" + assert surface["classification"] == "Hosted Agent" + assert surface["audience"] == "public-human-facing" + assert surface["productRuntime"] == "aks" + assert surface["productTrafficPath"] == "APIM -> AGC -> AKS" + assert surface["replacesProductRuntime"] is False + + template = manifest["template"] + assert isinstance(template, dict) + assert template["kind"] == "hosted" + assert ".main:app" in template["startupCommand"] + assert "--port 8088" in template["startupCommand"] + protocols = {entry["protocol"]: entry["version"] for entry in template["protocols"]} + assert protocols["responses"] == "1.0.0" + + env_vars = {entry["name"]: str(entry["value"]) for entry in template["environment_variables"]} + assert REQUIRED_HOSTED_ENV_VARS <= env_vars.keys() + assert env_vars["HOLIDAY_PEAK_FOUNDRY_HOSTED"] == "1" + assert env_vars["UVICORN_PORT"] == "8088" + for name in env_vars: + assert not name.startswith(FORBIDDEN_HOSTED_ENV_PREFIXES) + + +@pytest.mark.parametrize("service", sorted(EXPECTED_HOSTED_SURFACE_AGENTS)) +def test_hosted_surface_agents_include_responses_hosting_dependency(service: str) -> None: + app_dir = REPO_ROOT / "apps" / service / "src" + pyproject_path = app_dir / "pyproject.toml" + requirements_path = app_dir / "requirements.txt" + lock_path = app_dir / "uv.lock" + + assert pyproject_path.is_file() + assert requirements_path.is_file() + assert lock_path.is_file() + assert REQUIRED_HOSTED_DEPENDENCY in pyproject_path.read_text(encoding="utf-8") + assert REQUIRED_HOSTED_DEPENDENCY in requirements_path.read_text(encoding="utf-8") + lock_text = lock_path.read_text(encoding="utf-8") + assert f'name = "{REQUIRED_HOSTED_DEPENDENCY_NAME}"' in lock_text + assert ( + f'{{ name = "{REQUIRED_HOSTED_DEPENDENCY_NAME}", ' + f'specifier = "{REQUIRED_HOSTED_DEPENDENCY_SPECIFIER}" }}' + ) in lock_text + + +@pytest.mark.parametrize("service", sorted(EXPECTED_CUSTOM_SURFACE_AGENTS)) +def test_custom_surface_agents_proxy_existing_aks_apim_endpoint(service: str) -> None: + app_dir = REPO_ROOT / "apps" / service + for manifest_name in HOSTED_SURFACE_MANIFEST_NAMES: + assert not (app_dir / manifest_name).exists() + + metadata = _load_yaml(app_dir / ".foundry" / "agent-metadata.yaml") + surface = metadata["surface"] + assert isinstance(surface, dict) + assert surface["type"] == "custom" + assert surface["classification"] == "Custom Agent" + assert surface["audience"] == "non-public-internal" + assert surface["productRuntime"] == "aks" + assert surface["productTrafficPath"] == "APIM -> AGC -> AKS" + assert surface["foundryManagedCompute"] is False + proxy = surface["proxy"] + assert isinstance(proxy, dict) + assert proxy["target"] == "existing-aks-apim-endpoint" + assert proxy["endpoint"] == f"${{APIM_BASE_URL}}/agents/{service}" + + @pytest.mark.parametrize("service,purpose", sorted(_agent_services().items())) def test_direct_model_agent_has_foundry_portal_tracking_manifests( service: str, purpose: str @@ -80,7 +241,7 @@ def test_direct_model_agent_has_foundry_portal_tracking_manifests( assert agent_text.isascii() assert metadata_text.isascii() assert "priority:" not in metadata_text - for token in FORBIDDEN_DUAL_RUNTIME_TOKENS: + for token in FORBIDDEN_TRACKING_MANIFEST_TOKENS: assert token not in agent_text assert token not in metadata_text @@ -99,7 +260,11 @@ def test_direct_model_agent_has_foundry_portal_tracking_manifests( assert isinstance(template, dict) assert template["kind"] == "direct-model" protocols = {entry["protocol"]: entry["version"] for entry in template["protocols"]} - assert protocols == {"fastapi-rest": "1.0.0", "mcp": "1.0.0"} + assert BASE_DIRECT_MODEL_PROTOCOLS.items() <= protocols.items() + if service == "inventory-health-check": + assert protocols["responses"] == "1.0.0" + else: + assert protocols == BASE_DIRECT_MODEL_PROTOCOLS env_vars = {entry["name"]: entry["value"] for entry in template["environment_variables"]} assert REQUIRED_AGENT_ENV_VARS <= env_vars.keys() @@ -133,3 +298,47 @@ def test_direct_model_agent_has_foundry_portal_tracking_manifests( tags = evaluation_suite["tags"] assert isinstance(tags, dict) assert REQUIRED_EVALUATION_SUITE_TAGS.items() <= tags.items() + + +def test_inventory_health_check_tracking_manifest_stays_direct_model() -> None: + app_dir = REPO_ROOT / "apps" / "inventory-health-check" + agent_manifest = _load_yaml(app_dir / "agent.yaml") + + metadata = agent_manifest["metadata"] + assert isinstance(metadata, dict) + assert metadata["trackingOnly"] is True + + template = agent_manifest["template"] + assert isinstance(template, dict) + assert template["kind"] == "direct-model" + + +def test_inventory_health_check_hosted_surface_is_portal_only_when_present() -> None: + app_dir = REPO_ROOT / "apps" / "inventory-health-check" + manifest_paths = [ + app_dir / manifest_name + for manifest_name in HOSTED_SURFACE_MANIFEST_NAMES + if (app_dir / manifest_name).exists() + ] + if not manifest_paths: + pytest.skip("No Foundry-hosted portal/evaluation surface manifest present yet") + + for manifest_path in manifest_paths: + manifest = _load_yaml(manifest_path) + metadata = manifest.get("metadata") + assert isinstance(metadata, dict) + surface = metadata.get("surface") + assert isinstance(surface, dict) + assert surface.get("type") == "hosted" + assert surface.get("productRuntime") == "aks" + assert surface.get("replacesProductRuntime") is False + + template = manifest.get("template") + assert isinstance(template, dict) + assert template.get("kind") == "hosted" + protocols = {entry["protocol"]: entry["version"] for entry in template["protocols"]} + assert protocols["responses"] == "1.0.0" + + manifest_text = manifest_path.read_text(encoding="utf-8") + assert "hosted_main.py" not in manifest_text + assert "replacement product" not in manifest_text.lower() diff --git a/tests/ops/test_foundry_surface_registration.py b/tests/ops/test_foundry_surface_registration.py new file mode 100644 index 000000000..bd37216e4 --- /dev/null +++ b/tests/ops/test_foundry_surface_registration.py @@ -0,0 +1,178 @@ +"""Validate Foundry surface registration planning.""" + +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[2] +SCRIPT_PATH = REPO_ROOT / "scripts" / "ops" / "register_foundry_surfaces.py" + + +def _load_registration_module(): + spec = importlib.util.spec_from_file_location("register_foundry_surfaces", SCRIPT_PATH) + assert spec is not None + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def _inputs(module, **overrides): + defaults = { + "mode": "plan", + "environment": "dev", + "registry_path": REPO_ROOT / "apps" / "foundry-surfaces.yaml", + "project_endpoint": "https://example.services.ai.azure.com/api/projects/hph-dev", + "project_name": "hph-dev", + "acr_login_server": None, + "image_tag": None, + "image_map_file": None, + "apim_base_url": "https://apim.example.test", + "services": (), + "output": None, + "model_deployment_fast": "gpt-5-nano", + "model_deployment_rich": "gpt-5", + "allow_unresolved": False, + } + defaults.update(overrides) + return module.RegistrationInputs(**defaults) + + +@pytest.fixture(name="registration") +def registration_fixture(): + return _load_registration_module() + + +@pytest.fixture(name="_hosted_runtime_env") +def hosted_runtime_env_fixture(monkeypatch: pytest.MonkeyPatch) -> None: + values = { + "REDIS_HOST": "redis.example.test", + "REDIS_URL": "rediss://redis.example.test:6380", + "COSMOS_ACCOUNT_URI": "https://cosmos.example.test/", + "COSMOS_DATABASE": "holiday-peak", + "COSMOS_CONTAINER": "agent-memory", + "BLOB_ACCOUNT_URL": "https://blob.example.test/", + "BLOB_CONTAINER": "cold-memory", + "EVENT_HUB_NAMESPACE": "hph-eventhub.servicebus.windows.net", + "KEY_VAULT_URI": "https://hph-kv.vault.azure.net/", + "APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=00000000-0000-0000-0000-000000000000", + } + for key, value in values.items(): + monkeypatch.setenv(key, value) + + +def test_hosted_surface_plan_materializes_foundry_hosted_definition( + registration, _hosted_runtime_env, tmp_path: Path +) -> None: + image_map_path = tmp_path / "images.json" + image_ref = "hphdevacr.azurecr.io/inventory-health-check@sha256:abc123" + image_map_path.write_text( + json.dumps({"inventory-health-check": image_ref}), encoding="utf-8" + ) + inputs = _inputs( + registration, + services=("inventory-health-check",), + image_map_file=image_map_path, + ) + + plan = registration.build_registration_plan(inputs) + + assert plan["counts"] == {"hosted": 1, "custom": 0, "total": 1} + operation = plan["operations"][0] + assert operation["service"] == "inventory-health-check" + assert operation["surface"] == "hosted" + assert operation["applySupported"] is True + assert operation["agentEndpoint"].endswith( + "/agents/inventory-health-check/endpoint/protocols/openai/v1/responses" + ) + definition = operation["agentDefinition"] + assert definition["kind"] == "hosted" + assert definition["image"] == image_ref + assert definition["cpu"] == "0.5" + assert definition["memory"] == "1Gi" + assert definition["container_protocol_versions"] == [ + {"protocol": "responses", "version": "1.0.0"} + ] + env_vars = definition["environment_variables"] + assert env_vars["HOLIDAY_PEAK_FOUNDRY_HOSTED"] == "1" + assert env_vars["UVICORN_PORT"] == "8088" + assert env_vars["PROJECT_ENDPOINT"] == inputs.project_endpoint + assert env_vars["HPH_AGENT_ID_FAST"] == "inventory-health-check-fast" + assert env_vars["MODEL_DEPLOYMENT_NAME_RICH"] == "gpt-5" + assert all(not name.startswith(("FOUNDRY_", "AGENT_")) for name in env_vars) + assert operation["unresolvedVariables"] == [] + assert operation["metadata"]["hphDefinitionSha256"] == operation["definitionSha256"] + + +def test_custom_surface_plan_stays_proxy_metadata_only(registration) -> None: + inputs = _inputs(registration, services=("crm-campaign-intelligence",)) + + plan = registration.build_registration_plan(inputs) + + assert plan["counts"] == {"hosted": 0, "custom": 1, "total": 1} + operation = plan["operations"][0] + assert operation["service"] == "crm-campaign-intelligence" + assert operation["surface"] == "custom" + assert operation["action"] == "validate_custom_proxy_metadata" + assert operation["applySupported"] is False + assert "agentDefinition" not in operation + proxy = operation["customProxyDefinition"] + assert proxy["kind"] == "custom-proxy" + assert proxy["endpoint"] == "https://apim.example.test/agents/crm-campaign-intelligence" + assert proxy["productRuntime"] == "aks" + assert proxy["productTrafficPath"] == "APIM -> AGC -> AKS" + assert proxy["foundryManagedCompute"] is False + + +def test_hosted_surface_requires_image_source(registration, _hosted_runtime_env) -> None: + inputs = _inputs(registration, services=("inventory-health-check",)) + + with pytest.raises(registration.SurfaceRegistrationError, match="Hosted surfaces require"): + registration.build_registration_plan(inputs) + + +def test_acr_and_tag_can_materialize_hosted_image_ref( + registration, _hosted_runtime_env +) -> None: + inputs = _inputs( + registration, + services=("inventory-health-check",), + acr_login_server="hphdevacr.azurecr.io", + image_tag="commit-123", + ) + + plan = registration.build_registration_plan(inputs) + + definition = plan["operations"][0]["agentDefinition"] + assert definition["image"] == "hphdevacr.azurecr.io/inventory-health-check:commit-123" + + +def test_apply_mode_rejects_unresolved_values(registration, monkeypatch: pytest.MonkeyPatch) -> None: + for key in ( + "REDIS_HOST", + "COSMOS_ACCOUNT_URI", + "COSMOS_DATABASE", + "COSMOS_CONTAINER", + "BLOB_ACCOUNT_URL", + "BLOB_CONTAINER", + "EVENT_HUB_NAMESPACE", + "KEY_VAULT_URI", + "APPLICATIONINSIGHTS_CONNECTION_STRING", + ): + monkeypatch.delenv(key, raising=False) + inputs = _inputs( + registration, + mode="apply", + services=("inventory-health-check",), + acr_login_server="hphdevacr.azurecr.io", + image_tag="commit-123", + ) + + with pytest.raises(registration.SurfaceRegistrationError, match="Unresolved required values"): + registration.build_registration_plan(inputs) \ No newline at end of file