Python SDK
Source: rahvis/cci-sdk. Package
cci-sdk, module cli_sdk, Python 3.9 or later. Dependencies: httpx
and numpy.
Install
pip install cci-sdkEverything below is importable from the top-level package:
from cli_sdk import (
CLIClient, AsyncCLIClient, RetryConfig, # clients
Belief, Set, Interval, Gate, Claim, Judge, Route, # queries
BeliefAnswer, SetAnswer, IntervalAnswer, GateAnswer, # answers
ClaimAnswer, JudgeAnswer, RouteAnswer, EvaluateResponse, Guarantee, Usage,
CalibrationProfile, CalibrationExample, LocalMonitor, Alert, # calibration, monitoring
OpenAIBackend, AzureOpenAIBackend, AnthropicBackend, GeminiBackend, # backends
BedrockBackend, OpenRouterBackend, VLLMBackend, SGLangBackend, CustomBackend,
CLIError, AuthenticationError, ValidationError, # errors
InsufficientCalibrationError, RateLimitError, BackendError, ConfigurationError,
)The HTTP clients are imported lazily, so import cli_sdk and the offline
engine cli_sdk.stats never load the HTTP stack.
Clients
from cli_sdk import Belief, CLIClient, OpenAIBackend
with CLIClient(backend=OpenAIBackend(model="gpt-4.1-2025-04-14")) as client: # default backend
result = client.evaluate(
context={"message": "Help! My payouts have been failing for 3 days."},
queries={"urgent": Belief(instructions="Does this message convey urgency?",
calibration_profile="urgency-v1")},
)CLIClient and AsyncCLIClient take the same arguments:
| Argument | Default | Meaning |
|---|---|---|
api_key | CLI_API_KEY | Bearer token. Required unless base_url is on localhost, 127.0.0.1, 0.0.0.0 or ::1; otherwise a missing key raises ConfigurationError. |
base_url | CLI_BASE_URL, else https://cci.gitdate.ink/api/v1 | API root. |
backend | None | Default backend for evaluate. |
timeout | 60.0 | Seconds, passed to the httpx client. |
retry | RetryConfig() | Retry policy; see Errors and retries. |
strict_guarantees | False | Raise InsufficientCalibrationError instead of returning heuristic answers. |
sample_count | 20 | Samples requested from a CustomBackend at L0. |
default_headers | None | Extra headers on every request. |
http_client | None | Your own httpx.Client / httpx.AsyncClient (proxies, custom TLS, httpx.MockTransport in tests). The SDK does not close a client you pass in. |
| Method | Returns |
|---|---|
evaluate(context, queries, backend=None, response_model=None) | EvaluateResponse |
list_backends() | list[dict] |
calibration_profiles | Profile, audit, and monitor operations (Calibration profiles) |
close() | Closes the HTTP client; called for you by with / async with |
On AsyncCLIClient, evaluate, list_backends, close, and every
calibration_profiles method are coroutines.
Queries
| Class | Required | Optional |
|---|---|---|
Belief | instructions, calibration_profile | criteria |
Set | instructions, options (2 or more), calibration_profile | alpha, method, group_by, backend_access_hint |
Interval | instructions, levels (2 to 10), calibration_profile | alpha, method |
Gate | instructions, calibration_profile, target | guarantee (default "risk"), delta, loss |
Claim | instructions, calibration_profile | alpha, support_source |
Judge | instructions, calibration_profile | alpha, cascade |
Route | cascade, calibration_profile | guarantee (default "cost_budget"), target_cents, alpha |
Queries are dataclasses that validate in __post_init__ and raise
ValueError on a missing or invalid field. query.to_payload() returns
the exact JSON object sent on the wire.
Responses
result = client.evaluate(...)
result.answers # dict: query id -> typed answer
result.usage # Usage(backend_calls=..., backend_tokens=...)
result.request_id # from the x-request-id header
result.warnings # list[str]
result.backend # dict
result.heuristic_answers # ids of answers that carry no formal guaranteeEvery answer is a frozen dataclass with guarantee (a
Guarantee), raw (the untouched payload), and
is_heuristic, plus its own fields:
| Answer | Fields and helpers |
|---|---|
BeliefAnswer | probability, venn_abers, interval_width, straddles(threshold) |
SetAnswer | set, probabilities, venn_abers, is_singleton, is_empty, top |
IntervalAnswer | point_estimate, interval, legend, width, contains(value) |
GateAnswer | decision, approved |
ClaimAnswer | retained_claims, dropped_claims, retention_rate, as_text(separator=" ") |
JudgeAnswer | winner, escalated_to, needs_human |
RouteAnswer | served_by, escalated, cost_cents, output |
Typed responses
Subclass EvaluateResponse and annotate query ids with their answer types
to get attribute access, checked when the response is parsed:
from cli_sdk import CLIClient, EvaluateResponse, GateAnswer, Gate, OpenAIBackend, Set, SetAnswer
class RoutingResponse(EvaluateResponse):
department: SetAnswer
route: GateAnswer
with CLIClient() as client:
result = client.evaluate(
context={"ticket": "I was charged twice."},
backend=OpenAIBackend(model="gpt-4.1-2025-04-14"),
queries={
"department": Set(
instructions="Which team should handle this ticket?",
options={"billing": "Payments, invoicing, refunds",
"technical": "Bugs, outages, integrations"},
calibration_profile="support-routing-v3",
),
"route": Gate(
instructions="Auto-route this ticket without human review?",
calibration_profile="support-routing-v3",
guarantee="fdr",
target=0.05,
),
},
response_model=RoutingResponse,
)
assert result.department is result.answers["department"]A declared answer that is missing raises ValueError; one of the wrong
type raises TypeError; a response_model that is not an
EvaluateResponse subclass raises ConfigurationError.
Backends
Typed backend classes (OpenAIBackend, AzureOpenAIBackend,
AnthropicBackend, GeminiBackend, BedrockBackend,
OpenRouterBackend, VLLMBackend, SGLangBackend) or plain dicts with a
"provider" key are accepted anywhere a backend is. Their fields are on
Backends.
Bring your own model
from cli_sdk import CLIClient, CustomBackend
class MyEngine(CustomBackend):
access_level = "L3"
def score_options(self, context, instructions, options):
return my_model.option_probabilities(context, instructions, list(options))
def sample(self, context, instructions, n):
return [my_model.generate(context, instructions) for _ in range(n)]
def score_text(self, context, instructions, candidate):
return my_model.support_score(context, instructions, candidate)
client = CLIClient(backend=MyEngine(base_url="http://localhost:8000"))Implement only the methods your access level supports. The SDK calls them
locally and sends only the evidence, with backend provider
"client_evidence". The full method table, and which queries use which
method, is on Backends: bring your own model.
Calibration profiles and monitors
client.calibration_profiles.create(name, backend, method="APS", alpha=0.10,
group_by=None, prompt_template_hash=None,
strict_fingerprint=True) # -> CalibrationProfile
client.calibration_profiles.get(name) # -> CalibrationProfile
client.calibration_profiles.list() # -> list[CalibrationProfile]
client.calibration_profiles.add_examples(name, examples) # -> CalibrationProfile
client.calibration_profiles.label_with_judge(name, judge, unlabelled_examples,
human_labelled_sample_size=300) # -> dict
client.calibration_profiles.audit(name, fresh_examples) # -> AuditResult
client.calibration_profiles.monitors.create(profile, type="coverage", target=None,
false_alarm_rate=0.05,
labelled_sample_rate=None,
use_judge_pseudo_labels=False) # -> Monitor
client.calibration_profiles.monitors.list(profile) # -> list[Monitor]
client.calibration_profiles.monitors.poll(profile, monitor_id=None) # -> iterator of AlertDetails: Calibration profiles, Drift monitoring.
Network-free helpers live in cli_sdk.calibration (audit_coverage,
audit_risk, clopper_pearson, judge_quality,
estimate_rate_with_judge, recommended_size) and cli_sdk.LocalMonitor.
Errors and retries
429, 502, and 529 are retried with full-jitter exponential backoff,
honoring retry-after:
from cli_sdk import CLIClient, RetryConfig
client = CLIClient(retry=RetryConfig(max_attempts=5, base_delay=0.5, max_delay=8.0))The full exception map, the 424 heuristic behavior, and
strict_guarantees are on Errors and retries.
Package layout
src/cli_sdk/
client/ sync and async clients, retry policy
queries/ Belief, Set, Interval, Gate, Claim, Judge, Route
answers/ typed answers, the Guarantee card, EvaluateResponse
calibration/ profiles, examples, audits, label-efficient calibration
monitoring/ hosted monitors, alerts, LocalMonitor
backends/ one adapter per provider, plus CustomBackend
stats/ offline engine: conformal/, venn_abers/, evalues/
cli_tool.py the `cli` command
exceptions.py CLIError and subclasses
constants.py base URL, environment variable names, guarantee types, access levels