<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Guillaume Laforge</title>
    <description>The latest articles on Forem by Guillaume Laforge (@glaforge).</description>
    <link>https://forem.com/glaforge</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F176707%2Faf2a94e6-0062-4170-ac85-f316bcfbd579.jpg</url>
      <title>Forem: Guillaume Laforge</title>
      <link>https://forem.com/glaforge</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/glaforge"/>
    <language>en</language>
    <item>
      <title>Text classification with Gemini and LangChain4j</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Thu, 11 Jul 2024 20:26:36 +0000</pubDate>
      <link>https://forem.com/googlecloud/text-classification-with-gemini-and-langchain4j-37n5</link>
      <guid>https://forem.com/googlecloud/text-classification-with-gemini-and-langchain4j-37n5</guid>
      <description>&lt;p&gt;Generative AI has potential applications far beyond chatbots and Retrieval Augmented Generation. For example, a nice use case is: &lt;strong&gt;text classification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I had the chance of meeting some customers and prospects who had the need for triaging incoming requests, or for labeling existing data. In the first case, a government entity was tasked with routing citizen requests to access undisclosed information to the right governmental service that could grant or reject that access. In the second case, a company needed to sort out tons of existing internal documents that were not properly organized, and they wanted to quickly start better structuring this trove of information, by labelling each of these docs into different categories.&lt;/p&gt;

&lt;p&gt;In both situations, the task was a &lt;strong&gt;text classification&lt;/strong&gt; one: to put each request or document in a distinct pile, so they could more easily be sorted out, organized, and treated more rapidly.&lt;/p&gt;

&lt;p&gt;Before generative AI, text classification would be handled by data scientists who would craft and train dedicated machine learning models for that purpose. But it is now also possible to do the same with the help of large language models. That’s what I’d like to explore with you in this article today.&lt;/p&gt;

&lt;p&gt;As usual, I’ll be using the &lt;a href="https://deepmind.google/technologies/gemini/" rel="noopener noreferrer"&gt;Gemini model&lt;/a&gt;, and the &lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j framework&lt;/a&gt; for implementing illustrative examples in Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text classification: putting a label on a document
&lt;/h2&gt;

&lt;p&gt;Before diving into the code, let’s step back a short moment to clarify what text classification is about. When we classify documents, we put a label on them.&lt;/p&gt;

&lt;p&gt;For example, in a bug tracker, we could automate adding labels on new tickets that say that the bug report is related to a certain component. So we would put the name of the component as the label for that new ticket.&lt;/p&gt;

&lt;p&gt;For routing incoming document access requests, we could put the label of the service that must treat the request, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filtering&lt;/strong&gt; is also a text classification problem: we can filter the content of emails to state whether they are spam or not. And we can also use LLMs to filter harmful content from users’ inputs, and even classify the category of harm (hateful speech, harrasment, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-shot prompting: just ask the model!
&lt;/h2&gt;

&lt;p&gt;What about just asking a large language model what it thinks the classification, or the label should be? And indeed, LLMs are often very smart and can figure out the correct classification, without being trained specifically for that purpose.&lt;/p&gt;

&lt;p&gt;Let’s illustrate this with a very common type of text classification: &lt;strong&gt;sentiment analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we can define an &lt;code&gt;enum&lt;/code&gt; representing the various sentiments that can be recognized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Sentiment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="no"&gt;POSITIVE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NEUTRAL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NEGATIVE&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a &lt;code&gt;record&lt;/code&gt; which will hold the result of the sentiment analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;SentimentClassification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;Sentiment&lt;/span&gt; &lt;span class="n"&gt;sentiment&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also need an &lt;code&gt;interface&lt;/code&gt; to represent the type-safe Java service that the developers integrating this LLM-backed solution will call to retrieve the sentiment of the text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SentimentClassifier&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;SentimentClassification&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that it takes in input an unstructured &lt;code&gt;String&lt;/code&gt; text, but in output, you’ll manipulate a strongly typed object, not just a mere string.&lt;/p&gt;

&lt;p&gt;It’s time to prepare our Gemini model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-pro"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OBJECT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sentiment"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAllEnum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Sentiment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Enum:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re taking advantage of the latest feature of Gemini and LangChain4j, which permits to specify that we want 100% valid JSON in output, and even better than this, we want the generated JSON output to comply with a JSON schema!&lt;/p&gt;

&lt;p&gt;Now we create the sentiment analysis service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SentimentClassifier&lt;/span&gt; &lt;span class="n"&gt;sentimentClassifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="nc"&gt;AiServices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SentimentClassifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we call it to retrieve the sentiment of the text we want to analyze:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SentimentClassification&lt;/span&gt; &lt;span class="n"&gt;classification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="n"&gt;sentimentClassifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am happy!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sentiment&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// POSITIVE&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We didn’t even need to give Gemini examples, this is why it’s called &lt;em&gt;zero-shot prompting&lt;/em&gt;. LLMs are usually smart enough to easily handle familiar classification tasks like sentiment analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Few-shot prompting: when the model needs a little help
&lt;/h2&gt;

&lt;p&gt;A more common approach with LLMs for text classification is &lt;em&gt;few-shot prompting&lt;/em&gt;. As the name implies, it’s a prompting technique.&lt;/p&gt;

&lt;p&gt;You give the model a task (classifying text), and you show it examples of classifications, with a clear input/output format, to force the LLM to reply with just the expected class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ChatLanguageModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash-001"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxOutputTokens&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;PromptTemplate&lt;/span&gt; &lt;span class="n"&gt;promptTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 Analyze the sentiment of the text below.
 Respond only with one word to describe the sentiment.

 INPUT: This is fantastic news!
 OUTPUT: POSITIVE

 INPUT: Pi is roughly equal to 3.14
 OUTPUT: NEUTRAL

 INPUT: I hate disliked the pizza. Who'd put pineapple toppings?
 OUTPUT: NEGATIVE

 INPUT: {{text}}
 OUTPUT:
 """&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Prompt&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;promptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"I love strawberries!"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AiMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUserMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// POSITIVE&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above approach, we use LangChain4j’s &lt;code&gt;PromptTemplate&lt;/code&gt;, with a placeholder value &lt;code&gt;{{text}}&lt;/code&gt; that will contain the text to classify. We don’t use an &lt;code&gt;enum&lt;/code&gt; value though, so we have to discriminate against a string in the end. But we could also apply the same schema response handling as in our previous zero-shot example.&lt;/p&gt;

&lt;p&gt;Let’s rewrite this code a little bit differently, to &lt;em&gt;fake&lt;/em&gt; a conversation with the model. The model will see an exchange between a user and itself, and will also follow the same syntax, and will reply with just one word: the sentiment. We’ll use system instructions, and alternating AI and user messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChatMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fewShotPrompts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 Analyze the sentiment of the text below.
 Respond only with one word to describe the sentiment.
 """&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

 &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is fantastic news!"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nc"&gt;AiMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POSITIVE"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

 &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pi is roughly equal to 3.14"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nc"&gt;AiMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NEUTRAL"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

 &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I hate disliked the pizza. "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="s"&gt;"Who'd put pineapple toppings?"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nc"&gt;AiMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NEGATIVE"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

 &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I love strawberries!"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fewShotPrompts&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// POSITIVE&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same outcome, stawberries are yummy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Text classification with embedding models
&lt;/h2&gt;

&lt;p&gt;In the two previous sections, we took advantage of LLMs’ abilities to classify text on their own, based on their intrinsic knowledge, or with the help of a few examples. But there’s another way we can investigate: &lt;strong&gt;using embedding vectors&lt;/strong&gt; to compare texts.&lt;/p&gt;

&lt;p&gt;Embedding vectors are mathematical representations of words/sentences/paragraphs, in the form of a vector of floating point values. The way those vectors are calculated by &lt;em&gt;embedding models&lt;/em&gt; makes those vector close to each other (in terms of distance) when they are semantically close. You can have a look at my recent article&lt;a href="https://dev.to/glaforge/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data-4k90-temp-slug-2701054"&gt;introducing vector embeddings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;LangChain4j provides a &lt;code&gt;TextClassifier&lt;/code&gt; interface which allows to classify text, by comparing it to sets of other texts that belong to a same class. So we give a map of possible labels, associated with lists of texts that belong to that category.&lt;/p&gt;

&lt;p&gt;In particular, there’s an &lt;code&gt;EmbeddingModelTextClassifier&lt;/code&gt; that uses embedding models to compare the texts with the examples of each labels. We can even tweak its internal algorithm to say whether we prefer if a text should be closer to the average of all the examples, or if we prefer if it’s closer to one of the examples (by default, it’s half distance to the mean, and half distance to the closest example.)&lt;/p&gt;

&lt;p&gt;So let’s have a look at this solution.&lt;/p&gt;

&lt;p&gt;Instead of doing sentiment analysis, we’ll go with recipe classification: our goal will be to classify a recipe, to know if it’s an &lt;em&gt;appetizer&lt;/em&gt;, a &lt;em&gt;main course&lt;/em&gt;, or a &lt;em&gt;dessert&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First, we need to define our labels, with an &lt;code&gt;enum&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;DishType&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="no"&gt;APPETIZER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MAIN&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DESSERT&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we don’t have a dataset of recipes, we’ll use Gemini to generate sample recipes, for each label. For that, we need to configure Gemini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt; &lt;span class="no"&gt;CHAT_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll also configure an embedding model to calculate the vector embeddings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;VertexAiEmbeddingModel&lt;/span&gt; &lt;span class="no"&gt;EMBEDDING_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="nc"&gt;VertexAiEmbeddingModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publisher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"google"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text-embedding-004"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;taskType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VertexAiEmbeddingModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TaskType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CLASSIFICATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vertex AI’s embedding models are capable of handling various tasks, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;classification&lt;/strong&gt; ,&lt;/li&gt;
&lt;li&gt;semantic similarity,&lt;/li&gt;
&lt;li&gt;clustering,&lt;/li&gt;
&lt;li&gt;question answering,&lt;/li&gt;
&lt;li&gt;fact verification,&lt;/li&gt;
&lt;li&gt;query or document retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s create a method to generate a recipe for a particular type of dish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;recipeOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DishType&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;CHAT_MODEL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"Write a recipe for a %s dish"&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we’ll collect 3 examples of recipes for each type of dish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;examplesOfRecipes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DishType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;dishType&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dishType&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;dishType&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
 &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;recipeOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dishType&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way, we have our dataset ready, and we’ll prepare a text classifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;EmbeddingModelTextClassifier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DishType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;recipeClassifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
 &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmbeddingModelTextClassifier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="no"&gt;EMBEDDING_MODEL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;examplesOfRecipes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes a little while to calculate the initial embedding vectors of all the samples, but now our classifier is ready! Let’s see if the following recipe is an &lt;em&gt;appertizer&lt;/em&gt;, a &lt;em&gt;main course&lt;/em&gt;, or a &lt;em&gt;dessert&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DishType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;classifiedDishes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recipeClassifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 **Classic Moist Chocolate Cake**

 This recipe delivers a rich, moist chocolate cake that's
 perfect for any occasion.

 Ingredients:
 * 1 ¾ cups all-purpose flour
 * 2 cups granulated sugar
 * ¾ cup unsweetened cocoa powder
 * 1 ½ teaspoons baking powder
 * 1 ½ teaspoons baking soda
 * 1 teaspoon salt
 * 2 large eggs
 * 1 cup milk
 * ½ cup vegetable oil
 * 2 teaspoons vanilla extract
 * 1 cup boiling water

 Instructions:
 * Preheat oven to 350°F (175°C). Grease and flour two 9-inch
 round cake pans.
 * Combine dry ingredients: In a large bowl, whisk together flour,
 sugar, cocoa powder, baking powder, baking soda, and salt.
 * Add wet ingredients: Beat in eggs, milk, oil, and vanilla until
 combined.
 * Stir in boiling water: Carefully stir in boiling water. The
 batter will be thin.
 * Bake: Pour batter evenly into prepared pans. Bake for 30-35
 minutes, or until a toothpick inserted into the center comes
 out clean.
 * Cool: Let cakes cool in pans for 10 minutes before transferring
 to a wire rack to cool completely.
 """&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This recipe is of type: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;classifiedDishes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// This recipe is of type: [DESSERT]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà, we used the full power of embedding models to calculate text similarity to classify our chocolate cake recipe as a dessert!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Large Language Models like Gemini are great at classifying text, thanks to their general knowledge of the world that they acquired during their training. But for more specialized use cases, we might need to guide the LLM to recognize labels, because the subject is very specific to our data. That’s when few-shot prompting or embedding model-based classification helps.&lt;/p&gt;

&lt;p&gt;If we have lots of samples for each label, using a few-shot prompting approach means we’ll have to pass all those examples again and again in the context window of the LLM, which yields a high token count. So if you pay per tokens, it can become a bit expensive.&lt;/p&gt;

&lt;p&gt;If we use the embedding model text classifier, it might take a while to compute all the embedding vectors, but we’ll do it only once, and then we can just calculate the vector embedding for the text to classify, so it’s just the tokens of the text to classify that is incurred. If we have lots of samples, the classifier needs to do quite a few vector / matrix computations to calculate the distance to the samples, but it’s usually quite fast (unless we really have hundreds or thousands of samples).&lt;/p&gt;

&lt;p&gt;I hope this article showed you that Generative AI is useful beyond the usual chatbots and RAG use cases. It’s great at text classification as well. And LangChain4j and Gemini are well suited for that use case, and you learned how to implement different approaches to do text classification.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Latest Gemini features support in LangChain4j 0.32.0</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Fri, 05 Jul 2024 09:53:30 +0000</pubDate>
      <link>https://forem.com/googlecloud/latest-gemini-features-support-in-langchain4j-0320-b7j</link>
      <guid>https://forem.com/googlecloud/latest-gemini-features-support-in-langchain4j-0320-b7j</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt; 0.32.0 was released yesterday, including my &lt;a href="https://github.com/langchain4j/langchain4j/pull/1278" rel="noopener noreferrer"&gt;pull request&lt;/a&gt;with the support for lots of new Gemini features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON output mode&lt;/strong&gt; , to force Gemini to reply using JSON, without any markup,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON schema&lt;/strong&gt; , to control and constrain the JSON output to comply with a schema,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response grounding&lt;/strong&gt; with Google Search web results and with private data in Vertex AI datastores,&lt;/li&gt;
&lt;li&gt;Easier debugging, thanks to new builder methods to &lt;strong&gt;log requests and responses&lt;/strong&gt; ,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function calling mode&lt;/strong&gt; (none, automatic, or a subset of functions),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety settings&lt;/strong&gt; to catch harmful prompts and responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore those new features together, thanks to some code examples! And at the end of the article, if you make it through, you’ll also discover &lt;strong&gt;2 extra bonus points&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON output mode
&lt;/h2&gt;

&lt;p&gt;Creating LLM-powered applications means working with text, as this is what LLMs return. But to facilitate this integration between LLM responses and your code, the text format of choice is usually JSON, as it’s human-readable, and easy to parse programmatically.&lt;/p&gt;

&lt;p&gt;However, LLMs are a bit chatty, and rather than sending you back a nice raw JSON document, instead, it replies with some extra sentence, and some markdown markup to wrap the piece of JSON.&lt;/p&gt;

&lt;p&gt;Fortunately, Gemini 1.5 (Flash and Pro) allows you to specify the response MIME type. Currently, only &lt;code&gt;application/json&lt;/code&gt; is supported, but other formats may come later.&lt;/p&gt;

&lt;p&gt;To do that, when instantiating the Gemini model, use the &lt;code&gt;responseMimeType()&lt;/code&gt; builder method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Roll a dice"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No sentence, no markdown markup, nothing, just pure JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"roll": 3}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We didn’t even need to say in the prompt we wanted to get a JSON response!&lt;/p&gt;

&lt;p&gt;However, the JSON key of that document may vary from time to time, so you may still wish to be a bit more prescriptive in your prompt, and ask the model to return JSON explicitly, give it an example of the JSON output you expect, etc. That’s the usual prompting approach…&lt;/p&gt;

&lt;p&gt;But now there’s more!&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON Schema output
&lt;/h2&gt;

&lt;p&gt;This is quite unique in the LLM ecosystem, as I believe it’s the only model out there that allows you to specify a JSON schema for constraining the JSON output. This works for Gemini 1.5 Pro only, not with Gemini 1.5 Flash.&lt;/p&gt;

&lt;p&gt;Let’s have another look at our previous dice roll example, and let’s update it to specify a JSON schema for the output generation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;langchain4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertexai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SchemaHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromClass&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;

&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;DiceRoll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"genai-java-demos"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-central1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-pro"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DiceRoll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Roll a dice"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated JSON document will always contain the &lt;code&gt;roll&lt;/code&gt; key&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ "roll": 5 }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we used a convenience method called &lt;code&gt;fromClass()&lt;/code&gt; that creates a JSON schema that corresponds to a Java type (here a Java record).&lt;/p&gt;

&lt;p&gt;But there’s also another convenient method that lets us pass a JSON schema string, called &lt;code&gt;fromJsonSchema()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"genai-java-demos"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-central1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-pro"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromJsonSchema&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 {
 "type": "object",
 "properties": {
 "roll": {
 "type": "integer"
 }
 }
 }
 """&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s also possible to construct a JSON schema programmatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"genai-java-demos"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-central1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-pro"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OBJECT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"roll"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INTEGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you always get consistent JSON outputs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Response grounding with Google Search web results and Vertex AI datastores
&lt;/h2&gt;

&lt;p&gt;Large Language Models are wonderful creative machines, but rather than benefiting from their high degree of creativity, we’d prefer having factual responses grounded on data and documents.&lt;/p&gt;

&lt;p&gt;Gemini offers the ability to &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/ground-gemini" rel="noopener noreferrer"&gt;ground responses&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;against Google Search web results,&lt;/li&gt;
&lt;li&gt;against Vertex AI search datastores.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Google Search to ground responses
&lt;/h3&gt;

&lt;p&gt;The training of an LLM ended at a certain date: its &lt;em&gt;cut-off&lt;/em&gt; date. So it doesn’t know about news that happened after that date. But you can request Gemini to use Google Search to find more up-to-date information.&lt;/p&gt;

&lt;p&gt;For example, if we ask Gemini about the current elections going on in France, it could reply with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;There is no current national election happening in France right now.
The last major national election in France was the **Presidential
election in April and May 2022**, where Emmanuel Macron won a second
term.
There are, however, **local elections** happening regularly in
different regions of France.
To stay updated on French elections, you can check the website of
the **French Ministry of the Interior** or reputable news sources
like **The Guardian, BBC, CNN, or Le Monde**.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s enable the use of Google Search web result with the &lt;code&gt;useGoogleSearch(true)&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;useGoogleSearch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"What is the current election going on in France?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer will be much different, and indeed factual and up-to-date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;France held the first round of a parliamentary election on July 4,
2024. The second round will be on July 7, 2024. The election is
significant because it could result in the first far-right government
in France since World War II. The National Rally, President Emmanuel
Macron’s centrist alliance, and the New Popular Front coalition are
the three major political blocs competing in the election. The
outcome of the election is highly uncertain, with the far-right
National Rally potentially gaining a parliamentary majority. If the
National Rally wins a majority, Macron would be expected to appoint
Jordan Bardella, the party's president, as prime minister.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s indeed a parliamentary election going on right now in France. Those elections were decided only a month ago, thus past the cut-of-date of the knowledge of the model.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For my French audience, don’t forget to go voting next Sunday!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Grounding with Vertex AI Search
&lt;/h3&gt;

&lt;p&gt;The idea is that we want to ground responses on our own data. This is particularly important when the knowledge required is actually private information, like our internal docs, or our customers’ docs.&lt;/p&gt;

&lt;p&gt;My colleague Mete wrote a great&lt;a href="https://atamel.dev/posts/2024/07-01_grounding_with_own_data_vertexai_search/" rel="noopener noreferrer"&gt;article explaining how to setup grounding with private data&lt;/a&gt;. Below, I’ll assume that we created a Vertex AI search app with a datastore backed by a Google Cloud Storage bucket that contains a fictious document which is a car manual, about the &lt;em&gt;Cymbel Starlight&lt;/em&gt; car model! I’m taking the same example as in Mete’s article.&lt;/p&gt;

&lt;p&gt;This time, we specify the search location to point at the Vertex AI search datastore with &lt;code&gt;vertexSearchDatastore()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertexSearchDatastore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"projects/%s/locations/%s/collections/%s/dataStores/%s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"global"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"default_collection"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;"cymbal-datastore_1720169982142"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"What is the cargo capacity of Cymbal Starlight?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a fictious car that doesn’t exist, but it’s covered in that private document, and indeed, Gemini is now able to respond to that question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The Cymbal Starlight 2024 has a cargo capacity of 13.5 cubic feet.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s interesting as well is that the response returned by Gemini provides some context about the source document that helped it answer the user query (we’ll see in the next section how to enable logging requests and responses):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; grounding_metadata {
2: {
1: {
3: 66
}
2: 0x3f7deee0
}
5: {
2: {
1: "gs://genai-java-demos-documents/cymbal-starlight-2024.pdf"
2: "cymbal-starlight-2024"
}
}
6: {
1: {
3: 66
4: "The Cymbal Starlight 2024 has a cargo capacity of 13.5 cubic feet."
}
2: "\000"
3: {
257772: 63
}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, to be honest, I’m not quite sure what the numbers exactly mean, but this metadata mentions that the PDF uploaded in cloud storage is the one that was used to shape the answer of the LLM, and gives an excerpt of the sentence that was found in the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request and response logging
&lt;/h2&gt;

&lt;p&gt;To better understand what’s going on under the hood, you can enable request and response logging. That way, you’re able to see exactly what is sent to Gemini, and what Gemini replies.&lt;/p&gt;

&lt;p&gt;To enable logging, there are two methods we can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;logRequests(true)&lt;/code&gt; to log the request sent to Gemini,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logResponse(true)&lt;/code&gt; to log the response received from Gemini.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see that in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logResponses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why is the sky blue?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what’s logged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[main] DEBUG dev.langchain4j.model.vertexai.VertexAiGeminiChatModel -
GEMINI (gemini-1.5-flash) request: InstructionAndContent {
systemInstruction = null,
contents = [role: "user"
parts {
text: "Why is the sky blue?"
}
]
} tools: []
[main] DEBUG dev.langchain4j.model.vertexai.VertexAiGeminiChatModel -
GEMINI (gemini-1.5-flash) response: candidates {
content {
role: "model"
parts {
text: "The sky appears blue due to a phenomenon called
**Rayleigh scattering**. Here\'s a breakdown:\n\n* **Sunlight
is made up of all colors of the rainbow.** When sunlight enters
the Earth\'s atmosphere, it encounters tiny particles like
nitrogen and oxygen molecules.\n* **These particles scatter the
sunlight in all directions.** However, shorter wavelengths of
light, like blue and violet, scatter more strongly than longer
wavelengths, like red and orange.\n* **This preferential
scattering of shorter wavelengths is called Rayleigh
scattering.**
As a result, we see more blue light scattered throughout the sky,
making it appear blue.\n\n **Why is the sky not violet?** \n\nEven
though violet light scatters even more strongly than blue, our
eyes are more sensitive to blue light. This is why we perceive
the sky as blue rather than violet.\n\n**Other factors that
affect sky color: **\n\n*** Time of day:** The sky appears more
red or orange at sunrise and sunset because the sunlight has to
travel through more of the atmosphere, scattering away most of
the blue light.\n* **Clouds:** Clouds are made up of larger water
droplets or ice crystals, which scatter all wavelengths of light
equally. This is why clouds appear white.\n* **Pollution:**
Pollution particles can scatter light differently, sometimes
making the sky appear hazy or even reddish.\n\nLet me know if
you have any other questions about the sky! \n"
}
}
finish_reason: STOP
safety_ratings {
category: HARM_CATEGORY_HATE_SPEECH
probability: NEGLIGIBLE
probability_score: 0.054802597
severity: HARM_SEVERITY_NEGLIGIBLE
severity_score: 0.03314852
}
safety_ratings {
category: HARM_CATEGORY_DANGEROUS_CONTENT
probability: NEGLIGIBLE
probability_score: 0.100348406
severity: HARM_SEVERITY_NEGLIGIBLE
severity_score: 0.06359858
}
safety_ratings {
category: HARM_CATEGORY_HARASSMENT
probability: NEGLIGIBLE
probability_score: 0.10837755
severity: HARM_SEVERITY_NEGLIGIBLE
severity_score: 0.021491764
}
safety_ratings {
category: HARM_CATEGORY_SEXUALLY_EXPLICIT
probability: NEGLIGIBLE
probability_score: 0.10338596
severity: HARM_SEVERITY_NEGLIGIBLE
severity_score: 0.020410307
}
}
usage_metadata {
prompt_token_count: 6
candidates_token_count: 288
total_token_count: 294
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me give you a bit more details about the logging. LangChain4j uses Slf4j by default for logging. Request &amp;amp; Response logging is logged at &lt;code&gt;DEBUG&lt;/code&gt; level. So we have to configure our logger and/or logger façace accordingly.&lt;/p&gt;

&lt;p&gt;In my test project for this article, I configured the following &lt;code&gt;Maven&lt;/code&gt; dependencies for &lt;code&gt;Slf4j&lt;/code&gt; and the &lt;code&gt;Simple&lt;/code&gt; logger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.slf4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;slf4j-api&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.0.13&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.slf4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;slf4j-simple&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.0.13&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a properties file to configure the loggers: &lt;code&gt;src/main/resources/simplelogger.properties&lt;/code&gt;, which contains the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org.slf4j.simpleLogger.defaultLogLevel=debug
org.slf4j.simpleLogger.log.io.grpc.netty.shaded=info

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set the default logging level to be &lt;code&gt;debug&lt;/code&gt;. But there’s also Netty, the networking library used under the hood by the Gemini Java SDK, that logs at debug level. So I specified that the logging for this library should only be at &lt;code&gt;info&lt;/code&gt; and above, otherwise the output is super chatty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Function calling mode
&lt;/h2&gt;

&lt;p&gt;So far, when using Gemini for&lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling" rel="noopener noreferrer"&gt;function calling&lt;/a&gt;, the model would decide on its own if a function would be useful to call, and which function to call.&lt;/p&gt;

&lt;p&gt;But Gemini introduces the ability to&lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#tool-config" rel="noopener noreferrer"&gt;control the function or tool choice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are 3 options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AUTO&lt;/code&gt; — The familiar and default mode, where Gemini decides on its own if a function call is necessary and which one should be made,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ANY&lt;/code&gt; — Allows to specify a subset of functions from all those available, but also forces the model to pick up one of them (only supported by Gemini 1.5 Pro),&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NONE&lt;/code&gt; — Even if tools are defined and available, prevents Gemini to use any of those tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s have a look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-pro"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logResponses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toolCallingMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ToolCallingMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ANY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allowedFunctionNames&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;ToolSpecification&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolSpecification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"adds two numbers"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;JsonSchemaProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INTEGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;JsonSchemaProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INTEGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;UserMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"How much is 3 + 4?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AiMessage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toolExecutionRequests&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFirst&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We specify the &lt;code&gt;ToolCallingMode.ANY&lt;/code&gt; mode, and we list the allowed function names of the functions that the model must pick in order to reply to the request (with the &lt;code&gt;allowedFunctionNames()&lt;/code&gt; builder method).&lt;/p&gt;

&lt;p&gt;We describe the tool that can be called. We create a message. And when calling &lt;code&gt;generate()&lt;/code&gt;, we pass the tool specification corresponding to the function we want to be called.&lt;/p&gt;

&lt;p&gt;The output will show that the model replied with the mandatory tool execution request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ToolExecutionRequest { id = null, name = "add",
arguments = "{"a":3.0,"b":4.0}" }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it’s our turn to call the &lt;code&gt;add&lt;/code&gt; function with the arguments. And then send back the function execution result back to Gemini.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt; : Currently, it is not possible to use the &lt;code&gt;ANY&lt;/code&gt; forced function calling mode when using LangChain4j’s &lt;code&gt;AiServices&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AiServices&lt;/code&gt; takes care of automatic function calling. But the process is a two-step request / response mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we ask the model the math question and pass the tool specification along.&lt;/li&gt;
&lt;li&gt;The model replies with a &lt;code&gt;ToolExecutionRequest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then &lt;code&gt;AiServices&lt;/code&gt; makes the function call locally, and replies to the model with the function execution result. However, since the &lt;code&gt;ANY&lt;/code&gt; calling mode is specified at the model level, the model still wants to reply with yet another tool execution request. Although at this point, the second call made to the model was &lt;em&gt;just&lt;/em&gt; to pass the function execution result, not to request another tool execution.&lt;/li&gt;
&lt;li&gt;So &lt;code&gt;AiServices&lt;/code&gt; enters an infite loop as the model requests a function execution again and again, not taking into account the execution result that it received.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When using &lt;code&gt;AiServices&lt;/code&gt;, it’s better to let Gemini operate under the default &lt;code&gt;AUTO&lt;/code&gt; tool mode. So it knows when it needs to request a tool execution, or if just needs to handle the tool execution response.&lt;/p&gt;

&lt;p&gt;If you want to use the &lt;code&gt;ANY&lt;/code&gt; mode with &lt;code&gt;allowedFunctionNames()&lt;/code&gt;, then don’t use &lt;code&gt;AiServices&lt;/code&gt;, and handle the function calls on your own in your code, to avoid such infite loop situations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Specify safety settings
&lt;/h2&gt;

&lt;p&gt;In LLM-powered applications, where users can enter any kind of weird textual inputs, you may want to limit harmful content that may be ingested. To do so, you can specify some safety settings, for different categories of content, with different thresholds of acceptance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;langchain4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertexai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;HarmCategory&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;langchain4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertexai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SafetyThreshold&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;safetySettings&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="no"&gt;HARM_CATEGORY_DANGEROUS_CONTENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BLOCK_LOW_AND_ABOVE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;HARM_CATEGORY_SEXUALLY_EXPLICIT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BLOCK_MEDIUM_AND_ABOVE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;HARM_CATEGORY_HARASSMENT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BLOCK_ONLY_HIGH&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="no"&gt;HARM_CATEGORY_HATE_SPEECH&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BLOCK_MEDIUM_AND_ABOVE&lt;/span&gt;
 &lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to make your app safer for your end-users, and to avoid malicious or ill-disposed users, that’s the way to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus point #1: Streaming responses with lambda functions
&lt;/h2&gt;

&lt;p&gt;I’ll round up the review of Gemini-focused features with one little addition I contributed to the project: the ability to pass a lambda instead of a streaming content handler, when using a streaming model.&lt;/p&gt;

&lt;p&gt;This is not Gemini-related, you can use it with any model!&lt;/p&gt;

&lt;p&gt;More concretely, if you want to use Gemini or another model in streaming mode, to see the response being printed as it’s generated by the model, you would usually write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiStreamingChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why is the sky blue?"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamingResponseHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@Override&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;aFewTokens&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aFewTokens&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;

 &lt;span class="nd"&gt;@Override&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using an anonymous inner class implementing the &lt;code&gt;StreamingResponseHandler&lt;/code&gt; interface is quite verbose. Fortunately, I contributed a couple static methods you can import, to make the code a little bit more concise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;langchain4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LambdaStreamingResponseHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onNext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;langchain4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LambdaStreamingResponseHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onNextAndError&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;

&lt;span class="c1"&gt;// onNext&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why is the sky blue?"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;onNext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// onNextAndError&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why is the sky blue?"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;onNextAndError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can stream your LLM output in a single instruction!&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus point #2: Generating stunning images with Imagen v3
&lt;/h2&gt;

&lt;p&gt;A second bonus point in this new LangChain4j release is the fact that the Vertex AI Image model now supports&lt;a href="https://deepmind.google/technologies/imagen-3/" rel="noopener noreferrer"&gt;Imagen v3&lt;/a&gt; (Google DeepMind’s latest high-quality image generation model).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; To use the Imagen model, you’ll still have to be allow-listed for now. You’ll need to &lt;a href="https://docs.google.com/forms/d/1cqt9padvfMgqn23W5FMPTqh7bW1KLkEOsC5G6uC-uuM/viewform" rel="noopener noreferrer"&gt;fill this form&lt;/a&gt;to request access to the model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a few new parameters that are available that you can take advantage of when generating pictures. Let’s have a look at the following image generation code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;imagenModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiImageModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PROJECT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publisher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"google"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"imagen-3.0-generate-preview-0611"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;aspectRatio&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VertexAiImageModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AspectRatio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LANDSCAPE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VertexAiImageModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MimeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;JPEG&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compressionQuality&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watermark&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true by default with Imagen v3&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPersisting&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logResponses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""
 An oil painting close-up, with heavy brush strokes full of
 paint, of two hands shaking together, a young one, and an
 old one conveying a sense of heartfelt thanks and connection
 between generations
 """&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;imageResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imagenModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imageResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see the resulting picture?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bPaDanc2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/imagen-v3-two-hands-shaking.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bPaDanc2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/imagen-v3-two-hands-shaking.jpg" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the code above, you certainly noticed the new builder methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aspectRatio()&lt;/code&gt; — not only square, but wide and narrow landscape and portrait modes are available,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mimeType()&lt;/code&gt; — in addition to PNG, you can request JPEG image generation,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;comressionQuality()&lt;/code&gt; — when requesting JPEG, you can chose the level of compression for encoding the image,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watermark()&lt;/code&gt; — to have all your generated images be watermarked with &lt;a href="https://deepmind.google/technologies/synthid/" rel="noopener noreferrer"&gt;SynthId&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logRequest()&lt;/code&gt; / &lt;code&gt;logResponse()&lt;/code&gt; — to see what is exchanged with the model, in and out,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;persistToCloudStorage()&lt;/code&gt; — to specify you want the image saved in a cloud storage bucket (not used in this example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you get a chance, and request access to Imagen v3, you’ll notice really great quality improvements compared to v2!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Lots of new Gemini related features in this&lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.32.0" rel="noopener noreferrer"&gt;release of LangChain4j&lt;/a&gt;! I hope this article helped you learn about them, and will make you want to use them in your projects.&lt;/p&gt;

&lt;p&gt;If you want to go hands-on with Gemini with LangChain4j, don’t forget to check out my self-paced codelab:&lt;a href="https://dev.to/glaforge/gemini-codelab-for-java-developers-using-langchain4j-g5n-temp-slug-1278985"&gt;Gemini codelabg for Java developers, using LangChain4j&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The power of embeddings: How numbers unlock the meaning of data</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Tue, 02 Jul 2024 07:05:07 +0000</pubDate>
      <link>https://forem.com/googlecloud/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data-527k</link>
      <guid>https://forem.com/googlecloud/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data-527k</guid>
      <description>&lt;h2&gt;
  
  
  Prelude
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;As I’m focusing a lot on Generative AI, I’m curious about how things work under the hood, to better understand what I’m using in my gen-ai powered projects. A topic I’d like to focus on more is: &lt;strong&gt;vector embeddings&lt;/strong&gt; , to explain more clearly what they are, how they are calculated, and what you can do with them.&lt;/p&gt;

&lt;p&gt;A colleague of mine, &lt;a href="https://x.com/andreban" rel="noopener noreferrer"&gt;André&lt;/a&gt;, was showing me a &lt;a href="https://writer-m4n3dyfjhq-uc.a.run.app/" rel="noopener noreferrer"&gt;cool experiment&lt;/a&gt;he’s been working on, to help people prepare an interview, with the help of an AI, to shape the structure of the resulting final article to write.&lt;/p&gt;

&lt;p&gt;The idea is to provide: a topic, a target audience, and to describe the goals for the audience. Then, a large language model like &lt;a href="https://deepmind.google/technologies/gemini/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; prepares a list of questions (that you can update freely) on that topic. Next, it’s your turn to fill in the blanks, answer those questions, and then the LLM generates an article, with a plan following those key questions and your provided answers. I cheated a bit, and asked &lt;a href="https://gemini.google.com/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; itself those questions, and honestly, I really liked how the resulting article came to be, and I wanted to share with you the outcome below.&lt;/p&gt;

&lt;p&gt;It’s a great and simple introduction to vector embeddings! I like how AI can help organize information, shape the structure and the content for an article. &lt;strong&gt;I’m not advocating for letting AI write all your articles&lt;/strong&gt; , far from that, but as an author, however, I like that it can help me avoid the blank page syndrome, avoid missing key elements in my dissertation, improve the quality of my written prose.&lt;/p&gt;

&lt;p&gt;Generative AI, in its creative aspect, and as your assistant, can be super useful! Use it as &lt;strong&gt;a tool to help drive your creativity&lt;/strong&gt;! But &lt;strong&gt;always use your critical sense to gauge the quality and factuality of the content&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction: What are vector embeddings?
&lt;/h2&gt;

&lt;p&gt;Imagine you have a vast library filled with books on every topic imaginable. Finding a specific book can be a daunting task, especially if you only know the general subject matter. Now imagine a magical system that can understand the meaning of each book and represent it as a unique code. This code, called a vector embedding, can then be used to quickly find the most relevant books based on your search query, even if you only have a vague idea of what you’re looking for.&lt;/p&gt;

&lt;p&gt;This is the power of vector embeddings. They are essentially numerical representations of complex data, like text, images, or audio, that capture the underlying meaning and relationships within the data. These numerical codes, arranged as vectors, allow computers to process and compare data in a way that mimics human understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Text to Numbers: The Journey of Embedding Creation
&lt;/h2&gt;

&lt;p&gt;Creating vector embeddings involves a multi-step process that transforms raw data into meaningful mathematical representations. The journey begins with  &lt;strong&gt;data preprocessing&lt;/strong&gt; , where the data is cleaned, normalized, and prepared for embedding generation. This might involve tasks like removing irrelevant information, standardizing data formats, and breaking text into individual words or subwords (tokenization).&lt;/p&gt;

&lt;p&gt;Next comes the heart of the process:  &lt;strong&gt;embedding generation&lt;/strong&gt;. This step leverages various techniques and algorithms, such as Word2Vec, GloVe, BERT, and ResNet, to convert each data point into a high-dimensional vector. The specific algorithm chosen depends on the type of data being embedded (text, images, or audio) and the intended application.&lt;/p&gt;

&lt;p&gt;For instance, Word2Vec uses a neural network to learn relationships between words by analyzing how they co-occur in large text corpora. This results in vector representations for words, where similar words have similar vectors, capturing semantic relationships. Similarly, for images, convolutional neural networks (CNNs) like ResNet can be used to extract features from images, resulting in vectors that represent the visual content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector Databases: The Power of Storing and Searching Embeddings
&lt;/h2&gt;

&lt;p&gt;Once embeddings are generated, they need a dedicated storage system for efficient retrieval and comparison. This is where  &lt;strong&gt;vector databases&lt;/strong&gt;  come into play. Unlike traditional databases designed for structured data, vector databases are optimized for storing and searching high-dimensional vector data.&lt;/p&gt;

&lt;p&gt;Vector databases employ specialized indexing techniques, such as Annoy, HNSW, and Faiss, to create efficient data structures that allow for fast similarity search. This means that when a user submits a query (e.g., a search term, an image), the database can quickly find the most similar data points based on the similarity of their vector representations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embeddings Empower Search: Finding the Needle in the Haystack
&lt;/h2&gt;

&lt;p&gt;The combination of vector embeddings and vector databases revolutionizes search by enabling  &lt;strong&gt;semantic search&lt;/strong&gt;. This means that instead of relying solely on keyword matching, search engines can understand the meaning behind the data and find relevant results even if the query doesn’t use exact keywords.&lt;/p&gt;

&lt;p&gt;For example, imagine searching for “a picture of a dog with a hat.” Traditional keyword-based search might struggle to find relevant images, as the search term might not match the image description. However, with vector embeddings, the search engine can understand the semantic meaning of the query and find images that contain both a dog and a hat, even if those words are not explicitly mentioned in the image description.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Search: Expanding the Reach of Embeddings
&lt;/h2&gt;

&lt;p&gt;Vector embeddings are not limited to search applications. They have become essential tools in a wide range of fields, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval Augmented Generation (RAG):&lt;/strong&gt; This technique combines the power of information retrieval and generative models to create more informative and relevant responses. Embeddings are used to find relevant information in large text corpora, which is then used to augment prompts for language models, resulting in more accurate and context-aware outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Classification:&lt;/strong&gt;  Embeddings enable the classification of data points into different categories based on their similarity. This finds application in areas like sentiment analysis, spam detection, object recognition, and music genre classification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anomaly Detection:&lt;/strong&gt;  By representing data points as vectors, anomalies can be identified as data points that are significantly different from the majority. This technique is used in various fields, including network intrusion detection, fraud detection, and industrial sensor monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Facing the Challenges and Shaping the Future
&lt;/h2&gt;

&lt;p&gt;While vector embeddings have revolutionized data analysis, they still face some challenges. These include the difficulty of capturing polysemy (multiple meanings of a word), contextual dependence, and the challenge of interpreting the meaning behind the high-dimensional vector representations.&lt;/p&gt;

&lt;p&gt;Despite these limitations, research continues to push the boundaries of vector embeddings. Researchers are exploring techniques like contextual embeddings, multilingual embeddings, knowledge graph integration, and explainable embeddings to overcome existing limitations and unlock the full potential of these powerful representations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stepping into the World of Embeddings: Resources and Next Steps
&lt;/h2&gt;

&lt;p&gt;For those interested in diving deeper into the world of vector embeddings, a wealth of resources is available. Online courses and tutorials on platforms like Coursera, Fast.ai, and Stanford’s online learning platform provide a solid foundation in the underlying concepts and techniques.&lt;/p&gt;

&lt;p&gt;Books like “Speech and Language Processing” by Jurafsky and Martin and “Deep Learning” by Goodfellow, Bengio, and Courville offer in-depth coverage of the field. Additionally, research papers and articles on platforms like arXiv and Medium offer insights into the latest advancements and applications.&lt;/p&gt;

&lt;p&gt;To gain practical experience, explore Python libraries like Gensim, spaCy, and TensorFlow/PyTorch. These libraries provide tools for creating and working with embeddings, allowing you to build your own models and experiment with various applications.&lt;/p&gt;

&lt;p&gt;The world of vector embeddings is constantly evolving, offering exciting opportunities for innovation and discovery. By understanding the power of these representations, you can unlock new possibilities for data analysis, information retrieval, and artificial intelligence applications.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Functional builders in Java with Jilt</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Mon, 17 Jun 2024 18:31:25 +0000</pubDate>
      <link>https://forem.com/glaforge/functional-builders-in-java-with-jilt-24ih</link>
      <guid>https://forem.com/glaforge/functional-builders-in-java-with-jilt-24ih</guid>
      <description>&lt;p&gt;A few months ago, I shared an article about what I called Java&lt;a href="https://dev.to/glaforge/functional-builder-approach-in-java-55j5"&gt;functional builders&lt;/a&gt;, inspired by an equivalent pattern found in Go. The main idea was to have builders that looked like this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LanguageModel&lt;/span&gt; &lt;span class="n"&gt;languageModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cool-model"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-project"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a generative model"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to the more tranditional builder approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re using the &lt;code&gt;new&lt;/code&gt; keyword again to construct instances.&lt;/li&gt;
&lt;li&gt;There’s no more &lt;code&gt;build()&lt;/code&gt; method, which felt a bit verbose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared to using constructors with tons of parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have methods like in traditional builders, that say what each parameter is about (&lt;code&gt;name()&lt;/code&gt;, &lt;code&gt;temperature()&lt;/code&gt;…) a bit similar to named parameters in some programming languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The approach I followed was to take advantage of lambda functions under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;ModelOption&lt;/span&gt; &lt;span class="nf"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Float&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there were a few downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Of course, it’s not very conventional! So it can be a bit disturbing for people used to classical builders.&lt;/li&gt;
&lt;li&gt;I didn’t make the distinction between required and optional parameters (they were all optional!)&lt;/li&gt;
&lt;li&gt;The internal fields were not &lt;code&gt;final&lt;/code&gt;, and I felt they should be.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discovering Jilt
&lt;/h2&gt;

&lt;p&gt;When searching on this topic, I found &lt;a href="https://x.com/adam_ruka" rel="noopener noreferrer"&gt;Adam Ruka&lt;/a&gt;’s great annotation processor library:&lt;a href="https://github.com/skinny85/jilt" rel="noopener noreferrer"&gt;Jilt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the really cool features of Jilt is its staged builder concept, which makes builders very type-safe, and forces you to call all the required property methods by chaining them. I found this approach very elegant.&lt;/p&gt;

&lt;p&gt;Adam heard about my functional builder approach, and decided to implement this new style of builder in Jilt. There are a few differences with my implementation, but it palliates some of the downsides I mentioned.&lt;/p&gt;

&lt;p&gt;Let’s have a look at what functional builders looks like from a usage standpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LanguageModel&lt;/span&gt; &lt;span class="n"&gt;languageModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;languageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cool-model"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-project"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a generative model"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to my approach, you’re not using constructors (as annotation processors can’t change existing classes), so you have to use a static method instead. But otherwise, inside that method call, you have the named-parameter-like methods you’re used to use in builders.&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;name()&lt;/code&gt;, &lt;code&gt;project()&lt;/code&gt; and &lt;code&gt;temperature()&lt;/code&gt; are mandatory, and you’d get a compilation error if you forgot one of them. But &lt;code&gt;description()&lt;/code&gt; is optional and can be ommitted.&lt;/p&gt;

&lt;p&gt;Let’s now look at the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jilt.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jilt.BuilderStyle&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jilt.Opt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;jilt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LanguageModelBuilder&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;jilt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LanguageModelBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="nc"&gt;LanguageModel&lt;/span&gt; &lt;span class="n"&gt;languageModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;languageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cool-model"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-project"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a generative model"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuilderStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FUNCTIONAL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;LanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
 &lt;span class="nd"&gt;@Opt&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used a Java &lt;code&gt;record&lt;/code&gt; but it could be a good old POJO. You must annotate that class with the &lt;code&gt;@Builder&lt;/code&gt; annotation. The &lt;code&gt;style&lt;/code&gt; parameter specifies that you want to use a &lt;em&gt;functional&lt;/em&gt; builder. Notice the use of the &lt;code&gt;@Opt&lt;/code&gt; annotation to say that a parameter is not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Derived instance creation
&lt;/h2&gt;

&lt;p&gt;Let me close this article with another neat trick offered by Jilt, which is how to build other instances from existing ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BuilderStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FUNCTIONAL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"derive"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;LanguageModel&lt;/span&gt;&lt;span class="o"&gt;(...)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="nc"&gt;LanguageModel&lt;/span&gt; &lt;span class="n"&gt;derivedModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;derive&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;languageModel&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"new-name"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding the &lt;code&gt;toBuilder = "derive"&lt;/code&gt; parameter to the annotation, you get the ability to create new instances similar to the original one, but you can change both required and optional parameters, to derive a new instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to try Jilt!
&lt;/h2&gt;

&lt;p&gt;You can try functional builders in &lt;a href="https://github.com/skinny85/jilt" rel="noopener noreferrer"&gt;Jilt 1.6&lt;/a&gt; which was just released a few days ago!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Let's make Gemini Groovy!</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Mon, 03 Jun 2024 09:49:26 +0000</pubDate>
      <link>https://forem.com/googlecloud/lets-make-gemini-groovy-dgh</link>
      <guid>https://forem.com/googlecloud/lets-make-gemini-groovy-dgh</guid>
      <description>&lt;p&gt;The happy users of &lt;a href="https://gemini.google.com/advanced" rel="noopener noreferrer"&gt;Gemini Advanced&lt;/a&gt;, the powerful AI web assistant powered by the Gemini model, can execute some Python code, thanks to a built-in Python interpreter. So, for math, logic, calculation questions, the assistant can let Gemini invent a Python script, and execute it, to let users get a more accurate answer to their queries.&lt;/p&gt;

&lt;p&gt;But wearing my &lt;a href="https://groovy-lang.org/" rel="noopener noreferrer"&gt;Apache Groovy&lt;/a&gt; hat on, I wondered if I could get Gemini to invoke some Groovy scripts as well, for advanced math questions!&lt;/p&gt;

&lt;h2&gt;
  
  
  LangChain4j based approach
&lt;/h2&gt;

&lt;p&gt;As usual, my tool of choice for any LLM problem is the powerful &lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt; framework! Interestingly, there are already some code engine integrations,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;a href="https://www.graalvm.org/latest/reference-manual/polyglot-programming/" rel="noopener noreferrer"&gt;GraalVM Polyglot Truffle&lt;/a&gt; engine, that can execute Python and JavaScript code,&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://judge0.com/" rel="noopener noreferrer"&gt;Judge0&lt;/a&gt; engine that uses the Judge0 online code execution system, which also supports Groovy!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I haven’t tried Judge0 yet, as I saw it was supporting Groovy 3 only, and not yet Groovy 4. But for math or logic questions, Groovy 3 is just fine anyway. Instead, I wanted to explore how to create my own Groovy interpreter!&lt;/p&gt;

&lt;p&gt;In the following experiment, I’m going to use the &lt;a href="https://deepmind.google/technologies/gemini/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; model, because it supports &lt;em&gt;function calling&lt;/em&gt;, which means we can instruct the model that it can use some tools when needed.&lt;/p&gt;

&lt;p&gt;Let’s walk through this step by step.&lt;/p&gt;

&lt;p&gt;First, I instantiate a Gemini chat model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MY_GCP_PROJECT_ID"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-central1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash-001"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I create a tool that is able to run Groovy code, thanks to the &lt;code&gt;GroovyShell&lt;/code&gt; evaluator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroovyInterpreter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@Tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Execute a Groovy script and return the result of its execution."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;executeGroovyScript&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nd"&gt;@P&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The groovy script source code to execute"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;groovyScript&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groovyScript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%n--&amp;gt; Executing the following Groovy script:%n%s%n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GroovyShell&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"null"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;@Tool&lt;/code&gt; annotation that describes what this tool can do. And the &lt;code&gt;@P&lt;/code&gt; annotation which explains what the parameter is about.&lt;/p&gt;

&lt;p&gt;I noticed that sometimes the raw script that Gemini suggested contained some &lt;code&gt;\n&lt;/code&gt; strings, instead of the plain newline characters, so I’m replacing them with newlines instead.&lt;/p&gt;

&lt;p&gt;I return a map containing either a result (as a string), or an error message if one was encountered.&lt;/p&gt;

&lt;p&gt;Now it’s time to create our assistant contract, in the form of an interface, but with a very carefully crafted system instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;GroovyAssistant&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 You are a problem solver equipped with the capability of \
 executing Groovy scripts.
 When you need to or you're asked to evaluate some math \
 function, some algorithm, or some code, use the \
 `executeGroovyScript` function, passing a Groovy script \
 that implements the function, the algorithm, or the code \
 that needs to be run.
 In the Groovy script, return a value. Don't print the result \
 to the console.
 Don't use semicolons in your Groovy scripts, it's not necessary.
 When reporting the result of the execution of a script, \
 be sure to show the content of that script.
 Call the `executeGroovyScript` function only once, \
 don't call it in a loop.
 """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This complex system instruction above tells the model what its role is, and that it should call the provided Groovy script execution function whenever it encounters the need to calculate some function, or execute some logic.&lt;/p&gt;

&lt;p&gt;I also instruct it to return values instead of printing results.&lt;/p&gt;

&lt;p&gt;Funnily, Gemini is a pretty decent Groovy programmer, but it insists on always adding semi-colons like in Java, so for a more &lt;em&gt;idiomatic&lt;/em&gt; code style, I suggest it to get rid of them!&lt;/p&gt;

&lt;p&gt;The final step is now to create our LangChain4j AI service with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;assistant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AiServices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GroovyAssistant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatLanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatMemory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MessageWindowChatMemory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withMaxMessages&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GroovyInterpreter&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I combine the Gemini chat model, with a memory to keep track of users’ requests, and the Groovy interpreter tool I’ve just created.&lt;/p&gt;

&lt;p&gt;Now let’s see if Gemini is able to create and calculate a fibonacci function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;assistant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"Write a `fibonacci` function, and calculate `fibonacci(18)`"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output is as follows:&lt;/p&gt;

&lt;blockquote&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def fibonacci(n) {
  if (n &amp;lt;= 1) {
    return n
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2)
  }
}
fibonacci(18)

&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The result of executing the script is: 2584.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;It took me a bit of time to find the right system instruction to get Groovy scripts that complied to my requirements. However, I noticed sometimes some internal errors returned by the model, which I haven’t fully understood (and particularly why those happen at all)&lt;/p&gt;

&lt;p&gt;On some occasions, I also noticed that LangChain4j keeps sending the same script for execution, in a loop. Same thing: I still have to investigate why this rare behavior happens.&lt;/p&gt;

&lt;p&gt;So this solution is a fun experiment, but I’d call it just that, an experiment, as it’s not as rock-solid as I want it to be. But if I manage to make it more bullet-proof, maybe I could contribute it back as a dedicated execution engine for LangChain4j!&lt;/p&gt;

&lt;h2&gt;
  
  
  Full source code
&lt;/h2&gt;

&lt;p&gt;Here’s the full content of my experiment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.agent.tool.P&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.agent.tool.Tool&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.memory.chat.MessageWindowChatMemory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.model.vertexai.VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.service.AiServices&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.service.SystemMessage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;groovy.lang.GroovyShell&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroovyCodeInterpreterAssistant&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MY_GCP_PROJECT_ID"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-central1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash-001"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroovyInterpreter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@Tool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Execute a Groovy script and return the result of its execution."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;executeGroovyScript&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nd"&gt;@P&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The groovy script source code to execute"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;groovyScript&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%n--&amp;gt; Raw Groovy script:%n%s%n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groovyScript&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groovyScript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%n--&amp;gt; Executing:%n%s%n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GroovyShell&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;evaluate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"null"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;GroovyAssistant&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 You are a problem solver equipped with the capability of \
 executing Groovy scripts.
 When you need to or you're asked to evaluate some math \
 function, some algorithm, or some code, use the \
 `executeGroovyScript` function, passing a Groovy script \
 that implements the function, the algorithm, or the code \
 that needs to be run.
 In the Groovy script, return a value. Don't print the result \
 to the console.
 Don't use semicolons in your Groovy scripts, it's not necessary.
 When reporting the result of the execution of a script, \
 be sure to show the content of that script.
 Call the `executeGroovyScript` function only once, \
 don't call it in a loop.
 """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;

 &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;assistant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AiServices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GroovyAssistant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatLanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatMemory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MessageWindowChatMemory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withMaxMessages&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GroovyInterpreter&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;assistant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"Write a `fibonacci` function, and calculate `fibonacci(18)`"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Grounding Gemini with Web Search results in LangChain4j</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Tue, 28 May 2024 05:42:43 +0000</pubDate>
      <link>https://forem.com/googlecloud/grounding-gemini-with-web-search-results-in-langchain4j-4f49</link>
      <guid>https://forem.com/googlecloud/grounding-gemini-with-web-search-results-in-langchain4j-4f49</guid>
      <description>&lt;p&gt;The latest &lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.31.0" rel="noopener noreferrer"&gt;release of LangChain4j&lt;/a&gt; (version 0.31) added the capability of &lt;em&gt;grounding&lt;/em&gt; large language models with results from web searches. There’s an integration with&lt;a href="https://developers.google.com/custom-search/v1/overview" rel="noopener noreferrer"&gt;Google Custom Search Engine&lt;/a&gt;, and also &lt;a href="https://tavily.com/" rel="noopener noreferrer"&gt;Tavily&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The fact of &lt;em&gt;grounding&lt;/em&gt; an LLM’s response with the results from a search engine allows the LLM to find relevant information about the query from web searches, which will likely include up-to-date information that the model won’t have seen during its training, past its cut-off date when the training ended.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Remark:&lt;/strong&gt; Gemini has a built-in &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/overview#ground-public" rel="noopener noreferrer"&gt;Google Web Search grounding&lt;/a&gt;capability, however, LangChain4j’s Gemini integration doesn’t yet surface this feature. I’m currently working on a pull request to support this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Asking questions to your website
&lt;/h2&gt;

&lt;p&gt;An interesting use case for LLM web search grounding is for example if you want to search a particular website. I was interested in asking questions related to articles that I have posted on my personal website and blog. Let’s see, step by step, how you can implement this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a custom search engine
&lt;/h3&gt;

&lt;p&gt;First of all, as I decided to use Google Custom Search, I created a new custom search engine. I won’t detail the steps involved in this process, as it’s explained in the &lt;a href="https://developers.google.com/custom-search/docs/tutorial/creatingcse" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. I created a custom search searching only the content on my website: &lt;a href="https://glaforge.dev" rel="noopener noreferrer"&gt;glaforge.dev&lt;/a&gt;. But you can potentially search the whole internet if you wish, or just your company website, etc.&lt;/p&gt;

&lt;p&gt;Google Custom Search gave me an API key, as well as a Custom Search ID (csi) for my newly created custom search engine. You can test the custom search engine with that ID with this URL:&lt;a href="https://programmablesearchengine.google.com/controlpanel/overview?cx=YOUR_CSI_HERE" rel="noopener noreferrer"&gt;https://programmablesearchengine.google.com/controlpanel/overview?cx=YOUR_CSI_HERE&lt;/a&gt;. It gives you a Google Search-like interface where you can enter your queries. There’s also a widget that you can integrate in your website if you wish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;First of all, I configure the chat model I want to use. I’m using the latest and fastest Gemini model: &lt;a href="https://deepmind.google/technologies/gemini/flash/" rel="noopener noreferrer"&gt;Gemini 1.5 Flash&lt;/a&gt;. I’ve saved my Google Cloud project ID and locaction in environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PROJECT_ID"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOCATION"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash-001"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I configure my web search engine. Here, I’m using Google Search, but it could be Tavily as well. I also saved my API key and the ID of my custom web search in environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebSearchEngine&lt;/span&gt; &lt;span class="n"&gt;webSearchEngine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GoogleCustomWebSearchEngine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CUSTOM_SEARCH_API_KEY"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CUSTOM_SEARCH_CSI"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// .logRequests(true)&lt;/span&gt;
&lt;span class="c1"&gt;// .logResponses(true)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you can log the requests and responses, for debugging purpose.&lt;/p&gt;

&lt;p&gt;Next, I define a &lt;em&gt;content retriever&lt;/em&gt;, this is a way to let LangChain4j know that &lt;em&gt;content&lt;/em&gt; can be &lt;em&gt;retrieved&lt;/em&gt; from a particular tool or location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ContentRetriever&lt;/span&gt; &lt;span class="n"&gt;contentRetriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSearchContentRetriever&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;webSearchEngine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webSearchEngine&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxResults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I define the contract I want to use to interact with my Gemini model, by creating my own custom search &lt;code&gt;interface&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SearchWebsite&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface will be implemented by LangChain4j’s &lt;code&gt;AiServices&lt;/code&gt; system that binds several components together: the chat language model (here, Gemini), and the web search content retriever I created above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;SearchWebsite&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AiServices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SearchWebsite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatLanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contentRetriever&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentRetriever&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I can ask my question to the LLM, which will find the relevant information in my blog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"How can I call the Gemma model from LangChain4j?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I comment out the line &lt;code&gt;contentRetriever(contentRetriever)&lt;/code&gt;, Gemini does a best effort at answering my question, but since there’s nothing in its training data (before its cut-off date) about how to call the &lt;a href="https://blog.google/technology/developers/gemma-open-models/" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt; model from LangChain4j, it is not able to provide a useful answer.&lt;/p&gt;

&lt;p&gt;But with the web search content retriever, Gemini is able to find the right material to ground its answer, as the custom search returns my article on&lt;a href="https://dev.to/glaforge/calling-gemma-with-ollama-testcontainers-and-langchain4j-3jk0-temp-slug-5463502"&gt;calling Gemma with Ollama, Testcontainers, and LangChain4j&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Based on the provided information, you can call the Gemma model from
LangChain4j using the following approach:
1. **Use Ollama:** The articles highlight Ollama as a tool for
interacting with Gemma. You would need to set up Ollama and ensure it
has access to the Gemma model.
2. **Integrate TestContainers:** TestContainers helps you manage
containerized environments for testing. You can use it to run Ollama
within a container alongside LangChain4j.
3. **Utilize LangChain4j:** LangChain4j provides the framework for
interacting with large language models. You would define your prompt,
send it to Ollama (which runs Gemma), and receive the response back
through LangChain4j.
**Example Steps:**
1. **Set up Ollama:** Install Ollama and configure it to use the
Gemma model.
2. **Create a Dockerfile:** Use a Dockerfile to define an image that
includes Ollama and any dependencies.
3. **Run Ollama in a container using TestContainers:** Start the
container using TestContainers and ensure it is accessible from your
LangChain4j code.
4. **Implement LangChain4j calls:** Use LangChain4j to construct your
prompt and send it to Ollama (which will pass it to Gemma).
5. **Receive and process the response:** Receive the generated response
from Gemma and process it as needed in your Java application.
**Note:** These steps provide a general approach. You will need to
refer to the documentation for Ollama, TestContainers, and LangChain4j
for specific implementation details.
This method leverages Ollama as an intermediary to access Gemma.
If you have access to Google's Gemini model directly, you might be
able to integrate it with LangChain4j without the Ollama step,
depending on the specific API or SDK offered by Google.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM found that I have to use &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; and&lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;TestContainers&lt;/a&gt;, as explained in my article. This information wasn’t part of my query, so it proves that it really found the info in the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;The LLM based its answer on the &lt;em&gt;excerpts&lt;/em&gt; contained in the search results, not the whole content of the article, so some aspects of this answer are not totally correct: For instance, you don’t have to &lt;em&gt;install&lt;/em&gt; Ollama or create your own &lt;em&gt;Dockerfile&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To make the response perfect, I believe we would have to combine web search results with Retrieval Augmented Generation, or pass the whole context of the article to the model, so that it could provide a more thorough and factual answer.&lt;/p&gt;

&lt;p&gt;For different queries that lead to shorter answers, the answer would probably be more to the point.&lt;/p&gt;

&lt;p&gt;Another approach is to annotate our &lt;code&gt;String search(String query)&lt;/code&gt; method with a &lt;code&gt;@SystemInstruction()&lt;/code&gt;with instructions that encourage the LLM to provide a shorter answer. But it’s difficult to find the right balance between too long and too short, and of course without any sort of hallucinations!&lt;/p&gt;

&lt;p&gt;For example, you can try with the following system instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SearchWebsite&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 Provide a paragraph-long answer, not a long step by step explanation.
 Reply with "I don't know the answer" if the provided information isn't relevant.
 """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I got the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The provided information mentions using Gemma with Ollama,
TestContainers, and LangChain4j. You can use Ollama, a local
LLM server, and TestContainers, which provides lightweight,
disposable containers, to set up a testing environment.
Then, with LangChain4j, a Java library for interacting with LLMs,
you can call Gemma through the Ollama server.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is shorter and more factual, without being too short either!&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;In an upcoming article, I’ll show you how to use Gemini’s built-in Google Search grounding, but first, I have to finish my pull request for the LangChain4j project!&lt;/p&gt;

&lt;p&gt;Or I can explore how to reply more precisely to queries that lead to complex answers like the above, maybe combinging a RAG approach to get the full context of the article found by the web search.&lt;/p&gt;

&lt;p&gt;Also, the Tavily API seems to be able to return the raw content of the article, so maybe it can help giving the LLM the full context of the article to base its answers on it. So that may be worth comparing those two web search integrations too.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Full sample code
&lt;/h2&gt;

&lt;p&gt;For reference, here is the full sample (with the system instruction approach):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.model.vertexai.VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.rag.content.retriever.ContentRetriever&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.rag.content.retriever.WebSearchContentRetriever&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.service.AiServices&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.service.SystemMessage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.web.search.WebSearchEngine&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.langchain4j.web.search.google.customsearch.GoogleCustomWebSearchEngine&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GroundingWithSearch&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VertexAiGeminiChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PROJECT_ID"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LOCATION"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemini-1.5-flash-001"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="nc"&gt;WebSearchEngine&lt;/span&gt; &lt;span class="n"&gt;webSearchEngine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GoogleCustomWebSearchEngine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CUSTOM_SEARCH_API_KEY"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csi&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GOOGLE_CUSTOM_SEARCH_CSI"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// .logRequests(true)&lt;/span&gt;
&lt;span class="c1"&gt;// .logResponses(true)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="nc"&gt;ContentRetriever&lt;/span&gt; &lt;span class="n"&gt;contentRetriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebSearchContentRetriever&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;webSearchEngine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webSearchEngine&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxResults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SearchWebsite&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nd"&gt;@SystemMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""
 Provide a paragraph-long answer, not a long step by step explanation.
 Reply with "I don't know the answer" if the provided information isn't relevant.
 """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;

 &lt;span class="nc"&gt;SearchWebsite&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AiServices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SearchWebsite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatLanguageModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contentRetriever&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentRetriever&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

 &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"How can I call the Gemma model from LangChain4j?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"response = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Calling Gemma with Ollama, TestContainers, and LangChain4j</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Wed, 03 Apr 2024 17:02:01 +0000</pubDate>
      <link>https://forem.com/googlecloud/calling-gemma-with-ollama-testcontainers-and-langchain4j-4ppk</link>
      <guid>https://forem.com/googlecloud/calling-gemma-with-ollama-testcontainers-and-langchain4j-4ppk</guid>
      <description>&lt;p&gt;Lately, for my Generative AI powered Java apps, I’ve used the &lt;a href="https://deepmind.google/technologies/gemini/#introduction" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt;multimodal large language model from Google. But there’s also &lt;a href="https://blog.google/technology/developers/gemma-open-models/" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt;, its little sister model.&lt;/p&gt;

&lt;p&gt;Gemma is a family of lightweight, state-of-the-art open models built from the same research and technology used to create the Gemini models. Gemma is available in two sizes: 2B and 7B. Its weights are freely available, and its small size means you can run it on your own, even on your laptop. So I was curious to give it a run with &lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run Gemma
&lt;/h2&gt;

&lt;p&gt;There are many ways to run Gemma: in the cloud, via &lt;a href="https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/335?project=glaforge" rel="noopener noreferrer"&gt;Vertex AI&lt;/a&gt;with a click of a button, or &lt;a href="https://cloud.google.com/kubernetes-engine/docs/tutorials/serve-gemma-gpu-vllm" rel="noopener noreferrer"&gt;GKE&lt;/a&gt; with some GPUs, but you can also run it locally with &lt;a href="https://github.com/tjake/Jlama" rel="noopener noreferrer"&gt;Jlama&lt;/a&gt; or&lt;a href="https://github.com/google/gemma.cpp" rel="noopener noreferrer"&gt;Gemma.cpp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another good option is to run Gemma with &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, a tool that you install on your machine, and which lets you run small models, like Llama 2, Mistral, and &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;many others&lt;/a&gt;. They quickly added support for &lt;a href="https://ollama.com/library/gemma" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Once installed locally, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ollama run gemma:2b
ollama run gemma:7b

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cherry on the cake, the LangChain4j library provides an&lt;a href="https://docs.langchain4j.dev/integrations/language-models/ollama" rel="noopener noreferrer"&gt;Ollama module&lt;/a&gt;, so you can plug Ollama supported models in your Java applications easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containerization
&lt;/h2&gt;

&lt;p&gt;After a great discussion with my colleague &lt;a href="https://twitter.com/ddobrin" rel="noopener noreferrer"&gt;Dan Dobrin&lt;/a&gt;who had worked with Ollama and TestContainers (&lt;a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/blob/main/sessions/next24/books-genai-vertex-langchain4j/src/test/java/services/OllamaContainerTest.java" rel="noopener noreferrer"&gt;#1&lt;/a&gt; and&lt;a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/blob/main/sessions/next24/books-genai-vertex-langchain4j/src/test/java/services/OllamaChatModelTest.java#L37" rel="noopener noreferrer"&gt;#2&lt;/a&gt;) in his &lt;a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/tree/main" rel="noopener noreferrer"&gt;serverless production readiness workshop&lt;/a&gt;, I decided to try the approach below.&lt;/p&gt;

&lt;p&gt;Which brings us to the last piece of the puzzle: Instead of having to install and run Ollama on my computer, I decided to use Ollama within a container, handled by &lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;TestContainers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;TestContainers is not only useful for testing, but you can also use it for driving containers. There’s even a specific &lt;a href="https://java.testcontainers.org/modules/ollama/" rel="noopener noreferrer"&gt;OllamaContainer&lt;/a&gt; you can take advantage of!&lt;/p&gt;

&lt;p&gt;So here’s the whole picture: &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U-JRUDVQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/gemma-ollama-testcontainers-langchain4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U-JRUDVQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/gemma-ollama-testcontainers-langchain4j.png" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to implement this approach!
&lt;/h2&gt;

&lt;p&gt;You’ll find the code in the Github&lt;a href="https://github.com/glaforge/gemini-workshop-for-java-developers/blob/main/app/src/main/java/gemini/workshop/CallGemma.java" rel="noopener noreferrer"&gt;repository&lt;/a&gt;accompanying my recent &lt;a href="https://codelabs.developers.google.com/codelabs/gemini-java-developers" rel="noopener noreferrer"&gt;Gemini workshop&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with the easy part, interacting with an Ollama supported model with LangChain4j:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;OllamaContainer&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createGemmaOllamaContainer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;ChatLanguageModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OllamaChatModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://%s:%d"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHost&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFirstMappedPort&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gemma:2b"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why is the sky blue?"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You run an Ollama test container.&lt;/li&gt;
&lt;li&gt;You create an Ollama chat model, by pointing at the address and port of the container.&lt;/li&gt;
&lt;li&gt;You specify the model you want to use.&lt;/li&gt;
&lt;li&gt;Then, you just need to call &lt;code&gt;model.generate(yourPrompt)&lt;/code&gt; as usual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Easy? Now let’s have a look at the trickier part, my local method that creates the Ollama container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// check if the custom Gemma Ollama image exists already&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;listImagesCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DockerClientFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lazyClient&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listImagesCmd&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withImageNameFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TC_OLLAMA_GEMMA_2_B&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listImagesCmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Creating a new Ollama container with Gemma 2B image..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="nc"&gt;OllamaContainer&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OllamaContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama/ollama:0.1.26"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
 &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execInContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pull"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gemma:2b"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commitToImage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TC_OLLAMA_GEMMA_2_B&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using existing Ollama container with Gemma 2B image..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
 &lt;span class="c1"&gt;// Substitute the default Ollama image with our Gemma variant&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OllamaContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
 &lt;span class="nc"&gt;DockerImageName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TC_OLLAMA_GEMMA_2_B&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asCompatibleSubstituteFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ollama/ollama"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to create a derived Ollama container that pulls in the Gemma model. Either this image was already created beforehand, or if it doesn’t exist yet, you create it.&lt;/p&gt;

&lt;p&gt;Use the Docker Java client to check if the custom Gemma image exists. If it doesn’t exist, notice how TestContainers let you create an image derived from the base Ollama image, pull the Gemma model, and then commit that image to your local Docker registry.&lt;/p&gt;

&lt;p&gt;Otherwise, if the image already exists (ie. you created it in a previous run of the application), you’re just going to tell TestContainers that you want to substitute the default Ollama image with your Gemma-powered variant.&lt;/p&gt;

&lt;h2&gt;
  
  
  And voila!
&lt;/h2&gt;

&lt;p&gt;You can &lt;strong&gt;call Gemma locally on your laptop, in your Java apps, using LangChain4j&lt;/strong&gt; , without having to install and run Ollama locally (but of course, you need to have a Docker daemon running).&lt;/p&gt;

&lt;p&gt;Big thanks to &lt;a href="https://twitter.com/ddobrin" rel="noopener noreferrer"&gt;Dan Dobrin&lt;/a&gt; for the approach, and to &lt;a href="https://twitter.com/bsideup" rel="noopener noreferrer"&gt;Sergei&lt;/a&gt;, &lt;a href="https://twitter.com/EdduMelendez" rel="noopener noreferrer"&gt;Eddú&lt;/a&gt;and &lt;a href="https://twitter.com/shelajev" rel="noopener noreferrer"&gt;Oleg&lt;/a&gt; from TestContainers for the help and useful pointers.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Gemini codelab for Java developers using LangChain4j</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Wed, 27 Mar 2024 18:11:58 +0000</pubDate>
      <link>https://forem.com/googlecloud/gemini-codelab-for-java-developers-using-langchain4j-9pb</link>
      <guid>https://forem.com/googlecloud/gemini-codelab-for-java-developers-using-langchain4j-9pb</guid>
      <description>&lt;p&gt;No need to be a Python developer to do Generative AI! If you’re a Java developer, you can take advantage of &lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt;to implement some advanced LLM integrations in your Java applications. And if you’re interested in using&lt;a href="https://blog.google/technology/ai/google-gemini-next-generation-model-february-2024/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt;, one of the best models available, I invite you to have a look at the following “codelab” that I worked on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codelabs.developers.google.com/codelabs/gemini-java-developers" rel="noopener noreferrer"&gt;Codelab — Gemini for Java Developers using LangChain4j&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this workshop, you’ll find various examples covering the following use cases, in &lt;em&gt;crescendo&lt;/em&gt; approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making your fist call to Gemini (streaming &amp;amp; non-streaming)&lt;/li&gt;
&lt;li&gt;Maintaining a conversation&lt;/li&gt;
&lt;li&gt;Taking advantage of multimodality by analysing images with your prompts&lt;/li&gt;
&lt;li&gt;Extracting structured information from unstructured text&lt;/li&gt;
&lt;li&gt;Using prompt templates&lt;/li&gt;
&lt;li&gt;Doing text classification with few-shot prompting&lt;/li&gt;
&lt;li&gt;Implementing Retrieval Augmented Generation to chat with your documentation&lt;/li&gt;
&lt;li&gt;How to do Function Calling to expand the LLM to interact with external APIs and services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll find all the &lt;a href="https://github.com/glaforge/gemini-workshop-for-java-developers" rel="noopener noreferrer"&gt;code samples on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re attending Devoxx France, be sure to attend the&lt;a href="https://www.devoxx.fr/en/schedule/talk/?id=40285" rel="noopener noreferrer"&gt;Hands-on-Lab workshop&lt;/a&gt; with my colleagues&lt;a href="https://twitter.com/meteatamel" rel="noopener noreferrer"&gt;Mete Atamel&lt;/a&gt; and &lt;a href="https://twitter.com/val_deleplace" rel="noopener noreferrer"&gt;Valentin Deleplace&lt;/a&gt;who will guide you through this codelab.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Visualize PaLM-based LLM tokens</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Mon, 05 Feb 2024 08:44:22 +0000</pubDate>
      <link>https://forem.com/googlecloud/visualize-palm-based-llm-tokens-1bc5</link>
      <guid>https://forem.com/googlecloud/visualize-palm-based-llm-tokens-1bc5</guid>
      <description>&lt;p&gt;As I was working on tweaking the Vertex AI text embedding model in &lt;a href="https://github.com/langchain4j"&gt;LangChain4j&lt;/a&gt;, I wanted to better understand how the &lt;code&gt;textembedding-gecko&lt;/code&gt;&lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text-embeddings"&gt;model&lt;/a&gt;tokenizes the text, in particular when we implement the&lt;a href="https://arxiv.org/abs/2005.11401"&gt;Retrieval Augmented Generation&lt;/a&gt; approach.&lt;/p&gt;

&lt;p&gt;The various PaLM-based models offer a &lt;code&gt;computeTokens&lt;/code&gt; endpoint, which returns a list of tokens (encoded in Base 64) and their respective IDs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; At the time of this writing, there’s no equivalent endpoint for Gemini models.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I decided to create a &lt;a href="https://tokens-lpj6s2duga-ew.a.run.app/"&gt;small application&lt;/a&gt; that lets users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input some text,&lt;/li&gt;
&lt;li&gt;select a model,&lt;/li&gt;
&lt;li&gt;calculate the number of tokens,&lt;/li&gt;
&lt;li&gt;and visualize them with some nice pastel colors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The available PaLM-based models are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;textembedding-gecko&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;textembedding-gecko-multilingual&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-bison&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;text-unicorn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chat-bison&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code-gecko&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code-bison&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codechat-bison&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can &lt;a href="https://tokens-lpj6s2duga-ew.a.run.app/"&gt;try the application&lt;/a&gt; online.&lt;/p&gt;

&lt;p&gt;And also have a look at the &lt;a href="https://github.com/glaforge/llm-text-tokenization"&gt;source code&lt;/a&gt; on Github. It’s a &lt;a href="https://micronaut.io/"&gt;Micronaut&lt;/a&gt; application. I serve the static assets as explained in my recent&lt;a href="https://dev.to/glaforge/serving-static-assets-with-micronaut-k85"&gt;article&lt;/a&gt;. I deployed the application on &lt;a href="https://cloud.run/"&gt;Google Cloud Run&lt;/a&gt;, the easiest way to deploy a container, and let it auto-scale for you. I did a source based deployment, as explained at the bottom&lt;a href="https://dev.to/glaforge/build-and-deploy-java-17-apps-on-cloud-run-with-cloud-native-buildpacks-on-temurin-e5-temp-slug-9671273"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And &lt;em&gt;voilà&lt;/em&gt; I can visualize my LLM tokens!&lt;/p&gt;

</description>
      <category>java</category>
      <category>llm</category>
      <category>generativeai</category>
    </item>
    <item>
      <title>Image generation with Imagen and LangChain4j</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Thu, 01 Feb 2024 08:25:56 +0000</pubDate>
      <link>https://forem.com/googlecloud/image-generation-with-imagen-and-langchain4j-1f9d</link>
      <guid>https://forem.com/googlecloud/image-generation-with-imagen-and-langchain4j-1f9d</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk4f5abaj812kurqh0xc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk4f5abaj812kurqh0xc.jpg" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week &lt;a href="https://github.com/langchain4j"&gt;LangChain4j&lt;/a&gt;, the LLM orchestration framework for Java developers, released version&lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.26.1"&gt;0.26.1&lt;/a&gt;, which contains my first significant contribution to the open source project: &lt;strong&gt;support for the Imagen image generation model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imagen&lt;/strong&gt; is a text-to-image diffusion model that was&lt;a href="https://imagen.research.google/"&gt;announced&lt;/a&gt; last year. And it recently upgraded to &lt;a href="https://deepmind.google/technologies/imagen-2/"&gt;Imagen v2&lt;/a&gt;, with even higher quality graphics generation. As I was curious to integrate it in some of my generative AI projects, I thought that would be a great first&lt;a href="https://github.com/langchain4j/langchain4j/pull/456"&gt;contribution&lt;/a&gt; to LangChain4j.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt; At the time of this writing, image generation is still only for allow-listed accounts.&lt;/p&gt;

&lt;p&gt;Furthermore, to run the snippets covered below, you should have an account on Google Cloud Platform, created a project, configured a billing account, enabled the Vertex AI API, and authenticated with the gcloud SDK and the command:&lt;code&gt;gcloud auth application-default login&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let’s dive in how to use Imagen v1 and v2 with LangChain4j in Java!&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate your first images
&lt;/h2&gt;

&lt;p&gt;In the following examples, I’m using the following constants, to point at my project details, the endpoint, the region, etc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static final String ENDPOINT = "us-central1-aiplatform.googleapis.com:443";
private static final String LOCATION = "us-central1";
private static final String PROJECT = "YOUR_PROJECT_ID";
private static final String PUBLISHER = "google";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we’re going to create an instance of the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VertexAiImageModel imagenModel = VertexAiImageModel.builder()
 .endpoint(ENDPOINT)
 .location(LOCATION)
 .project(PROJECT)
 .publisher(PUBLISHER)
 .modelName("imagegeneration@005")
 .maxRetries(2)
 .withPersisting()
 .build();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are 2 models you can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;imagegeneration@005&lt;/code&gt; corresponds to Imagen 2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;imagegeneration@002&lt;/code&gt; is the previous version (Imagen 1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we’ll use both models. Why? Because currently Imagen 2 doesn’t support image editing, so we’ll have to use Imagen 1 for that purpose.&lt;/p&gt;

&lt;p&gt;The configuration above uses &lt;code&gt;withPersisting()&lt;/code&gt; to save the generated images in a temporary folder on your system. If you don’t persist the image files, the content of the image is avaiable as Base 64 encoded bytes in the &lt;code&gt;Image&lt;/code&gt;s objects returned. You can also specify &lt;code&gt;persistTo(somePath)&lt;/code&gt; to specify a particular directory where you want the generated files to be saved.&lt;/p&gt;

&lt;p&gt;Let’s create our first image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Response&amp;lt;Image&amp;gt; imageResponse = imagenModel.generate(
 "watercolor of a colorful parrot drinking a cup of coffee");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Response&lt;/code&gt; object wraps the created &lt;code&gt;Image&lt;/code&gt;. You can get the &lt;code&gt;Image&lt;/code&gt; by calling &lt;code&gt;imageResponse.getContent()&lt;/code&gt;. And you can retrieve the URL of the image (if saved locally) with &lt;code&gt;imageResponse.getContent().url()&lt;/code&gt;. The Base 64 encoded bytes can be retrieved with &lt;code&gt;imageResponse.getContent().base64Data()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Some other tweaks to the model configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify the &lt;strong&gt;language&lt;/strong&gt; of the prompt: &lt;code&gt;language("ja")&lt;/code&gt;(if the language is not officially supported, it’s usually translated back to English anyway).&lt;/li&gt;
&lt;li&gt;Define a &lt;strong&gt;negative prompt&lt;/strong&gt; with things you don’t want to see in the picture: &lt;code&gt;negativePrompt("black feathers")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use a particular &lt;strong&gt;seed&lt;/strong&gt; to always generate the same image with the same seed: &lt;code&gt;seed(1234L)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you want to generate a picture of a pizza with a prompt in Japanese, but you don’t want to have pepperoni and pineapple, you could configure your model and generate as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VertexAiImageModel imagenModel = VertexAiImageModel.builder()
 .endpoint(ENDPOINT)
 .location(LOCATION)
 .project(PROJECT)
 .publisher(PUBLISHER)
 .modelName("imagegeneration@005")
 .language("ja")
 .negativePrompt("pepperoni, pineapple")
 .maxRetries(2)
 .withPersisting()
 .build();

Response&amp;lt;Image&amp;gt; imageResponse = imagenModel.generate("ピザ"); // pizza

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Image editing with Imagen 1
&lt;/h2&gt;

&lt;p&gt;With Imagen 1, you can &lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/image/edit-images?hl=en"&gt;edit&lt;/a&gt; existing images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;mask-based editing:&lt;/strong&gt; you can specify a mask, a black &amp;amp; white image where the white parts are the corresponding parts of the original image that should be edited,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mask free editing:&lt;/strong&gt; where you just give a prompt and let the model figure out what should be edited on its own or following the prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When generating and editing with Imagen 1, you can also configure the model to use a particular style (with Imagen 2, you just specify it in the prompt) with &lt;code&gt;sampleImageStyle(VertexAiImageModel.ImageStyle.photograph)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;photograph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;digital_art&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;landscape&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sketch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;watercolor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cyberpunk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop_art&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When editing an image, you may wish to decide how strong or not the modification should be, with &lt;code&gt;.guidanceScale(100)&lt;/code&gt;. Usually, between 0 and 20 or so, it’s lightly edited, between 20 and 100 it’s getting more impactful edits, and 100 and above it’s the maximum edition level.&lt;/p&gt;

&lt;p&gt;Let’s say I generated an image of a lush forrest (I’ll use that as my original image):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VertexAiImageModel model = VertexAiImageModel.builder()
 .endpoint(ENDPOINT)
 .location(LOCATION)
 .project(PROJECT)
 .publisher(PUBLISHER)
 .modelName("imagegeneration@002")
 .seed(19707L)
 .sampleImageStyle(VertexAiImageModel.ImageStyle.photograph)
 .guidanceScale(100)
 .maxRetries(4)
 .withPersisting()
 .build();

Response&amp;lt;Image&amp;gt; forestResp = model.generate("lush forest");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I want to edit my forrest to add a small red tree in the bottom of the image. I’m loading a black and white mask image with a white square at the bottom. And I pass the original image, the mask image, and the modification prompt, to the new &lt;code&gt;edit()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URI maskFileUri = getClass().getClassLoader().getResource("mask.png").toURI();

Response&amp;lt;Image&amp;gt; compositeResp = model.edit(
 forestResp.content(), // original image to edit
 fromPath(Paths.get(maskFileUri)), // the mask image
 "red trees" // the new prompt
);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eBKO5YDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/lush-forrest-red-tree.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eBKO5YDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/gemini/lush-forrest-red-tree.jpg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another kind of editing you can do is to upscale an existing image. As far as I know, it’s only supported for Imagen v1 for now, so we’ll continue with that model.&lt;/p&gt;

&lt;p&gt;In this example, we’ll generate an image of 1024x1024 pixels, and we’ll scale it to 4096x4096:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VertexAiImageModel imagenModel = VertexAiImageModel.builder()
 .endpoint(ENDPOINT)
 .location(LOCATION)
 .project(PROJECT)
 .publisher(PUBLISHER)
 .modelName("imagegeneration@002")
 .sampleImageSize(1024)
 .withPersisting()
 .persistTo(defaultTempDirPath)
 .maxRetries(3)
 .build();

Response&amp;lt;Image&amp;gt; imageResponse =
 imagenModel.generate("A black bird looking itself in an antique mirror");

VertexAiImageModel imagenModelForUpscaling = VertexAiImageModel.builder()
 .endpoint(ENDPOINT)
 .location(LOCATION)
 .project(PROJECT)
 .publisher(PUBLISHER)
 .modelName("imagegeneration@002")
 .sampleImageSize(4096)
 .withPersisting()
 .persistTo(defaultTempDirPath)
 .maxRetries(3)
 .build();

Response&amp;lt;Image&amp;gt; upscaledImageResponse =
 imagenModelForUpscaling.edit(imageResponse.content(), "");

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you have a much bigger image!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s about it for image generation and editing with Imagen in LangChain4j today! Be sure to use LangChain4j v0.26.1 which contains that new integration. And I’m looking forward to seeing the pictures you generate with it!&lt;/p&gt;

</description>
      <category>java</category>
      <category>imagen</category>
      <category>generativeai</category>
      <category>langchain4j</category>
    </item>
    <item>
      <title>Serving static assets with Micronaut</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Sun, 21 Jan 2024 16:23:25 +0000</pubDate>
      <link>https://forem.com/glaforge/serving-static-assets-with-micronaut-k85</link>
      <guid>https://forem.com/glaforge/serving-static-assets-with-micronaut-k85</guid>
      <description>&lt;p&gt;My go-to framework when developing Java apps or microservices is &lt;a href="https://micronaut.io"&gt;Micronaut&lt;/a&gt;. For the apps that should have a web frontend, I rarely use &lt;a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/"&gt;Micronaut Views&lt;/a&gt; and its templating support. Instead, I prefer to just &lt;strong&gt;serve static assets&lt;/strong&gt; from my resource folder, and have some JavaScript framework (usually &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;) to populate my HTML content (often using &lt;a href="https://shoelace.style/"&gt;Shoelace&lt;/a&gt; for its nice Web Components). However, the &lt;a href="https://docs.micronaut.io/latest/guide/#staticResources"&gt;static asset documentation&lt;/a&gt;is a bit light on explanations. So, since I always forget how to configure Micronaut to serve static assets, I thought that would be useful to document this here.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/src/main/resources/application.properties&lt;/code&gt;, I’m adding the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;micronaut.router.static-resources.default.paths=classpath:public
micronaut.router.static-resources.default.mapping=/**
micronaut.router.static-resources.default.enabled=true

micronaut.server.cors.enabled=true

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first line says that my resources will live in &lt;code&gt;src/main/resources/public/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The second line means the pattern will match recursively for sub-directories as well.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;enabled&lt;/code&gt; flag is to activate static serviing (not strictly needed as it’s supposed to be enabled by default).&lt;/li&gt;
&lt;li&gt;I also enabled CORS (cross-origin resource sharing).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then in &lt;code&gt;src/main/resources/public/&lt;/code&gt;, I’ll have my &lt;code&gt;index.html&lt;/code&gt; file, my &lt;code&gt;css&lt;/code&gt; and &lt;code&gt;js&lt;/code&gt; folders.&lt;/p&gt;

</description>
      <category>micronaut</category>
      <category>java</category>
    </item>
    <item>
      <title>Light Mode Bookmarlet</title>
      <dc:creator>Guillaume Laforge</dc:creator>
      <pubDate>Thu, 18 Jan 2024 08:49:01 +0000</pubDate>
      <link>https://forem.com/glaforge/light-mode-bookmarlet-44m2</link>
      <guid>https://forem.com/glaforge/light-mode-bookmarlet-44m2</guid>
      <description>&lt;p&gt;A while ago, my friend Sylvain Wallez shared a little &lt;a href="https://twitter.com/bluxte/status/1729912211882094701"&gt;bookmarlet&lt;/a&gt;on Twitter/X that transforms a dark mode site into light mode. I know the trend is towards dark mode, but for a lot of people with certain vision issues, for example with astigmatism like me, certain dark modes can very painful.&lt;/p&gt;

&lt;p&gt;This site about &lt;a href="https://www.allaboutvision.com/digital-eye-strain/is-dark-mode-better-for-eyes/"&gt;vision&lt;/a&gt;(and you’ll find other similar references) mentions that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;People who have myopia or &lt;strong&gt;astigmatism&lt;/strong&gt; also may experience &lt;strong&gt;halation&lt;/strong&gt; (from the word “halo”). Halation occurs when light spreads past a certain boundary, creating a foggy or blurry appearance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So for certain websites, often with a too strong contrast, I’m using the following bookmarklet trick.&lt;/p&gt;

&lt;p&gt;Go to your bookmark manager, and save the following bookmarklet (I called mine “light mode”):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;javascript:(function(){document.documentElement.style.filter=document.documentElement.style.filter?%27%27:%27invert(100%)%20hue-rotate(180deg)%27})();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to pretty print the above code and remove the URL encoded characters, to decypher what it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(function () {
 document.documentElement.style.filter = document.documentElement.style.filter
 ? ""
 : "invert(100%) hue-rotate(180deg)";
})();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two filters are going to be applied to your current web page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, it will completely &lt;strong&gt;invert&lt;/strong&gt; all the colors, like a negative photography&lt;/li&gt;
&lt;li&gt;Second, compared to Sylvain, I also add a &lt;strong&gt;hue rotation&lt;/strong&gt; of 180 degrees&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the hue rotation
&lt;/h2&gt;

&lt;p&gt;Because the color inversion is also going to shift the colors: a red will become blue, a yellow will be dark blue, a violet will turn pink, etc. With a hue rotation, we get back the right color, a red is still red, a blue is still blue, etc. The different however will be in the lightness, as a light blue becomes dark, and a dark green becomes light. But at least, it’s a bit more faithful to the original images.&lt;/p&gt;

&lt;p&gt;Here’s a picture to highlight the differences. See how the rainbow picture is transformed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pMWBLJSg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/misc/invert-hue-roate.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pMWBLJSg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://glaforge.dev/img/misc/invert-hue-roate.jpg" alt="" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible improvements
&lt;/h2&gt;

&lt;p&gt;Perhaps we could avoid applying the filter globally, or at least avoid to apply it somehow to the images, so that they are not affected by those filters. At least for now, that’s good enough for me!&lt;/p&gt;

</description>
      <category>browser</category>
      <category>chrome</category>
      <category>dark</category>
    </item>
  </channel>
</rss>
