Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions java/agents/firestore-session-service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/target
*.prefs
*.iml
.idea/
.vscode/
.DS_Store
logs/
*.log
*.project
78 changes: 78 additions & 0 deletions java/agents/firestore-session-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Weather Forecasting Agent with Google ADK and Firestore for persistant user sessions

This sample application demonstrates a Sample Weather Agent built using the Google Agent Development Kit (ADK) for Java. The agent leverages Firestore for storing user session data to database. This allows the agent to maintain context across multiple interactions with users and in distributed environments.

## Features ✨

- **AI-Powered Forecasting**: Utilizes a Large Language Model (LLM) (e.g., Gemini) to understand user requests and invoke Function.
- **Firebase Integration**: Uses `Firestore` to store user session data.
- **Interactive CLI**: Allows users to interact with the agent, request forecasts, and receive results and insights.

## Prerequisites ✅

- **Java Development Kit (JDK)**: Version 17 or higher. (Official downloads: [Oracle JDK](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html), [OpenJDK](https://openjdk.java.net/projects/jdk/17/))
- **Apache Maven**: To build the project. (Official download: [Apache Maven](https://maven.apache.org/download.cgi))
- **Google Cloud SDK (gcloud CLI)**: Installed and authenticated. (Installation guide: [Google Cloud SDK](https://cloud.google.com/sdk/docs/install))
- **Google Cloud Project**:

* A Google Cloud Project with billing enabled.
* Firestore API enabled in the project.
* Service account with appropriate permissions to access Firestore.

- **Firestore Database**: Set up in Native mode.
- **Google ADK for Java**: Ensure you have access to the Google Agent Development Kit for Java.
- **LLM Access**: Access to a Large Language Model (e.g., Gemini) via Google Cloud.

## Setup and Local Execution 🛠

```xml
<dependencies>
<!-- ADK Core -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>0.4.0-SNAPSHOT</version>
</dependency>
<!-- Firestore Session Service -->
<dependency>
<groupId>com.google.adk.contrib</groupId>
<artifactId>firestore-session-service</artifactId>
<version>0.4.0-SNAPSHOT</version>
</dependency>
</dependencies>
```

```gradle
dependencies {
// ADK Core
implementation 'com.google.adk:google-adk:0.4.0-SNAPSHOT'
// Firestore Session Service
implementation 'com.google.adk.contrib:firestore-session-service:0.4.0-SNAPSHOT'
}
```

```
mvn clean compile exec:java -Dexec.mainClass="com.google.adk.samples.agents.firestorerunner.FirestoreRunner"

```

## Test the local agent 🤖

Once the application is running, you can interact with the agent via the CLI. You can request weather forecasts and see how the agent utilizes Firestore to store session data.

The sample console output is below: (console_output.png)

![Console Output](console_output.png)

## Customization 🎨

You can custoimize the firestore collection name

```properties
# Firestore collection name for storing session data
adk.firestore.collection.name=adk-session
# Google Cloud Storage bucket name for artifact storage
adk.gcs.bucket.name=your-gcs-bucket-name
#stop words for keyword extraction
adk.stop.words=a,about,above,after,again,against,all,am,an,and,any,are,aren't,as,at,be,because,been,before,being,below,between,both,but,by,can't,cannot,could,couldn't,did,didn't,do,does,doesn't,doing,don't,down,during,each,few,for,from,further,had,hadn't,has,hasn't,have,haven't,having,he,he'd,he'll,he's,her,here,here's,hers,herself,him,himself,his,how,i,i'd,i'll,i'm,i've,if,in,into,is
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 127 additions & 0 deletions java/agents/firestore-session-service/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2025 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

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>adk-agents</groupId>
<artifactId>firestore-runner</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Firestore Runner</name>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mainClass>com.google.adk.samples.agents.firestorerunner.FirestoreRunner</mainClass>
<junit.jupiter.version>5.10.2</junit.jupiter.version>
<maven.surefire.version>3.2.5</maven.surefire.version>
</properties>

<dependencies>
<!-- Google ADK -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>0.4.0</version>
</dependency>
<!-- Firestore Session Service -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-firestore-session-service</artifactId>
<version>0.4.0</version>
</dependency>

<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
<version>3.30.3</version>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.21.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.21.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@

/*
* Copyright 2025 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.adk.samples.agents.firestorerunner;

import java.util.Scanner;
import java.util.logging.Logger;
import com.google.adk.agents.RunConfig;
import com.google.adk.sessions.FirestoreSessionService;
import com.google.adk.events.Event;
import com.google.adk.runner.FirestoreDatabaseRunner;
import com.google.adk.sessions.Session;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import io.reactivex.rxjava3.core.Flowable;
import com.google.adk.agents.BaseAgent;
import com.google.adk.tools.Annotations.Schema;
import java.util.Map;
import com.google.adk.agents.LlmAgent;
import com.google.adk.tools.FunctionTool;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
* The FirestoreRunner class serves as an entry point for running Firestore-related operations.
* This class can be expanded to include methods for initializing Firestore,
* performing CRUD operations, and handling Firestore events.
*/

public final class FirestoreRunner {

private static final Logger logger = Logger.getLogger(FirestoreRunner.class.getName());
private static final String APP_NAME = "time-agent";
public static void main(String[] args) {

logger.info("Starting FirestoreRunner...");

RunConfig runConfig = RunConfig.builder().build();

BaseAgent timeAgent = initAgent(APP_NAME);
// Initialize Firestore
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
Firestore firestore = firestoreOptions.getService();

// Use FirestoreDatabaseRunner to persist session state
FirestoreDatabaseRunner runner = new FirestoreDatabaseRunner(
timeAgent,
APP_NAME,
firestore
);


// Create a new session
// Replace "user1234" with the actual user ID
String userId = "user-"+System.currentTimeMillis();
String sessionId = "session-"+System.currentTimeMillis();


Session session = new FirestoreSessionService(firestore)
.createSession(APP_NAME,userId,null,sessionId)
.blockingGet();


try (Scanner scanner = new Scanner(System.in, UTF_8)) {
while (true) {
System.out.print("\nYou > ");
String userInput = scanner.nextLine();
if ("quit".equalsIgnoreCase(userInput) || "exit".equalsIgnoreCase(userInput)) {
break;
}

Content userMsg = Content.fromParts(Part.fromText(userInput));
Flowable<Event> events = runner.runAsync(session.userId(), session.id(), userMsg, runConfig);

System.out.print("\nAgent > ");
events.blockingForEach(event -> {
if (event.finalResponse()) {
System.out.println(event.stringifyContent());
}
});
}
} finally {
// Clean up resources
if (firestore != null) {
try {
firestore.close();
logger.info("Firestore client closed successfully.");
} catch (Exception e) {
logger.warning( "Error closing Firestore client.");
}
}
}

logger.info("FirestoreRunner finished execution.");
System.exit(0);
}

/** Mock tool implementation */
@Schema(description = "Get the current time for a given city")
public static Map<String, String> getCurrentTime(
@Schema(name = "city", description = "Name of the city to get the time for") String city) {
return Map.of(
"city", city,
"forecast", "The time is 10:30 am."
);
}

/**
* Initialize the agent with tools and configuration.
* @return An initialized {@link BaseAgent}.
*/
protected static BaseAgent initAgent(String agentName) {
return LlmAgent.builder()
.name(agentName)
.description("Tells the current time in a specified city")
.instruction("""
You are a helpful assistant that tells the current time in a city.
Use the 'getCurrentTime' tool for this purpose.
""")
.model("gemini-2.5-flash")
.tools(FunctionTool.create(FirestoreRunner.class, "getCurrentTime"))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gcs.adk.bucket.name=adk-samples-firestore-agent
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- src/main/resources/logback.xml -->
<configuration>
<!-- Define an appender to output logs to the console -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- Define the log pattern to match your existing format -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<!-- Set the logging level for a specific package or class -->
<!-- You can change INFO to DEBUG, TRACE, WARN, ERROR, or OFF -->
<logger name="com.google.adk.sessions.FirestoreSessionService" level="WARN" />

<!-- You can also set it for the entire package if you want -->
<!-- <logger name="com.google.adk.sessions" level="DEBUG"/> -->

<!-- Root logger configuration -->
<!-- This sets the default level for all other loggers -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>

</configuration>
Loading