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.0.tar.gz (368.4 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.0-py3-none-any.whl (103.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cendor_sdk-1.19.0.tar.gz
  • Upload date:
  • Size: 368.4 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.0.tar.gz
Algorithm Hash digest
SHA256 329f9b110481755a78a88959ba160f47891beb81d1a6b182b07ed27660a4e601
MD5 1b9325357fc73db575d608706aa97b32
BLAKE2b-256 d0edf39e13d7325000588786b972b117f22439c066fc997b271ded503448bef9

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_sdk-1.19.0.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.0-py3-none-any.whl.

File metadata

  • Download URL: cendor_sdk-1.19.0-py3-none-any.whl
  • Upload date:
  • Size: 103.6 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b8d5c33254a71a9787ca8777ca14ac95946c236218f617bace4cc0d2ec002e28
MD5 0515944754ea6115e509c0c89d8b4b16
BLAKE2b-256 632a5e23bb693f85f800ae6505920d337aaf4eec69af9ab6f4f82c309ce7d8dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for cendor_sdk-1.19.0-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