Skip to main content

Tenet AI SDK

Git for AI Agent Decisions - Complete audit trail, replay, and divergence detection for enterprise AI agents.

PyPI version Python 3.9+ License: MIT

Why Tenet AI?

When your AI agent approves a $10,000 refund or denies a loan application, can you answer:

  • Why did the agent make that decision?
  • What context did it have at the time?
  • Would it decide the same way if we ran it again?
  • Who overrode the agent's recommendation?

Tenet AI captures every decision your agent makes with full context, enabling audit trails, replay verification, divergence detection, and guardrails enforcement.


Installation

pip install tenet-ai

With framework integrations:

pip install tenet-ai[langchain]     # LangChain
pip install tenet-ai[google-adk]    # Google ADK
pip install tenet-ai[crewai]        # CrewAI
pip install tenet-ai[openai]        # OpenAI Assistants
pip install tenet-ai[all]           # All integrations

30-Second Quickstart

from tenet import TenetClient, ActionOption, ResultType

tenet = TenetClient(api_key="tnt_your_key")

with tenet.intent("Process refund request", agent_id="support-agent") as intent:
    # 1. Capture what the agent saw
    intent.snapshot_context({
        "customer_tier": "gold",
        "order_amount": 149.99,
        "days_since_delivery": 5,
    })

    # 2. Record the decision with options considered
    intent.decide(
        options=[
            ActionOption(action="approve_refund", score=0.95, reason="Gold customer, within policy"),
            ActionOption(action="deny_refund", score=0.05, reason="N/A"),
        ],
        chosen_action="approve_refund",
        confidence=0.95,
        reasoning="Customer eligible per 30-day return policy"
    )

    # 3. Record execution
    intent.execute(action="approve_refund", target={"order_id": "ORD-123"}, result=ResultType.SUCCESS)

Now query any decision: "Why did we approve this refund?" - Full context, reasoning, and alternatives considered.


Key Features

1. Decision Replay with Divergence Detection

Store the prompt and re-run decisions to verify consistency:

from tenet import TenetClient, ActionOption, ReplayConfig

tenet = TenetClient(api_key="tnt_your_key")

# Record decision WITH replay data
with tenet.intent("Handle support ticket") as intent:
    intent.snapshot_context({"ticket_id": "T-123", "priority": "high"})

    intent.decide(
        options=[ActionOption(action="escalate", score=0.9)],
        chosen_action="escalate",
        confidence=0.9,
        reasoning="High priority ticket requires escalation",
        # Enable replay
        replay_prompt="You are a support agent. Ticket: high priority. Action?",
        replay_config=ReplayConfig(model="gpt-4o-mini", temperature=0.0),
    )

    exec_id = intent.execute(action="escalate", target={"ticket_id": "T-123"}, result=ResultType.SUCCESS)

# Later: Verify the decision is still consistent
replay_result = tenet.replay(exec_id, force_llm_call=True)

if replay_result["diverged"]:
    print(f"WARNING: Model behavior changed!")
    print(f"Original: {replay_result['original_decision']['chosen_action']}")
    print(f"Replayed: {replay_result['replayed_decision']['chosen_action']}")
    print(f"Reason: {replay_result['divergence_reason']}")
else:
    print("Decision verified - model behavior consistent")

2. Multi-Agent Tracking (Parent-Child Intents)

Track complex workflows with multiple agents or MCP tool calls:

with tenet.intent("Process customer request", agent_id="orchestrator") as parent:
    parent.snapshot_context({"request": "refund for order 123"})

    # Spawn child intent for sub-agent
    with parent.child_intent("Search order history", mcp_server="database") as child:
        child.snapshot_context({"query": "order 123"})
        child.decide(
            options=[ActionOption(action="query_db", score=1.0)],
            chosen_action="query_db",
            confidence=1.0
        )
        child.execute(action="query_db", target={"order_id": "123"}, result=ResultType.SUCCESS)

    # Parent continues with child's result
    parent.decide(...)
    parent.execute(...)

3. Human Override Tracking

When humans override agent decisions:

intent.execute(
    action="deny_refund",  # Human chose differently
    target={"order_id": "123"},
    result=ResultType.SUCCESS,
    actor=ActorType.HUMAN,
    override_reason="Customer has history of fraud"
)

4. Guardrails — Block, Warn, or Log Agent Actions

Define rules that are evaluated server-side before decisions are recorded. Rules that trigger with block enforcement reject the decision with HTTP 403.

from tenet import TenetClient

tenet = TenetClient(api_key="tnt_your_key")

# Create a guardrail rule (stored server-side)
tenet.create_guardrail({
    "name": "Block dangerous actions",
    "rule_type": "action_blocklist",
    "enforcement_level": "block",          # block | warn | log
    "rule_config": {"blocked_actions": ["delete_account", "drop_table"]},
})

# Supported rule types:
#   action_allowlist  — only allow specific actions
#   action_blocklist  — block specific actions
#   confidence_threshold — require minimum confidence
#   context_field_check — check a context input field (e.g. amount > 10000)
#   regex_match       — regex on action or field
#   json_path_check   — dot-path check into context
#   value_range       — min/max range on any numeric field

SDK-Side Pre-Flight Evaluation

Run the same rules locally before calling the server:

from tenet import GuardrailEvaluator, GuardrailRule, EnforcementLevel, GuardrailType

evaluator = GuardrailEvaluator()

# Option A: Sync rules from server
evaluator.sync_from_server(tenet)

# Option B: Add rules manually
evaluator.add_rule(GuardrailRule(
    name="Min confidence",
    rule_type=GuardrailType.CONFIDENCE_THRESHOLD,
    enforcement_level=EnforcementLevel.BLOCK,
    rule_config={"min_confidence": 0.7},
))

# Evaluate locally
result = evaluator.evaluate(
    chosen_action="approve",
    confidence=0.5,
    context_inputs={"amount": 500},
)

if result.blocked:
    print(f"Blocked by: {result.blocking_rule}")
elif result.warnings:
    print(f"Warnings: {result.warnings}")
else:
    print(f"All clear — {result.triggered_count} rules triggered")

5. Ghost SDK — Zero-Impact Observability

The Ghost SDK ensures Tenet never slows down your agent. All write operations (intent, context, decision, execution) are pushed to a background queue and sent asynchronously. Your LLM agent never waits for Tenet.

The Ghost SDK Promise:

  • Main-thread overhead: <5ms per full decision lifecycle (4 writes)
  • Silent failure: SDK errors never propagate to your host process
  • Zero network dependency on the write path — your agent runs at full speed
  • 100% uptime guarantee for your primary process

How it works: The SDK generates UUIDs client-side and enqueues JSON payloads onto a thread-safe background queue. A single daemon thread drains the queue and sends HTTP requests. If the network is down, payloads are retried up to max_retries times then dropped — your agent is never affected.

import tenet

# Ghost SDK is enabled by default — no code changes needed
tenet.init(api_key="tnt_your_key")

with tenet.trace(agent_id="finance-bot-v1") as decision:
    response = agent.run(prompt)
    # All writes happen in the background — zero blocking

# In tests, call flush() to wait for writes to land
tenet.flush()

# For graceful shutdown
tenet.shutdown()

Configuration:

Parameter Default Description
async_writes True Set to False for synchronous mode (tests, debugging)
on_error None Callback (exception, payload) for observability
max_retries 3 Retry count before silent drop

Testing with flush():

# flush() blocks until all queued writes are sent
tenet.init(api_key="tnt_your_key")

with tenet.trace(agent_id="test-agent") as decision:
    decision.decide(chosen_action="approve", confidence=0.9)
    decision.execute(action="approve", target={"order": "123"})

tenet.flush()  # Wait for writes — then assert on read-back

# Or disable async entirely for simpler test setup:
tenet.init(api_key="tnt_your_key", async_writes=False)

6. Capture Health Monitoring

Check whether data is actually reaching the Tenet backend — useful for integration health checks and alerting:

stats = tenet.get_stats()
# {
#   'sent': 42,
#   'failed': 0,
#   'dropped': 0,
#   'last_success_at': '2026-03-15T12:00:00+00:00'
# }

if stats['failed'] > 0:
    print(f"WARNING: {stats['failed']} captures did not reach Tenet")
if stats['dropped'] > 0:
    print(f"WARNING: {stats['dropped']} captures were dropped (queue full or retries exhausted)")
  • sent — total successful writes to the backend
  • failed — writes that exhausted all retries without success (data NOT in Tenet)
  • dropped — writes dropped because the background queue was full
  • last_success_at — ISO timestamp of the most recent successful write

Failed and dropped captures also emit WARNING-level log messages via logging.getLogger("tenet.transport") so they surface in your production logs automatically.


Getting Started

1. Create an Account

Go to tenetai.dev and sign in with Google.

2. Create a Workspace

After signing in, create a workspace (e.g., "Production Agents").

3. Generate an API Key

Navigate to API Keys > Create API Key > Copy your key (tnt_xxxx...).

4. Use the SDK

from tenet import TenetClient

tenet = TenetClient(api_key="tnt_your_key")

Framework Integrations

LangChain

from langchain_openai import ChatOpenAI
from tenet import TenetClient, ActionOption, ResultType

tenet = TenetClient(api_key="tnt_xxx")
llm = ChatOpenAI(model="gpt-4o-mini")

def run_agent(query: str):
    with tenet.intent(query, agent_id="langchain-agent") as intent:
        intent.snapshot_context({"query": query})

        response = llm.invoke(query)

        intent.decide(
            options=[ActionOption(action="respond", score=0.9)],
            chosen_action="respond",
            confidence=0.9,
            reasoning=response.content[:100]
        )
        intent.execute(action="respond", result=ResultType.SUCCESS)
        return response.content

Google ADK

from google.adk.agents import Agent
from tenet import TenetClient
from tenet.integrations.google_adk import TenetTracker

tenet = TenetClient(api_key="tnt_xxx")
tracker = TenetTracker(tenet, agent_id="support-agent")

agent = Agent(name="support", model="gemini-2.0-flash")

with tracker.track("Handle customer request") as t:
    t.context({"customer_id": "123"})
    # ... run agent ...
    t.decision(chosen="resolve", confidence=0.92, options=[...])
    t.execute(action="resolve", result="success")

CrewAI

from crewai import Agent, Task, Crew
from tenet import TenetClient
from tenet.integrations.crewai import TenetCrewTracker

tenet = TenetClient(api_key="tnt_xxx")
tracker = TenetCrewTracker(tenet)

crew = Crew(agents=[...], tasks=[...])

with tracker.track_crew("Content pipeline") as t:
    result = crew.kickoff()
    t.record_result(result, agents=[...], tasks=[...])

API Reference

TenetClient

tenet = TenetClient(
    api_key="tnt_xxx",                                # Required
    endpoint="https://ael-backend.onrender.com",    # Optional (default: production)
    timeout=30.0                                       # Optional
)

Core Methods

Method Description
tenet.intent(goal, agent_id) Context manager for tracking an intent
intent.snapshot_context(inputs) Capture what the agent sees
intent.decide(options, chosen_action, confidence) Record a decision
intent.execute(action, target, result) Record execution
tenet.replay(execution_id, force_llm_call) Replay and verify a decision
tenet.get_execution(id) Get execution details
tenet.get_session_timeline(session_id) Get full session timeline
tenet.get_stats() Return {sent, failed, dropped, last_success_at} capture counters

Replay Methods

Method Description
tenet.replay(exec_id) Compare against original (no LLM call)
tenet.replay(exec_id, force_llm_call=True) Re-run LLM and check for divergence

Multi-Agent Methods

Method Description
intent.child_intent(goal, mcp_server) Create child intent
tenet.get_intent_hierarchy(id) Get parent chain (breadcrumbs)
tenet.get_intent_tree(id) Get full descendant tree

Guardrail Methods

Method Description
tenet.create_guardrail(data) Create a guardrail rule
tenet.list_guardrails(is_active?) List guardrails
tenet.get_guardrail(id) Get guardrail by ID
tenet.update_guardrail(id, data) Update a guardrail
tenet.delete_guardrail(id) Delete a guardrail

Guardrail Evaluator (Local Pre-Flight)

Method Description
GuardrailEvaluator() Create a local evaluator
evaluator.add_rule(rule) Add a rule for local evaluation
evaluator.sync_from_server(client) Pull active rules from server
evaluator.evaluate(action, confidence, ...) Run all rules locally
evaluator.clear_rules() Remove all local rules

Dashboard

View your agent's decisions at tenetai.dev:

  • Execution Timeline: See all decisions chronologically
  • Session View: Group related decisions by session
  • Decision Details: Full context, options considered, and reasoning
  • Hierarchy View: Navigate parent-child intent relationships

Use Cases

Industry Use Case
FinTech Audit loan decisions, fraud detection reasoning
Healthcare Track triage recommendations, document clinical AI decisions
E-commerce Refund approvals, pricing decisions, inventory management
Legal Contract analysis decisions, compliance checking
HR Resume screening, candidate ranking explanations

License

MIT License - see LICENSE for details.


Metadata

Release files for tenet-ai 0.6.0

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

Source distribution (sdist)

Source distribution for tenet-ai 0.6.0
File Size Uploaded Provenance
tenet_ai-0.6.0.tar.gz 33.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for tenet-ai 0.6.0
File Interpreter ABI Platform
tenet_ai-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 67.9 kB

Release files / tenet_ai-0.6.0.tar.gz

Download URL tenet_ai-0.6.0.tar.gz
Size 33.3 kB
Tags Source
SHA-256 checksum
How to use checksums
dbca98e744b940bd6ca450798f46a55057ccaa4258390afaabf981adbd9905dd
BLAKE2b-256 checksum
How to use checksums
4c97fbd4c1af8840d8d844719e182224d185e340bf14c9705b473df40c12a503
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.8

Release files / tenet_ai-0.6.0-py3-none-any.whl

Download URL tenet_ai-0.6.0-py3-none-any.whl
Size 34.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
718651c3aad6efad282533d3bf53278320ad5f3d6c9ed9e323c842511d9256c0
BLAKE2b-256 checksum
How to use checksums
10795fc10361e433407372eb580627150a5af18b278c9a8e1ba4d2740856746f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.8

Security for tenet-ai 0.6.0

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.

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