diff --git a/libraries-ai/pom.xml b/libraries-ai/pom.xml
index e1f97e693fa5..3ad7dbc5a9b9 100644
--- a/libraries-ai/pom.xml
+++ b/libraries-ai/pom.xml
@@ -108,6 +108,11 @@
opencsv
${opencsv.version}
+
+ io.github.sashirestela
+ simple-openai
+ ${simpleopenai.version}
+
@@ -133,6 +138,7 @@
4.3.2
3.17.0
5.11
+ 3.22.2
\ No newline at end of file
diff --git a/libraries-ai/src/main/java/com/baeldung/simpleopenai/Client.java b/libraries-ai/src/main/java/com/baeldung/simpleopenai/Client.java
new file mode 100644
index 000000000000..2119e0a6fd0c
--- /dev/null
+++ b/libraries-ai/src/main/java/com/baeldung/simpleopenai/Client.java
@@ -0,0 +1,25 @@
+package com.baeldung.simpleopenai;
+
+import java.lang.System.Logger;
+
+import io.github.sashirestela.openai.SimpleOpenAIGeminiGoogle;
+
+public final class Client {
+
+ public static final Logger LOGGER = System.getLogger("simpleopenai");
+ public static final String CHAT_MODEL = "gemini-2.5-flash";
+ public static final String EMBEDDING_MODEL = "text-embedding-004";
+
+ private Client() {
+ }
+
+ public static SimpleOpenAIGeminiGoogle getClient() {
+ String apiKey = System.getenv("GEMINI_API_KEY");
+ if (apiKey == null || apiKey.isBlank()) {
+ throw new IllegalStateException("GEMINI_API_KEY is not set");
+ }
+ return SimpleOpenAIGeminiGoogle.builder()
+ .apiKey(apiKey)
+ .build();
+ }
+}
diff --git a/libraries-ai/src/main/java/com/baeldung/simpleopenai/GeminiApiKeyCheck.java b/libraries-ai/src/main/java/com/baeldung/simpleopenai/GeminiApiKeyCheck.java
new file mode 100644
index 000000000000..05179014a4f8
--- /dev/null
+++ b/libraries-ai/src/main/java/com/baeldung/simpleopenai/GeminiApiKeyCheck.java
@@ -0,0 +1,17 @@
+package com.baeldung.simpleopenai;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+public class GeminiApiKeyCheck {
+
+ public static void main(String[] args) {
+
+ Logger logger = System.getLogger("simpleopenai");
+ logger.log(Level.INFO,
+ "GEMINI_API_KEY configured: {0}",
+ System.getenv("GEMINI_API_KEY") != null);
+
+ }
+
+}
diff --git a/libraries-ai/src/main/java/com/baeldung/simpleopenai/KeepingConversationStateInJava.java b/libraries-ai/src/main/java/com/baeldung/simpleopenai/KeepingConversationStateInJava.java
new file mode 100644
index 000000000000..91b9981b96cd
--- /dev/null
+++ b/libraries-ai/src/main/java/com/baeldung/simpleopenai/KeepingConversationStateInJava.java
@@ -0,0 +1,59 @@
+package com.baeldung.simpleopenai;
+
+import java.lang.System.Logger.Level;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+
+import io.github.sashirestela.openai.domain.chat.Chat;
+import io.github.sashirestela.openai.domain.chat.ChatMessage;
+import io.github.sashirestela.openai.domain.chat.ChatMessage.AssistantMessage;
+import io.github.sashirestela.openai.domain.chat.ChatMessage.SystemMessage;
+import io.github.sashirestela.openai.domain.chat.ChatMessage.UserMessage;
+import io.github.sashirestela.openai.domain.chat.ChatRequest;
+
+public class KeepingConversationStateInJava {
+
+ public static void main(String[] args) {
+ var client = Client.getClient();
+
+ List history = new ArrayList<>();
+ history.add(SystemMessage.of(
+ "You are a helpful travel assistant. Answer briefly."
+ ));
+
+ try (Scanner scanner = new Scanner(System.in)) {
+ while (true) {
+ System.out.print("You: ");
+ String input = scanner.nextLine();
+ if (input == null || input.isBlank()) {
+ continue;
+ }
+ if ("exit".equalsIgnoreCase(input.trim())) {
+ break;
+ }
+
+ history.add(UserMessage.of(input));
+
+ ChatRequest.ChatRequestBuilder chatRequestBuilder =
+ ChatRequest.builder().model(Client.CHAT_MODEL);
+
+ for (ChatMessage message : history) {
+ chatRequestBuilder.message(message);
+ }
+
+ ChatRequest chatRequest = chatRequestBuilder.build();
+
+ CompletableFuture chatFuture =
+ client.chatCompletions().create(chatRequest);
+ Chat chat = chatFuture.join();
+
+ String reply = chat.firstContent().toString();
+ Client.LOGGER.log(Level.INFO, "Assistant: {0}", reply);
+
+ history.add(AssistantMessage.of(reply));
+ }
+ }
+ }
+}
diff --git a/libraries-ai/src/main/java/com/baeldung/simpleopenai/SingleTurnChatCompletion.java b/libraries-ai/src/main/java/com/baeldung/simpleopenai/SingleTurnChatCompletion.java
new file mode 100644
index 000000000000..2c24174c1d23
--- /dev/null
+++ b/libraries-ai/src/main/java/com/baeldung/simpleopenai/SingleTurnChatCompletion.java
@@ -0,0 +1,30 @@
+package com.baeldung.simpleopenai;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.concurrent.CompletableFuture;
+
+import io.github.sashirestela.openai.domain.chat.Chat;
+import io.github.sashirestela.openai.domain.chat.ChatMessage.UserMessage;
+import io.github.sashirestela.openai.domain.chat.ChatRequest;
+
+public class SingleTurnChatCompletion {
+
+ public static void main(String[] args) {
+ var client = Client.getClient();
+
+ ChatRequest chatRequest = ChatRequest.builder()
+ .model(Client.CHAT_MODEL)
+ .message(UserMessage.of(
+ "Suggest a weekend trip in Japan, no more than 60 words."
+ ))
+ .build();
+
+ CompletableFuture chatFuture =
+ client.chatCompletions().create(chatRequest);
+ Chat chat = chatFuture.join();
+
+ Logger logger = Client.LOGGER;
+ logger.log(Level.INFO, "Model reply: {0}", chat.firstContent());
+ }
+}
diff --git a/libraries-ai/src/main/resources/simpleopenai/gemini-curl-tests.txt b/libraries-ai/src/main/resources/simpleopenai/gemini-curl-tests.txt
new file mode 100644
index 000000000000..e741105c910f
--- /dev/null
+++ b/libraries-ai/src/main/resources/simpleopenai/gemini-curl-tests.txt
@@ -0,0 +1,193 @@
+# Gemini curl tests for simple-openai compatible features
+#
+# Environment
+# These examples assume a POSIX compatible shell such as bash on a Linux machine.
+# The commands have been exercised on Linux and should also work on macOS with a few notes:
+# - macOS provides base64 without the -w0 option. To produce base64 on a single line we can use:
+# BASE64_IMAGE=$(base64 "$IMAGE_PATH" | tr -d '\n')
+# instead of the base64 -w0 call in Test 4.
+# On Windows the simplest way to run these commands is to use a Unix like environment such as
+# Windows Subsystem for Linux or Git Bash. PowerShell can also be used but the syntax for
+# environment variables and pipes will be different from the examples here.
+#
+# Prerequisites
+# - curl
+# - jq
+# - base64 utility
+# - a valid Gemini API key
+#
+# Before running the tests, let's set the GEMINI_API_KEY environment variable in our shell:
+#
+# export GEMINI_API_KEY="GEMINI_API_KEY_HERE"
+#
+# Every request uses the OpenAI compatible Gemini endpoints and will consume tokens from
+# our Gemini free tier or paid quota.
+
+export GEMINI_API_KEY="..."
+
+---
+Test 1 - Basic chat completion (required for SimpleOpenAIGeminiGoogle)
+
+curl -s "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $GEMINI_API_KEY" \
+ -d '{
+ "model": "gemini-2.5-flash",
+ "messages": [
+ {
+ "role": "user",
+ "content": "Explain what Baeldung is in at most 50 words."
+ }
+ ]
+ }' | jq .
+
+---
+Test 2 - Chat completions in streaming with Gemini (stream=true)
+
+curl -sN "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $GEMINI_API_KEY" \
+ -d '{
+ "model": "gemini-2.5-flash",
+ "stream": true,
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are an assistant that always reply in English."
+ },
+ {
+ "role": "user",
+ "content": "Briefly describe the city of Florence in at most 80 words."
+ }
+ ]
+ }'
+
+---
+Test 3 - Function Calling (Tools) with Gemini
+
+curl -s "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $GEMINI_API_KEY" \
+ -d '{
+ "model": "gemini-2.5-flash",
+ "messages": [
+ {
+ "role": "user",
+ "content": "What is the weather like in Chicago today? Please call the get_weather function instead of answering directly."
+ }
+ ],
+ "tools": [
+ {
+ "type": "function",
+ "function": {
+ "name": "get_weather",
+ "description": "Get the current weather in a given location",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "location": {
+ "type": "string",
+ "description": "The city and state, e.g. Chicago, IL"
+ },
+ "unit": {
+ "type": "string",
+ "enum": ["celsius", "fahrenheit"]
+ }
+ },
+ "required": ["location"]
+ }
+ }
+ }
+ ],
+ "tool_choice": "auto"
+ }' | jq .
+
+---
+Test 4 - Chat completion with vision (image understanding)
+
+1. Convert the image to base64 and write the JSON payload to a file
+
+IMAGE_PATH="image.jpg"
+
+# Base64 on a single line
+BASE64_IMAGE=$(base64 -w0 "$IMAGE_PATH")
+
+cat > /tmp/gemini_vision_request.json <