A thin adapter layer for Google's ADK (Agent Development Kit) to the AG-UI protocol, built on top of the official AG-UI Go SDK.
This package provides seamless integration between Google ADK agents and AG-UI compatible frontends. It uses the official AG-UI SDK for all event types and SSE encoding, keeping this library minimal and protocol-compliant.
go get github.com/sicko7947/agui-gopackage main
import (
"log"
"net/http"
aguigo "github.com/sicko7947/agui-go"
"google.golang.org/adk/session"
)
func main() {
myAgent := createYourAgent()
handler, err := aguigo.NewADKHandler(
myAgent,
session.InMemoryService(),
"my-app",
)
if err != nil {
log.Fatal(err)
}
http.Handle("/api/ag-ui", handler)
log.Println("AG-UI server running on :8081")
http.ListenAndServe(":8081", nil)
}handler, err := aguigo.NewADKHandler(
myAgent,
session.InMemoryService(),
"my-app",
aguigo.WithStepEvents(true), // Emit STEP_STARTED/STEP_FINISHED for thinking
)Implement the EventSource interface to use with any agent framework:
package main
import (
"net/http"
aguigo "github.com/sicko7947/agui-go"
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/events"
)
type MyEventSource struct{}
func (s *MyEventSource) Run(ctx aguigo.HandlerContext, input aguigo.RunAgentInput) <-chan events.Event {
ch := make(chan events.Event)
go func() {
defer close(ch)
// Send run started
ch <- events.NewRunStartedEvent(ctx.ThreadID, ctx.RunID)
// Send a message
msgID := events.GenerateMessageID()
ch <- events.NewTextMessageStartEvent(msgID, events.WithRole("assistant"))
ch <- events.NewTextMessageContentEvent(msgID, "Hello from my agent!")
ch <- events.NewTextMessageEndEvent(msgID)
// Send run finished
ch <- events.NewRunFinishedEvent(ctx.ThreadID, ctx.RunID)
}()
return ch
}
func main() {
h := aguigo.New(aguigo.Config{
EventSource: &MyEventSource{},
AppName: "my-app",
Logger: aguigo.StdLogger{},
})
http.Handle("/api/ag-ui", h)
http.ListenAndServe(":8081", nil)
}github.com/sicko7947/agui-go/
├── adapter.go # ADKConverter, ADKHandler - Google ADK integration
├── handler.go # Generic Handler, EventSource interface, utilities
All AG-UI event types come from the official SDK:
import "github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/events"aguigo.NewADKConverter(threadID, runID,
aguigo.WithStepEvents(true), // Emit STEP_* events for thinking/reasoning
aguigo.WithActivityEvents(true), // Emit ACTIVITY_DELTA events
aguigo.WithRawEvents(true), // Include original events
)The ADKConverter handles:
| ADK Event | AG-UI Events |
|---|---|
| Text content | TEXT_MESSAGE_START → TEXT_MESSAGE_CONTENT → TEXT_MESSAGE_END |
| Function calls | TOOL_CALL_START → TOOL_CALL_ARGS → TOOL_CALL_END |
| Function responses | TOOL_CALL_RESULT |
| Thought/reasoning | STEP_STARTED/STEP_FINISHED or CUSTOM("thinking") |
| State delta | STATE_DELTA (JSON Patch operations) |
| Agent transfer | CUSTOM("agent_transfer") |
| Escalation | CUSTOM("escalation") |
import { useAgUiRuntime } from "@assistant-ui/react-ag-ui";
import { HttpAgent } from "@ag-ui/client";
import { AssistantRuntimeProvider, Thread } from "@assistant-ui/react";
function App() {
const agent = new HttpAgent({
url: "http://localhost:8081/api/ag-ui",
});
const runtime = useAgUiRuntime({ agent });
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}const response = await fetch("http://localhost:8081/api/ag-ui", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "text/event-stream",
},
body: JSON.stringify({
threadId: "thread-123",
messages: [{ role: "user", content: [{ type: "text", text: "Hello" }] }],
}),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const lines = decoder.decode(value).split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
console.log(event.type, event);
}
}
}// RunAgentInput - AG-UI protocol input
type RunAgentInput struct {
ThreadID string `json:"threadId"`
RunID string `json:"runId,omitempty"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
Context any `json:"context,omitempty"`
State any `json:"state,omitempty"`
}
// EventSource - implement for custom agents
type EventSource interface {
Run(ctx HandlerContext, input RunAgentInput) <-chan events.Event
}
// HandlerContext - context for agent runs
type HandlerContext struct {
ThreadID string
RunID string
UserID string
Request *http.Request
}// ADK integration
func NewADKHandler(agent agent.Agent, sessionService session.Service, appName string, opts ...Option) (*ADKHandler, error)
func NewADKConverter(threadID, runID string, opts ...Option) *ADKConverter
// Generic handler
func New(config Config) *Handler
// Utilities
func HealthHandler(w http.ResponseWriter, r *http.Request)
func CORSMiddleware(next http.Handler) http.HandlerMIT