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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

redlynr-0.1.1.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

redlynr-0.1.1-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file redlynr-0.1.1.tar.gz.

File metadata

  • Download URL: redlynr-0.1.1.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for redlynr-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e2036d39699dc284fb73a91c2207a412e755dab3be1480f5e8539114a524cf04
MD5 aa25c1b0ee744f3ea3ba1b29d079982d
BLAKE2b-256 0f781a67b60a2a4e62e4dc50e6b2b89c44ac6dcc5b2f89ac917dc828dc26abd8

See more details on using hashes here.

File details

Details for the file redlynr-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: redlynr-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 15.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for redlynr-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b1b5a1d6d96316ec6daaafacf631b226c7b5f5aa4c23b5910bda4ffccde5e4d7
MD5 b56291901dd0684700b8a2c19634804a
BLAKE2b-256 57032261e830dbea7f83444e8e7594d665c3120560d06ff777aa886f892926b2

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

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