From 5e7e8dcec0df6f8bb9650f406fff200b7b913c58 Mon Sep 17 00:00:00 2001 From: Nadine2016 Date: Tue, 10 Nov 2026 15:02:49 -0500 Subject: [PATCH 1/4] Various updates Signed-off-by: Nadine2016 --- .../kagent/latest/agents/egress-policies.md | 114 +++++++++++++++--- .../kagent/latest/examples/a2a-agents.md | 58 +++++++++ .../latest/get-started/your-first-agent.md | 10 +- .../operations/operational-considerations.md | 58 ++++++++- .../setup/model-providers/azure-ai-foundry.md | 44 +++++-- .../content/kagent/latest/ui/launch-ui.md | 3 + .../kagent/main/agents/egress-policies.md | 58 ++++++++- .../kagent/main/examples/a2a-agents.md | 58 +++++++++ .../main/get-started/your-first-agent.md | 10 +- .../operations/operational-considerations.md | 8 +- docs-site/content/kagent/main/ui/launch-ui.md | 3 + 11 files changed, 379 insertions(+), 45 deletions(-) diff --git a/docs-site/content/kagent/latest/agents/egress-policies.md b/docs-site/content/kagent/latest/agents/egress-policies.md index c4b625c0..5df1ba88 100644 --- a/docs-site/content/kagent/latest/agents/egress-policies.md +++ b/docs-site/content/kagent/latest/agents/egress-policies.md @@ -19,9 +19,46 @@ Create the resource that you want to grant access to. The examples on this page ## Grant access to a destination -Create an EgressPolicy that lists the destinations that you want to grant access to, and then select the policy on the Agent or SandboxTemplate. +Create an EgressPolicy that lists the destinations that you want to grant access to, and then select the policy on the Agent or SandboxTemplate. The following example shows the steps for an Agent. -1. Create the EgressPolicy in the namespace of the Agent or SandboxTemplate. A resource can select only the policies in its own namespace. This example grants access to the npm registry and the Node.js download site. +1. Check that the destination is blocked before you grant access. + {{< tabs >}} + {{% tab name="Agent" %}} + The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. + ```bash + export SESSION_ID=$(kagent agent session create --agent my-first-agent -o json | jq -r '.session.id') + kagent agent invoke --session $SESSION_ID \ + --task "Use your bash tool to run exactly this command, then reply with only its output: bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked'" + ``` + + Example output: + ```console + blocked + ``` + {{% /tab %}} + {{% tab name="Standalone sandbox" %}} + Create a Sandbox from the template, and run the check in it. The check uses `bash`, so the tools image of the template must include `bash`. + ```bash + export SANDBOX_ID=$(kagent sandbox create scratch --request-id egress-check -o json | jq -r '.id') + kagent sandbox exec $SANDBOX_ID -- bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked' + ``` + + Example output: + ```console + Process proc-5182872b055b6f61a9dc86cc in sandbox 0198c3f1-2a44-7c90-b5e1-9d8f3a7b2c04: started (stdout offset 0, stderr offset 0) + blocked + bash: line 1: registry.npmjs.org: Name or service not known + bash: line 1: /dev/tcp/registry.npmjs.org/443: Invalid argument + Process proc-5182872b055b6f61a9dc86cc: PROCESS_STATE_EXITED, exit_code=0 + ``` + + The process exits with code `0` in either case, so read the word that the command prints and not the exit code. + {{% /tab %}} + {{< /tabs >}} + + A destination that no grant allows does not resolve, so the connection fails. + +2. Create the EgressPolicy in the namespace of the Agent or SandboxTemplate. A resource can select only the policies in its own namespace. This example grants access to the npm registry and the Node.js download site. ```yaml kubectl apply -f - <}} {{% tab name="Agent" %}} The AgentTemplate and the Harness cannot select a policy, so two Agents that share an AgentTemplate can have different access. The grant applies to the whole {{< gloss "Actor" >}}Actor{{< /gloss >}}, including the subagents that run in it. @@ -62,7 +99,7 @@ Create an EgressPolicy that lists the destinations that you want to grant access {{% /tab %}} {{< /tabs >}} -3. Check that kagent admitted the grant. When kagent admits every selected policy, the `EgressPoliciesApplied` condition reports `True` with the reason `Applied`. +4. Check that kagent accepted the policy. The `EgressPoliciesApplied` condition reports `True` with the reason `Applied` when kagent has validated every selected policy and sent its rules to the egress gateway. The condition does not test a request, so it can report `True` for a moment before running Sessions pick up the change. {{< tabs >}} {{% tab name="Agent" %}} ```bash @@ -83,24 +120,38 @@ Create an EgressPolicy that lists the destinations that you want to grant access True Applied All requested egress grants are admitted ``` -The grant reaches existing workloads as well as new ones. kagent updates the policy of every running and suspended Actor without restarting it. For an Agent, this update covers every {{< gloss "Session" >}}Session{{< /gloss >}}. For a SandboxTemplate, the update covers every Sandbox that was created from the template. The update is asynchronous, so a request that is sent immediately after the change might still use the previous policy. - -## Origin format - -An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. +5. Send the same request again. The condition from the previous step does not test a request, so this result shows whether the Agent or Sandbox reaches the destination now. + {{< tabs >}} + {{% tab name="Agent" %}} + Use the same Session. + ```bash + kagent agent invoke --session $SESSION_ID \ + --task "Use your bash tool to run exactly this command, then reply with only its output: bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked'" + ``` -A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. + Example output: + ```console + reachable + ``` + {{% /tab %}} + {{% tab name="Standalone sandbox" %}} + Use the same Sandbox. It picks up the grant without being created again. + ```bash + kagent sandbox exec $SANDBOX_ID -- bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked' + ``` -A redirect to another origin needs its own grant. For example, a package registry often serves downloads from a separate host. An install fails at that host until the policy grants it as well. + Example output: + ```console + Process proc-360e1e86c15990081d976630 in sandbox 0198c3f1-2a44-7c90-b5e1-9d8f3a7b2c04: started (stdout offset 0, stderr offset 0) + reachable + Process proc-360e1e86c15990081d976630: PROCESS_STATE_EXITED, exit_code=0 + ``` + {{% /tab %}} + {{< /tabs >}} -The API server rejects an `egressPolicyRefs` list that selects more than 16 policies and a policy that lists more than 64 destinations. The compiled policy limit also counts the destinations that kagent inferred, so an Agent with many tools reaches it sooner. + The update is asynchronous, so if the result still says `blocked`, wait a few seconds and send the request again. -| Limit | Value | -| ----- | ----- | -| Policies that one Agent or SandboxTemplate selects | 16 | -| Destinations in one policy | 64 | -| Rules in the compiled policy of one Actor | 256 | -| Credential headers for one origin | 16 | +The grant reaches existing workloads as well as new ones. kagent updates the policy of every running and suspended Actor without restarting it. For an Agent, this update covers every {{< gloss "Session" >}}Session{{< /gloss >}}. For a SandboxTemplate, the update covers every Sandbox that was created from the template. The update is asynchronous, so a request that is sent immediately after the change might still use the previous policy. ## Inject a credential @@ -220,7 +271,7 @@ The gateway mints, injects, and refreshes the token, so the policy needs no Secr ## Troubleshoot a grant -The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations. +The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations. The UI follows the same rule, so an Agent whose condition is `False` does not appear as failed. The condition message names the affected policies, and the reason identifies the problem. @@ -242,6 +293,29 @@ A grant conflict happens in the following cases: In a standalone Sandbox, a destination that no grant allows does not resolve. A client therefore reports a name-resolution error, not an HTTP error. For example, `curl` fails with `Could not resolve host` and exit code `6`. For how to find a denied request in the gateway's log, see [Diagnose a denied request]({{< link path="about/substrate-runtime/networking-and-egress#diagnose-a-denied-request" >}}). +A destination that redirects to another origin needs a grant for that origin as well. For example, a package registry often serves downloads from a separate host, so an install fails at that host until the policy grants it too. + +## Origin format and limits {#origin-format} + +An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. + +A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. + +The API server rejects an `egressPolicyRefs` list that selects more than 16 policies and a policy that lists more than 64 destinations. The compiled policy limit also counts the destinations that kagent inferred, so an Agent with many tools reaches it sooner. + +| Limit | Value | +| ----- | ----- | +| Policies that one Agent or SandboxTemplate selects | 16 | +| Destinations in one policy | 64 | +| Rules in the compiled policy of one Actor | 256 | +| Credential headers for one origin | 16 | + +## Manage policies in the UI {#manage-policies-in-the-ui} + +You can create, edit, and delete EgressPolicy resources in the **Policies** page of the kagent UI without `kubectl`. The **Egress Policies** tab on the page lists the policies that you can read, with each policy's name, namespace, origins, and authentication type, which can be **None**, **Secret key**, or **Actor JWT**. You can filter the list by name, origins, or namespaces by using the search box. For how to open the UI, see [Launch the UI]({{< link path="ui/launch-ui" >}}). + +For who can reach this path, see [Access control](#access-control). + ## Access control An EgressPolicy is namespace-scoped, and kagent does not check who may select it. A user who can create or edit an Agent or a SandboxTemplate in a namespace can select every EgressPolicy in that namespace and use its credentials. Treat the namespace as the boundary for sharing policies and the Secrets that they reference. To give each team a namespace of its own, see [Run agents in multiple namespaces]({{< link path="operations/multiple-namespaces" >}}).{{% upstream %}} For more information about how Kubernetes role-based access control (RBAC) governs who can write these resources, see [The Kubernetes plane]({{< link path="about/substrate-runtime/identity#the-kubernetes-plane" >}}).{{% /upstream %}} diff --git a/docs-site/content/kagent/latest/examples/a2a-agents.md b/docs-site/content/kagent/latest/examples/a2a-agents.md index 10f10318..95610360 100644 --- a/docs-site/content/kagent/latest/examples/a2a-agents.md +++ b/docs-site/content/kagent/latest/examples/a2a-agents.md @@ -44,6 +44,7 @@ The following table covers the methods that this example uses. The `TaskPushNoti | `SendMessage` | Sends a message and returns the task after the agent either finishes the turn or pauses for a person. | | `SendStreamingMessage` | Sends a message and streams events as the agent works. | | `GetTask` | Reads a task that a previous call created. | +| `ListTasks` | Lists the tasks of one Session, or of every Session that the Agent has and the caller can see. | | `CancelTask` | Stops a task that is still running. | | `CreateTaskPushNotificationConfig` | Adds or replaces a callback for a task that is still active. | | `ListTaskPushNotificationConfigs` | Lists the active callbacks for one task. | @@ -217,6 +218,34 @@ A task outlives the call that created it, so a caller that lost its connection c A task that is still running reports `TASK_STATE_WORKING` and does not have artifacts yet. To stop a task that is still running, call `CancelTask` with the same `id`. +## List tasks {#list-tasks} + +`ListTasks` finds tasks without a task ID. Use it to recover a task ID that a caller lost, or to see what an Agent has run. + +Setting `contextId` lists the tasks of that one Session. Leaving it out lists the tasks of every Session that the Agent has and the caller can see. + +1. List the completed tasks of a Session, with a page size of 5. + ```bash + grpcurl -plaintext \ + -H 'A2A-Version: 1.0' \ + -d '{"tenant":"'"$AGENT"'","contextId":"'"$SESSION_ID"'","status":"TASK_STATE_COMPLETED","pageSize":5}' \ + localhost:8083 lf.a2a.v1.A2AService/ListTasks + ``` + +2. Narrow or shape the list with the following request fields. Every field is optional. + + | Field | Meaning | + | ----- | ------- | + | `contextId` | Limits the list to one Session. | + | `status` | Returns only the tasks in this state, such as `TASK_STATE_COMPLETED`. | + | `statusTimestampAfter` | Returns only the tasks whose status changed after this timestamp. | + | `pageSize` | Sets the number of tasks in a page. The default is `50`. A value above `100` fails with an invalid request error instead of being capped. | + | `pageToken` | Continues a list from the `nextPageToken` of the previous response. | + | `historyLength` | Keeps only the most recent messages of each task. Setting `0` returns none, and omitting the field returns every message. | + | `includeArtifacts` | Returns each task's artifacts. The default is `false`, so a list omits artifacts unless you ask for them. | + +3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller pages until it comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. + ## Read token usage {#read-token-usage} The `kagent` harness reports the model tokens that a task uses through the optional usage extension, `https://kagent.dev/extensions/usage/v1`. The agent card of an Agent on this harness advertises the extension. A `byo` image that is built with the Go or Python helper also reports usage, but the agent card does not advertise the extension. The `codex` and `claude` harnesses do not report usage. @@ -301,6 +330,35 @@ The kagent CLI wraps the push notification configuration methods, so this sectio kagent agent session push delete $SESSION_ID $PUSH_TASK_ID audit-webhook ``` +### Handle deliveries + +A delivery is an A2A `StreamResponse` that holds a `statusUpdate` with the task ID, the context ID, and the latest published status, including any status message. Ordinary progress updates do not trigger a callback. For example, the receiver gets this body when a task completes: + +```json +{ + "statusUpdate": { + "taskId": "task-123", + "contextId": "context-456", + "status": { "state": "TASK_STATE_COMPLETED" } + } +} +``` + +Build the receiver around these rules: + +* Any `2xx` response acknowledges a delivery. Every other response counts as a failed attempt, and a redirect is one of them, so register the final callback URL. +* A delivery can arrive more than once, or out of order. Treat `GetTask` as the source of truth for the task's current state. +* kagent makes up to ten attempts for each state that a task publishes, and the count carries across controller restarts. After the last attempt, kagent drops that delivery but keeps the callback registered, so the next state still reaches the receiver. +* kagent does not expose delivery status, so nothing reports whether a delivery failed. + +Three Helm values tune delivery. Set them under `config.push`. + +| Value | Default | Effect | +| ----- | ------- | ------ | +| `maxConcurrency` | `10` | The number of deliveries that each controller replica sends at once. Setting `1` sends them one at a time. | +| `sendTimeout` | `5s` | How long one HTTP attempt can take before it counts as failed. | +| `recoveryInterval` | `60s` | How often an idle controller scans for deliveries that a missed wake-up left pending. | + ### Secure callback deliveries A callback URL is a public endpoint that receives task state from kagent. Before you register a callback in production, decide how the receiver establishes that a delivery came from kagent. diff --git a/docs-site/content/kagent/latest/get-started/your-first-agent.md b/docs-site/content/kagent/latest/get-started/your-first-agent.md index f0b19a7c..99babb7e 100644 --- a/docs-site/content/kagent/latest/get-started/your-first-agent.md +++ b/docs-site/content/kagent/latest/get-started/your-first-agent.md @@ -23,6 +23,7 @@ Three resources define a runnable agent: a Harness holds the runtime, an AgentTe 1. Apply a `Harness` that uses kagent's native runtime. Its `substrate` section names the [WorkerPool]({{< link path="about/architecture/agent-substrate#workers-and-workerpools" >}}) that this Harness's Actors run on, and the object storage location for their [snapshots]({{< link path="about/architecture/agent-substrate#suspend-snapshot-and-resume" >}}). ```yaml + kubectl apply -f - <}}ModelConfig{{< /gloss >}} that was automatically created for the model provider API key that you provided during kagent installation. ```yaml + kubectl apply -f - < [!NOTE] > `spec.template` takes either a reference, as shown here, or a complete AgentTemplate spec under `inline`. An inline template is a complete value rather than an override, and kagent does not create a Kubernetes object to back it. `spec.harnessRef` takes a reference only, so a Harness must exist before an Agent can name it. @@ -123,9 +129,9 @@ A Session is one running conversation with an Agent. Creating it starts an Actor kagent agent invoke --session $SESSION_ID --task "What is 2+2?" ``` - The agent's reply prints as text. + The agent's reply prints as text. The wording of the reply depends on the model. ```console - 4 + 2+2 equals 4. ``` 2. Send a follow-up message to the same Session. A Session holds the {{< gloss "Transcript" >}}transcript{{< /gloss >}} of its conversation, so the agent answers with the earlier turns in context. diff --git a/docs-site/content/kagent/latest/operations/operational-considerations.md b/docs-site/content/kagent/latest/operations/operational-considerations.md index d72c6c97..ade4f4ef 100644 --- a/docs-site/content/kagent/latest/operations/operational-considerations.md +++ b/docs-site/content/kagent/latest/operations/operational-considerations.md @@ -210,7 +210,7 @@ Agent Substrate connects to the same database with its own owner and read/write ## Run multiple controller replicas -So that a controller failure does not stop reconciliation, set `controller.replicas` to a number higher than `1`. +To ensure that a controller failure does not stop reconciliation, increase the number of controller replicas by setting the `controller.replicas` Helm value to a number higher than `1`. ```yaml controller: @@ -222,6 +222,26 @@ Leader election keeps the replicas from conflicting. One replica takes a Kuberne > [!IMPORTANT] > If an installation runs on the development database, extra controller replicas do not make the installation highly available. That database is a single pod backed by one PersistentVolumeClaim, so losing it stops every replica at once. Run an external PostgreSQL instance before counting controller replicas toward availability. +Configure a [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) to ensure that a minimum number of control plane instances are up and running at any given time during voluntary disruptions, such as cluster upgrades or node drains. The controller's budget is off by default. + +The following example keeps two of three controller replicas available. Note that the two budget values are mutually exclusive, and the chart fails the install when both are set. Because `maxUnavailable` defaults to `1`, setting `minAvailable` also requires setting `maxUnavailable` to `null`, as shown in the example. + +```yaml +controller: + replicas: 3 + pdb: + enabled: true + minAvailable: 2 + maxUnavailable: null +``` + +| Value | Description | +| ----- | ----------- | +| `controller.pdb.enabled` | Set to `true` to create the budget. Defaults to `false`. | +| `controller.pdb.maxUnavailable` | The number of pods, or a percentage such as `"50%"`, that can be unavailable. Defaults to `1`. | +| `controller.pdb.minAvailable` | The number of pods, or a percentage, that must stay available. Unset by default. | + + ## Sign push notification callbacks Agent2Agent (A2A) push notifications are unsigned by default. A callback carries no `Authorization` header unless you supply an Ed25519 signing key, and the JSON Web Key Set (JWKS) endpoint returns `404`. Push notifications still work without a key, so sign them only where a receiver must verify that a callback came from your installation. @@ -310,6 +330,34 @@ ui: periodSeconds: 30 ``` +## Deploy extra resources with the release {#extra-objects} + +The chart can create resources of your own as part of the kagent release. Helm then installs, upgrades, and removes them together with kagent, so you do not maintain a separate chart for them. List each manifest under `extraObjects`. Typical uses are an ExternalSecret that fills the model provider Secret, an HTTPRoute, or a NetworkPolicy. + +Helm renders every entry with `tpl`, so a value can reference the release, such as `{{ include "kagent.fullname" . }}` or `{{ .Release.Namespace }}`. An entry can be a map or a multi-line string. + +The following example creates the `kagent-openai` Secret from an external secret store, in place of the Secret that [Install kagent with Helm]({{< link path="setup/installation#install-kagent-helm" >}}) has you create by hand. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. + +```yaml +extraObjects: + - apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: '{{ include "kagent.fullname" . }}-openai' + namespace: '{{ .Release.Namespace }}' + spec: + secretStoreRef: + name: aws-secretsmanager + kind: ClusterSecretStore + target: + name: kagent-openai + data: + - secretKey: OPENAI_API_KEY + remoteRef: + key: prod/kagent/openai + property: api_key +``` + ## Node operations A {{< gloss "Worker" >}}Worker{{< /gloss >}} is an ordinary Kubernetes pod, so every node event reaches the {{< gloss "Actor" >}}Actors{{< /gloss >}} that this Worker hosts. What the event costs depends on what each Actor was doing when the node went away. @@ -317,11 +365,11 @@ A {{< gloss "Worker" >}}Worker{{< /gloss >}} is an ordinary Kubernetes pod, so e * A suspended Actor costs nothing. Its state is already written to snapshot storage, and it occupies no Worker while it waits, so the next message resumes it onto whichever Worker in the pool has availability. * An Actor that is still running a turn is lost. The Session's in-flight execution state lives in the Actor's sandbox memory on the Worker, and Agent Substrate writes nothing new to snapshot storage until this Actor suspends. -A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses `resume` and `suspend` on an Actor in this state. Through kagent, deleting the Actor and starting a new {{< gloss "Session" >}}Session{{< /gloss >}} is the only available option, so the Session ends with the node. +A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses the `resume` and `suspend` operation on an Actor in this state. kagent does not recover the Actor on its own, so the {{< gloss "Session" >}}Session{{< /gloss >}} stays crashed until a client asks for recovery. - +A client with update access to the Session can recover a crashed agent by calling `RevertSession` on the `SessionService` gRPC API. The call restores the Actor to its last durable snapshot and marks the turn that was running as failed. If the Actor is missing or was replaced, the call fails with `FAILED_PRECONDITION`, and starting a new Session is the only option. Neither the `kagent` CLI nor the UI offers a way to revert a Session. -Many Actors on a node are suspended at any moment, so losing the node crashes only the ones that are mid-turn. Those Actors still lose their Sessions, and a node pool that runs Workers therefore needs stricter rules than the rest of the cluster. A Worker pod that Kubernetes deletes gives its Actors time to suspend. Node auto-upgrade, a spot reclaim, and a pool scale-down do not. +Many Actors on a node are suspended at any moment, so losing the node crashes only the ones that are mid-turn. Those Actors lose everything since their last snapshot, and a node pool that runs Workers therefore needs stricter rules than the rest of the cluster. A Worker pod that Kubernetes deletes gives its Actors time to suspend. Node auto-upgrade, a spot reclaim, and a pool scale-down do not. ### Graceful pod deletion @@ -339,7 +387,7 @@ gcloud container node-pools update "${NODE_POOL}" \ ### Spot and preemptible nodes -A provider reclaims spot and preemptible nodes on its own schedule, and each reclaim crashes every Actor that was awake on the node. A pool on reclaimable capacity therefore trades a lower bill for Sessions that end without warning. Use these nodes only where a lost turn is acceptable for the agents on the pool. +A provider reclaims spot and preemptible nodes on its own schedule, and each reclaim crashes every Actor that was awake on the node. A pool on reclaimable capacity therefore trades a lower bill for turns that fail without warning. Use these nodes only where a lost turn is acceptable for the agents on the pool. ### WorkerPool scale-down diff --git a/docs-site/content/kagent/latest/setup/model-providers/azure-ai-foundry.md b/docs-site/content/kagent/latest/setup/model-providers/azure-ai-foundry.md index 6c5fe67f..a45db5c8 100644 --- a/docs-site/content/kagent/latest/setup/model-providers/azure-ai-foundry.md +++ b/docs-site/content/kagent/latest/setup/model-providers/azure-ai-foundry.md @@ -5,7 +5,7 @@ weight: 20 author: kagent.dev --- -The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. The chat model must be reachable through Foundry's OpenAI-compatible chat completions API. +The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. By default, the chat model must be reachable through Foundry's OpenAI-compatible chat completions API. To call a Claude model, which Foundry serves over the Anthropic Messages API, set `foundry.apiFormat` to `Anthropic`. For more information, see [Use a Claude model](#use-a-claude-model). ## Create the ModelConfig @@ -42,13 +42,15 @@ The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. | Field | Description | | ----- | ----------- | | `apiKeySecret` | The name of the Kubernetes Secret that stores the API key, in the same namespace as this ModelConfig. | - | `apiKeySecretKey` | The key within that Secret that holds the API key. | + | `apiKeySecretKey` | The key within that Secret that holds the API key. For other ways to authenticate, see [Authentication](#authentication). | | `model` | The model that the deployment serves. | | `provider` | The provider to use, `Foundry`. | - | `foundry.endpoint` | The endpoint of your Foundry account. | + | `foundry.endpoint` | The endpoint of your Foundry account. To read it from a ConfigMap instead, see [Read the endpoint from a ConfigMap](#read-the-endpoint-from-a-configmap). | | `foundry.deployment` | The name of the deployment within the account. This field is required. | | `foundry.apiVersion` | The Foundry API version to call. Defaults to `2024-10-21`. | + For the other `foundry` settings, see [Azure AI Foundry provider settings](#azure-ai-foundry-provider-settings). + ## Azure AI Foundry provider settings The `foundry` block takes the following settings. For every field, including its type, default, and validation rules, see the [API reference]({{< link path="reference/api-ref#foundryconfig" >}}). @@ -57,8 +59,9 @@ The `foundry` block takes the following settings. For every field, including its | ----- | ----------- | | `deployment` | The deployment name within the Foundry account. Required. | | `endpoint` | The endpoint of the Foundry account. | -| `endpointFrom` | Read the endpoint from a key in a ConfigMap instead of setting it inline. Use this field when the endpoint differs per environment and is published by a platform team. | -| `apiVersion` | The Foundry API version. Defaults to `2024-10-21`. | +| `endpointFrom` | Read the endpoint from a key in a ConfigMap instead of setting it inline. Use this field when the endpoint differs per environment and is published by a platform team. For more information, see [Read the endpoint from a ConfigMap](#read-the-endpoint-from-a-configmap). | +| `apiVersion` | The Foundry API version. Defaults to `2024-10-21`. kagent ignores this field when `apiFormat` is `Anthropic`. | +| `apiFormat` | The API that kagent calls on the deployment. `OpenAI`, the default, calls the OpenAI-compatible chat completions API. `Anthropic` calls the Anthropic Messages API, which serves Claude models. For more information, see [Use a Claude model](#use-a-claude-model). | ### Read the endpoint from a ConfigMap @@ -83,6 +86,31 @@ When the named ConfigMap or key does not exist, the {{< gloss "ModelConfig" >}}M kubectl describe modelconfig foundry-model-config -n kagent ``` +## Use a Claude model {#use-a-claude-model} + +Foundry serves Claude models over the Anthropic Messages API and not over the OpenAI-compatible API. Set `foundry.apiFormat` to `Anthropic` so that kagent calls the Messages API. The example reuses the Secret that [Create the ModelConfig](#create-the-modelconfig) creates. + +```yaml +kubectl apply -f - <.cognitiveservices.azure.com/ + deployment: claude-sonnet-4-5 + apiFormat: Anthropic +EOF +``` + +kagent sends each request to `/anthropic/v1/messages` and puts the deployment name in the request's `model` field. The `apiVersion` field has no effect, because the Messages API takes its version from a request header instead. Authentication works as it does for any other Foundry deployment, as described in [Authentication](#authentication). + ## Authentication | Configuration | Credential | @@ -106,7 +134,7 @@ spec: ## Memory embeddings -A Foundry deployment can also serve the embedding model behind [long-term memory]({{< link path="agents/agent-memory" >}}). Point a second ModelConfig at your embedding deployment, then name it from the Harness's `spec.kagent.memory.modelConfigRef`. +A Foundry deployment can also serve the embedding model behind [long-term memory]({{< link path="agents/agent-memory" >}}). Point a second ModelConfig at your embedding deployment, reusing the Secret that [Create the ModelConfig](#create-the-modelconfig) creates, then name it from the Harness's `spec.kagent.memory.modelConfigRef`. ```yaml kubectl apply -f - <}}). | | **Models** | The {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} resources that agents reference, and the providers behind them. | | **MCP Servers** | Connected {{< gloss "RemoteMCPServer" >}}RemoteMCPServer{{< /gloss >}} resources and the tools discovered from each. | +| **Policies** | The [EgressPolicy]({{< link path="agents/egress-policies" >}}) resources, which list the extra HTTP(S) hosts that agents and sandbox templates can reach. The page has one tab, **Egress Policies**. For more information, see [Manage policies in the UI]({{< link path="agents/egress-policies#manage-policies-in-the-ui" >}}). | | **Prompts** | Prompt libraries, which store reusable fragments that an AgentTemplate includes in its instructions. | | **Substrate** | WorkerPools, Actors, and Workers, read from both Kubernetes and the Agent Substrate API. | @@ -113,6 +114,8 @@ Select the mark to open the record behind it. The record shows the checkpoint's The bell icon on the latest message of a task opens **Task notifications**. Use it to add, edit, or remove the callback URLs that kagent posts task updates to. A new callback receives only the updates that its task sends after you register it, and only when the task needs input or reaches a final state. After the task finishes, you can view and remove its callbacks, but you cannot add or edit them. For how kagent delivers and signs a callback, see [Register task callbacks]({{< link path="examples/a2a-agents#task-callbacks" >}}). +The UI renders an agent's replies as markdown. A fenced code block whose language is `mermaid` appears as a diagram, in the light or dark theme that the UI uses. While a diagram loads, or when its source does not parse, the UI shows the source in a code block, so a reply never loses content. + ## Attach files to a message In the chat interface for agents on the `kagent` runtime, you can attach files to send with a message. An agent reads any files that you attach, so you can ask the agent about a log, a manifest, or a screenshot without pasting the contents into the message box. diff --git a/docs-site/content/kagent/main/agents/egress-policies.md b/docs-site/content/kagent/main/agents/egress-policies.md index c4b625c0..f2dd8ad0 100644 --- a/docs-site/content/kagent/main/agents/egress-policies.md +++ b/docs-site/content/kagent/main/agents/egress-policies.md @@ -21,7 +21,21 @@ Create the resource that you want to grant access to. The examples on this page Create an EgressPolicy that lists the destinations that you want to grant access to, and then select the policy on the Agent or SandboxTemplate. -1. Create the EgressPolicy in the namespace of the Agent or SandboxTemplate. A resource can select only the policies in its own namespace. This example grants access to the npm registry and the Node.js download site. +1. For an Agent, check that the destination is blocked before you grant access. The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. + ```bash + export SESSION_ID=$(kagent agent session create --agent my-first-agent -o json | jq -r '.session.id') + kagent agent invoke --session $SESSION_ID \ + --task "Use your bash tool to run exactly this command, then reply with only its output: bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked'" + ``` + + Example output: + ```console + blocked + ``` + + A destination that no grant allows does not resolve, so the connection fails. Because a model runs the command, repeat the request if the reply describes the result in other words instead of printing it. + +2. Create the EgressPolicy in the namespace of the Agent or SandboxTemplate. A resource can select only the policies in its own namespace. This example grants access to the npm registry and the Node.js download site. ```yaml kubectl apply -f - <}} {{% tab name="Agent" %}} The AgentTemplate and the Harness cannot select a policy, so two Agents that share an AgentTemplate can have different access. The grant applies to the whole {{< gloss "Actor" >}}Actor{{< /gloss >}}, including the subagents that run in it. @@ -62,7 +76,7 @@ Create an EgressPolicy that lists the destinations that you want to grant access {{% /tab %}} {{< /tabs >}} -3. Check that kagent admitted the grant. When kagent admits every selected policy, the `EgressPoliciesApplied` condition reports `True` with the reason `Applied`. +4. Check that kagent accepted the policy. The `EgressPoliciesApplied` condition reports `True` with the reason `Applied` when kagent has validated every selected policy and sent its rules to the egress gateway. The condition does not test a request, so it can report `True` for a moment before running Sessions pick up the change. {{< tabs >}} {{% tab name="Agent" %}} ```bash @@ -83,6 +97,19 @@ Create an EgressPolicy that lists the destinations that you want to grant access True Applied All requested egress grants are admitted ``` +5. For an Agent, send the same request again in the same Session. The condition from the previous step does not test a request, so this result shows whether the Agent reaches the destination now. + ```bash + kagent agent invoke --session $SESSION_ID \ + --task "Use your bash tool to run exactly this command, then reply with only its output: bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked'" + ``` + + Example output: + ```console + reachable + ``` + + The update is asynchronous, so if the reply still says `blocked`, wait a few seconds and send the request again. + The grant reaches existing workloads as well as new ones. kagent updates the policy of every running and suspended Actor without restarting it. For an Agent, this update covers every {{< gloss "Session" >}}Session{{< /gloss >}}. For a SandboxTemplate, the update covers every Sandbox that was created from the template. The update is asynchronous, so a request that is sent immediately after the change might still use the previous policy. ## Origin format @@ -218,9 +245,32 @@ The gateway mints, injects, and refreshes the token, so the policy needs no Secr https://tickets.example.com/tickets ``` +## Manage policies in the UI {#manage-policies-in-the-ui} + +The **Policies** page of the kagent UI creates, edits, and deletes EgressPolicy resources without `kubectl`. Its **Egress Policies** tab lists the policies that you can read, with each policy's name, namespace, origins, and authentication type, which is **None**, **Secret key**, or **Actor JWT**. A search box matches names and origins, and a namespace filter narrows the list. For how to open the UI, see [Launch the UI]({{< link path="ui/launch-ui" >}}). + +Selecting a policy opens its details page. The page lists the destinations, with the origin, header, prefix, and credential of each. A **Used by** table lists the Agents and SandboxTemplates in the policy's namespace that select the policy, and its **Applied** column reports **Applied**, **Not applied**, or **Not reported yet** for each one. The table counts only the resources that you can see, so the numbers can be lower than the real ones. + +To create a policy, select **Create egress policy**. + +1. Enter a name and choose a namespace. Neither can change after you create the policy. + +2. Add one destination for each origin that agents need, up to 64 per policy. Enter the origin in the [format described earlier](#origin-format), and optionally set authentication. A **Secret key** sets the header and prefix, and names a Secret and key in the policy's namespace. An **Actor JWT** sets the audiences and the token lifetime, which is between 300 and 3600 seconds and defaults to 900. For what each field does, see [Inject a credential](#inject-a-credential). + +3. Select **Create policy**. + +Before the form saves, it checks the policy against the API server's rules and lists any problems under **Fix these before you save**. For example, the form blocks a wildcard origin that has authentication. + +Editing a policy replaces its destinations. When someone else saves the policy after you open it, the form reports that your copy is out of date and asks you to reload before you redo your changes. Deleting a policy that Agents or SandboxTemplates still select warns you first, because they lose access to the hosts that the policy allows as soon as you delete it. + +To attach a policy to an Agent, select it in the **Egress policies** field of the Agent form, which lists the policies in the Agent's namespace. The Agent's details page links back to the policies that it uses. To check that the Agent reaches the destination, send the request from [Grant access to a destination](#grant-access-to-a-destination) before and after you select the policy. + +> [!NOTE] +> The UI writes EgressPolicy resources through the controller, not through your own Kubernetes credentials. For who can reach that path, see [Access control](#access-control). + ## Troubleshoot a grant -The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations. +The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations. The UI follows the same rule, so an Agent whose condition is `False` does not appear as failed. The condition message names the affected policies, and the reason identifies the problem. diff --git a/docs-site/content/kagent/main/examples/a2a-agents.md b/docs-site/content/kagent/main/examples/a2a-agents.md index 10f10318..86da0c52 100644 --- a/docs-site/content/kagent/main/examples/a2a-agents.md +++ b/docs-site/content/kagent/main/examples/a2a-agents.md @@ -44,6 +44,7 @@ The following table covers the methods that this example uses. The `TaskPushNoti | `SendMessage` | Sends a message and returns the task after the agent either finishes the turn or pauses for a person. | | `SendStreamingMessage` | Sends a message and streams events as the agent works. | | `GetTask` | Reads a task that a previous call created. | +| `ListTasks` | Lists the tasks of one Session, or of every Session that the Agent has and the caller can see. | | `CancelTask` | Stops a task that is still running. | | `CreateTaskPushNotificationConfig` | Adds or replaces a callback for a task that is still active. | | `ListTaskPushNotificationConfigs` | Lists the active callbacks for one task. | @@ -217,6 +218,34 @@ A task outlives the call that created it, so a caller that lost its connection c A task that is still running reports `TASK_STATE_WORKING` and does not have artifacts yet. To stop a task that is still running, call `CancelTask` with the same `id`. +## List tasks {#list-tasks} + +`ListTasks` finds tasks without a task ID. Use it to recover a task ID that a caller lost, or to see what an Agent has run. + +Setting `contextId` lists the tasks of that one Session. Leaving it out lists the tasks of every Session that the Agent has and the caller can see. + +1. List the completed tasks of a Session, with a page size of 5. + ```bash + grpcurl -plaintext \ + -H 'A2A-Version: 1.0' \ + -d '{"tenant":"'"$AGENT"'","contextId":"'"$SESSION_ID"'","status":"TASK_STATE_COMPLETED","pageSize":5}' \ + localhost:8083 lf.a2a.v1.A2AService/ListTasks + ``` + +2. Narrow or shape the list with the request fields. Every field is optional. + + | Field | Meaning | + | ----- | ------- | + | `contextId` | Limits the list to one Session. | + | `status` | Returns only the tasks in this state, such as `TASK_STATE_COMPLETED`. | + | `statusTimestampAfter` | Returns only the tasks whose status changed after this timestamp. | + | `pageSize` | Sets the number of tasks in a page. The default is `50`. A value above `100` fails with an invalid request error instead of being capped. | + | `pageToken` | Continues a list from the `nextPageToken` of the previous response. | + | `historyLength` | Keeps only the most recent messages of each task. Setting `0` returns none, and omitting the field returns every message. | + | `includeArtifacts` | Returns each task's artifacts. The default is `false`, so a list omits artifacts unless you ask for them. | + +3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller pages until it comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. + ## Read token usage {#read-token-usage} The `kagent` harness reports the model tokens that a task uses through the optional usage extension, `https://kagent.dev/extensions/usage/v1`. The agent card of an Agent on this harness advertises the extension. A `byo` image that is built with the Go or Python helper also reports usage, but the agent card does not advertise the extension. The `codex` and `claude` harnesses do not report usage. @@ -301,6 +330,35 @@ The kagent CLI wraps the push notification configuration methods, so this sectio kagent agent session push delete $SESSION_ID $PUSH_TASK_ID audit-webhook ``` +### Handle deliveries + +A delivery is an A2A `StreamResponse` that holds a `statusUpdate` with the task ID, the context ID, and the latest published status, including any status message. Ordinary progress updates do not trigger a callback. For example, the receiver gets this body when a task completes: + +```json +{ + "statusUpdate": { + "taskId": "task-123", + "contextId": "context-456", + "status": { "state": "TASK_STATE_COMPLETED" } + } +} +``` + +Build the receiver around these rules: + +* Any `2xx` response acknowledges a delivery. Every other response counts as a failed attempt, and a redirect is one of them, so register the final callback URL. +* A delivery can arrive more than once, or out of order. Treat `GetTask` as the source of truth for the task's current state. +* kagent makes up to ten attempts for each state that a task publishes, and the count carries across controller restarts. After the last attempt, kagent drops that delivery but keeps the callback registered, so the next state still reaches the receiver. +* kagent does not expose delivery status, so nothing reports whether a delivery failed. + +Three Helm values tune delivery. Set them under `config.push`. + +| Value | Default | Effect | +| ----- | ------- | ------ | +| `maxConcurrency` | `10` | The number of deliveries that each controller replica sends at once. Setting `1` sends them one at a time. | +| `sendTimeout` | `5s` | How long one HTTP attempt can take before it counts as failed. | +| `recoveryInterval` | `60s` | How often an idle controller scans for deliveries that a missed wake-up left pending. | + ### Secure callback deliveries A callback URL is a public endpoint that receives task state from kagent. Before you register a callback in production, decide how the receiver establishes that a delivery came from kagent. diff --git a/docs-site/content/kagent/main/get-started/your-first-agent.md b/docs-site/content/kagent/main/get-started/your-first-agent.md index f0b19a7c..99babb7e 100644 --- a/docs-site/content/kagent/main/get-started/your-first-agent.md +++ b/docs-site/content/kagent/main/get-started/your-first-agent.md @@ -23,6 +23,7 @@ Three resources define a runnable agent: a Harness holds the runtime, an AgentTe 1. Apply a `Harness` that uses kagent's native runtime. Its `substrate` section names the [WorkerPool]({{< link path="about/architecture/agent-substrate#workers-and-workerpools" >}}) that this Harness's Actors run on, and the object storage location for their [snapshots]({{< link path="about/architecture/agent-substrate#suspend-snapshot-and-resume" >}}). ```yaml + kubectl apply -f - <}}ModelConfig{{< /gloss >}} that was automatically created for the model provider API key that you provided during kagent installation. ```yaml + kubectl apply -f - < [!NOTE] > `spec.template` takes either a reference, as shown here, or a complete AgentTemplate spec under `inline`. An inline template is a complete value rather than an override, and kagent does not create a Kubernetes object to back it. `spec.harnessRef` takes a reference only, so a Harness must exist before an Agent can name it. @@ -123,9 +129,9 @@ A Session is one running conversation with an Agent. Creating it starts an Actor kagent agent invoke --session $SESSION_ID --task "What is 2+2?" ``` - The agent's reply prints as text. + The agent's reply prints as text. The wording of the reply depends on the model. ```console - 4 + 2+2 equals 4. ``` 2. Send a follow-up message to the same Session. A Session holds the {{< gloss "Transcript" >}}transcript{{< /gloss >}} of its conversation, so the agent answers with the earlier turns in context. diff --git a/docs-site/content/kagent/main/operations/operational-considerations.md b/docs-site/content/kagent/main/operations/operational-considerations.md index d72c6c97..de0c14ce 100644 --- a/docs-site/content/kagent/main/operations/operational-considerations.md +++ b/docs-site/content/kagent/main/operations/operational-considerations.md @@ -317,11 +317,11 @@ A {{< gloss "Worker" >}}Worker{{< /gloss >}} is an ordinary Kubernetes pod, so e * A suspended Actor costs nothing. Its state is already written to snapshot storage, and it occupies no Worker while it waits, so the next message resumes it onto whichever Worker in the pool has availability. * An Actor that is still running a turn is lost. The Session's in-flight execution state lives in the Actor's sandbox memory on the Worker, and Agent Substrate writes nothing new to snapshot storage until this Actor suspends. -A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses `resume` and `suspend` on an Actor in this state. Through kagent, deleting the Actor and starting a new {{< gloss "Session" >}}Session{{< /gloss >}} is the only available option, so the Session ends with the node. +A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses `resume` and `suspend` on an Actor in this state. kagent does not recover the Actor on its own, so the {{< gloss "Session" >}}Session{{< /gloss >}} stays crashed until a client asks for recovery. - +A client with update access to the Session recovers it by calling `RevertSession` on the `SessionService` gRPC API. The call restores the Actor to its last durable snapshot and marks the turn that was running as failed. Calling it on a Session whose Actor is not crashed returns the Session unchanged, so a retry is safe. If the Actor is missing or was replaced, the call fails with `FAILED_PRECONDITION`, and starting a new Session is the only option. Neither the `kagent` CLI nor the UI offers a way to revert a Session. -Many Actors on a node are suspended at any moment, so losing the node crashes only the ones that are mid-turn. Those Actors still lose their Sessions, and a node pool that runs Workers therefore needs stricter rules than the rest of the cluster. A Worker pod that Kubernetes deletes gives its Actors time to suspend. Node auto-upgrade, a spot reclaim, and a pool scale-down do not. +Many Actors on a node are suspended at any moment, so losing the node crashes only the ones that are mid-turn. Those Actors lose everything since their last snapshot, and a node pool that runs Workers therefore needs stricter rules than the rest of the cluster. A Worker pod that Kubernetes deletes gives its Actors time to suspend. Node auto-upgrade, a spot reclaim, and a pool scale-down do not. ### Graceful pod deletion @@ -339,7 +339,7 @@ gcloud container node-pools update "${NODE_POOL}" \ ### Spot and preemptible nodes -A provider reclaims spot and preemptible nodes on its own schedule, and each reclaim crashes every Actor that was awake on the node. A pool on reclaimable capacity therefore trades a lower bill for Sessions that end without warning. Use these nodes only where a lost turn is acceptable for the agents on the pool. +A provider reclaims spot and preemptible nodes on its own schedule, and each reclaim crashes every Actor that was awake on the node. A pool on reclaimable capacity therefore trades a lower bill for turns that fail without warning. Use these nodes only where a lost turn is acceptable for the agents on the pool. ### WorkerPool scale-down diff --git a/docs-site/content/kagent/main/ui/launch-ui.md b/docs-site/content/kagent/main/ui/launch-ui.md index 1b4bfc26..4224b35f 100644 --- a/docs-site/content/kagent/main/ui/launch-ui.md +++ b/docs-site/content/kagent/main/ui/launch-ui.md @@ -79,6 +79,7 @@ The left menu groups the console pages by the resource that each page reads. | **Schedules** | Agents that receive a fixed prompt on a cron schedule. Each execution starts a new conversation. For more information, see [Scheduled runs]({{< link path="agents/scheduled-runs" >}}). | | **Models** | The {{< gloss "ModelConfig" >}}ModelConfig{{< /gloss >}} resources that agents reference, and the providers behind them. | | **MCP Servers** | Connected {{< gloss "RemoteMCPServer" >}}RemoteMCPServer{{< /gloss >}} resources and the tools discovered from each. | +| **Policies** | The [EgressPolicy]({{< link path="agents/egress-policies" >}}) resources, which list the extra HTTP(S) hosts that agents and sandbox templates can reach. The page has one tab, **Egress Policies**. For more information, see [Manage policies in the UI]({{< link path="agents/egress-policies#manage-policies-in-the-ui" >}}). | | **Prompts** | Prompt libraries, which store reusable fragments that an AgentTemplate includes in its instructions. | | **Substrate** | WorkerPools, Actors, and Workers, read from both Kubernetes and the Agent Substrate API. | @@ -113,6 +114,8 @@ Select the mark to open the record behind it. The record shows the checkpoint's The bell icon on the latest message of a task opens **Task notifications**. Use it to add, edit, or remove the callback URLs that kagent posts task updates to. A new callback receives only the updates that its task sends after you register it, and only when the task needs input or reaches a final state. After the task finishes, you can view and remove its callbacks, but you cannot add or edit them. For how kagent delivers and signs a callback, see [Register task callbacks]({{< link path="examples/a2a-agents#task-callbacks" >}}). +The UI renders an agent's replies as markdown. A fenced code block whose language is `mermaid` appears as a diagram, in the light or dark theme that the UI uses. While a diagram loads, or when its source does not parse, the UI shows the source in a code block, so a reply never loses content. + ## Attach files to a message In the chat interface for agents on the `kagent` runtime, you can attach files to send with a message. An agent reads any files that you attach, so you can ask the agent about a log, a manifest, or a screenshot without pasting the contents into the message box. From ad9520f11ba1ea826e331dc63dad40f31536e758 Mon Sep 17 00:00:00 2001 From: Nadine2016 Date: Tue, 10 Nov 2026 15:10:11 -0500 Subject: [PATCH 2/4] Fixes Signed-off-by: Nadine2016 --- .../kagent/latest/agents/egress-policies.md | 2 +- .../kagent/main/agents/egress-policies.md | 118 +++++++++++------- .../kagent/main/examples/a2a-agents.md | 2 +- .../operations/operational-considerations.md | 54 +++++++- .../setup/model-providers/azure-ai-foundry.md | 44 +++++-- 5 files changed, 160 insertions(+), 60 deletions(-) diff --git a/docs-site/content/kagent/latest/agents/egress-policies.md b/docs-site/content/kagent/latest/agents/egress-policies.md index 5df1ba88..aa6987d0 100644 --- a/docs-site/content/kagent/latest/agents/egress-policies.md +++ b/docs-site/content/kagent/latest/agents/egress-policies.md @@ -81,7 +81,7 @@ Create an EgressPolicy that lists the destinations that you want to grant access | `destinations[].origin` | A scheme, a DNS hostname, and an optional port, such as `https://registry.npmjs.org` or `https://search.example.com:8443`. An origin has no path, and a wildcard can replace only the leftmost label of the hostname, such as `https://*.example.com`. For all of the accepted forms and the limits, see [Origin format and limits](#origin-format). | | `destinations[].authentication` | Optional. A credential for the gateway to add to each request. For more information, see [Inject a credential](#inject-a-credential). | -3. Update the Agent or SandboxTemplate to referencethe policy in the `spec.egressPolicyRefs` field. +3. Update the Agent or SandboxTemplate to reference the policy in the `spec.egressPolicyRefs` field. {{< tabs >}} {{% tab name="Agent" %}} The AgentTemplate and the Harness cannot select a policy, so two Agents that share an AgentTemplate can have different access. The grant applies to the whole {{< gloss "Actor" >}}Actor{{< /gloss >}}, including the subagents that run in it. diff --git a/docs-site/content/kagent/main/agents/egress-policies.md b/docs-site/content/kagent/main/agents/egress-policies.md index f2dd8ad0..aa6987d0 100644 --- a/docs-site/content/kagent/main/agents/egress-policies.md +++ b/docs-site/content/kagent/main/agents/egress-policies.md @@ -19,9 +19,12 @@ Create the resource that you want to grant access to. The examples on this page ## Grant access to a destination -Create an EgressPolicy that lists the destinations that you want to grant access to, and then select the policy on the Agent or SandboxTemplate. +Create an EgressPolicy that lists the destinations that you want to grant access to, and then select the policy on the Agent or SandboxTemplate. The following example shows the steps for an Agent. -1. For an Agent, check that the destination is blocked before you grant access. The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. +1. Check that the destination is blocked before you grant access. + {{< tabs >}} + {{% tab name="Agent" %}} + The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. ```bash export SESSION_ID=$(kagent agent session create --agent my-first-agent -o json | jq -r '.session.id') kagent agent invoke --session $SESSION_ID \ @@ -32,8 +35,28 @@ Create an EgressPolicy that lists the destinations that you want to grant access ```console blocked ``` + {{% /tab %}} + {{% tab name="Standalone sandbox" %}} + Create a Sandbox from the template, and run the check in it. The check uses `bash`, so the tools image of the template must include `bash`. + ```bash + export SANDBOX_ID=$(kagent sandbox create scratch --request-id egress-check -o json | jq -r '.id') + kagent sandbox exec $SANDBOX_ID -- bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked' + ``` - A destination that no grant allows does not resolve, so the connection fails. Because a model runs the command, repeat the request if the reply describes the result in other words instead of printing it. + Example output: + ```console + Process proc-5182872b055b6f61a9dc86cc in sandbox 0198c3f1-2a44-7c90-b5e1-9d8f3a7b2c04: started (stdout offset 0, stderr offset 0) + blocked + bash: line 1: registry.npmjs.org: Name or service not known + bash: line 1: /dev/tcp/registry.npmjs.org/443: Invalid argument + Process proc-5182872b055b6f61a9dc86cc: PROCESS_STATE_EXITED, exit_code=0 + ``` + + The process exits with code `0` in either case, so read the word that the command prints and not the exit code. + {{% /tab %}} + {{< /tabs >}} + + A destination that no grant allows does not resolve, so the connection fails. 2. Create the EgressPolicy in the namespace of the Agent or SandboxTemplate. A resource can select only the policies in its own namespace. This example grants access to the npm registry and the Node.js download site. ```yaml @@ -55,10 +78,10 @@ Create an EgressPolicy that lists the destinations that you want to grant access | Field | Description | | ----- | ----------- | | `destinations` | The destinations to grant, from 1 to 64 entries. | - | `destinations[].origin` | A scheme, a DNS hostname, and an optional port, such as `https://registry.npmjs.org` or `https://search.example.com:8443`. For the accepted forms, see [Origin format](#origin-format). | + | `destinations[].origin` | A scheme, a DNS hostname, and an optional port, such as `https://registry.npmjs.org` or `https://search.example.com:8443`. An origin has no path, and a wildcard can replace only the leftmost label of the hostname, such as `https://*.example.com`. For all of the accepted forms and the limits, see [Origin format and limits](#origin-format). | | `destinations[].authentication` | Optional. A credential for the gateway to add to each request. For more information, see [Inject a credential](#inject-a-credential). | -3. Select the policy in the `spec.egressPolicyRefs` field. +3. Update the Agent or SandboxTemplate to reference the policy in the `spec.egressPolicyRefs` field. {{< tabs >}} {{% tab name="Agent" %}} The AgentTemplate and the Harness cannot select a policy, so two Agents that share an AgentTemplate can have different access. The grant applies to the whole {{< gloss "Actor" >}}Actor{{< /gloss >}}, including the subagents that run in it. @@ -97,7 +120,10 @@ Create an EgressPolicy that lists the destinations that you want to grant access True Applied All requested egress grants are admitted ``` -5. For an Agent, send the same request again in the same Session. The condition from the previous step does not test a request, so this result shows whether the Agent reaches the destination now. +5. Send the same request again. The condition from the previous step does not test a request, so this result shows whether the Agent or Sandbox reaches the destination now. + {{< tabs >}} + {{% tab name="Agent" %}} + Use the same Session. ```bash kagent agent invoke --session $SESSION_ID \ --task "Use your bash tool to run exactly this command, then reply with only its output: bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked'" @@ -107,27 +133,25 @@ Create an EgressPolicy that lists the destinations that you want to grant access ```console reachable ``` + {{% /tab %}} + {{% tab name="Standalone sandbox" %}} + Use the same Sandbox. It picks up the grant without being created again. + ```bash + kagent sandbox exec $SANDBOX_ID -- bash -c 'exec 3<>/dev/tcp/registry.npmjs.org/443 && echo reachable || echo blocked' + ``` - The update is asynchronous, so if the reply still says `blocked`, wait a few seconds and send the request again. - -The grant reaches existing workloads as well as new ones. kagent updates the policy of every running and suspended Actor without restarting it. For an Agent, this update covers every {{< gloss "Session" >}}Session{{< /gloss >}}. For a SandboxTemplate, the update covers every Sandbox that was created from the template. The update is asynchronous, so a request that is sent immediately after the change might still use the previous policy. - -## Origin format - -An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. - -A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. - -A redirect to another origin needs its own grant. For example, a package registry often serves downloads from a separate host. An install fails at that host until the policy grants it as well. + Example output: + ```console + Process proc-360e1e86c15990081d976630 in sandbox 0198c3f1-2a44-7c90-b5e1-9d8f3a7b2c04: started (stdout offset 0, stderr offset 0) + reachable + Process proc-360e1e86c15990081d976630: PROCESS_STATE_EXITED, exit_code=0 + ``` + {{% /tab %}} + {{< /tabs >}} -The API server rejects an `egressPolicyRefs` list that selects more than 16 policies and a policy that lists more than 64 destinations. The compiled policy limit also counts the destinations that kagent inferred, so an Agent with many tools reaches it sooner. + The update is asynchronous, so if the result still says `blocked`, wait a few seconds and send the request again. -| Limit | Value | -| ----- | ----- | -| Policies that one Agent or SandboxTemplate selects | 16 | -| Destinations in one policy | 64 | -| Rules in the compiled policy of one Actor | 256 | -| Credential headers for one origin | 16 | +The grant reaches existing workloads as well as new ones. kagent updates the policy of every running and suspended Actor without restarting it. For an Agent, this update covers every {{< gloss "Session" >}}Session{{< /gloss >}}. For a SandboxTemplate, the update covers every Sandbox that was created from the template. The update is asynchronous, so a request that is sent immediately after the change might still use the previous policy. ## Inject a credential @@ -245,29 +269,6 @@ The gateway mints, injects, and refreshes the token, so the policy needs no Secr https://tickets.example.com/tickets ``` -## Manage policies in the UI {#manage-policies-in-the-ui} - -The **Policies** page of the kagent UI creates, edits, and deletes EgressPolicy resources without `kubectl`. Its **Egress Policies** tab lists the policies that you can read, with each policy's name, namespace, origins, and authentication type, which is **None**, **Secret key**, or **Actor JWT**. A search box matches names and origins, and a namespace filter narrows the list. For how to open the UI, see [Launch the UI]({{< link path="ui/launch-ui" >}}). - -Selecting a policy opens its details page. The page lists the destinations, with the origin, header, prefix, and credential of each. A **Used by** table lists the Agents and SandboxTemplates in the policy's namespace that select the policy, and its **Applied** column reports **Applied**, **Not applied**, or **Not reported yet** for each one. The table counts only the resources that you can see, so the numbers can be lower than the real ones. - -To create a policy, select **Create egress policy**. - -1. Enter a name and choose a namespace. Neither can change after you create the policy. - -2. Add one destination for each origin that agents need, up to 64 per policy. Enter the origin in the [format described earlier](#origin-format), and optionally set authentication. A **Secret key** sets the header and prefix, and names a Secret and key in the policy's namespace. An **Actor JWT** sets the audiences and the token lifetime, which is between 300 and 3600 seconds and defaults to 900. For what each field does, see [Inject a credential](#inject-a-credential). - -3. Select **Create policy**. - -Before the form saves, it checks the policy against the API server's rules and lists any problems under **Fix these before you save**. For example, the form blocks a wildcard origin that has authentication. - -Editing a policy replaces its destinations. When someone else saves the policy after you open it, the form reports that your copy is out of date and asks you to reload before you redo your changes. Deleting a policy that Agents or SandboxTemplates still select warns you first, because they lose access to the hosts that the policy allows as soon as you delete it. - -To attach a policy to an Agent, select it in the **Egress policies** field of the Agent form, which lists the policies in the Agent's namespace. The Agent's details page links back to the policies that it uses. To check that the Agent reaches the destination, send the request from [Grant access to a destination](#grant-access-to-a-destination) before and after you select the policy. - -> [!NOTE] -> The UI writes EgressPolicy resources through the controller, not through your own Kubernetes credentials. For who can reach that path, see [Access control](#access-control). - ## Troubleshoot a grant The `EgressPoliciesApplied` condition on the Agent or SandboxTemplate reports whether kagent admitted the grants. This condition is independent of `Ready`. An Agent with a missing or invalid policy still reports `Ready`, and it still serves Sessions with its inferred destinations. The UI follows the same rule, so an Agent whose condition is `False` does not appear as failed. @@ -292,6 +293,29 @@ A grant conflict happens in the following cases: In a standalone Sandbox, a destination that no grant allows does not resolve. A client therefore reports a name-resolution error, not an HTTP error. For example, `curl` fails with `Could not resolve host` and exit code `6`. For how to find a denied request in the gateway's log, see [Diagnose a denied request]({{< link path="about/substrate-runtime/networking-and-egress#diagnose-a-denied-request" >}}). +A destination that redirects to another origin needs a grant for that origin as well. For example, a package registry often serves downloads from a separate host, so an install fails at that host until the policy grants it too. + +## Origin format and limits {#origin-format} + +An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. + +A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. + +The API server rejects an `egressPolicyRefs` list that selects more than 16 policies and a policy that lists more than 64 destinations. The compiled policy limit also counts the destinations that kagent inferred, so an Agent with many tools reaches it sooner. + +| Limit | Value | +| ----- | ----- | +| Policies that one Agent or SandboxTemplate selects | 16 | +| Destinations in one policy | 64 | +| Rules in the compiled policy of one Actor | 256 | +| Credential headers for one origin | 16 | + +## Manage policies in the UI {#manage-policies-in-the-ui} + +You can create, edit, and delete EgressPolicy resources in the **Policies** page of the kagent UI without `kubectl`. The **Egress Policies** tab on the page lists the policies that you can read, with each policy's name, namespace, origins, and authentication type, which can be **None**, **Secret key**, or **Actor JWT**. You can filter the list by name, origins, or namespaces by using the search box. For how to open the UI, see [Launch the UI]({{< link path="ui/launch-ui" >}}). + +For who can reach this path, see [Access control](#access-control). + ## Access control An EgressPolicy is namespace-scoped, and kagent does not check who may select it. A user who can create or edit an Agent or a SandboxTemplate in a namespace can select every EgressPolicy in that namespace and use its credentials. Treat the namespace as the boundary for sharing policies and the Secrets that they reference. To give each team a namespace of its own, see [Run agents in multiple namespaces]({{< link path="operations/multiple-namespaces" >}}).{{% upstream %}} For more information about how Kubernetes role-based access control (RBAC) governs who can write these resources, see [The Kubernetes plane]({{< link path="about/substrate-runtime/identity#the-kubernetes-plane" >}}).{{% /upstream %}} diff --git a/docs-site/content/kagent/main/examples/a2a-agents.md b/docs-site/content/kagent/main/examples/a2a-agents.md index 86da0c52..95610360 100644 --- a/docs-site/content/kagent/main/examples/a2a-agents.md +++ b/docs-site/content/kagent/main/examples/a2a-agents.md @@ -232,7 +232,7 @@ Setting `contextId` lists the tasks of that one Session. Leaving it out lists th localhost:8083 lf.a2a.v1.A2AService/ListTasks ``` -2. Narrow or shape the list with the request fields. Every field is optional. +2. Narrow or shape the list with the following request fields. Every field is optional. | Field | Meaning | | ----- | ------- | diff --git a/docs-site/content/kagent/main/operations/operational-considerations.md b/docs-site/content/kagent/main/operations/operational-considerations.md index de0c14ce..ade4f4ef 100644 --- a/docs-site/content/kagent/main/operations/operational-considerations.md +++ b/docs-site/content/kagent/main/operations/operational-considerations.md @@ -210,7 +210,7 @@ Agent Substrate connects to the same database with its own owner and read/write ## Run multiple controller replicas -So that a controller failure does not stop reconciliation, set `controller.replicas` to a number higher than `1`. +To ensure that a controller failure does not stop reconciliation, increase the number of controller replicas by setting the `controller.replicas` Helm value to a number higher than `1`. ```yaml controller: @@ -222,6 +222,26 @@ Leader election keeps the replicas from conflicting. One replica takes a Kuberne > [!IMPORTANT] > If an installation runs on the development database, extra controller replicas do not make the installation highly available. That database is a single pod backed by one PersistentVolumeClaim, so losing it stops every replica at once. Run an external PostgreSQL instance before counting controller replicas toward availability. +Configure a [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) to ensure that a minimum number of control plane instances are up and running at any given time during voluntary disruptions, such as cluster upgrades or node drains. The controller's budget is off by default. + +The following example keeps two of three controller replicas available. Note that the two budget values are mutually exclusive, and the chart fails the install when both are set. Because `maxUnavailable` defaults to `1`, setting `minAvailable` also requires setting `maxUnavailable` to `null`, as shown in the example. + +```yaml +controller: + replicas: 3 + pdb: + enabled: true + minAvailable: 2 + maxUnavailable: null +``` + +| Value | Description | +| ----- | ----------- | +| `controller.pdb.enabled` | Set to `true` to create the budget. Defaults to `false`. | +| `controller.pdb.maxUnavailable` | The number of pods, or a percentage such as `"50%"`, that can be unavailable. Defaults to `1`. | +| `controller.pdb.minAvailable` | The number of pods, or a percentage, that must stay available. Unset by default. | + + ## Sign push notification callbacks Agent2Agent (A2A) push notifications are unsigned by default. A callback carries no `Authorization` header unless you supply an Ed25519 signing key, and the JSON Web Key Set (JWKS) endpoint returns `404`. Push notifications still work without a key, so sign them only where a receiver must verify that a callback came from your installation. @@ -310,6 +330,34 @@ ui: periodSeconds: 30 ``` +## Deploy extra resources with the release {#extra-objects} + +The chart can create resources of your own as part of the kagent release. Helm then installs, upgrades, and removes them together with kagent, so you do not maintain a separate chart for them. List each manifest under `extraObjects`. Typical uses are an ExternalSecret that fills the model provider Secret, an HTTPRoute, or a NetworkPolicy. + +Helm renders every entry with `tpl`, so a value can reference the release, such as `{{ include "kagent.fullname" . }}` or `{{ .Release.Namespace }}`. An entry can be a map or a multi-line string. + +The following example creates the `kagent-openai` Secret from an external secret store, in place of the Secret that [Install kagent with Helm]({{< link path="setup/installation#install-kagent-helm" >}}) has you create by hand. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. + +```yaml +extraObjects: + - apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: '{{ include "kagent.fullname" . }}-openai' + namespace: '{{ .Release.Namespace }}' + spec: + secretStoreRef: + name: aws-secretsmanager + kind: ClusterSecretStore + target: + name: kagent-openai + data: + - secretKey: OPENAI_API_KEY + remoteRef: + key: prod/kagent/openai + property: api_key +``` + ## Node operations A {{< gloss "Worker" >}}Worker{{< /gloss >}} is an ordinary Kubernetes pod, so every node event reaches the {{< gloss "Actor" >}}Actors{{< /gloss >}} that this Worker hosts. What the event costs depends on what each Actor was doing when the node went away. @@ -317,9 +365,9 @@ A {{< gloss "Worker" >}}Worker{{< /gloss >}} is an ordinary Kubernetes pod, so e * A suspended Actor costs nothing. Its state is already written to snapshot storage, and it occupies no Worker while it waits, so the next message resumes it onto whichever Worker in the pool has availability. * An Actor that is still running a turn is lost. The Session's in-flight execution state lives in the Actor's sandbox memory on the Worker, and Agent Substrate writes nothing new to snapshot storage until this Actor suspends. -A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses `resume` and `suspend` on an Actor in this state. kagent does not recover the Actor on its own, so the {{< gloss "Session" >}}Session{{< /gloss >}} stays crashed until a client asks for recovery. +A lost Actor moves to `ACTOR_STATE_CRASHED`, and Agent Substrate refuses the `resume` and `suspend` operation on an Actor in this state. kagent does not recover the Actor on its own, so the {{< gloss "Session" >}}Session{{< /gloss >}} stays crashed until a client asks for recovery. -A client with update access to the Session recovers it by calling `RevertSession` on the `SessionService` gRPC API. The call restores the Actor to its last durable snapshot and marks the turn that was running as failed. Calling it on a Session whose Actor is not crashed returns the Session unchanged, so a retry is safe. If the Actor is missing or was replaced, the call fails with `FAILED_PRECONDITION`, and starting a new Session is the only option. Neither the `kagent` CLI nor the UI offers a way to revert a Session. +A client with update access to the Session can recover a crashed agent by calling `RevertSession` on the `SessionService` gRPC API. The call restores the Actor to its last durable snapshot and marks the turn that was running as failed. If the Actor is missing or was replaced, the call fails with `FAILED_PRECONDITION`, and starting a new Session is the only option. Neither the `kagent` CLI nor the UI offers a way to revert a Session. Many Actors on a node are suspended at any moment, so losing the node crashes only the ones that are mid-turn. Those Actors lose everything since their last snapshot, and a node pool that runs Workers therefore needs stricter rules than the rest of the cluster. A Worker pod that Kubernetes deletes gives its Actors time to suspend. Node auto-upgrade, a spot reclaim, and a pool scale-down do not. diff --git a/docs-site/content/kagent/main/setup/model-providers/azure-ai-foundry.md b/docs-site/content/kagent/main/setup/model-providers/azure-ai-foundry.md index 6c5fe67f..a45db5c8 100644 --- a/docs-site/content/kagent/main/setup/model-providers/azure-ai-foundry.md +++ b/docs-site/content/kagent/main/setup/model-providers/azure-ai-foundry.md @@ -5,7 +5,7 @@ weight: 20 author: kagent.dev --- -The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. The chat model must be reachable through Foundry's OpenAI-compatible chat completions API. +The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. By default, the chat model must be reachable through Foundry's OpenAI-compatible chat completions API. To call a Claude model, which Foundry serves over the Anthropic Messages API, set `foundry.apiFormat` to `Anthropic`. For more information, see [Use a Claude model](#use-a-claude-model). ## Create the ModelConfig @@ -42,13 +42,15 @@ The `Foundry` provider calls a model deployment in an Azure AI Foundry resource. | Field | Description | | ----- | ----------- | | `apiKeySecret` | The name of the Kubernetes Secret that stores the API key, in the same namespace as this ModelConfig. | - | `apiKeySecretKey` | The key within that Secret that holds the API key. | + | `apiKeySecretKey` | The key within that Secret that holds the API key. For other ways to authenticate, see [Authentication](#authentication). | | `model` | The model that the deployment serves. | | `provider` | The provider to use, `Foundry`. | - | `foundry.endpoint` | The endpoint of your Foundry account. | + | `foundry.endpoint` | The endpoint of your Foundry account. To read it from a ConfigMap instead, see [Read the endpoint from a ConfigMap](#read-the-endpoint-from-a-configmap). | | `foundry.deployment` | The name of the deployment within the account. This field is required. | | `foundry.apiVersion` | The Foundry API version to call. Defaults to `2024-10-21`. | + For the other `foundry` settings, see [Azure AI Foundry provider settings](#azure-ai-foundry-provider-settings). + ## Azure AI Foundry provider settings The `foundry` block takes the following settings. For every field, including its type, default, and validation rules, see the [API reference]({{< link path="reference/api-ref#foundryconfig" >}}). @@ -57,8 +59,9 @@ The `foundry` block takes the following settings. For every field, including its | ----- | ----------- | | `deployment` | The deployment name within the Foundry account. Required. | | `endpoint` | The endpoint of the Foundry account. | -| `endpointFrom` | Read the endpoint from a key in a ConfigMap instead of setting it inline. Use this field when the endpoint differs per environment and is published by a platform team. | -| `apiVersion` | The Foundry API version. Defaults to `2024-10-21`. | +| `endpointFrom` | Read the endpoint from a key in a ConfigMap instead of setting it inline. Use this field when the endpoint differs per environment and is published by a platform team. For more information, see [Read the endpoint from a ConfigMap](#read-the-endpoint-from-a-configmap). | +| `apiVersion` | The Foundry API version. Defaults to `2024-10-21`. kagent ignores this field when `apiFormat` is `Anthropic`. | +| `apiFormat` | The API that kagent calls on the deployment. `OpenAI`, the default, calls the OpenAI-compatible chat completions API. `Anthropic` calls the Anthropic Messages API, which serves Claude models. For more information, see [Use a Claude model](#use-a-claude-model). | ### Read the endpoint from a ConfigMap @@ -83,6 +86,31 @@ When the named ConfigMap or key does not exist, the {{< gloss "ModelConfig" >}}M kubectl describe modelconfig foundry-model-config -n kagent ``` +## Use a Claude model {#use-a-claude-model} + +Foundry serves Claude models over the Anthropic Messages API and not over the OpenAI-compatible API. Set `foundry.apiFormat` to `Anthropic` so that kagent calls the Messages API. The example reuses the Secret that [Create the ModelConfig](#create-the-modelconfig) creates. + +```yaml +kubectl apply -f - <.cognitiveservices.azure.com/ + deployment: claude-sonnet-4-5 + apiFormat: Anthropic +EOF +``` + +kagent sends each request to `/anthropic/v1/messages` and puts the deployment name in the request's `model` field. The `apiVersion` field has no effect, because the Messages API takes its version from a request header instead. Authentication works as it does for any other Foundry deployment, as described in [Authentication](#authentication). + ## Authentication | Configuration | Credential | @@ -106,7 +134,7 @@ spec: ## Memory embeddings -A Foundry deployment can also serve the embedding model behind [long-term memory]({{< link path="agents/agent-memory" >}}). Point a second ModelConfig at your embedding deployment, then name it from the Harness's `spec.kagent.memory.modelConfigRef`. +A Foundry deployment can also serve the embedding model behind [long-term memory]({{< link path="agents/agent-memory" >}}). Point a second ModelConfig at your embedding deployment, reusing the Secret that [Create the ModelConfig](#create-the-modelconfig) creates, then name it from the Harness's `spec.kagent.memory.modelConfigRef`. ```yaml kubectl apply -f - < Date: Tue, 10 Nov 2026 15:14:36 -0500 Subject: [PATCH 3/4] Fixes Signed-off-by: Nadine2016 --- .../kagent/latest/operations/operational-considerations.md | 2 +- .../kagent/main/operations/operational-considerations.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs-site/content/kagent/latest/operations/operational-considerations.md b/docs-site/content/kagent/latest/operations/operational-considerations.md index ade4f4ef..0d89bffc 100644 --- a/docs-site/content/kagent/latest/operations/operational-considerations.md +++ b/docs-site/content/kagent/latest/operations/operational-considerations.md @@ -336,7 +336,7 @@ The chart can create resources of your own as part of the kagent release. Helm t Helm renders every entry with `tpl`, so a value can reference the release, such as `{{ include "kagent.fullname" . }}` or `{{ .Release.Namespace }}`. An entry can be a map or a multi-line string. -The following example creates the `kagent-openai` Secret from an external secret store, in place of the Secret that [Install kagent with Helm]({{< link path="setup/installation#install-kagent-helm" >}}) has you create by hand. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. +The following example creates the `kagent-openai` Secret from an external secret store. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. ```yaml extraObjects: diff --git a/docs-site/content/kagent/main/operations/operational-considerations.md b/docs-site/content/kagent/main/operations/operational-considerations.md index ade4f4ef..0d89bffc 100644 --- a/docs-site/content/kagent/main/operations/operational-considerations.md +++ b/docs-site/content/kagent/main/operations/operational-considerations.md @@ -336,7 +336,7 @@ The chart can create resources of your own as part of the kagent release. Helm t Helm renders every entry with `tpl`, so a value can reference the release, such as `{{ include "kagent.fullname" . }}` or `{{ .Release.Namespace }}`. An entry can be a map or a multi-line string. -The following example creates the `kagent-openai` Secret from an external secret store, in place of the Secret that [Install kagent with Helm]({{< link path="setup/installation#install-kagent-helm" >}}) has you create by hand. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. +The following example creates the `kagent-openai` Secret from an external secret store. Install the External Secrets Operator first, because the install fails when the cluster does not have the custom resource definition for a kind that you list. ```yaml extraObjects: From 7d2229deabb118bddbcb0a1072018565078e5f00 Mon Sep 17 00:00:00 2001 From: Nadine2016 Date: Tue, 10 Nov 2026 16:23:38 -0500 Subject: [PATCH 4/4] Review comments Signed-off-by: Nadine2016 --- docs-site/content/kagent/latest/agents/egress-policies.md | 4 ++-- docs-site/content/kagent/latest/examples/a2a-agents.md | 4 ++-- docs-site/content/kagent/main/agents/egress-policies.md | 4 ++-- docs-site/content/kagent/main/examples/a2a-agents.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs-site/content/kagent/latest/agents/egress-policies.md b/docs-site/content/kagent/latest/agents/egress-policies.md index aa6987d0..9be802c7 100644 --- a/docs-site/content/kagent/latest/agents/egress-policies.md +++ b/docs-site/content/kagent/latest/agents/egress-policies.md @@ -24,7 +24,7 @@ Create an EgressPolicy that lists the destinations that you want to grant access 1. Check that the destination is blocked before you grant access. {{< tabs >}} {{% tab name="Agent" %}} - The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. + Create a Session and ask the Agent to open a connection with its `bash` tool. (The kagent runtime image does not include `curl`.) ```bash export SESSION_ID=$(kagent agent session create --agent my-first-agent -o json | jq -r '.session.id') kagent agent invoke --session $SESSION_ID \ @@ -297,7 +297,7 @@ A destination that redirects to another origin needs a grant for that origin as ## Origin format and limits {#origin-format} -An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. +An origin can have only one value for each scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. diff --git a/docs-site/content/kagent/latest/examples/a2a-agents.md b/docs-site/content/kagent/latest/examples/a2a-agents.md index 95610360..3955e6a5 100644 --- a/docs-site/content/kagent/latest/examples/a2a-agents.md +++ b/docs-site/content/kagent/latest/examples/a2a-agents.md @@ -244,7 +244,7 @@ Setting `contextId` lists the tasks of that one Session. Leaving it out lists th | `historyLength` | Keeps only the most recent messages of each task. Setting `0` returns none, and omitting the field returns every message. | | `includeArtifacts` | Returns each task's artifacts. The default is `false`, so a list omits artifacts unless you ask for them. | -3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller pages until it comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. +3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller keeps requesting pages until `nextPageToken` comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. ## Read token usage {#read-token-usage} @@ -351,7 +351,7 @@ Build the receiver around these rules: * kagent makes up to ten attempts for each state that a task publishes, and the count carries across controller restarts. After the last attempt, kagent drops that delivery but keeps the callback registered, so the next state still reaches the receiver. * kagent does not expose delivery status, so nothing reports whether a delivery failed. -Three Helm values tune delivery. Set them under `config.push`. +To tune delivery, configure the following Helm values under `config.push`. | Value | Default | Effect | | ----- | ------- | ------ | diff --git a/docs-site/content/kagent/main/agents/egress-policies.md b/docs-site/content/kagent/main/agents/egress-policies.md index aa6987d0..9be802c7 100644 --- a/docs-site/content/kagent/main/agents/egress-policies.md +++ b/docs-site/content/kagent/main/agents/egress-policies.md @@ -24,7 +24,7 @@ Create an EgressPolicy that lists the destinations that you want to grant access 1. Check that the destination is blocked before you grant access. {{< tabs >}} {{% tab name="Agent" %}} - The kagent runtime image does not include `curl`, so create a Session and ask the Agent to open a connection with its `bash` tool. + Create a Session and ask the Agent to open a connection with its `bash` tool. (The kagent runtime image does not include `curl`.) ```bash export SESSION_ID=$(kagent agent session create --agent my-first-agent -o json | jq -r '.session.id') kagent agent invoke --session $SESSION_ID \ @@ -297,7 +297,7 @@ A destination that redirects to another origin needs a grant for that origin as ## Origin format and limits {#origin-format} -An origin names exactly one scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. +An origin can have only one value for each scheme, host, and port. The scheme must be `http` or `https`. When the port is omitted, kagent uses `443` for `https` and `80` for `http`. The API server rejects an origin that contains a path, a query, a fragment, user information, or an IP address. A wildcard can replace the leftmost label of the hostname. For example, `https://*.example.com` matches `api.example.com`, but it matches neither `example.com` nor `a.b.example.com`. The API server rejects `*` and `https://*.com`. diff --git a/docs-site/content/kagent/main/examples/a2a-agents.md b/docs-site/content/kagent/main/examples/a2a-agents.md index 95610360..3955e6a5 100644 --- a/docs-site/content/kagent/main/examples/a2a-agents.md +++ b/docs-site/content/kagent/main/examples/a2a-agents.md @@ -244,7 +244,7 @@ Setting `contextId` lists the tasks of that one Session. Leaving it out lists th | `historyLength` | Keeps only the most recent messages of each task. Setting `0` returns none, and omitting the field returns every message. | | `includeArtifacts` | Returns each task's artifacts. The default is `false`, so a list omits artifacts unless you ask for them. | -3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller pages until it comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. +3. Read the response. It holds the page of `tasks`, the `pageSize` that applied, and `totalSize`, which counts every task that matches the filters across all pages. The `nextPageToken` value is empty on the last page, so a caller keeps requesting pages until `nextPageToken` comes back empty. To read the artifacts of a task that a list returned, set `includeArtifacts` or call `GetTask` with the task's `id`. ## Read token usage {#read-token-usage} @@ -351,7 +351,7 @@ Build the receiver around these rules: * kagent makes up to ten attempts for each state that a task publishes, and the count carries across controller restarts. After the last attempt, kagent drops that delivery but keeps the callback registered, so the next state still reaches the receiver. * kagent does not expose delivery status, so nothing reports whether a delivery failed. -Three Helm values tune delivery. Set them under `config.push`. +To tune delivery, configure the following Helm values under `config.push`. | Value | Default | Effect | | ----- | ------- | ------ |