Added Google Gemini module
This commit is contained in:
parent
8412a8c26d
commit
4a4e702d9f
10 changed files with 230 additions and 26 deletions
|
@ -50,7 +50,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
|
||||
import static rife.bld.dependencies.Repository.*;
|
||||
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
|
||||
import static rife.bld.dependencies.Repository.MAVEN_LOCAL;
|
||||
import static rife.bld.dependencies.Scope.compile;
|
||||
import static rife.bld.dependencies.Scope.test;
|
||||
|
||||
|
@ -82,9 +83,9 @@ public class MobibotBuild extends Project {
|
|||
// Google
|
||||
.include(dependency("com.google.code.gson", "gson", "2.10.1"))
|
||||
.include(dependency("com.google.guava", "guava", "32.1.3-jre"))
|
||||
.include(dependency("com.google.cloud", "google-cloud-vertexai", version(0, 1, 0)))
|
||||
// Kotlin
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin))
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin))
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk7", kotlin))
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin))
|
||||
.include(dependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.7.3"))
|
||||
|
@ -100,19 +101,20 @@ public class MobibotBuild extends Project {
|
|||
.include(dependency("net.aksingh", "owm-japis", "2.5.3.0"))
|
||||
.include(dependency("net.objecthunter", "exp4j", "0.4.8"))
|
||||
.include(dependency("org.json", "json", "20231013"))
|
||||
.include(dependency("org.jsoup", "jsoup", "1.16.2"))
|
||||
.include(dependency("org.jsoup", "jsoup", "1.17.1"))
|
||||
// Thauvin
|
||||
.include(dependency("net.thauvin.erik", "cryptoprice", "1.0.2"))
|
||||
.include(dependency("net.thauvin.erik", "jokeapi", "0.9.1"))
|
||||
.include(dependency("net.thauvin.erik", "pinboard-poster", "1.1.1"))
|
||||
.include(dependency("net.thauvin.erik.urlencoder", "urlencoder-lib-jvm", "1.4.0"));
|
||||
scope(test)
|
||||
.include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 27, 0)))
|
||||
.include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 0)))
|
||||
.include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", version(1, 9, 21)))
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1)));
|
||||
|
||||
List<String> jars = new ArrayList<>();
|
||||
runtimeClasspathJars().forEach(f -> jars.add("./lib/" + f.getName()));
|
||||
compileClasspathJars().forEach(f -> jars.add("./lib/" + f.getName()));
|
||||
jarOperation()
|
||||
.manifestAttribute(Attributes.Name.MAIN_CLASS, mainClass())
|
||||
|
@ -149,6 +151,9 @@ public class MobibotBuild extends Project {
|
|||
for (var jar : compileClasspathJars()) {
|
||||
FileUtils.copy(jar, new File(lib, jar.getName()));
|
||||
}
|
||||
for (var jar : runtimeClasspathJars()) {
|
||||
FileUtils.copy(jar, new File(lib, jar.getName()));
|
||||
}
|
||||
FileUtils.copy(new File(buildDistDirectory(), jarFileName()), new File(deploy, "mobibot.jar"));
|
||||
}
|
||||
|
||||
|
|
|
@ -400,6 +400,7 @@ class Mobibot(nickname: String, val channel: String, logsDirPath: String, p: Pro
|
|||
addons.add(CryptoPrices())
|
||||
addons.add(CurrencyConverter())
|
||||
addons.add(Dice())
|
||||
addons.add(Gemini())
|
||||
addons.add(GoogleSearch())
|
||||
addons.add(Info(tell, seen))
|
||||
addons.add(Joke())
|
||||
|
|
|
@ -14,12 +14,12 @@ import java.time.ZoneId
|
|||
*/
|
||||
object ReleaseInfo {
|
||||
const val PROJECT = "mobibot"
|
||||
const val VERSION = "0.8.0-rc+20231125224843"
|
||||
const val VERSION = "0.8.0-rc+20231217213124"
|
||||
|
||||
@JvmField
|
||||
@Suppress("MagicNumber")
|
||||
val BUILD_DATE: LocalDateTime = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(1700981323173L), ZoneId.systemDefault()
|
||||
Instant.ofEpochMilli(1702877484912L), ZoneId.systemDefault()
|
||||
)
|
||||
|
||||
const val WEBSITE = "https://www.mobitopia.org/mobibot/"
|
||||
|
|
|
@ -42,7 +42,7 @@ class Versions : AbstractCommand() {
|
|||
private val allVersions = listOf(
|
||||
"Version: ${ReleaseInfo.VERSION} (${ReleaseInfo.BUILD_DATE.toIsoLocalDate()})",
|
||||
"${System.getProperty("os.name")} ${System.getProperty("os.version")} (${System.getProperty("os.arch")})" +
|
||||
", JVM ${System.getProperty("java.runtime.version")}",
|
||||
", JVM ${System.getProperty("java.version")}",
|
||||
"Kotlin ${KotlinVersion.CURRENT}, PircBotX ${PircBotX.VERSION}"
|
||||
)
|
||||
override val name = "versions"
|
||||
|
|
116
src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt
Normal file
116
src/main/kotlin/net/thauvin/erik/mobibot/modules/Gemini.kt
Normal file
|
@ -0,0 +1,116 @@
|
|||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import com.google.auth.Credentials
|
||||
import com.google.cloud.vertexai.VertexAI
|
||||
import com.google.cloud.vertexai.api.GenerateContentResponse
|
||||
import com.google.cloud.vertexai.api.GenerationConfig
|
||||
import com.google.cloud.vertexai.generativeai.preview.ChatSession
|
||||
import com.google.cloud.vertexai.generativeai.preview.GenerativeModel
|
||||
import com.google.cloud.vertexai.generativeai.preview.ResponseHandler
|
||||
import net.thauvin.erik.mobibot.Utils
|
||||
import net.thauvin.erik.mobibot.Utils.sendMessage
|
||||
import okio.IOException
|
||||
import org.apache.commons.text.WordUtils
|
||||
import org.pircbotx.hooks.types.GenericMessageEvent
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
||||
class Gemini : AbstractModule() {
|
||||
private val logger: Logger = LoggerFactory.getLogger(Gemini::class.java)
|
||||
|
||||
override val name = GEMINI_NAME
|
||||
|
||||
override fun commandResponse(channel: String, cmd: String, args: String, event: GenericMessageEvent) {
|
||||
if (args.isNotBlank()) {
|
||||
try {
|
||||
val answer = chat(
|
||||
args.trim(),
|
||||
properties[PROJECT_ID_PROP],
|
||||
properties[LOCATION_PROPR],
|
||||
properties.getOrDefault(MAX_TOKENS_PROP, "1024").toInt()
|
||||
)
|
||||
if (!answer.isNullOrEmpty()) {
|
||||
event.sendMessage(answer)
|
||||
} else {
|
||||
event.respond("$name is stumped.")
|
||||
}
|
||||
} catch (e: ModuleException) {
|
||||
if (logger.isWarnEnabled) logger.warn(e.debugMessage, e)
|
||||
e.message?.let {
|
||||
event.respond(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helpResponse(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The service name.
|
||||
*/
|
||||
const val GEMINI_NAME = "Gemini"
|
||||
|
||||
/**
|
||||
* The Google cloud project ID.
|
||||
*/
|
||||
const val PROJECT_ID_PROP = "gemini-project-id"
|
||||
|
||||
/**
|
||||
* The Vertex AI location.
|
||||
*/
|
||||
const val LOCATION_PROPR = "gemini-location"
|
||||
|
||||
/**
|
||||
* The max tokens property.
|
||||
*/
|
||||
const val MAX_TOKENS_PROP = "gemini-max-tokens"
|
||||
|
||||
// ChatGPT command
|
||||
private const val GEMINI_CMD = "gemini"
|
||||
|
||||
@JvmStatic
|
||||
@Throws(ModuleException::class)
|
||||
fun chat(
|
||||
query: String,
|
||||
projectId: String?,
|
||||
location: String?,
|
||||
maxToken: Int
|
||||
): String? {
|
||||
if (!projectId.isNullOrEmpty() && !location.isNullOrEmpty()) {
|
||||
try {
|
||||
VertexAI(projectId, location).use { vertexAI ->
|
||||
val generationConfig = GenerationConfig.newBuilder().setMaxOutputTokens(maxToken).build()
|
||||
val model = GenerativeModel("gemini-pro-vision", generationConfig, vertexAI)
|
||||
val session = ChatSession(model)
|
||||
val response = session.sendMessage(query)
|
||||
|
||||
return ResponseHandler.getText(response);
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw ModuleException(
|
||||
"$GEMINI_CMD($query): IO",
|
||||
"An IO error has occurred while conversing with ${GEMINI_NAME}.",
|
||||
e
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw ModuleException("${GEMINI_CMD}($query)", "No ${GEMINI_NAME} Project ID or Location specified.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
commands.add(GEMINI_CMD)
|
||||
with(help) {
|
||||
add("To get answers from $name:")
|
||||
add(Utils.helpFormat("%c ${GEMINI_CMD} <query>"))
|
||||
add("For example:")
|
||||
add(Utils.helpFormat("%c ${GEMINI_CMD} explain quantum computing in simple terms"))
|
||||
add(Utils.helpFormat("%c ${GEMINI_CMD} how do I make an HTTP request in Javascript?"))
|
||||
}
|
||||
initProperties(PROJECT_ID_PROP, LOCATION_PROPR, MAX_TOKENS_PROP)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* ChatGptTest.kt
|
||||
*
|
||||
* Copyright 2004-2023 Erik C. Thauvin (erik@thauvin.net)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of this project nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.thauvin.erik.mobibot.modules
|
||||
|
||||
import assertk.assertFailure
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import net.thauvin.erik.mobibot.DisableOnCi
|
||||
import net.thauvin.erik.mobibot.LocalProperties
|
||||
import kotlin.test.Test
|
||||
|
||||
class GeminiTest : LocalProperties() {
|
||||
@Test
|
||||
fun testApiKey() {
|
||||
assertFailure { Gemini.chat("1 gallon to liter", "", "", 1024) }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
.hasNoCause()
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableOnCi
|
||||
fun chatPrompt() {
|
||||
val projectId = getProperty(Gemini.PROJECT_ID_PROP)
|
||||
val location = getProperty(Gemini.LOCATION_PROPR)
|
||||
val maxTokens = getProperty(Gemini.MAX_TOKENS_PROP).toInt()
|
||||
|
||||
assertThat(
|
||||
Gemini.chat("how do I make an HTTP request in Javascript?", projectId, location, maxTokens)
|
||||
).isNotNull().contains("XMLHttpRequest")
|
||||
|
||||
assertThat(
|
||||
Gemini.chat("how do I encode a URL in java?", projectId, location, 60)
|
||||
).isNotNull().contains("URLEncoder")
|
||||
|
||||
assertFailure { Gemini.chat("1 liter to gallon", projectId, "blah", 40) }
|
||||
.isInstanceOf(ModuleException::class.java)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue