Skip to content

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

Strahl(
    *,
    api_key: str | None = None,
    base_url: str = "https://api.strahl.io"
)

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
from strahl import Strahl, Label

agent = Strahl(api_key=os.environ["STRAHL_API_KEY"])
agent.set_role_labels({
    "user":      Label(source={"user:alice"}, visibility={"user:alice"}),
    "assistant": Label(source={"assistant"}, visibility={"user:alice"}),
})
analysis = agent.analyze(messages)

Create a client with empty state.

Parameters:

Name Type Description Default
api_key str | None

API key for the Strahl API. Equivalent to calling set_api_key immediately after construction. May be left unset and provided later.

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'

api_key instance-attribute

api_key: str | None = api_key

base_url instance-attribute

base_url: str = base_url

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 Strahl instance.

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 Strahl instance with copied state.

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 __name__.

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.

Example
@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:
    ...

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 Label.

None
name str | None

Registered tool name. May only be set when fn is a callable; otherwise the name is taken from the schema.

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 params omits a declared parameter or names an undeclared one.

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:

strahl.add_tool(
    fn={"name": "lookup_order", "input_schema": {"type": "object",
        "properties": {"order_id": {"type": "string"}}}},
    requires=Label(source={"user"}, visibility={"user"}),
    produces=Label(source={"orders"}, visibility={"user"}),
)

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 Label describing the document's provenance and reach.

required

Raises:

Type Description
ValueError

If a document with name is already registered, or if label uses callable expressions.

set_api_key

set_api_key(api_key: str)

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. "user", "assistant", "system") to a static Label.

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 Label.

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 with one result

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.

Example
analysis = strahl.analyze(messages)
analysis.raise_if_denied()

set_api_key

set_api_key(api_key: str)

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.