Skip to content

Commit e56381c

Browse files
feat(vertexai): Support AgentEngine for Agent Development Kit (googleapis#14168)
feat(vertexai): Support AgentEngine for Agent Development Kit
1 parent d38abf9 commit e56381c

16 files changed

Lines changed: 7562 additions & 4 deletions

vertexai/genai/agentengines.go

Lines changed: 878 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package genai
16+
17+
import (
18+
"flag"
19+
"fmt"
20+
"strings"
21+
"testing"
22+
"time"
23+
24+
"google.golang.org/genai"
25+
)
26+
27+
const (
28+
apiMode = "api" // API mode runs the tests in any environment where the tests can hit the actual service.
29+
unitMode = "unit" // Unit mode runs the test in the github actions using the mocked service (this is the default).
30+
)
31+
32+
var mode = flag.String("mode", unitMode, "Test mode")
33+
34+
func waitForAgentEngineOperation(tb testing.TB, name string, done bool, c *Client) any {
35+
tb.Helper()
36+
var res any
37+
for !done {
38+
tb.Logf("Waiting for operation to complete: [%s]\n", name)
39+
time.Sleep(5 * time.Second)
40+
op, err := c.AgentEngines.getAgentOperation(tb.Context(), name, nil)
41+
if err != nil {
42+
tb.Fatalf("getAgentOperation failed, err: %v", err)
43+
}
44+
done = op.Done
45+
res = op
46+
}
47+
return res
48+
}
49+
50+
func newTestClient(tb testing.TB) *Client {
51+
tb.Helper()
52+
client, err := NewGenAIClient(tb.Context(), &genai.ClientConfig{
53+
Backend: genai.BackendVertexAI,
54+
})
55+
if err != nil {
56+
tb.Fatal(err)
57+
}
58+
return client
59+
}
60+
61+
func getResourceNameFromOperation(operationName string) string {
62+
i := strings.Index(operationName, "/operations/")
63+
return operationName[:i]
64+
}
65+
66+
func createAgentEngineAndWait(t testing.TB, tt testing.TB, client *Client, config *CreateAgentEngineConfig) *ReasoningEngine {
67+
tt.Helper()
68+
if config == nil {
69+
config = &CreateAgentEngineConfig{
70+
DisplayName: tt.Name(),
71+
Description: "You can remove this agent engine if it is older than 10 minutes. It must be an orphan AE.",
72+
}
73+
}
74+
if config.DisplayName == "" {
75+
config.DisplayName = tt.Name()
76+
}
77+
if config.Description == "" {
78+
config.Description = "You can remove this agent engine if it is older than 10 minutes. It must be an orphan AE."
79+
}
80+
createOp, err := client.AgentEngines.create(tt.Context(), config)
81+
if err != nil {
82+
tt.Fatalf("create() failed unexpectedly: %v", err)
83+
}
84+
tt.Cleanup(cleanupAgentEngine(t, client, getResourceNameFromOperation(createOp.Name)))
85+
createOp = waitForAgentEngineOperation(tt, createOp.Name, createOp.Done, client).(*AgentEngineOperation)
86+
return createOp.Response
87+
}
88+
89+
func cleanupAgentEngine(t testing.TB, client *Client, name string) func() {
90+
return func() {
91+
t.Logf("Cleaning up AgentEngine: %s", name)
92+
deleteOp, err := client.AgentEngines.delete(t.Context(), name, genai.Ptr(true), nil)
93+
if err != nil {
94+
t.Logf("cleanup() failed, err: %v", err)
95+
} else {
96+
waitForAgentEngineOperation(t, deleteOp.Name, deleteOp.Done, client)
97+
}
98+
}
99+
}
100+
101+
func TestAgentEngines(t *testing.T) {
102+
if *mode != apiMode {
103+
t.Skipf("Skipping %s. We only tun these in the api mode.", t.Name())
104+
}
105+
106+
t.Run("Create", func(tt *testing.T) {
107+
client := newTestClient(tt)
108+
l := client.AgentEngines.apiClient.ClientConfig().Location
109+
p := client.AgentEngines.apiClient.ClientConfig().Project
110+
model := fmt.Sprintf("projects/%s/locations/%s/publishers/google/models/gemini-2.0-flash-001", p, l)
111+
embeddingModel := fmt.Sprintf("projects/%s/locations/%s/publishers/google/models/text-embedding-005", p, l)
112+
request := &CreateAgentEngineConfig{
113+
ContextSpec: &ReasoningEngineContextSpec{
114+
MemoryBankConfig: &ReasoningEngineContextSpecMemoryBankConfig{
115+
GenerationConfig: &ReasoningEngineContextSpecMemoryBankConfigGenerationConfig{
116+
Model: model,
117+
},
118+
SimilaritySearchConfig: &ReasoningEngineContextSpecMemoryBankConfigSimilaritySearchConfig{
119+
EmbeddingModel: embeddingModel,
120+
},
121+
TTLConfig: &ReasoningEngineContextSpecMemoryBankConfigTTLConfig{
122+
DefaultTTL: 120 * time.Second,
123+
},
124+
CustomizationConfigs: []*MemoryBankCustomizationConfig{{
125+
MemoryTopics: []*MemoryBankCustomizationConfigMemoryTopic{{
126+
ManagedMemoryTopic: &MemoryBankCustomizationConfigMemoryTopicManagedMemoryTopic{
127+
ManagedTopicEnum: ManagedTopicEnumUserPreferences,
128+
},
129+
}},
130+
GenerateMemoriesExamples: []*MemoryBankCustomizationConfigGenerateMemoriesExample{{
131+
ConversationSource: &MemoryBankCustomizationConfigGenerateMemoriesExampleConversationSource{
132+
Events: []*MemoryBankCustomizationConfigGenerateMemoriesExampleConversationSourceEvent{{
133+
Content: &genai.Content{
134+
Role: "user",
135+
Parts: []*genai.Part{{
136+
Text: "Hello",
137+
}},
138+
},
139+
}},
140+
},
141+
GeneratedMemories: []*MemoryBankCustomizationConfigGenerateMemoriesExampleGeneratedMemory{{
142+
Fact: "I like to say hello.",
143+
Topics: []*MemoryTopicID{{
144+
ManagedMemoryTopic: ManagedTopicEnumUserPreferences,
145+
}},
146+
}},
147+
}},
148+
EnableThirdPersonMemories: genai.Ptr(true),
149+
}},
150+
},
151+
},
152+
}
153+
re := createAgentEngineAndWait(t, tt, client, request)
154+
if got, want := re.DisplayName, request.DisplayName; got != want {
155+
tt.Errorf("create() returned DisplayName %v, want %v", got, want)
156+
}
157+
if got, want := re.Description, request.Description; got != want {
158+
tt.Errorf("create() returned Description %v, want %v", got, want)
159+
}
160+
})
161+
162+
t.Run("Delete", func(tt *testing.T) {
163+
ctx := tt.Context()
164+
client := newTestClient(t)
165+
re := createAgentEngineAndWait(t, tt, client, nil)
166+
167+
deleteOp, err := client.AgentEngines.delete(t.Context(), re.Name, nil, nil)
168+
if err != nil {
169+
tt.Fatalf("delete() failed unexpectedly: %v", err)
170+
}
171+
waitForAgentEngineOperation(t, deleteOp.Name, deleteOp.Done, client)
172+
173+
got, err := client.AgentEngines.get(ctx, re.Name, nil)
174+
if err == nil {
175+
t.Errorf("delete() didn't remove the reasoning engine, want error(NOT_FOUND), got: %v", got)
176+
}
177+
})
178+
179+
t.Run("Get", func(tt *testing.T) {
180+
ctx := tt.Context()
181+
client := newTestClient(tt)
182+
re := createAgentEngineAndWait(t, tt, client, nil)
183+
_, err := client.AgentEngines.get(ctx, re.Name, nil)
184+
if err != nil {
185+
tt.Errorf("get() failed unexpectedly: %v", err)
186+
}
187+
})
188+
189+
t.Run("List", func(tt *testing.T) {
190+
ctx := tt.Context()
191+
client := newTestClient(tt)
192+
createAgentEngineAndWait(t, tt, client, nil)
193+
list, err := client.AgentEngines.list(ctx, &ListAgentEngineConfig{PageSize: 2})
194+
if err != nil {
195+
tt.Fatalf("list() failed unexpectedly: %v", err)
196+
}
197+
if len(list.ReasoningEngines) == 0 {
198+
tt.Errorf("list(), want !0 but got %v", len(list.ReasoningEngines))
199+
}
200+
})
201+
202+
t.Run("Update", func(tt *testing.T) {
203+
ctx := tt.Context()
204+
client := newTestClient(tt)
205+
re := createAgentEngineAndWait(t, tt, client, nil)
206+
want := fmt.Sprintf("Updated(%s)", re.DisplayName)
207+
op, err := client.AgentEngines.update(ctx, re.Name, &UpdateAgentEngineConfig{
208+
DisplayName: want,
209+
})
210+
if err != nil {
211+
tt.Fatalf("update() failed unexpectedly: %v", err)
212+
}
213+
waitForAgentEngineOperation(tt, op.Name, op.Done, client)
214+
updated, err := client.AgentEngines.get(ctx, re.Name, nil)
215+
if err != nil {
216+
tt.Errorf("get() failed unexpectedly: %v", err)
217+
}
218+
if got := updated.DisplayName; got != want {
219+
tt.Errorf("update() returned DisplayName %v, want %v", got, want)
220+
}
221+
222+
})
223+
}

vertexai/genai/client.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,33 @@ import (
3232
"cloud.google.com/go/vertexai/internal"
3333
"google.golang.org/api/iterator"
3434
"google.golang.org/api/option"
35+
"google.golang.org/genai"
3536
)
3637

38+
type clientAgentEngines struct {
39+
AgentEngines
40+
Memories *clientMemories
41+
Sessions *clientSessions
42+
Sandboxes *Sandboxes
43+
}
44+
45+
type clientMemories struct {
46+
Memories
47+
Revisions *MemoryRevisions
48+
}
49+
50+
type clientSessions struct {
51+
Sessions
52+
Events *SessionEvents
53+
}
54+
3755
// A Client is a Google Vertex AI client.
3856
type Client struct {
39-
pc *aiplatform.PredictionClient
40-
cc *cacheClient
41-
projectID string
42-
location string
57+
pc *aiplatform.PredictionClient
58+
cc *cacheClient
59+
projectID string
60+
location string
61+
AgentEngines *clientAgentEngines
4362
}
4463

4564
// NewClient creates a new Google Vertex AI client.
@@ -439,3 +458,25 @@ func float32pToInt32p(x *float32) *int32 {
439458
i := int32(*x)
440459
return &i
441460
}
461+
462+
// NewGenAIClient creates a new Google Vertex AI client and configures the the GenAI components.
463+
func NewGenAIClient(ctx context.Context, cc *genai.ClientConfig) (*Client, error) {
464+
ac, err := genai.NewInternalAPIClient(ctx, cc)
465+
if err != nil {
466+
return nil, err
467+
}
468+
return &Client{
469+
AgentEngines: &clientAgentEngines{
470+
AgentEngines: AgentEngines{apiClient: ac},
471+
Memories: &clientMemories{
472+
Memories: Memories{apiClient: ac},
473+
Revisions: &MemoryRevisions{apiClient: ac},
474+
},
475+
Sessions: &clientSessions{
476+
Sessions: Sessions{apiClient: ac},
477+
Events: &SessionEvents{apiClient: ac},
478+
},
479+
Sandboxes: &Sandboxes{apiClient: ac},
480+
},
481+
}, nil
482+
}

0 commit comments

Comments
 (0)