Extension library for Google's ADK-Go.
adk-go-pkg provides production-ready building blocks that complement
ADK-Go with capabilities it does not ship
out of the box.
| Feature | Description |
|---|---|
| OpenAI Model Provider | Drop-in model.LLM adapter for any OpenAI-compatible API (OpenAI, Ollama, LiteLLM, OpenRouter, vLLM, Together AI). Supports both /v1/chat/completions and /v1/responses endpoints. |
| Anthropic Model Provider | Drop-in model.LLM adapter for Anthropic's Messages API. Supports streaming, tool calling, images, structured output, thinking blocks, and prompt caching. Bedrock and Vertex AI are only supported through gateways that expose the Anthropic /v1/messages endpoint. |
| Generic AG-UI Server | Framework-agnostic AG-UI protocol server (agui/) with event emitter, state management (RFC 6902 JSON Patch via evanphx/json-patch), predictive state tracker, tool orchestration, middleware, encrypted-value scrubbing, and SSE handler. Zero ADK dependency. |
| ADK-Go AG-UI Bridge | Translates ADK-Go session events to AG-UI events (aguiadk/). Thread-to-session mapping, state/message snapshots, streaming tool calls, client tool hand-back (NextRun, Inline, and HandBack modes), HITL runstore & resume, tool call validation, activity snapshots, suppressed tool mode, and preset configurations. |
| Prompt Templating | text/template-based prompt rendering engine with agent context data (state, user, session, artifacts, memory), 12 built-in functions, template registry, loader (files/embed.FS), and llmagent.InstructionProvider integration. |
| Planners | Structured plan generation (ReAct JSON and free-form Thinking) that separates reasoning from execution. |
| File Artifact Service | Filesystem-backed artifact.Service with automatic versioning and metadata sidecars. |
| Session Rewind | Roll a session back to any prior event, recalculating state from replayed deltas. |
| Config Agent Loader | Declare entire agent trees in YAML/JSON and build them at runtime via a factory registry. Now includes Agent Skills support. |
| Agent Skills Config | Declarative skill integration via YAML/JSON. Supports filesystem sources with preload optimization and specific skill loading (wildcard or filtered by name). |
| Test Utilities | Complete fake implementations of all ADK-Go interfaces for deterministic testing without external LLM providers. Includes FakeLLM, FakeAgent, FakeSession, FakeArtifactService, FakeMemoryService, FakeSessionService, and RunnerBuilder. |
| Evaluation Framework | Evaluate agent performance with eval sets, built-in metrics (trajectory, response match, rubrics, safety, hallucinations), LLM-as-judge auto-raters, user simulation, and a local eval service. Mirrors ADK Python's eval package. |
| AG-UI MCP Support | Inject MCP (Model Context Protocol) server tools into AG-UI agents. Two integration paths: MCPMiddleware for generic tool injection + server-side execution, and MCPAppsMiddleware for UI-enabled tools + proxied MCP requests. Bridge wiring via aguiadk.BuildMCPServerToolsets using ADK-Go's mcptoolset. |
go get github.com/ieshan/adk-go-pkgpackage main
import (
"context"
"fmt"
"log"
"os"
"github.com/ieshan/adk-go-pkg/model/openai"
"google.golang.org/adk/v2/model"
"google.golang.org/genai"
)
func main() {
m, err := openai.New(openai.Config{
Model: "gpt-4o",
APIKey: os.Getenv("OPENAI_API_KEY"),
})
if err != nil {
log.Fatal(err)
}
req := &model.LLMRequest{
Contents: []*genai.Content{
genai.NewContentFromText("Hello!", "user"),
},
}
for resp, err := range m.GenerateContent(context.Background(), req, false) {
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Content.Parts[0].Text)
}
}package main
import (
"context"
"fmt"
"log"
"os"
"github.com/ieshan/adk-go-pkg/model/anthropic"
"google.golang.org/adk/v2/model"
"google.golang.org/genai"
)
func main() {
m, err := anthropic.New(anthropic.Config{
Model: "claude-sonnet-4-20250514",
APIKey: os.Getenv("ANTHROPIC_API_KEY"),
})
if err != nil {
log.Fatal(err)
}
req := &model.LLMRequest{
Contents: []*genai.Content{
genai.NewContentFromText("Hello!", "user"),
},
}
for resp, err := range m.GenerateContent(context.Background(), req, false) {
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Content.Parts[0].Text)
}
}package main
import (
"context"
"iter"
"log"
"net/http"
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/events"
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/types"
"github.com/ieshan/adk-go-pkg/agui"
)
func main() {
agent := agui.AgentFunc(func(ctx context.Context, input types.RunAgentInput) iter.Seq2[events.Event, error] {
ch := make(chan events.Event, 64)
emitter := agui.NewEventEmitter(ch)
go func() {
defer close(ch)
emitter.RunStarted(input.ThreadID, input.RunID)
msgID := emitter.GenerateMessageID()
role := "assistant"
emitter.TextMessageStart(msgID, &role)
emitter.TextMessageContent(msgID, "Hello from AG-UI!")
emitter.TextMessageEnd(msgID)
emitter.RunFinishedWithOptions(input.ThreadID, input.RunID)
}()
return agui.ChanToIter(ctx, ch)
})
handler, err := agui.Handler(agui.Config{Agent: agent})
if err != nil {
log.Fatal(err)
}
log.Fatal(http.ListenAndServe(":8080", handler))
}package main
import (
"iter"
"log"
"net/http"
"github.com/ieshan/adk-go-pkg/agui"
"github.com/ieshan/adk-go-pkg/aguiadk"
"google.golang.org/adk/v2/agent"
"google.golang.org/adk/v2/session"
"google.golang.org/genai"
)
func main() {
myAgent, err := agent.New(agent.Config{
Name: "greeter",
Run: func(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] {
return func(yield func(*session.Event, error) bool) {
content := genai.NewContentFromText("Hello from ADK!", genai.RoleModel)
yield(&session.Event{Author: "greeter", Content: content}, nil)
}
},
})
if err != nil {
log.Fatal(err)
}
handler, err := aguiadk.Handler(
aguiadk.Config{
Agent: myAgent,
AppName: "my-chatbot",
UserID: "default-user",
},
agui.Config{},
)
if err != nil {
log.Fatal(err)
}
http.Handle("/api/agent", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}package main
import (
"fmt"
"log"
"github.com/ieshan/adk-go-pkg/prompt"
)
func main() {
engine := prompt.New()
tmpl, err := engine.Parse("greeting", "Hello {{.Input.name}}!")
if err != nil {
log.Fatal(err)
}
data := prompt.BuildData(map[string]any{"name": "world"})
rendered, err := tmpl.Execute(data)
if err != nil {
log.Fatal(err)
}
fmt.Println(rendered)
}For llmagent.InstructionProvider integration:
provider, err := prompt.NewInstructionProvider(
"You are {{.Agent.Name}}. User: {{.User.Text}}. Country: {{.State.Get \"country\"}}",
)
// Pass to llmagent.Config{InstructionProvider: provider}package main
import (
"context"
"fmt"
"log"
"github.com/ieshan/adk-go-pkg/planner"
"google.golang.org/adk/v2/model"
)
func main() {
var myLLM model.LLM // your model
p := planner.NewPlanReAct(planner.PlanReActConfig{
Model: myLLM,
MaxSteps: 5,
})
plan, err := p.GeneratePlan(context.Background(), &planner.PlanRequest{
UserMessage: "Book a flight and send a confirmation email",
ToolDescriptions: []planner.ToolDescription{
{Name: "book_flight", Description: "Books a flight"},
{Name: "send_email", Description: "Sends an email"},
},
})
if err != nil {
log.Fatal(err)
}
for i, step := range plan.Steps {
fmt.Printf("Step %d: %s\n", i+1, step.Description)
}
}package main
import (
"context"
"fmt"
"log"
"github.com/ieshan/adk-go-pkg/artifact/file"
"google.golang.org/adk/v2/artifact"
"google.golang.org/genai"
)
func main() {
svc, err := file.New(file.Config{RootDir: "/tmp/artifacts"})
if err != nil {
log.Fatal(err)
}
defer svc.Close()
resp, err := svc.Save(context.Background(), &artifact.SaveRequest{
AppName: "myapp",
UserID: "alice",
SessionID: "session-1",
FileName: "report.txt",
Part: &genai.Part{Text: "quarterly report"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("saved version:", resp.Version)
// Get artifact version metadata without loading content
versionResp, err := svc.GetArtifactVersion(context.Background(), &artifact.GetArtifactVersionRequest{
AppName: "myapp",
UserID: "alice",
SessionID: "session-1",
FileName: "report.txt",
Version: 0, // 0 means latest
})
if err != nil {
log.Fatal(err)
}
fmt.Println("MIME type:", versionResp.ArtifactVersion.MimeType)
}package main
import (
"context"
"fmt"
"log"
"github.com/ieshan/adk-go-pkg/session/rewind"
"google.golang.org/adk/v2/session"
)
func main() {
ctx := context.Background()
svc := session.InMemoryService()
// ... create session, append events ...
rewound, err := rewind.RewindToIndex(ctx, svc, "my-app", "user-1", "session-abc", 2)
if err != nil {
log.Fatal(err)
}
fmt.Println("events remaining:", rewound.Events().Len())
}package main
import (
"context"
"log"
"os"
"github.com/ieshan/adk-go-pkg/config"
"google.golang.org/adk/v2/model"
"google.golang.org/adk/v2/tool"
)
func main() {
reg := config.NewRegistry()
reg.RegisterModel("openai", func(cfg map[string]any) (model.LLM, error) {
// build your model from cfg["model"] and other keys
return nil, nil
})
reg.RegisterTool("search", func(cfg map[string]any) (tool.Tool, error) {
// build your tool
return nil, nil
})
root, err := os.OpenRoot(".")
if err != nil {
log.Fatal(err)
}
defer root.Close()
agent, runCfg, liveRunCfg, ctxCacheCfg, err := config.LoadAndBuild(context.Background(), root, "agents/root.yaml", reg)
if err != nil {
log.Fatal(err)
}
_ = agent
_ = runCfg // *agent.RunConfig (may be nil)
_ = liveRunCfg // *agent.LiveRunConfig (may be nil)
_ = ctxCacheCfg // *config.ContextCacheConfig (may be nil)
}package main
import (
"context"
"log"
"os"
"github.com/ieshan/adk-go-pkg/config"
)
func main() {
reg := config.NewRegistry()
// Filesystem skill factory is built-in, no registration needed
root, err := os.OpenRoot(".")
if err != nil {
log.Fatal(err)
}
defer root.Close()
// Load agent with skills from YAML
agent, _, _, _, err := config.LoadAndBuild(context.Background(), root, "agents/skills-agent.yaml", reg)
if err != nil {
log.Fatal(err)
}
// Agent now has access to skills defined in ./skills/
_ = agent
}Example YAML configuration:
name: skills-agent
agent_class: LlmAgent
model: openai/gpt-4o
instruction: "You are a helpful assistant with access to specialized skills."
skill_sets:
- name: filesystem
config:
path: "./skills"
preload: complete
# Optional: load only specific skills instead of all
# names: ["weather", "cooking"]package main
import (
"context"
"testing"
"github.com/ieshan/adk-go-pkg/testutil"
"google.golang.org/adk/v2/agent"
"google.golang.org/adk/v2/agent/llmagent"
"google.golang.org/adk/v2/runner"
"google.golang.org/genai"
)
func TestMyAgent(t *testing.T) {
ctx := context.Background()
// Create fake LLM with preconfigured responses
llm := testutil.NewFakeLLM(
testutil.NewTextResponse("I'll help you!"),
)
// Build agent with fake LLM
ag, _ := llmagent.New(llmagent.Config{
Name: "test-agent",
Model: llm,
Instruction: "You are helpful.",
})
// Use RunnerBuilder for end-to-end testing
r, fakes, _ := testutil.NewRunnerBuilder().
WithAgent(ag).
BuildWithFakes()
// Run and collect events
events, _ := testutil.CollectEvents(r.Run(ctx, "user-1", "session-1",
genai.NewContentFromText("Hello", "user"), agent.RunConfig{}))
// Assert on results and calls
if len(events) == 0 {
t.Error("expected events")
}
if fakes.SessionService.AppendEventCount() == 0 {
t.Error("expected events to be appended")
}
}package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/ieshan/adk-go-pkg/eval"
"google.golang.org/adk/v2/model"
"google.golang.org/genai"
)
func main() {
ctx := context.Background()
// Create an in-memory eval sets manager and add a case.
setsMgr := eval.NewInMemoryEvalSetsManager()
if _, err := setsMgr.CreateEvalSet(ctx, "my-app", "basic-eval"); err != nil {
log.Fatal(err)
}
if err := setsMgr.AddEvalCase(ctx, "my-app", "basic-eval", eval.EvalCase{
EvalID: "case-1",
Conversation: []eval.Invocation{
{UserContent: genai.NewContentFromText("Hello", "user")},
},
}); err != nil {
log.Fatal(err)
}
// Create an agent evaluator with your agent runner and LLM.
var agentRunner eval.AgentRunner // your agent runner
var llm model.LLM // your judge LLM (optional)
evaluator := eval.NewAgentEvaluator(
agentRunner, setsMgr, nil, eval.DefaultMetricEvaluatorRegistry(),
llm,
)
// Configure metrics with thresholds.
config := eval.EvalConfig{
Criteria: map[string]json.RawMessage{
"tool_trajectory_avg_score": json.RawMessage(`{"threshold": 0.8}`),
},
}
result, err := evaluator.Evaluate(ctx, "my-app", "basic-eval", config)
if err != nil {
log.Fatal(err)
}
for _, cr := range result.EvalCaseResults {
fmt.Printf("Case %s: %s\n", cr.EvalID, cr.FinalEvalStatus)
}
}Inject MCP server tools into any AG-UI agent and execute them server-side:
package main
import (
"context"
"iter"
"log"
"net/http"
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/events"
"github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/core/types"
"github.com/ieshan/adk-go-pkg/agui"
)
func main() {
agent := agui.AgentFunc(func(ctx context.Context, input types.RunAgentInput) iter.Seq2[events.Event, error] {
// your agent logic
return func(yield func(events.Event, error) bool) {}
})
mcpMW := agui.NewMCPMiddleware([]agui.MCPClientConfig{
{Type: "http", URL: "https://example.com/mcp", ServerID: "srv1"},
}, agui.MCPMiddlewareOptions{MaxIterations: 32})
handler, err := agui.Handler(agui.Config{Agent: mcpMW(agent)})
if err != nil {
log.Fatal(err)
}
log.Fatal(http.ListenAndServe(":8080", handler))
}For ADK-Go native integration, use aguiadk.BuildMCPServerToolsets to create mcptoolset.Toolset instances from MCP configs. See docs/agui-mcp.md for both integration paths, MCPAppsMiddleware for UI-enabled tools, and proxied MCP request handling.
- Go 1.27+ — Uses
iter.Seq2and range-over-func. - ADK-Go v2.3.0+ (
google.golang.org/adk/v2) — Required for Agent Skills support - GenAI v1.71.0 (
google.golang.org/genai)
- OpenAI Responses API: The
model/openaipackage now supports both/v1/chat/completionsand/v1/responsesendpoints. Select viaConfig.API. The Responses path supports reasoning items (Thought:true), flat tool declarations, function_call/function_call_output items, and typed SSE streaming events. See docs/openai-model.md. - Prompt Templating: New
promptpackage withtext/template-based rendering engine, agent context data (state, user, session, artifacts, memory), 12 built-in functions, thread-safeTemplateRegistry,TemplateLoader(files/embed.FS),TemplateReftagged union, andllmagent.InstructionProviderintegration. Config loader supportsInstructionTemplatefield for declarative templated instructions. See docs/prompt.md. - AG-UI ADK Bridge Gap Fix: Closed all 10 AG-UI protocol feature gaps between the
agui/aguiadkpackages and the AG-UI example server. New features: disconnect cancellation, client tool hand-back (NextRun, Inline, and HandBack modes viaClientToolset), streaming tool calls (progressiveTOOL_CALL_*from partialFunctionCallparts), HITL runstore & resume (RunStorewith TTL, atomic claim, approval interrupts), tool call validation (synthetic IDs, errorTOOL_CALL_RESULTfor malformed calls), suppressed tool mode (Config.SuppressToolEvents+Config.ToolToStateMapperemitsSTATE_DELTAinstead ofTOOL_CALL_*), predictive state tracker (agui.PredictiveStateTrackerfor ghosted/_predictivedeltas), activity snapshots (tool_useandapproval_request), encrypted value scrubbing inMessagesSnapshot, and preset configurations (AgenticChatPreset,HumanInTheLoopPreset,GenerativeUIPreset,SharedStatePreset,InlineToolsPreset,HandBackPreset,PredictiveStatePreset,AgenticGenerativeUIPreset).StateManager.Applynow usesevanphx/json-patch/v5for RFC 6902 compliance. See docs/aguiadk-bridge.md. - Evaluation Framework: New
evalpackage with eval sets, 13 built-in metrics, LLM-as-judge evaluators, user simulation, and local eval service. Mirrors ADK Python eval package. See docs/eval.md. - Anthropic Model Provider: Drop-in
model.LLMadapter for Anthropic's Messages API. Supports streaming, tool calling, images, structured output, thinking blocks, and prompt caching. See docs/anthropic-model.md. - Test Utilities: New
testutilpackage with fake implementations of all ADK-Go interfaces (FakeLLM, FakeAgent, FakeSession, FakeArtifactService, FakeMemoryService, FakeSessionService, RunnerBuilder). Enables fast, deterministic testing without external LLM providers. See docs/testutil.md. - Agent Skills Config: Skillset support in config loader. Define skills in YAML/JSON with filesystem sources, preload optimization, and specific skill loading (wildcard or filtered by name).
- OpenAI Model Provider: Supports genai
FunctionResponse.Partsstructure for function calling. - AG-UI MCP Support: MCP (Model Context Protocol) integration for AG-UI agents.
MCPMiddlewareinjects MCP server tools and executes them server-side in an agentic loop.MCPAppsMiddlewarehandles UI-enabled tools (SEP-1865) and proxied MCP requests from frontends.aguiadk.BuildMCPServerToolsetsbridges MCP servers to ADK-Go's nativemcptoolset. See docs/agui-mcp.md. - File Artifact Service:
GetArtifactVersionmethod for metadata retrieval without loading full content.
Beyond ADK-Go and google.golang.org/genai, the only additional direct dependencies are:
github.com/ag-ui-protocol/ag-ui/sdks/community/go-- AG-UI event types and helpersgithub.com/evanphx/json-patch/v5-- RFC 6902 JSON Patch forStateManager.Applygithub.com/google/jsonschema-go-- JSON Schema forClientToolsetparameter validationgithub.com/google/uuid-- UUID generation for eval session IDsgithub.com/modelcontextprotocol/go-sdk-- MCP client for transport, tool listing, and executiongo.yaml.in/yaml/v4-- YAML parsing for the config loadergolang.org/x/sync--errgroupfor parallel MCP server queries
TBD