STATE bar påstanden «uverifisert: om FoundryChatClient kan binde en Claude- deployment» som en bisetning. Operatøren ba om at slikt ikke får ligge. Målt mot Microsoft Learn i stedet, og antakelsen er FALSIFISERT. Claude-modeller solgt via Azure Marketplace kalles med Anthropics egen Messages API på en egen endepunkt-form: https://<resource>.services.ai.azure.com/anthropic/v1/messages <- Claude https://<resource>.services.ai.azure.com/api/projects/<project> <- FoundryChatClient Klienter: `anthropic`-pakka, `@anthropic-ai/foundry-sdk`, eller REST med `anthropic-version: 2023-06-01`. Kun /v1/messages og /v1/messages/count_tokens er eksponert på «Hosted on Azure»-versjonen. Deployment-navnet er `model`-parameteren. Konsekvens for M1 (fase 1b), og den er en BESLUTNING, ikke en detalj: - Microsoft-solgt modell (gpt-5-mini o.l.) -> 1b er REN KONFIG, azure-profilen som shippet er riktig søm, ingenting å bygge. - Claude -> 1b krever en NY BACKEND-PROFIL i produksjonskode. Samme klasse som notatet under «Endpoint format»: *.openai.azure.com og *.cognitiveservices.azure.com er andre flater igjen. Tre flater, én ressurs-host. Samme pass fanget forutsetningene som gater portalarbeidet: Marketplace-subscribe- tillatelse, Contributor/Owner på ressursgruppa, prosjekt i støttet region, og data retention PÅ abonnementet for Covered Models (ZDR gir 400, og Microsoft kan ikke endre det for deg). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X737cbkG2uAXJ2Bvhf6X5M
100 lines
5.7 KiB
Markdown
100 lines
5.7 KiB
Markdown
# Azure AI Foundry auth recipe (S4.1)
|
|
|
|
> Verified against Microsoft Learn and the pinned `agent-framework-foundry==1.8.2` in this repo's
|
|
> `.venv`.
|
|
> This note is the operator's manual auth recipe for the single planned live Foundry run (M1). The
|
|
> offline `python -m portfolio_optimiser.preflight --profile azure` checks the config; this doc
|
|
> covers what preflight cannot: the actual authentication.
|
|
|
|
## The recipe (local dev, Intel Mac)
|
|
|
|
1. **`az login`** — sign in to the correct Entra tenant with the Azure CLI. This is a **manual**
|
|
operator step; the code never runs it (no auto-login).
|
|
2. The code passes a **lazy `AzureCliCredential()`** (`azure.identity.aio`) to `FoundryChatClient`
|
|
(`AzureFoundryBackend.create_chat_client`). Constructing the credential acquires **no token** —
|
|
the token is fetched from the `az login` session only on the first live call. So passing a
|
|
credential object is *not* auto-login.
|
|
- `AzureCliCredential` is preferred over `DefaultAzureCredential` on a non-Azure host: the latter
|
|
probes the IMDS managed-identity endpoint (times out / stalls) and can pick a stray identity.
|
|
3. Set the config the preflight validates:
|
|
- `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` — the Foundry **project** endpoint (below).
|
|
- `PORTFOLIO_MODEL_MAP` — path to an out-of-tree model_map with real azure deployment names, so
|
|
tenant-specific names are never committed. (Or replace the `REPLACE-WITH-*` placeholders.)
|
|
|
|
## FoundryChatClient signature (pinned 1.8.2)
|
|
|
|
`FoundryChatClient(*, project_endpoint, model, credential, ...)` — all keyword-only. **`credential`
|
|
is REQUIRED** on the `project_endpoint` path: the 1.8.2 client raises
|
|
`ValueError("Azure credential is required when using project_endpoint without a project_client.")`
|
|
at construction if omitted. There is **no** lazy `DefaultAzureCredential` default. The model
|
|
parameter is `model` (the portal **deployment name**), not `deployment_name`.
|
|
|
|
## Endpoint format
|
|
|
|
Canonical Foundry project endpoint:
|
|
|
|
```
|
|
https://<resource>.services.ai.azure.com/api/projects/<project>
|
|
```
|
|
|
|
The bare-host form `https://<resource>.services.ai.azure.com` also appears in official samples and
|
|
is accepted. The preflight requires `https://` + a host ending `.services.ai.azure.com`; it does
|
|
**not** require the `/api/projects/` path. A `*.openai.azure.com` or `*.cognitiveservices.azure.com`
|
|
endpoint is a **different** client surface (use `OpenAIChatClient`, not `FoundryChatClient`).
|
|
|
|
## Claude models on Foundry are a THIRD client surface (measured 2026-08-13)
|
|
|
|
Verified against Microsoft Learn (`microsoft_docs_search`, "Deploy and use Claude models in Microsoft
|
|
Foundry" / "Claude models in Microsoft Foundry — API overview"), because the question came up while
|
|
planning M1 and an assumption was cheaper to falsify than to inherit.
|
|
|
|
**`FoundryChatClient` CANNOT serve a Claude deployment.** Claude models sold through the Azure
|
|
Marketplace are called with Anthropic's own Messages API, on their own endpoint shape:
|
|
|
|
```
|
|
https://<resource>.services.ai.azure.com/anthropic/v1/messages # Claude
|
|
https://<resource>.services.ai.azure.com/api/projects/<project> # FoundryChatClient
|
|
```
|
|
|
|
Clients: the `anthropic` Python package (or `@anthropic-ai/foundry-sdk`, or REST with the
|
|
`anthropic-version: 2023-06-01` header). Only `POST /v1/messages` and `POST /v1/messages/count_tokens`
|
|
are exposed on the *Hosted on Azure* version. The **deployment name** is what goes in the `model`
|
|
parameter — same rule as the Foundry surface, different endpoint.
|
|
|
|
**What this means for M1 (fase 1b):**
|
|
|
|
- **A Microsoft-sold model** (`gpt-5-mini`, `gpt-4.1-mini`, …) → M1 is **configuration only**. The
|
|
`azure` profile as shipped is the right seam; nothing to build.
|
|
- **A Claude model** → M1 needs a **NEW backend profile** (production code, `anthropic` SDK, Entra ID
|
|
or key auth). That is a decision to take before the portal work, not a discovery during it.
|
|
|
|
This is the same class as the note under *Endpoint format*: `*.openai.azure.com` and
|
|
`*.cognitiveservices.azure.com` are other surfaces again. Three surfaces, one resource host.
|
|
|
|
**Extra prerequisites measured in the same pass** (they gate the portal work, not the code):
|
|
Marketplace subscription permission, **Contributor or Owner on the resource group**, a project in a
|
|
region the model supports, and — for Anthropic-designated *Covered Models* — data retention enabled
|
|
on the subscription (zero-data-retention subscriptions get a 400 `invalid_request_error`, and
|
|
Microsoft cannot change that setting for you).
|
|
|
|
## RBAC role
|
|
|
|
Assign **`Foundry User`** (role GUID `53ca6127-db72-4b80-b1b0-d745d6d5456d`) to the identity, at the
|
|
Foundry **resource/project** scope. Do **NOT** use `Azure AI Developer` (scoped to hubs/ML
|
|
workspaces, not Foundry projects) or `Cognitive Services User` (the classic Azure OpenAI surface).
|
|
Note the recent rename: `Foundry User` was formerly `Azure AI User` — reference the GUID, not the
|
|
display name, since older community answers use both.
|
|
|
|
## Preflight is necessary-but-not-sufficient
|
|
|
|
A green `preflight` rules out the **offline-detectable** misconfiguration class: unset endpoint,
|
|
malformed/wrong-surface endpoint URL, unresolved `REPLACE-WITH-*` placeholder deployments, and an
|
|
inconsistent model-map. It **cannot** prove the paid live call will succeed. The following surface
|
|
**only at the live call** and are out of scope for an offline check:
|
|
|
|
- **RBAC** — missing/wrong role → 403 (propagation lag can cause 401-then-200).
|
|
- **Token / tenant / consent** — expired or wrong-tenant credential → 401.
|
|
- **`DeploymentNotFound`** — a well-formed deployment name that does not exist in the project → 404.
|
|
- **api-version skew** and regional outages.
|
|
|
|
Treat a green preflight as "config is shaped correctly", never as "M1 will work".
|