|
1 | 1 | # ADK Spring Boot Starter |
2 | 2 |
|
3 | | -This starter integrates the Google Agent Development Kit (ADK) into Spring Boot applications. |
4 | | -It provides auto-configuration for core services like Artifacts, Session, and Memory management. |
| 3 | +Spring Boot auto-configuration for the [Agent Development Kit (ADK)](https://github.com/google/adk-java) runtime. |
5 | 4 |
|
6 | | -## Installation |
| 5 | +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`. |
7 | 6 |
|
8 | | -Add the following dependency to your `pom.xml`: |
| 7 | +## Installation |
9 | 8 |
|
10 | 9 | ```xml |
11 | 10 | <dependency> |
12 | 11 | <groupId>com.google.adk</groupId> |
13 | | - <artifactId>adk-spring-boot-starter</artifactId> |
14 | | - <version>${adk-starter.version}</version> |
| 12 | + <artifactId>google-adk-spring-boot-starter</artifactId> |
| 13 | + <version>${adk.version}</version> |
| 14 | +</dependency> |
| 15 | + |
| 16 | +<!-- Recommended companion for Spring AI users (OpenAI / Anthropic / Gemini / Ollama / Azure OpenAI / Bedrock): --> |
| 17 | +<dependency> |
| 18 | + <groupId>com.google.adk</groupId> |
| 19 | + <artifactId>google-adk-spring-ai</artifactId> |
| 20 | + <version>${adk.version}</version> |
15 | 21 | </dependency> |
16 | 22 | ``` |
17 | 23 |
|
18 | | -## Configuration |
| 24 | +The starter declares `google-adk-firestore-session-service` as an `<optional>true</optional>` dependency. It is on your runtime classpath by default — explicitly exclude it if you do not want Firestore-related auto-configurations to activate. |
19 | 25 |
|
20 | | -By default, the starter configures all services to use **In-Memory** implementations. This is great for local development and testing. |
| 26 | +## Quick Start |
21 | 27 |
|
22 | | -You can configure the behavior using `application.yaml`: |
| 28 | +```java |
| 29 | +@SpringBootApplication |
| 30 | +class MyApp { |
| 31 | + public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } |
| 32 | +} |
23 | 33 |
|
24 | | -### 1. Artifacts Service |
| 34 | +@Configuration |
| 35 | +class MyAgents { |
| 36 | + @Bean public LlmAgent rootAgent(SpringAI llm) { // SpringAI bean comes from google-adk-spring-ai |
| 37 | + return LlmAgent.builder() |
| 38 | + .name("root_agent") |
| 39 | + .model(llm) |
| 40 | + .instruction("Answer concisely.") |
| 41 | + .build(); |
| 42 | + } |
| 43 | + @Bean public App app(LlmAgent rootAgent, |
| 44 | + @Value("${spring.application.name}") String appName) { |
| 45 | + return App.builder().name(appName).rootAgent(rootAgent).build(); |
| 46 | + } |
| 47 | +} |
25 | 48 |
|
26 | | -* **Default**: `InMemoryArtifactService` |
27 | | -* **Production**: `GcsArtifactService` (Google Cloud Storage) |
| 49 | +@RestController |
| 50 | +class ChatController { |
| 51 | + private final Runner runner; // provided by starter |
| 52 | + private final RunConfig runConfig; // provided by starter |
| 53 | + private final BaseSessionService sessionService; // provided by starter |
| 54 | + ChatController(Runner r, RunConfig c, BaseSessionService s) { |
| 55 | + this.runner = r; this.runConfig = c; this.sessionService = s; |
| 56 | + } |
| 57 | + @PostMapping("/chat") String chat(@RequestBody String prompt) { |
| 58 | + String userId = "alice"; |
| 59 | + String sessionId = UUID.randomUUID().toString(); |
| 60 | + sessionService.createSession(runner.appName(), userId, null, sessionId).blockingGet(); |
| 61 | + Content msg = Content.builder().role("user").parts(List.of(Part.builder().text(prompt).build())).build(); |
| 62 | + return runner.runAsync(userId, sessionId, msg, runConfig) |
| 63 | + .toList().blockingGet() |
| 64 | + .stream().map(Event::stringifyContent).collect(joining()); |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
28 | 68 |
|
29 | | -To enable GCS: |
| 69 | +## Property Reference |
30 | 70 |
|
31 | 71 | ```yaml |
| 72 | +spring: |
| 73 | + application: |
| 74 | + name: my_app # used as the App's appName (must match validateAppName regex) |
| 75 | + |
32 | 76 | adk: |
33 | 77 | artifacts: |
34 | | - gcs-enabled: true |
35 | | - bucket-name: "my-agent-artifacts-bucket" |
36 | | -``` |
37 | | -
|
38 | | -### 2. Session Service |
39 | | -
|
40 | | -* **Default**: `InMemorySessionService` |
41 | | -* **Production**: `VertexAiSessionService` |
| 78 | + gcs-enabled: false # default; switch to true to use Google Cloud Storage |
| 79 | + # bucket-name: my-artifacts-bucket # required when gcs-enabled=true (fail-fast otherwise) |
42 | 80 |
|
43 | | -To enable Vertex AI Sessions: |
44 | | - |
45 | | -```yaml |
46 | | -adk: |
47 | 81 | session: |
48 | | - type: VERTEX_AI |
49 | | - project-id: "my-gcp-project" |
50 | | - location: "us-central1" |
51 | | -``` |
| 82 | + type: IN_MEMORY # IN_MEMORY | VERTEX_AI | FIRESTORE |
| 83 | + # project-id: my-gcp-project # required for VERTEX_AI |
| 84 | + # location: us-central1 # required for VERTEX_AI |
52 | 85 |
|
53 | | -### 3. Memory Service |
| 86 | + memory: |
| 87 | + type: IN_MEMORY # IN_MEMORY | FIRESTORE |
| 88 | + # VERTEX_AI is reserved — fails fast (no impl exists in ADK) |
54 | 89 |
|
55 | | -* **Default**: `InMemoryMemoryService` |
| 90 | + run-config: |
| 91 | + streaming-mode: NONE # NONE | SSE | BIDI |
| 92 | + max-llm-calls: 500 |
| 93 | + tool-execution-mode: NONE # NONE | SEQUENTIAL | PARALLEL | PARALLEL_SUBSCRIBE |
| 94 | + save-input-blobs-as-artifacts: false |
| 95 | + auto-create-session: false |
| 96 | + |
| 97 | + firestore: # only consulted when session/memory type=FIRESTORE |
| 98 | + # project-id: my-gcp-project # optional — falls back to ADC project |
| 99 | + # database-id: "(default)" # optional — falls back to "(default)" |
| 100 | +``` |
56 | 101 |
|
57 | | -Currently, only In-Memory memory service is supported by default. |
| 102 | +## Persistence Backends |
58 | 103 |
|
| 104 | +| Concern | `IN_MEMORY` | `VERTEX_AI` | `FIRESTORE` | GCS (artifacts only) | |
| 105 | +|------------|-------------|------------------------------------|--------------------------------------------|----------------------| |
| 106 | +| Sessions | default | `VertexAiSessionService` (managed) | `FirestoreSessionService` (contrib module) | — | |
| 107 | +| Memory | default | n/a — fails fast | `FirestoreMemoryService` (contrib module) | — | |
| 108 | +| Artifacts | default | n/a | n/a | `GcsArtifactService` | |
59 | 109 |
|
60 | | -### 4. Run Configuration |
| 110 | +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. |
61 | 111 |
|
62 | | -* **Default Streaming Mode**: `NONE` |
| 112 | +## Multiple business-unit agents in one Spring Boot app |
63 | 113 |
|
64 | | -To enable streaming (e.g., SSE or BIDI): |
| 114 | +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: |
65 | 115 |
|
66 | | -```yaml |
67 | | -adk: |
68 | | - run-config: |
69 | | - streaming-mode: SSE # Options: NONE, SSE, BIDI |
| 116 | +```java |
| 117 | +@Configuration |
| 118 | +class BusinessUnits { |
| 119 | + |
| 120 | + @Bean App salesApp(SpringAI llm) { |
| 121 | + return App.builder().name("sales").rootAgent(salesRootAgent(llm)).build(); |
| 122 | + } |
| 123 | + @Bean App supportApp(SpringAI llm) { |
| 124 | + return App.builder().name("support").rootAgent(supportRootAgent(llm)).build(); |
| 125 | + } |
| 126 | + // ... per-BU rootAgent bean factories ... |
| 127 | + |
| 128 | + @Bean Runner salesRunner( |
| 129 | + @Qualifier("salesApp") App app, |
| 130 | + BaseArtifactService artifactService, |
| 131 | + BaseSessionService sessionService) { |
| 132 | + return Runner.builder().app(app).artifactService(artifactService).sessionService(sessionService).build(); |
| 133 | + } |
| 134 | + @Bean Runner supportRunner( |
| 135 | + @Qualifier("supportApp") App app, |
| 136 | + BaseArtifactService artifactService, |
| 137 | + BaseSessionService sessionService) { |
| 138 | + return Runner.builder().app(app).artifactService(artifactService).sessionService(sessionService).build(); |
| 139 | + } |
| 140 | +} |
70 | 141 | ``` |
71 | 142 |
|
72 | | -*Note: Other `RunConfig` options (like modalities, audio config) will be added in future versions.* |
| 143 | +The shared `BaseSessionService`, `BaseArtifactService`, `BaseMemoryService`, and `RunConfig` beans are singletons partitioned internally by `appName`. No duplication — and no per-BU service wiring boilerplate. |
73 | 144 |
|
74 | 145 | ## Architecture |
75 | 146 |
|
76 | | -The starter follows the Single Responsibility Principle by splitting configuration into: |
77 | | -* `AdkArtifactsAutoConfiguration` |
78 | | -* `AdkSessionAutoConfiguration` |
79 | | -* `AdkMemoryAutoConfiguration` |
80 | | -* `AdkRunConfigAutoConfiguration` |
| 147 | +Eight auto-configuration classes, each owning one concern: |
81 | 148 |
|
82 | | -Each has its own properties class (e.g., `AdkArtifactProperties`). |
| 149 | +| Auto-config | Produces | Activation | |
| 150 | +|------------------------------------------------|---------------------------------------|-------------------------------------------------------------------------------| |
| 151 | +| `AdkArtifactsAutoConfiguration` | `BaseArtifactService`, conditional `Storage` | always; `Storage` only when `adk.artifacts.gcs-enabled=true` | |
| 152 | +| `AdkSessionAutoConfiguration` | `BaseSessionService` (IN_MEMORY, VERTEX_AI) | always; FIRESTORE falls through to the Firestore variant when on classpath | |
| 153 | +| `AdkFirestoreSessionAutoConfiguration` | `BaseSessionService` (FIRESTORE) | `@ConditionalOnClass(FirestoreSessionService.class)`; `@AutoConfigureBefore` | |
| 154 | +| `AdkMemoryAutoConfiguration` | `BaseMemoryService` (IN_MEMORY) | always; FIRESTORE falls through; VERTEX_AI fails fast | |
| 155 | +| `AdkFirestoreMemoryAutoConfiguration` | `BaseMemoryService` (FIRESTORE) | `@ConditionalOnClass(FirestoreMemoryService.class)`; `@AutoConfigureBefore` | |
| 156 | +| `AdkRunConfigAutoConfiguration` | `RunConfig` | always | |
| 157 | +| `AdkFirestoreAutoConfiguration` | `Firestore` client | `@ConditionalOnClass(Firestore.class)` | |
| 158 | +| `AdkRunnerAutoConfiguration` | `Runner` | `@ConditionalOnBean(App.class)` + `@ConditionalOnSingleCandidate(App.class)` | |
83 | 159 |
|
84 | | -## Usage |
| 160 | +Every `@Bean` factory uses `@ConditionalOnMissingBean` so any user-declared override (`Storage`, `Firestore`, `BaseSessionService`, `Runner`, etc.) always wins. |
85 | 161 |
|
86 | | -Simply inject the beans into your application: |
| 162 | +## Failure Modes |
87 | 163 |
|
88 | | -```java |
89 | | -@Service |
90 | | -public class MyAgentService { |
| 164 | +The starter fails fast at startup (throwing `BeanCreationException` with a clear remediation message) when: |
91 | 165 |
|
92 | | - private final Runner runner; |
93 | | - // Agent bean is no longer auto-configured by default. |
94 | | - // You must define your own Agent bean or use the Runner directly. |
| 166 | +- `adk.artifacts.gcs-enabled=true` but `adk.artifacts.bucket-name` is blank. |
| 167 | +- `adk.session.type=VERTEX_AI` but `project-id` or `location` is blank. |
| 168 | +- `adk.session.type=FIRESTORE` but `google-adk-firestore-session-service` is not on the classpath. |
| 169 | +- `adk.memory.type=VERTEX_AI` (no Vertex AI memory service exists in ADK today). |
| 170 | +- `adk.memory.type=FIRESTORE` but the contrib jar is missing. |
95 | 171 |
|
96 | | - public MyAgentService(Runner runner) { |
97 | | - this.runner = runner; |
98 | | - } |
| 172 | +## Roadmap — not in this module |
99 | 173 |
|
100 | | - // ... use runner |
101 | | -} |
102 | | -``` |
| 174 | +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. |
0 commit comments