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 the mix.exs declaration).
  • mix quality: a single gate that chains format --check-formatted, compile --warnings-as-errors, deps.unlock --check-unused, credo --strict, unit tests, and dialyzer. Run it before committing/pushing.
  • mix quality.audit: mix hex.audit as a separate, non-gating alias — it can fail for reasons outside this repo's control (an unpatched upstream CVE), so it isn't part of mix quality.
  • .formatter.exs, .credo.exs (both exclude the generated, gitignored lib/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.md for AI coding agents.

Fixed

  • Xai.Chat.stream/2 no longer crashes: it was calling GRPC.Stub.recv/2 in a polling loop against a value that gRPC's server-streaming API already resolves to {:ok, Enumerable.t()} before it's returned, so recv/2 had no matching clause and would raise.
  • Xai.Video.poll_for_video/4 was checking if attempts <= 0, do: {:error, :timeout} without an else; the result was discarded and the retry budget was never actually enforced. Rewritten as pattern-matched function heads.
  • Xai.Client.new/1 silently swallowed gRPC connection failures via elem(connect(...), 1); a failed connect now raises with the real error instead of storing it as a bogus channel.
  • Xai.Chat.extract_delta/1 matched on %{message: ...}, but streamed chunks carry a delta key; it always returned "".
  • Xai.Client connected with ssl: [verify: :verify_none] for the default TLS endpoint, disabling certificate verification. Now verifies peer certs against the system CA store.
  • Xai.Realtime.connect_tts/1 registered 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.generate never actually worked: .protobuf.exs was never read by anything (the installed protobuf_generate version takes CLI flags, not a config file). Replaced with a working alias in mix.exs (@proto_files + explicit flags) and regenerated real bindings from the priv/xai_proto submodule.
  • README's Quick Start examples referenced response.content and an undefined extract_text/1 that don't match the real generated response shape; fixed to use response.outputs |> hd() |> ... and Xai.Chat.extract_delta/1.
  • mix hex.build/hex.publish would have failed outright (Can't build package with overridden dependency gun); removed the unnecessary override: true on the gun dep — it wasn't resolving a real conflict (~> 2.0 already satisfies grpc's own ~> 2.4.0).
  • package[:files] wasn't set, so Hex's default (lib, priv, ...) would have swept the ~160KB vendored priv/xai_proto git submodule (raw .proto sources, buf tooling, CI configs) into the published package. Now an explicit allowlist.

Changed

  • Dropped the unused grpc_server dependency (and its transitive flow/gen_stage/cowboy tree), added for a server-side feature that was never implemented.
  • Added :inets and :ssl to extra_applications (needed by the integration test's :httpc call and by TLS gRPC connections).
  • Bumped minimum Elixir requirement from ~> 1.16 to ~> 1.18.
  • Replaced direct Jason usage with the built-in JSON module (Elixir 1.18+) in Xai.Realtime and the integration test; dropped the direct jason dependency. jason still appears in mix.lock as a transitive dependency (grpc_core, protobuf, and credo each require it directly), but our own code no longer calls it.
  • Aliased XaiApi.Chat.Stub/XaiApi.Video.Stub in Xai.Chat/Xai.Video instead of reaching through the nested Proto.Chat.Stub/Proto.Video.Stub path, clearing Credo's strict-mode design suggestions.

Security

  • API keys are never committed. Integration tests require the XAI_API_KEY environment variable.