Official Python SDK for the zenture Public API.
Project description
The official zenture-sdk
Official server-side only Python SDK for the zenture Public API.
zenture-sdk is for backend services, automation jobs, evaluation pipelines,
CI tasks, and controlled notebook environments. zenture API tokens are
server-side credentials. Do not put them in browsers, mobile apps, frontend
bundles, public notebooks, logs, analytics, traces, or customer-visible errors.
Package Names
- Distribution:
zenture-sdk - Import package:
zenture - Sync client:
Zenture - Async client:
AsyncZenture - Supported Python: Python 3.11, 3.12, and 3.13
Installation
pip install zenture-sdk
Until the public beta is published to PyPI, use the internally shared wheel or release artifact from the repository owner.
Authentication
Create API tokens in the existing zenture webapp with an existing account:
https://ai.zenture.app/profile?tab=api-tokens. The SDK does not provide an
API-token management surface.
Keep the token outside Python source code:
# .env, not committed
ZENTURE_API_KEY=your_webapp_created_api_token
set -a
. ./.env
set +a
The SDK intentionally does not parse .env files. Load environment variables
through your runtime, deployment platform, secrets manager, or preferred local
loader. Public SDK usage targets the production API origin:
https://api.zenture.app.
Local Scratch Workspace
For ad-hoc local experiments, create a scratch/ directory in the repository
root:
mkdir -p scratch
Use it for temporary request payloads, response captures, throwaway scripts, and local notes while testing the SDK. The directory is ignored by Git and must not contain real API tokens, production data, customer data, or other secrets.
Reproducible tests belong in tests/; reusable examples belong in examples/.
API Documentation
The public API documentation lives at
https://www.zenture.app/developers. Treat the committed OpenAPI
artifact in this repository as the SDK's local contract source of truth and use
the public documentation as the human-facing API reference.
For copy-paste SDK usage, routes, and example response structures for every
public call, see docs/sdk-call-reference.md.
For raw JSON response bodies and SDK model mapping, see
docs/response-shapes.md.
Sync Quickstart
from zenture import Zenture
with Zenture.from_env() as client:
print(client.helloworld())
models = client.models.list(mode="single")
for model in models.models:
print(model.id, model.display_name)
Async Quickstart
import asyncio
from zenture import AsyncZenture
async def main() -> None:
async with AsyncZenture.from_env() as client:
result = await client.chat.run(
message="Summarize this support note.",
mode="single",
idempotency_key="case-123-chat-single-v1",
timeout=120.0,
)
print(result.status)
if __name__ == "__main__":
asyncio.run(main())
Models
from zenture import Zenture
with Zenture.from_env() as client:
single_models = client.models.list(mode="single")
multi_models = client.models.list(mode="multi")
print(single_models.models[0].id)
print(multi_models.models[0].id)
Chat
Single-model chat:
from zenture import Zenture
with Zenture.from_env() as client:
available_models = [
model.id
for model in client.models.list(mode="single").models
if model.is_available
]
if not available_models:
raise RuntimeError("No available single-mode model for this token.")
result = client.chat.run(
message="Summarize this customer update.",
mode="single",
model=available_models[0],
idempotency_key="case-123-chat-single-v1",
timeout=120.0,
)
print(result.operation_id, result.status)
print(result.result.amount_billed)
print(result.result.chat_id, result.result.turn_id, result.result.model_response_id)
Multi-model chat:
from zenture import Zenture
with Zenture.from_env() as client:
available_models = [
model.id
for model in client.models.list(mode="multi").models
if model.is_available
]
if len(available_models) < 2:
raise RuntimeError("At least two available multi-mode models are required.")
result = client.chat.run(
message="Compare these draft answers for factual consistency.",
mode="multi",
models=available_models[:2],
idempotency_key="case-123-chat-multi-v1",
timeout=120.0,
)
print(result.status)
print(result.result.amount_billed)
Agentic chat mode is not part of Public V1. Do not document or add public helpers for it in this SDK.
Continue a chat with a follow-up turn:
from zenture import Zenture
from zenture.idempotency import idempotency_key
with Zenture.from_env() as client:
first = client.chat.run(
message="Give me a concise onboarding checklist for a new API user.",
mode="single",
idempotency_key=idempotency_key("case-123", "chat-turn-1", "v1"),
timeout=120.0,
)
chat_id = first.result.chat_id
follow_up = client.chat.run(
message="Turn that checklist into three implementation steps.",
chat_id=chat_id,
mode="single",
idempotency_key=idempotency_key("case-123", "chat-turn-2", "v1"),
timeout=120.0,
)
print(follow_up.result.amount_billed)
print(follow_up.result.turn_id, follow_up.result.model_response_id)
Read chat turns when you need the exact user_message, model_answer, and
model_response_id for evaluation:
from zenture import Zenture
with Zenture.from_env() as client:
messages = client.chat.messages("chat_example")
turn = messages.turns[0]
print(turn.user_message, turn.model_answer, turn.model_response_id)
Input Wizard
from zenture import Zenture
with Zenture.from_env() as client:
result = client.input_wizard.run(
prompt="Improve this onboarding prompt for a support assistant.",
idempotency_key="case-123-input-wizard-v1",
timeout=120.0,
)
print(result.operation_id, result.status)
if result.result and result.result.optimized_prompt:
print(result.result.optimized_prompt)
Evaluations
External answer evaluation creates an external evaluation-only record. It does
not add the submitted content to normal zenture chat history. If the answer
contains sources, include them directly in ai_answer as Markdown links,
footnotes, or plain URLs:
from zenture import Zenture
from zenture.idempotency import idempotency_key
with Zenture.from_env() as client:
result = client.evaluations.run(
user_message="What is zenture?",
ai_answer=(
"zenture evaluates AI outputs. "
"[Source](https://example.com/product-brief)"
),
external_id="support-ticket-123-answer-a",
metadata={"source": "support_bot", "answer_format": "markdown_with_sources"},
idempotency_key=idempotency_key("support-ticket-123-answer-a", "evaluate", "v1"),
timeout=120.0,
)
print(result.operation_id, result.status)
print(result.result.amount_billed)
detail = client.evaluations.get(result.result.evaluation_id)
print(detail.zenture_summary)
print(detail.sources)
Internal zenture chat-answer evaluation uses the AI-answer model_response_id.
That id is not the user-message id.
from zenture import Zenture
from zenture.idempotency import idempotency_key
with Zenture.from_env() as client:
chat = client.chat.run(
message="Draft three customer-support next steps.",
mode="single",
idempotency_key=idempotency_key("case-456", "chat-turn-1", "v1"),
timeout=120.0,
)
chat_id = chat.result.chat_id
turn_id = chat.result.turn_id
model_response_id = chat.result.model_response_id or chat.result.model_response_ids[0]
turn = next(
item for item in client.chat.messages(chat_id).turns
if item.turn_id == turn_id
)
evaluation = client.evaluations.run(
user_message=turn.user_message,
ai_answer=turn.model_answer,
chat_id=chat_id,
turn_id=turn_id,
model_response_id=model_response_id,
idempotency_key=idempotency_key(model_response_id, "evaluate", "v1"),
timeout=120.0,
)
print(evaluation.result.evaluation_id, evaluation.status)
print(evaluation.result.amount_billed)
Use external_id only as optional caller-side correlation. Use
idempotency_key as the required retry-safety key for each mutating request.
End-to-End Chat Evaluation
For a complete local smoke flow, use:
python3 examples/end_to_end_chat_evaluation.py
The script reads configuration from environment variables, calls wallet.get(),
selects an available single-model chat model with a Haiku preference, runs
input_wizard, chats, reads the generated turn, evaluates the answer, and
prints a JSON summary with per-operation amount_billed plus total_billed.
For smaller application code, chat.run(..., include_content=True) attaches
the matching public chat turn as result.chat_turn, and
evaluations.run(..., include_detail=True) attaches the public evaluation
detail as result.evaluation. These flags are explicit so normal operation
polling does not perform extra read requests.
Account Reads
Read wallet, usage, and route limits without creating billable work:
from zenture import Zenture
with Zenture.from_env() as client:
wallet = client.wallet.get()
api_usage = client.usage.get(scope="api")
all_usage = client.usage.get(scope="all")
limits = client.limits.get()
print(wallet.plan, wallet.status, wallet.credits_available.amount)
print(api_usage.operation_count, all_usage.operation_count)
print(limits.operation_statuses)
Operation Polling
Low-level create methods return an operation immediately. Use
client.operations.wait(...) when you want to poll explicitly.
from zenture import Zenture
with Zenture.from_env() as client:
operation = client.chat.create_operation(
message="Review this response.",
mode="single",
idempotency_key="case-123-chat-create-v1",
)
final_operation = client.operations.wait(operation.operation_id, timeout=120.0)
print(final_operation.status)
Terminal statuses are succeeded, failed, cancelled, and expired.
If .run(...) creates an operation and local polling later times out or is
stopped, the exception exposes operation_id and idempotency_key attributes.
Use client.operations.get(exc.operation_id) or retry with the same
exc.idempotency_key. Do not retry a billable mutation with a new key.
Pagination
List-style read helpers support limit and cursor. The default page size is
limit=50, the maximum is 100, and cursors are opaque strings. Responses
include next_cursor; None means there is no further page.
from zenture import Zenture
with Zenture.from_env() as client:
page = client.chat.list(limit=50)
print(page.next_cursor)
for chat in client.chat.iter(limit=50):
print(chat.chat_id)
Available iterators:
client.chat.iter(limit=50, cursor=None)client.chat.iter_messages(chat_id, limit=50, cursor=None)client.evaluations.iter(limit=50, cursor=None)
Idempotency
Mutating routes require Idempotency-Key. Use stable caller-owned keys that do
not contain prompts, answers, API tokens, customer PII, or request bodies.
from zenture import Zenture
from zenture.idempotency import idempotency_key
with Zenture.from_env() as client:
key = idempotency_key("case-123", "chat-turn-1", "v1")
result = client.chat.run(
message="Create a concise summary.",
idempotency_key=key,
timeout=120.0,
)
print(result.idempotency_key)
Example keys:
idempotency_key("case-123", "chat-turn-1", "v1")idempotency_key("case-123", "chat-turn-2", "v1")idempotency_key("support-ticket-123-answer-a", "evaluate", "v1")idempotency_key("response_abc123", "evaluate", "v1")
Errors
from zenture import Zenture
from zenture.errors import (
ZentureAPIError,
ZentureInsufficientCreditsError,
ZenturePollingTimeoutError,
ZentureRateLimitError,
)
with Zenture.from_env() as client:
try:
result = client.chat.run(
message="Summarize this incident.",
idempotency_key="case-123-error-example-v1",
timeout=120.0,
)
print(result.status)
except ZenturePollingTimeoutError as exc:
print(exc.operation_id)
except ZentureInsufficientCreditsError:
wallet = client.wallet.get()
print(wallet.credits_available)
except ZentureRateLimitError as exc:
print(exc.retry_after)
except ZentureAPIError as exc:
print(exc.request_id)
SDK exceptions redact sensitive content. Request and response bodies are not included in exception strings.
Timeout, Retry, Polling, and Rate Limits
- SDK-owned HTTP clients use explicit timeouts: connect
5s, read/write/pool30s. operations.wait(timeout=...)and.run(timeout=...)use a total operation polling budget, not the raw HTTP read timeout.- Polling uses deterministic intervals:
initial_interval=1s, doubled up tomax_interval=8s, with no jitter. - Manual
GET /v1/operations/{operation_id}polling should use the same1s -> 2s -> 4s -> 8scadence, should not poll faster than once per second per operation, and must stop at terminal status. - Retry default is
max_retries=2. - Retryable HTTP responses include
429,500,502,503, and504when the public error code is retryable. Retry-Afteris honored before deterministic exponential backoff.- Mutating requests are retried only when an
Idempotency-Keyis present.
Base URL Policy
- Default production API origin:
https://api.zenture.app - Never derive
base_urlfrom user input. - URL credentials, paths, query strings, fragments, and arbitrary HTTPS origins are rejected.
Public V1 Scope
- No browser, mobile, or frontend bundle usage.
- No API-token management surface.
- No public helper for agentic chat mode in Public V1.
- No top-level exports from internal
_contractmodules.
Local Verification
python3 -m ruff format --check .
python3 -m ruff check .
python3 -m mypy
python3 -m pyright
python3 -m pytest
python3 -m coverage run -m pytest
python3 -m coverage report
python3 -m build
python3 -m twine check dist/*
Security
See SECURITY.md for vulnerability reporting and security
expectations. Do not publish API tokens in issues, logs, screenshots, or support
requests.
License
Apache License 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zenture_sdk-1.0.0rc2.tar.gz.
File metadata
- Download URL: zenture_sdk-1.0.0rc2.tar.gz
- Upload date:
- Size: 85.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e99d11f5d896ea77fb4122d7f30b83946e2c399eca6cd043c8c69b3e6335f9f7
|
|
| MD5 |
99c8227b0981f524171afbbf9821b60b
|
|
| BLAKE2b-256 |
20f901f0cfd6456782c8995c6dcc91539cf8737d2c186df846358dc80fedc824
|
Provenance
The following attestation bundles were made for zenture_sdk-1.0.0rc2.tar.gz:
Publisher:
release.yml on zenture95/zenture-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenture_sdk-1.0.0rc2.tar.gz -
Subject digest:
e99d11f5d896ea77fb4122d7f30b83946e2c399eca6cd043c8c69b3e6335f9f7 - Sigstore transparency entry: 1983014037
- Sigstore integration time:
-
Permalink:
zenture95/zenture-sdk@4a8ed0d078ae6ec761479a6d5e0f7d663dde80b7 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/zenture95
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4a8ed0d078ae6ec761479a6d5e0f7d663dde80b7 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zenture_sdk-1.0.0rc2-py3-none-any.whl.
File metadata
- Download URL: zenture_sdk-1.0.0rc2-py3-none-any.whl
- Upload date:
- Size: 44.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
895fc2c511498fa81b5896d5c749307978804956376d43df038998206a2d0f18
|
|
| MD5 |
1f8fb46c2ac8ff9de13dd44da9fab893
|
|
| BLAKE2b-256 |
60a0cbe83f603ed649adcb64c23c276b80d148700642ab7875ddbc03e2f7c947
|
Provenance
The following attestation bundles were made for zenture_sdk-1.0.0rc2-py3-none-any.whl:
Publisher:
release.yml on zenture95/zenture-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zenture_sdk-1.0.0rc2-py3-none-any.whl -
Subject digest:
895fc2c511498fa81b5896d5c749307978804956376d43df038998206a2d0f18 - Sigstore transparency entry: 1983014103
- Sigstore integration time:
-
Permalink:
zenture95/zenture-sdk@4a8ed0d078ae6ec761479a6d5e0f7d663dde80b7 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/zenture95
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4a8ed0d078ae6ec761479a6d5e0f7d663dde80b7 -
Trigger Event:
workflow_dispatch
-
Statement type: