Observed since 20 Oct, 17:45 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ABTO server-side SDK for Python — gateway baseURL + x-abto-* header injection via contextvars and httpx event hooks.
The ABTO Python Server SDK is a thin Gateway helper. It carries request-scoped x-abto-* identifiers with contextvars and injects them into outbound provider requests through httpx. The Gateway owns tokens, cost, latency, request_id, and variant assignment.
This package is the Python counterpart of the Node.js @abto-app/calling package. npm and PyPI releases remain separate for each language.
pip install abto # Core context and header helpers
pip install "abto[openai]" # OpenAI and httpx integration
import os
from abto import init_abto
abto = init_abto(
api_key=os.environ["ABTO_API_KEY"],
gateway_base_url="https://gateway.abto.app/v1",
provider_keys={
"openai": os.environ["OPENAI_API_KEY"],
# Include candidate keys for every provider the project may route to.
# "anthropic": os.environ["ANTHROPIC_API_KEY"],
# "gemini": os.environ["GEMINI_API_KEY"],
# "deepseek": os.environ["DEEPSEEK_API_KEY"],
# "kimi": os.environ["KIMI_API_KEY"],
},
)
openai = abto.openai()
def generate(device_id: str):
with abto.with_context(
device_id=device_id,
feature_id="resume.make",
):
return openai.chat.completions.create(
model="gpt-4.1",
messages=[{"role": "user", "content": "Create a resume draft"}],
)
Pass ABTO's connection options to your existing ChatOpenAI model.
Keep your chains, retrievers, prompts, callbacks, and retry settings.
pip install "abto[openai]" langchain-openai
This example connects both sync and async calls. Set your Calling Key and OpenAI API key in server environment variables.
import asyncio
import os
from abto import init_abto
from langchain_openai import ChatOpenAI
abto = init_abto(
api_key=os.environ["ABTO_CALLING_KEY"],
gateway_base_url="https://gateway.abto.app/v1",
provider_keys={"openai": os.environ["OPENAI_API_KEY"]},
)
sync_options = abto.openai_options()
async_options = abto.async_openai_options()
model = ChatOpenAI(
model="gpt-4.1-mini",
use_responses_api=False,
**sync_options,
http_async_client=async_options["http_client"],
)
async def main():
try:
with abto.with_context(feature_id="support.reply"):
reply = await model.ainvoke("How can I check my delivery status?")
print(reply.content)
finally:
sync_options["http_client"].close()
await async_options["http_client"].aclose()
asyncio.run(main())
In synchronous code, call model.invoke(...) on the same model.
In a server, create and reuse the model and connection options, then close the HTTP clients at shutdown.
After a call, find support.reply in the Dashboard request list to check the response, cost, and latency.
Wrap the transport where you create your existing client, then pass the client to ABTO.
Your custom send() method, request/response hooks, connection pools, and cookies stay in use.
import httpx
from abto import wrap_httpx_transport
# If you already have a transport, wrap that object instead of HTTPTransport().
existing_sync_client = httpx.Client(
transport=wrap_httpx_transport(httpx.HTTPTransport()),
trust_env=False,
)
sync_options = abto.openai_options(http_client=existing_sync_client)
existing_async_client = httpx.AsyncClient(
transport=wrap_httpx_transport(httpx.AsyncHTTPTransport()),
trust_env=False,
)
async_options = abto.async_openai_options(http_client=existing_async_client)
Keep your existing timeout, TLS, and proxy settings.
This example disables automatic proxy selection from environment variables. If you use a proxy, configure it explicitly on the transport.
Wrap each transport in mounts as well.
ABTO does not replace the internals of an already-created client; make this change where you construct the client.
An unwrapped route receives no ABTO credentials, and ABTO reports a configuration error after receiving its response.
Closing the returned ABTO clients does not close the clients you supplied. Keep their existing shutdown handling.
ABTO owns Gateway and direct-fallback authentication and disables automatic redirects to keep keys on the configured destinations.
After customer hooks run, ABTO checks the destination and Host header, then adds keys only to a separate wire request.
Your request/response hooks and send() do not receive real ABTO credentials added by the SDK.
Errors from your hooks or observation code in send() propagate without triggering ABTO direct fallback.
Your existing OpenAI and LangChain retry settings still apply.
Place manually configured logging transports outside the ABTO wrapper. The inner transport that performs network I/O necessarily receives authentication keys.
You can also pass openai_options() to the official OpenAI client and async_openai_options() to AsyncOpenAI.
The returned api_key is a placeholder that keeps the real key out of framework trace serialization. Credentials are injected when the request is sent.
Invoke each user's chain inside with abto.with_context(feature_id=..., device_id=...).
Python contextvars keeps concurrent async requests separate.
For background jobs, also store and forward the device ID received from the browser with the job data.
Keep Langfuse or LangSmith callbacks, tags, and metadata in your existing invocation configuration. Tracing tools record the input your app sends. If you change the model, prompt, or parameters in the Dashboard, check the ABTO request record for what actually ran. The Gateway may remove tracing headers before forwarding to the provider, so a single trace extending through the provider is not guaranteed.
This integration is for OpenAI Chat Completions. Do not use this example to convert calls that use streaming or the Responses API. Keep TXT loaders and vector retrievers in your existing chain, and keep embeddings on their existing provider connection. To continue calls directly during Gateway outages, also configure direct fallback below.
import httpx
from abto import with_context
client = httpx.Client(event_hooks=abto.httpx_event_hooks())
with with_context(device_id="d1", feature_id="resume.make"):
client.post("https://gateway.abto.app/v1/...") # Adds x-abto-* headers
x-abto-device-id optional; without it, user analytics and sticky assignment are unavailable
x-abto-feature-id required; feature ID, for example resume.make
x-abto-key-openai candidate provider key; add only the providers the project may route to
Authorization required; Bearer ABTO Calling Key
The Gateway maps the Calling Key to tenant_id, creates request_id in the x-abto-request-id response header, assigns variant_id, and removes x-abto-* headers before provider egress.
When an OpenAI provider key and an explicit fallback base URL are configured, direct fallback handles Gateway failures that can be classified safely. It sends the original Chat Completions body and model to OpenAI and does not reproduce the Gateway's provider or model policy.
from abto import OpenAIDirectFallbackOptions, init_abto
abto = init_abto(
api_key=os.environ["ABTO_API_KEY"],
gateway_base_url="https://gateway.abto.app/v1",
provider_keys={
"openai": os.environ["OPENAI_API_KEY"],
"anthropic": os.getenv("ANTHROPIC_API_KEY"),
"gemini": os.getenv("GEMINI_API_KEY"),
"deepseek": os.getenv("DEEPSEEK_API_KEY"),
"kimi": os.getenv("KIMI_API_KEY"),
},
fallback=OpenAIDirectFallbackOptions(
base_url="https://api.openai.com/v1",
timeout_seconds=30,
on_timeout=False,
),
)
Default behavior matches the JavaScript Calling SDK:
502, 503, or 504; or pre-provider admission 503: send the current request directly to OpenAI.on_timeout=True: replay the timed-out request directly, explicitly accepting duplicate execution and billing risk.x-abto-error-source: provider|transport, deterministic 4xx or 429: do not fall back for the current request.The direct path sends only the OpenAI key and OpenAI-safe headers such as accept, content-type, idempotency-key, traceparent, tracestate, openai-*, and x-stainless-*. It removes ABTO headers, the Calling Key, cookies, proxy credentials, and custom Gateway headers. Direct calls bypass Gateway policy, ABTO telemetry, and request_id.
The ABTO transport performs at most one Gateway decision and one direct send per official OpenAI SDK attempt. It returns direct responses and errors to the official SDK, which remains the only retry authority.
fallback.base_url is required to enable direct fallback and has no default: it is the endpoint this application used before ABTO. Leave it unset and there is no fallback — a Gateway outage makes the request fail outright. Set it if the existing path should keep serving traffic after adoption. ABTO does not guess the destination, because the provider key leaves on that path; enabling fallback without it raises ValueError.
gateway_base_url is required as well — pass it or set ABTO_GATEWAY_BASE_URL.
Use init_abto(..., fallback=False) to disable direct fallback.
abto.openai(**kwargs) forwards official OpenAI options such as max_retries, timeout, organization, project, default_headers, and default_query unchanged, except for the three values ABTO must own:
api_key is a placeholder; the real Calling Key is injected at dispatch.base_url is the configured ABTO Gateway URL.http_client is the ABTO routing client that enforces Gateway/direct-fallback boundaries.Passing any of those three reserved arguments raises ValueError instead of silently discarding it.
timeout_seconds is an inactivity ceiling for each Gateway connection-pool wait, connect, write, and response-header-read stage. The body after the headers and direct requests retain abto.openai(timeout=...).
There is no fallback retry count. abto.openai(max_retries=...) alone controls official OpenAI SDK retries and remains at the official default when omitted.
openai = abto.openai(max_retries=2)
max_retries keeps the official OpenAI meaning: retries after the initial request. 0 means one Gateway round trip and 1 means two. OpenAI and model-provider error policy belongs to the customer application and the official OpenAI SDK.
Note that max_retries counts round trips, not provider invocations. Inside a single round trip the Gateway may retry along the same path — always for pre-send network failures (up to 2), and for transient provider failures (429, 500, 502, 503, 504, 529) when the node retry policy opts in (up to 2). The x-abto-attempt response header reports which attempt produced the response. See Retries happen at two layers.
Anthropic and Gemini keys remain Gateway routing candidates. This SDK does not provide native direct fallback for those providers.
See the full Python guide.
ABTO keeps the traceparent and tracestate headers supplied by your tracing tool, including during direct fallback.
It adds feature and device information separately without replacing your trace ID.
trace_id is sent only as x-abto-trace-id, never turned into a traceparent.
Keep your tracing tool's request header setup. ABTO does not configure tracing for you or connect traces through to the provider.
init_abto(api_key=None, gateway_base_url=None, provider_keys=None, fallback=None) -> Abtoabto.openai(**kwargs)abto.openai_options(**kwargs) / abto.async_openai_options(**kwargs)OpenAIDirectFallbackOptionsabto.with_context(device_id=?, feature_id=?, trace_id=?, node_id=?): node_id names the call site inside the feature and is sent as x-abto-node-id. An inner context that switches to another feature_id without a node_id does not inherit the outer node_id. trace_id groups the calls one agent or workflow run makes and is sent as x-abto-trace-id. The outermost with_context creates one when it is absent and inner blocks inherit it, so wrap one whole run in one with_context. A call outside any with_context gets its own run ID, and a context passed directly without trace_id uses the enclosing scope's; set it yourself only to continue a run another service passed to you.abto.get_headers(ctx=None) / abto.create_trace_id() / abto.httpx_event_hooks()with_context, get_context, get_headers, create_trace_id, create_traceparent (deprecated), abto_request_hook, ABTO_HEADER, and AbtoContextpip install pytest
pytest
pypistats · 81 observations shown. Missing days are unknown. Source category: overall: without mirrors.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-20 – 2026-10-20 | 85 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-19 – 2026-10-19 | 101 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-18 – 2026-10-18 | 15 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-17 – 2026-10-17 | 18 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-16 – 2026-10-16 | 28 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-15 – 2026-10-15 | 14 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-14 – 2026-10-14 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-13 – 2026-10-13 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-12 – 2026-10-12 | 6 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-11 – 2026-10-11 | 3 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-10 – 2026-10-10 | 26 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-09 – 2026-10-09 | 30 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-08 – 2026-10-08 | 14 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-07 – 2026-10-07 | 11 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-06 – 2026-10-06 | 2 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-05 – 2026-10-05 | 26 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-04 – 2026-10-04 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-10-01 – 2026-10-01 | 2 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-30 – 2026-09-30 | 17 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-29 – 2026-09-29 | 33 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-28 – 2026-09-28 | 34 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-27 – 2026-09-27 | 16 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-26 – 2026-09-26 | 14 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-25 – 2026-09-25 | 131 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-24 – 2026-09-24 | 118 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-23 – 2026-09-23 | 3 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-22 – 2026-09-22 | 3 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-21 – 2026-09-21 | 114 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-20 – 2026-09-20 | 15 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-19 – 2026-09-19 | 3 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-18 – 2026-09-18 | 67 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-17 – 2026-09-17 | 6 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-16 – 2026-09-16 | 127 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-15 – 2026-09-15 | 27 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-14 – 2026-09-14 | 16 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-13 – 2026-09-13 | 6 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-12 – 2026-09-12 | 43 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-11 – 2026-09-11 | 19 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-10 – 2026-09-10 | 172 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-09 – 2026-09-09 | 28 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-07 – 2026-09-07 | 369 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-06 – 2026-09-06 | 14 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-05 – 2026-09-05 | 15 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-04 – 2026-09-04 | 105 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-03 – 2026-09-03 | 31 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-02 – 2026-09-02 | 10 | 2026-10-21T09:14:18.7434241Z |
| 2026-09-01 – 2026-09-01 | 169 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-31 – 2026-08-31 | 106 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-30 – 2026-08-30 | 5 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-29 – 2026-08-29 | 95 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-28 – 2026-08-28 | 17 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-27 – 2026-08-27 | 73 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-24 – 2026-08-24 | 13 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-23 – 2026-08-23 | 14 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-21 – 2026-08-21 | 1 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-20 – 2026-08-20 | 1 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-18 – 2026-08-18 | 61 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-17 – 2026-08-17 | 8 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-15 – 2026-08-15 | 2 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-14 – 2026-08-14 | 4 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-13 – 2026-08-13 | 4 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-12 – 2026-08-12 | 10 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-11 – 2026-08-11 | 12 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-10 – 2026-08-10 | 15 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-09 – 2026-08-09 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-08 – 2026-08-08 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-07 – 2026-08-07 | 5 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-06 – 2026-08-06 | 21 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-05 – 2026-08-05 | 83 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-04 – 2026-08-04 | 13 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-03 – 2026-08-03 | 183 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-02 – 2026-08-02 | 11 | 2026-10-21T09:14:18.7434241Z |
| 2026-08-01 – 2026-08-01 | 4 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-31 – 2026-07-31 | 19 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-30 – 2026-07-30 | 1 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-29 – 2026-07-29 | 5 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-28 – 2026-07-28 | 113 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-27 – 2026-07-27 | 94 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-26 – 2026-07-26 | 1 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-25 – 2026-07-25 | 7 | 2026-10-21T09:14:18.7434241Z |
| 2026-07-24 – 2026-07-24 | 164 | 2026-10-21T09:14:18.7434241Z |
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.2.1 | 20 Oct 2026 | 2 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 17:45 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:2728f2cfee9a8ef5aae3af305eb7342d6e860c5704cf8b16a1e9e8e7f8a25e7d