-
Notifications
You must be signed in to change notification settings - Fork 339
Expand file tree
/
Copy pathadapter.go
More file actions
139 lines (118 loc) · 3.6 KB
/
adapter.go
File metadata and controls
139 lines (118 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package a2a
import (
"cmp"
"fmt"
"iter"
"log/slog"
"strings"
"google.golang.org/adk/agent"
"google.golang.org/adk/model"
adksession "google.golang.org/adk/session"
"google.golang.org/genai"
dagent "github.com/docker/docker-agent/pkg/agent"
"github.com/docker/docker-agent/pkg/runtime"
"github.com/docker/docker-agent/pkg/session"
"github.com/docker/docker-agent/pkg/team"
)
// newDockerAgentAdapter creates a new ADK agent adapter from a docker agent team and agent name
func newDockerAgentAdapter(t *team.Team, agentName string) (agent.Agent, error) {
a, err := t.Agent(agentName)
if err != nil {
return nil, fmt.Errorf("failed to get agent %s: %w", agentName, err)
}
desc := cmp.Or(a.Description(), "Agent "+agentName)
return agent.New(agent.Config{
Name: agentName,
Description: desc,
Run: func(ctx agent.InvocationContext) iter.Seq2[*adksession.Event, error] {
return runDockerAgent(ctx, t, agentName, a)
},
})
}
// runDockerAgent executes a docker agent and returns ADK session events
func runDockerAgent(ctx agent.InvocationContext, t *team.Team, agentName string, a *dagent.Agent) iter.Seq2[*adksession.Event, error] {
return func(yield func(*adksession.Event, error) bool) {
// Extract user message from the ADK context
userContent := ctx.UserContent()
message := contentToMessage(userContent)
// Create a session
sess := session.New(
session.WithUserMessage(message),
session.WithMaxIterations(a.MaxIterations()),
session.WithMaxConsecutiveToolCalls(a.MaxConsecutiveToolCalls()),
session.WithMaxOldToolCallTokens(a.MaxOldToolCallTokens()),
session.WithToolsApproved(true),
)
// Create runtime
rt, err := runtime.New(t,
runtime.WithCurrentAgent(agentName),
)
if err != nil {
yield(nil, fmt.Errorf("failed to create runtime: %w", err))
return
}
// Run the agent and collect events
eventsChan := rt.RunStream(ctx, sess)
// Track accumulated content for chunked responses
var contentBuilder strings.Builder
// Convert docker agent events to ADK events and yield them
for event := range eventsChan {
if ctx.Ended() {
slog.Debug("Invocation ended, stopping agent", "agent", agentName)
return
}
switch e := event.(type) {
case *runtime.AgentChoiceEvent:
// Accumulate content chunks
contentBuilder.WriteString(e.Content)
// Create a partial response event
adkEvent := &adksession.Event{
Author: agentName,
LLMResponse: model.LLMResponse{
Content: genai.NewContentFromParts([]*genai.Part{{Text: e.Content}}, genai.RoleModel),
Partial: true,
TurnComplete: false,
},
}
if !yield(adkEvent, nil) {
return
}
case *runtime.ErrorEvent:
// Yield error and stop
yield(nil, fmt.Errorf("%s", e.Error))
return
case *runtime.StreamStoppedEvent:
// Send final complete event with all accumulated content
if contentBuilder.Len() > 0 {
finalEvent := &adksession.Event{
Author: agentName,
LLMResponse: model.LLMResponse{
Content: genai.NewContentFromParts([]*genai.Part{{Text: contentBuilder.String()}}, genai.RoleModel),
Partial: false,
TurnComplete: true,
FinishReason: genai.FinishReasonStop,
},
}
yield(finalEvent, nil)
return
}
}
}
}
}
// contentToMessage converts a genai.Content to a string message
func contentToMessage(content *genai.Content) string {
if content == nil {
return ""
}
var message string
for _, part := range content.Parts {
if part.Text != "" {
if message != "" {
message += "\n"
}
message += part.Text
}
}
return message
}