Skip to main content

Build an LLM agent with spending limits, a tamper-evident audit trail, PII redaction, and record/replay testing built in from the start — a governed agent in about 10 lines.

Project description

cendor-sdk

A governed agent in 10 lines — cost budgets, tamper-evident audit, and PII redaction built in.

PyPI Python License Ruff types: mypy

provider-agnostic · local-first · offline by default · sync and async

The only agent SDK where cost budgets, tamper-evident audit, PII redaction, context governance, and record/replay testing are the foundation, not plugins.

cendor-sdk owns the agent loop, so every governance concern that is best-effort beneath a framework becomes first-class here: usage is never lost, budgets enforce before the model call, PII is redacted before send, and the whole run correlates under one trace_id. It's the simple, batteries-included door into the Cendor stack — you don't need to pick a framework or wire the libraries. (Already have a framework? Compose the libraries beneath it: pip install cendor-libs.)

Install

pip install "cendor-sdk[openai,anthropic]"     # provider SDKs are optional extras
pip install "cendor-sdk[all]"                  # every provider + interop, batteries included

The install bundles the whole Cendor stack (cendor-core, tokenguard, acttrace, contextkit, squeeze, cassette) by dependency — you install once and import only from cendor.sdk. Provider SDKs stay optional extras: [openai], [anthropic], [google], [bedrock], [ollama], [huggingface], [azure], [foundry-local], plus [mcp] and [otel].

A governed agent in 10 lines

from cendor.sdk import Agent, tool, run, budget, guard, Policy, AuditLog

@tool
def get_weather(city: str) -> str:
    """Current weather for a city."""
    return f"Sunny in {city}"

agent = Agent(name="assistant", model="gpt-4o", tools=[get_weather],
              instructions="Answer using tools when helpful.")

log = AuditLog(system="support", risk_tier="limited", path="audit.jsonl")
with budget(usd=0.25, on_exceed="block"), guard(Policy.default(), audit=log):
    result = run(agent, "What's the weather in Paris?", audit=log)

print(result.output)                        # -> "It's sunny in Paris."
print(result.cost, result.usage)            # priced in Decimal, budgeted
print([s.name for s in result.tool_steps])  # -> ["get_weather"]
# audit.jsonl: audit_open -> decision -> llm_call -> tool_call -> llm_call, hash-chained &
# verify()-able, all correlated by one trace_id. Wrap in cassette.using("run.json") to replay it.

Ungoverned still works — on cendor-core alone. Every governance layer is optional and removable; drop the with block and run(agent, ...) runs bare:

from cendor.sdk import Agent, run
result = run(Agent(name="a", model="gpt-4o", instructions="Be brief."), "Hi")
result = await run.aio(agent, "Hi")   # same call, async

run.aio is natively async for OpenAI (Chat + Responses), Anthropic, Ollama, and Hugging Face. Gemini and Bedrock have no native async client, so run.aio runs them synchronously for now.

Why it's different

Provider lock Cost budgets Tamper-evident audit PII redaction Record/replay tests Local-first
OpenAI Agents SDK OpenAI-centric lib
LangGraph agnostic DIY DIY DIY DIY lib
Anthropic Agent SDK Anthropic-centric lib
CrewAI / Pydantic AI / ADK varies ✗/DIY lib
cendor-sdk agnostic built-in built-in built-in built-in yes

Governance is composed through Cendor's existing bus / interceptor / Sink / Compressor seams, correlated by trace()zero SDK-specific glue. Budgets, audit, redaction, and record/replay all ride the agent loop through those seams, so removing any one is just not entering its context.

Multi-agent, one correlated tree

Handoff, supervisor/router, and sequential/parallel pipelines — with the correlation that was impossible beneath frameworks. A whole multi-agent run is one governed, trace_id-correlated tree, on one verifiable audit chain. Handoff even works across providers:

from cendor.sdk import Agent, run

writer  = Agent(name="writer",  model="claude-opus-4-8", instructions="Write the brief.")
planner = Agent(name="planner", model="gpt-4o", instructions="Plan, then hand off.",
                handoffs=["writer"])

result = run([planner, writer], "Research X and write a brief")   # OpenAI -> Anthropic handoff
print(result.agents)     # ["planner", "writer"]

Every major provider — one canonical loop

The provider is inferred from the model id (override with provider=). History is held in one canonical shape, so a run can hand off between providers without rewriting it.

Provider Models Extra
OpenAI Chat Completions + Responses API [openai]
Anthropic Messages API [anthropic]
Google Gemini google-genai [google]
AWS Bedrock Converse API [bedrock]
Ollama local models [ollama]
Hugging Face Inference / endpoints [huggingface]
Azure AI Foundry deployments via the OpenAI v1 endpoint (Chat + Responses) [azure]
Foundry Local on-device, OpenAI-compatible [foundry-local]

More in the box

Everything a real agent needs — all governed through the same seams:

  • Streamingrun.stream / run.astream yield text deltas + tool events (native for the OpenAI family + Ollama).
  • Structured output — a dataclass / Pydantic / JSON-schema output_type uses each provider's native schema mode.
  • Reasoning & controlAgent.extra passes tool_choice, reasoning_effort, top_p, stop, …; o-series temperature is handled for you.
  • RAGVectorIndex + Agent(retriever=…) inject governed retrieval, or expose your store as a @tool.
  • MemorySession (conversation), SummarizingSession (rolling summary), SQLiteSessionStore (durable), context_budget (fit the window).
  • Embeddingsembed() / aembed() capture RAG calls on the same cost/audit tree.
  • Cost governance for any modelregister_model_price(...) so budgets bind on custom / deployment-named ids.

Docs

License

Apache-2.0.

Project details


Download files

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

Source Distribution

cendor_sdk-1.19.1.tar.gz (375.2 kB view details)

Uploaded Source

Built Distribution

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

cendor_sdk-1.19.1-py3-none-any.whl (105.1 kB view details)

Uploaded Python 3

File details

Details for the file cendor_sdk-1.19.1.tar.gz.

File metadata

  • Download URL: cendor_sdk-1.19.1.tar.gz
  • Upload date:
  • Size: 375.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cendor_sdk-1.19.1.tar.gz
Algorithm Hash digest
SHA256 f3ff23c6b9d85c8dc69b29e4fd87366531562b9fbc3d853f631bec9eeaf8fda8
MD5 f23d49057f41896c8ca94295531265d7
BLAKE2b-256 1851393b84366fdb663bec47a54ca9cb9fc4cc4accd931b04493942f518c4b66

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_sdk-1.19.1.tar.gz:

Publisher: release.yml on cendorhq/cendor-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cendor_sdk-1.19.1-py3-none-any.whl.

File metadata

  • Download URL: cendor_sdk-1.19.1-py3-none-any.whl
  • Upload date:
  • Size: 105.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for cendor_sdk-1.19.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d2ce8c5ea57b54e8309348008820501f08a8afa2505ef166038d0a7270107357
MD5 3f135812c434055c52b0fc6d0128853c
BLAKE2b-256 599b8881cdc2246de649c671adee2817287a09a6f8c829c560df60d95e3780dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_sdk-1.19.1-py3-none-any.whl:

Publisher: release.yml on cendorhq/cendor-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page