Skip to content

Commit 744df42

Browse files
peguesjclaude
andcommitted
feat: add Elixir community SDK (ag_ui_ex v0.1.0)
Adds the ag_ui_ex Elixir SDK as a community SDK for the AG-UI protocol. - Copy full ag_ui_ex v0.1.0 source into sdks/community/elixir/ - Add docs/sdk/elixir/overview.mdx with installation, quick start, and module reference - Add Elixir row (✅ Supported) to README.md SDKs table after Ruby - Add Ruby (missing) + Elixir rows to docs/introduction.mdx SDKs table ag_ui_ex provides: - 30+ typed event structs with validation - SSE transport (Plug-compatible) and Phoenix Channel transport - Composable middleware pipeline - GenServer state manager with RFC 6902 JSON Patch - HTTP client for consuming AG-UI event streams Hex.pm: https://hex.pm/packages/ag_ui_ex Source: https://github.com/peguesj/ag_ui_ex Fixes #1291 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f441477 commit 744df42

28 files changed

Lines changed: 3166 additions & 0 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ AG-UI was born from CopilotKit's initial **partnership** with LangGraph and Crew
145145
| [Java]() | ✅ Supported | ➡️ [Getting Started](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/java/overview.mdx) | Community |
146146
| [Rust]() | ✅ Supported | ➡️ [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/rust/crates/ag-ui-client) | Community |
147147
| [Ruby]() | ✅ Supported | ➡️ [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/ruby) | Community |
148+
| [Elixir](https://hex.pm/packages/ag_ui_ex) | ✅ Supported | ➡️ [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/elixir) | Community |
148149
| [.NET]() | 🛠️ In Progress | ➡️ [PR](https://github.com/ag-ui-protocol/ag-ui/pull/38) | Community |
149150
| [Nim]() | 🛠️ In Progress | ➡️ [PR](https://github.com/ag-ui-protocol/ag-ui/pull/29) | Community |
150151
| [Flowise]() | 🛠️ In Progress | ➡️ [GitHub Source](https://github.com/ag-ui-protocol/ag-ui/issues/367) | Community |

docs/introduction.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ AG-UI was born from CopilotKit's initial **partnership** with LangGraph and Crew
289289
| [Dart]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/dart) | Community |
290290
| [Java]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/java/overview.mdx) | Community |
291291
| [Rust]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/rust/crates/ag-ui-client) | Community |
292+
| [Ruby]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/ruby) | Community |
293+
| [Elixir](https://hex.pm/packages/ag_ui_ex) | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/elixir) | Community |
292294
| [.NET]() | In Progress | [PR](https://github.com/ag-ui-protocol/ag-ui/pull/38) | Community |
293295
| [Nim]() | In Progress | [PR](https://github.com/ag-ui-protocol/ag-ui/pull/29) | Community |
294296
| [Flowise]() | In Progress | [GitHub Source](https://github.com/ag-ui-protocol/ag-ui/issues/367) | Community |

docs/sdk/elixir/overview.mdx

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
title: "Elixir SDK Overview"
3+
description: "Connect to AG-UI agents using the community Elixir SDK (ag_ui_ex)"
4+
---
5+
6+
The AG-UI Elixir SDK (`ag_ui_ex`) provides a complete, idiomatic Elixir implementation of the AG-UI protocol. It includes typed event structs, SSE and Phoenix Channel transports, a composable middleware pipeline, GenServer-based state management with RFC 6902 JSON Patch, and an HTTP client for consuming AG-UI event streams.
7+
8+
## Installation
9+
10+
Add `ag_ui_ex` to your dependencies in `mix.exs`:
11+
12+
```elixir
13+
def deps do
14+
[
15+
{:ag_ui_ex, "~> 0.1.0"}
16+
]
17+
end
18+
```
19+
20+
Then fetch dependencies:
21+
22+
```bash
23+
mix deps.get
24+
```
25+
26+
## Quick Start
27+
28+
### Emit Events
29+
30+
```elixir
31+
alias AgUi.Core.Events
32+
33+
# Create a run started event
34+
event = %Events.RunStarted{
35+
run_id: "run-001",
36+
thread_id: "thread-001",
37+
timestamp: DateTime.utc_now() |> DateTime.to_unix(:millisecond)
38+
}
39+
40+
# Encode as SSE
41+
{:ok, sse_data} = AgUi.Encoder.EventEncoder.encode(event)
42+
```
43+
44+
### SSE Transport (Plug / Phoenix)
45+
46+
```elixir
47+
# In your Phoenix router
48+
forward "/ag-ui/events", AgUi.Transport.SSE
49+
50+
# Or use directly in a controller
51+
def stream(conn, _params) do
52+
AgUi.Transport.SSE.stream_events(conn, fn send_fn ->
53+
send_fn.(%Events.RunStarted{run_id: "run-001"})
54+
Process.sleep(100)
55+
send_fn.(%Events.RunFinished{run_id: "run-001"})
56+
end)
57+
end
58+
```
59+
60+
### Channel Transport (Phoenix WebSocket)
61+
62+
```elixir
63+
defmodule MyApp.AgUiChannel do
64+
use Phoenix.Channel
65+
use AgUi.Transport.Channel
66+
67+
def join("ag_ui:" <> _scope, _payload, socket) do
68+
{:ok, socket}
69+
end
70+
end
71+
```
72+
73+
### State Management
74+
75+
```elixir
76+
# Start the state manager
77+
{:ok, pid} = AgUi.State.Manager.start_link(name: :my_agent_state)
78+
79+
# Set state
80+
AgUi.State.Manager.set(pid, %{status: "running", progress: 0})
81+
82+
# Apply a JSON Patch delta (RFC 6902)
83+
AgUi.State.Manager.patch(pid, [
84+
%{"op" => "replace", "path" => "/progress", "value" => 75}
85+
])
86+
87+
# Read current state
88+
{:ok, state} = AgUi.State.Manager.get(pid)
89+
# => %{status: "running", progress: 75}
90+
```
91+
92+
### Middleware Pipeline
93+
94+
```elixir
95+
alias AgUi.Middleware.Pipeline
96+
97+
pipeline =
98+
Pipeline.new()
99+
|> Pipeline.add(&Pipeline.add_timestamp/2)
100+
|> Pipeline.add(&Pipeline.validate_type/2)
101+
102+
{:ok, processed_event} = Pipeline.run(pipeline, event)
103+
```
104+
105+
### HTTP Client
106+
107+
```elixir
108+
# Consume an AG-UI event stream over SSE
109+
{:ok, _client} = AgUi.Client.HttpAgent.start_link(
110+
url: "http://localhost:4000/ag-ui/events",
111+
handler: fn event -> IO.inspect(event, label: "AG-UI Event") end
112+
)
113+
```
114+
115+
## Package Structure
116+
117+
### Core (`AgUi.Core`)
118+
119+
Defines all 30+ typed event structs with validation. Events are organized by category:
120+
121+
| Category | Events |
122+
|:---------|:-------|
123+
| Lifecycle | `RunStarted`, `RunFinished`, `RunError` |
124+
| Progress | `StepStarted`, `StepFinished` |
125+
| Text | `TextMessageStart`, `TextMessageContent`, `TextMessageEnd` |
126+
| Thinking | `ThinkingTextMessageStart`, `ThinkingTextMessageContent`, `ThinkingTextMessageEnd`, `ThinkingStart`, `ThinkingEnd` |
127+
| Tools | `ToolCallStart`, `ToolCallArgs`, `ToolCallEnd` |
128+
| State | `StateSnapshot`, `StateDelta` |
129+
| Messages | `MessagesSnapshot` |
130+
| Activity | `ActivitySnapshot`, `ActivityDelta` |
131+
| Reasoning | `ReasoningStart`, `ReasoningMessageStart`, `ReasoningMessageContent`, `ReasoningMessageEnd`, `ReasoningMessageChunk`, `ReasoningEnd` |
132+
| Other | `EncryptedValue`, `Raw`, `Custom` |
133+
134+
### Encoder (`AgUi.Encoder`)
135+
136+
SSE and JSON encoding/decoding with automatic camelCase ↔ snake_case conversion.
137+
138+
```elixir
139+
{:ok, sse_string} = AgUi.Encoder.EventEncoder.encode(event)
140+
{:ok, event_struct} = AgUi.Encoder.EventEncoder.decode(sse_string)
141+
```
142+
143+
### Transports (`AgUi.Transport`)
144+
145+
- **`AgUi.Transport.SSE`** — Plug-compatible SSE handler for streaming events to browser clients
146+
- **`AgUi.Transport.Channel`** — Phoenix Channel macros for WebSocket-based event streaming
147+
148+
### State (`AgUi.State`)
149+
150+
GenServer-backed state manager using a snapshot/delta pattern with full RFC 6902 JSON Patch support via the `json_patch` library.
151+
152+
### Middleware (`AgUi.Middleware`)
153+
154+
Composable pipeline for event processing. Ships with built-in middleware for timestamp injection and type validation. Add custom middleware with any `(event, opts) -> {:ok, event} | {:error, reason}` function.
155+
156+
### Client (`AgUi.Client`)
157+
158+
`AgUi.Client.HttpAgent` — A GenServer that connects to a remote AG-UI event stream over HTTP SSE and dispatches events to a handler function.
159+
160+
## Requirements
161+
162+
- Elixir ~> 1.15
163+
- Erlang/OTP ~> 26
164+
165+
## Source & Package
166+
167+
- **Hex.pm**: [hex.pm/packages/ag_ui_ex](https://hex.pm/packages/ag_ui_ex)
168+
- **GitHub**: [github.com/peguesj/ag_ui_ex](https://github.com/peguesj/ag_ui_ex)
169+
- **License**: MIT
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## Summary
2+
3+
<!-- Brief description of the changes -->
4+
5+
## Type of Change
6+
7+
- [ ] Bug fix (non-breaking change that fixes an issue)
8+
- [ ] New feature (non-breaking change that adds functionality)
9+
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
10+
- [ ] Documentation update
11+
12+
## Checklist
13+
14+
- [ ] `mix format --check-formatted` passes
15+
- [ ] `mix compile --warnings-as-errors` passes
16+
- [ ] `mix test` passes
17+
- [ ] Added/updated tests for new functionality
18+
- [ ] Updated documentation if public APIs changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: Test (Elixir ${{ matrix.elixir }} / OTP ${{ matrix.otp }})
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
include:
19+
- elixir: "1.15"
20+
otp: "25"
21+
- elixir: "1.16"
22+
otp: "26"
23+
- elixir: "1.17"
24+
otp: "27"
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: erlef/setup-beam@v1
29+
with:
30+
elixir-version: ${{ matrix.elixir }}
31+
otp-version: ${{ matrix.otp }}
32+
33+
- name: Cache deps
34+
uses: actions/cache@v4
35+
with:
36+
path: |
37+
deps
38+
_build
39+
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }}
40+
restore-keys: |
41+
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
42+
43+
- name: Install dependencies
44+
run: mix deps.get
45+
46+
- name: Check formatting
47+
run: mix format --check-formatted
48+
49+
- name: Compile (warnings as errors)
50+
run: mix compile --warnings-as-errors
51+
52+
- name: Run tests
53+
run: mix test
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Deploy Docs to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
permissions:
8+
contents: read
9+
pages: write
10+
id-token: write
11+
12+
concurrency:
13+
group: "pages"
14+
cancel-in-progress: true
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: erlef/setup-beam@v1
23+
with:
24+
elixir-version: "1.17"
25+
otp-version: "27"
26+
27+
- name: Install dependencies
28+
run: mix deps.get
29+
30+
- name: Generate docs
31+
run: mix docs
32+
33+
- name: Upload artifact
34+
uses: actions/upload-pages-artifact@v3
35+
with:
36+
path: doc/
37+
38+
deploy:
39+
environment:
40+
name: github-pages
41+
url: ${{ steps.deployment.outputs.page_url }}
42+
runs-on: ubuntu-latest
43+
needs: build
44+
steps:
45+
- name: Deploy to GitHub Pages
46+
id: deployment
47+
uses: actions/deploy-pages@v4

sdks/community/elixir/.gitignore

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Elixir/Mix build artifacts
2+
/_build/
3+
/deps/
4+
/doc/
5+
*.ez
6+
*.beam
7+
8+
# Hex package tarball
9+
ag_ui-*.tar
10+
11+
# Generated on crash dump
12+
erl_crash.dump
13+
14+
# Test coverage
15+
/cover/
16+
17+
# Temporary files
18+
/tmp/
19+
20+
# OS files
21+
.DS_Store
22+
Thumbs.db
23+
24+
# Editor files
25+
*.swp
26+
*.swo
27+
*~
28+
.idea/
29+
.vscode/
30+
31+
# Claude Code artifacts
32+
.claude/
33+
.claude-*
34+
CLAUDE.md
35+
*.claude.md
36+
.force/
37+
lessons/
38+
.hook_state/
39+
40+
# Environment
41+
.env
42+
.env.*

0 commit comments

Comments
 (0)