All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.0] - 2026-07-14
First published release — nothing shipped to Hex before this version, so everything below (including the initial implementation) is folded into it.
Added
- Initial implementation of the xAI Elixir SDK:
Xai.Client– core client for gRPC connections.Xai.Chat– chat completions with support for messages, streaming, and reasoning.Xai.Video– image and video generation with automatic polling for async operations.- WebSocket support for realtime features via
websockex(streaming TTS, voice agent API).
- Real generated protobuf bindings (via
mix proto.generate, see below) for chat, video, image, sample, usage, deferred, and documents — replacing the hand-written placeholder structs the implementation started from. LICENSE(Apache-2.0, matching themix.exsdeclaration).mix quality: a single gate that chainsformat --check-formatted,compile --warnings-as-errors,deps.unlock --check-unused,credo --strict, unit tests, anddialyzer. Run it before committing/pushing.mix quality.audit:mix hex.auditas a separate, non-gating alias — it can fail for reasons outside this repo's control (an unpatched upstream CVE), so it isn't part ofmix quality..formatter.exs,.credo.exs(both exclude the generated, gitignoredlib/xai/proto/), and.dialyzer_ignore.exs(documents each currently-accepted dialyzer finding and why).- Test suite: unit tests (no network required), integration tests (gated behind
XAI_API_KEY), Mox-based mocking. AGENTS.mdfor AI coding agents.
Fixed
Xai.Chat.stream/2no longer crashes: it was callingGRPC.Stub.recv/2in a polling loop against a value that gRPC's server-streaming API already resolves to{:ok, Enumerable.t()}before it's returned, sorecv/2had no matching clause and would raise.Xai.Video.poll_for_video/4was checkingif attempts <= 0, do: {:error, :timeout}without anelse; the result was discarded and the retry budget was never actually enforced. Rewritten as pattern-matched function heads.Xai.Client.new/1silently swallowed gRPC connection failures viaelem(connect(...), 1); a failed connect now raises with the real error instead of storing it as a bogus channel.Xai.Chat.extract_delta/1matched on%{message: ...}, but streamed chunks carry adeltakey; it always returned"".Xai.Clientconnected withssl: [verify: :verify_none]for the default TLS endpoint, disabling certificate verification. Now verifies peer certs against the system CA store.Xai.Realtime.connect_tts/1registered under a hardcoded{:via, Registry, {Xai.RealtimeRegistry, :default}}name, but that registry is never started — the first call would crash, and only one TTS connection could ever be alive at a time. Removed the vestigial naming.mix proto.generatenever actually worked:.protobuf.exswas never read by anything (the installedprotobuf_generateversion takes CLI flags, not a config file). Replaced with a working alias inmix.exs(@proto_files+ explicit flags) and regenerated real bindings from thepriv/xai_protosubmodule.- README's Quick Start examples referenced
response.contentand an undefinedextract_text/1that don't match the real generated response shape; fixed to useresponse.outputs |> hd() |> ...andXai.Chat.extract_delta/1. mix hex.build/hex.publishwould have failed outright (Can't build package with overridden dependency gun); removed the unnecessaryoverride: trueon thegundep — it wasn't resolving a real conflict (~> 2.0already satisfiesgrpc's own~> 2.4.0).package[:files]wasn't set, so Hex's default (lib,priv, ...) would have swept the ~160KB vendoredpriv/xai_protogit submodule (raw.protosources, buf tooling, CI configs) into the published package. Now an explicit allowlist.
Changed
- Dropped the unused
grpc_serverdependency (and its transitiveflow/gen_stage/cowboytree), added for a server-side feature that was never implemented. - Added
:inetsand:ssltoextra_applications(needed by the integration test's:httpccall and by TLS gRPC connections). - Bumped minimum Elixir requirement from
~> 1.16to~> 1.18. - Replaced direct
Jasonusage with the built-inJSONmodule (Elixir 1.18+) inXai.Realtimeand the integration test; dropped the directjasondependency.jasonstill appears inmix.lockas a transitive dependency (grpc_core,protobuf, andcredoeach require it directly), but our own code no longer calls it. - Aliased
XaiApi.Chat.Stub/XaiApi.Video.StubinXai.Chat/Xai.Videoinstead of reaching through the nestedProto.Chat.Stub/Proto.Video.Stubpath, clearing Credo's strict-mode design suggestions.
Security
- API keys are never committed. Integration tests require the
XAI_API_KEYenvironment variable.