Skip to main content

redlynr-python

Python SDK for Redlynr — the agent guardrail decision engine.

Detection isn't enforcement. Redlynr identifies when an agent should stop. This SDK makes sure it does.

Installation

pip install redlynr

Zero external dependencies for trial token usage. For x402 payment support, install:

pip install redlynr requests "x402[evm]" eth_account

Quickstart

1. Register your tenant (one-time)

from redlynr import RedlynrClient

owner_token = RedlynrClient.register(
    base_url="https://redlynr.com",
    tenant_id="my-agent-prod",
)
# Store owner_token securely — it is never shown again.

2. Create a client

client = RedlynrClient(
    base_url="https://redlynr.com",
    tenant_id="my-agent-prod",
    owner_token=owner_token,
    chain_id="chain-001",
)

3. Guard every tool call

from redlynr import RedlynrBlocked, RedlynrSlowDown

try:
    result = client.guard(my_tool_fn, {"query": "..."}, step_cost=0.01)
except RedlynrBlocked as e:
    print(f"Stopped: {e.reason}")   # tool was NOT called
except RedlynrSlowDown as e:
    print(f"Slow down: wait {e.wait_seconds}s")  # only if raise_on_slow_down=True

When Redlynr says stop, guard() raises RedlynrBlocked and the tool function is never called. When Redlynr says proceed or slow_down (default), guard() calls the tool and returns its result.


Authentication

Redlynr supports two authentication modes for /run calls.

Trial token (free tier)

Obtain a PoW trial token for 30 free calls:

import hashlib, json, urllib.request

def get_trial_token(base_url="https://redlynr.com"):
    with urllib.request.urlopen(f"{base_url}/trial/challenge") as r:
        ch = json.loads(r.read())
    nonce, difficulty = ch["nonce"], ch["difficulty"]
    prefix = "0" * difficulty
    counter = 0
    while not hashlib.sha256(f"{nonce}{counter}".encode()).hexdigest().startswith(prefix):
        counter += 1
    data = json.dumps({"nonce": nonce, "solution": str(counter)}).encode()
    req = urllib.request.Request(
        f"{base_url}/trial/claim", data=data,
        headers={"Content-Type": "application/json"}, method="POST"
    )
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())["trial_token"]

trial_token = get_trial_token()

client = RedlynrClient(
    base_url="https://redlynr.com",
    tenant_id="my-tenant",
    owner_token=owner_token,
    chain_id="chain-001",
    trial_token=trial_token,
)

x402 payment (paid tier)

For production use, pass an x402-wrapped requests.Session. The session handles USDC payment on Base mainnet automatically on every /run call.

import requests as req_lib
from eth_account import Account
from x402.client import x402ClientSync
from x402.mechanisms.evm.exact import ExactEvmScheme
from x402.http.clients.requests import wrapRequestsWithPayment

# Build x402 session once
account = Account.from_key(os.environ["BUYER_PRIVATE_KEY"])
x402_client = x402ClientSync()
x402_client.register("eip155:8453", ExactEvmScheme(signer=account))
session = wrapRequestsWithPayment(req_lib.Session(), x402_client)

client = RedlynrClient(
    base_url="https://redlynr.com",
    tenant_id="my-tenant",
    owner_token=owner_token,
    chain_id="chain-001",
    session=session,    # x402 payments handled automatically
)

When session is provided it is used for all /run calls. All other endpoints (/register, /policy, /audit, /reset, /pause) use plain urllib and are unpriced.


RedlynrClient reference

RedlynrClient(
    base_url,           # str — Redlynr service URL
    tenant_id,          # str — your registered tenant namespace
    owner_token,        # str — token from /register
    chain_id,           # str — unique ID for this agent chain
    agent_id="orchestrator",        # str — identifier for this agent
    depth=0,                        # int — nesting level (0=top, 1=sub-agent, ...)
    chain_type=None,                # str|None — "interactive", "batch", "scheduled", or None (auto)
    cost_fn=None,                   # callable(tool_name, args) -> float — auto cost resolution
    raise_on_slow_down=False,       # bool — raise RedlynrSlowDown instead of transparent wait
    trial_token=None,               # str|None — PoW trial token for free tier
    session=None,                   # requests.Session|None — x402-wrapped session for paid tier
)

One client per chain. Each RedlynrClient owns state for exactly one chain_id. For multi-agent systems, each agent instantiates its own client with the same chain_id and tenant_id but a distinct agent_id and appropriate depth.

chain_type — declare explicitly

If chain_type is not declared, Redlynr auto-detects the chain type from call patterns and may promote to "batch" after 5 consecutive novel-target calls. Batch chains have a default step limit of 100. Always declare chain_type="interactive" explicitly when you want tight step caps to fire:

client = RedlynrClient(
    ...
    chain_type="interactive",   # pins limits for the chain's lifetime
)

guard()

result = client.guard(fn, args, *, tool_name=None, step_cost=None)

The main enforcement primitive. Calls /run before executing fn. Enforces the decision:

Decision Behavior
proceed fn(**args) is called, result returned
slow_down Waits wait_seconds if set, then calls fn. If raise_on_slow_down=True, raises RedlynrSlowDown instead
stop Raises RedlynrBlocked. fn is never called

Chain state (step count, retry detection, tool fixation, novelty) is tracked automatically in-process with no external dependencies.

args are passed to fn as keyword arguments:

def search(query: str, limit: int = 10):
    ...

result = client.guard(search, {"query": "...", "limit": 5}, step_cost=0.01)
# SDK calls: search(query="...", limit=5)

Policy

Set guardrail limits for your tenant:

client.set_policy(policy={
    "steps_retries": {
        "max_steps_per_chain": 20,
        "max_retries": 2,
        "warn_at_steps_pct": 75,
    },
    "cost": {
        "max_spend_per_period": 1.00,
        "period_seconds": 86400,
    },
})

Or apply a named template:

client.set_policy(template="interactive_agent")
# Available: "interactive_agent", "batch_pipeline", "autonomous_researcher"

Default policy limits

Limit Interactive Batch
max_steps_per_chain 20 100
max_retries 2 2
max_depth 5 5
max_spend_per_chain $0.30 $2.00
warn_at_steps_pct 75% 75%

Multi-agent usage

# Orchestrator (depth=0)
orchestrator = RedlynrClient(
    base_url="https://redlynr.com",
    tenant_id="my-tenant",
    owner_token=owner_token,
    chain_id="chain-001",
    agent_id="orchestrator",
    depth=0,
    chain_type="interactive",
    session=session,
)

# Sub-agent (depth=1) — same chain_id and tenant_id
researcher = RedlynrClient(
    base_url="https://redlynr.com",
    tenant_id="my-tenant",
    owner_token=owner_token,
    chain_id="chain-001",
    agent_id="researcher",
    depth=1,
    chain_type="interactive",
    session=session,
)

Both agents call guard() before every tool execution. Step counters are shared server-side by chain_id. The chain stops when either agent exceeds the limit.


Exception reference

from redlynr import RedlynrBlocked, RedlynrSlowDown, RedlynrError
Exception When raised Key attributes
RedlynrBlocked Redlynr returns stop reason, response
RedlynrSlowDown Redlynr returns slow_down and raise_on_slow_down=True reason, wait_seconds, response
RedlynrError API or connection error message

Stop reasons

retries_exhausted, step_cap_exceeded, cost_budget_exceeded, chain_cost_exceeded, chain_depth_exceeded, repetition_detected, tool_fixation_detected, volume_pressure_blocked, agent_paused, lock_contention


Other methods

client.reset()                    # clear this chain's step counter server-side
client.audit()                    # last 100 /run decisions for this tenant
client.audit_analyze(min_chains=3) # advisory threshold suggestions
client.pause(agent_id=None, paused=True)  # pause or unpause an agent
client.health()                   # service health check
client.run_raw(**kwargs)          # raw /run call, bypasses guard() state tracking

Pricing

  • /run calls: $0.001 USDC per call (Base mainnet)
  • All other endpoints (/register, /policy, /audit, /reset, /pause, /health): free
  • Free trial: 30 /run calls per PoW token

Links

Metadata

Release files for redlynr 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for redlynr 0.1.1
File Size Uploaded Provenance Actions
redlynr-0.1.1.tar.gz 22.1 kB Without provenance Details for redlynr-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for redlynr 0.1.1
File Interpreter ABI Platform Actions
redlynr-0.1.1-py3-none-any.whl Python 3 none any Details for redlynr-0.1.1-py3-none-any.whl

Total release size: 37.8 kB

Release files / redlynr-0.1.1.tar.gz

Download URL redlynr-0.1.1.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e2036d39699dc284fb73a91c2207a412e755dab3be1480f5e8539114a524cf04
BLAKE2b-256 checksum
How to use checksums
0f781a67b60a2a4e62e4dc50e6b2b89c44ac6dcc5b2f89ac917dc828dc26abd8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / redlynr-0.1.1-py3-none-any.whl

Download URL redlynr-0.1.1-py3-none-any.whl
Size 15.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b1b5a1d6d96316ec6daaafacf631b226c7b5f5aa4c23b5910bda4ffccde5e4d7
BLAKE2b-256 checksum
How to use checksums
57032261e830dbea7f83444e8e7594d665c3120560d06ff777aa886f892926b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for redlynr 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page