├── examples ├── tools │ ├── loadartifacts │ │ └── animal_picture.png │ └── multipletools │ │ └── main.go ├── README.md ├── quickstart │ └── main.go ├── workflowagents │ ├── loop │ │ └── main.go │ ├── sequential │ │ └── main.go │ └── parallel │ │ └── main.go ├── rest │ └── main.go └── web │ ├── agents │ └── image_generator.go │ └── main.go ├── .gitignore ├── agent ├── doc.go ├── remoteagent │ └── doc.go ├── llmagent │ ├── doc.go │ └── testdata │ │ └── TestLLMAgent_healthy_backend.httprr ├── run_config.go ├── workflowagents │ ├── sequentialagent │ │ └── agent.go │ └── loopagent │ │ └── agent.go ├── loader_test.go ├── loader.go └── context.go ├── server ├── adka2a │ ├── doc.go │ └── executor_context.go ├── doc.go └── adkrest │ ├── internal │ ├── services │ │ ├── doc.go │ │ └── apiserverspanexporter.go │ ├── models │ │ ├── models.go │ │ ├── runtime.go │ │ ├── session.go │ │ └── event.go │ └── routers │ │ ├── apps.go │ │ ├── eval.go │ │ ├── runtime.go │ │ ├── routers.go │ │ ├── debug.go │ │ ├── artifacts.go │ │ └── sessions.go │ ├── controllers │ ├── errors.go │ ├── apps.go │ └── handlers.go │ └── handler.go ├── session ├── doc.go └── service.go ├── .github ├── workflows │ ├── nightly.yml │ └── go.yml ├── actions │ └── setup │ │ └── action.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── internal ├── utils │ └── utils_test.go ├── version │ └── version.go ├── sessioninternal │ ├── session.go │ └── mutablesession.go ├── cli │ └── util │ │ ├── text_helpers.go │ │ ├── doc.go │ │ ├── flagset_helpers.go │ │ └── oscmd.go ├── toolinternal │ ├── tool.go │ ├── context_test.go │ ├── toolutils │ │ └── toolutils.go │ └── context.go ├── agent │ ├── state.go │ ├── runconfig │ │ └── run_config.go │ └── parentmap │ │ ├── map.go │ │ └── map_test.go ├── memory │ └── memory.go ├── llminternal │ ├── googlellm │ │ └── variant.go │ ├── file_uploads_processor.go │ ├── agent.go │ ├── other_processors.go │ ├── converters │ │ └── converters.go │ └── clone_test.go ├── context │ ├── context_test.go │ ├── readonly_context.go │ └── invocation_context.go ├── httprr │ └── LICENSE ├── converters │ └── map_structure.go ├── typeutil │ └── convert.go ├── artifact │ ├── artifacts.go │ └── artifacts_test.go ├── testutil │ └── genai.go ├── sessionutils │ └── utils.go └── style_test.go ├── cmd ├── adkgo │ ├── adkgo.go │ └── internal │ │ ├── root │ │ └── root.go │ │ └── deploy │ │ └── deploy.go └── launcher │ ├── prod │ └── prod.go │ ├── full │ └── full.go │ ├── web │ └── webui │ │ └── distr │ │ ├── chunk-2WH2EVR6.js │ │ ├── assets │ │ ├── audio-processor.js │ │ └── ADK-512-color.svg │ │ └── adk_favicon.svg │ └── launcher.go ├── artifact ├── inmemory_test.go └── artifact_key_test.go ├── telemetry └── telemetry.go ├── model ├── gemini │ └── testdata │ │ ├── TestModel_GenerateStream_ok.httprr │ │ ├── TestModel_Generate_ok.httprr │ │ └── TestModel_TrackingHeaders_verifies_headers_are_set.httprr └── llm.go ├── tool ├── exitlooptool │ └── tool.go ├── geminitool │ ├── google_search.go │ ├── tool.go │ └── tool_test.go ├── tool.go └── tool_test.go ├── .golangci.yml ├── util └── instructionutil │ └── instruction.go ├── memory └── service.go └── README.md /examples/tools/loadartifacts/animal_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/adk-go/HEAD/examples/tools/loadartifacts/animal_picture.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore is restricted to the artifacts produced by go build and test. 2 | # for personal setup you can use personal .gitignore: `git config --global core.excludesfile ~/.gitignore` 3 | 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Code coverage profiles and other test artifacts 15 | *.out 16 | coverage.* 17 | *.coverprofile 18 | profile.cov 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | -------------------------------------------------------------------------------- /agent/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent provides entities to build agents using ADK. 16 | package agent 17 | -------------------------------------------------------------------------------- /server/adka2a/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 adka2a allows to expose ADK agents via A2A. 16 | package adka2a 17 | -------------------------------------------------------------------------------- /agent/remoteagent/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 remoteagent allows to use a remote ADK agents. 16 | package remoteagent 17 | -------------------------------------------------------------------------------- /server/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 server hosts protocol implementations to expose and serve ADK agents. 16 | package server 17 | -------------------------------------------------------------------------------- /session/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 session provides types to manage user sessions and their states. 16 | package session 17 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Nightly 2 | 3 | on: 4 | schedule: 5 | - cron: '0 2 * * *' # 2 AM UTC daily 6 | 7 | workflow_dispatch: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 14 | 15 | - name: Setup 16 | uses: ./.github/actions/setup 17 | 18 | - name: Nightly Test 19 | run: go test -race -mod=readonly -v -count=1 -shuffle=on ./... 20 | 21 | vulncheck: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Run govulncheck 25 | uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 26 | with: 27 | go-version-file: go.mod 28 | repo-checkout: true -------------------------------------------------------------------------------- /server/adkrest/internal/services/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 services contains services defined for the ADK-Web REST API. 16 | package services 17 | -------------------------------------------------------------------------------- /internal/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 utils_test 16 | 17 | import "testing" 18 | 19 | func TestNothing(t *testing.T) { 20 | // To make it buildable. 21 | } 22 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 version 16 | 17 | // Version exposes the current ADK Go version, used for llm request tagging 18 | const Version string = "0.3.0" 19 | -------------------------------------------------------------------------------- /internal/sessioninternal/session.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 tool defines internal-only interfaces and logic for tools. 16 | package sessioninternal 17 | 18 | type MutableState interface { 19 | Set(string, any) error 20 | } 21 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup the environment 3 | 4 | outputs: 5 | go-version: 6 | description: 'Go version' 7 | value: ${{ steps.setup-go.outputs.go-version }} 8 | 9 | runs: 10 | using: 'composite' 11 | steps: 12 | - name: Set up Go 13 | id: setup-go 14 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 15 | with: 16 | go-version-file: 'go.mod' 17 | cache: false # wrapper for actions/cache that doesn't support all functionality 18 | 19 | - name: Load Go cache 20 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 21 | with: 22 | path: | 23 | ~/.cache/go-build 24 | ~/go/pkg/mod 25 | key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.mod', '**/go.sum') }} 26 | restore-keys: ${{ runner.os }}-go-build- 27 | -------------------------------------------------------------------------------- /internal/cli/util/text_helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 util 16 | 17 | import "strings" 18 | 19 | func CenterString(s string, w int) string { 20 | sw := w - len(s) 21 | lw := sw / 2 22 | rw := sw - lw 23 | return strings.Repeat(" ", lw) + s + strings.Repeat(" ", rw) 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ** Please make sure you read the contribution guide and file the issues in the right place. ** 11 | [Contribution guide.](https://google.github.io/adk-docs/contributing-guide/) 12 | 13 | **Is your feature request related to a problem? Please describe.** 14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | 16 | **Describe the solution you'd like** 17 | A clear and concise description of what you want to happen. 18 | 19 | **Describe alternatives you've considered** 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /internal/cli/util/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 util provides helper functions for execution of commands and presenting their stderr and stdout in uniform way. 16 | // Provides also a way to capture text generated by FlagSet as command line arguments' documentation (PrintDefaults) 17 | package util 18 | -------------------------------------------------------------------------------- /cmd/adkgo/adkgo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 | // adkgo is a CLI tool to help deploy and test an ADK application. 16 | package main 17 | 18 | import ( 19 | _ "google.golang.org/adk/cmd/adkgo/internal/deploy/cloudrun" 20 | "google.golang.org/adk/cmd/adkgo/internal/root" 21 | ) 22 | 23 | func main() { 24 | root.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /agent/llmagent/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 llmagent provides an LLM-based agent. 16 | // LLM agents use large language models to perform tasks based on instructions, user input, 17 | // deciding on actions to take, and executing actions using available tools or 18 | // delegating to sub agents. 19 | package llmagent 20 | -------------------------------------------------------------------------------- /server/adkrest/internal/models/models.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 models defines the data structures for the REST API. 16 | package models 17 | 18 | import "reflect" 19 | 20 | // IsZeroValue checks if the val is the zero-ed value. 21 | func IsZeroValue(val any) bool { 22 | return val == nil || reflect.DeepEqual(val, reflect.Zero(reflect.TypeOf(val)).Interface()) 23 | } 24 | -------------------------------------------------------------------------------- /internal/cli/util/flagset_helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 util 16 | 17 | import ( 18 | "flag" 19 | "strings" 20 | ) 21 | 22 | // FormatFlagUsage returns a string containing the usage information for the given FlagSet. 23 | func FormatFlagUsage(fs *flag.FlagSet) string { 24 | var b strings.Builder 25 | o := fs.Output() 26 | fs.SetOutput(&b) 27 | fs.PrintDefaults() 28 | fs.SetOutput(o) 29 | return b.String() 30 | } 31 | -------------------------------------------------------------------------------- /artifact/inmemory_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 artifact_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "google.golang.org/adk/artifact" 21 | "google.golang.org/adk/internal/artifact/tests" 22 | ) 23 | 24 | func TestInMemoryArtifactService(t *testing.T) { 25 | factory := func(t *testing.T) (artifact.Service, error) { 26 | return artifact.InMemoryService(), nil 27 | } 28 | tests.TestArtifactService(t, "InMemory", factory) 29 | } 30 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # ADK GO samples 2 | This folder hosts examples to test different features. The examples are usually minimal and simplistic to test one or a few scenarios. 3 | 4 | 5 | **Note**: This is different from the [google/adk-samples](https://github.com/google/adk-samples) repo, which hosts more complex e2e samples for customers to use or modify directly. 6 | 7 | 8 | # Launcher 9 | In many examples you can see such lines: 10 | ```go 11 | l := full.NewLauncher() 12 | err = l.ParseAndRun(ctx, config, os.Args[1:], universal.ErrorOnUnparsedArgs) 13 | if err != nil { 14 | log.Fatalf("run failed: %v\n\n%s", err, l.FormatSyntax()) 15 | } 16 | ``` 17 | 18 | it allows to decide, which launching options are supported in the run-time. 19 | `full.NewLauncher()` includes all major ways you can run the example: 20 | * console 21 | * restapi 22 | * a2a 23 | * webui (it can run standalone or with restapi or a2a). 24 | 25 | Run `go run ./example/quickstart/main.go help` for details 26 | 27 | As an alternative, you may want to use `prod.NewLauncher()` which only builds-in restapi and a2a launchers. 28 | -------------------------------------------------------------------------------- /server/adkrest/controllers/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 controllers 16 | 17 | type statusError struct { 18 | Err error 19 | Code int 20 | } 21 | 22 | func newStatusError(err error, code int) statusError { 23 | return statusError{Err: err, Code: code} 24 | } 25 | 26 | // Error returns an associated error 27 | func (se statusError) Error() string { 28 | return se.Err.Error() 29 | } 30 | 31 | // Status returns an associated status code 32 | func (se statusError) Status() int { 33 | return se.Code 34 | } 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ** Please make sure you read the contribution guide and file the issues in the right place. ** 11 | [Contribution guide.](https://google.github.io/adk-docs/contributing-guide/) 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Please share a minimal code and data to reproduce your problem. 18 | Steps to reproduce the behavior: 19 | 1. Install '...' 20 | 2. Run '....' 21 | 3. Open '....' 22 | 4. Provide error or a stacktrace 23 | 24 | **Expected behavior** 25 | A clear and concise description of what you expected to happen. 26 | 27 | **Screenshots** 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Desktop (please complete the following information):** 31 | - OS: [e.g. macOS, Linux, Windows] 32 | - Go version: 33 | - ADK version: 34 | 35 | **Model Information:** 36 | - Which model is being used (e.g. gemini-2.5-pro) 37 | 38 | **Additional context** 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /internal/toolinternal/tool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 tool defines internal-only interfaces and logic for tools. 16 | package toolinternal 17 | 18 | import ( 19 | "google.golang.org/genai" 20 | 21 | "google.golang.org/adk/model" 22 | "google.golang.org/adk/tool" 23 | ) 24 | 25 | type FunctionTool interface { 26 | tool.Tool 27 | Declaration() *genai.FunctionDeclaration 28 | Run(ctx tool.Context, args any) (result map[string]any, err error) 29 | } 30 | 31 | type RequestProcessor interface { 32 | ProcessRequest(ctx tool.Context, req *model.LLMRequest) error 33 | } 34 | -------------------------------------------------------------------------------- /artifact/artifact_key_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 artifact 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | ) 22 | 23 | func TestArtifactKey(t *testing.T) { 24 | key := artifactKey{ 25 | AppName: "testapp", 26 | UserID: "testuser", 27 | SessionID: "testsession", 28 | FileName: "testfile", 29 | Version: 123, 30 | } 31 | var key2 artifactKey 32 | err := key2.Decode(key.Encode()) 33 | if err != nil { 34 | t.Fatalf("error decoding key:%s", err) 35 | } 36 | if diff := cmp.Diff(key, key2); diff != "" { 37 | t.Errorf("key mismatch (-want +got):\n%s", diff) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/agent/state.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent 16 | 17 | // holds Agent internal state 18 | type Agent interface { 19 | internal() *State 20 | } 21 | 22 | type State struct { 23 | AgentType Type 24 | Config any 25 | } 26 | 27 | type Type string 28 | 29 | const ( 30 | TypeLLMAgent Type = "LLMAgent" 31 | TypeLoopAgent Type = "LoopAgent" 32 | TypeSequentialAgent Type = "SequentialAgent" 33 | TypeParallelAgent Type = "ParallelAgent" 34 | TypeCustomAgent Type = "CustomAgent" 35 | ) 36 | 37 | func (s *State) internal() *State { return s } 38 | 39 | func Reveal(a Agent) *State { return a.internal() } 40 | -------------------------------------------------------------------------------- /cmd/adkgo/internal/root/root.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 root handles command line parameters 16 | package root 17 | 18 | import ( 19 | "os" 20 | 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | // RootCmd represents the base command when called without any subcommands 25 | var RootCmd = &cobra.Command{ 26 | Use: "adkgo", 27 | Short: "CLI tool for use with ADK-GO", 28 | Long: `adkgo is a CLI tool which allows developer to quickly deploy and test an agentic application`, 29 | } 30 | 31 | // Execute adds all child commands to the root command and sets flags appropriately. 32 | func Execute() { 33 | err := RootCmd.Execute() 34 | if err != nil { 35 | os.Exit(1) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cmd/adkgo/internal/deploy/deploy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 deploy allows to run deployment-related subcommands. 16 | package deploy 17 | 18 | import ( 19 | "github.com/spf13/cobra" 20 | 21 | "google.golang.org/adk/cmd/adkgo/internal/root" 22 | ) 23 | 24 | // DeployCmd represents the deploy command. 25 | var DeployCmd = &cobra.Command{ 26 | Use: "deploy", 27 | Short: "Makes deployment to various platforms easy", 28 | Long: `Please see subcommands for details`, 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | if len(args) == 0 { 31 | return cmd.Help() 32 | } 33 | return nil 34 | }, 35 | } 36 | 37 | func init() { 38 | root.RootCmd.AddCommand(DeployCmd) 39 | } 40 | -------------------------------------------------------------------------------- /internal/memory/memory.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 memory 16 | 17 | import ( 18 | "context" 19 | 20 | "google.golang.org/adk/memory" 21 | "google.golang.org/adk/session" 22 | ) 23 | 24 | type Memory struct { 25 | Service memory.Service 26 | SessionID string 27 | UserID string 28 | AppName string 29 | } 30 | 31 | func (a *Memory) AddSession(ctx context.Context, session session.Session) error { 32 | return a.Service.AddSession(ctx, session) 33 | } 34 | 35 | func (a *Memory) Search(ctx context.Context, query string) (*memory.SearchResponse, error) { 36 | return a.Service.Search(ctx, &memory.SearchRequest{ 37 | AppName: a.AppName, 38 | UserID: a.UserID, 39 | Query: query, 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /server/adkrest/controllers/apps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 controllers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/agent" 21 | ) 22 | 23 | // AppsAPIController is the controller for the Apps API. 24 | type AppsAPIController struct { 25 | agentLoader agent.Loader 26 | } 27 | 28 | // NewAppsAPIController creates a controller for Apps API. 29 | func NewAppsAPIController(agentLoader agent.Loader) *AppsAPIController { 30 | return &AppsAPIController{agentLoader: agentLoader} 31 | } 32 | 33 | // ListAppsHandler handles listing all loaded agents. 34 | func (c *AppsAPIController) ListAppsHandler(rw http.ResponseWriter, req *http.Request) { 35 | apps := c.agentLoader.ListAgents() 36 | EncodeJSONResponse(apps, http.StatusOK, rw) 37 | } 38 | -------------------------------------------------------------------------------- /internal/llminternal/googlellm/variant.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 googlellm 16 | 17 | import ( 18 | "os" 19 | "slices" 20 | ) 21 | 22 | const ( 23 | // For using credentials from Google Vertex AI 24 | GoogleLLMVariantVertexAI = "VERTEX_AI" 25 | // For using API Key from Google AI Studio 26 | GoogleLLMVariantGeminiAPI = "GEMINI_API" 27 | ) 28 | 29 | // GetGoogleLLMVariant returns the Google LLM variant to use. 30 | // see https://google.github.io/adk-docs/get-started/quickstart/#set-up-the-model 31 | func GetGoogleLLMVariant() string { 32 | useVertexAI, _ := os.LookupEnv("GOOGLE_GENAI_USE_VERTEXAI") 33 | if slices.Contains([]string{"1", "true"}, useVertexAI) { 34 | return GoogleLLMVariantVertexAI 35 | } 36 | return GoogleLLMVariantGeminiAPI 37 | } 38 | -------------------------------------------------------------------------------- /cmd/launcher/prod/prod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 prod provides easy way to play with ADK with all available options without 16 | // development support (no console, no ADK Web UI) including only production 17 | // options like the REST API and A2A support. 18 | package prod 19 | 20 | import ( 21 | "google.golang.org/adk/cmd/launcher" 22 | "google.golang.org/adk/cmd/launcher/universal" 23 | "google.golang.org/adk/cmd/launcher/web" 24 | "google.golang.org/adk/cmd/launcher/web/a2a" 25 | "google.golang.org/adk/cmd/launcher/web/api" 26 | ) 27 | 28 | // NewLauncher returns a launcher capable of serving ADK REST API and A2A. 29 | func NewLauncher() launcher.Launcher { 30 | return universal.NewLauncher(web.NewLauncher(api.NewLauncher(), a2a.NewLauncher())) 31 | } 32 | -------------------------------------------------------------------------------- /internal/agent/runconfig/run_config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 runconfig 16 | 17 | import "context" 18 | 19 | type StreamingMode string 20 | 21 | const ( 22 | StreamingModeNone StreamingMode = "none" 23 | StreamingModeSSE StreamingMode = "sse" 24 | StreamingModeBidi StreamingMode = "bidi" 25 | ) 26 | 27 | type RunConfig struct { 28 | StreamingMode StreamingMode 29 | } 30 | 31 | func ToContext(ctx context.Context, cfg *RunConfig) context.Context { 32 | return context.WithValue(ctx, runConfigCtxKey, cfg) 33 | } 34 | 35 | func FromContext(ctx context.Context) *RunConfig { 36 | m, ok := ctx.Value(runConfigCtxKey).(*RunConfig) 37 | if !ok { 38 | return nil 39 | } 40 | return m 41 | } 42 | 43 | type ctxKey int 44 | 45 | const runConfigCtxKey ctxKey = 0 46 | -------------------------------------------------------------------------------- /cmd/launcher/full/full.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 full provides easy way to play with ADK with all available options 16 | package full 17 | 18 | import ( 19 | "google.golang.org/adk/cmd/launcher" 20 | "google.golang.org/adk/cmd/launcher/console" 21 | "google.golang.org/adk/cmd/launcher/universal" 22 | "google.golang.org/adk/cmd/launcher/web" 23 | "google.golang.org/adk/cmd/launcher/web/a2a" 24 | "google.golang.org/adk/cmd/launcher/web/api" 25 | "google.golang.org/adk/cmd/launcher/web/webui" 26 | ) 27 | 28 | // NewLauncher returnes the most versatile universal launcher with all options built-in. 29 | func NewLauncher() launcher.Launcher { 30 | return universal.NewLauncher(console.NewLauncher(), web.NewLauncher(api.NewLauncher(), a2a.NewLauncher(), webui.NewLauncher())) 31 | } 32 | -------------------------------------------------------------------------------- /telemetry/telemetry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 telemetry allows to set up custom telemetry processors that the ADK events 16 | // will be emitted to. 17 | package telemetry 18 | 19 | import ( 20 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 21 | 22 | internaltelemetry "google.golang.org/adk/internal/telemetry" 23 | ) 24 | 25 | // RegisterSpanProcessor registers the span processor to local trace provider instance. 26 | // Any processor should be registered BEFORE any of the events are emitted, otherwise 27 | // the registration will be ignored. 28 | // In addition to the RegisterSpanProcessor function, global trace provider configs 29 | // are respected. 30 | func RegisterSpanProcessor(processor sdktrace.SpanProcessor) { 31 | internaltelemetry.AddSpanProcessor(processor) 32 | } 33 | -------------------------------------------------------------------------------- /agent/run_config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent 16 | 17 | // StreamingMode defines the streaming mode for agent execution. 18 | type StreamingMode string 19 | 20 | const ( 21 | // StreamingModeNone indicates no streaming. 22 | StreamingModeNone StreamingMode = "none" 23 | // StreamingModeSSE enables server-sent events streaming, one-way, where 24 | // LLM response parts are streamed immediately as they are generated. 25 | StreamingModeSSE StreamingMode = "sse" 26 | ) 27 | 28 | // RunConfig controls runtime behavior of an agent. 29 | type RunConfig struct { 30 | // StreamingMode defines the streaming mode for an agent. 31 | StreamingMode StreamingMode 32 | // If true, ADK runner will save each part of the user input that is a blob 33 | // (e.g., images, files) as an artifact. 34 | SaveInputBlobsAsArtifacts bool 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: 3 | # - https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 4 | # - https://golangci-lint.run/usage/configuration/ 5 | 6 | name: Go 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | 12 | pull_request: 13 | branches: [ "main" ] 14 | 15 | workflow_dispatch: 16 | 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | test: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup 28 | uses: ./.github/actions/setup 29 | 30 | - name: Tidy 31 | run: go mod tidy -diff 32 | 33 | - name: Build 34 | run: go build -mod=readonly -v ./... 35 | 36 | - name: Test 37 | run: go test -mod=readonly -v ./... 38 | 39 | lint: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 43 | 44 | - name: Setup 45 | uses: ./.github/actions/setup 46 | 47 | - name: Lint 48 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 49 | with: 50 | install-mode: goinstall 51 | version: 5256574b81bcedfbcae9099f745f6aee9335da10 # v2.3.1 52 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/apps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // AppsAPIRouter defines the routes for the Apps API. 24 | type AppsAPIRouter struct { 25 | appsController *controllers.AppsAPIController 26 | } 27 | 28 | // NewAppsAPIRouter creates a new AppsAPIRouter. 29 | func NewAppsAPIRouter(controller *controllers.AppsAPIController) *AppsAPIRouter { 30 | return &AppsAPIRouter{appsController: controller} 31 | } 32 | 33 | // Routes returns the routes for the Apps API. 34 | func (r *AppsAPIRouter) Routes() Routes { 35 | return Routes{ 36 | Route{ 37 | Name: "ListApps", 38 | Methods: []string{http.MethodGet}, 39 | Pattern: "/list-apps", 40 | HandlerFunc: r.appsController.ListAppsHandler, 41 | }, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /internal/toolinternal/context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 toolinternal 16 | 17 | import ( 18 | "testing" 19 | 20 | "google.golang.org/adk/agent" 21 | contextinternal "google.golang.org/adk/internal/context" 22 | "google.golang.org/adk/session" 23 | ) 24 | 25 | func TestToolContext(t *testing.T) { 26 | inv := contextinternal.NewInvocationContext(t.Context(), contextinternal.InvocationContextParams{}) 27 | toolCtx := NewToolContext(inv, "fn1", &session.EventActions{}) 28 | 29 | if _, ok := toolCtx.(agent.ReadonlyContext); !ok { 30 | t.Errorf("ToolContext(%+T) is unexpectedly not a ReadonlyContext", toolCtx) 31 | } 32 | if _, ok := toolCtx.(agent.CallbackContext); !ok { 33 | t.Errorf("ToolContext(%+T) is unexpectedly not a CallbackContext", toolCtx) 34 | } 35 | if got, ok := toolCtx.(agent.InvocationContext); ok { 36 | t.Errorf("ToolContext(%+T) is unexpectedly an InvocationContext", got) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/context/context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 context 16 | 17 | import ( 18 | "testing" 19 | 20 | "google.golang.org/adk/agent" 21 | ) 22 | 23 | func TestReadonlyContext(t *testing.T) { 24 | inv := NewInvocationContext(t.Context(), InvocationContextParams{}) 25 | readonly := NewReadonlyContext(inv) 26 | 27 | if got, ok := readonly.(agent.InvocationContext); ok { 28 | t.Errorf("ReadonlyContext(%+T) is unexpectedly an InvocationContext", got) 29 | } 30 | } 31 | 32 | func TestCallbackContext(t *testing.T) { 33 | inv := NewInvocationContext(t.Context(), InvocationContextParams{}) 34 | callback := NewCallbackContext(inv) 35 | 36 | if _, ok := callback.(agent.ReadonlyContext); !ok { 37 | t.Errorf("CallbackContext(%+T) is unexpectedly not a ReadonlyContext", callback) 38 | } 39 | if got, ok := callback.(agent.InvocationContext); ok { 40 | t.Errorf("CallbackContext(%+T) is unexpectedly an InvocationContext", got) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/httprr/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions are 4 | met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above 8 | copyright notice, this list of conditions and the following disclaimer 9 | in the documentation and/or other materials provided with the 10 | distribution. 11 | * Neither the name of Google Inc. nor the names of its 12 | contributors may be used to endorse or promote products derived from 13 | this software without specific prior written permission. 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /model/gemini/testdata/TestModel_GenerateStream_ok.httprr: -------------------------------------------------------------------------------- 1 | httprr trace v1 2 | 375 1028 3 | POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent?alt=sse HTTP/1.1 4 | Host: generativelanguage.googleapis.com 5 | User-Agent: Go-http-client/1.1 6 | Content-Length: 129 7 | Content-Type: application/json 8 | 9 | {"contents":[{"parts":[{"text":"What is the capital of France? One word."}],"role":"user"}],"generationConfig":{"temperature":0}}HTTP/2.0 200 OK 10 | Connection: close 11 | Content-Disposition: attachment 12 | Content-Type: text/event-stream 13 | Date: Mon, 18 Aug 2025 13:55:15 GMT 14 | Server: scaffolding on HTTPServer2 15 | Server-Timing: gfet4t7; dur=327 16 | Vary: Origin 17 | Vary: X-Origin 18 | Vary: Referer 19 | X-Content-Type-Options: nosniff 20 | X-Frame-Options: SAMEORIGIN 21 | X-Xss-Protection: 0 22 | 23 | data: {"candidates": [{"content": {"parts": [{"text": "Paris"}],"role": "model"}}],"usageMetadata": {"promptTokenCount": 11,"totalTokenCount": 11,"promptTokensDetails": [{"modality": "TEXT","tokenCount": 11}]},"modelVersion": "gemini-2.0-flash","responseId": "wzCjaPa4As7shMIP2Mei0AI"} 24 | 25 | data: {"candidates": [{"content": {"parts": [{"text": "\n"}],"role": "model"},"finishReason": "STOP"}],"usageMetadata": {"promptTokenCount": 10,"candidatesTokenCount": 2,"totalTokenCount": 12,"promptTokensDetails": [{"modality": "TEXT","tokenCount": 10}],"candidatesTokensDetails": [{"modality": "TEXT","tokenCount": 2}]},"modelVersion": "gemini-2.0-flash","responseId": "wzCjaPa4As7shMIP2Mei0AI"} 26 | 27 | -------------------------------------------------------------------------------- /model/gemini/testdata/TestModel_Generate_ok.httprr: -------------------------------------------------------------------------------- 1 | httprr trace v1 2 | 361 954 3 | POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent HTTP/1.1 4 | Host: generativelanguage.googleapis.com 5 | User-Agent: Go-http-client/1.1 6 | Content-Length: 129 7 | Content-Type: application/json 8 | 9 | {"contents":[{"parts":[{"text":"What is the capital of France? One word."}],"role":"user"}],"generationConfig":{"temperature":0}}HTTP/2.0 200 OK 10 | Content-Type: application/json; charset=UTF-8 11 | Date: Mon, 18 Aug 2025 13:55:14 GMT 12 | Server: scaffolding on HTTPServer2 13 | Server-Timing: gfet4t7; dur=394 14 | Vary: Origin 15 | Vary: X-Origin 16 | Vary: Referer 17 | X-Content-Type-Options: nosniff 18 | X-Frame-Options: SAMEORIGIN 19 | X-Xss-Protection: 0 20 | 21 | { 22 | "candidates": [ 23 | { 24 | "content": { 25 | "parts": [ 26 | { 27 | "text": "Paris\n" 28 | } 29 | ], 30 | "role": "model" 31 | }, 32 | "finishReason": "STOP", 33 | "avgLogprobs": -0.00055273278849199414 34 | } 35 | ], 36 | "usageMetadata": { 37 | "promptTokenCount": 10, 38 | "candidatesTokenCount": 2, 39 | "totalTokenCount": 12, 40 | "promptTokensDetails": [ 41 | { 42 | "modality": "TEXT", 43 | "tokenCount": 10 44 | } 45 | ], 46 | "candidatesTokensDetails": [ 47 | { 48 | "modality": "TEXT", 49 | "tokenCount": 2 50 | } 51 | ] 52 | }, 53 | "modelVersion": "gemini-2.0-flash", 54 | "responseId": "wjCjaI3AI7mokdUPn8Gf2Ac" 55 | } 56 | -------------------------------------------------------------------------------- /internal/llminternal/file_uploads_processor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 llminternal 16 | 17 | import ( 18 | "google.golang.org/adk/agent" 19 | "google.golang.org/adk/internal/llminternal/googlellm" 20 | "google.golang.org/adk/model" 21 | ) 22 | 23 | // The Gemini API (non-Vertex) backend does not support the display_name parameter for file uploads, 24 | // so it must be removed to prevent request failures. 25 | func removeDisplayNameIfExists(ctx agent.InvocationContext, req *model.LLMRequest) error { 26 | if req.Contents == nil { 27 | return nil 28 | } 29 | for _, content := range req.Contents { 30 | if content.Parts == nil { 31 | continue 32 | } 33 | if googlellm.GetGoogleLLMVariant() == googlellm.GoogleLLMVariantGeminiAPI { 34 | for _, part := range content.Parts { 35 | if part.InlineData != nil { 36 | part.InlineData.DisplayName = "" 37 | } 38 | if part.FileData != nil { 39 | part.FileData.DisplayName = "" 40 | } 41 | } 42 | } 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /model/gemini/testdata/TestModel_TrackingHeaders_verifies_headers_are_set.httprr: -------------------------------------------------------------------------------- 1 | httprr trace v1 2 | 309 994 3 | POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent HTTP/1.1 4 | Host: generativelanguage.googleapis.com 5 | User-Agent: Go-http-client/1.1 6 | Content-Length: 78 7 | Content-Type: application/json 8 | 9 | {"contents":[{"parts":[{"text":"ping"}],"role":"user"}],"generationConfig":{}}HTTP/2.0 200 OK 10 | Content-Type: application/json; charset=UTF-8 11 | Date: Thu, 06 Nov 2025 11:23:18 GMT 12 | Server: scaffolding on HTTPServer2 13 | Server-Timing: gfet4t7; dur=585 14 | Vary: Origin 15 | Vary: X-Origin 16 | Vary: Referer 17 | X-Content-Type-Options: nosniff 18 | X-Frame-Options: SAMEORIGIN 19 | X-Xss-Protection: 0 20 | 21 | { 22 | "candidates": [ 23 | { 24 | "content": { 25 | "parts": [ 26 | { 27 | "text": "Pong! \n\nIs there anything I can help you with?\n" 28 | } 29 | ], 30 | "role": "model" 31 | }, 32 | "finishReason": "STOP", 33 | "avgLogprobs": -0.35232135227748324 34 | } 35 | ], 36 | "usageMetadata": { 37 | "promptTokenCount": 1, 38 | "candidatesTokenCount": 14, 39 | "totalTokenCount": 15, 40 | "promptTokensDetails": [ 41 | { 42 | "modality": "TEXT", 43 | "tokenCount": 1 44 | } 45 | ], 46 | "candidatesTokensDetails": [ 47 | { 48 | "modality": "TEXT", 49 | "tokenCount": 14 50 | } 51 | ] 52 | }, 53 | "modelVersion": "gemini-2.0-flash", 54 | "responseId": "JYUMaciaHZ7hnsEP9amb6Qg" 55 | } 56 | -------------------------------------------------------------------------------- /server/adkrest/internal/models/runtime.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 models 16 | 17 | import ( 18 | "fmt" 19 | 20 | "google.golang.org/genai" 21 | ) 22 | 23 | type RunAgentRequest struct { 24 | AppName string `json:"appName"` 25 | 26 | UserId string `json:"userId"` 27 | 28 | SessionId string `json:"sessionId"` 29 | 30 | NewMessage genai.Content `json:"newMessage"` 31 | 32 | Streaming bool `json:"streaming,omitempty"` 33 | 34 | StateDelta *map[string]any `json:"stateDelta,omitempty"` 35 | } 36 | 37 | // AssertRunAgentRequestRequired checks if the required fields are not zero-ed 38 | func (req RunAgentRequest) AssertRunAgentRequestRequired() error { 39 | elements := map[string]any{ 40 | "appName": req.AppName, 41 | "userId": req.UserId, 42 | "sessionId": req.SessionId, 43 | "newMessage": req.NewMessage, 44 | } 45 | for name, el := range elements { 46 | if isZero := IsZeroValue(el); isZero { 47 | return fmt.Errorf("%s is required", name) 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /tool/exitlooptool/tool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 exitlooptool provides a tool that allows an agent to exit a loop. 16 | package exitlooptool 17 | 18 | import ( 19 | "fmt" 20 | 21 | "google.golang.org/adk/tool" 22 | "google.golang.org/adk/tool/functiontool" 23 | ) 24 | 25 | // EmptyArgs is an empty struct used as an argument for the exitLoop tool. 26 | type EmptyArgs struct{} 27 | 28 | func exitLoop(ctx tool.Context, myArgs EmptyArgs) (map[string]string, error) { 29 | ctx.Actions().Escalate = true 30 | ctx.Actions().SkipSummarization = true 31 | return map[string]string{}, nil 32 | } 33 | 34 | // New creates an instance of an exitLoop tool. 35 | func New() (tool.Tool, error) { 36 | exitLoopTool, err := functiontool.New(functiontool.Config{ 37 | Name: "exit_loop", 38 | Description: "Exits the loop.\nCall this function only when you are instructed to do so.\n", 39 | }, exitLoop) 40 | if err != nil { 41 | return nil, fmt.Errorf("error creating exit loop tool: %w", err) 42 | } 43 | return exitLoopTool, nil 44 | } 45 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/eval.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // EvalAPIRouter defines the routes for the Eval API. 24 | type EvalAPIRouter struct{} 25 | 26 | // Routes returns the routes for the Apps API. 27 | func (r *EvalAPIRouter) Routes() Routes { 28 | return Routes{ 29 | Route{ 30 | Name: "ListEvalSets", 31 | Methods: []string{http.MethodGet}, 32 | Pattern: "/apps/{app_name}/eval_sets", 33 | HandlerFunc: controllers.Unimplemented, 34 | }, 35 | Route{ 36 | Name: "ListEvalSets", 37 | Methods: []string{http.MethodPost, http.MethodOptions}, 38 | Pattern: "/apps/{app_name}/eval_sets/{eval_set_name}", 39 | HandlerFunc: controllers.Unimplemented, 40 | }, 41 | Route{ 42 | Name: "ListEvalResults", 43 | Methods: []string{http.MethodGet}, 44 | Pattern: "/apps/{app_name}/eval_results", 45 | HandlerFunc: controllers.Unimplemented, 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/llminternal/agent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 llminternal 16 | 17 | import ( 18 | "google.golang.org/genai" 19 | 20 | "google.golang.org/adk/agent" 21 | "google.golang.org/adk/model" 22 | "google.golang.org/adk/tool" 23 | ) 24 | 25 | // holds LLMAgent internal state 26 | type Agent interface { 27 | internal() *State 28 | } 29 | 30 | type State struct { 31 | Model model.LLM 32 | 33 | Tools []tool.Tool 34 | Toolsets []tool.Toolset 35 | 36 | IncludeContents string 37 | 38 | GenerateContentConfig *genai.GenerateContentConfig 39 | 40 | Instruction string 41 | InstructionProvider InstructionProvider 42 | GlobalInstruction string 43 | GlobalInstructionProvider InstructionProvider 44 | 45 | DisallowTransferToParent bool 46 | DisallowTransferToPeers bool 47 | 48 | InputSchema *genai.Schema 49 | OutputSchema *genai.Schema 50 | 51 | OutputKey string 52 | } 53 | 54 | type InstructionProvider func(ctx agent.ReadonlyContext) (string, error) 55 | 56 | func (s *State) internal() *State { return s } 57 | 58 | func Reveal(a Agent) *State { return a.internal() } 59 | -------------------------------------------------------------------------------- /agent/llmagent/testdata/TestLLMAgent_healthy_backend.httprr: -------------------------------------------------------------------------------- 1 | httprr trace v1 2 | 508 945 3 | POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent HTTP/1.1 4 | Host: generativelanguage.googleapis.com 5 | User-Agent: Go-http-client/1.1 6 | Content-Length: 276 7 | Content-Type: application/json 8 | 9 | {"contents":[{"parts":[{"text":"Handle the requests as specified in the System Instruction."}],"role":"user"}],"generationConfig":{},"systemInstruction":{"parts":[{"text":"Answer as precisely as possible."},{"text":"Roll the dice and report only the result."}],"role":"user"}}HTTP/2.0 200 OK 10 | Content-Type: application/json; charset=UTF-8 11 | Date: Mon, 29 Sep 2025 08:03:49 GMT 12 | Server: scaffolding on HTTPServer2 13 | Server-Timing: gfet4t7; dur=618 14 | Vary: Origin 15 | Vary: X-Origin 16 | Vary: Referer 17 | X-Content-Type-Options: nosniff 18 | X-Frame-Options: SAMEORIGIN 19 | X-Xss-Protection: 0 20 | 21 | { 22 | "candidates": [ 23 | { 24 | "content": { 25 | "parts": [ 26 | { 27 | "text": "6\n" 28 | } 29 | ], 30 | "role": "model" 31 | }, 32 | "finishReason": "STOP", 33 | "avgLogprobs": -0.2207629382610321 34 | } 35 | ], 36 | "usageMetadata": { 37 | "promptTokenCount": 25, 38 | "candidatesTokenCount": 2, 39 | "totalTokenCount": 27, 40 | "promptTokensDetails": [ 41 | { 42 | "modality": "TEXT", 43 | "tokenCount": 25 44 | } 45 | ], 46 | "candidatesTokensDetails": [ 47 | { 48 | "modality": "TEXT", 49 | "tokenCount": 2 50 | } 51 | ] 52 | }, 53 | "modelVersion": "gemini-2.0-flash", 54 | "responseId": "ZD3aaI7bIqenkdUPq4ONQA" 55 | } 56 | -------------------------------------------------------------------------------- /tool/geminitool/google_search.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 geminitool 16 | 17 | import ( 18 | "google.golang.org/genai" 19 | 20 | "google.golang.org/adk/model" 21 | "google.golang.org/adk/tool" 22 | ) 23 | 24 | // GoogleSearch is a built-in tool that is automatically invoked by Gemini 2 25 | // models to retrieve search results from Google Search. 26 | // The tool operates internally within the model and does not require or 27 | // perform local code execution. 28 | type GoogleSearch struct{} 29 | 30 | // Name implements tool.Tool. 31 | func (s GoogleSearch) Name() string { 32 | return "google_search" 33 | } 34 | 35 | // Description implements tool.Tool. 36 | func (s GoogleSearch) Description() string { 37 | return "Performs a Google search to retrieve information from the web." 38 | } 39 | 40 | // ProcessRequest adds the GoogleSearch tool to the LLM request. 41 | func (s GoogleSearch) ProcessRequest(ctx tool.Context, req *model.LLMRequest) error { 42 | return setTool(req, &genai.Tool{ 43 | GoogleSearch: &genai.GoogleSearch{}, 44 | }) 45 | } 46 | 47 | // IsLongRunning implements tool.Tool. 48 | func (t GoogleSearch) IsLongRunning() bool { 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /cmd/launcher/web/webui/distr/chunk-2WH2EVR6.js: -------------------------------------------------------------------------------- 1 | var q=Object.create;var m=Object.defineProperty,r=Object.defineProperties,s=Object.getOwnPropertyDescriptor,t=Object.getOwnPropertyDescriptors,u=Object.getOwnPropertyNames,j=Object.getOwnPropertySymbols,v=Object.getPrototypeOf,n=Object.prototype.hasOwnProperty,p=Object.prototype.propertyIsEnumerable;var l=(b,a)=>(a=Symbol[b])?a:Symbol.for("Symbol."+b),w=b=>{throw TypeError(b)};var o=(b,a,c)=>a in b?m(b,a,{enumerable:!0,configurable:!0,writable:!0,value:c}):b[a]=c,z=(b,a)=>{for(var c in a||={})n.call(a,c)&&o(b,c,a[c]);if(j)for(var c of j(a))p.call(a,c)&&o(b,c,a[c]);return b},A=(b,a)=>r(b,t(a));var B=(b,a)=>{var c={};for(var d in b)n.call(b,d)&&a.indexOf(d)<0&&(c[d]=b[d]);if(b!=null&&j)for(var d of j(b))a.indexOf(d)<0&&p.call(b,d)&&(c[d]=b[d]);return c};var C=(b,a)=>()=>(a||b((a={exports:{}}).exports,a),a.exports);var x=(b,a,c,d)=>{if(a&&typeof a=="object"||typeof a=="function")for(let e of u(a))!n.call(b,e)&&e!==c&&m(b,e,{get:()=>a[e],enumerable:!(d=s(a,e))||d.enumerable});return b};var D=(b,a,c)=>(c=b!=null?q(v(b)):{},x(a||!b||!b.__esModule?m(c,"default",{value:b,enumerable:!0}):c,b));var E=(b,a,c)=>new Promise((d,e)=>{var f=g=>{try{i(c.next(g))}catch(k){e(k)}},h=g=>{try{i(c.throw(g))}catch(k){e(k)}},i=g=>g.done?d(g.value):Promise.resolve(g.value).then(f,h);i((c=c.apply(b,a)).next())}),y=function(b,a){this[0]=b,this[1]=a};var F=b=>{var a=b[l("asyncIterator")],c=!1,d,e={};return a==null?(a=b[l("iterator")](),d=f=>e[f]=h=>a[f](h)):(a=a.call(b),d=f=>e[f]=h=>{if(c){if(c=!1,f==="throw")throw h;return h}return c=!0,{done:!1,value:new y(new Promise(i=>{var g=a[f](h);g instanceof Object||w("Object expected"),i(g)}),1)}}),e[l("iterator")]=()=>e,d("next"),"throw"in a?d("throw"):e.throw=f=>{throw f},"return"in a&&d("return"),e};export{z as a,A as b,B as c,C as d,D as e,E as f,F as g}; 2 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/runtime.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // RuntimeAPIRouter defines the routes for the Runtime API. 24 | type RuntimeAPIRouter struct { 25 | runtimeController *controllers.RuntimeAPIController 26 | } 27 | 28 | // NewRuntimeAPIRouter creates a new RuntimeAPIRouter. 29 | func NewRuntimeAPIRouter(controller *controllers.RuntimeAPIController) *RuntimeAPIRouter { 30 | return &RuntimeAPIRouter{runtimeController: controller} 31 | } 32 | 33 | // Routes returns the routes for the Runtime API. 34 | func (r *RuntimeAPIRouter) Routes() Routes { 35 | return Routes{ 36 | Route{ 37 | Name: "RunAgent", 38 | Methods: []string{http.MethodPost, http.MethodOptions}, 39 | Pattern: "/run", 40 | HandlerFunc: controllers.NewErrorHandler(r.runtimeController.RunHandler), 41 | }, 42 | Route{ 43 | Name: "RunAgentSse", 44 | Methods: []string{http.MethodPost, http.MethodOptions}, 45 | Pattern: "/run_sse", 46 | HandlerFunc: controllers.NewErrorHandler(r.runtimeController.RunSSEHandler), 47 | }, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | formatters: 4 | enable: 5 | - goimports 6 | - gofumpt 7 | 8 | settings: 9 | goimports: 10 | local-prefixes: 11 | - google.golang.org/adk 12 | gofumpt: 13 | extra-rules: true 14 | module-path: google.golang.org/adk 15 | 16 | linters: 17 | enable: 18 | - goheader 19 | 20 | settings: 21 | 22 | goheader: 23 | values: 24 | const: 25 | COMPANY: Google LLC 26 | regexp: 27 | YEAR: 20\d\d 28 | template: |- 29 | Copyright {{ YEAR }} {{ COMPANY }} 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); 32 | you may not use this file except in compliance with the License. 33 | You may obtain a copy of the License at 34 | 35 | http://www.apache.org/licenses/LICENSE-2.0 36 | 37 | Unless required by applicable law or agreed to in writing, software 38 | distributed under the License is distributed on an "AS IS" BASIS, 39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 | See the License for the specific language governing permissions and 41 | limitations under the License. 42 | 43 | staticcheck: 44 | checks: 45 | # The default config: 46 | - all 47 | - -ST1000 48 | - -ST1003 49 | - -ST1016 50 | - -ST1020 51 | - -ST1021 52 | - -ST1022 53 | # Changes to the default config: 54 | - -QF1001 # Exclude "Apply De Morgan's law" 55 | - -QF1008 # Exclude "Omit embedded fields from selector expression" 56 | 57 | exclusions: 58 | rules: 59 | - path: 'internal/httprr/.*' 60 | linters: 61 | - goheader 62 | - errcheck 63 | - staticcheck 64 | -------------------------------------------------------------------------------- /internal/converters/map_structure.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 converters 16 | 17 | import ( 18 | "encoding/json" 19 | ) 20 | 21 | // ToMapStructure converts any to map[string]any. 22 | // We can't use mapstructure library in a way compatible with ADK-python, because genai type fields 23 | // don't have proper field tags. 24 | // TODO(yarolegovich): field annotation PR for genai types. 25 | func ToMapStructure(data any) (map[string]any, error) { 26 | bytes, err := json.Marshal(data) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | var result map[string]any 32 | if err := json.Unmarshal(bytes, &result); err != nil { 33 | return nil, err 34 | } 35 | return result, nil 36 | } 37 | 38 | // FromMapStructure converts map[string]any to the type parameter T. 39 | // We can't use mapstructure library in a way compatible with ADK-python, because genai type fields 40 | // don't have proper field tags. 41 | // TODO(yarolegovich): field annotation PR for genai types. 42 | func FromMapStructure[T any](data map[string]any) (*T, error) { 43 | bytes, err := json.Marshal(data) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | var zero T 49 | if err := json.Unmarshal(bytes, &zero); err != nil { 50 | return nil, err 51 | } 52 | return &zero, nil 53 | } 54 | -------------------------------------------------------------------------------- /internal/typeutil/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 typeutil is a collection of type handling utility functions. 16 | package typeutil 17 | 18 | import ( 19 | "encoding/json" 20 | 21 | "github.com/google/jsonschema-go/jsonschema" 22 | ) 23 | 24 | // ConvertToWithJSONSchema converts the given value to another type using json marshal/unmarshal. 25 | // If non-nil resolvedSchema is provided, validation against the resolvedSchema will run 26 | // during the conversion. 27 | func ConvertToWithJSONSchema[From, To any](v From, resolvedSchema *jsonschema.Resolved) (To, error) { 28 | var zero To 29 | rawArgs, err := json.Marshal(v) 30 | if err != nil { 31 | return zero, err 32 | } 33 | if resolvedSchema != nil { 34 | // See https://github.com/google/jsonschema-go/issues/23: in order to 35 | // validate, we must validate against a map[string]any. Struct validation 36 | // does not work as it cannot account for `omitempty` or custom marshalling. 37 | var m map[string]any 38 | if err := json.Unmarshal(rawArgs, &m); err != nil { 39 | return zero, err 40 | } 41 | if err := resolvedSchema.Validate(m); err != nil { 42 | return zero, err 43 | } 44 | } 45 | var typed To 46 | if err := json.Unmarshal(rawArgs, &typed); err != nil { 47 | return zero, err 48 | } 49 | return typed, nil 50 | } 51 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/routers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers defines the HTTP routes for the ADK REST API. 16 | package routers 17 | 18 | import ( 19 | "net/http" 20 | 21 | "github.com/gorilla/mux" 22 | ) 23 | 24 | // A Route defines the parameters for an api endpoint 25 | type Route struct { 26 | Name string 27 | Methods []string 28 | Pattern string 29 | HandlerFunc http.HandlerFunc 30 | } 31 | 32 | // Routes is a list of defined api endpoints 33 | type Routes []Route 34 | 35 | // Router defines the required methods for retrieving api routes 36 | type Router interface { 37 | Routes() Routes 38 | } 39 | 40 | // NewRouter creates a new router for any number of api routers 41 | func NewRouter(routers ...Router) *mux.Router { 42 | router := mux.NewRouter().StrictSlash(true) 43 | SetupSubRouters(router) 44 | return router 45 | } 46 | 47 | // SetupSubRouters adds routes from subrouter to the naub router 48 | func SetupSubRouters(router *mux.Router, subrouters ...Router) { 49 | for _, api := range subrouters { 50 | for _, route := range api.Routes() { 51 | var handler http.Handler = route.HandlerFunc 52 | 53 | router. 54 | Methods(route.Methods...). 55 | Path(route.Pattern). 56 | Name(route.Name). 57 | Handler(handler) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /agent/workflowagents/sequentialagent/agent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 sequentialagent provides an agent that runs its sub-agents in a sequence. 16 | package sequentialagent 17 | 18 | import ( 19 | "fmt" 20 | 21 | "google.golang.org/adk/agent" 22 | "google.golang.org/adk/agent/workflowagents/loopagent" 23 | agentinternal "google.golang.org/adk/internal/agent" 24 | ) 25 | 26 | // New creates a SequentialAgent. 27 | // 28 | // SequentialAgent executes its sub-agents once, in the order they are listed. 29 | // 30 | // Use the SequentialAgent when you want the execution to occur in a fixed, 31 | // strict order. 32 | func New(cfg Config) (agent.Agent, error) { 33 | sequentialAgent, err := loopagent.New(loopagent.Config{ 34 | AgentConfig: cfg.AgentConfig, 35 | MaxIterations: 1, 36 | }) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | internalAgent, ok := sequentialAgent.(agentinternal.Agent) 42 | if !ok { 43 | return nil, fmt.Errorf("internal error: failed to convert to internal agent") 44 | } 45 | state := agentinternal.Reveal(internalAgent) 46 | state.AgentType = agentinternal.TypeSequentialAgent 47 | state.Config = cfg 48 | 49 | return sequentialAgent, nil 50 | } 51 | 52 | // Config defines the configuration for a SequentialAgent. 53 | type Config struct { 54 | // Basic agent setup. 55 | AgentConfig agent.Config 56 | } 57 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // DebugAPIRouter defines the routes for the Debug API. 24 | type DebugAPIRouter struct { 25 | runtimeController *controllers.DebugAPIController 26 | } 27 | 28 | // NewDebugAPIRouter creates a new DebugAPIRouter. 29 | func NewDebugAPIRouter(controller *controllers.DebugAPIController) *DebugAPIRouter { 30 | return &DebugAPIRouter{runtimeController: controller} 31 | } 32 | 33 | // Routes returns the routes for the Debug API. 34 | func (r *DebugAPIRouter) Routes() Routes { 35 | return Routes{ 36 | Route{ 37 | Name: "GetTraceDict", 38 | Methods: []string{http.MethodGet}, 39 | Pattern: "/debug/trace/{event_id}", 40 | HandlerFunc: r.runtimeController.TraceDictHandler, 41 | }, 42 | Route{ 43 | Name: "GetEventGraph", 44 | Methods: []string{http.MethodGet}, 45 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}/events/{event_id}/graph", 46 | HandlerFunc: r.runtimeController.EventGraphHandler, 47 | }, 48 | Route{ 49 | Name: "GetSessionTrace", 50 | Methods: []string{http.MethodGet}, 51 | Pattern: "/debug/trace/session/{session_id}", 52 | HandlerFunc: controllers.Unimplemented, 53 | }, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cmd/launcher/web/webui/distr/assets/audio-processor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class AudioProcessor extends AudioWorkletProcessor { 18 | constructor() { 19 | super(); 20 | this.targetSampleRate = 22000; // Change to your desired rate 21 | this.originalSampleRate = sampleRate; // Browser's sample rate 22 | this.resampleRatio = this.originalSampleRate / this.targetSampleRate; 23 | } 24 | 25 | process(inputs, outputs, parameters) { 26 | const input = inputs[0]; 27 | if (input.length > 0) { 28 | let audioData = input[0]; // Get first channel's data 29 | 30 | if (this.resampleRatio !== 1) { 31 | audioData = this.resample(audioData); 32 | } 33 | 34 | this.port.postMessage(audioData); 35 | } 36 | return true; // Keep processor alive 37 | } 38 | 39 | resample(audioData) { 40 | const newLength = Math.round(audioData.length / this.resampleRatio); 41 | const resampled = new Float32Array(newLength); 42 | 43 | for (let i = 0; i < newLength; i++) { 44 | const srcIndex = Math.floor(i * this.resampleRatio); 45 | resampled[i] = audioData[srcIndex]; // Nearest neighbor resampling 46 | } 47 | return resampled; 48 | } 49 | } 50 | 51 | registerProcessor('audio-processor', AudioProcessor); 52 | -------------------------------------------------------------------------------- /internal/llminternal/other_processors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 llminternal 16 | 17 | import ( 18 | "google.golang.org/adk/agent" 19 | "google.golang.org/adk/model" 20 | ) 21 | 22 | func identityRequestProcessor(ctx agent.InvocationContext, req *model.LLMRequest) error { 23 | // TODO: implement (adk-python src/google/adk/flows/llm_flows/identity.py) 24 | return nil 25 | } 26 | 27 | func nlPlanningRequestProcessor(ctx agent.InvocationContext, req *model.LLMRequest) error { 28 | // TODO: implement (adk-python src/google/adk/flows/llm_flows/_nl_plnning.py) 29 | return nil 30 | } 31 | 32 | func codeExecutionRequestProcessor(ctx agent.InvocationContext, req *model.LLMRequest) error { 33 | // TODO: implement (adk-python src/google/adk/flows/llm_flows/_code_execution.py) 34 | return nil 35 | } 36 | 37 | func authPreprocessor(ctx agent.InvocationContext, req *model.LLMRequest) error { 38 | // TODO: implement (adk-python src/google/adk/auth/auth_preprocessor.py) 39 | return nil 40 | } 41 | 42 | func nlPlanningResponseProcessor(ctx agent.InvocationContext, req *model.LLMRequest, resp *model.LLMResponse) error { 43 | // TODO: implement (adk-python src/google/adk/_nl_planning.py) 44 | return nil 45 | } 46 | 47 | func codeExecutionResponseProcessor(ctx agent.InvocationContext, req *model.LLMRequest, resp *model.LLMResponse) error { 48 | // TODO: implement (adk-python src/google/adk_code_execution.py) 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /util/instructionutil/instruction.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 instructionutil provides utilities to work with agent instructions. 16 | package instructionutil 17 | 18 | import ( 19 | "fmt" 20 | 21 | "google.golang.org/adk/agent" 22 | icontext "google.golang.org/adk/internal/context" 23 | "google.golang.org/adk/internal/llminternal" 24 | ) 25 | 26 | // InjectSessionState populates values in the instruction template, e.g. state, 27 | // artifact, etc. 28 | // - There can be placeholders like {key_name} that will be resolved by ADK 29 | // at runtime using session state and context. 30 | // - key_name must match "^[a-zA-Z_][a-zA-Z0-9_]*$", otherwise it will be 31 | // treated as a literal. 32 | // - {artifact.key_name} can be used to insert the text content of the 33 | // artifact named key_name. 34 | // 35 | // If the state variable or artifact does not exist, the agent will raise an 36 | // error. If you want to ignore the error, you can append a ? to the 37 | // variable name as in {var?}. 38 | // 39 | // This method is intended to be used in InstructionProvider based Instruction 40 | // and GlobalInstruction which are called with ReadonlyContext. 41 | func InjectSessionState(ctx agent.ReadonlyContext, template string) (string, error) { 42 | ictx, ok := ctx.(*icontext.ReadonlyContext) 43 | if !ok { 44 | return "", fmt.Errorf("unexpected context type: %T", ctx) 45 | } 46 | return llminternal.InjectSessionState(ictx.InvocationContext, template) 47 | } 48 | -------------------------------------------------------------------------------- /server/adkrest/controllers/handlers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 controllers contains the controllers for the ADK REST API. 16 | package controllers 17 | 18 | import ( 19 | "encoding/json" 20 | "net/http" 21 | ) 22 | 23 | // TODO: Move to an internal package, controllers doesn't have to be public API. 24 | 25 | // EncodeJSONResponse uses the json encoder to write an interface to the http response with an optional status code 26 | func EncodeJSONResponse(i any, status int, w http.ResponseWriter) { 27 | wHeader := w.Header() 28 | wHeader.Set("Content-Type", "application/json; charset=UTF-8") 29 | 30 | w.WriteHeader(status) 31 | 32 | if i != nil { 33 | err := json.NewEncoder(w).Encode(i) 34 | if err != nil { 35 | http.Error(w, err.Error(), http.StatusInternalServerError) 36 | } 37 | } 38 | } 39 | 40 | type errorHandler func(http.ResponseWriter, *http.Request) error 41 | 42 | // NewErrorHandler writes the error code returned from the http handler. 43 | func NewErrorHandler(fn errorHandler) http.HandlerFunc { 44 | return func(w http.ResponseWriter, r *http.Request) { 45 | err := fn(w, r) 46 | if err != nil { 47 | if statusErr, ok := err.(statusError); ok { 48 | http.Error(w, statusErr.Error(), statusErr.Status()) 49 | } else { 50 | http.Error(w, err.Error(), http.StatusInternalServerError) 51 | } 52 | } 53 | } 54 | } 55 | 56 | // Unimplemented returns 501 - Status Not Implemented error 57 | func Unimplemented(rw http.ResponseWriter, req *http.Request) { 58 | rw.WriteHeader(http.StatusNotImplemented) 59 | } 60 | -------------------------------------------------------------------------------- /examples/quickstart/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 provides a quickstart ADK agent. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "log" 21 | "os" 22 | 23 | "google.golang.org/genai" 24 | 25 | "google.golang.org/adk/agent" 26 | "google.golang.org/adk/agent/llmagent" 27 | "google.golang.org/adk/cmd/launcher" 28 | "google.golang.org/adk/cmd/launcher/full" 29 | "google.golang.org/adk/model/gemini" 30 | "google.golang.org/adk/tool" 31 | "google.golang.org/adk/tool/geminitool" 32 | ) 33 | 34 | func main() { 35 | ctx := context.Background() 36 | 37 | model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{ 38 | APIKey: os.Getenv("GOOGLE_API_KEY"), 39 | }) 40 | if err != nil { 41 | log.Fatalf("Failed to create model: %v", err) 42 | } 43 | 44 | a, err := llmagent.New(llmagent.Config{ 45 | Name: "weather_time_agent", 46 | Model: model, 47 | Description: "Agent to answer questions about the time and weather in a city.", 48 | Instruction: "Your SOLE purpose is to answer questions about the current time and weather in a specific city. You MUST refuse to answer any questions unrelated to time or weather.", 49 | Tools: []tool.Tool{ 50 | geminitool.GoogleSearch{}, 51 | }, 52 | }) 53 | if err != nil { 54 | log.Fatalf("Failed to create agent: %v", err) 55 | } 56 | 57 | config := &launcher.Config{ 58 | AgentLoader: agent.NewSingleLoader(a), 59 | } 60 | 61 | l := full.NewLauncher() 62 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 63 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /memory/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 memory defines the entities to interact with agent memory (long-term knowledge). 16 | package memory 17 | 18 | import ( 19 | "context" 20 | "time" 21 | 22 | "google.golang.org/genai" 23 | 24 | "google.golang.org/adk/session" 25 | ) 26 | 27 | // Service is a definition of the memory service. 28 | // 29 | // The service ingests sessions into memory so that it can be used for 30 | // user queries across user-scoped sessions. 31 | type Service interface { 32 | // AddSession adds a session to the memory service. 33 | // 34 | // A session can be added multiple times during its lifetime. 35 | AddSession(ctx context.Context, s session.Session) error 36 | // Search returns memory entries relevant to the given query. 37 | // Empty slice is returned if there are no matches. 38 | Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error) 39 | } 40 | 41 | // SearchRequest represents a request for memory search. 42 | type SearchRequest struct { 43 | Query string 44 | UserID string 45 | AppName string 46 | } 47 | 48 | // SearchResponse represents the response from a memory search. 49 | type SearchResponse struct { 50 | Memories []Entry 51 | } 52 | 53 | // Entry represents a single memory entry. 54 | type Entry struct { 55 | // Content contains the main content of the memory. 56 | Content *genai.Content 57 | // Author of the memory. 58 | Author string 59 | // Timestamp shows when the original content of this memory happened. 60 | // This string will be forwarded to LLM. Preferred format is ISO 8601 format. 61 | Timestamp time.Time 62 | } 63 | -------------------------------------------------------------------------------- /internal/artifact/artifacts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 artifact 16 | 17 | import ( 18 | "context" 19 | 20 | "google.golang.org/genai" 21 | 22 | "google.golang.org/adk/agent" 23 | "google.golang.org/adk/artifact" 24 | ) 25 | 26 | // Artifacts implements Artifacts 27 | type Artifacts struct { 28 | Service artifact.Service 29 | AppName string 30 | UserID string 31 | SessionID string 32 | } 33 | 34 | func (a *Artifacts) Save(ctx context.Context, name string, data *genai.Part) (*artifact.SaveResponse, error) { 35 | return a.Service.Save(ctx, &artifact.SaveRequest{ 36 | AppName: a.AppName, 37 | UserID: a.UserID, 38 | SessionID: a.SessionID, 39 | FileName: name, 40 | Part: data, 41 | }) 42 | } 43 | 44 | func (a *Artifacts) Load(ctx context.Context, name string) (*artifact.LoadResponse, error) { 45 | return a.Service.Load(ctx, &artifact.LoadRequest{ 46 | AppName: a.AppName, 47 | UserID: a.UserID, 48 | SessionID: a.SessionID, 49 | FileName: name, 50 | }) 51 | } 52 | 53 | func (a *Artifacts) LoadVersion(ctx context.Context, name string, version int) (*artifact.LoadResponse, error) { 54 | return a.Service.Load(ctx, &artifact.LoadRequest{ 55 | AppName: a.AppName, 56 | UserID: a.UserID, 57 | SessionID: a.SessionID, 58 | FileName: name, 59 | Version: int64(version), 60 | }) 61 | } 62 | 63 | func (a *Artifacts) List(ctx context.Context) (*artifact.ListResponse, error) { 64 | return a.Service.List(ctx, &artifact.ListRequest{ 65 | AppName: a.AppName, 66 | UserID: a.UserID, 67 | SessionID: a.SessionID, 68 | }) 69 | } 70 | 71 | var _ agent.Artifacts = (*Artifacts)(nil) 72 | -------------------------------------------------------------------------------- /internal/testutil/genai.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 testutil 16 | 17 | import ( 18 | "bytes" 19 | "encoding/json" 20 | "fmt" 21 | "net/http" 22 | "strings" 23 | 24 | "google.golang.org/adk/internal/httprr" 25 | ) 26 | 27 | // NewGeminiTransport returns the genai.ClientConfig configured for record and replay. 28 | func NewGeminiTransport(rrfile string) (http.RoundTripper, error) { 29 | rr, err := httprr.Open(rrfile, http.DefaultTransport) 30 | if err != nil { 31 | return nil, fmt.Errorf("httprr.Open(%q) failed: %w", rrfile, err) 32 | } 33 | rr.ScrubReq(scrubGeminiRequest) 34 | return rr, nil 35 | } 36 | 37 | func scrubGeminiRequest(req *http.Request) error { 38 | delete(req.Header, "x-goog-api-key") // genai does not canonicalize 39 | req.Header.Del("X-Goog-Api-Key") // in case it starts 40 | delete(req.Header, "x-goog-api-client") // contains version numbers 41 | req.Header.Del("X-Goog-Api-Client") 42 | delete(req.Header, "user-agent") // contains google-genai-sdk and gl-go version numbers 43 | req.Header.Del("User-Agent") 44 | 45 | if ctype := req.Header.Get("Content-Type"); ctype == "application/json" || strings.HasPrefix(ctype, "application/json;") { 46 | // Canonicalize JSON body. 47 | // google.golang.org/protobuf/internal/encoding.json 48 | // goes out of its way to randomize the JSON encodings 49 | // of protobuf messages by adding or not adding spaces 50 | // after commas. Derandomize by compacting the JSON. 51 | b := req.Body.(*httprr.Body) 52 | var buf bytes.Buffer 53 | if err := json.Compact(&buf, b.Data); err == nil { 54 | b.Data = buf.Bytes() 55 | } 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /internal/context/readonly_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 context 16 | 17 | import ( 18 | "context" 19 | 20 | "google.golang.org/genai" 21 | 22 | "google.golang.org/adk/agent" 23 | "google.golang.org/adk/session" 24 | ) 25 | 26 | func NewReadonlyContext(ctx agent.InvocationContext) agent.ReadonlyContext { 27 | return &ReadonlyContext{ 28 | Context: ctx, 29 | InvocationContext: ctx, 30 | } 31 | } 32 | 33 | type ReadonlyContext struct { 34 | context.Context 35 | InvocationContext agent.InvocationContext 36 | } 37 | 38 | // AppName implements agent.ReadonlyContext. 39 | func (c *ReadonlyContext) AppName() string { 40 | return c.InvocationContext.Session().AppName() 41 | } 42 | 43 | // Branch implements agent.ReadonlyContext. 44 | func (c *ReadonlyContext) Branch() string { 45 | return c.InvocationContext.Branch() 46 | } 47 | 48 | // SessionID implements agent.ReadonlyContext. 49 | func (c *ReadonlyContext) SessionID() string { 50 | return c.InvocationContext.Session().ID() 51 | } 52 | 53 | // UserID implements agent.ReadonlyContext. 54 | func (c *ReadonlyContext) UserID() string { 55 | return c.InvocationContext.Session().UserID() 56 | } 57 | 58 | func (c *ReadonlyContext) AgentName() string { 59 | return c.InvocationContext.Agent().Name() 60 | } 61 | 62 | func (c *ReadonlyContext) ReadonlyState() session.ReadonlyState { 63 | return c.InvocationContext.Session().State() 64 | } 65 | 66 | func (c *ReadonlyContext) InvocationID() string { 67 | return c.InvocationContext.InvocationID() 68 | } 69 | 70 | func (c *ReadonlyContext) UserContent() *genai.Content { 71 | return c.InvocationContext.UserContent() 72 | } 73 | -------------------------------------------------------------------------------- /model/llm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 model defines the interfaces and data structures for interacting with LLMs. 16 | package model 17 | 18 | import ( 19 | "context" 20 | "iter" 21 | 22 | "google.golang.org/genai" 23 | ) 24 | 25 | // LLM provides the access to the underlying LLM. 26 | type LLM interface { 27 | Name() string 28 | GenerateContent(ctx context.Context, req *LLMRequest, stream bool) iter.Seq2[*LLMResponse, error] 29 | } 30 | 31 | // LLMRequest is the raw LLM request. 32 | type LLMRequest struct { 33 | Model string 34 | Contents []*genai.Content 35 | Config *genai.GenerateContentConfig 36 | 37 | Tools map[string]any `json:"-"` 38 | } 39 | 40 | // LLMResponse is the raw LLM response. 41 | // It provides the first candidate response from the model if available. 42 | type LLMResponse struct { 43 | Content *genai.Content 44 | CitationMetadata *genai.CitationMetadata 45 | GroundingMetadata *genai.GroundingMetadata 46 | UsageMetadata *genai.GenerateContentResponseUsageMetadata 47 | CustomMetadata map[string]any 48 | LogprobsResult *genai.LogprobsResult 49 | // Partial indicates whether the content is part of a unfinished content stream. 50 | // Only used for streaming mode and when the content is plain text. 51 | Partial bool 52 | // Indicates whether the response from the model is complete. 53 | // Only used for streaming mode. 54 | TurnComplete bool 55 | // Flag indicating that LLM was interrupted when generating the content. 56 | // Usually it is due to user interruption during a bidi streaming. 57 | Interrupted bool 58 | ErrorCode string 59 | ErrorMessage string 60 | FinishReason genai.FinishReason 61 | AvgLogprobs float64 62 | } 63 | -------------------------------------------------------------------------------- /internal/toolinternal/toolutils/toolutils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 tool defines internal-only interfaces and logic for tools. 16 | package toolutils 17 | 18 | import ( 19 | "fmt" 20 | 21 | "google.golang.org/genai" 22 | 23 | "google.golang.org/adk/model" 24 | ) 25 | 26 | type Tool interface { 27 | Name() string 28 | Declaration() *genai.FunctionDeclaration 29 | } 30 | 31 | // The PackTool ensures that in case there is a usage of multiple function tools, 32 | // all of them are consolidated into one genai tool that has all the function declarations 33 | // provided by the tools. So, if there is already a tool with a function declaration, 34 | // it appends another to it; otherwise, it creates a new genai tool. 35 | func PackTool(req *model.LLMRequest, tool Tool) error { 36 | if req.Tools == nil { 37 | req.Tools = make(map[string]any) 38 | } 39 | 40 | name := tool.Name() 41 | 42 | if _, ok := req.Tools[name]; ok { 43 | return fmt.Errorf("duplicate tool: %q", name) 44 | } 45 | req.Tools[name] = tool 46 | 47 | if req.Config == nil { 48 | req.Config = &genai.GenerateContentConfig{} 49 | } 50 | if decl := tool.Declaration(); decl == nil { 51 | return nil 52 | } 53 | // Find an existing genai.Tool with FunctionDeclarations 54 | var funcTool *genai.Tool 55 | for _, tool := range req.Config.Tools { 56 | if tool != nil && tool.FunctionDeclarations != nil { 57 | funcTool = tool 58 | break 59 | } 60 | } 61 | if funcTool == nil { 62 | req.Config.Tools = append(req.Config.Tools, &genai.Tool{ 63 | FunctionDeclarations: []*genai.FunctionDeclaration{tool.Declaration()}, 64 | }) 65 | } else { 66 | funcTool.FunctionDeclarations = append(funcTool.FunctionDeclarations, tool.Declaration()) 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/llminternal/converters/converters.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 converters 16 | 17 | import ( 18 | "google.golang.org/genai" 19 | 20 | "google.golang.org/adk/model" 21 | ) 22 | 23 | func Genai2LLMResponse(res *genai.GenerateContentResponse) *model.LLMResponse { 24 | usageMetadata := res.UsageMetadata 25 | if len(res.Candidates) > 0 && res.Candidates[0] != nil { 26 | candidate := res.Candidates[0] 27 | if candidate.Content != nil && len(candidate.Content.Parts) > 0 { 28 | return &model.LLMResponse{ 29 | Content: candidate.Content, 30 | GroundingMetadata: candidate.GroundingMetadata, 31 | FinishReason: candidate.FinishReason, 32 | CitationMetadata: candidate.CitationMetadata, 33 | AvgLogprobs: candidate.AvgLogprobs, 34 | LogprobsResult: candidate.LogprobsResult, 35 | UsageMetadata: usageMetadata, 36 | } 37 | } 38 | return &model.LLMResponse{ 39 | ErrorCode: string(candidate.FinishReason), 40 | ErrorMessage: candidate.FinishMessage, 41 | GroundingMetadata: candidate.GroundingMetadata, 42 | FinishReason: candidate.FinishReason, 43 | CitationMetadata: candidate.CitationMetadata, 44 | AvgLogprobs: candidate.AvgLogprobs, 45 | LogprobsResult: candidate.LogprobsResult, 46 | UsageMetadata: usageMetadata, 47 | } 48 | 49 | } 50 | if res.PromptFeedback != nil { 51 | return &model.LLMResponse{ 52 | ErrorCode: string(res.PromptFeedback.BlockReason), 53 | ErrorMessage: res.PromptFeedback.BlockReasonMessage, 54 | UsageMetadata: usageMetadata, 55 | } 56 | } 57 | return &model.LLMResponse{ 58 | ErrorCode: "UNKNOWN_ERROR", 59 | ErrorMessage: "Unknown error.", 60 | UsageMetadata: usageMetadata, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /server/adkrest/handler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 adkrest 16 | 17 | import ( 18 | "net/http" 19 | "time" 20 | 21 | "github.com/gorilla/mux" 22 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 23 | 24 | "google.golang.org/adk/cmd/launcher" 25 | "google.golang.org/adk/internal/telemetry" 26 | "google.golang.org/adk/server/adkrest/controllers" 27 | "google.golang.org/adk/server/adkrest/internal/routers" 28 | "google.golang.org/adk/server/adkrest/internal/services" 29 | ) 30 | 31 | // NewHandler creates and returns an http.Handler for the ADK REST API. 32 | func NewHandler(config *launcher.Config, sseWriteTimeout time.Duration) http.Handler { 33 | adkExporter := services.NewAPIServerSpanExporter() 34 | telemetry.AddSpanProcessor(sdktrace.NewSimpleSpanProcessor(adkExporter)) 35 | 36 | router := mux.NewRouter().StrictSlash(true) 37 | // TODO: Allow taking a prefix to allow customizing the path 38 | // where the ADK REST API will be served. 39 | setupRouter(router, 40 | routers.NewSessionsAPIRouter(controllers.NewSessionsAPIController(config.SessionService)), 41 | routers.NewRuntimeAPIRouter(controllers.NewRuntimeAPIController(config.SessionService, config.AgentLoader, config.ArtifactService, sseWriteTimeout)), 42 | routers.NewAppsAPIRouter(controllers.NewAppsAPIController(config.AgentLoader)), 43 | routers.NewDebugAPIRouter(controllers.NewDebugAPIController(config.SessionService, config.AgentLoader, adkExporter)), 44 | routers.NewArtifactsAPIRouter(controllers.NewArtifactsAPIController(config.ArtifactService)), 45 | &routers.EvalAPIRouter{}, 46 | ) 47 | return router 48 | } 49 | 50 | func setupRouter(router *mux.Router, subrouters ...routers.Router) *mux.Router { 51 | routers.SetupSubRouters(router, subrouters...) 52 | return router 53 | } 54 | -------------------------------------------------------------------------------- /cmd/launcher/web/webui/distr/assets/ADK-512-color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/artifacts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // ArtifactsAPIRouter defines the routes for the Artifacts API. 24 | type ArtifactsAPIRouter struct { 25 | artifactsController *controllers.ArtifactsAPIController 26 | } 27 | 28 | // NewArtifactsAPIRouter creates a new ArtifactsAPIRouter. 29 | func NewArtifactsAPIRouter(controller *controllers.ArtifactsAPIController) *ArtifactsAPIRouter { 30 | return &ArtifactsAPIRouter{artifactsController: controller} 31 | } 32 | 33 | // Routes returns the routes for the Artifacts API. 34 | func (r *ArtifactsAPIRouter) Routes() Routes { 35 | return Routes{ 36 | Route{ 37 | Name: "ListArtifacts", 38 | Methods: []string{http.MethodGet}, 39 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts", 40 | HandlerFunc: r.artifactsController.ListArtifactsHandler, 41 | }, 42 | Route{ 43 | Name: "LoadArtifact", 44 | Methods: []string{http.MethodGet}, 45 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}", 46 | HandlerFunc: r.artifactsController.LoadArtifactHandler, 47 | }, 48 | Route{ 49 | Name: "LoadArtifact", 50 | Methods: []string{http.MethodGet}, 51 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}/versions/{version}", 52 | HandlerFunc: r.artifactsController.LoadArtifactVersionHandler, 53 | }, 54 | Route{ 55 | Name: "DeleteArtifact", 56 | Methods: []string{http.MethodDelete, http.MethodOptions}, 57 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}", 58 | HandlerFunc: r.artifactsController.DeleteArtifactHandler, 59 | }, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/workflowagents/loop/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 demonstrates a workflow agent that runs a loop agent. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "iter" 21 | "log" 22 | "os" 23 | 24 | "google.golang.org/genai" 25 | 26 | "google.golang.org/adk/agent" 27 | "google.golang.org/adk/agent/workflowagents/loopagent" 28 | "google.golang.org/adk/cmd/launcher" 29 | "google.golang.org/adk/cmd/launcher/full" 30 | "google.golang.org/adk/model" 31 | "google.golang.org/adk/session" 32 | ) 33 | 34 | func CustomAgentRun(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] { 35 | return func(yield func(*session.Event, error) bool) { 36 | yield(&session.Event{ 37 | LLMResponse: model.LLMResponse{ 38 | Content: &genai.Content{ 39 | Parts: []*genai.Part{ 40 | { 41 | Text: "Hello from MyAgent!\n", 42 | }, 43 | }, 44 | }, 45 | }, 46 | }, nil) 47 | } 48 | } 49 | 50 | func main() { 51 | ctx := context.Background() 52 | 53 | customAgent, err := agent.New(agent.Config{ 54 | Name: "my_custom_agent", 55 | Description: "A custom agent that responds with a greeting.", 56 | Run: CustomAgentRun, 57 | }) 58 | if err != nil { 59 | log.Fatalf("Failed to create agent: %v", err) 60 | } 61 | 62 | loopAgent, err := loopagent.New(loopagent.Config{ 63 | MaxIterations: 3, 64 | AgentConfig: agent.Config{ 65 | Name: "loop_agent", 66 | Description: "A loop agent that runs sub-agents", 67 | SubAgents: []agent.Agent{customAgent}, 68 | }, 69 | }) 70 | if err != nil { 71 | log.Fatalf("Failed to create agent: %v", err) 72 | } 73 | 74 | config := &launcher.Config{ 75 | AgentLoader: agent.NewSingleLoader(loopAgent), 76 | } 77 | 78 | l := full.NewLauncher() 79 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 80 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tool/geminitool/tool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 geminitool provides access to Gemini native tools. You can use any 16 | // tool from genai with geminitool.New(). 17 | // 18 | // For example, to create a Gemini retrieval tool: 19 | // 20 | // geminitool.New("data_retrieval", &genai.Tool{ 21 | // Retrieval: &genai.Retrieval{ 22 | // ExternalAPI: &genai.ExternalAPI{ 23 | // Endpoint: , 24 | // AuthConfig: 25 | // }, 26 | // }, 27 | // }) 28 | // 29 | // Package also provides default tools like GoogleSearch. 30 | package geminitool 31 | 32 | import ( 33 | "fmt" 34 | 35 | "google.golang.org/genai" 36 | 37 | "google.golang.org/adk/model" 38 | "google.golang.org/adk/tool" 39 | ) 40 | 41 | // New creates gemini API tool. 42 | func New(name string, t *genai.Tool) tool.Tool { 43 | return &geminiTool{ 44 | name: name, 45 | value: t, 46 | } 47 | } 48 | 49 | // geminiTool is a wrapper around a genai.Tool. 50 | type geminiTool struct { 51 | name string 52 | value *genai.Tool 53 | } 54 | 55 | // ProcessRequest adds the Gemini tool to the LLM request. 56 | func (t *geminiTool) ProcessRequest(ctx tool.Context, req *model.LLMRequest) error { 57 | return setTool(req, t.value) 58 | } 59 | 60 | // Name implements tool.Tool. 61 | func (t *geminiTool) Name() string { 62 | return t.name 63 | } 64 | 65 | // Description implements tool.Tool. 66 | func (t *geminiTool) Description() string { 67 | return "Performs a Google search to retrieve information from the web." 68 | } 69 | 70 | // IsLongRunning implements tool.Tool. 71 | func (t *geminiTool) IsLongRunning() bool { 72 | return false 73 | } 74 | 75 | func setTool(req *model.LLMRequest, t *genai.Tool) error { 76 | if req == nil { 77 | return fmt.Errorf("llm request is nil") 78 | } 79 | 80 | if req.Config == nil { 81 | req.Config = &genai.GenerateContentConfig{} 82 | } 83 | 84 | req.Config.Tools = append(req.Config.Tools, t) 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /internal/context/invocation_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 context 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/google/uuid" 21 | "google.golang.org/genai" 22 | 23 | "google.golang.org/adk/agent" 24 | "google.golang.org/adk/session" 25 | ) 26 | 27 | type InvocationContextParams struct { 28 | Artifacts agent.Artifacts 29 | Memory agent.Memory 30 | Session session.Session 31 | 32 | Branch string 33 | Agent agent.Agent 34 | 35 | UserContent *genai.Content 36 | RunConfig *agent.RunConfig 37 | EndInvocation bool 38 | } 39 | 40 | func NewInvocationContext(ctx context.Context, params InvocationContextParams) agent.InvocationContext { 41 | return &InvocationContext{ 42 | Context: ctx, 43 | params: params, 44 | invocationID: "e-" + uuid.NewString(), 45 | } 46 | } 47 | 48 | type InvocationContext struct { 49 | context.Context 50 | 51 | params InvocationContextParams 52 | invocationID string 53 | } 54 | 55 | func (c *InvocationContext) Artifacts() agent.Artifacts { 56 | return c.params.Artifacts 57 | } 58 | 59 | func (c *InvocationContext) Agent() agent.Agent { 60 | return c.params.Agent 61 | } 62 | 63 | func (c *InvocationContext) Branch() string { 64 | return c.params.Branch 65 | } 66 | 67 | func (c *InvocationContext) InvocationID() string { 68 | return c.invocationID 69 | } 70 | 71 | func (c *InvocationContext) Memory() agent.Memory { 72 | return c.params.Memory 73 | } 74 | 75 | func (c *InvocationContext) Session() session.Session { 76 | return c.params.Session 77 | } 78 | 79 | func (c *InvocationContext) UserContent() *genai.Content { 80 | return c.params.UserContent 81 | } 82 | 83 | func (c *InvocationContext) RunConfig() *agent.RunConfig { 84 | return c.params.RunConfig 85 | } 86 | 87 | func (c *InvocationContext) EndInvocation() { 88 | c.params.EndInvocation = true 89 | } 90 | 91 | func (c *InvocationContext) Ended() bool { 92 | return c.params.EndInvocation 93 | } 94 | -------------------------------------------------------------------------------- /server/adkrest/internal/routers/sessions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 routers 16 | 17 | import ( 18 | "net/http" 19 | 20 | "google.golang.org/adk/server/adkrest/controllers" 21 | ) 22 | 23 | // SessionsAPIRouter defines the routes for the Sessions API. 24 | type SessionsAPIRouter struct { 25 | sessionController *controllers.SessionsAPIController 26 | } 27 | 28 | // NewSessionsAPIRouter creates a new SessionsAPIRouter. 29 | func NewSessionsAPIRouter(controller *controllers.SessionsAPIController) *SessionsAPIRouter { 30 | return &SessionsAPIRouter{sessionController: controller} 31 | } 32 | 33 | // Routes returns the routes for the Sessions API. 34 | func (r *SessionsAPIRouter) Routes() Routes { 35 | return Routes{ 36 | Route{ 37 | Name: "GetSession", 38 | Methods: []string{http.MethodGet}, 39 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}", 40 | HandlerFunc: r.sessionController.GetSessionHandler, 41 | }, 42 | Route{ 43 | Name: "CreateSession", 44 | Methods: []string{http.MethodPost}, 45 | Pattern: "/apps/{app_name}/users/{user_id}/sessions", 46 | HandlerFunc: r.sessionController.CreateSessionHandler, 47 | }, 48 | Route{ 49 | Name: "CreateSessionWithId", 50 | Methods: []string{http.MethodPost}, 51 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}", 52 | HandlerFunc: r.sessionController.CreateSessionHandler, 53 | }, 54 | Route{ 55 | Name: "DeleteSession", 56 | Methods: []string{http.MethodDelete, http.MethodOptions}, 57 | Pattern: "/apps/{app_name}/users/{user_id}/sessions/{session_id}", 58 | HandlerFunc: r.sessionController.DeleteSessionHandler, 59 | }, 60 | Route{ 61 | Name: "ListSessions", 62 | Methods: []string{http.MethodGet}, 63 | Pattern: "/apps/{app_name}/users/{user_id}/sessions", 64 | HandlerFunc: r.sessionController.ListSessionsHandler, 65 | }, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /internal/agent/parentmap/map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 parentmap 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "google.golang.org/adk/agent" 22 | ) 23 | 24 | type Map map[string]agent.Agent 25 | 26 | // New creates parent map allowing to fetch agent's parent. 27 | // It ensures that agent can have at most one parent. 28 | // It ensures that the root node name is not referenced again in the agent tree 29 | func New(root agent.Agent) (Map, error) { 30 | res := make(map[string]agent.Agent) 31 | rootName := root.Name() 32 | pointerMap := map[agent.Agent]string{root: "is root agent"} 33 | 34 | var f func(cur agent.Agent) error 35 | f = func(cur agent.Agent) error { 36 | for _, subAgent := range cur.SubAgents() { 37 | if p, ok := pointerMap[subAgent]; ok { 38 | return fmt.Errorf("%q agent cannot have >1 parents, found: %q, %q", subAgent.Name(), p, cur.Name()) 39 | } 40 | if _, ok := res[subAgent.Name()]; ok || subAgent.Name() == rootName { 41 | return fmt.Errorf("agent names must be unique in the agent tree, found duplicate: %q", subAgent.Name()) 42 | } 43 | res[subAgent.Name()] = cur 44 | pointerMap[subAgent] = cur.Name() 45 | 46 | if err := f(subAgent); err != nil { 47 | return err 48 | } 49 | } 50 | return nil 51 | } 52 | 53 | return res, f(root) 54 | } 55 | 56 | // RootAgent returns the root of the agent tree. 57 | func (m Map) RootAgent(cur agent.Agent) agent.Agent { 58 | if cur == nil { 59 | return nil 60 | } 61 | for { 62 | parent := m[cur.Name()] 63 | if parent == nil { 64 | return cur 65 | } 66 | cur = parent 67 | } 68 | } 69 | 70 | func ToContext(ctx context.Context, parents Map) context.Context { 71 | return context.WithValue(ctx, mapCtxKey, parents) 72 | } 73 | 74 | func FromContext(ctx context.Context) Map { 75 | m, ok := ctx.Value(mapCtxKey).(Map) 76 | if !ok { 77 | return nil 78 | } 79 | return m 80 | } 81 | 82 | type ctxKey int 83 | 84 | const mapCtxKey ctxKey = 0 85 | -------------------------------------------------------------------------------- /internal/sessioninternal/mutablesession.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 sessioninternal 16 | 17 | import ( 18 | "fmt" 19 | "iter" 20 | "time" 21 | 22 | "google.golang.org/adk/session" 23 | ) 24 | 25 | // MutableSession implements session.Session 26 | type MutableSession struct { 27 | service session.Service 28 | storedSession session.Session 29 | } 30 | 31 | // NewMutableSession creates and returns session.Session implementation. 32 | func NewMutableSession(service session.Service, storedSession session.Session) *MutableSession { 33 | return &MutableSession{ 34 | service: service, 35 | storedSession: storedSession, 36 | } 37 | } 38 | 39 | func (s *MutableSession) State() session.State { 40 | return s 41 | } 42 | 43 | func (s *MutableSession) AppName() string { 44 | return s.storedSession.AppName() 45 | } 46 | 47 | func (s *MutableSession) UserID() string { 48 | return s.storedSession.UserID() 49 | } 50 | 51 | func (s *MutableSession) ID() string { 52 | return s.storedSession.ID() 53 | } 54 | 55 | func (s *MutableSession) Events() session.Events { 56 | return s.storedSession.Events() 57 | } 58 | 59 | func (s *MutableSession) LastUpdateTime() time.Time { 60 | return s.storedSession.LastUpdateTime() 61 | } 62 | 63 | func (s *MutableSession) Get(key string) (any, error) { 64 | value, err := s.storedSession.State().Get(key) 65 | if err != nil { 66 | return nil, fmt.Errorf("failed to get key %q from state: %w", key, err) 67 | } 68 | return value, nil 69 | } 70 | 71 | func (s *MutableSession) All() iter.Seq2[string, any] { 72 | return s.storedSession.State().All() 73 | } 74 | 75 | func (s *MutableSession) Set(key string, value any) error { 76 | mutableState, ok := s.storedSession.State().(MutableState) 77 | if !ok { 78 | return fmt.Errorf("this session state is not mutable") 79 | } 80 | if err := mutableState.Set(key, value); err != nil { 81 | return fmt.Errorf("failed to set key %q in state: %w", key, err) 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /cmd/launcher/launcher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 launcher provides ways to interact with agents. 16 | package launcher 17 | 18 | import ( 19 | "context" 20 | 21 | "github.com/a2aproject/a2a-go/a2asrv" 22 | 23 | "google.golang.org/adk/agent" 24 | "google.golang.org/adk/artifact" 25 | "google.golang.org/adk/memory" 26 | "google.golang.org/adk/session" 27 | ) 28 | 29 | // Launcher is the main interface for running an ADK application. 30 | // It is responsible for parsing command-line arguments and executing the 31 | // corresponding logic. 32 | type Launcher interface { 33 | // Execute parses command-line arguments and runs the launcher. 34 | Execute(ctx context.Context, config *Config, args []string) error 35 | // CommandLineSyntax returns a string describing the command-line flags and arguments. 36 | CommandLineSyntax() string 37 | } 38 | 39 | // SubLauncher is an interface for launchers that can be composed within a parent 40 | // launcher, like the universal launcher. Each SubLauncher corresponds to a 41 | // specific mode of operation (e.g., 'console' or 'web'). 42 | type SubLauncher interface { 43 | // Keyword returns the command-line keyword that activates this sub-launcher. 44 | Keyword() string 45 | // Parse parses the arguments for the sub-launcher. It should return any unparsed arguments. 46 | Parse(args []string) ([]string, error) 47 | // CommandLineSyntax returns a string describing the command-line flags and arguments for the sub-launcher. 48 | CommandLineSyntax() string 49 | // SimpleDescription provides a brief, one-line description of the sub-launcher's function. 50 | SimpleDescription() string 51 | // Run executes the sub-launcher's main logic. 52 | Run(ctx context.Context, config *Config) error 53 | } 54 | 55 | // Config contains parameters for web & console execution: sessions, artifacts, agents etc 56 | type Config struct { 57 | SessionService session.Service 58 | ArtifactService artifact.Service 59 | MemoryService memory.Service 60 | AgentLoader agent.Loader 61 | A2AOptions []a2asrv.RequestHandlerOption 62 | } 63 | -------------------------------------------------------------------------------- /internal/sessionutils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 sessionutils 16 | 17 | import ( 18 | "maps" 19 | "strings" 20 | ) 21 | 22 | const ( 23 | appPrefix = "app:" 24 | userPrefix = "user:" 25 | tempPrefix = "temp:" 26 | ) 27 | 28 | // ExtractStateDeltas splits a single state delta map into three separate maps 29 | // for app, user, and session states based on key prefixes. 30 | // Temporary keys (starting with TempStatePrefix) are ignored. 31 | func ExtractStateDeltas(delta map[string]any) ( 32 | appStateDelta, userStateDelta, sessionStateDelta map[string]any, 33 | ) { 34 | // Initialize the maps to be returned. 35 | appStateDelta = make(map[string]any) 36 | userStateDelta = make(map[string]any) 37 | sessionStateDelta = make(map[string]any) 38 | 39 | if delta == nil { 40 | return appStateDelta, userStateDelta, sessionStateDelta 41 | } 42 | 43 | for key, value := range delta { 44 | if cleanKey, found := strings.CutPrefix(key, appPrefix); found { 45 | appStateDelta[cleanKey] = value 46 | } else if cleanKey, found := strings.CutPrefix(key, userPrefix); found { 47 | userStateDelta[cleanKey] = value 48 | } else if !strings.HasPrefix(key, tempPrefix) { 49 | // This key belongs to the session state, as long as it's not temporary. 50 | sessionStateDelta[key] = value 51 | } 52 | } 53 | return appStateDelta, userStateDelta, sessionStateDelta 54 | } 55 | 56 | // MergeStates combines app, user, and session state maps into a single map 57 | // for client-side responses, adding the appropriate prefixes back. 58 | func MergeStates(appState, userState, sessionState map[string]any) map[string]any { 59 | // Pre-allocate map capacity for efficiency. 60 | totalSize := len(appState) + len(userState) + len(sessionState) 61 | mergedState := make(map[string]any, totalSize) 62 | 63 | maps.Copy(mergedState, sessionState) 64 | 65 | for key, value := range appState { 66 | mergedState[appPrefix+key] = value 67 | } 68 | 69 | for key, value := range userState { 70 | mergedState[userPrefix+key] = value 71 | } 72 | 73 | return mergedState 74 | } 75 | -------------------------------------------------------------------------------- /tool/geminitool/tool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 geminitool_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | "google.golang.org/genai" 22 | 23 | "google.golang.org/adk/internal/toolinternal" 24 | "google.golang.org/adk/model" 25 | "google.golang.org/adk/tool/geminitool" 26 | ) 27 | 28 | func TestGeminiTool_ProcessRequest(t *testing.T) { 29 | testCases := []struct { 30 | name string 31 | inputTool *genai.Tool 32 | req *model.LLMRequest 33 | wantTools []*genai.Tool 34 | wantErr bool 35 | }{ 36 | { 37 | name: "add to empty request", 38 | inputTool: &genai.Tool{ 39 | GoogleSearch: &genai.GoogleSearch{}, 40 | }, 41 | req: &model.LLMRequest{}, 42 | wantTools: []*genai.Tool{ 43 | {GoogleSearch: &genai.GoogleSearch{}}, 44 | }, 45 | }, 46 | { 47 | name: "add to existing tools", 48 | inputTool: &genai.Tool{ 49 | GoogleSearch: &genai.GoogleSearch{}, 50 | }, 51 | req: &model.LLMRequest{ 52 | Config: &genai.GenerateContentConfig{ 53 | Tools: []*genai.Tool{ 54 | { 55 | GoogleMaps: &genai.GoogleMaps{}, 56 | }, 57 | }, 58 | }, 59 | }, 60 | wantTools: []*genai.Tool{ 61 | {GoogleMaps: &genai.GoogleMaps{}}, 62 | {GoogleSearch: &genai.GoogleSearch{}}, 63 | }, 64 | }, 65 | { 66 | name: "error on nil request", 67 | wantErr: true, 68 | }, 69 | } 70 | 71 | for _, tt := range testCases { 72 | t.Run(tt.name, func(t *testing.T) { 73 | geminiTool := geminitool.New("test_tool", tt.inputTool) 74 | 75 | requestProcessor, ok := geminiTool.(toolinternal.RequestProcessor) 76 | if !ok { 77 | t.Fatal("geminiTool does not implement RequestProcessor") 78 | } 79 | 80 | err := requestProcessor.ProcessRequest(nil, tt.req) 81 | if (err != nil) != tt.wantErr { 82 | t.Fatalf("ProcessRequest() error = %v, wantErr %v", err, tt.wantErr) 83 | } 84 | if tt.wantErr { 85 | return 86 | } 87 | 88 | if diff := cmp.Diff(tt.wantTools, tt.req.Config.Tools); diff != "" { 89 | t.Errorf("ProcessRequest returned unexpected tools (-want +got):\n%s", diff) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /agent/loader_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent 16 | 17 | import ( 18 | "iter" 19 | "testing" 20 | 21 | "google.golang.org/adk/session" 22 | ) 23 | 24 | var _ Agent = (*testAgent)(nil) 25 | 26 | type testAgent struct { 27 | name string 28 | } 29 | 30 | func (a *testAgent) Name() string { 31 | return a.name 32 | } 33 | 34 | func (a *testAgent) Description() string { 35 | panic("not implemented") 36 | } 37 | 38 | func (a *testAgent) Run(InvocationContext) iter.Seq2[*session.Event, error] { 39 | panic("not implemented") 40 | } 41 | 42 | func (a *testAgent) SubAgents() []Agent { 43 | panic("not implemented") 44 | } 45 | 46 | func (a *testAgent) internal() *agent { 47 | panic("not implemented") 48 | } 49 | 50 | func TestDuplicateName(t *testing.T) { 51 | agent1 := &testAgent{name: "weather_time_agent"} 52 | // duplicate name 53 | agent2 := &testAgent{name: "weather_time_agent"} 54 | agent3 := &testAgent{name: "unique"} 55 | 56 | tests := []struct { 57 | name string 58 | root Agent 59 | agents []Agent 60 | wantErr bool 61 | }{ 62 | { 63 | name: "root only", 64 | root: agent1, 65 | agents: []Agent{}, 66 | wantErr: false, 67 | }, 68 | { 69 | name: "root duplicate object", 70 | root: agent1, 71 | agents: []Agent{agent1}, 72 | wantErr: true, 73 | }, 74 | { 75 | name: "root duplicate name", 76 | root: agent1, 77 | agents: []Agent{agent2}, 78 | wantErr: true, 79 | }, 80 | { 81 | name: "non-root duplicate name", 82 | root: agent3, 83 | agents: []Agent{agent1, agent2}, 84 | wantErr: true, 85 | }, 86 | { 87 | name: "non-root duplicate object", 88 | root: agent3, 89 | agents: []Agent{agent1, agent1}, 90 | wantErr: true, 91 | }, 92 | { 93 | name: "no duplicates", 94 | root: agent1, 95 | agents: []Agent{agent3}, 96 | wantErr: false, 97 | }, 98 | } 99 | for _, tt := range tests { 100 | _, err := NewMultiLoader(tt.root, tt.agents...) 101 | if (err != nil) != tt.wantErr { 102 | t.Errorf("NewMultiLoader() name=%v, error = %v, wantErr %v", tt.name, err, tt.wantErr) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /server/adkrest/internal/services/apiserverspanexporter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 services 16 | 17 | import ( 18 | "context" 19 | "strings" 20 | 21 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 22 | ) 23 | 24 | // APIServerSpanExporter is a custom SpanExporter that stores relevant span data. 25 | // Stores attributes of specific spans (call_llm, send_data, execute_tool) keyed by `gcp.vertex.agent.event_id`. 26 | // This is used for debugging individual events. 27 | // APIServerSpanExporter implements sdktrace.SpanExporter interface. 28 | type APIServerSpanExporter struct { 29 | traceDict map[string]map[string]string 30 | } 31 | 32 | // NewAPIServerSpanExporter returns a APIServerSpanExporter instance 33 | func NewAPIServerSpanExporter() *APIServerSpanExporter { 34 | return &APIServerSpanExporter{ 35 | traceDict: make(map[string]map[string]string), 36 | } 37 | } 38 | 39 | // GetTraceDict returns stored trace informations 40 | func (s *APIServerSpanExporter) GetTraceDict() map[string]map[string]string { 41 | return s.traceDict 42 | } 43 | 44 | // ExportSpans implements custom export function for sdktrace.SpanExporter. 45 | func (s *APIServerSpanExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error { 46 | for _, span := range spans { 47 | if span.Name() == "call_llm" || span.Name() == "send_data" || strings.HasPrefix(span.Name(), "execute_tool") { 48 | spanAttributes := span.Attributes() 49 | attributes := make(map[string]string) 50 | for _, attribute := range spanAttributes { 51 | key := string(attribute.Key) 52 | attributes[key] = attribute.Value.AsString() 53 | } 54 | attributes["trace_id"] = span.SpanContext().TraceID().String() 55 | attributes["span_id"] = span.SpanContext().SpanID().String() 56 | if eventID, ok := attributes["gcp.vertex.agent.event_id"]; ok { 57 | s.traceDict[eventID] = attributes 58 | } 59 | } 60 | } 61 | return nil 62 | } 63 | 64 | // Shutdown is a function that sdktrace.SpanExporter has, should close the span exporter connections. 65 | // Since APIServerSpanExporter holds only in-memory dictionary, no additional logic required. 66 | func (s *APIServerSpanExporter) Shutdown(ctx context.Context) error { 67 | return nil 68 | } 69 | 70 | var _ sdktrace.SpanExporter = (*APIServerSpanExporter)(nil) 71 | -------------------------------------------------------------------------------- /examples/workflowagents/sequential/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 demonstrates a workflow agent that runs sub-agents sequentially. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | "iter" 22 | "log" 23 | "os" 24 | 25 | "google.golang.org/genai" 26 | 27 | "google.golang.org/adk/agent" 28 | "google.golang.org/adk/agent/workflowagents/sequentialagent" 29 | "google.golang.org/adk/cmd/launcher" 30 | "google.golang.org/adk/cmd/launcher/full" 31 | "google.golang.org/adk/model" 32 | "google.golang.org/adk/session" 33 | ) 34 | 35 | type myAgent struct { 36 | id int 37 | } 38 | 39 | func (a myAgent) Run(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] { 40 | return func(yield func(*session.Event, error) bool) { 41 | yield(&session.Event{ 42 | LLMResponse: model.LLMResponse{ 43 | Content: &genai.Content{ 44 | Parts: []*genai.Part{ 45 | { 46 | Text: fmt.Sprintf("Hello from MyAgent id: %v!\n", a.id), 47 | }, 48 | }, 49 | }, 50 | }, 51 | }, nil) 52 | } 53 | } 54 | 55 | func main() { 56 | ctx := context.Background() 57 | 58 | myAgent1, err := agent.New(agent.Config{ 59 | Name: "my_custom_agent_1", 60 | Description: "A custom agent that responds with a greeting.", 61 | Run: myAgent{id: 1}.Run, 62 | }) 63 | if err != nil { 64 | log.Fatalf("Failed to create agent: %v", err) 65 | } 66 | 67 | myAgent2, err := agent.New(agent.Config{ 68 | Name: "my_custom_agent_2", 69 | Description: "A custom agent that responds with a greeting.", 70 | Run: myAgent{id: 2}.Run, 71 | }) 72 | if err != nil { 73 | log.Fatalf("Failed to create agent: %v", err) 74 | } 75 | 76 | sequentialAgent, err := sequentialagent.New(sequentialagent.Config{ 77 | AgentConfig: agent.Config{ 78 | Name: "sequential_agent", 79 | Description: "A sequential agent that runs sub-agents", 80 | SubAgents: []agent.Agent{myAgent1, myAgent2}, 81 | }, 82 | }) 83 | if err != nil { 84 | log.Fatalf("Failed to create agent: %v", err) 85 | } 86 | 87 | config := &launcher.Config{ 88 | AgentLoader: agent.NewSingleLoader(sequentialAgent), 89 | } 90 | 91 | l := full.NewLauncher() 92 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 93 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agent Development Kit (ADK) for Go 2 | 3 | [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) 4 | [![Go Doc](https://img.shields.io/badge/Go%20Package-Doc-blue.svg)](https://pkg.go.dev/google.golang.org/adk) 5 | [![Nightly Check](https://github.com/google/adk-go/actions/workflows/nightly.yml/badge.svg)](https://github.com/google/adk-go/actions/workflows/nightly.yml) 6 | [![r/agentdevelopmentkit](https://img.shields.io/badge/Reddit-r%2Fagentdevelopmentkit-FF4500?style=flat&logo=reddit&logoColor=white)](https://www.reddit.com/r/agentdevelopmentkit/) 7 | [![View Code Wiki](https://www.gstatic.com/_/boq-sdlc-agents-ui/_/r/YUi5dj2UWvE.svg)](https://codewiki.google/github.com/google/adk-go) 8 | 9 | 10 |

11 | 12 |

13 |

14 | An open-source, code-first Go toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control. 15 |

16 |

17 | Important Links: 18 | Docs & 19 | Samples & 20 | Python ADK & 21 | Java ADK & 22 | ADK Web. 23 |

24 | 25 | 26 | Agent Development Kit (ADK) is a flexible and modular framework that applies software development principles to AI agent creation. It is designed to simplify building, deploying, and orchestrating agent workflows, from simple tasks to complex systems. While optimized for Gemini, ADK is model-agnostic, deployment-agnostic, and compatible with other frameworks. 27 | 28 | This Go version of ADK is ideal for developers building cloud-native agent applications, leveraging Go's strengths in concurrency and performance. 29 | 30 | --- 31 | 32 | ## ✨ Key Features 33 | 34 | * **Idiomatic Go:** Designed to feel natural and leverage the power of Go. 35 | * **Rich Tool Ecosystem:** Utilize pre-built tools, custom functions, or integrate existing tools to give agents diverse capabilities. 36 | * **Code-First Development:** Define agent logic, tools, and orchestration directly in Go for ultimate flexibility, testability, and versioning. 37 | * **Modular Multi-Agent Systems:** Design scalable applications by composing multiple specialized agents. 38 | * **Deploy Anywhere:** Easily containerize and deploy agents, with strong support for cloud-native environments like Google Cloud Run. 39 | 40 | ## 🚀 Installation 41 | 42 | To add ADK Go to your project, run: 43 | 44 | ```bash 45 | go get google.golang.org/adk 46 | ``` 47 | 48 | ## 📄 License 49 | 50 | This project is licensed under the Apache 2.0 License - see the 51 | [LICENSE](LICENSE) file for details. 52 | 53 | The exception is internal/httprr - see its [LICENSE file](internal/httprr/LICENSE). 54 | -------------------------------------------------------------------------------- /session/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 session 16 | 17 | import ( 18 | "context" 19 | "time" 20 | ) 21 | 22 | // Service is a session storage service. 23 | // 24 | // It provides a set of methods for managing sessions and events. 25 | type Service interface { 26 | Create(context.Context, *CreateRequest) (*CreateResponse, error) 27 | Get(context.Context, *GetRequest) (*GetResponse, error) 28 | List(context.Context, *ListRequest) (*ListResponse, error) 29 | Delete(context.Context, *DeleteRequest) error 30 | // AppendEvent is used to append an event to a session, and remove temporary state keys from the event. 31 | AppendEvent(context.Context, Session, *Event) error 32 | } 33 | 34 | // InMemoryService returns an in-memory implementation of the session service. 35 | func InMemoryService() Service { 36 | return &inMemoryService{ 37 | appState: make(map[string]stateMap), 38 | userState: make(map[string]map[string]stateMap), 39 | } 40 | } 41 | 42 | // CreateRequest represents a request to create a session. 43 | type CreateRequest struct { 44 | AppName string 45 | UserID string 46 | // SessionID is the client-provided ID of the session to create. 47 | // Optional: if not set, it will be autogenerated. 48 | SessionID string 49 | // State is the initial state of the session. 50 | State map[string]any 51 | } 52 | 53 | // CreateResponse represents a response for newly created session. 54 | type CreateResponse struct { 55 | Session Session 56 | } 57 | 58 | // GetRequest represents a request to get a session. 59 | type GetRequest struct { 60 | AppName string 61 | UserID string 62 | SessionID string 63 | 64 | // NumRecentEvents returns at most NumRecentEvents most recent events. 65 | // Optional: if zero, the filter is not applied. 66 | NumRecentEvents int 67 | // After returns events with timestamp >= the given time. 68 | // Optional: if zero, the filter is not applied. 69 | After time.Time 70 | } 71 | 72 | // GetResponse represents a response from [Service.Get]. 73 | type GetResponse struct { 74 | Session Session 75 | } 76 | 77 | // ListRequest represents a request to list sessions. 78 | type ListRequest struct { 79 | AppName string 80 | UserID string 81 | } 82 | 83 | // ListResponse represents a response from [Service.List]. 84 | type ListResponse struct { 85 | Sessions []Session 86 | } 87 | 88 | // DeleteRequest represents a request to delete a session. 89 | type DeleteRequest struct { 90 | AppName string 91 | UserID string 92 | SessionID string 93 | } 94 | -------------------------------------------------------------------------------- /cmd/launcher/web/webui/distr/adk_favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/rest/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 provides an example ADK REST API server with an ADK agent. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "log" 21 | "net/http" 22 | "os" 23 | "time" 24 | 25 | "google.golang.org/genai" 26 | 27 | "google.golang.org/adk/agent" 28 | "google.golang.org/adk/agent/llmagent" 29 | "google.golang.org/adk/cmd/launcher" 30 | "google.golang.org/adk/model/gemini" 31 | "google.golang.org/adk/server/adkrest" 32 | "google.golang.org/adk/session" 33 | "google.golang.org/adk/tool" 34 | "google.golang.org/adk/tool/geminitool" 35 | ) 36 | 37 | func main() { 38 | ctx := context.Background() 39 | 40 | // Create a Gemini model 41 | model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{ 42 | APIKey: os.Getenv("GOOGLE_API_KEY"), 43 | }) 44 | if err != nil { 45 | log.Fatalf("Failed to create model: %v", err) 46 | } 47 | 48 | // Create an agent 49 | a, err := llmagent.New(llmagent.Config{ 50 | Name: "weather_time_agent", 51 | Model: model, 52 | Description: "Agent to answer questions about the time and weather in a city.", 53 | Instruction: "I can answer your questions about the time and weather in a city.", 54 | Tools: []tool.Tool{ 55 | geminitool.GoogleSearch{}, 56 | }, 57 | }) 58 | if err != nil { 59 | log.Fatalf("Failed to create agent: %v", err) 60 | } 61 | 62 | // Configure the ADK REST API 63 | config := &launcher.Config{ 64 | AgentLoader: agent.NewSingleLoader(a), 65 | SessionService: session.InMemoryService(), 66 | } 67 | 68 | // Create the REST API handler - this returns a standard http.Handler 69 | apiHandler := adkrest.NewHandler(config, 120*time.Second) 70 | 71 | // Create a standard net/http ServeMux 72 | mux := http.NewServeMux() 73 | 74 | // Register the API handler at the /api/ path 75 | // You can use any HTTP server or router here - not tied to gorilla/mux 76 | mux.Handle("/api/", http.StripPrefix("/api", apiHandler)) 77 | 78 | // Add a simple health check endpoint 79 | mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { 80 | w.WriteHeader(http.StatusOK) 81 | if _, err := w.Write([]byte("OK")); err != nil { 82 | log.Printf("Failed to write response: %v", err) 83 | } 84 | }) 85 | 86 | // Start the server 87 | log.Println("Starting server on :8080") 88 | log.Println("API available at http://localhost:8080/api/") 89 | log.Println("Health check at http://localhost:8080/health") 90 | 91 | if err := http.ListenAndServe(":8080", mux); err != nil { 92 | log.Fatalf("Server failed: %v", err) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/workflowagents/parallel/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 demonstrates a workflow agent that runs sub-agents in parallel. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | "iter" 22 | "log" 23 | rand "math/rand/v2" 24 | "os" 25 | "time" 26 | 27 | "google.golang.org/genai" 28 | 29 | "google.golang.org/adk/agent" 30 | "google.golang.org/adk/agent/workflowagents/parallelagent" 31 | "google.golang.org/adk/cmd/launcher" 32 | "google.golang.org/adk/cmd/launcher/full" 33 | "google.golang.org/adk/model" 34 | "google.golang.org/adk/session" 35 | ) 36 | 37 | func main() { 38 | ctx := context.Background() 39 | 40 | subAgent1, err := agent.New(agent.Config{ 41 | Name: "my_custom_agent_1", 42 | Description: "A custom agent that responds with a greeting.", 43 | Run: myAgent{id: 1}.Run, 44 | }) 45 | if err != nil { 46 | log.Fatalf("Failed to create agent: %v", err) 47 | } 48 | 49 | subAgent2, err := agent.New(agent.Config{ 50 | Name: "my_custom_agent_2", 51 | Description: "A custom agent that responds with a greeting.", 52 | Run: myAgent{id: 2}.Run, 53 | }) 54 | if err != nil { 55 | log.Fatalf("Failed to create agent: %v", err) 56 | } 57 | 58 | parallelAgent, err := parallelagent.New(parallelagent.Config{ 59 | AgentConfig: agent.Config{ 60 | Name: "parallel_agent", 61 | Description: "A parallel agent that runs sub-agents", 62 | SubAgents: []agent.Agent{subAgent1, subAgent2}, 63 | }, 64 | }) 65 | if err != nil { 66 | log.Fatalf("Failed to create agent: %v", err) 67 | } 68 | 69 | config := &launcher.Config{ 70 | AgentLoader: agent.NewSingleLoader(parallelAgent), 71 | } 72 | 73 | l := full.NewLauncher() 74 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 75 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 76 | } 77 | } 78 | 79 | type myAgent struct { 80 | id int 81 | } 82 | 83 | func (a myAgent) Run(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] { 84 | return func(yield func(*session.Event, error) bool) { 85 | for range 3 { 86 | if !yield(&session.Event{ 87 | LLMResponse: model.LLMResponse{ 88 | Content: &genai.Content{ 89 | Parts: []*genai.Part{ 90 | { 91 | Text: fmt.Sprintf("Hello from MyAgent id: %v!\n", a.id), 92 | }, 93 | }, 94 | }, 95 | }, 96 | }, nil) { 97 | return 98 | } 99 | 100 | r := 1 + rand.IntN(5) 101 | time.Sleep(time.Duration(r) * time.Second) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /server/adka2a/executor_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 adka2a 16 | 17 | import ( 18 | "context" 19 | "iter" 20 | 21 | "github.com/a2aproject/a2a-go/a2asrv" 22 | "google.golang.org/genai" 23 | 24 | "google.golang.org/adk/session" 25 | ) 26 | 27 | // ExecutorContext provides read-only information about the context of an A2A agent execution. 28 | // An execution starts with a user message and ends with a task in a terminal or input-required state. 29 | type ExecutorContext interface { 30 | context.Context 31 | 32 | // SessionID is ID of the session. It is passed as contextID in A2A request. 33 | SessionID() string 34 | // UserID is ID of the user who made the request. The information is either extracted from [a2asrv.CallContext] 35 | // or derived from session ID for unauthenticated requests. 36 | UserID() string 37 | // AgentName is the name of the root agent. 38 | AgentName() string 39 | // ReadonlyState provides a view of the current session state. 40 | ReadonlyState() session.ReadonlyState 41 | // UserContent is a converted A2A message which is passed to runner.Run. 42 | UserContent() *genai.Content 43 | // RequestContext containts information about the original A2A Request, the current task and related tasks. 44 | RequestContext() *a2asrv.RequestContext 45 | } 46 | 47 | type executorContext struct { 48 | context.Context 49 | meta invocationMeta 50 | session session.ReadonlyState 51 | userContent *genai.Content 52 | } 53 | 54 | func newExecutorContext(ctx context.Context, meta invocationMeta, session session.ReadonlyState, userContent *genai.Content) ExecutorContext { 55 | return &executorContext{ 56 | Context: ctx, 57 | meta: meta, 58 | session: session, 59 | userContent: userContent, 60 | } 61 | } 62 | 63 | func (ec *executorContext) SessionID() string { 64 | return ec.meta.sessionID 65 | } 66 | 67 | func (ec *executorContext) UserID() string { 68 | return ec.meta.userID 69 | } 70 | 71 | func (ec *executorContext) AgentName() string { 72 | return ec.meta.agentName 73 | } 74 | 75 | func (ec *executorContext) ReadonlyState() session.ReadonlyState { 76 | return ec.session 77 | } 78 | 79 | func (ec *executorContext) RequestContext() *a2asrv.RequestContext { 80 | return ec.meta.reqCtx 81 | } 82 | 83 | func (ec *executorContext) UserContent() *genai.Content { 84 | return ec.userContent 85 | } 86 | 87 | type emptySessionState struct{} 88 | 89 | func (emptySessionState) Get(string) (any, error) { 90 | return nil, session.ErrStateKeyNotExist 91 | } 92 | 93 | func (emptySessionState) All() iter.Seq2[string, any] { 94 | return func(yield func(string, any) bool) {} 95 | } 96 | -------------------------------------------------------------------------------- /tool/tool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 tool defines the interfaces for tools that can be called by an agent. 16 | // A tool is a piece of code that performs a specific task. You can either define 17 | // your own custom tools or use built-in ones, for example, GoogleSearch. 18 | package tool 19 | 20 | import ( 21 | "context" 22 | 23 | "google.golang.org/adk/agent" 24 | "google.golang.org/adk/memory" 25 | "google.golang.org/adk/session" 26 | ) 27 | 28 | // Tool defines the interface for a callable tool. 29 | type Tool interface { 30 | // Name returns the name of the tool. 31 | Name() string 32 | // Description returns a description of the tool. 33 | Description() string 34 | // IsLongRunning indicates whether the tool is a long-running operation, 35 | // which typically returns a resource id first and finishes the operation later. 36 | IsLongRunning() bool 37 | } 38 | 39 | // Context defines the interface for the context passed to a tool when it's 40 | // called. It provides access to invocation-specific information and allows 41 | // the tool to interact with the agent's state and memory. 42 | type Context interface { 43 | agent.CallbackContext 44 | // FunctionCallID returns the unique identifier of the function call 45 | // that triggered this tool execution. 46 | FunctionCallID() string 47 | 48 | // Actions returns the EventActions for the current event. This can be 49 | // used by the tool to modify the agent's state, transfer to another 50 | // agent, or perform other actions. 51 | Actions() *session.EventActions 52 | // SearchMemory performs a semantic search on the agent's memory. 53 | SearchMemory(context.Context, string) (*memory.SearchResponse, error) 54 | } 55 | 56 | // Toolset is an interface for a collection of tools. It allows grouping 57 | // related tools together and providing them to an agent. 58 | type Toolset interface { 59 | // Name returns the name of the toolset. 60 | Name() string 61 | // Tools returns a list of tools in the toolset. The provided 62 | // ReadonlyContext can be used to dynamically determine which tools 63 | // to return based on the current invocation state. 64 | Tools(ctx agent.ReadonlyContext) ([]Tool, error) 65 | } 66 | 67 | // Predicate is a function which decides whether a tool should be exposed to LLM. 68 | type Predicate func(ctx agent.ReadonlyContext, tool Tool) bool 69 | 70 | // StringPredicate is a helper that creates a Predicate from a string slice. 71 | func StringPredicate(allowedTools []string) Predicate { 72 | m := make(map[string]bool) 73 | for _, t := range allowedTools { 74 | m[t] = true 75 | } 76 | 77 | return func(ctx agent.ReadonlyContext, tool Tool) bool { 78 | return m[tool.Name()] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /internal/llminternal/clone_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 llminternal 16 | 17 | import ( 18 | "reflect" 19 | "testing" 20 | ) 21 | 22 | func TestClone(t *testing.T) { 23 | type testStruct struct { 24 | S string 25 | I int 26 | Sl []string 27 | M map[string]string 28 | P *int 29 | N *testStruct 30 | } 31 | 32 | testData := func() *testStruct { 33 | return &testStruct{ 34 | S: "test", 35 | I: 123, 36 | Sl: []string{"a", "b"}, 37 | M: map[string]string{"k": "v"}, 38 | P: func() *int { i := 456; return &i }(), 39 | N: &testStruct{ 40 | S: "nested", 41 | }, 42 | } 43 | } 44 | 45 | check := func(t *testing.T, original, cloned *testStruct) { 46 | if !reflect.DeepEqual(original, cloned) { 47 | t.Errorf("clone() = %+v, want %+v", cloned, original) 48 | } 49 | 50 | // Modify cloned and check if original is affected 51 | cloned.Sl[0] = "c" 52 | cloned.M["k"] = "v2" 53 | *cloned.P = 789 54 | cloned.N.S = "nested2" 55 | 56 | if reflect.DeepEqual(original, cloned) { 57 | t.Errorf("clone() should not be affected by modifications to original") 58 | } 59 | if original.Sl[0] != "a" { 60 | t.Errorf("original slice was modified") 61 | } 62 | if original.M["k"] != "v" { 63 | t.Errorf("original map was modified") 64 | } 65 | if *original.P != 456 { 66 | t.Errorf("original pointer value was modified") 67 | } 68 | if original.N.S != "nested" { 69 | t.Errorf("original nested struct was modified") 70 | } 71 | } 72 | 73 | t.Run("pointer", func(t *testing.T) { 74 | original := testData() 75 | cloned := clone(original) 76 | check(t, original, cloned) 77 | }) 78 | t.Run("value", func(t *testing.T) { 79 | original := testData() 80 | cloned := clone(*original) 81 | check(t, original, &cloned) 82 | }) 83 | t.Run("interface", func(t *testing.T) { 84 | original := testData() 85 | cloned := clone(any(original)) 86 | typed, ok := cloned.(*testStruct) 87 | if !ok { 88 | t.Fatalf("clone failed with interface: %v", cloned) 89 | } 90 | check(t, original, typed) 91 | }) 92 | } 93 | 94 | func TestCloneNil(t *testing.T) { 95 | var original *int 96 | cloned := clone(original) 97 | if cloned != nil { 98 | t.Errorf("clone(nil) = %v, want nil", cloned) 99 | } 100 | } 101 | 102 | func TestCloneUnexported(t *testing.T) { 103 | type testStructUnexported struct { 104 | s string 105 | } 106 | original := &testStructUnexported{s: "test"} 107 | 108 | defer func() { 109 | if r := recover(); r == nil { 110 | t.Errorf("clone() did not panic on unexported field") 111 | } 112 | }() 113 | clone(original) 114 | } 115 | -------------------------------------------------------------------------------- /agent/workflowagents/loopagent/agent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 loopagent provides an agent that repeatedly runs its sub-agents for a 16 | // specified number of iterations or until termination condition is met. 17 | package loopagent 18 | 19 | import ( 20 | "fmt" 21 | "iter" 22 | 23 | "google.golang.org/adk/agent" 24 | agentinternal "google.golang.org/adk/internal/agent" 25 | "google.golang.org/adk/session" 26 | ) 27 | 28 | // Config defines the configuration for a LoopAgent. 29 | type Config struct { 30 | // Basic agent setup. 31 | AgentConfig agent.Config 32 | 33 | // If MaxIterations == 0, then LoopAgent runs indefinitely or until any 34 | // sub-agent escalates. 35 | MaxIterations uint 36 | } 37 | 38 | // New creates a LoopAgent. 39 | // 40 | // LoopAgent repeatedly runs its sub-agents in sequence for a specified number 41 | // of iterations or until a termination condition is met. 42 | // 43 | // Use the LoopAgent when your workflow involves repetition or iterative 44 | // refinement, such as like revising code. 45 | func New(cfg Config) (agent.Agent, error) { 46 | if cfg.AgentConfig.Run != nil { 47 | return nil, fmt.Errorf("LoopAgent doesn't allow custom Run implementations") 48 | } 49 | 50 | loopAgentImpl := &loopAgent{ 51 | maxIterations: cfg.MaxIterations, 52 | } 53 | cfg.AgentConfig.Run = loopAgentImpl.Run 54 | 55 | loopAgent, err := agent.New(cfg.AgentConfig) 56 | if err != nil { 57 | return nil, fmt.Errorf("failed to create base agent: %w", err) 58 | } 59 | 60 | internalAgent, ok := loopAgent.(agentinternal.Agent) 61 | if !ok { 62 | return nil, fmt.Errorf("internal error: failed to convert to internal agent") 63 | } 64 | state := agentinternal.Reveal(internalAgent) 65 | state.AgentType = agentinternal.TypeLoopAgent 66 | state.Config = cfg 67 | 68 | return loopAgent, nil 69 | } 70 | 71 | type loopAgent struct { 72 | maxIterations uint 73 | } 74 | 75 | func (a *loopAgent) Run(ctx agent.InvocationContext) iter.Seq2[*session.Event, error] { 76 | count := a.maxIterations 77 | 78 | return func(yield func(*session.Event, error) bool) { 79 | for { 80 | shouldExit := false 81 | for _, subAgent := range ctx.Agent().SubAgents() { 82 | for event, err := range subAgent.Run(ctx) { 83 | // TODO: ensure consistency -- if there's an error, return and close iterator, verify everywhere in ADK. 84 | if !yield(event, err) { 85 | return 86 | } 87 | 88 | if event.Actions.Escalate { 89 | shouldExit = true 90 | } 91 | } 92 | if shouldExit { 93 | return 94 | } 95 | } 96 | 97 | if count > 0 { 98 | count-- 99 | if count == 0 { 100 | return 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /internal/toolinternal/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 toolinternal 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/google/uuid" 21 | "google.golang.org/genai" 22 | 23 | "google.golang.org/adk/agent" 24 | "google.golang.org/adk/artifact" 25 | contextinternal "google.golang.org/adk/internal/context" 26 | "google.golang.org/adk/memory" 27 | "google.golang.org/adk/session" 28 | "google.golang.org/adk/tool" 29 | ) 30 | 31 | type internalArtifacts struct { 32 | agent.Artifacts 33 | eventActions *session.EventActions 34 | } 35 | 36 | func (ia *internalArtifacts) Save(ctx context.Context, name string, data *genai.Part) (*artifact.SaveResponse, error) { 37 | resp, err := ia.Artifacts.Save(ctx, name, data) 38 | if err != nil { 39 | return resp, err 40 | } 41 | if ia.eventActions != nil { 42 | if ia.eventActions.ArtifactDelta == nil { 43 | ia.eventActions.ArtifactDelta = make(map[string]int64) 44 | } 45 | // TODO: RWLock, check the version stored is newer in case multiple tools save the same file. 46 | ia.eventActions.ArtifactDelta[name] = resp.Version 47 | } 48 | return resp, nil 49 | } 50 | 51 | func NewToolContext(ctx agent.InvocationContext, functionCallID string, actions *session.EventActions) tool.Context { 52 | if functionCallID == "" { 53 | functionCallID = uuid.NewString() 54 | } 55 | if actions == nil { 56 | actions = &session.EventActions{StateDelta: make(map[string]any)} 57 | } 58 | if actions.StateDelta == nil { 59 | actions.StateDelta = make(map[string]any) 60 | } 61 | cbCtx := contextinternal.NewCallbackContextWithDelta(ctx, actions.StateDelta) 62 | 63 | return &toolContext{ 64 | CallbackContext: cbCtx, 65 | invocationContext: ctx, 66 | functionCallID: functionCallID, 67 | eventActions: actions, 68 | artifacts: &internalArtifacts{ 69 | Artifacts: ctx.Artifacts(), 70 | eventActions: actions, 71 | }, 72 | } 73 | } 74 | 75 | type toolContext struct { 76 | agent.CallbackContext 77 | invocationContext agent.InvocationContext 78 | functionCallID string 79 | eventActions *session.EventActions 80 | artifacts *internalArtifacts 81 | } 82 | 83 | func (c *toolContext) Artifacts() agent.Artifacts { 84 | return c.artifacts 85 | } 86 | 87 | func (c *toolContext) FunctionCallID() string { 88 | return c.functionCallID 89 | } 90 | 91 | func (c *toolContext) Actions() *session.EventActions { 92 | return c.eventActions 93 | } 94 | 95 | func (c *toolContext) AgentName() string { 96 | return c.invocationContext.Agent().Name() 97 | } 98 | 99 | func (c *toolContext) SearchMemory(ctx context.Context, query string) (*memory.SearchResponse, error) { 100 | return c.invocationContext.Memory().Search(ctx, query) 101 | } 102 | -------------------------------------------------------------------------------- /examples/web/agents/image_generator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agents contains sample agents to demonstate ADK Web Capabilities. 16 | package agents 17 | 18 | import ( 19 | "context" 20 | "log" 21 | "os" 22 | 23 | "google.golang.org/genai" 24 | 25 | "google.golang.org/adk/agent" 26 | "google.golang.org/adk/agent/llmagent" 27 | "google.golang.org/adk/model" 28 | "google.golang.org/adk/tool" 29 | "google.golang.org/adk/tool/functiontool" 30 | "google.golang.org/adk/tool/loadartifactstool" 31 | ) 32 | 33 | func generateImage(ctx tool.Context, input generateImageInput) (generateImageResult, error) { 34 | client, err := genai.NewClient(ctx, &genai.ClientConfig{ 35 | Project: os.Getenv("GOOGLE_CLOUD_PROJECT"), 36 | Location: os.Getenv("GOOGLE_CLOUD_LOCATION"), 37 | Backend: genai.BackendVertexAI, 38 | }) 39 | if err != nil { 40 | return generateImageResult{ 41 | Status: "fail", 42 | }, nil 43 | } 44 | 45 | response, err := client.Models.GenerateImages( 46 | ctx, 47 | "imagen-3.0-generate-002", 48 | input.Prompt, 49 | &genai.GenerateImagesConfig{NumberOfImages: 1}) 50 | if err != nil { 51 | return generateImageResult{ 52 | Status: "fail", 53 | }, nil 54 | } 55 | 56 | _, err = ctx.Artifacts().Save(ctx, input.Filename, genai.NewPartFromBytes(response.GeneratedImages[0].Image.ImageBytes, "image/png")) 57 | if err != nil { 58 | return generateImageResult{ 59 | Status: "fail", 60 | }, nil 61 | } 62 | 63 | return generateImageResult{ 64 | Status: "success", 65 | Filename: input.Filename, 66 | }, nil 67 | } 68 | 69 | type generateImageInput struct { 70 | Prompt string `json:"prompt"` 71 | Filename string `json:"filename"` 72 | } 73 | 74 | type generateImageResult struct { 75 | Filename string `json:"filename"` 76 | Status string `json:"Status"` 77 | } 78 | 79 | func GetImageGeneratorAgent(ctx context.Context, model model.LLM) agent.Agent { 80 | generateImageTool, err := functiontool.New( 81 | functiontool.Config{ 82 | Name: "generate_image", 83 | Description: "Generates image and saves in artifact service.", 84 | }, 85 | generateImage) 86 | if err != nil { 87 | log.Fatalf("Failed to create generate image tool: %v", err) 88 | } 89 | imageGeneratorAgent, err := llmagent.New(llmagent.Config{ 90 | Name: "image_generator", 91 | Model: model, 92 | Description: "Agent to generate pictures, answers questions about it and saves it locally if asked.", 93 | Instruction: "You are an agent whose job is to generate or edit an image based on the user's prompt.", 94 | Tools: []tool.Tool{ 95 | generateImageTool, loadartifactstool.New(), 96 | }, 97 | }) 98 | if err != nil { 99 | log.Fatalf("Failed to create agent: %v", err) 100 | } 101 | return imageGeneratorAgent 102 | } 103 | -------------------------------------------------------------------------------- /server/adkrest/internal/models/session.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 models 16 | 17 | import ( 18 | "fmt" 19 | "maps" 20 | 21 | "github.com/mitchellh/mapstructure" 22 | 23 | "google.golang.org/adk/session" 24 | ) 25 | 26 | // Session represents an agent's session. 27 | type Session struct { 28 | ID string `json:"id"` 29 | AppName string `json:"appName"` 30 | UserID string `json:"userId"` 31 | UpdatedAt int64 `json:"lastUpdateTime"` 32 | Events []Event `json:"events"` 33 | State map[string]any `json:"state"` 34 | } 35 | 36 | type CreateSessionRequest struct { 37 | State map[string]any `json:"state"` 38 | Events []Event `json:"events"` 39 | } 40 | 41 | type SessionID struct { 42 | ID string `mapstructure:"session_id,optional"` 43 | AppName string `mapstructure:"app_name,required"` 44 | UserID string `mapstructure:"user_id,required"` 45 | } 46 | 47 | func SessionIDFromHTTPParameters(vars map[string]string) (SessionID, error) { 48 | var sessionID SessionID 49 | decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 50 | WeaklyTypedInput: true, 51 | Result: &sessionID, 52 | }) 53 | if err != nil { 54 | return sessionID, err 55 | } 56 | err = decoder.Decode(vars) 57 | if err != nil { 58 | return sessionID, err 59 | } 60 | if sessionID.AppName == "" { 61 | return sessionID, fmt.Errorf("app_name parameter is required") 62 | } 63 | if sessionID.UserID == "" { 64 | return sessionID, fmt.Errorf("user_id parameter is required") 65 | } 66 | return sessionID, nil 67 | } 68 | 69 | func FromSession(session session.Session) (Session, error) { 70 | state := map[string]any{} 71 | maps.Insert(state, session.State().All()) 72 | events := []Event{} 73 | for event := range session.Events().All() { 74 | events = append(events, FromSessionEvent(*event)) 75 | } 76 | mappedSession := Session{ 77 | ID: session.ID(), 78 | AppName: session.AppName(), 79 | UserID: session.UserID(), 80 | UpdatedAt: session.LastUpdateTime().Unix(), 81 | Events: events, 82 | State: state, 83 | } 84 | return mappedSession, mappedSession.Validate() 85 | } 86 | 87 | func (s Session) Validate() error { 88 | if s.AppName == "" { 89 | return fmt.Errorf("app_name is empty in received session") 90 | } 91 | if s.UserID == "" { 92 | return fmt.Errorf("user_id is empty in received session") 93 | } 94 | if s.ID == "" { 95 | return fmt.Errorf("session_id is empty in received session") 96 | } 97 | if s.UpdatedAt == 0 { 98 | return fmt.Errorf("updated_at is empty") 99 | } 100 | if s.State == nil { 101 | return fmt.Errorf("state is nil") 102 | } 103 | if s.Events == nil { 104 | return fmt.Errorf("events is nil") 105 | } 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /internal/style_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 internal_test 16 | 17 | import ( 18 | "flag" 19 | "os" 20 | "path/filepath" 21 | "strings" 22 | "testing" 23 | ) 24 | 25 | const copyrightHeader = `// Copyright 2025 Google LLC 26 | // 27 | // Licensed under the Apache License, Version 2.0 (the "License"); 28 | // you may not use this file except in compliance with the License. 29 | // You may obtain a copy of the License at 30 | // 31 | // http://www.apache.org/licenses/LICENSE-2.0 32 | // 33 | // Unless required by applicable law or agreed to in writing, software 34 | // distributed under the License is distributed on an "AS IS" BASIS, 35 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | // See the License for the specific language governing permissions and 37 | // limitations under the License. 38 | ` 39 | 40 | var fixError = flag.Bool("fix", false, "fix detected problems (e.g. add missing copyright headers)") 41 | 42 | func TestCopyrightHeader(t *testing.T) { 43 | // Start test from the parent directory, root of the module. 44 | t.Chdir("..") 45 | 46 | ignore := map[string]bool{ 47 | // Skip directories that are not relevant for copyright checks. 48 | // The followings were copied from golang.org/x/tools. 49 | "internal/jsonschema": true, 50 | "internal/util": true, 51 | // The following was copied from golang.org/x/oscar. 52 | "internal/httprr": true, 53 | } 54 | _ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 55 | if err != nil { 56 | return err 57 | } 58 | if info.IsDir() { 59 | if ignore[path] { 60 | return filepath.SkipDir 61 | } 62 | return nil 63 | } 64 | if !strings.HasSuffix(path, ".go") { 65 | return nil 66 | } 67 | hasHeader, err := hasCopyrightHeader(path) 68 | switch { 69 | case err != nil: 70 | t.Errorf("failed to check file %q: %v", path, err) 71 | case !hasHeader && !*fixError: 72 | t.Errorf("file %q does not have the copyright header", path) 73 | case !hasHeader && *fixError: 74 | t.Logf("updating file %q with copyright header", path) 75 | if err := addCopyrightHeader(path); err != nil { 76 | t.Errorf("failed to update file %q: %v", path, err) 77 | } 78 | } 79 | return nil 80 | }) 81 | } 82 | 83 | func hasCopyrightHeader(path string) (bool, error) { 84 | content, err := os.ReadFile(path) 85 | if err != nil { 86 | return false, err 87 | } 88 | return strings.HasPrefix(string(content), copyrightHeader), nil 89 | } 90 | 91 | func addCopyrightHeader(path string) error { 92 | content, err := os.ReadFile(path) 93 | if err != nil { 94 | return err 95 | } 96 | newContent := []byte(copyrightHeader) 97 | newContent = append(newContent, content...) 98 | return os.WriteFile(path, newContent, 0o644) 99 | } 100 | -------------------------------------------------------------------------------- /internal/cli/util/oscmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 util 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | "io" 21 | "os" 22 | "os/exec" 23 | "path" 24 | ) 25 | 26 | // Printer is a function printing its arguments 27 | type Printer func(a ...any) 28 | 29 | var ( 30 | Reset = "\033[0m" 31 | Red = "\033[31m" 32 | Green = "\033[32m" 33 | Yellow = "\033[33m" 34 | Blue = "\033[34m" 35 | Magenta = "\033[35m" 36 | Cyan = "\033[36m" 37 | Gray = "\033[37m" 38 | White = "\033[97m" 39 | ) 40 | 41 | // LogStartStop is a helper function which executes a particular command with logging 42 | func LogStartStop(msg string, command func(p Printer) error) error { 43 | fmt.Println(msg, ": "+Green+"Starting"+Reset) 44 | err := command(func(a ...any) { fmt.Println(" "+Green+"> "+Reset, a) }) 45 | fmt.Println() 46 | if err == nil { 47 | fmt.Println(msg, ": "+Green+"Finished successfully"+Reset) 48 | } else { 49 | fmt.Println(msg, ": "+Red+"Finished with error"+Reset) 50 | fmt.Println("Error:", err) 51 | } 52 | 53 | return err 54 | } 55 | 56 | type reprintableStream struct { 57 | prefix []byte 58 | clean bool 59 | stream io.Writer 60 | } 61 | 62 | // function Write is an interceptor of a stream adding some decorations 63 | func (s *reprintableStream) Write(p []byte) (total int, err error) { 64 | start := 0 65 | err = nil 66 | if s.clean { 67 | _, err = s.stream.Write(s.prefix) 68 | if err != nil { 69 | return total, err 70 | } 71 | s.clean = false 72 | } 73 | for i, c := range p { 74 | if c == '\n' { 75 | _, err = s.stream.Write(p[start:i]) 76 | if err != nil { 77 | return len(p), err 78 | } 79 | _, err = s.stream.Write(s.prefix) 80 | if err != nil { 81 | return len(p), err 82 | } 83 | start = i + 1 84 | } 85 | } 86 | if start < len(p) { 87 | _, err = s.stream.Write(p[start:]) 88 | } 89 | 90 | return len(p), err 91 | } 92 | 93 | func newReprintableStream(s io.Writer, prefix, color string) io.Writer { 94 | return &reprintableStream{prefix: []byte("\n " + color + prefix + " > " + Reset), stream: s, clean: true} 95 | } 96 | 97 | // function LogCommand runs a command pretty-printing its stdout and stderr 98 | func LogCommand(c *exec.Cmd, p Printer) error { 99 | p("Running : ", Yellow, c.Dir, Reset, " ", c) 100 | c.Stdout = newReprintableStream(os.Stdout, " ", Yellow) 101 | c.Stderr = newReprintableStream(os.Stdout, " ", Yellow) 102 | return c.Run() 103 | } 104 | 105 | func StripExtension(p, expected string) (string, error) { 106 | ex := path.Ext(p) 107 | if ex == "" { 108 | return "", errors.New("Cannot find extension in '" + p + "'") 109 | } 110 | if ex != expected { 111 | return "", errors.New("Unexpected extension. Found '" + ex + "' instead of '" + expected + "'") 112 | } 113 | return p[:len(p)-len(ex)], nil 114 | } 115 | -------------------------------------------------------------------------------- /internal/agent/parentmap/map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 parentmap_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | 22 | "google.golang.org/adk/agent" 23 | "google.golang.org/adk/agent/llmagent" 24 | "google.golang.org/adk/internal/agent/parentmap" 25 | "google.golang.org/adk/internal/utils" 26 | "google.golang.org/adk/model" 27 | ) 28 | 29 | func TestNew(t *testing.T) { 30 | child1_1 := utils.Must(agent.New(agent.Config{ 31 | Name: "child1_1", 32 | })) 33 | 34 | child1 := utils.Must(agent.New(agent.Config{ 35 | Name: "child1", 36 | SubAgents: []agent.Agent{child1_1}, 37 | })) 38 | 39 | child2 := utils.Must(agent.New(agent.Config{ 40 | Name: "child2", 41 | })) 42 | 43 | root := utils.Must(agent.New(agent.Config{ 44 | Name: "root", 45 | SubAgents: []agent.Agent{child1, child2}, 46 | })) 47 | 48 | got, err := parentmap.New(root) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | want := parentmap.Map{ 53 | child1_1.Name(): child1, 54 | child1.Name(): root, 55 | child2.Name(): root, 56 | } 57 | 58 | agentNames := cmp.Transformer("agentNames", func(m parentmap.Map) map[string]string { 59 | if m == nil { 60 | return nil 61 | } 62 | res := make(map[string]string) 63 | for k, v := range m { 64 | res[k] = v.Name() 65 | } 66 | return res 67 | }) 68 | 69 | if diff := cmp.Diff(want, got, agentNames); diff != "" { 70 | t.Errorf("New() = %v, got %v diff (-want/+got): %v", got, want, diff) 71 | } 72 | } 73 | 74 | func TestMap_RootAgent(t *testing.T) { 75 | model := struct { 76 | model.LLM 77 | }{} 78 | 79 | nonLLM := utils.Must(agent.New(agent.Config{ 80 | Name: "mock", 81 | })) 82 | b := utils.Must(llmagent.New(llmagent.Config{ 83 | Name: "b", 84 | Model: model, 85 | SubAgents: []agent.Agent{nonLLM}, 86 | })) 87 | a := utils.Must(llmagent.New(llmagent.Config{ 88 | Name: "a", 89 | Model: model, 90 | SubAgents: []agent.Agent{b}, 91 | })) 92 | root := utils.Must(llmagent.New(llmagent.Config{ 93 | Name: "root", 94 | Model: model, 95 | SubAgents: []agent.Agent{a}, 96 | })) 97 | 98 | agentName := func(a agent.Agent) string { 99 | if a == nil { 100 | return "nil" 101 | } 102 | return a.Name() 103 | } 104 | 105 | parents, err := parentmap.New(root) 106 | if err != nil { 107 | t.Fatal(err) 108 | } 109 | 110 | for _, tc := range []struct { 111 | agent agent.Agent 112 | want agent.Agent 113 | }{ 114 | {root, root}, 115 | {a, root}, 116 | {b, root}, 117 | {nonLLM, root}, 118 | {nil, nil}, 119 | } { 120 | t.Run("agent="+agentName(tc.agent), func(t *testing.T) { 121 | gotRoot := parents.RootAgent(tc.agent) 122 | if got, want := agentName(gotRoot), agentName(tc.want); got != want { 123 | t.Errorf("rootAgent(%q) = %q, want %q", agentName(tc.agent), got, want) 124 | } 125 | }) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tool/tool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 tool_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "google.golang.org/adk/internal/toolinternal" 21 | "google.golang.org/adk/tool" 22 | "google.golang.org/adk/tool/agenttool" 23 | "google.golang.org/adk/tool/functiontool" 24 | "google.golang.org/adk/tool/geminitool" 25 | "google.golang.org/adk/tool/loadartifactstool" 26 | ) 27 | 28 | func TestTypes(t *testing.T) { 29 | const ( 30 | functionTool = "FunctionTool" 31 | requestProc = "RequestProcessor" 32 | ) 33 | 34 | type intInput struct { 35 | Value int `json:"value"` 36 | } 37 | type intOutput struct { 38 | Value int `json:"value"` 39 | } 40 | 41 | tests := []struct { 42 | name string 43 | constructor func() (tool.Tool, error) 44 | expectedTypes []string 45 | }{ 46 | { 47 | name: "FunctionTool", 48 | constructor: func() (tool.Tool, error) { 49 | return functiontool.New(functiontool.Config{}, func(_ tool.Context, input intInput) (intOutput, error) { 50 | return intOutput(input), nil 51 | }) 52 | }, 53 | expectedTypes: []string{requestProc, functionTool}, 54 | }, 55 | { 56 | name: "geminitool", 57 | constructor: func() (tool.Tool, error) { return geminitool.New("", nil), nil }, 58 | expectedTypes: []string{requestProc}, 59 | }, 60 | { 61 | name: "geminitool.GoogleSearch{}", 62 | constructor: func() (tool.Tool, error) { return geminitool.GoogleSearch{}, nil }, 63 | expectedTypes: []string{requestProc}, 64 | }, 65 | { 66 | name: "LoadArtifactsTool", 67 | constructor: func() (tool.Tool, error) { return loadartifactstool.New(), nil }, 68 | expectedTypes: []string{requestProc, functionTool}, 69 | }, 70 | { 71 | name: "AgentTool", 72 | constructor: func() (tool.Tool, error) { return agenttool.New(nil, nil), nil }, 73 | expectedTypes: []string{requestProc, functionTool}, 74 | }, 75 | { 76 | name: "LoadArtifactsTool", 77 | constructor: func() (tool.Tool, error) { return loadartifactstool.New(), nil }, 78 | expectedTypes: []string{requestProc, functionTool}, 79 | }, 80 | } 81 | 82 | for _, tt := range tests { 83 | t.Run(tt.name, func(t *testing.T) { 84 | tool, err := tt.constructor() 85 | if err != nil { 86 | t.Fatalf("Failed to create tool %s: %v", tt.name, err) 87 | } 88 | 89 | for _, s := range tt.expectedTypes { 90 | switch s { 91 | case functionTool: 92 | if _, ok := tool.(toolinternal.FunctionTool); !ok { 93 | t.Errorf("Expected %s to implement toolinternal.FunctionTool", tt.name) 94 | } 95 | case requestProc: 96 | if _, ok := tool.(toolinternal.RequestProcessor); !ok { 97 | t.Errorf("Expected %s to implement toolinternal.RequestProcessor", tt.name) 98 | } 99 | default: 100 | t.Fatalf("Unknown expected type: %s", s) 101 | } 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /agent/loader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent 16 | 17 | import ( 18 | "fmt" 19 | ) 20 | 21 | // Loader allows to load a particular agent by name and get the root agent 22 | type Loader interface { 23 | // ListAgents returns a list of names of all agents 24 | ListAgents() []string 25 | // LoadAgent returns an agent by its name. Returns error if there is no agent with such a name. 26 | LoadAgent(name string) (Agent, error) 27 | // RootAgent returns the root agent 28 | RootAgent() Agent 29 | } 30 | 31 | // multiLoader should be used when you have multiple agents 32 | type multiLoader struct { 33 | agentMap map[string]Agent 34 | root Agent 35 | } 36 | 37 | // singleLoader should be used when you have only one agent 38 | type singleLoader struct { 39 | root Agent 40 | } 41 | 42 | // NewSingleLoader returns a loader with only one agent, which becomes the root agent 43 | func NewSingleLoader(a Agent) Loader { 44 | return &singleLoader{root: a} 45 | } 46 | 47 | // singleAgentLoader implements AgentLoader. Returns root agent's name 48 | func (s *singleLoader) ListAgents() []string { 49 | return []string{s.root.Name()} 50 | } 51 | 52 | // singleAgentLoader implements AgentLoader. Returns root for empty name and for root.Name(), error otherwise. 53 | func (s *singleLoader) LoadAgent(name string) (Agent, error) { 54 | if name == "" { 55 | return s.root, nil 56 | } 57 | if name == s.root.Name() { 58 | return s.root, nil 59 | } 60 | return nil, fmt.Errorf("cannot load agent '%s' - provide an empty string or use '%s'", name, s.root.Name()) 61 | } 62 | 63 | // singleAgentLoader implements AgentLoader. Returns the root agent. 64 | func (s *singleLoader) RootAgent() Agent { 65 | return s.root 66 | } 67 | 68 | // NewMultiLoader returns a new AgentLoader with the given root Agent and other agents. 69 | // Returns an error if more than one agent (including root) shares the same name 70 | func NewMultiLoader(root Agent, agents ...Agent) (Loader, error) { 71 | m := make(map[string]Agent) 72 | m[root.Name()] = root 73 | for _, a := range agents { 74 | if _, ok := m[a.Name()]; ok { 75 | // duplicate name 76 | return nil, fmt.Errorf("duplicate agent name: %s", a.Name()) 77 | } 78 | m[a.Name()] = a 79 | } 80 | return &multiLoader{ 81 | agentMap: m, 82 | root: root, 83 | }, nil 84 | } 85 | 86 | // multiAgentLoader implements AgentLoader. Returns the list of all agents' names (including root agent) 87 | func (m *multiLoader) ListAgents() []string { 88 | agents := make([]string, 0, len(m.agentMap)) 89 | for name := range m.agentMap { 90 | agents = append(agents, name) 91 | } 92 | return agents 93 | } 94 | 95 | // multiAgentLoader implements LoadAgent. Returns an agent with given name or error if no such an agent is found 96 | func (m *multiLoader) LoadAgent(name string) (Agent, error) { 97 | agent, ok := m.agentMap[name] 98 | if !ok { 99 | return nil, fmt.Errorf("agent %s not found. Please specify one of those: %v", name, m.ListAgents()) 100 | } 101 | return agent, nil 102 | } 103 | 104 | // multiAgentLoader implements LoadAgent. 105 | func (m *multiLoader) RootAgent() Agent { 106 | return m.root 107 | } 108 | -------------------------------------------------------------------------------- /server/adkrest/internal/models/event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 models 16 | 17 | import ( 18 | "time" 19 | 20 | "google.golang.org/genai" 21 | 22 | "google.golang.org/adk/model" 23 | "google.golang.org/adk/session" 24 | ) 25 | 26 | // EventActions represent a data model for session.EventActions 27 | type EventActions struct { 28 | StateDelta map[string]any `json:"stateDelta"` 29 | ArtifactDelta map[string]int64 `json:"artifactDelta"` 30 | } 31 | 32 | // Event represents a single event in a session. 33 | type Event struct { 34 | ID string `json:"id"` 35 | Time int64 `json:"time"` 36 | InvocationID string `json:"invocationId"` 37 | Branch string `json:"branch"` 38 | Author string `json:"author"` 39 | Partial bool `json:"partial"` 40 | LongRunningToolIDs []string `json:"longRunningToolIds"` 41 | Content *genai.Content `json:"content"` 42 | GroundingMetadata *genai.GroundingMetadata `json:"groundingMetadata"` 43 | TurnComplete bool `json:"turnComplete"` 44 | Interrupted bool `json:"interrupted"` 45 | ErrorCode string `json:"errorCode"` 46 | ErrorMessage string `json:"errorMessage"` 47 | Actions EventActions `json:"actions"` 48 | } 49 | 50 | // ToSessionEvent maps Event data struct to session.Event 51 | func ToSessionEvent(event Event) *session.Event { 52 | return &session.Event{ 53 | ID: event.ID, 54 | Timestamp: time.Unix(event.Time, 0), 55 | InvocationID: event.InvocationID, 56 | Branch: event.Branch, 57 | Author: event.Author, 58 | LongRunningToolIDs: event.LongRunningToolIDs, 59 | LLMResponse: model.LLMResponse{ 60 | Content: event.Content, 61 | GroundingMetadata: event.GroundingMetadata, 62 | Partial: event.Partial, 63 | TurnComplete: event.TurnComplete, 64 | Interrupted: event.Interrupted, 65 | ErrorCode: event.ErrorCode, 66 | ErrorMessage: event.ErrorMessage, 67 | }, 68 | Actions: session.EventActions{ 69 | StateDelta: event.Actions.StateDelta, 70 | ArtifactDelta: event.Actions.ArtifactDelta, 71 | }, 72 | } 73 | } 74 | 75 | // FromSessionEvent maps session.Event to Event data struct 76 | func FromSessionEvent(event session.Event) Event { 77 | return Event{ 78 | ID: event.ID, 79 | Time: event.Timestamp.Unix(), 80 | InvocationID: event.InvocationID, 81 | Branch: event.Branch, 82 | Author: event.Author, 83 | Partial: event.Partial, 84 | LongRunningToolIDs: event.LongRunningToolIDs, 85 | Content: event.LLMResponse.Content, 86 | GroundingMetadata: event.LLMResponse.GroundingMetadata, 87 | TurnComplete: event.LLMResponse.TurnComplete, 88 | Interrupted: event.LLMResponse.Interrupted, 89 | ErrorCode: event.LLMResponse.ErrorCode, 90 | ErrorMessage: event.LLMResponse.ErrorMessage, 91 | Actions: EventActions{ 92 | StateDelta: event.Actions.StateDelta, 93 | ArtifactDelta: event.Actions.ArtifactDelta, 94 | }, 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/tools/multipletools/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 demonstrates a workaround for using Google Search tool with other tools. 16 | package main 17 | 18 | import ( 19 | "context" 20 | "log" 21 | "os" 22 | "strings" 23 | 24 | "google.golang.org/genai" 25 | 26 | "google.golang.org/adk/agent" 27 | "google.golang.org/adk/agent/llmagent" 28 | "google.golang.org/adk/cmd/launcher" 29 | "google.golang.org/adk/cmd/launcher/full" 30 | "google.golang.org/adk/model/gemini" 31 | "google.golang.org/adk/tool" 32 | "google.golang.org/adk/tool/agenttool" 33 | "google.golang.org/adk/tool/functiontool" 34 | "google.golang.org/adk/tool/geminitool" 35 | ) 36 | 37 | // Package main demonstrates a workaround for using multiple tool types (e.g., 38 | // Google Search and custom functions) in a single agent. This is necessary 39 | // due to limitations in the genai API. The approach is to wrap agents with 40 | // different tool types into sub-agents, which are then managed by a root agent. 41 | func main() { 42 | ctx := context.Background() 43 | 44 | model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{ 45 | APIKey: os.Getenv("GOOGLE_API_KEY"), 46 | }) 47 | if err != nil { 48 | log.Fatalf("Failed to create model: %v", err) 49 | } 50 | 51 | searchAgent, err := llmagent.New(llmagent.Config{ 52 | Name: "search_agent", 53 | Model: model, 54 | Description: "Does google search.", 55 | Instruction: "You're a specialist in Google Search.", 56 | Tools: []tool.Tool{ 57 | geminitool.GoogleSearch{}, 58 | }, 59 | }) 60 | if err != nil { 61 | log.Fatalf("Failed to create agent: %v", err) 62 | } 63 | 64 | type Input struct { 65 | LineCount int `json:"lineCount"` 66 | } 67 | type Output struct { 68 | Poem string `json:"poem"` 69 | } 70 | handler := func(ctx tool.Context, input Input) (Output, error) { 71 | return Output{ 72 | Poem: strings.Repeat("A line of a poem,", input.LineCount) + "\n", 73 | }, nil 74 | } 75 | poemTool, err := functiontool.New(functiontool.Config{ 76 | Name: "poem", 77 | Description: "Returns poem", 78 | }, handler) 79 | if err != nil { 80 | log.Fatalf("Failed to create tool: %v", err) 81 | } 82 | poemAgent, err := llmagent.New(llmagent.Config{ 83 | Name: "poem_agent", 84 | Model: model, 85 | Description: "returns poem", 86 | Instruction: "You return poems.", 87 | Tools: []tool.Tool{ 88 | poemTool, 89 | }, 90 | }) 91 | if err != nil { 92 | log.Fatalf("Failed to create agent: %v", err) 93 | } 94 | 95 | a, err := llmagent.New(llmagent.Config{ 96 | Name: "root_agent", 97 | Model: model, 98 | Description: "You can do a google search and generate poems.", 99 | Instruction: "Answer questions about weather based on google search unless asked for a poem," + 100 | " for a poem generate it with a tool.", 101 | Tools: []tool.Tool{ 102 | agenttool.New(searchAgent, nil), agenttool.New(poemAgent, nil), 103 | }, 104 | }) 105 | if err != nil { 106 | log.Fatalf("Failed to create agent: %v", err) 107 | } 108 | 109 | config := &launcher.Config{ 110 | AgentLoader: agent.NewSingleLoader(a), 111 | } 112 | 113 | l := full.NewLauncher() 114 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 115 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/web/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 main 16 | 17 | import ( 18 | "context" 19 | "log" 20 | "os" 21 | 22 | "github.com/a2aproject/a2a-go/a2asrv" 23 | "github.com/google/uuid" 24 | "google.golang.org/genai" 25 | 26 | "google.golang.org/adk/agent" 27 | "google.golang.org/adk/agent/llmagent" 28 | "google.golang.org/adk/artifact" 29 | "google.golang.org/adk/cmd/launcher" 30 | "google.golang.org/adk/cmd/launcher/full" 31 | "google.golang.org/adk/examples/web/agents" 32 | "google.golang.org/adk/model" 33 | "google.golang.org/adk/model/gemini" 34 | "google.golang.org/adk/session" 35 | "google.golang.org/adk/tool" 36 | "google.golang.org/adk/tool/geminitool" 37 | ) 38 | 39 | func saveReportfunc(ctx agent.CallbackContext, llmResponse *model.LLMResponse, llmResponseError error) (*model.LLMResponse, error) { 40 | if llmResponse == nil || llmResponse.Content == nil || llmResponseError != nil { 41 | return llmResponse, llmResponseError 42 | } 43 | for _, part := range llmResponse.Content.Parts { 44 | _, err := ctx.Artifacts().Save(ctx, uuid.NewString(), part) 45 | if err != nil { 46 | return nil, err 47 | } 48 | } 49 | return llmResponse, llmResponseError 50 | } 51 | 52 | // AuthInterceptor sets 'user' name needed for both a2a and webui launchers which sharing the same sessions service. 53 | type AuthInterceptor struct { 54 | a2asrv.PassthroughCallInterceptor 55 | } 56 | 57 | // Before implements a before request callback. 58 | func (a *AuthInterceptor) Before(ctx context.Context, callCtx *a2asrv.CallContext, req *a2asrv.Request) (context.Context, error) { 59 | callCtx.User = &a2asrv.AuthenticatedUser{ 60 | UserName: "user", 61 | } 62 | return ctx, nil 63 | } 64 | 65 | func main() { 66 | ctx := context.Background() 67 | apiKey := os.Getenv("GOOGLE_API_KEY") 68 | 69 | model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{ 70 | APIKey: apiKey, 71 | }) 72 | if err != nil { 73 | log.Fatalf("Failed to create model: %v", err) 74 | } 75 | sessionService := session.InMemoryService() 76 | rootAgent, err := llmagent.New(llmagent.Config{ 77 | Name: "weather_time_agent", 78 | Model: model, 79 | Description: "Agent to answer questions about the time and weather in a city.", 80 | Instruction: "I can answer your questions about the time and weather in a city.", 81 | Tools: []tool.Tool{ 82 | geminitool.GoogleSearch{}, 83 | }, 84 | AfterModelCallbacks: []llmagent.AfterModelCallback{saveReportfunc}, 85 | }) 86 | if err != nil { 87 | log.Fatalf("Failed to create agent: %v", err) 88 | } 89 | llmAuditor := agents.GetLLMAuditorAgent(ctx, model) 90 | imageGeneratorAgent := agents.GetImageGeneratorAgent(ctx, model) 91 | 92 | agentLoader, err := agent.NewMultiLoader( 93 | rootAgent, 94 | llmAuditor, 95 | imageGeneratorAgent, 96 | ) 97 | if err != nil { 98 | log.Fatalf("Failed to create agent loader: %v", err) 99 | } 100 | 101 | artifactservice := artifact.InMemoryService() 102 | 103 | config := &launcher.Config{ 104 | ArtifactService: artifactservice, 105 | SessionService: sessionService, 106 | AgentLoader: agentLoader, 107 | A2AOptions: []a2asrv.RequestHandlerOption{ 108 | a2asrv.WithCallInterceptor(&AuthInterceptor{}), 109 | }, 110 | } 111 | 112 | l := full.NewLauncher() 113 | if err = l.Execute(ctx, config, os.Args[1:]); err != nil { 114 | log.Fatalf("Run failed: %v\n\n%s", err, l.CommandLineSyntax()) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /agent/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 agent 16 | 17 | import ( 18 | "context" 19 | 20 | "google.golang.org/genai" 21 | 22 | "google.golang.org/adk/session" 23 | ) 24 | 25 | /* 26 | InvocationContext represents the context of an agent invocation. 27 | 28 | An invocation: 29 | 1. Starts with a user message and ends with a final response. 30 | 2. Can contain one or multiple agent calls. 31 | 3. Is handled by runner.Run(). 32 | 33 | An invocation runs an agent until it does not request to transfer to another 34 | agent. 35 | 36 | An agent call: 37 | 1. Is handled by agent.Run(). 38 | 2. Ends when agent.Run() ends. 39 | 40 | An agent call can contain one or multiple steps. 41 | For example, LLM agent runs steps in a loop until: 42 | 1. A final response is generated. 43 | 2. The agent transfers to another agent. 44 | 3. EndInvocation() was called by the invocation context. 45 | 46 | A step: 47 | 1. Calls the LLM only once and yields its response. 48 | 2. Calls the tools and yields their responses if requested. 49 | 50 | The summarization of the function response is considered another step, since 51 | it is another LLM call. 52 | A step ends when it's done calling LLM and tools, or if the EndInvocation() was 53 | called by invocation context at any time. 54 | 55 | ┌─────────────────────── invocation ──────────────────────────┐ 56 | ┌──────────── llm_agent_call_1 ────────────┐ ┌─ agent_call_2 ─┐ 57 | ┌──── step_1 ────────┐ ┌───── step_2 ──────┐ 58 | [call_llm] [call_tool] [call_llm] [transfer] 59 | */ 60 | type InvocationContext interface { 61 | context.Context 62 | 63 | // Agent of this invocation context. 64 | Agent() Agent 65 | 66 | // Artifacts of the current session. 67 | Artifacts() Artifacts 68 | 69 | // Memory is scoped to sessions of the current user_id. 70 | Memory() Memory 71 | 72 | // Session of the current invocation context. 73 | Session() session.Session 74 | 75 | InvocationID() string 76 | 77 | // Branch of the invocation context. 78 | // The format is like agent_1.agent_2.agent_3, where agent_1 is the parent 79 | // of agent_2, and agent_2 is the parent of agent_3. 80 | // 81 | // Branch is used when multiple sub-agents shouldn't see their peer agents' 82 | // conversation history. 83 | // 84 | // Applicable to parallel agent because its sub-agents run concurrently. 85 | Branch() string 86 | 87 | // UserContent that started this invocation. 88 | UserContent() *genai.Content 89 | 90 | // RunConfig stores the runtime configuration used during this invocation. 91 | RunConfig() *RunConfig 92 | 93 | // EndInvocation ends the current invocation. This stops any planned agent 94 | // calls. 95 | EndInvocation() 96 | // Ended returns whether the invocation has ended. 97 | Ended() bool 98 | } 99 | 100 | // ReadonlyContext provides read-only access to invocation context data. 101 | type ReadonlyContext interface { 102 | context.Context 103 | 104 | // UserContent that started this invocation. 105 | UserContent() *genai.Content 106 | InvocationID() string 107 | AgentName() string 108 | ReadonlyState() session.ReadonlyState 109 | 110 | UserID() string 111 | AppName() string 112 | SessionID() string 113 | // Branch of the current invocation. 114 | Branch() string 115 | } 116 | 117 | // CallbackContext is passed to user callbacks during agent execution. 118 | type CallbackContext interface { 119 | ReadonlyContext 120 | 121 | Artifacts() Artifacts 122 | State() session.State 123 | } 124 | -------------------------------------------------------------------------------- /internal/artifact/artifacts_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 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 artifact_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | "google.golang.org/genai" 22 | 23 | "google.golang.org/adk/artifact" 24 | artifactinternal "google.golang.org/adk/internal/artifact" 25 | ) 26 | 27 | func TestArtifacts(t *testing.T) { 28 | a := artifactinternal.Artifacts{ 29 | Service: artifact.InMemoryService(), 30 | AppName: "testApp", 31 | UserID: "testUser", 32 | SessionID: "testSession", 33 | } 34 | 35 | // Save 36 | part := genai.NewPartFromText("test data") 37 | _, err := a.Save(t.Context(), "testArtifact", part) 38 | if err != nil { 39 | t.Fatalf("Save failed: %v", err) 40 | } 41 | 42 | // Load 43 | loadResp, err := a.Load(t.Context(), "testArtifact") 44 | if err != nil { 45 | t.Fatalf("Load failed: %v", err) 46 | } 47 | 48 | if diff := cmp.Diff(part, loadResp.Part); diff != "" { 49 | t.Errorf("Loaded part differs from saved part (-want +got):\n%s", diff) 50 | } 51 | 52 | // List 53 | listResp, err := a.List(t.Context()) 54 | if err != nil { 55 | t.Fatalf("List failed: %v", err) 56 | } 57 | 58 | expectedFileNames := []string{"testArtifact"} 59 | if diff := cmp.Diff(expectedFileNames, listResp.FileNames); diff != "" { 60 | t.Errorf("List returned unexpected file names (-want +got):\n%s", diff) 61 | } 62 | } 63 | 64 | func TestArtifacts_WithLoadVersion(t *testing.T) { 65 | a := artifactinternal.Artifacts{ 66 | Service: artifact.InMemoryService(), 67 | AppName: "testApp", 68 | UserID: "testUser", 69 | SessionID: "testSession", 70 | } 71 | 72 | part := genai.NewPartFromText("test data") 73 | _, err := a.Save(t.Context(), "testArtifact", part) 74 | if err != nil { 75 | t.Fatalf("Save failed: %v", err) 76 | } 77 | part2 := genai.NewPartFromText("test data 2") 78 | _, err = a.Save(t.Context(), "testArtifact", part2) 79 | if err != nil { 80 | t.Fatalf("Save failed: %v", err) 81 | } 82 | 83 | loadResp, err := a.LoadVersion(t.Context(), "testArtifact", 0) 84 | if err != nil { 85 | t.Fatalf("Load failed: %v", err) 86 | } 87 | 88 | if diff := cmp.Diff(part2, loadResp.Part); diff != "" { 89 | t.Errorf("Loaded part differs from saved part (-want +got):\n%s", diff) 90 | } 91 | } 92 | 93 | func TestArtifacts_Errors(t *testing.T) { 94 | a := artifactinternal.Artifacts{ 95 | Service: artifact.InMemoryService(), 96 | AppName: "testApp", 97 | UserID: "testUser", 98 | SessionID: "testSession", 99 | } 100 | 101 | // Attempt to Load non-existent artifact 102 | _, err := a.Load(t.Context(), "nonExistentArtifact") 103 | if err == nil { 104 | t.Errorf("Load(\"nonExistentArtifact\") succeeded, want error") 105 | } 106 | 107 | // Attempt to LoadVersion non-existent artifact 108 | _, err = a.LoadVersion(t.Context(), "nonExistentArtifact", 0) 109 | if err == nil { 110 | t.Errorf("LoadVersion(\"nonExistentArtifact\", 0) succeeded, want error") 111 | } 112 | 113 | // Save an artifact to test LoadVersion with an invalid version 114 | part := genai.NewPartFromText("test data") 115 | if _, err := a.Save(t.Context(), "existsArtifact", part); err != nil { 116 | t.Fatalf("Save(\"existsArtifact\") failed: %v", err) 117 | } 118 | 119 | // Attempt to LoadVersion with a version number that doesn't exist 120 | _, err = a.LoadVersion(t.Context(), "existsArtifact", 99) 121 | if err == nil { 122 | t.Errorf("LoadVersion(\"existsArtifact\", 99) succeeded, want error") 123 | } 124 | } 125 | --------------------------------------------------------------------------------