Syncs the Elixir port to Python guava-sdk 0.36.0.
Most of this release is reliability work found by repeatedly auditing the port against
the Python SDK with no version baseline. Every fix carries a regression test confirmed
to fail when that fix alone is reverted.
Breaking
- Campaigns are identified by code, not id.
%Guava.Campaign{}carries:codeand:name;:idremains only for the endpoints still keyed on it and is documented as internal. NoGuava.Campaignsfunction takes an id — pass a code or a%Guava.Campaign{}. The campaignupdatefunction is gone, having been deleted upstream. Single-field results are unwrapped:upload_contactsreturns a count andstatusa counts map. Guava.Usageis removed andconfig :guava, usage_telemetry:is now an ignored key. The uploader was a faithful port but nothing ever fed it, so enabling it uploaded nothing. The SDK's:telemetryspans are unaffected.
Changed — may affect a working deployment
- HTTP redirects are no longer followed. A 3xx now raises
%Guava.Error{type: :http}carrying the status, matchinghttpx, which defaults tofollow_redirects=False. Req had been following up to 10 redirects and rewriting POST to GET for 301–303, dropping the JSON body — so a redirectedsend_sms!/4could return:okfor a message that was never sent. If your base URL is fronted by something that redirects API paths, point it at the final URL. - The per-attempt HTTP timeout is 5s, connect and receive alike, matching
httpx'sTimeout(5.0). It was Req's 15s receive default with connect left on Mint's 30s. Retries are unchanged, so a hung host now costs ~27s across four attempts rather than ~127s. RAG and LLM calls keep their own longer budgets.
New public API
- Graceful shutdown.
Guava.drain/1closes listener sockets and waits out in-flight calls, wired to the application'sprep_stopcallback so aSIGTERMduring a rolling deploy no longer drops live calls. Guava.ready?/0for a readiness probe you mount yourself; the SDK does not serve HTTP. It reflects intake channels only, so placing an outbound call cannot make a node report unready.
Fixed
Highlights — see CHANGELOG.md for the full list.
- Multiple
Guava.Channelchildren in one supervisor collided on child id, so the documented multi-channel example could not boot. - Completed calls were restarted, and a supervised outbound channel re-dialled the callee indefinitely.
- Sockets: the reconnect budget never escalated or reported failure; a silently half-open connection went unnoticed for ~15 minutes; the opening handshake had no overall deadline, so a stalling peer could wedge a socket and with it
Guava.drain/1; a socket could outlive its owner, leaking a server-side connection per call. - Handlers that exited, threw, returned an unexpected shape, or returned a value the wire cannot carry all killed the call; each now logs and answers with the fallback. Expert-error wording matches upstream exactly, since the model reads it.
- A failed CLI OAuth refresh produced an empty bearer token and re-issued the failing request on every subsequent call.
Wire protocol
- No changes. All 87 wire fixtures regenerate byte-identically from upstream 0.36.0's own pydantic models, and the port emits and matches all 38
command_type/event_typediscriminators with none extra.
Housekeeping
- CI added (GitHub Actions): a test matrix over Elixir 1.15/1.17/1.18 — which validates the
~> 1.15floormix.exsclaims — plus format, credo and a docs build that fails on warnings. CHANGELOG.mdadded.PARITY.mdrecords the deliberate deviations, including several where upstream's behaviour was rejected on the merits rather than copied. See PARITY.md.
Install:
{:guava, "~> 0.36"}