diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/.gitattributes b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/.gitattributes new file mode 100644 index 000000000..43b1d6258 --- /dev/null +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/.gitattributes @@ -0,0 +1,3 @@ +# Keep the post-create hook LF so bash inside the dev container doesn't choke on +# CRLF when the repo is checked out on Windows. +*.sh text eol=lf diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/devcontainer.json b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/devcontainer.json index c0ab20546..f9d80e1ee 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/devcontainer.json +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/devcontainer.json @@ -9,7 +9,7 @@ }, "ghcr.io/devcontainers/features/github-cli:1": {} }, - "postCreateCommand": "pip install --upgrade pip && pip install -r requirements.txt", + "postCreateCommand": "bash .devcontainer/post-create.sh", "customizations": { "vscode": { "extensions": [ diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/post-create.sh b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/post-create.sh new file mode 100644 index 000000000..9e29a8141 --- /dev/null +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.devcontainer/post-create.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# ============================================================================= +# Dev container post-create hook (runs once after the container is built). +# +# 1. Install the Python dependencies (src/requirements.txt) — ESSENTIAL: +# every challenge needs these. +# 2. Best-effort install of the Microsoft ODBC Driver 18 for SQL Server so the +# OPTIONAL Azure SQL contract-status / renewal tool works out of the box. +# That tool's connection string uses "Driver={ODBC Driver 18 for SQL +# Server}" (see labautomation/infra/resources.bicep), so unixODBC alone is +# not enough. If this step fails (e.g. no network to packages.microsoft.com) +# the tool simply falls back to the bundled JSON corpus, so we NEVER fail +# the whole container build over an optional driver. +# ============================================================================= +set -euo pipefail + +echo "==> [1/2] Installing Python dependencies (src/requirements.txt)" +pip install --upgrade pip +pip install -r src/requirements.txt + +echo "==> [2/2] Installing Microsoft ODBC Driver 18 for SQL Server (optional Azure SQL tool)" +if command -v odbcinst >/dev/null 2>&1 && odbcinst -q -d 2>/dev/null | grep -q "ODBC Driver 18 for SQL Server"; then + echo " Driver already present — skipping." +else + # Debian 12 (bookworm) — matches the python:3.11-bookworm base image. + if ( + set -e + curl -sSL -O https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + rm -f packages-microsoft-prod.deb + sudo apt-get update + sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 + ); then + echo " ODBC Driver 18 installed." + else + echo " WARN: msodbcsql18 install failed — the Azure SQL tool will use the JSON fallback." + fi +fi + +echo "==> post-create complete." diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.dockerignore b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.dockerignore deleted file mode 100644 index f4d6e7c1c..000000000 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -# Keep the MCP server image build context minimal — only requirements.txt and -# src/ are needed to run the server. Everything else (docs, images, walkthrough, -# lab automation, screenshots) is excluded. -* -!requirements.txt -!src - -# Prune noise re-included with src/ -**/__pycache__ -**/*.pyc -**/*.pyo -**/.pytest_cache diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.github/workflows/ci-eval.yml b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.github/workflows/ci-eval.yml index 7adda2df5..4785fa3ca 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.github/workflows/ci-eval.yml +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.github/workflows/ci-eval.yml @@ -45,7 +45,7 @@ jobs: - name: Install dependencies if: steps.guard.outputs.configured == 'true' - run: pip install -r requirements.txt "azure-ai-evaluation[redteam]" + run: pip install -r src/requirements.txt "azure-ai-evaluation[redteam]" - name: Azure login (OIDC) if: steps.guard.outputs.configured == 'true' diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitignore b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitignore index 2e7550844..47533eee1 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitignore +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitignore @@ -1,9 +1,11 @@ # Secrets / local config +# The repo-root .env is written by src/scripts/write_env.py and holds live +# secrets (SharePoint app secret, connection strings). Never commit it. .env .env.* !.env.example -# Python +# Python (local runs at the repo root) __pycache__/ *.py[cod] .venv/ @@ -13,17 +15,6 @@ venv/ .ruff_cache/ .python-version -# Node / MCP tooling -node_modules/ - -# Build / conversion artifacts -*.log -*.pdf -# ...but the Challenge 1 CLM corpus PDFs ARE seed data and must be tracked. -!src/data/**/*.pdf -dist/ -build/ - # OS .DS_Store Thumbs.db diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/CODE_OF_CONDUCT.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/CODE_OF_CONDUCT.md deleted file mode 100644 index 686e5e7a0..000000000 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,10 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/LICENSE b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/LICENSE deleted file mode 100644 index cf7bcb2ba..000000000 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Microsoft Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/README.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/README.md index 39304f5de..04660f1c4 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/README.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/README.md @@ -300,10 +300,10 @@ challenges are a single story: ## Getting started 1. **Open this repo in Codespaces** (no fork needed — the optional Challenge 6 CI bonus is the only part that needs a fork) — **Code → Codespaces → Create codespace**. The devcontainer installs - Python 3.11, Azure CLI, `azd`, Node, and `requirements.txt` automatically. + Python 3.11, Azure CLI, `azd`, Node, and `src/requirements.txt` automatically. 2. `az login` (and `azd auth login` if you use the `azd up` path) 3. Do **[Challenge 1](challenges/challenge-01.md)** to deploy resources and seed the corpus — provision with - **`azd up`** (Bicep in `labautomation/infra/`), the **`labautomation/deploy`** script, or the one-click + **`azd up`** (run from `src/`; Bicep in `labautomation/infra/`), the **`labautomation/deploy`** script, or the one-click **Deploy to Azure** button (`infra/azuredeploy.json`). The first two autofill your `.env`. - **Seeding the corpus — default is Path B** (Challenge 1 · Task 6): **Path B (local-PDF)** needs no SharePoint and no admin consent, works in every tenant, and builds the `clm-corpus` index — @@ -317,12 +317,14 @@ challenges are a single story: ``` . -├── .devcontainer/ # Codespaces definition -├── azure.yaml # azd config (points at labautomation/infra, write-.env hook) +├── .devcontainer/ # Codespaces / Dev Containers definition ├── README.md # this file ├── challenges/ # challenge-01 … challenge-06 (one markdown brief per challenge) ├── walkthrough/ # challenge-0N/solution-0N.md — reference solution per challenge -├── src/ # all source code: agents/, clm_common/, mcp_server/, data/, scripts/ … +├── src/ # all source code + build/config: agents/, clm_common/, mcp_server/, data/, scripts/ … +│ ├── azure.yaml # azd config (points at ../labautomation/infra, write-.env hook) — run `cd src && azd up` +│ ├── Dockerfile # Challenge 4 MCP server image (.dockerignore alongside; build context = src/) +│ ├── requirements.txt # Python dependencies │ └── data/ # CLM corpus (PDF contracts/templates/clauses/policies) + eval datasets ├── labautomation/ # infra (Bicep) + deploy, seed corpus/SQL, write .env, smoke test ├── images/ # rendered images + per-challenge screenshots + diagrams diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SECURITY.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SECURITY.md deleted file mode 100644 index 656f79188..000000000 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SECURITY.md +++ /dev/null @@ -1,14 +0,0 @@ - - -## Security - -Microsoft takes the security of our software products and services seriously, which -includes all source code repositories in our GitHub organizations. - -**Please do not report security vulnerabilities through public GitHub issues.** - -For security reporting information, locations, contact information, and policies, -please review the latest guidance for Microsoft repositories at -[https://aka.ms/SECURITY.md](https://aka.ms/SECURITY.md). - - diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SUPPORT.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SUPPORT.md deleted file mode 100644 index 0cd2ae59f..000000000 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/SUPPORT.md +++ /dev/null @@ -1,18 +0,0 @@ -# Support - -## How to file issues and get help - -This project uses **GitHub Issues** to track bugs and feature requests. Please search the -existing issues before filing new ones to avoid duplicates. For a new issue, file your bug or -feature request as a new Issue. - -For help and questions about running this hackathon: - -- Read the top-level [README](README.md) and the per-challenge `README.md` files — each has a - **🛠️ Troubleshooting** section covering the most common blockers. -- If you're running this as a facilitated event, ask your **hack coach** first. -- Otherwise, open a **GitHub Issue** in this repository and tag it `question`. - -## Microsoft Support Policy - -Support for this hackathon content is limited to the resources listed above. diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-01.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-01.md index f55209e12..c5627509e 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-01.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-01.md @@ -5,7 +5,7 @@ Welcome to your very first challenge! Here you lay the foundation for the whole microhack: you'll deploy the Azure resources, wire up your development environment, and seed the contract corpus the later challenges build on. By the end you'll have the full **Microsoft Foundry** environment -running — with **zero local install** — so the rest of the hack is pure agent-building. +running in a **prebuilt dev container** — so the rest of the hack is pure agent-building. If something isn't working as expected, please let your coach know. @@ -13,8 +13,8 @@ If something isn't working as expected, please let your coach know. > **📋 Prerequisites:** > - An **Azure subscription** your lab was provisioned in *(or, if self-hosting, one with rights to create a Foundry project and deploy GPT models)*. -> - A **GitHub account** (to open the repo in Codespaces). -> - **GitHub Codespaces** access — everything runs in the browser; no local tooling required. +> - A **GitHub account** (to clone the repo). +> - **VS Code** with the **Dev Containers** extension and **Docker Desktop** — the repo's dev container has everything preinstalled *(or **GitHub Codespaces**, if you'd rather run in the browser)*. > 🧩 **How to use this challenge:** for a MicroHack event your Azure resources are **provisioned for > you** — you just point your `.env` at them (Task 3) and **confirm you understand what got created**: @@ -30,8 +30,8 @@ If something isn't working as expected, please let your coach know. ## 🧭 Context -Everything runs from **GitHub Codespaces** using the devcontainer in this repo (Python 3.11, Azure -CLI, `azd`, Node). For a MicroHack event the resources below are **already provisioned** into **one +Everything runs inside the **dev container** in this repo (Python 3.11, Azure +CLI, `azd`, Node) — open it locally in **VS Code** (Dev Containers) or in **GitHub Codespaces**. For a MicroHack event the resources below are **already provisioned** into **one resource group** and their endpoints appear on your **lab dashboard**; you copy them into `.env` in Task 3. *(Self-hosting? One **`azd up`** — Bicep in [`infra/`](../labautomation/infra/) — provisions the same resource group and autofills `.env`.)* @@ -128,36 +128,35 @@ text at crawl time); regenerate the PDFs with `python src/scripts/make_corpus_pd **Before you begin — tick these off:** -- [ ] You can sign in to [github.com](https://github.com). - [ ] You can sign in to the [Azure Portal](https://portal.azure.com) with the account your lab was provisioned for (or, if self-hosting, one that can **create resources**). - [ ] *(Self-hosting only)* Your Azure subscription can deploy **GPT** models (ask your coach if unsure). - [ ] You have ~30 minutes and a stable connection (provisioning takes 5–10 min on its own). -### Task 1 · Open the Codespace (~7 min) +### Task 1 · Open the project in VS Code (~7 min) -**No fork needed for the main hack (Challenges 1–5)** — the code you run lives in this repo. Open it in **GitHub Codespaces** (a full VS -Code + terminal in your browser, zero local install); because you work off the source repo, `git pull` +**No fork needed for the main hack (Challenges 1–5)** — the code you run lives in this repo. Clone it and open it in **VS +Code** using the **Dev Containers** extension (a prebuilt container with Python, Azure CLI, `azd`, and Node — no manual installs); because you work off the source repo, `git pull` always gets the latest fixes. *(The **one exception** is the optional **Challenge 6** CI bonus — it runs in **GitHub Actions**, so it needs **your own fork**; you'll create it there, not now.)* -1. On the repo's GitHub page, click **`< > Code` → Codespaces → Create codespace on `main`**. - *(Prefer local? `git clone` the repo and **Reopen in Container** with the VS Code Dev Containers - extension.)* -2. Wait for the container to build — it installs dependencies with `pip install -r requirements.txt` +1. Open the folder in **VS Code** (e.g. `code microhack-aiagents`). When VS + Code prompts **"Reopen in Container"**, click it — or run **Dev Containers: Reopen in Container** from + the Command Palette (**F1**). Requires the **Dev Containers** extension and **Docker Desktop**. +2. Wait for the container to build — it installs dependencies with `pip install -r src/requirements.txt` automatically. When the terminal stops scrolling and shows a prompt, it's ready. -GitHub · Code → Codespaces → Create codespace on main - -✅ **You'll know it worked when:** a browser VS Code editor opens with a **Terminal** panel showing a -ready prompt (e.g. `@your-username ➜ /workspaces/microhack-aiagents (main) $`). +✅ **You'll know it worked when:** a **VS Code** window opens (locally or in the browser) with a **Terminal** panel showing a +ready prompt. > [!NOTE] -> If Codespaces isn't enabled in your org, see [enabling Codespaces](https://docs.github.com/en/codespaces/managing-codespaces-for-your-organization/enabling-or-disabling-github-codespaces-for-your-organization) or use a [free personal account](https://github.com/signup) (120 core-hours/month free). While it builds, skim the [scenario & architecture](../README.md#the-scenario--contoso-global) so the pieces you deploy here make sense. +> **Prefer the browser? Use GitHub Codespaces instead.** On the repo's GitHub page, click **`< > Code` → Codespaces → Create codespace on `main`** — the same dev container builds in the cloud, so you need no local Docker. +> +> While it builds, skim the [scenario & architecture](../README.md#the-scenario--contoso-global) so the pieces you deploy here make sense. --- ### Task 2 · Log in to Azure (~3 min) -Now connect the terminal to your Azure account. In the Codespace **Terminal**, type this and press Enter: +Now connect the terminal to your Azure account. In the **VS Code Terminal**, type this and press Enter: ```bash az login --use-device-code @@ -198,13 +197,13 @@ For a **MicroHack event your Azure resources are already provisioned** — a res Foundry project, the three-model GPT fleet, and Azure AI Search. You don't deploy anything; you just point your `.env` at them using the values on your **lab dashboard**. -**Step 3a — create your `.env`** from the template (Codespace terminal, at the repo root): +**Step 3a — create your `.env`** from the template (VS Code terminal, at the repo root): ```bash cp src/.env.example .env ``` -**Step 3b — copy your dashboard values into `.env`.** Open `.env` in the Codespace editor and fill in +**Step 3b — copy your dashboard values into `.env`.** Open `.env` in the VS Code editor and fill in the values shown on your lab dashboard: | Lab dashboard credential | `.env` variable | Example value | @@ -245,6 +244,7 @@ everything. First pick a region that offers **all three** models — this repo's > old template is what triggers `DeploymentModelNotSupported` / `ServiceModelDeprecating`. ```bash +cd src # azure.yaml (the azd project) lives here azd auth login # separate from az login above azd up # answer: environment name (e.g. clm-microhack), your subscription, region = Sweden Central ``` @@ -257,7 +257,7 @@ It provisions for **5–10 minutes**, assigns the RBAC roles the later challenge grounding with `azd env set DEPLOY_BING true` before `azd up`. > **Prefer not to use `azd`?** `LOCATION=swedencentral ./labautomation/deploy.sh` (add `--with-sql` / -> `--with-bing`; on Windows outside Codespaces use `./labautomation/deploy.ps1`) provisions the same +> `--with-bing`; on native **Windows** use `./labautomation/deploy.ps1`) provisions the same > resources and writes `.env` too. If it fails with `DeploymentModelNotSupported`, a model/version > isn't offered in your region — see [🛠️ Troubleshooting](#️-troubleshooting). @@ -286,7 +286,7 @@ your **`clm-project`** → **Models + endpoints**. Confirm the deployments show > > Screenshot slot: model deployments -**4c — Your `.env` file.** In the Codespace file explorer, open **`.env`** at the repo root. Confirm the +**4c — Your `.env` file.** In the VS Code file explorer, open **`.env`** at the repo root. Confirm the values are filled in (every entry has a value **except** the `SHAREPOINT_*` corpus and the Challenge 5 `MICROSOFT_APP_*` / `TEAMS_*` variables, which you fill later). @@ -604,7 +604,7 @@ Smoke test: ✅ PASS | `Project can only be created under AIServices Kind account with allowProjectManagement set to true` | Fixed in the template (`account.properties.allowProjectManagement: true`). If you hit it, your checkout is behind — run `git pull` and redeploy. | | SharePoint: *"Tenant does not have a SPO license"*, or you can't grant the app's Graph **admin consent** (only Global Reader / **"Grant admin consent" greyed out**) | Only happens if you're **not** an admin of the tenant — in your own sandbox tenant the Path A script self-grants consent. If you hit it, it's **not** a failure: use the **local-PDF fallback (Path B)** — leave the `SHAREPOINT_*` values blank in `.env` and run `python src/scripts/seed_corpus.py`. It extracts `src/data/**/*.pdf` and populates `clm-corpus` directly (needs the Search Index Data Contributor role, granted during provisioning) — the **same index** the SharePoint path builds, so Challenges 2–6 are unaffected. See [Task 5, Path B](#task-5--seed-the-corpus). | | `account project create` unavailable | The CLI project command is preview. Create the project in the **Foundry portal**, then set `AZURE_AI_PROJECT_ENDPOINT` in `.env` manually (Overview → Endpoint). | -| `az login` in Codespaces | Use `az login --use-device-code`. | +| `az login` in a dev container / Codespaces | Use `az login --use-device-code`. | | Search / quota errors | Ensure the subscription has quota for Basic Search + the model SKUs; request quota if needed. | | `PermissionDenied` after deploy | RBAC can take 5–10 min to propagate. Wait, run `az login --use-device-code` again, and retry. | diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-04.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-04.md index 9dd76b121..ad3ec02ee 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-04.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/challenges/challenge-04.md @@ -272,7 +272,7 @@ agent, your orchestrator) can reach. No editor required. #### Part A · Host the server on Azure Container Apps -The server already speaks HTTP — `--http` (what the repo-root [`Dockerfile`](../Dockerfile) runs) serves +The server already speaks HTTP — `--http` (what the src/ [`Dockerfile`](../src/Dockerfile) runs) serves **streamable HTTP** at `/mcp` on port 8000. Deploy it (image builds **in the cloud** — no local Docker) from the **repo root**. The script **reads your `.env`** (the same one the agents use) and **auto-discovers** the resource group, Foundry account and region from your project endpoint — so there's @@ -362,12 +362,12 @@ at your URL. | `clm-mcp` not in *MCP: List Servers* | VS Code discovers a workspace MCP server only from `.vscode/mcp.json` at the **root of the opened folder** — running `python src/mcp_server/server.py` in a terminal does **not** register it. Open the **repo root** (not `src/`) and confirm the file is at `/.vscode/mcp.json`. If it's missing there, **pull the latest hack repo** (older copies shipped it under `src/.vscode/`), then reload VS Code. | | `Invalid JSON … Internal Server Error` after starting the server | **Harmless.** You typed or pressed **Enter** in the stdio window, so the server rejected the newline as invalid JSON-RPC. It's still running — don't type into it. Use `python src/mcp_server/server.py --list` to confirm the tools without the stdio loop. | | Orchestrator doesn't route correctly | Sharpen the routing rules in `INSTRUCTIONS`; make each specialist's `as_tool(description=...)` specific. | -| `ImportError: cannot import name 'Agent' from 'agent_framework'` (or other `agent_framework` import errors) | You have an **old/mismatched build**, or you `pip install`ed into a **different Python** than the one running the script (common with Microsoft Store Python). First see **which** interpreter actually runs the script: `python -c "import sys; print(sys.executable)"`. Then reinstall the pinned deps into **that same** interpreter — the `-U` matters, a plain install won't replace a stale version: `python -m pip install -U -r requirements.txt`. Finally verify: `python -c "import agent_framework as a; print(a.__version__)"` — you need **≥ 1.11.0**. | +| `ImportError: cannot import name 'Agent' from 'agent_framework'` (or other `agent_framework` import errors) | You have an **old/mismatched build**, or you `pip install`ed into a **different Python** than the one running the script (common with Microsoft Store Python). First see **which** interpreter actually runs the script: `python -c "import sys; print(sys.executable)"`. Then reinstall the pinned deps into **that same** interpreter — the `-U` matters, a plain install won't replace a stale version: `python -m pip install -U -r src/requirements.txt`. Finally verify: `python -c "import agent_framework as a; print(a.__version__)"` — you need **≥ 1.11.0**. | | MCP server not listed in VS Code | Ensure the MCP feature is enabled and `mcp.json` path is correct; confirm the server imports cleanly first with `python src/mcp_server/server.py --list`. | | MCP tool call times out | Each call spins up + tears down a Foundry agent (a few seconds). Keep drafts short while testing. | | `orchestrator_mcp.py` finds no tools / hangs at startup | The stdio server failed to import. Confirm `python src/mcp_server/server.py` starts standalone; `MCPStdioTool` sets `PYTHONPATH=src`, so run from the repo root. | | Web search tool not attaching | Confirm `AZURE_BING_CONNECTION_NAME` matches a **project connection** for your Grounding with Bing Search resource; run `python src/kb_setup.py` — it prints whether the web-grounding tool built. | -| `deploy.sh` fails / `az containerapp up` errors | Ensure `az` ≥ 2.53 and the **containerapp** extension (`az extension add -n containerapp`), you're logged in (`az login`) and on the lab subscription (`az account set -s `), and you're running it from the **repo root** (build context needs `Dockerfile`, `requirements.txt`, `src/`). First run also registers the `Microsoft.App`/`Microsoft.OperationalInsights` providers — that can take a minute. | +| `deploy.sh` fails / `az containerapp up` errors | Ensure `az` ≥ 2.53 and the **containerapp** extension (`az extension add -n containerapp`), you're logged in (`az login`) and on the lab subscription (`az account set -s `), and you're running it from the **repo root** (the script builds from the `src/` context — `src/Dockerfile`, `src/requirements.txt`, app code). First run also registers the `Microsoft.App`/`Microsoft.OperationalInsights` providers — that can take a minute. | | Foundry agent shows the MCP tool but tool calls fail / time out | Check the app is reachable: open `https://.azurecontainerapps.io/mcp` — it should respond (405/JSON, not a connection error). Confirm ingress is **external** (`az containerapp ingress show`), the URL **ends with `/mcp`**, and the Server URL in Foundry matches exactly. | | Remote tools return `401/403` / "credential" errors from Foundry | The **container's managed identity** lacks a data-plane role on your Foundry account. Re-run the role step in `deploy.sh` (or assign **Azure AI User** on `FOUNDRY_ACCOUNT_ID`), then wait ~1 min for propagation. Verify with `az containerapp identity show` + `az role assignment list --assignee `. | | `CLM_MCP_URL` run: connection refused / hangs | Confirm the app is running (`az containerapp show --query properties.runningStatus`) and the URL includes `/mcp`. If you added a key, set `CLM_MCP_KEY` too. Unset `CLM_MCP_URL` to fall back to the local stdio server. | diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/README.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/README.md index bf7777bcf..08c8f4ad2 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/README.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/README.md @@ -7,7 +7,7 @@ network — no local process required. ## What gets deployed - A single **Azure Container App** named `clm-mcp` (external HTTPS ingress on - port `8000`) built from the repo‑root [`Dockerfile`](../../Dockerfile). + port `8000`) built from the src/ [`Dockerfile`](../../src/Dockerfile). - The app runs `python src/mcp_server/server.py --http`, exposing the MCP endpoint at `https://..azurecontainerapps.io/mcp`. - A **system‑assigned managed identity** granted a data‑plane role on your diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.ps1 b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.ps1 index a87e2853b..7e6374334 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.ps1 +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.ps1 @@ -5,7 +5,7 @@ ----------------------------------------------------------------------------- Windows/PowerShell twin of deploy.sh. Builds the image in the cloud (no local Docker) and prints the /mcp URL a Foundry agent connects to. Run it from the - REPO ROOT so the build context (requirements.txt + src/) is correct. + REPO ROOT; the build context is the src/ folder (Dockerfile + requirements.txt + app code). ZERO-CONFIG by default: reads your repo-root .env for AZURE_AI_PROJECT_ENDPOINT + MODEL_*, then auto-discovers the resource group, Foundry account id and region @@ -94,7 +94,7 @@ az containerapp up ` --name $AppName ` --resource-group $ResourceGroup ` --location $Location ` - --source . ` + --source src ` --ingress external ` --target-port 8000 ` --env-vars ` diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.sh b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.sh index 3d3562aba..cd99c8f64 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.sh +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/deploy/mcp-server/deploy.sh @@ -3,8 +3,8 @@ # Challenge 4 · Deploy the CLM MCP server to Azure Container Apps (remote MCP) # ----------------------------------------------------------------------------- # Builds the image in the cloud (no local Docker needed) and prints the /mcp URL -# a Foundry agent connects to. Run this from the REPO ROOT so the Docker build -# context (requirements.txt + src/) is correct. +# a Foundry agent connects to. Run this from the REPO ROOT; the Docker build +# context is the src/ folder (Dockerfile + requirements.txt + app code). # # ZERO-CONFIG by default: it reads your repo-root `.env` (the same file the # agents use) for AZURE_AI_PROJECT_ENDPOINT + MODEL_*, then auto-discovers the @@ -85,7 +85,7 @@ az containerapp up \ --name "$APP_NAME" \ --resource-group "$RESOURCE_GROUP" \ --location "$LOCATION" \ - --source . \ + --source src \ --ingress external \ --target-port 8000 \ --env-vars \ diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/docs/coach-guide.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/docs/coach-guide.md index efe5bfe1b..cb7a21364 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/docs/coach-guide.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/docs/coach-guide.md @@ -150,7 +150,7 @@ of the challenge, what "done" looks like, where teams get stuck, and the hint to - **Watch for:** - *Orchestrator routes wrong* → sharpen `INSTRUCTIONS` routing rules and make each specialist's `as_tool(description=...)` specific. - - *`agent_framework` import error* → `pip install agent-framework-core agent-framework-foundry` (see requirements.txt). + - *`agent_framework` import error* → `pip install agent-framework-core agent-framework-foundry` (see src/requirements.txt). - *MCP server not listed in VS Code* → the workspace config must be at the repo-root `.vscode/mcp.json` and you must open the **repo root** (not `src/`); confirm the server imports cleanly first (`python src/mcp_server/server.py --list`). - *(Task 4) Remote deploy* → run `bash deploy/mcp-server/deploy.sh` from the **repo root** (needs the diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/README.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/README.md index 99eec7f91..11c961e5e 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/README.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/README.md @@ -41,7 +41,7 @@ same resources from `infra/`. [`infra/`](infra/) holds the Bicep templates (plus `azuredeploy.json` for the one-click **Deploy to Azure** button) that create the Microsoft Foundry project, the three GPT model deployments (`gpt-5.4`, `gpt-5.6-sol`, `gpt-5.4-nano`), Azure AI Search, Azure SQL, and Application Insights. -`azure.yaml` at the repo root points `azd` at this folder. +`src/azure.yaml` points `azd` at this folder (`../labautomation/infra`); run `azd up` from `src/`. ## Scripts @@ -68,7 +68,7 @@ Seeding, setup & gate scripts — run by participants/coaches during the hack ```bash az login -./labautomation/deploy.sh # or: azd up (Windows: labautomation\deploy.ps1) +./labautomation/deploy.sh # or: (cd src && azd up) (Windows: labautomation\deploy.ps1) # Default corpus path (own admin tenant): SharePoint app + consent + site + upload + index python src/scripts/setup_sharepoint_corpus.py # — or the no-SharePoint fallback: python src/scripts/seed_corpus.py diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/lab-defaults.json b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/lab-defaults.json index 3c1443a4c..866701b15 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/lab-defaults.json +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/labautomation/lab-defaults.json @@ -3,6 +3,6 @@ "groups": ["M365-E5-Users"], "deploymentType": "resourcegroup", "labsPerSubscription": 8, - "preferredLocation": "swedencentral, westeurope, norwayeast", - "estimatedDailyCostsUsd": 5.0 + "preferredLocation": "swedencentral, norwayeast, spaincentral", + "estimatedDailyCostsUsd": 15.0 } diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.dockerignore b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.dockerignore new file mode 100644 index 000000000..faf0b6e39 --- /dev/null +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.dockerignore @@ -0,0 +1,18 @@ +# The build context IS this src/ folder — it holds the Dockerfile, requirements.txt +# and all the app code the CLM MCP server needs (Dockerfile does COPY . ./src/). +# Exclude only caches, local secrets, and build/config files not needed at runtime. +**/__pycache__ +**/*.pyc +**/*.pyo +**/.pytest_cache + +# Never bake local secrets into the image +.env +.env.* +!.env.example + +# Build/config files the running server image doesn't need +.gitignore +.gitattributes +.dockerignore +azure.yaml diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitattributes b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.gitattributes similarity index 100% rename from 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/.gitattributes rename to 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.gitattributes diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.gitignore b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.gitignore new file mode 100644 index 000000000..8d31e92ee --- /dev/null +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/.gitignore @@ -0,0 +1,33 @@ +# Secrets / local config +.env +.env.* +!.env.example + +# Python +__pycache__/ +*.py[cod] +.venv/ +venv/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.python-version + +# Node / MCP tooling +node_modules/ + +# Build / conversion artifacts +*.log +*.pdf +# ...but the Challenge 1 CLM corpus PDFs ARE seed data and must be tracked. +!data/**/*.pdf +dist/ +build/ + +# OS +.DS_Store +Thumbs.db + +# Azure / tooling +.azure/ +*.azd diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/Dockerfile b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/Dockerfile similarity index 78% rename from 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/Dockerfile rename to 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/Dockerfile index a093187ce..c336799f2 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/Dockerfile +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/Dockerfile @@ -2,10 +2,11 @@ # ============================================================================= # Challenge 4 — container image for the CLM MCP server (remote / streamable-HTTP) # ----------------------------------------------------------------------------- -# The build CONTEXT must be the REPO ROOT — the image needs requirements.txt and -# src/. Deploy it with one command (builds in the cloud, no local Docker needed): +# The build CONTEXT is this `src/` folder — it holds this Dockerfile, +# requirements.txt and all the app code (COPY . ./src/). Deploy it with one +# command (builds in the cloud, no local Docker needed) by pointing at src/: # -# az containerapp up -n clm-mcp -g --source . \ +# az containerapp up -n clm-mcp -g --source ./src \ # --target-port 8000 --ingress external \ # --env-vars AZURE_AI_PROJECT_ENDPOINT= # @@ -34,7 +35,9 @@ RUN apt-get update \ COPY requirements.txt ./ RUN pip install -r requirements.txt -COPY src/ ./src/ +# The build context IS this src/ folder, so copy it all into /app/src/ (caches and +# build/config files are pruned by .dockerignore). CMD below runs src/mcp_server/server.py. +COPY . ./src/ EXPOSE 8000 diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/README.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/README.md index d36686b22..fa6edc843 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/README.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/README.md @@ -21,7 +21,7 @@ helpers live in [`clm_common/`](clm_common/); every entry-point script adds `src | [`mcp_server/server.py`](mcp_server/server.py) | MCP server exposing the CLM workflow — stdio (local) **and** streamable HTTP (`--http`, for hosting) | 4 | | [`../.vscode/mcp.json`](../.vscode/mcp.json) | VS Code MCP client config (`clm-mcp`, repo root) | 4 | | [`orchestrator_mcp.py`](orchestrator_mcp.py) | Orchestrator as MCP client — local stdio, or remote via `CLM_MCP_URL` | 4 | -| [`../Dockerfile`](../Dockerfile) + [`../deploy/mcp-server/`](../deploy/mcp-server/) | Containerize + deploy the MCP server to Azure Container Apps (remote `/mcp` for Foundry) | 4 | +| [`Dockerfile`](Dockerfile) + [`../deploy/mcp-server/`](../deploy/mcp-server/) | Containerize + deploy the MCP server to Azure Container Apps (remote `/mcp` for Foundry) | 4 | | [`manifest/`](manifest/) | Teams / M365 Copilot app package (manifest + icons) | 5 | | [`red_team.py`](red_team.py) | Automated red-teaming → `redteam_scorecard.json` | 6 | | [`safety_eval.py`](safety_eval.py) | Safety evaluation + CLM guardrail gate | 6 | @@ -40,5 +40,5 @@ python src/orchestrator.py # Challenge 4 — orchestration ``` > Config is read from the repo-root `.env` via `clm_common/config.py`. Run -> [`../labautomation/deploy.sh`](../labautomation/deploy.sh) (or `azd up`) first so the +> [`../labautomation/deploy.sh`](../labautomation/deploy.sh) (or `cd src && azd up`) first so the > `.env` is populated and the corpus is seeded. diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/azure.yaml b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/azure.yaml similarity index 68% rename from 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/azure.yaml rename to 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/azure.yaml index 63091a8ff..5b50484b1 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/azure.yaml +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/azure.yaml @@ -1,7 +1,8 @@ # ========================================================================== # Foundry CLM Microhack — Azure Developer CLI (azd) configuration -# `azd up` provisions the full Foundry environment defined in labautomation/infra/ and then -# writes a repo-root .env matching labautomation/deploy.sh. +# This file lives in src/, so run azd from there: `cd src && azd up`. +# `azd up` provisions the full Foundry environment defined in ../labautomation/infra/ +# and then writes a repo-root .env matching labautomation/deploy.sh. # Docs: https://aka.ms/azure-dev/schema # ========================================================================== name: foundry-clm-microhack @@ -10,7 +11,7 @@ metadata: infra: provider: bicep - path: labautomation/infra + path: ../labautomation/infra module: main hooks: @@ -19,11 +20,11 @@ hooks: postprovision: windows: shell: pwsh - run: python src/scripts/write_env.py + run: python scripts/write_env.py continueOnError: false interactive: false posix: shell: sh - run: python3 src/scripts/write_env.py + run: python3 scripts/write_env.py continueOnError: false interactive: false diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/clm_common/documents.py b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/clm_common/documents.py index 4307f9a1a..3ee64a827 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/clm_common/documents.py +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/clm_common/documents.py @@ -26,7 +26,7 @@ def read_document_text(path: str | Path) -> str: except ImportError as exc: # pragma: no cover - dependency hint raise RuntimeError( "Reading PDF documents requires 'pypdf'. Install it with " - "`pip install pypdf` (it is listed in requirements.txt)." + "`pip install pypdf` (it is listed in src/requirements.txt)." ) from exc reader = PdfReader(str(path)) diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/red_team.py b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/red_team.py index 97983656d..547042f04 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/red_team.py +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/red_team.py @@ -15,7 +15,7 @@ python src/red_team.py --num-objectives 3 --output redteam_scorecard.json Requires: `pip install "azure-ai-evaluation[redteam]"` (pulls PyRIT) and an -Azure AI (Foundry) project + login. See requirements.txt. +Azure AI (Foundry) project + login. See src/requirements.txt. """ from __future__ import annotations diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/requirements.txt b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/requirements.txt similarity index 100% rename from 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/requirements.txt rename to 03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/requirements.txt diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/make_step_placeholders.py b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/make_step_placeholders.py index a2f62e177..b2dc3d2d8 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/make_step_placeholders.py +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/make_step_placeholders.py @@ -25,7 +25,7 @@ ("challenge-0", "02-create-codespace", "GitHub · Create Codespace", "Code button → Codespaces tab → 'Create codespace on main' green button."), ("challenge-0", "03-codespace-ready", "Codespace · Ready", - "The VS Code-in-browser Codespace with a terminal open and 'pip install -r requirements.txt' finished."), + "The VS Code-in-browser Codespace with a terminal open and 'pip install -r src/requirements.txt' finished."), ("challenge-0", "04-az-login-device", "Azure · Device-code login", "The https://microsoft.com/devicelogin page where you paste the code printed by 'az login --use-device-code'."), ("challenge-0", "05-azd-up-prompts", "azd up · Prompts", diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/setup_sharepoint_corpus.py b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/setup_sharepoint_corpus.py index d443f7114..29bd843b3 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/setup_sharepoint_corpus.py +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/setup_sharepoint_corpus.py @@ -337,14 +337,15 @@ def upsert_env(values: dict[str, str]) -> None: print(f" ✓ wrote SHAREPOINT_* values to {ENV_PATH}") # Best-effort: mirror into the azd environment so a later `azd provision`/`azd up` - # regenerates .env with these values instead of blanking them. + # regenerates .env with these values instead of blanking them. azure.yaml lives + # in src/, so run azd from there. azd = shutil.which("azd") if azd: for key in SHAREPOINT_KEYS: if key in values: subprocess.run( [azd, "env", "set", key, values[key]], - cwd=REPO_ROOT, capture_output=True, text=True, check=False, + cwd=REPO_ROOT / "src", capture_output=True, text=True, check=False, ) diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/write_env.py b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/write_env.py index 23179dd18..b0c9ed763 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/write_env.py +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/src/scripts/write_env.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Write the repo-root .env from deployment outputs. -By default this runs as the azd `postprovision` hook (see azure.yaml) and reads +By default this runs as the azd `postprovision` hook (see src/azure.yaml) and reads the provisioned values via `azd env get-values`. Pass `--deployment ` to instead read the outputs of a subscription-scoped ARM deployment (the plain-ARM / "Deploy to Azure" path using infra/azuredeploy.json), e.g.: @@ -21,6 +21,8 @@ REPO_ROOT = Path(__file__).resolve().parents[2] ENV_PATH = REPO_ROOT / ".env" +# azure.yaml (the azd project) lives in src/, so azd commands must run from there. +AZD_DIR = REPO_ROOT / "src" # Keys sourced from Bicep outputs (via azd env). Fall back to the src/.env.example # defaults for the constant-valued ones if an output is missing. @@ -40,12 +42,12 @@ } -def _run(cmd: list[str]) -> str: - """Run a command in the repo root, exiting with a friendly message on error.""" +def _run(cmd: list[str], cwd: Path = REPO_ROOT) -> str: + """Run a command (default cwd = repo root), exiting with a friendly message on error.""" try: proc = subprocess.run( cmd, - cwd=REPO_ROOT, + cwd=cwd, capture_output=True, text=True, check=True, @@ -59,7 +61,7 @@ def _run(cmd: list[str]) -> str: def azd_env_values() -> dict[str, str]: """Return azd environment values as a dict (parsing KEY="value" lines).""" - stdout = _run(["azd", "env", "get-values"]) + stdout = _run(["azd", "env", "get-values"], cwd=AZD_DIR) values: dict[str, str] = {} for line in stdout.splitlines(): line = line.strip() diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-01/solution-01.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-01/solution-01.md index 302fa5d45..f38d5040c 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-01/solution-01.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-01/solution-01.md @@ -27,7 +27,7 @@ contract corpus the later challenges ground on. ## 🛠️ Task-by-task walkthrough ### Task 1 · Open the Codespace -No fork — the code lives in this repo. **`< > Code` ▸ Codespaces ▸ Create codespace on `main`**; the devcontainer builds and `pip install -r requirements.txt` runs automatically. *(Local alt: `git clone` then **Reopen in Container**.)* +No fork — the code lives in this repo. **`< > Code` ▸ Codespaces ▸ Create codespace on `main`**; the devcontainer builds and `pip install -r src/requirements.txt` runs automatically. *(Local alt: `git clone` then **Reopen in Container**.)* > 📸 **Screenshot slot:** creating the **Codespace**. > @@ -63,9 +63,10 @@ The model names + `AZURE_SEARCH_INDEX` (`clm-corpus`) / `AZURE_SEARCH_CONNECTION Self-hosting? One azd up provisions everything and writes .env for you ```bash +cd src # azure.yaml (the azd project) lives here azd auth login # separate from az login above azd up # env name, subscription, region = Sweden Central (offers all three models) -# — or the scripted path — LOCATION=swedencentral ./labautomation/deploy.sh (deploy.ps1 on Windows) +# — or the scripted path — LOCATION=swedencentral ./labautomation/deploy.sh (deploy.ps1 on Windows; from repo root) ``` The `postprovision` hook → [`src/scripts/write_env.py`](../../src/scripts/write_env.py) reads the deployment outputs (`azd env get-values`, or `--deployment` for the ARM path) and writes every env var the agents use — filling constants from a `DEFAULTS` map when an output is absent, so you can skip the manual paste above: ```python diff --git a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-04/solution-04.md b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-04/solution-04.md index f067def15..0ce50b6a5 100644 --- a/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-04/solution-04.md +++ b/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/walkthrough/challenge-04/solution-04.md @@ -17,7 +17,7 @@ called from a **Foundry** agent by URL. `draft_contract` · `analyze_contract` · `get_contract_status` over **stdio** (local) and **streamable HTTP** (`--http`, for hosting); VS Code loads the stdio server from [`.vscode/mcp.json`](../../.vscode/mcp.json) (repo root). -- [`Dockerfile`](../../Dockerfile) + [`deploy/mcp-server/deploy.sh`](../../deploy/mcp-server/deploy.sh) +- [`Dockerfile`](../../src/Dockerfile) + [`deploy/mcp-server/deploy.sh`](../../deploy/mcp-server/deploy.sh) host it on **Azure Container Apps** as a remote `https://…/mcp` endpoint a Foundry agent can call. - [`src/orchestrator_mcp.py`](../../src/orchestrator_mcp.py) runs the same GPT-5.4 orchestrator as an **MCP client** — local stdio, or the remote server via `CLM_MCP_URL`. @@ -118,9 +118,9 @@ spawns the stdio server for you (no IDE) and runs draft → analyze → status o start **clm-mcp** → call `#analyze_contract` in Copilot Chat. ### Task 4 · Host it remotely + call it from Foundry -**Part A — host on Azure Container Apps.** The repo-root [`Dockerfile`](../../Dockerfile) runs +**Part A — host on Azure Container Apps.** The src/ [`Dockerfile`](../../src/Dockerfile) runs `server.py --http` (streamable HTTP at `/mcp`). Deploy from the repo root — the image builds in the -cloud, no local Docker: +cloud (from the `src/` context), no local Docker: ```bash bash deploy/mcp-server/deploy.sh # reads .env, auto-discovers RG/account/region → https://clm-mcp..azurecontainerapps.io/mcp ``` @@ -174,7 +174,7 @@ propagation takes ~1 min, so just re-run the script or wait if a later model cal | [`src/mcp_server/server.py`](../../src/mcp_server/server.py) | MCP server exposing the CLM workflow (stdio **and** streamable HTTP via `--http`) | | [`.vscode/mcp.json`](../../.vscode/mcp.json) | VS Code MCP client config (`clm-mcp`, repo root) | | [`src/orchestrator_mcp.py`](../../src/orchestrator_mcp.py) | Orchestrator as MCP client — local stdio, or remote via `CLM_MCP_URL` | -| [`Dockerfile`](../../Dockerfile) + [`deploy/mcp-server/deploy.sh`](../../deploy/mcp-server/deploy.sh) | Containerize + deploy the server to Azure Container Apps as a remote `/mcp` endpoint | +| [`Dockerfile`](../../src/Dockerfile) + [`deploy/mcp-server/deploy.sh`](../../deploy/mcp-server/deploy.sh) | Containerize + deploy the server to Azure Container Apps as a remote `/mcp` endpoint | ## Run it