Skip to content

Commit ae38e18

Browse files
committed
exmaple ag-ui command line client
1 parent 6788465 commit ae38e18

File tree

11 files changed

+989
-0
lines changed

11 files changed

+989
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
.vscode/
22
.idea/
3+
4+
.DS_Store
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
7+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client/internal/agent"
8+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client/internal/message"
9+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client/internal/ui"
10+
tea "github.com/charmbracelet/bubbletea"
11+
)
12+
13+
func runTea(p *tea.Program, userInputCh chan string) error {
14+
defer close(userInputCh)
15+
_, err := p.Run()
16+
return err
17+
}
18+
19+
func main() {
20+
userInputCh := make(chan string)
21+
p := tea.NewProgram(ui.InitialModel(userInputCh), tea.WithAltScreen())
22+
23+
sendUserInput := func(msg *message.Message) {
24+
p.Send(msg)
25+
}
26+
go func() {
27+
28+
for msg := range userInputCh {
29+
err := agent.Chat(context.Background(), msg, agent.DefaultEndpoint(), sendUserInput)
30+
if err != nil {
31+
log.Fatal(err)
32+
}
33+
}
34+
}()
35+
36+
teaErr := runTea(p, userInputCh)
37+
if teaErr != nil {
38+
log.Fatal(teaErr)
39+
}
40+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client
2+
3+
go 1.24.4
4+
5+
require (
6+
github.com/ag-ui-protocol/ag-ui/sdks/community/go v0.0.0-20250816203601-e173ef3a0e9a
7+
github.com/charmbracelet/bubbles v0.21.0
8+
github.com/charmbracelet/bubbletea v1.3.6
9+
github.com/charmbracelet/lipgloss v1.1.0
10+
github.com/sirupsen/logrus v1.9.3
11+
)
12+
13+
require (
14+
github.com/atotto/clipboard v0.1.4 // indirect
15+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
16+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
17+
github.com/charmbracelet/x/ansi v0.9.3 // indirect
18+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
19+
github.com/charmbracelet/x/term v0.2.1 // indirect
20+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
21+
github.com/google/uuid v1.6.0 // indirect
22+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
23+
github.com/mattn/go-isatty v0.0.20 // indirect
24+
github.com/mattn/go-localereader v0.0.1 // indirect
25+
github.com/mattn/go-runewidth v0.0.16 // indirect
26+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
27+
github.com/muesli/cancelreader v0.2.2 // indirect
28+
github.com/muesli/termenv v0.16.0 // indirect
29+
github.com/rivo/uniseg v0.4.7 // indirect
30+
github.com/stretchr/testify v1.10.0 // indirect
31+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
32+
golang.org/x/sync v0.15.0 // indirect
33+
golang.org/x/sys v0.33.0 // indirect
34+
golang.org/x/text v0.25.0 // indirect
35+
)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
2+
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
3+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
4+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
5+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
6+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
7+
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
8+
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
9+
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
10+
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
11+
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
12+
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
13+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
14+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
15+
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
16+
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
17+
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
18+
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
19+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
20+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
21+
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
22+
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
23+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
25+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
27+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
28+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
29+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
30+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
31+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
32+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
33+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
34+
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
35+
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
36+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
37+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
38+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
39+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
40+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
41+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
42+
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
43+
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
44+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
45+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
46+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
47+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
48+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
49+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
50+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
51+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
52+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
53+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
54+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
55+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
56+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
57+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
58+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
59+
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
60+
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
61+
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
62+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
63+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
64+
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
65+
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
66+
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
67+
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
68+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
69+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
70+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
71+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package agent
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"time"
8+
9+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client/internal/event"
10+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/example/client/internal/message"
11+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/client/sse"
12+
"github.com/sirupsen/logrus"
13+
)
14+
15+
func DefaultEndpoint() string {
16+
return "http://localhost:8000/agentic"
17+
}
18+
19+
func Chat(ctx context.Context, inputMsg string, endpoint string, send func(msg *message.Message)) error {
20+
logger := logrus.New()
21+
logger.SetLevel(logrus.FatalLevel)
22+
sseConfig := sse.Config{
23+
Endpoint: endpoint,
24+
ConnectTimeout: 30 * time.Second,
25+
ReadTimeout: 5 * time.Minute,
26+
BufferSize: 100,
27+
Logger: logger,
28+
AuthHeader: "Authorization",
29+
AuthScheme: "Bearer",
30+
}
31+
32+
client := sse.NewClient(sseConfig)
33+
defer func() {
34+
client.Close()
35+
}()
36+
37+
sessionID := "test-session-1755371887"
38+
runID := "run-1755744865857245000"
39+
40+
payload := map[string]interface{}{
41+
"threadId": sessionID,
42+
"runId": runID,
43+
"state": map[string]interface{}{},
44+
"messages": []map[string]interface{}{
45+
{
46+
"id": "msg-1",
47+
"role": "user",
48+
"content": inputMsg,
49+
},
50+
},
51+
"tools": []interface{}{},
52+
"context": []interface{}{},
53+
"forwardedProps": map[string]interface{}{},
54+
}
55+
56+
// Start the SSE stream
57+
var err error
58+
frames, errorCh, err := client.Stream(sse.StreamOptions{
59+
Context: ctx,
60+
Payload: payload,
61+
})
62+
63+
if err != nil {
64+
return errors.New("Failed to establish SSE connection")
65+
}
66+
67+
// Parse SSE events
68+
for {
69+
select {
70+
case frame, ok := <-frames:
71+
if !ok {
72+
return nil
73+
}
74+
75+
rawEvent, err := event.Parse(frame.Data)
76+
if err != nil {
77+
return fmt.Errorf("failed to process SSE event %w", err)
78+
}
79+
currMsg := message.NewMessage(rawEvent)
80+
if currMsg == nil {
81+
return fmt.Errorf("failed to parse message %w", err)
82+
}
83+
send(currMsg)
84+
85+
case err, ok := <-errorCh:
86+
if !ok {
87+
break
88+
}
89+
if err != nil {
90+
break
91+
}
92+
93+
case <-ctx.Done():
94+
break
95+
}
96+
}
97+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package event
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/events"
8+
)
9+
10+
func Parse(data []byte) (events.Event, error) {
11+
// Parse the SSE event
12+
var eventData map[string]interface{}
13+
14+
err := json.Unmarshal(data, &eventData)
15+
if err != nil {
16+
return nil, fmt.Errorf("received non-JSON frame event data %w", err)
17+
}
18+
19+
decoder := events.NewEventDecoder(nil)
20+
21+
// Extract event type - the server sends it as "type" field directly
22+
eventType, _ := eventData["type"].(string)
23+
24+
event, err := decoder.DecodeEvent(eventType, data)
25+
if err != nil {
26+
return nil, fmt.Errorf("failed to decode event %w", err)
27+
}
28+
29+
return event, nil
30+
}

0 commit comments

Comments
 (0)