disclarion
AI disclosure logging SDK. Wraps your LLM provider responses and reports standardized disclosure/labeling metadata to Disclarion.
Install
pip install disclarion
Usage
from disclarion import Disclarion
import openai
client = openai.OpenAI()
dc = Disclarion(api_key="dcl_live_...")
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "hello"}],
)
dc.track(response, session_id="session-123")
track() never modifies response by default — it attaches a
disclarion_meta dict to it instead, and your own UI decides what to show:
tracked = dc.track(response, session_id="session-123")
tracked.disclarion_meta
# {"ai_generated": True, "is_first_message": True, "disclosed": False, "logged_at": "..."}
is_first_message is what a host-app disclosure widget should key off of —
show a one-time blocking notice on True, keep a small persistent badge up
for the rest of the session on False. A ready React and vanilla-JS
implementation of exactly that pattern ships inside every install — find it
with:
python -c "import disclarion; print(disclarion.widget_dir())"
This is the disclosure_mode="modal_once" default, set on the client, not
per call:
modal_once(default) — never touches response content; onlydisclarion_metachanges, as above.inline_every_message— the SDK's original behavior, kept for backward compatibility. Despite the name, it still only rewrites the response's own content on a session's first message (never later ones) to append/prependdisclosure_text;disclarion_meta["disclosed"]reflects whether that happened.off— pure logging, no disclosure signal at all:disclarion_metaomitsis_first_message/disclosedentirely. Use this if your product already has its own unrelated AI-disclosure UI.
dc = Disclarion(api_key="dcl_live_...", disclosure_mode="inline_every_message")
Regardless of disclosure_mode, every track() call is still logged to
Disclarion's audit trail (interaction_logs) exactly the same way — the UI
mode only changes what happens to response and disclarion_meta, never
what gets recorded for compliance.
track() makes one fast synchronous attempt (that's what makes
is_first_message available immediately) and never retries inline — a
slow or erroring Disclarion backend can never turn into multi-second
latency on your own request path. Anything worth retrying (a network
error, a 5xx, a transient 429) is handed to a background thread with
a bounded queue (up to 1000 pending logs, oldest dropped first if your
process is generating logs faster than Disclarion can accept them) that
retries with capped, jittered exponential backoff, honoring a 429's
Retry-After header when present. The queue is flushed on normal
process exit, SIGINT, and SIGTERM (bounded to a couple of seconds, so
shutdown is never blocked indefinitely by a backend that's still down).
A 429 that means "you've exceeded your monthly plan quota" is handled
differently from a transient rate limit: retrying it is pointless until
next period, so the SDK opens a circuit breaker instead — every track()
call skips the network entirely (sub-millisecond) until an hourly (then
exponentially longer, capped at 24h) cooldown elapses and one probe
request checks whether it's resolved. One logging.warning marks each
state change (opened/resolved), not one per call, so a month-long outage
doesn't spam your logs. Call dc.get_stats() any time for the queue size,
circuit state, and delivery counters.
An invalid or revoked API key is different from all of the above: the
backend is reachable and is explicitly rejecting it, which is a setup
mistake you need to see, so that raises disclarion.AuthenticationError
immediately — no retry, no circuit-breaking, no silent fallback.
Unsupported response types raise a ValueError immediately for the same
reason: both are programming/config errors to fix, not a runtime
condition to swallow.
OpenAI (ChatCompletion), Anthropic (Message), and Gemini
(GenerateContentResponse) responses are all supported — see
disclarion/adapters/.
Using this with an async framework (FastAPI, aiohttp, Starlette, ...)
track() is synchronous under the hood (it uses requests) and blocks for
up to FAST_PATH_TIMEOUT_SECONDS (currently 1.5s) even on a healthy
backend. Called directly inside an async def handler, that blocks the
whole worker's event loop for that long — stalling every other concurrent
request on the same worker, not just the one that called track(). Get it
off the event loop:
# FastAPI / Starlette
from starlette.concurrency import run_in_threadpool
tracked = await run_in_threadpool(dc.track, response, session_id=session_id)
# Any asyncio app, no Starlette dependency
import asyncio
tracked = await asyncio.to_thread(dc.track, response, session_id=session_id)
This applies even if the backend you're calling is your own service — a
synchronous track() call made from inside an async handler can, in the
worst case, block that same process against itself.
Testing without a live backend
To exercise _send_log() end to end without hitting the real
api.disclarion.com:
python scripts/mock_server.py # terminal 1 - fake /v1/logs endpoint
python scripts/manual_test.py # terminal 2 - sends a fake ChatCompletion
The mock server prints every received payload and the Authorization
header so you can confirm the normalized log shape is correct. See
scripts/README.md for details, or tests/ for the automated unit tests
(pytest) which mock the HTTP layer instead of needing a running server.
Metadata
Release files for disclarion 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| disclarion-0.2.3.tar.gz | 28.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| disclarion-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.5 kB
Release files / disclarion-0.2.3.tar.gz
| Download URL | disclarion-0.2.3.tar.gz |
|---|---|
| Size | 28.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
16d8d45ecd2dc72af7efe6c43f01230dab4378e888a68cf9999aa78868927d67
|
|
BLAKE2b-256 checksum How to use checksums |
bcf2c79ed88a5e1dabf0609c1193818ef4f8bc3e7efadfb9e3c166e82ddac462
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / disclarion-0.2.3-py3-none-any.whl
| Download URL | disclarion-0.2.3-py3-none-any.whl |
|---|---|
| Size | 23.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8a150bb927e720da9af44962f3d24e14559c367a845a87794001737929ab0ac1
|
|
BLAKE2b-256 checksum How to use checksums |
5d7d18c852878c5f0da495bbe102d16181ec0c3687e76e6a04562b554e4dbd10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|