Bookshelf builder: Use Gemini to build a Java Cloud Function for a Gemini application

1. Introduction

Do you love digging into books but get overwhelmed by the sheer volume of choices? Imagine having an AI-powered app that not only recommends the perfect read but also offers a concise summary based on your genre of choice, giving you a glimpse into the book's essence. In this codelab, I'll walk you through building such an app with BigQuery and Cloud Functions powered by Gemini.

Project Overview

Our use case centers around these 4 key components:

  • Book Database: The vast BigQuery public dataset of internet archive books will serve as our comprehensive book catalog.
  • AI Summarization Engine: Google Cloud Functions, equipped with the Gemini-Pro language model, will generate insightful summaries tailored to user requests.
  • BigQuery Integration: A remote function within BigQuery that calls our Cloud Function to deliver on-demand book summaries and themes.
  • User Interface: A web app hosted on Cloud Run that will offer a web application for users to view the results.

We will divide the implementation into 3 codelabs:

Codelab 1: Use Gemini to build a Java Cloud Function for a Gemini application.

Codelab 2: Use Gemini to build SQL-only Generative AI applications with BigQuery.

Codelab 3: Use Gemini to create a Java Spring Boot web application that interacts with BigQuery.

2. Use Gemini to build a Generative AI app serverlessly on Java Cloud Function

What you'll build

You'll create a

  • Java Cloud Functions application that implements Gemini 1.0 Pro to take a specific prompt as input in the form of JSON Array and returns a response (Json value labeled "replies").
  • You will do the build and deploy steps with the help of Gemini

3. Requirements

  • A browser, such as Chrome or Firefox
  • A Google Cloud project with billing enabled

Below are the prerequisites:

Create your project

  1. In the Google Cloud Console, on the project selector page, select or create a Google Cloud project.
  2. Make sure that billing is enabled for your Cloud project. Learn how to check if billing is enabled on a project.

Activate Cloud Shell

  1. You will use Cloud Shell, a command-line environment running in Google Cloud that comes pre-loaded with bq:

From the Cloud Console, click Activate Cloud Shell on the top right corner: 6757b2fb50ddcc2d.png

  1. Once connected to Cloud Shell, you should see that you are already authenticated and that the project is already set to your project ID. Run the following command in Cloud Shell to confirm that you are authenticated:
gcloud auth list
  1. Run the following command in Cloud Shell to confirm that the gcloud command knows about your project
gcloud config list project
  1. If your project is not set, use the following command to set it:
gcloud config set project <YOUR_PROJECT_ID>

Refer documentation for gcloud commands and usage.

4. Enabling Gemini for Google Cloud and necessary APIs

Enable Gemini

  1. Navigate to Gemini for Google Cloud in Marketplace to enable the API. You can also use the following command:
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID
  1. Visit the Gemini page and click on "Start chatting".

Important: Follow steps 1 and 2 in this codelab to get started with Gemini and to enable Gemini in the Cloud Shell IDE respectively.

Enable other necessary APIs

How would we do that? Let's ask Gemini that, shall we? But before that remember:

LLMs are non-deterministic. So while you are trying these prompts, the response that you receive might look different from the ones in my screenshot.

Go to the Gemini chat console by clicking the "Open Gemini" icon on the top right corner adjacent to the search bar in the Google Cloud console.

26e1491322855614.png

Type this question in the "Enter a prompt here" section:

How do I enable the cloud functions api using a gcloud command? 

You should get a response similar to:

gcloud services enable cloudfunctions.googleapis.com

Copy that (you can use the copy icon on the top of the command snippet) and run it in the Cloud Shell Terminal to enable the Cloud Functions. Do the same for Cloud Run as we need both to get the Cloud Functions built and deployed:

gcloud services enable \
  cloudfunctions.googleapis.com \
  aiplatform.googleapis.com \
  run.googleapis.com \
  cloudbuild.googleapis.com

5. Preparing Cloud Functions Template with Gemini

At this point, I am assuming that you already have Gemini enabled in your Cloud Shell IDE.

Open Cloud Shell Editor by clicking on the Open Editor icon on the top right corner of your Cloud Shell Terminal (I usually prefer opening the terminal and editor in separate tabs in parallel so we can write code in one and build in another).

edd258384bc74f1f.png

Once you have the editor open, make sure the Gemini logo in the bottom right corner of the editor console is active (and not canceled out). Also make sure that your Google Cloud Project on the bottom left corner is pointing to your current active project that you want to work with. If they are inactive, click them, authorize, select the Google Cloud Project that you want it to point to and get them activated.

Once both are active, click the project name in the bottom left corner and in the pop up list that opens up titled "Cloud Code", scroll down to "New Application".

ca08602b576ebd57.png

In that list, select Cloud Functions application. From the list that pops-up, select Java:

ac2b44245949da68.png

In the resulting list, type the project name "duetai-gemini-calling" instead of helloworld and click OK.

bf9cfe86e35cdced.png

Hooray! You have bootstrapped your simple Java Cloud Functions application with Gemini and you didn't do much apart from enabling and activation configurations, agreed?

This is the project structure you should see:

d56e410fb76f183f.png

Right now, you are good to deploy the function. But that's not why we started this. Let's go ahead and build the Gemini Pro API implementation in this Cloud Function using the Java SDK.

Now let's build the functionality for our use case, which is invoking the Gemini Pro model in this Cloud Function. To do this, you are free to add more prompts and get your code incrementally developed with Gemini or write the logic on your own. I am going to do a mix of both.

6. Add Dependencies

In the Gemini chat console (the one within the Cloud Code Editor on the left pane), type in the following prompt:

what is the maven dependency for com.google.cloud.vertexai library

The reason I am asking for specifically for com.google.cloud.vertexai package is because that is what I am using in my source code where I implement the Gemini invocation code.

I got this result:

62c4295b9b4654e9.png

 <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-vertexai</artifactId>
      <version>0.1.0</version>
    </dependency>

Copy this and paste it in the pom.xml file, right before the </dependencies> tag. Replace the version with 0.1.0 (You can remove the <version> tag if you are using the Spring Cloud GCP BOM to manage spring-cloud-gcp version numbers for you).

The dependency section should look like this:

1800f10af9331210.png

Make sure you update the version numbers, if required, to match the above. If you notice, I also included another dependency along with it:

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.10</version>
    </dependency>

7. Modify the function entry point and class name

  1. Navigate to the file "launch.json" under the ".vscode" folder. Edit the function name from "function-hello-world" to "function-gemini-calling".
  2. Update the entryPoint value from "cloudcode.helloworld.HelloWorld to cloudcode.bookshelf.Bookshelf.
  3. Now navigate to the Java class file "HelloWorld.java". Change the package name to package cloudcode.bookshelf; In the error that pops up, click the yellow bulb and click the option that says "Move HelloWorld.java" to package cloudcode.bookshelf;.

38d721978bddc8a8.png

  1. Update the class name to Bookshelf and in the error that pops up, click the little yellow bulb and select "Rename file to Bookshelf.java". Select that.

8. Create the method that calls Gemini Pro

Let's implement this functionality in the Bookshelf.java class. Replace your Bookshelf.java with the below code:

package cloudcode.bookshelf;
import java.io.BufferedWriter;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.cloud.vertexai.VertexAI;
import com.google.cloud.vertexai.api.GenerateContentResponse;
import com.google.cloud.vertexai.api.GenerationConfig;
import com.google.cloud.vertexai.generativeai.preview.GenerativeModel;
import com.google.cloud.vertexai.generativeai.preview.ResponseHandler;
import java.io.IOException;
import java.util.List;
import java.util.Arrays;
import java.util.Map;
import java.util.LinkedHashMap;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;

public class Bookshelf implements HttpFunction {
  private static final Gson gson = new Gson();

 @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    BufferedWriter writer = response.getWriter();

 // Get the request body as a JSON object.
 JsonObject requestJson = new Gson().fromJson(request.getReader(), JsonObject.class);
 JsonArray calls_array = requestJson.getAsJsonArray("calls");
 JsonArray calls = (JsonArray) calls_array.get(0);
 String context = calls.get(0).toString().replace("\"", "");

 //Invoke Gemini model
  String raw_result = callGemini(context);
  raw_result = raw_result.replace("\n","");
  String trimmed = raw_result.trim();
  List<String> result_list = Arrays.asList(trimmed);
  Map<String, List<String>> stringMap = new LinkedHashMap<>();
  stringMap.put("replies", result_list);
 
  // Serialization
  String return_value = gson.toJson(stringMap);
  writer.write(return_value);
    }
  public String callGemini(String context) throws IOException{
      String res = "";
        try (VertexAI vertexAi = new VertexAI("REPLACE_WITH_YOUR_PROJECT_ID", "us-central1"); ) {
          GenerationConfig generationConfig =
              GenerationConfig.newBuilder()
                  .setMaxOutputTokens(2048)
                  .setTemperature(0.4F)
                  .setTopK(32)
                  .setTopP(1)
                  .build();  
        GenerativeModel model = new GenerativeModel("gemini-pro", generationConfig, vertexAi);
        GenerateContentResponse response = model.generateContent(context);
        res = ResponseHandler.getText(response);
      }catch(Exception e){
        System.out.println(e);
        }
        return res;
    }
}

This class expects input in the JSON structure as below:

{ "calls": [["YOUR_PROMPT_HERE"]] }

It returns a response as below:

(Json) Map<String, List<String>> {"replies": ["response"]}

Try out the Gemini chat option from the Cloud Shell Editor on the left pane to explain the code. Alternatively you can select all of the code and click the yellow bulb on the top left corner of the selection and choose "Explain this" option.

66fb67507793e368.png

9. Deploy the Cloud Function

Now that the Cloud Function is ready, let's ask Gemini how to deploy it. Go to the Gemini chat in the Cloud Code editor and enter the following:

   How to deploy this Cloud Function with a gcloud command?

I got the below response:

9f9db98933841864.png

I now wanted to probe it further. So I went ahead and asked Gemini to give me the full gcloud functions deploy command. Response is as shown below:

b77701c00dc3eaf1.png

Now I can't say if you will receive the same response, but I found it quite interesting to see that it tops it up with a few more details to my surprise, as seen in the image below:

Request body format:

82bf20304143a374.png

and

Response format:

ade55b3de5d823a6.png

Now, let's go ahead and deploy the function by running the gcloud command that Gemini gave us. For this we need to open the Cloud Shell Terminal. You can open it in a new tab for https://console.cloud.google.com and make sure the right project is selected. Open Cloud Shell Terminal by clicking the Activate Cloud Shell icon in the top right corner of the console and make sure you are in the right project folder by using the below command:

cd duetai-gemini-calling

Followed by the below command:

gcloud functions deploy bookshelf --runtime java17 --trigger-http --entry-point cloudcode.bookshelf.Bookshelf --allow-unauthenticated

It will ask you "Allow unauthenticated invocations of new function [bookshelf]?" Say "y" and hit enter. After that a few questions if applicable and it will deploy your serverless Cloud Function with the deployed URL: https://us-central1-*******.cloudfunctions.net/bookshelf.

Now let's invoke the deployed Cloud Functions and test it!

Note: If you accidentally skipped the "Allow unauthenticated invocations" question or selected "N", then you will not be able to access the result of the Cloud Functions and would see a "permissions error" without granting additional IAM settings. So pay attention to that.

10. Call the deployed Cloud Function

Let's ask Gemini that? I entered the prompt

How to call the deployed cloud function?

I got the below result: (You may or may not see the same exact response, feel free to play around with the prompt and see the difference in responses).

1d2242715571fe6f.png

Probe the chat with specific questions around alternative ways to invoke the deployed Function, call using gcloud command etc. I submitted the below prompt:

how to call the deployed cloud function using gcloud

I got the below response: e7b29b2cfb57782c.png

You can use this response ("gcloud functions call" command) from the terminal with tweaks to make it work for our scenario (Alternatively, try passing the parameters in the prompt itself and see if you are able to get the detailed gcloud functions call in response):

gcloud functions call bookshelf --region=us-central1 --gen2 --data '{"calls":[["Hello! This is my test prompt."]]}'

Here is my result:

6f396d915251db78.png

11. Clean up

You can delete the Cloud Functions you created earlier by clicking the DELETE button in the detail page of the Cloud Functions.

12. Congratulations

You have successfully built, deployed and tested a Java Cloud Functions to call Gemini 1.0 Pro using Gemini! This application takes the input prompt related to Book recommendation with the summary and theme of books.