diff --git a/docs/a2a.md b/docs/a2a.md index 0628f2cba..03bf2ecb9 100644 --- a/docs/a2a.md +++ b/docs/a2a.md @@ -62,6 +62,8 @@ agents: `protocolVersion` can be set the same way when registering through the API. +An optional `litellm_params` block carries per-agent settings such as `api_key`, `headers`, `agent_card_path`, and Microsoft Entra credentials; [Foundry agents over A2A](./providers/azure_ai_agents#foundry-agents-over-a2a) shows a full entry + Config-defined agents show up in the Agents tab and in `GET /v1/agents` alongside agents created in the UI, and they survive the periodic reload from the database. Verify them with: ```shell @@ -83,6 +85,8 @@ The `agents` key is read correctly starting in the next release (after `v1.95.0` Follow [this guide, to add your azure ai foundry agent to LiteLLM Agent Gateway](./providers/azure_ai_agents#litellm-a2a-gateway) +Agents created in the current Foundry portal expose an A2A endpoint instead of the Assistants API; register those under `agents:` with Entra credentials as shown in [Foundry agents over A2A](./providers/azure_ai_agents#foundry-agents-over-a2a) + ### Add Vertex AI Agent Engine Follow [this guide, to add your Vertex AI Agent Engine to LiteLLM Agent Gateway](./providers/vertex_ai_agent_engine) diff --git a/docs/a2a_agent_card.md b/docs/a2a_agent_card.md index 7f007e5a9..c7ad3874b 100644 --- a/docs/a2a_agent_card.md +++ b/docs/a2a_agent_card.md @@ -48,6 +48,8 @@ The fields below mirror the A2A v1.0 specification ([§4.4 Agent Discovery Objec | `extensions` | ❌ | | `extendedAgentCard` | ❌ | +A `capabilities` block with no truthy `streaming` marks an agent that cannot stream: a streaming chat completion to it runs as one blocking `message/send` and is replayed as a single chunk. A card with no `capabilities` block keeps `message/stream` + ### AgentExtension (§4.4.4) | Field | Supported | @@ -92,7 +94,7 @@ The fields below mirror the A2A v1.0 specification ([§4.4 Agent Discovery Objec When you register an A2A agent in LiteLLM: 1. You provide a base URL (and, for some providers, an assistant identifier). -2. LiteLLM fetches the upstream agent card from the agent's `/.well-known/agent-card.json` (or the provider-specific equivalent). +2. LiteLLM fetches the upstream agent card from the agent's `/.well-known/agent-card.json`, then `/.well-known/agent.json`, then `/agentCard/v1.0`, stopping at the first path that answers. Set `agent_card_path` in the agent's `litellm_params` (for example `agentCard/v1.0`, the path Microsoft Foundry serves) to fetch that path directly. 3. You review the parsed card in the LiteLLM UI, choose which skills and fields to expose, and pick a **Protocol Version** (`1.0` or `0.3`) for clients. 4. LiteLLM saves the curated card and serves it at: diff --git a/docs/a2a_agent_headers.md b/docs/a2a_agent_headers.md index 3a9114a2e..2af53056a 100644 --- a/docs/a2a_agent_headers.md +++ b/docs/a2a_agent_headers.md @@ -179,6 +179,8 @@ Example: This ensures admin-controlled credentials cannot be overridden by client requests. +When LiteLLM mints the backend credential itself (a `databricks_oauth` block or Microsoft Entra fields on the agent), that minted `Authorization` wins over both sources, and a client header of the same name in any casing (`authorization`, `Authorization`) is dropped so the backend receives one `Authorization` line + --- ## Combining All Three Methods diff --git a/docs/providers/azure_ai_agents.md b/docs/providers/azure_ai_agents.md index 23ee5a395..12dd6507c 100644 --- a/docs/providers/azure_ai_agents.md +++ b/docs/providers/azure_ai_agents.md @@ -425,3 +425,60 @@ The agent responds with its capabilities. You can now interact with your Azure A - [Create Thread and Run API Reference](https://learn.microsoft.com/en-us/rest/api/aifoundry/aiagents/create-thread-and-run/create-thread-and-run) - [A2A Agent Gateway](../a2a.md) - [A2A Cost Tracking](../a2a_cost_tracking.md) + +## Foundry agents over A2A + +Agents created in the current Microsoft Foundry portal do not speak the Assistants API, so `azure_ai/agents/` answers `Invalid 'assistant_id'` for them. They expose an A2A endpoint instead, and LiteLLM registers them as plain A2A agents that authenticate with Microsoft Entra ID + +The endpoint is `https://.services.ai.azure.com/api/projects//agents//endpoint/protocols/a2a`. Foundry serves its agent card at `agentCard/v1.0` under it rather than at the well-known paths, and accepts only an Entra bearer for the `https://ai.azure.com/.default` scope, so the entry sets `agent_card_path` and the service principal fields: + +```yaml title="config.yaml" +agents: + - agent_name: foundry-agent + agent_card_params: + name: "Foundry Agent" + url: "https://.services.ai.azure.com/api/projects//agents//endpoint/protocols/a2a" + protocolVersion: "1.0" + capabilities: + streaming: false + litellm_params: + agent_card_path: agentCard/v1.0 + tenant_id: os.environ/AZURE_TENANT_ID + client_id: os.environ/AZURE_CLIENT_ID + client_secret: os.environ/AZURE_CLIENT_SECRET +``` + +`tenant_id`, `client_id` and `client_secret` are the names the `azure_ai` provider uses. A bearer you minted yourself goes in `azure_ad_token` (an `oidc/` federated token there needs `tenant_id` + `client_id` next to it), a user login in `client_id` + `azure_username` + `azure_password`, and `azure_scope` overrides the scope. Only the credentials on the agent are used; the process-wide `AZURE_*` variables are never read for an A2A agent. Without `agent_card_path`, discovery still reaches `agentCard/v1.0` after the two well-known paths fail, at the cost of two extra round trips on every call + +Foundry answers `message/send` with a submitted task unless the request asks for a blocking send, and it has no `message/stream`. The `capabilities.streaming: false` line makes LiteLLM run a streaming chat completion as one blocking send and replay the answer as a single chunk + +Call the agent over A2A with a blocking send: + +```bash +curl http://localhost:4000/a2a/foundry-agent \ + -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "id": "1", + "method": "message/send", + "params": { + "message": { + "kind": "message", + "role": "user", + "messageId": "m1", + "parts": [{"kind": "text", "text": "What is 2 + 2?"}] + }, + "configuration": {"blocking": true} + } + }' +``` + +Or through chat completions, where LiteLLM asks for the blocking send itself: + +```bash +curl http://localhost:4000/v1/chat/completions \ + -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ + -H "Content-Type: application/json" \ + -d '{"model": "a2a/foundry-agent", "messages": [{"role": "user", "content": "What is 2 + 2?"}]}' +```