strahl.client¶
client
¶
The Strahl client: register tools and documents, then analyze transcripts.
There are two ways to use Strahl. The simplest is the module-level functions
(set_api_key, tool,
analyze, ...), which are backed by a process-global default
client. Reach for the Strahl class directly when you need
isolated state — separate agents, per-tenant configuration, or test isolation.
A typical setup labels each message role, declares a flow policy for every
sensitive tool, and calls analyze after each assistant turn:
import os
import strahl
from strahl import Label
strahl.set_api_key(os.environ["STRAHL_API_KEY"])
strahl.set_role_labels({
"user": Label(source={"user"}, visibility={"user"}),
"assistant": Label(source={"assistant"}, visibility={"user"}),
})
@strahl.tool(
requires=Label(source={"user"}, visibility={"user"}),
produces=Label(source={"email-tool"}, visibility={"user"}),
)
def send_email(to: str, subject: str, body: str) -> str:
...
analysis = strahl.analyze(messages)
analysis.raise_if_denied()
Tools may be registered from a Python callable (name, parameters, types, and
description are inferred), an OpenAI tool schema, or an Anthropic tool schema —
see add_tool for all three forms.
Strahl
¶
An isolated Strahl client holding its own tools, documents, and role labels.
Use this directly when a process-global default client is not enough — for example one client per agent, per tenant, or per test. Each instance keeps its own state and HTTP connection; mutations on one instance never affect another (or the module-level default client).
Every method mirrors a module-level function of the same name, so code can move between the default client and a dedicated instance with no changes other than the receiver.
Example
Create a client with empty state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str | None
|
API key for the Strahl API. Equivalent to calling
|
None
|
base_url
|
str
|
Base URL of the Strahl API. Override only to target a different environment; the API version path is appended per request. |
'https://api.strahl.io'
|
from_default
classmethod
¶
from_default(
*,
api_key: str | None = None,
base_url: str = "https://api.strahl.io"
) -> Strahl
Create an instance pre-populated from the module-level default client.
The new instance is seeded with a snapshot of the default client's tools,
documents, role labels, and API key. Later changes to either client are
independent. Useful when tools are registered globally via
@strahl.tool decorators but you need a per-request or
per-tenant instance with its own role labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str | None
|
Override the inherited API key for this instance. |
None
|
base_url
|
str
|
Base URL for this instance. |
'https://api.strahl.io'
|
Returns:
| Type | Description |
|---|---|
Strahl
|
A new, independent |
copy
¶
copy(
*,
api_key: str | None = None,
base_url: str = "https://api.strahl.io"
) -> Strahl
Return an independent snapshot of this client.
Tools, documents, and role labels are shallow-copied into a fresh
instance. Used internally by from_default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str | None
|
API key for the copy. Defaults to this client's key. |
None
|
base_url
|
str
|
Base URL for the copy. |
'https://api.strahl.io'
|
Returns:
| Type | Description |
|---|---|
Strahl
|
A new |
tool
¶
tool(
name: str | None = None,
*,
requires: Label,
produces: Label,
params: Mapping[str, Label] | None = None
) -> Callable[[F], F]
Decorator that registers the decorated function as a Strahl tool.
The function is returned unchanged; registration happens as a side effect.
See add_tool for the meaning of each label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str | None
|
Registered tool name. Defaults to the function's |
None
|
requires
|
Label
|
Label that must be satisfied for information to control the call; also the default requirement for every argument. |
required |
produces
|
Label
|
Label assigned to the tool's result. |
required |
params
|
Mapping[str, Label] | None
|
Optional per-parameter requirements. If given, every declared top-level parameter must be listed. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[F], F]
|
A decorator that returns its input function unchanged. |
add_tool
¶
add_tool(
*,
fn: dict | Callable,
requires: Label,
produces: Label,
params: Mapping[str, Label] | None = None,
name: str | None = None
)
Register a tool with a flow policy.
requires labels the tool call and is the default requirement for every
argument. produces labels the tool result, which flows forward into
later analysis. Use params for per-argument requirements that differ
from requires; when provided, every declared top-level parameter must be
listed (optional parameters that are omitted from a given call simply
create no argument sink for that call).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
dict | Callable
|
A Python callable, an OpenAI tool schema dict, or an Anthropic tool schema dict. For a callable, the name, parameters, types, and description are inferred from the signature and docstring. |
required |
requires
|
Label
|
Label that must be satisfied by information selecting the tool. |
required |
produces
|
Label
|
Label assigned to the tool result. |
required |
params
|
Mapping[str, Label] | None
|
Optional mapping of parameter name to per-parameter |
None
|
name
|
str | None
|
Registered tool name. May only be set when |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the tool name is already registered, if a label
expression references a parameter the tool does not declare, or if
|
Example
From a Python callable:
strahl.add_tool(
fn=lookup_order,
requires=Label(source={"user"}, visibility={"user"}),
produces=Label(source={"orders"}, visibility={"user"}),
)
From an OpenAI tool schema:
strahl.add_tool(
fn={"type": "function", "function": {
"name": "lookup_order",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string"}}},
}},
requires=Label(source={"user"}, visibility={"user"}),
produces=Label(source={"orders"}, visibility={"user"}),
)
From an Anthropic tool schema:
add_document
¶
add_document(name: str, content: str, *, label: Label)
Register a named document analyzed alongside the transcript.
Documents represent retrieved content, policies, runbooks, or any context added outside the message transcript. A document exists before any tool call, so its label must be static (no callable tag set expressions).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Unique document name. |
required |
content
|
str
|
The document's text content. |
required |
label
|
Label
|
Static |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a document with |
set_api_key
¶
Set the API key used to authenticate requests to the Strahl API.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str
|
The Strahl API key. Sent as a bearer token on every request. |
required |
set_role_labels
¶
set_role_labels(labels: Mapping[str, Label])
Set the label applied to each message role in a transcript.
Must be called before analyze. Every
non-tool-result role that appears in the transcript must have an entry.
Role labels describe the trust level of the message author and must be
static — they apply to a role, not to individual tool arguments, so
callable tag set expressions are not permitted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
labels
|
Mapping[str, Label]
|
Mapping of role name (e.g. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a role name is empty or a label is not resolvable at creation time (i.e. uses a callable expression). |
TypeError
|
If a value is not a |
analyze
¶
analyze(messages: MessagesLike) -> AnalyzeResponse
Analyze a provider transcript and return a per-tool-call decision.
Call this after each assistant turn, before executing any requested tool. If the final assistant message contains no tool calls, the result is a permitted response produced locally without contacting the API.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
messages
|
MessagesLike
|
A single message dict or a list of message dicts, in either OpenAI or Anthropic format. The transcript must end with an assistant message. |
required |
Returns:
| Type | Description |
|---|---|
AnalyzeResponse
|
An |
AnalyzeResponse
|
per tool call in the final assistant message. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the transcript does not end with an assistant message, if a non-tool-result role is missing a label, or if the final assistant message requests tool calls while no tools are registered. |
set_api_key
¶
Set the API key on the default client. See Strahl.set_api_key.
set_role_labels
¶
set_role_labels(labels: Mapping[str, Label])
Set role labels on the default client. See Strahl.set_role_labels.
tool
¶
tool(
name: str | None = None,
*,
requires: Label,
produces: Label,
params: Mapping[str, Label] | None = None
)
Register a tool on the default client via decorator. See Strahl.tool.
add_tool
¶
add_tool(
*,
fn: dict | Callable,
requires: Label,
produces: Label,
params: Mapping[str, Label] | None = None,
name: str | None = None
)
Register a tool on the default client. See Strahl.add_tool.
add_document
¶
add_document(name: str, content: str, *, label: Label)
Register a document on the default client. See Strahl.add_document.
analyze
¶
analyze(messages: MessagesLike) -> AnalyzeResponse
Analyze a transcript with the default client. See Strahl.analyze.