From 32a4f2d14ef2c7302f48affebabdf4967fdc6383 Mon Sep 17 00:00:00 2001 From: mekkiamiri Date: Mon, 27 Jul 2026 15:28:14 +0200 Subject: [PATCH] feat(spring-boot-starter): add Spring Boot auto-configuration starter with sample app --- contrib/samples/pom.xml | 1 + .../spring-boot-adk-template/README.md | 55 ++++++ .../samples/spring-boot-adk-template/pom.xml | 67 +++++++ .../springbootadktemplate/AgentService.java | 24 +++ .../SpringBootAdkTemplateApplication.java | 21 +++ .../config/AgentConfig.java | 42 +++++ .../src/main/resources/application.yaml | 24 +++ .../SpringBootAdkTemplateApplicationTest.java | 36 ++++ contrib/spring-boot-starter/README.md | 174 ++++++++++++++++++ contrib/spring-boot-starter/pom.xml | 71 +++++++ .../AdkArtifactsAutoConfiguration.java | 61 ++++++ .../AdkFirestoreAutoConfiguration.java | 48 +++++ .../AdkFirestoreMemoryAutoConfiguration.java | 46 +++++ .../AdkFirestoreSessionAutoConfiguration.java | 47 +++++ .../AdkMemoryAutoConfiguration.java | 55 ++++++ .../AdkRunConfigAutoConfiguration.java | 35 ++++ .../AdkRunnerAutoConfiguration.java | 61 ++++++ .../AdkSessionAutoConfiguration.java | 69 +++++++ .../properties/AdkArtifactProperties.java | 25 +++ .../properties/AdkFirestoreProperties.java | 44 +++++ .../properties/AdkMemoryProperties.java | 42 +++++ .../properties/AdkRunConfigProperties.java | 82 +++++++++ .../properties/AdkSessionProperties.java | 66 +++++++ ...ot.autoconfigure.AutoConfiguration.imports | 8 + pom.xml | 1 + 25 files changed, 1205 insertions(+) create mode 100644 contrib/samples/spring-boot-adk-template/README.md create mode 100644 contrib/samples/spring-boot-adk-template/pom.xml create mode 100644 contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/AgentService.java create mode 100644 contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplication.java create mode 100644 contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/config/AgentConfig.java create mode 100644 contrib/samples/spring-boot-adk-template/src/main/resources/application.yaml create mode 100644 contrib/samples/spring-boot-adk-template/src/test/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplicationTest.java create mode 100644 contrib/spring-boot-starter/README.md create mode 100644 contrib/spring-boot-starter/pom.xml create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkArtifactsAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreMemoryAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreSessionAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkMemoryAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunConfigAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunnerAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkSessionAutoConfiguration.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkArtifactProperties.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkFirestoreProperties.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkMemoryProperties.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkRunConfigProperties.java create mode 100644 contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkSessionProperties.java create mode 100644 contrib/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/contrib/samples/pom.xml b/contrib/samples/pom.xml index a926d3b85..2d6c9482b 100644 --- a/contrib/samples/pom.xml +++ b/contrib/samples/pom.xml @@ -27,5 +27,6 @@ github/adktriaging helloworld mcpfilesystem + spring-boot-adk-template diff --git a/contrib/samples/spring-boot-adk-template/README.md b/contrib/samples/spring-boot-adk-template/README.md new file mode 100644 index 000000000..b1b8bd864 --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/README.md @@ -0,0 +1,55 @@ +# Spring Boot ADK Template + +Sample Spring Boot application demonstrating the **`google-adk-spring-boot-starter`** in its simplest form. The user code consists of: + +- One `@Bean LlmAgent rootAgent()` — the agent topology +- One `@Bean App` — wraps the root agent under an app name +- One `@Service AgentService` — illustrates injection of the starter-provided `Runner` + +Everything else (`Runner`, `BaseSessionService`, `BaseArtifactService`, `RunConfig`) is wired by the starter. + +## Prerequisites + +- Java 17 +- Maven +- This module is built as part of the ADK aggregator; no separate install of the starter is required. + +## Build and run + +From the repository root: + +```bash +mvn -pl contrib/samples/spring-boot-adk-template -am verify +``` + +To run interactively: + +```bash +mvn -pl contrib/samples/spring-boot-adk-template -am spring-boot:run +``` + +## Configuration + +`src/main/resources/application.yaml` shows the full property surface as comments. The defaults give an all-in-memory configuration with no GCP credentials required — switch backends by uncommenting the relevant blocks (`adk.session.type=VERTEX_AI`, `adk.session.type=FIRESTORE`, `adk.artifacts.gcs-enabled=true`, etc.). + +See the [starter README](../../spring-boot-starter/README.md) for the full property reference. + +## Use Spring AI as the LLM substrate (optional) + +This sample uses a string model name (`"gemini-2.5-flash"`). To swap to Spring AI's `ChatModel` ecosystem (OpenAI, Anthropic, Gemini, Ollama, Vertex AI, Azure OpenAI, Bedrock): + +1. Add `com.google.adk:google-adk-spring-ai` and your preferred Spring AI provider artifact to `pom.xml`. +2. Configure the provider via `spring.ai.*` properties (e.g. `spring.ai.openai.api-key`). +3. Inject the auto-configured `SpringAI` bean into `AgentConfig.rootAgent()` and pass it to `.model(...)`. + +## Project structure + +``` +src/main/java/com/example/springbootadktemplate/ +├── SpringBootAdkTemplateApplication.java — @SpringBootApplication entry point +├── AgentService.java — sample @Service injecting Runner + LlmAgent +└── config/AgentConfig.java — @Bean LlmAgent + @Bean App +src/main/resources/application.yaml — spring.application.name + (commented) adk.* properties +src/test/java/com/example/springbootadktemplate/ +└── SpringBootAdkTemplateApplicationTest.java — context-load test asserting every starter bean is reachable +``` diff --git a/contrib/samples/spring-boot-adk-template/pom.xml b/contrib/samples/spring-boot-adk-template/pom.xml new file mode 100644 index 000000000..6a82ace58 --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + + com.google.adk + google-adk-samples + 1.7.2-SNAPSHOT + .. + + + com.google.adk.samples + google-adk-sample-spring-boot-template + Google ADK - Sample - Spring Boot Template + Spring Boot template demonstrating the google-adk-spring-boot-starter. + jar + + + UTF-8 + 17 + com.example.springbootadktemplate.SpringBootAdkTemplateApplication + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + com.google.adk + google-adk-spring-boot-starter + ${project.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${exec.mainClass} + + + + + diff --git a/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/AgentService.java b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/AgentService.java new file mode 100644 index 000000000..1f0215a96 --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/AgentService.java @@ -0,0 +1,24 @@ +package com.example.springbootadktemplate; + +import com.google.adk.agents.LlmAgent; +import com.google.adk.runner.Runner; +import org.springframework.stereotype.Service; + +@Service +public class AgentService { + + private final Runner runner; + private final LlmAgent agent; + + public AgentService(Runner runner, LlmAgent agent) { + this.runner = runner; + this.agent = agent; + } + + public String getAgentInfo() { + return "Agent created: " + + agent.getClass().getSimpleName() + + ", Runner: " + + runner.getClass().getSimpleName(); + } +} diff --git a/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplication.java b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplication.java new file mode 100644 index 000000000..37e8577c0 --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplication.java @@ -0,0 +1,21 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.example.springbootadktemplate; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootAdkTemplateApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootAdkTemplateApplication.class, args); + } +} diff --git a/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/config/AgentConfig.java b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/config/AgentConfig.java new file mode 100644 index 000000000..8dce9415e --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/src/main/java/com/example/springbootadktemplate/config/AgentConfig.java @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.example.springbootadktemplate.config; + +import com.google.adk.agents.LlmAgent; +import com.google.adk.apps.App; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Sample agent topology — defines a root LlmAgent and packages it into an App. + * + *

The starter provides {@code Runner}, {@code BaseSessionService}, {@code BaseArtifactService}, + * and {@code RunConfig} automatically — this configuration only declares the user-specific agent + * topology. + */ +@Configuration +public class AgentConfig { + + @Bean + public LlmAgent rootAgent() { + return LlmAgent.builder() + .name("root_agent") + .description("Sample assistant agent.") + .model("gemini-2.5-flash") + .instruction("Answer user questions to the best of your knowledge.") + .build(); + } + + @Bean + public App app(LlmAgent rootAgent, @Value("${spring.application.name}") String appName) { + return App.builder().name(appName).rootAgent(rootAgent).build(); + } +} diff --git a/contrib/samples/spring-boot-adk-template/src/main/resources/application.yaml b/contrib/samples/spring-boot-adk-template/src/main/resources/application.yaml new file mode 100644 index 000000000..3f5f71e3c --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/src/main/resources/application.yaml @@ -0,0 +1,24 @@ +spring: + application: + name: spring_boot_adk_template # must be a valid identifier (App#validateAppName regex) + +# Defaults — services are in-memory unless these are uncommented. +# adk: +# artifacts: +# gcs-enabled: false +# # bucket-name: my-bucket +# session: +# type: IN_MEMORY # IN_MEMORY | VERTEX_AI | FIRESTORE +# # project-id: my-project +# # location: us-central1 +# memory: +# type: IN_MEMORY # IN_MEMORY | FIRESTORE +# run-config: +# streaming-mode: NONE # NONE | SSE | BIDI +# max-llm-calls: 500 +# tool-execution-mode: NONE # NONE | SEQUENTIAL | PARALLEL | PARALLEL_SUBSCRIBE +# save-input-blobs-as-artifacts: false +# auto-create-session: true +# firestore: +# # project-id: my-gcp-project +# # database-id: "(default)" diff --git a/contrib/samples/spring-boot-adk-template/src/test/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplicationTest.java b/contrib/samples/spring-boot-adk-template/src/test/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplicationTest.java new file mode 100644 index 000000000..1d4f7bb7d --- /dev/null +++ b/contrib/samples/spring-boot-adk-template/src/test/java/com/example/springbootadktemplate/SpringBootAdkTemplateApplicationTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.example.springbootadktemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.RunConfig; +import com.google.adk.apps.App; +import com.google.adk.artifacts.BaseArtifactService; +import com.google.adk.runner.Runner; +import com.google.adk.sessions.BaseSessionService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; + +@SpringBootTest +class SpringBootAdkTemplateApplicationTest { + + @Test + void contextLoadsAndStarterBeansArePresent(ApplicationContext ctx) { + assertThat(ctx.getBean(LlmAgent.class)).isNotNull(); + assertThat(ctx.getBean(App.class).name()).isEqualTo("spring_boot_adk_template"); + assertThat(ctx.getBean(BaseArtifactService.class)).isNotNull(); + assertThat(ctx.getBean(BaseSessionService.class)).isNotNull(); + assertThat(ctx.getBean(RunConfig.class)).isNotNull(); + assertThat(ctx.getBean(Runner.class).appName()).isEqualTo("spring_boot_adk_template"); + } +} diff --git a/contrib/spring-boot-starter/README.md b/contrib/spring-boot-starter/README.md new file mode 100644 index 000000000..815ed9869 --- /dev/null +++ b/contrib/spring-boot-starter/README.md @@ -0,0 +1,174 @@ +# ADK Spring Boot Starter + +Spring Boot auto-configuration for the [Agent Development Kit (ADK)](https://github.com/google/adk-java) runtime. + +After adding this starter, declare a single `@Bean App` (your agent topology) and the starter wires the rest: `Runner`, `BaseSessionService`, `BaseArtifactService`, optionally `BaseMemoryService`, and `RunConfig`. The starter is **LLM-agnostic** — pair it with `google-adk-spring-ai` for Spring AI LLMs, or declare your own `@Bean BaseLlm`. + +## Installation + +```xml + + com.google.adk + google-adk-spring-boot-starter + ${adk.version} + + + + + com.google.adk + google-adk-spring-ai + ${adk.version} + +``` + +The starter declares `google-adk-firestore-session-service` as an `true` dependency. It is on your runtime classpath by default — explicitly exclude it if you do not want Firestore-related auto-configurations to activate. + +## Quick Start + +```java +@SpringBootApplication +class MyApp { + public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } +} + +@Configuration +class MyAgents { + @Bean public LlmAgent rootAgent(SpringAI llm) { // SpringAI bean comes from google-adk-spring-ai + return LlmAgent.builder() + .name("root_agent") + .model(llm) + .instruction("Answer concisely.") + .build(); + } + @Bean public App app(LlmAgent rootAgent, + @Value("${spring.application.name}") String appName) { + return App.builder().name(appName).rootAgent(rootAgent).build(); + } +} + +@RestController +class ChatController { + private final Runner runner; // provided by starter + private final RunConfig runConfig; // provided by starter + private final BaseSessionService sessionService; // provided by starter + ChatController(Runner r, RunConfig c, BaseSessionService s) { + this.runner = r; this.runConfig = c; this.sessionService = s; + } + @PostMapping("/chat") String chat(@RequestBody String prompt) { + String userId = "alice"; + String sessionId = UUID.randomUUID().toString(); + sessionService.createSession(runner.appName(), userId, null, sessionId).blockingGet(); + Content msg = Content.builder().role("user").parts(List.of(Part.builder().text(prompt).build())).build(); + return runner.runAsync(userId, sessionId, msg, runConfig) + .toList().blockingGet() + .stream().map(Event::stringifyContent).collect(joining()); + } +} +``` + +## Property Reference + +```yaml +spring: + application: + name: my_app # used as the App's appName (must match validateAppName regex) + +adk: + artifacts: + gcs-enabled: false # default; switch to true to use Google Cloud Storage + # bucket-name: my-artifacts-bucket # required when gcs-enabled=true (fail-fast otherwise) + + session: + type: IN_MEMORY # IN_MEMORY | VERTEX_AI | FIRESTORE + # project-id: my-gcp-project # required for VERTEX_AI + # location: us-central1 # required for VERTEX_AI + + memory: + type: IN_MEMORY # IN_MEMORY | FIRESTORE + # VERTEX_AI is reserved — fails fast (no impl exists in ADK) + + run-config: + streaming-mode: NONE # NONE | SSE | BIDI + max-llm-calls: 500 + tool-execution-mode: NONE # NONE | SEQUENTIAL | PARALLEL | PARALLEL_SUBSCRIBE + save-input-blobs-as-artifacts: false + auto-create-session: false + + firestore: # only consulted when session/memory type=FIRESTORE + # project-id: my-gcp-project # optional — falls back to ADC project + # database-id: "(default)" # optional — falls back to "(default)" +``` + +## Persistence Backends + +| Concern | `IN_MEMORY` | `VERTEX_AI` | `FIRESTORE` | GCS (artifacts only) | +|------------|-------------|------------------------------------|--------------------------------------------|----------------------| +| Sessions | default | `VertexAiSessionService` (managed) | `FirestoreSessionService` (contrib module) | — | +| Memory | default | n/a — fails fast | `FirestoreMemoryService` (contrib module) | — | +| Artifacts | default | n/a | n/a | `GcsArtifactService` | + +The Firestore branches activate only when the `google-adk-firestore-session-service` module is on the classpath (gated by `@ConditionalOnClass`). The starter declares it as an optional dependency — exclude it from your application pom if you want to keep Firestore wiring off the classpath entirely. + +## Multiple business-unit agents in one Spring Boot app + +ADK's `appName` is the partition key for sessions, memory, and artifacts. A single Spring Boot app can host multiple independent agentic applications (one per business unit) by declaring multiple `@Bean App`. The starter's `@Bean Runner` auto-config uses `@ConditionalOnSingleCandidate(App.class)`, so it steps aside cleanly when multiple Apps exist — wire one `Runner` per `App` explicitly: + +```java +@Configuration +class BusinessUnits { + + @Bean App salesApp(SpringAI llm) { + return App.builder().name("sales").rootAgent(salesRootAgent(llm)).build(); + } + @Bean App supportApp(SpringAI llm) { + return App.builder().name("support").rootAgent(supportRootAgent(llm)).build(); + } + // ... per-BU rootAgent bean factories ... + + @Bean Runner salesRunner( + @Qualifier("salesApp") App app, + BaseArtifactService artifactService, + BaseSessionService sessionService) { + return Runner.builder().app(app).artifactService(artifactService).sessionService(sessionService).build(); + } + @Bean Runner supportRunner( + @Qualifier("supportApp") App app, + BaseArtifactService artifactService, + BaseSessionService sessionService) { + return Runner.builder().app(app).artifactService(artifactService).sessionService(sessionService).build(); + } +} +``` + +The shared `BaseSessionService`, `BaseArtifactService`, `BaseMemoryService`, and `RunConfig` beans are singletons partitioned internally by `appName`. No duplication — and no per-BU service wiring boilerplate. + +## Architecture + +Eight auto-configuration classes, each owning one concern: + +| Auto-config | Produces | Activation | +|------------------------------------------------|---------------------------------------|-------------------------------------------------------------------------------| +| `AdkArtifactsAutoConfiguration` | `BaseArtifactService`, conditional `Storage` | always; `Storage` only when `adk.artifacts.gcs-enabled=true` | +| `AdkSessionAutoConfiguration` | `BaseSessionService` (IN_MEMORY, VERTEX_AI) | always; FIRESTORE falls through to the Firestore variant when on classpath | +| `AdkFirestoreSessionAutoConfiguration` | `BaseSessionService` (FIRESTORE) | `@ConditionalOnClass(FirestoreSessionService.class)`; `@AutoConfigureBefore` | +| `AdkMemoryAutoConfiguration` | `BaseMemoryService` (IN_MEMORY) | always; FIRESTORE falls through; VERTEX_AI fails fast | +| `AdkFirestoreMemoryAutoConfiguration` | `BaseMemoryService` (FIRESTORE) | `@ConditionalOnClass(FirestoreMemoryService.class)`; `@AutoConfigureBefore` | +| `AdkRunConfigAutoConfiguration` | `RunConfig` | always | +| `AdkFirestoreAutoConfiguration` | `Firestore` client | `@ConditionalOnClass(Firestore.class)` | +| `AdkRunnerAutoConfiguration` | `Runner` | `@ConditionalOnBean(App.class)` + `@ConditionalOnSingleCandidate(App.class)` | + +Every `@Bean` factory uses `@ConditionalOnMissingBean` so any user-declared override (`Storage`, `Firestore`, `BaseSessionService`, `Runner`, etc.) always wins. + +## Failure Modes + +The starter fails fast at startup (throwing `BeanCreationException` with a clear remediation message) when: + +- `adk.artifacts.gcs-enabled=true` but `adk.artifacts.bucket-name` is blank. +- `adk.session.type=VERTEX_AI` but `project-id` or `location` is blank. +- `adk.session.type=FIRESTORE` but `google-adk-firestore-session-service` is not on the classpath. +- `adk.memory.type=VERTEX_AI` (no Vertex AI memory service exists in ADK today). +- `adk.memory.type=FIRESTORE` but the contrib jar is missing. + +## Roadmap — not in this module + +Bridges from Spring AI primitives into ADK service interfaces (`ChatMemory` → sessions, `VectorStore` → memory, `ToolCallback` → tools incl. MCP) are planned for `contrib/spring-ai` as a follow-up PR. Once they land, this starter will gain `SPRING_AI_CHAT_MEMORY` / `SPRING_AI_VECTOR_STORE` enum values that activate the bridges when the contrib classes are on the classpath. New artifact backends (S3, Azure Blob, filesystem) require new `BaseArtifactService` impls upstream in ADK first. diff --git a/contrib/spring-boot-starter/pom.xml b/contrib/spring-boot-starter/pom.xml new file mode 100644 index 000000000..96e8050fc --- /dev/null +++ b/contrib/spring-boot-starter/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + + com.google.adk + google-adk-parent + 1.7.2-SNAPSHOT + ../../pom.xml + + + google-adk-spring-boot-starter + Agent Development Kit - Spring Boot Starter + Spring Boot auto-configuration for the Agent Development Kit (ADK) runtime. + + + + + com.google.adk + google-adk + ${project.version} + + + + + com.google.adk + google-adk-firestore-session-service + ${project.version} + true + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + com.google.cloud + google-cloud-storage + + + + + org.springframework.boot + spring-boot-starter-test + test + + + diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkArtifactsAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkArtifactsAutoConfiguration.java new file mode 100644 index 000000000..15503cfa8 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkArtifactsAutoConfiguration.java @@ -0,0 +1,61 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.artifacts.BaseArtifactService; +import com.google.adk.artifacts.GcsArtifactService; +import com.google.adk.artifacts.InMemoryArtifactService; +import com.google.adk.autoconfigure.properties.AdkArtifactProperties; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import java.util.Optional; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Auto-configures the {@link BaseArtifactService} bean. + * + *

Default: {@link InMemoryArtifactService}. With {@code adk.artifacts.gcs-enabled=true}: {@link + * GcsArtifactService} backed by a {@link Storage} bean (user-supplied or auto-created via {@link + * StorageOptions#getDefaultInstance()}). + */ +@AutoConfiguration +@EnableConfigurationProperties(AdkArtifactProperties.class) +public class AdkArtifactsAutoConfiguration { + + @Bean + @ConditionalOnProperty(prefix = "adk.artifacts", name = "gcs-enabled", havingValue = "true") + @ConditionalOnMissingBean + public Storage googleCloudStorage() { + return StorageOptions.getDefaultInstance().getService(); + } + + @Bean + @ConditionalOnMissingBean + public BaseArtifactService artifactService( + AdkArtifactProperties properties, ObjectProvider storageProvider) { + if (!properties.isGcsEnabled()) { + return new InMemoryArtifactService(); + } + String bucketName = + Optional.ofNullable(properties.getBucketName()) + .filter(s -> !s.isBlank()) + .orElseThrow( + () -> + new BeanCreationException( + "adk.artifacts.bucket-name must be set when adk.artifacts.gcs-enabled=true")); + return new GcsArtifactService(bucketName, storageProvider.getObject()); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreAutoConfiguration.java new file mode 100644 index 000000000..20ae46c99 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreAutoConfiguration.java @@ -0,0 +1,48 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.autoconfigure.properties.AdkFirestoreProperties; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOptions; +import java.util.Optional; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Produces a {@link Firestore} client bean from {@code adk.firestore.*} properties when the + * Firestore client library is on the classpath and the user has not declared their own. + * + *

Active whenever {@code com.google.cloud.firestore.Firestore} is on the classpath — typically + * because the {@code google-adk-firestore-session-service} contrib module is a dependency. Users + * who already declare a {@link Firestore} bean (e.g. via Spring Cloud GCP) take precedence through + * {@code @ConditionalOnMissingBean}. + */ +@AutoConfiguration +@ConditionalOnClass(Firestore.class) +@EnableConfigurationProperties(AdkFirestoreProperties.class) +public class AdkFirestoreAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public Firestore firestore(AdkFirestoreProperties properties) { + FirestoreOptions.Builder builder = FirestoreOptions.newBuilder(); + Optional.ofNullable(properties.getProjectId()) + .filter(s -> !s.isBlank()) + .ifPresent(builder::setProjectId); + Optional.ofNullable(properties.getDatabaseId()) + .filter(s -> !s.isBlank()) + .ifPresent(builder::setDatabaseId); + return builder.build().getService(); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreMemoryAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreMemoryAutoConfiguration.java new file mode 100644 index 000000000..99e47636c --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreMemoryAutoConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.autoconfigure.properties.AdkMemoryProperties; +import com.google.adk.memory.BaseMemoryService; +import com.google.adk.memory.FirestoreMemoryService; +import com.google.adk.memory.InMemoryMemoryService; +import com.google.cloud.firestore.Firestore; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Firestore-backed {@link BaseMemoryService} variant. Active only when {@code + * FirestoreMemoryService} is on the classpath; runs before {@link + * AdkMemoryAutoConfiguration}. + */ +@AutoConfiguration +@AutoConfigureBefore(AdkMemoryAutoConfiguration.class) +@ConditionalOnClass(FirestoreMemoryService.class) +@EnableConfigurationProperties(AdkMemoryProperties.class) +public class AdkFirestoreMemoryAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public BaseMemoryService memoryService( + AdkMemoryProperties properties, ObjectProvider firestoreProvider) { + return switch (properties.getType()) { + case VERTEX_AI -> throw AdkMemoryAutoConfiguration.vertexAiUnsupported(); + case FIRESTORE -> new FirestoreMemoryService(firestoreProvider.getObject()); + case IN_MEMORY -> new InMemoryMemoryService(); + }; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreSessionAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreSessionAutoConfiguration.java new file mode 100644 index 000000000..1e0eca16c --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkFirestoreSessionAutoConfiguration.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.autoconfigure.properties.AdkSessionProperties; +import com.google.adk.sessions.BaseSessionService; +import com.google.adk.sessions.FirestoreSessionService; +import com.google.adk.sessions.InMemorySessionService; +import com.google.cloud.firestore.Firestore; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Firestore-backed {@link BaseSessionService} variant. Active only when {@code + * FirestoreSessionService} is on the classpath; runs before {@link + * AdkSessionAutoConfiguration} so the regular factory's {@code @ConditionalOnMissingBean} steps + * aside whenever this branch is eligible. + */ +@AutoConfiguration +@AutoConfigureBefore(AdkSessionAutoConfiguration.class) +@ConditionalOnClass(FirestoreSessionService.class) +@EnableConfigurationProperties(AdkSessionProperties.class) +public class AdkFirestoreSessionAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public BaseSessionService sessionService( + AdkSessionProperties properties, ObjectProvider firestoreProvider) { + return switch (properties.getType()) { + case VERTEX_AI -> AdkSessionAutoConfiguration.buildVertexAiSession(properties); + case FIRESTORE -> new FirestoreSessionService(firestoreProvider.getObject()); + case IN_MEMORY -> new InMemorySessionService(); + }; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkMemoryAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkMemoryAutoConfiguration.java new file mode 100644 index 000000000..e1916e9dc --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkMemoryAutoConfiguration.java @@ -0,0 +1,55 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.autoconfigure.properties.AdkMemoryProperties; +import com.google.adk.memory.BaseMemoryService; +import com.google.adk.memory.InMemoryMemoryService; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Auto-configures the {@link BaseMemoryService} bean for the {@code IN_MEMORY} backend. + * + *

{@code VERTEX_AI} fails fast — no Vertex AI memory service exists in ADK today. {@code + * FIRESTORE} is handled by {@code AdkFirestoreMemoryAutoConfiguration} when the contrib module is + * on the classpath. + */ +@AutoConfiguration +@EnableConfigurationProperties(AdkMemoryProperties.class) +public class AdkMemoryAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public BaseMemoryService memoryService(AdkMemoryProperties properties) { + return switch (properties.getType()) { + case VERTEX_AI -> throw vertexAiUnsupported(); + case FIRESTORE -> throw firestoreModuleMissing(); + case IN_MEMORY -> new InMemoryMemoryService(); + }; + } + + static BeanCreationException vertexAiUnsupported() { + return new BeanCreationException( + "adk.memory.type=VERTEX_AI is not supported — no Vertex AI memory service exists in ADK" + + " today. Use IN_MEMORY, FIRESTORE, or provide your own BaseMemoryService bean."); + } + + static BeanCreationException firestoreModuleMissing() { + return new BeanCreationException( + "adk.memory.type=FIRESTORE requires the 'google-adk-firestore-session-service' contrib" + + " module on the classpath. It is declared as an optional dependency by" + + " google-adk-spring-boot-starter — add it explicitly to your application pom if it" + + " was excluded."); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunConfigAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunConfigAutoConfiguration.java new file mode 100644 index 000000000..dd33cdc10 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunConfigAutoConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.agents.RunConfig; +import com.google.adk.autoconfigure.properties.AdkRunConfigProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** Auto-configures the {@link RunConfig} bean from {@code adk.run-config.*} properties. */ +@AutoConfiguration +@EnableConfigurationProperties(AdkRunConfigProperties.class) +public class AdkRunConfigAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public RunConfig runConfig(AdkRunConfigProperties properties) { + return RunConfig.builder() + .setStreamingMode(properties.getStreamingMode()) + .setMaxLlmCalls(properties.getMaxLlmCalls()) + .setToolExecutionMode(properties.getToolExecutionMode()) + .setSaveInputBlobsAsArtifacts(properties.isSaveInputBlobsAsArtifacts()) + .setAutoCreateSession(properties.isAutoCreateSession()) + .build(); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunnerAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunnerAutoConfiguration.java new file mode 100644 index 000000000..43487e449 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkRunnerAutoConfiguration.java @@ -0,0 +1,61 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.apps.App; +import com.google.adk.artifacts.BaseArtifactService; +import com.google.adk.memory.BaseMemoryService; +import com.google.adk.runner.Runner; +import com.google.adk.sessions.BaseSessionService; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.context.annotation.Bean; + +/** + * Auto-configures a {@link Runner} bean from a user-declared {@link App} plus the starter-provided + * service beans. + * + *

Single-BU case: when exactly one {@link App} bean exists in the context, this factory wires a + * single {@link Runner}. Multi-BU case: when multiple {@link App} beans exist (different business + * units in the same Spring Boot app), {@link ConditionalOnSingleCandidate} suppresses this factory + * and the user is expected to declare one explicit {@link Runner} per business unit. + * + *

The {@link BaseMemoryService} dependency is injected via {@link ObjectProvider} because it is + * optional — {@link Runner.Builder} accepts {@code null} memory and the starter does not always + * produce a memory bean. + */ +@AutoConfiguration( + after = { + AdkArtifactsAutoConfiguration.class, + AdkSessionAutoConfiguration.class, + AdkMemoryAutoConfiguration.class, + AdkFirestoreSessionAutoConfiguration.class, + AdkFirestoreMemoryAutoConfiguration.class + }) +public class AdkRunnerAutoConfiguration { + + @Bean + @ConditionalOnBean(App.class) + @ConditionalOnSingleCandidate(App.class) + @ConditionalOnMissingBean(Runner.class) + public Runner runner( + App app, + BaseArtifactService artifactService, + BaseSessionService sessionService, + ObjectProvider memoryServiceProvider) { + Runner.Builder builder = + Runner.builder().app(app).artifactService(artifactService).sessionService(sessionService); + memoryServiceProvider.ifAvailable(builder::memoryService); + return builder.build(); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkSessionAutoConfiguration.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkSessionAutoConfiguration.java new file mode 100644 index 000000000..7277e0a4c --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/AdkSessionAutoConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure; + +import com.google.adk.autoconfigure.properties.AdkSessionProperties; +import com.google.adk.sessions.BaseSessionService; +import com.google.adk.sessions.InMemorySessionService; +import com.google.adk.sessions.VertexAiSessionService; +import java.util.Optional; +import java.util.function.Supplier; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * Auto-configures the {@link BaseSessionService} bean for the {@code IN_MEMORY} and {@code + * VERTEX_AI} backends. + * + *

{@code FIRESTORE} is handled by {@code AdkFirestoreSessionAutoConfiguration} (gated by + * {@code @ConditionalOnClass(FirestoreSessionService.class)} and {@code @AutoConfigureBefore} this + * class — so when both are eligible, the Firestore branch wins). + */ +@AutoConfiguration +@EnableConfigurationProperties(AdkSessionProperties.class) +public class AdkSessionAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public BaseSessionService sessionService(AdkSessionProperties properties) { + return switch (properties.getType()) { + case VERTEX_AI -> buildVertexAiSession(properties); + case FIRESTORE -> throw firestoreModuleMissing(); + case IN_MEMORY -> new InMemorySessionService(); + }; + } + + static VertexAiSessionService buildVertexAiSession(AdkSessionProperties properties) { + String projectId = require(properties.getProjectId(), "adk.session.project-id"); + String location = require(properties.getLocation(), "adk.session.location"); + return new VertexAiSessionService(projectId, location, null, null); + } + + static String require(String value, String propertyKey) { + return Optional.ofNullable(value) + .filter(s -> !s.isBlank()) + .orElseThrow(missing(propertyKey + " must be set when adk.session.type=VERTEX_AI")); + } + + static Supplier missing(String message) { + return () -> new BeanCreationException(message); + } + + static BeanCreationException firestoreModuleMissing() { + return new BeanCreationException( + "adk.session.type=FIRESTORE requires the 'google-adk-firestore-session-service' contrib" + + " module on the classpath. It is declared as an optional dependency by" + + " google-adk-spring-boot-starter — add it explicitly to your application pom if it" + + " was excluded."); + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkArtifactProperties.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkArtifactProperties.java new file mode 100644 index 000000000..1f74b3eef --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkArtifactProperties.java @@ -0,0 +1,25 @@ +package com.google.adk.autoconfigure.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "adk.artifacts") +public class AdkArtifactProperties { + private boolean gcsEnabled = false; + private String bucketName; + + public boolean isGcsEnabled() { + return gcsEnabled; + } + + public void setGcsEnabled(boolean gcsEnabled) { + this.gcsEnabled = gcsEnabled; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkFirestoreProperties.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkFirestoreProperties.java new file mode 100644 index 000000000..ae3e6a942 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkFirestoreProperties.java @@ -0,0 +1,44 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for the {@code com.google.cloud.firestore.Firestore} client bean the + * starter creates when one of {@code adk.session.type=FIRESTORE} or {@code + * adk.memory.type=FIRESTORE} is requested. Both fields are optional — when unset the starter relies + * on Application Default Credentials and Firestore's {@code "(default)"} database. + */ +@ConfigurationProperties(prefix = "adk.firestore") +public class AdkFirestoreProperties { + + /** GCP project id. Optional — falls back to the ADC project. */ + private String projectId; + + /** Firestore database id. Optional — falls back to {@code "(default)"}. */ + private String databaseId; + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getDatabaseId() { + return databaseId; + } + + public void setDatabaseId(String databaseId) { + this.databaseId = databaseId; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkMemoryProperties.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkMemoryProperties.java new file mode 100644 index 000000000..05b16a27c --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkMemoryProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for ADK memory-service auto-configuration. Prefix {@code adk.memory}. + */ +@ConfigurationProperties(prefix = "adk.memory") +public class AdkMemoryProperties { + + /** Which memory-service backend to wire. Defaults to {@link Type#IN_MEMORY}. */ + private Type type = Type.IN_MEMORY; + + public enum Type { + /** {@code com.google.adk.memory.InMemoryMemoryService} — default, no external storage. */ + IN_MEMORY, + /** Reserved — no concrete Vertex AI memory service exists in ADK today; fails fast. */ + VERTEX_AI, + /** + * {@code com.google.adk.memory.FirestoreMemoryService} — requires the {@code + * google-adk-firestore-session-service} contrib module on the classpath. + */ + FIRESTORE + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkRunConfigProperties.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkRunConfigProperties.java new file mode 100644 index 000000000..62f838cd9 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkRunConfigProperties.java @@ -0,0 +1,82 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure.properties; + +import com.google.adk.agents.RunConfig; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for ADK {@link RunConfig} auto-configuration. Prefix {@code + * adk.run-config}. + * + *

Exposes the operational subset of {@link RunConfig} called out in PR review feedback. The + * remaining {@link RunConfig} fields ({@code speechConfig}, {@code responseModalities}, audio + * transcription configs, {@code avatarConfig}) are not exposed here — they require richer binding + * logic and are application-specific; users who need them can declare their own {@link RunConfig} + * bean which will take precedence via {@code @ConditionalOnMissingBean}. + */ +@ConfigurationProperties(prefix = "adk.run-config") +public class AdkRunConfigProperties { + + /** Streaming mode for the agent loop. Defaults to {@link RunConfig.StreamingMode#NONE}. */ + private RunConfig.StreamingMode streamingMode = RunConfig.StreamingMode.NONE; + + /** Maximum number of LLM calls per invocation before the agent loop is interrupted. */ + private int maxLlmCalls = 500; + + /** Tool execution policy. Defaults to {@link RunConfig.ToolExecutionMode#NONE} (parallel-ish). */ + private RunConfig.ToolExecutionMode toolExecutionMode = RunConfig.ToolExecutionMode.NONE; + + /** Persist user-supplied input blobs as artifacts on session creation. */ + private boolean saveInputBlobsAsArtifacts = false; + + /** Auto-create the session if a runner invocation references an unknown session id. */ + private boolean autoCreateSession = false; + + public RunConfig.StreamingMode getStreamingMode() { + return streamingMode; + } + + public void setStreamingMode(RunConfig.StreamingMode streamingMode) { + this.streamingMode = streamingMode; + } + + public int getMaxLlmCalls() { + return maxLlmCalls; + } + + public void setMaxLlmCalls(int maxLlmCalls) { + this.maxLlmCalls = maxLlmCalls; + } + + public RunConfig.ToolExecutionMode getToolExecutionMode() { + return toolExecutionMode; + } + + public void setToolExecutionMode(RunConfig.ToolExecutionMode toolExecutionMode) { + this.toolExecutionMode = toolExecutionMode; + } + + public boolean isSaveInputBlobsAsArtifacts() { + return saveInputBlobsAsArtifacts; + } + + public void setSaveInputBlobsAsArtifacts(boolean saveInputBlobsAsArtifacts) { + this.saveInputBlobsAsArtifacts = saveInputBlobsAsArtifacts; + } + + public boolean isAutoCreateSession() { + return autoCreateSession; + } + + public void setAutoCreateSession(boolean autoCreateSession) { + this.autoCreateSession = autoCreateSession; + } +} diff --git a/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkSessionProperties.java b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkSessionProperties.java new file mode 100644 index 000000000..4ac419b5d --- /dev/null +++ b/contrib/spring-boot-starter/src/main/java/com/google/adk/autoconfigure/properties/AdkSessionProperties.java @@ -0,0 +1,66 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +package com.google.adk.autoconfigure.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for ADK session-service auto-configuration. Prefix {@code adk.session}. + */ +@ConfigurationProperties(prefix = "adk.session") +public class AdkSessionProperties { + + /** Which session-service backend to wire. Defaults to {@link Type#IN_MEMORY}. */ + private Type type = Type.IN_MEMORY; + + /** GCP project id (required when {@link #type} = {@link Type#VERTEX_AI}). */ + private String projectId; + + /** GCP location (required when {@link #type} = {@link Type#VERTEX_AI}). */ + private String location; + + public enum Type { + /** {@code com.google.adk.sessions.InMemorySessionService} — default, no external storage. */ + IN_MEMORY, + /** + * {@code com.google.adk.sessions.VertexAiSessionService} — managed Vertex AI Reasoning Engine. + */ + VERTEX_AI, + /** + * {@code com.google.adk.sessions.FirestoreSessionService} — requires the {@code + * google-adk-firestore-session-service} contrib module on the classpath. + */ + FIRESTORE + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } +} diff --git a/contrib/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/contrib/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..a99ecf604 --- /dev/null +++ b/contrib/spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,8 @@ +com.google.adk.autoconfigure.AdkArtifactsAutoConfiguration +com.google.adk.autoconfigure.AdkSessionAutoConfiguration +com.google.adk.autoconfigure.AdkFirestoreSessionAutoConfiguration +com.google.adk.autoconfigure.AdkMemoryAutoConfiguration +com.google.adk.autoconfigure.AdkFirestoreMemoryAutoConfiguration +com.google.adk.autoconfigure.AdkRunConfigAutoConfiguration +com.google.adk.autoconfigure.AdkFirestoreAutoConfiguration +com.google.adk.autoconfigure.AdkRunnerAutoConfiguration diff --git a/pom.xml b/pom.xml index 31fe8a93f..710188361 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ tutorials/city-time-weather tutorials/live-audio-single-agent a2a + contrib/spring-boot-starter