Ciphyrs — observability for AI agents
See every agent, tool call and model call your application makes, as one connected graph. Built on OpenTelemetry, so the spans are standard OTLP and sit beside whatever tracing you already have — and your model calls stay yours, made directly against your provider.
Install
pip install ciphyrs
One command. OpenTelemetry and httpx come with it. Python 3.9 through
3.14, including the free-threaded 3.14 build (PEP 703).
No framework-specific install, and none exists. @agent and @tool wrap
plain functions, so they work with any agent framework, or none.
Monitor your agents
import ciphyrs
ciphyrs.init(api_key="cyp_live_...", project="customer-support")
@ciphyrs.tool
def issue_refund(order: str, amount: float) -> dict:
return payments.refund(order, amount) # traced, and checked against policy
@ciphyrs.agent("BillingAgent", role="worker")
def billing(question: str) -> str:
answer = my_model.generate(question) # your model call, unchanged
issue_refund(order="A-1041", amount=12.0)
return answer
@ciphyrs.agent("RouterAgent", role="router")
def router(message: str) -> str:
return billing(message) # called INSIDE router -> a RouterAgent→BillingAgent edge
with ciphyrs.session("conv-7f3a"): # ties one conversation together
router("I was charged twice for order A-1041")
ciphyrs.shutdown() # flush; short-lived scripts only
That is the whole integration. Nesting is the topology: because billing
was called inside router, the dashboard draws the edge. Both agents appear
in the fleet at start-up — decorators register themselves, before any traffic.
init() |
Configures an OTLP exporter to Ciphyrs, or attaches to a TracerProvider you already have and leaves your exporters alone. Falls back to CIPHYRS_API_KEY, CIPHYRS_PROJECT, CIPHYRS_BASE_URL. |
@agent |
One span per call, named for the agent. Everything it calls nests underneath. |
@tool |
One span per tool call, with arguments and result. Also checked against policy before it runs — enforce inherits init(enforce=True), so a block verdict raises ToolBlocked and the function never executes. Pass enforce=False to trace only. |
session() |
Tags every span inside with session.id. |
init(agents=...) |
Declares the roster and the designed peer graph at boot, so the fleet is complete before the first request. Starts a heartbeat (default 60 s) so idle and dead are distinguishable. |
Async is automatic — declare the function async def and the decorators
install async wrappers. Every platform call they make runs off your event
loop.
See the inputs and outputs
On by default. Pass capture_io=False to an individual @agent or @tool
whose arguments must not be recorded; its spans still carry timing, nesting
and errors.
Agents in separate processes
Already on. Outgoing HTTP requests carry the trace context, so an agent that
calls another service shows up as one connected graph rather than two
disconnected fleets. init(propagate=False) turns it off.
Model and framework internals
Ciphyrs is not in the path of your model call and does not want to be. For
spans inside a framework or provider — retriever calls, per-node detail —
add that framework's own OpenTelemetry instrumentation (OpenInference or
OpenLLMetry). Its spans travel through the exporter init() already installed
and nest inside your agent spans.
Is it actually on?
state = ciphyrs.selfcheck()
if state["problems"]:
log.error("Ciphyrs is not live in this process: %s", state["problems"])
Reports what is genuinely running and names each problem in words. Makes no network call, so it is safe in a readiness probe.
Beyond tracing
The platform also enforces policy on agent messages, masks PII, and can
quarantine a misbehaving agent from the dashboard. Tool checks, message
checks and PII masking are all on by default once init() has an API key,
which means text leaves your process to be evaluated. The documentation says
what each one sends and how to turn it off (enforce=, guard_input=,
guard_output=, pii=).
Release files for ciphyrs 4.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ciphyrs-4.1.1.tar.gz | 287.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ciphyrs-4.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 460.2 kB
Release files / ciphyrs-4.1.1.tar.gz
| Download URL | ciphyrs-4.1.1.tar.gz |
|---|---|
| Size | 287.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aae025fdf0d4956c1ff208c89945c404eefb2dc9203e3945b0fffa796a5861fa
|
|
BLAKE2b-256 checksum How to use checksums |
f295a56e61c38d8288208ba8f0d1977cf5a65217778eb5a70cd2deac0555693b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / ciphyrs-4.1.1-py3-none-any.whl
| Download URL | ciphyrs-4.1.1-py3-none-any.whl |
|---|---|
| Size | 173.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8640e8b947338b02138070b71d7e0a80cdd4079b9f948fa85f94fae810c33849
|
|
BLAKE2b-256 checksum How to use checksums |
a4f1701cab6c965af9924b9d4f23ef20d16eba8706bd22a86c1f4e8a20cb72a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|