coding-sh (Python SDK)
Send LLM calls, traces and tool spans from any Python service to a
coding.sh server (coding.sh serve). Stdlib only, Python 3.9+.
pip install coding-sh
export CODING_SH_URL=https://obs.example.com CODING_SH_INGEST_KEY=csh_ik_...
import coding_sh
from openai import OpenAI
client = coding_sh.observe(OpenAI()) # also AsyncOpenAI, Anthropic, AsyncAnthropic
That's it: every chat.completions.create, responses.create and messages.create /
messages.stream call (sync, async and streaming) becomes an llm.call with tokens, model,
latency, time to first token, stop reason and tool calls. The server prices it.
Without CODING_SH_URL the SDK does nothing. It never changes return values, errors or stream
semantics, and never raises into your app.
Traces, tools and conversations
with coding_sh.trace("support-reply", session_id=conversation_id, user_id=user.id, input=question) as t:
with coding_sh.span("tool", "lookup_order", input={"id": 42}) as s:
s.set_output(orders.get(42))
reply = client.chat.completions.create(model="gpt-4o-mini", messages=msgs)
t.set_output(reply.choices[0].message.content)
- Each top-level
trace()(or untraced call) is its own session. Passsession_id(or wrap code incoding_sh.with_session(id)) to make each trace a turn of one conversation. traceandspanare context managers (with/async with) and decorators. A decoratedspan("tool", ...)records the call's arguments as input and its return value as output.span("step", name)records a generic step.- Context follows
contextvars, so it crossesawaitand asyncio tasks. Work handed to a thread pool starts untraced unless you run it withcontextvars.copy_context().run.
LLM gateways
Point the client at a gateway and each call records which one it went through, and the request id
the gateway's lookup API takes, so coding.sh gateway enrich can add the billed cost and
coding.sh sessions --flag gateway:<name> finds the sessions. Detected: Vercel AI Gateway,
OpenRouter, LiteLLM, Cloudflare AI Gateway, Portkey, Helicone, Requesty, Ramp Router (router.com),
TrueFoundry, Kong, Bifrost and Martian, by base URL (a self-hosted gateway by its URL env var, e.g.
LITELLM_PROXY_API_BASE), by response header (cf-aig-log-id, helicone-id, x-litellm-call-id,
x-portkey-trace-id, …) or by response id (gen_…, gen-…, rqsty-…).
observe() adds a response hook to the client's HTTP client that reads headers only, and only for
calls it records. Details.
client = coding_sh.observe(OpenAI(base_url="https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/compat"))
Delivery
One daemon thread sends batches (up to 500 events or 4 MiB, gzip) to POST /v1/ingest. It retries
429, 5xx and network errors (honoring Retry-After, else 1, 2, 4 ... s, at most 5 attempts) with
stable event ids, so retries never duplicate. 400/401/403 drop the batch with one warning. A full
queue drops new events and counts them. An atexit flush is bounded to 2 s.
coding_sh.flush(timeout=5) # e.g. at the end of a script, a job or a serverless handler
coding_sh.shutdown() # flush, then stop
coding_sh.stats() # {"queued", "sent", "dropped", "failed"}
Config
coding_sh.init(url=, key=, service=, capture=, flush_interval_ms=1000, max_batch=500, max_queue=10000, disabled=, debug=)
is optional; arguments override env vars.
| Env | Meaning |
|---|---|
CODING_SH_URL |
Server base URL. Unset = SDK off. |
CODING_SH_INGEST_KEY |
csh_ik_... ingest key. It decides the project. |
CODING_SH_SERVICE (or OTEL_SERVICE_NAME) |
Service name; becomes the session's agent id. |
CODING_SH_CAPTURE_MODE |
full (default) or metadata (no message text or tool I/O). |
CODING_SH_DISABLED=1 |
Turn the SDK off. |
CODING_SH_DEBUG=1 |
Log transport details (never keys or bodies). |
OpenTelemetry
Already emitting gen_ai spans? Point OTel at the server instead (no SDK needed), or let the helper do the setup:
from coding_sh.otel import configure, otel_env
configure(service="checkout-api") # TracerProvider + OTLP/HTTP exporter + Bearer key
env = otel_env() # OTEL_EXPORTER_OTLP_* vars for a subprocess
pip install 'coding-sh[otel]'. configure() uses the standard OTEL_EXPORTER_OTLP_* env vars
when set, else CODING_SH_URL / CODING_SH_INGEST_KEY, and stamps gen_ai.conversation.id from
the current with_session / trace(session_id=...).
Tests
python3 -m unittest discover -s tests -t tests
The provider tests use fake clients and always run. Tests against the real openai, anthropic
and OpenTelemetry packages run when those are installed.
Metadata
Release files for coding-sh 0.1.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 | Provenance | Actions |
|---|---|---|---|---|
| coding_sh-0.1.3.tar.gz | 28.3 kB | With provenance | Details for coding_sh-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| coding_sh-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for coding_sh-0.1.3-py3-none-any.whl |
Total release size: 57.6 kB
Release files / coding_sh-0.1.3.tar.gz
| Download URL | coding_sh-0.1.3.tar.gz |
|---|---|
| Size | 28.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
24e915fc2510ee58ee3672e18bb110db5e8ba57d8a6bf81947cecc5d42583447
|
|
BLAKE2b-256 checksum How to use checksums |
6565be34227ada6d6343d2d6aa5065d260cc855b8e6199e98435dfeb8bbab1b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logRelease files / coding_sh-0.1.3-py3-none-any.whl
| Download URL | coding_sh-0.1.3-py3-none-any.whl |
|---|---|
| Size | 29.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d715241c2c5462585d59df5a09cde70313eb8dfd383b42b2599b108cab59f545
|
|
BLAKE2b-256 checksum How to use checksums |
40dd54a749fb7de94de109446916eea06b51fbb25a147a8dc3bad6952170f076
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logSecurity for coding-sh 0.1.3
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for coding_sh-0.1.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logA 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.