Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions docs/configure-rails/guardrail-catalog/tool-calling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,14 @@ models:

## Tool Call Validation

The `tool call validation` rail inspects every tool call in the model's response and blocks the request on the first violation.
The `tool call validation` rail inspects every tool call in the model's response and blocks the request when any call breaks a rule.

- **Allowlist.** The call must name a tool you declared.
IORails blocks a call to a tool that is not in the declared set, for example `tool call 'delete_database' is not an allowed tool`.
- **Argument schema.** The call's arguments must validate against the tool's declared JSON Schema (the `function.parameters` block).
IORails blocks arguments that violate the schema, for example `arguments for tool 'get_weather' do not match its schema: 'city' is a required property`.
IORails blocks arguments that violate the schema, for example `arguments for tool 'get_weather' do not match its schema: 'required' failed at '/city'`.
The reason names the failing argument as the schema declares it, and the JSON Schema keyword.
It never quotes an argument's value or a key the model chose, which appears as `*`.
- **No-argument tools.** A function tool that declares no parameters must receive no arguments.
IORails blocks any supplied argument.
- **Hosted tools.** IORails allowlists a hosted or server-side tool that only `type` identifies (for example a built-in web search) by type, and does not schema-validate its arguments because the provider owns the call shape.
Expand Down Expand Up @@ -343,6 +345,65 @@ response = await rails.generate_async(
)
```

## Check Tool Calls and Results Without Generation

An agent harness that calls its own model can use the same rails to validate tool traffic without IORails generating a response.
Run a check with `RailType.TOOL_CALL` on the conversation that ends in the model's tool calls, and with `RailType.TOOL_RESULT` after appending the tool results:

```python
from nemoguardrails import Guardrails, RailsConfig
from nemoguardrails.rails.llm.options import RailStatus, RailType

rails = Guardrails(RailsConfig.from_path("./config"), require_iorails=True)

call_check = await rails.check_async(messages, rail_types=[RailType.TOOL_CALL], tools=tools)
if call_check.status == RailStatus.BLOCKED:
... # Drop or re-request the calls named in call_check.tool_violations.

messages.append({"role": "tool", "tool_call_id": "call_1", "content": "18C"})
result_check = await rails.check_async(messages, rail_types=[RailType.TOOL_RESULT])
```

A `tool_call` check validates the tool calls on the last assistant message against `tools`, which replace the tools declared on the main model.
Only a `tool_call` check reads `tools`, so passing them to any other check raises `InvalidCheckRequestError`, or returns HTTP 422 from the server.
The allowlist and argument schemas are enforced for every call by `tool call validation`, and for the calls a `per_tool` rail checks by that rail; `per_tool` rails check only the tools they are listed under.
A `tool_result` check validates every `tool` message against the calls it answers.
Both run the global validator and any `per_tool` rails, and neither calls the model.
A blocked check reports each failing call or result in `tool_violations`, with a `violation_type` to switch on.
For the full behavior, the violation types, and the engine requirements, refer to [Checking Messages Against Rails: Tool Rails](/run-guardrailed-inference/using-python-apis/check-messages#tool-rails-iorails-only).

The server exposes the same check through `/v1/checks`.
Run the server with `NEMO_GUARDRAILS_IORAILS_ENGINE=1` so the configuration is served by IORails; on `LLMRails`, the tool rail types return HTTP 422.
Pass the tools in the request's top-level `tools` field:

```json
{
"model": "meta/llama-3.3-70b-instruct",
"messages": [
{"role": "user", "content": "What's the weather in Paris?"},
{"role": "assistant", "content": null, "tool_calls": [
{"id": "call_1", "type": "function", "function": {"name": "delete_files", "arguments": "{}"}}
]}
],
"tools": [{"type": "function", "function": {"name": "get_weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}],
"guardrails": {"config_id": "tools", "rail_types": ["tool_call"]}
}
```

The call names a tool that is not declared, so the response is:

```json
{
"status": "blocked",
"content": "I'm sorry, I can't respond to that.",
"rail": "tool call validation",
"reason": "tool call 'delete_files' is not an allowed tool",
"tool_violations": [
{"kind": "tool_call", "violation_type": "tool_not_allowed", "reason": "tool call 'delete_files' is not an allowed tool", "tool_call_id": "call_1", "tool_name": "delete_files", "index": 0}
]
}
```

## Streaming

Tool-calling rails work with `stream_async`.
Expand Down
16 changes: 10 additions & 6 deletions docs/reference/engine-feature-support.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,13 @@ With `require_iorails=True`, `Guardrails` raises `ValueError` instead of falling
| Feature | LLMRails | IORails | Notes |
|---|:---:|:---:|---|
| Tool-call passthrough | ✓ | ✓ | |
| Tool-call validation rail | ✓ | ✓ | IORails flow: `tool call validation` |
| Tool-result validation rail | ✓ | ✓ | IORails flow: `tool result validation` |
| Tool-call validation rail | ✗ | ✓ | IORails flow: `tool call validation` |
| Tool-result validation rail | ✗ | ✓ | IORails flow: `tool result validation` |
| Tool rails in `check` / `check_async` | ✗ | ✓ | `RailType.TOOL_CALL` and `RailType.TOOL_RESULT`. LLMRails raises `RailTypeNotSupportedError` |

Both engines support passing model tool calls through to the caller and validating tool calls and tool results.
`LLMRails` handles these through the Colang runtime and tool rails.
Both engines support passing model tool calls through to the caller.
`LLMRails` runs the Colang flows you configure under `rails.tool_output` and `rails.tool_input` through the Colang runtime.
The built-in `tool call validation` and `tool result validation` validators run on `IORails` only.

`IORails` validates tool calls and tool results through directional flows: `tool call validation` on the tool-output rail and `tool result validation` on the tool-input rail.
Tool calls are returned in the OpenAI-style `tool_calls` field of the response message.
Expand All @@ -163,7 +165,7 @@ Tool calls are returned in the OpenAI-style `tool_calls` field of the response m
| `generate` / `generate_async` | ✓ | ✓ | |
| `stream_async` | ✓ | ✓ | |
| Event-based API (`generate_events` / `process_events`) | ✓ | ✗ | Requires the Colang runtime |
| `check` / `check_async` (rails-only validation) | ✓ | ✓ | Input and output rails only on both engines. Neither runs tool rails |
| `check` / `check_async` (rails-only validation) | ✓ | ✓ | Input and output rails on both engines. Only IORails runs tool rails, when they are named in `rail_types` |
| `GenerationOptions` | ✓ | ◐ | IORails supports rail toggles, `llm_params`, and the `log` options that do not need Colang. `output_vars` raises. The separate `state` keyword argument also raises. |
| `GenerationResponse` (structured response object) | ✓ | ✓ | Returned by both engines when `options` is passed |
| `GenerationLog` `internal_events` / `colang_history` | ✓ | ✗ | Colang runtime only. IORails raises `NotImplementedError`. |
Expand All @@ -187,7 +189,9 @@ For the field-by-field comparison, refer to [Generation Options: IORails and LLM
The event-based API and `explain()` are not available on `IORails`.
On the `Guardrails` facade, these raise `NotImplementedError` when `IORails` is the active engine.

Both engines accept the same `check` arguments and return the same `RailsResult`, and neither runs tool rails from a check.
Both engines accept `messages` and `rail_types` and return the same `RailsResult`.
Only `IORails` runs tool rails from a check, when `rail_types` names `tool_call` or `tool_result`.
`LLMRails` raises `RailTypeNotSupportedError` for those rail types and for the `tools` argument.
`LLMRails` runs the check through the Colang runtime with the main model call disabled.
`IORails` runs the rails directly through its rails manager and reuses the `generate_async` admission queue, request span, and request metrics.
The two differ in what a blocked check reports and in how a direction with no content to check is handled.
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/rail-engine-support.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ Tool rails are not manifest surfaces.

| Flow | Section | LLMRails | IORails | Notes |
|---|---|:---:|:---:|---|
| `tool call validation` | `rails.tool_output.flows` | ✓ | ✓ | Validates model-emitted tool calls |
| `tool result validation` | `rails.tool_input.flows` | ✓ | ✓ | Validates application-returned tool results |
| `tool call validation` | `rails.tool_output.flows` | ✗ | ✓ | Validates model-emitted tool calls, in generation and in a `tool_call` check |
| `tool result validation` | `rails.tool_input.flows` | ✗ | ✓ | Validates application-returned tool results, in generation and in a `tool_result` check |

For configuration and behavior, refer to [Tool Calling](/configure-guardrails/guardrail-catalog/tool-calling).

Expand Down
Loading
Loading