Repository navigation
Release v9.3.0
AxonFlow Python SDK v9.3.0
Installation
pip install axonflow==9.3.0Added
-
Per-call
extra_headerson the governed methods (mcp_check_input,mcp_check_output,check_tool_input,check_tool_output). Headers passed this way are merged into that one request and are never added to the client's default header set, so a header supplied for one call cannot leak into the next one or into any other method. -
This is the ADR-065 PEP capability handshake's attach point for this SDK (getaxonflow/axonflow-enterprise#3763). What makes a per-call parameter necessary rather than a convenience over the client's defaults is that one process can be two enforcement points: a client whose request path and response path discharge different obligations must present a different declaration on each, and a process-wide default header can only carry one document. When this SDK presents its own declaration it will build on this parameter rather than adding a second mechanism.
-
Strictly additive. Omitting the parameter sends exactly the bytes the previous release sent, which is asserted rather than argued: a test drives a governed call without it and checks the handshake header is absent while the credential headers are untouched.
-
register_adapter(name)declares a framework adapter on the existing
heartbeat (axonflow-enterprise#3682). A LangChain / LangGraph / LiteLLM
wrapper — or your own in-house adapter — was previously indistinguishable
from bare SDK use on every telemetry dimension: samesdk, same
sdk_version, same endpoint.register_adapter("langchain")adds
adapter:langchainto thefeaturesarray of the ping that already fires.
No new network request, no new configuration surface, no second endpoint.
Idempotent and thread-safe. The name is lowercased and trimmed and otherwise
sent as given — deliberately NOT checked against a list of known frameworks,
because the canonical vocabulary lives on the receiver, which preserves an
unrecognised name on the row while bucketing it for reporting. Only adoption
signal is collected: nothing about what the adapter does.The SDK's own adapters now declare themselves, so using them is enough:
AxonFlowLangGraphAdapter,wrap_langgraph/GovernedGraph,
AxonFlowChatModelandAxonFlowRunnableBinding. This is the FIRST producer
offeaturesin this SDK — the array was a hardcoded[]. -
The heartbeat now relays
editionandplatform_deployment_modefrom the
same/healthresponse it already fetches for the platform version and
licence tier — no new request. Both are omitted entirely when not learned,
never sent as""ornull.platform_deployment_modeis the PLATFORM's own
deployment mode and travels under its own key; it never overwrites the
SDK-deriveddeployment_modetopology field. Adoption analytics only: the
endpoint's operator controls both values and the SDK verifies neither, so they
must never gate entitlement or any authorization decision.
Changed
-
AUTHZEN_PATHandAUTHZEN_PROFILE_HEADERare generated from the surface
artifact, not written here (axonflow-enterprise#3603 follow-up). The route
(/api/v1/access/evaluation) and the negotiation header
(X-Axonflow-AuthZEN-Profile) were literals in this SDK, in the other four
SDKs and in the platform's handler, with nothing comparing the copies; a
rename on the platform would have surfaced as a 404 in whichever SDK next had
a live run. The platform now publishes both inauthzen-surface.json
(route,profile_header); the generator requires them and emits the two
constants;axonflow.authzenre-exports them under the names it always had.
The vendored artifact is refreshed to the publishing platform commit. No wire
change. -
The telemetry heartbeat now fires on the client's first outbound request
rather than at client construction. A process that constructs a client and
never sends a request no longer pings at all — a heartbeat is a claim about
usage.The reason is
register_adapter. Every framework adapter takes a client, so
an adapter cannot exist until the constructor has returned; pinging there
meant an adapter registering from its own constructor could never reach the
first ping, and the 7-day stamp then suppressed the next one for a week. For a
short-lived process — a CLI, a serverless handler, a CI job — the adapter was
never reported at all.Ten request paths that issued HTTP without consulting the gate now do:
stream_execution_status,mcp_query,mcp_check_input,mcp_check_output,
login_to_portal,logout_from_portal, the two customer-portal request
helpers,_step_request_with_idempotency_checkand_do_request. Before the
move this cost nothing because the constructor pinged; after it, a process
whose only outbound call was one of those would never have pinged at all.Short-lived delivery (issue #1692) is unaffected: the gate spawns a daemon
thread tracked by an atexit flush handler, so a process that makes one call
and exits still delivers the POST. -
Every value the SDK relays but did not author is now bounded at 64 bytes and
DROPPED WHOLE when it exceeds that — the values promoted out of/health
and adapter names alike, through one helper so the two paths cannot drift.
Dropping rather than truncating is the point: a truncated version string is a
version nobody is running, and the receiver would record it as a real value.
The bound is measured in bytes (len(s.encode("utf-8"))), not characters:
33 accented characters are 33 code points and 66 bytes. Thefeaturesarray
is bounded at 32 entries of 128 bytes. -
A checkpoint response of any 2xx now counts as delivery, not
200exactly.
Every sibling SDK already accepted any 2xx; Python alone compared against 200,
so a checkpoint answering202read as a failure and the same ping was
retried at every gate run forever with the stamp never advancing. -
Redirects are refused explicitly on both telemetry legs, and a refused
redirect is now logged.follow_redirects=Falsewas already httpx's default
so behaviour is unchanged, but it is now a stated decision rather than an
inherited default — a library default change, or this call being copied to a
client configured differently, would otherwise silently start relaying
whatever answered at a redirect target, and on the POST leg would report
delivery for a ping converted to a bodyless GET.
Fixed
-
A delivered heartbeat could recur hourly, forever, where the stamp file
cannot be written. The 7-day cadence was enforced only by a stamp file, so in
a runtime with no usable cache directory — distroless and scratch containers,
Lambda custom runtimes — or on a read-only root filesystem (ordinary
Kubernetes hardening), a successful ping left no record and the gate
re-opened an hour later, indefinitely: 168 pings a week against a contract
that discloses one. The cadence is now also enforced in memory. Note what this
means in those runtimes: with no writable stamp the cadence is enforced PER
PROCESS, not per machine. -
A deployment that cannot reach the checkpoint service probed the customer's
own platform every hour, indefinitely. There was no failure backoff: the
7-day stamp advances only on delivery and the gate is consulted on every
request, so with egress blocked — the normal state of air-gapped and in-VPC
self-hosted topologies — every process issued a/healthGET against the
customer's own platform hourly, with a failed POST beside it. The re-check
interval now doubles per consecutive undelivered attempt, capped at the 7-day
interval — in numbers, from 1 hour through 2, 4, 8 … up to a ceiling of
7 days (604,800 seconds), reached after 8 consecutive failures. No ping is
lost: the stamp is untouched, so the first attempt after the widened interval
sends normally, and a single delivery resets the interval to 1 hour.