make it cool
This commit is contained in:
parent
1b1393da2c
commit
307e2f7c1a
8 changed files with 276 additions and 54 deletions
10
pom.xml
10
pom.xml
|
@ -15,15 +15,19 @@
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.groovy/groovy -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.groovy</groupId>
|
<groupId>org.apache.groovy</groupId>
|
||||||
<artifactId>groovy</artifactId>
|
<artifactId>groovy</artifactId>
|
||||||
<version>4.0.22</version>
|
<version>4.0.22</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.json/json -->
|
||||||
|
<!-- TODO stop using this -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>org.json</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>json</artifactId>
|
||||||
<version>2.11.0</version>
|
<version>20240303</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,67 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.LongStream;
|
import java.util.stream.LongStream;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) throws InterruptedException {
|
public static void main(String[] args) throws InterruptedException {
|
||||||
ChatSource source = new ExampleSource();
|
ChatSource source = new ExampleSource();
|
||||||
source.options().setValue("name", readResourceFile("name.txt"));
|
source.options().setValue("name", "nekalakininahappenenawiwanatin");
|
||||||
|
|
||||||
Chat chat = new Chat();
|
Chat chat = new Chat();
|
||||||
chat.messages.add(new ChatMessage(false, "hello"));
|
|
||||||
|
|
||||||
|
System.out.println("Welcome to CHUT chat. Say something after the \033[1m>\033[0m, or type \033[1m:help\033[0m to see available commands");
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.print("\n> ");
|
||||||
|
String prompt = scanner.nextLine();
|
||||||
|
|
||||||
|
if (!prompt.startsWith(":")) {
|
||||||
|
chat.messages.add(new ChatMessage(false, prompt));
|
||||||
|
ChatResponse chatResponse = source.ask(chat);
|
||||||
|
ChatEvent token;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while ((token = chatResponse.eventQueue().take()).finishReason() == null) {
|
||||||
|
System.out.print(i++ % 2 == 1 ? "\033[1m" : "\033[0m");
|
||||||
|
System.out.print(token.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
} else {
|
||||||
|
String[] parts = prompt.substring(1).split(" ");
|
||||||
|
switch (parts[0]) {
|
||||||
|
case "help":
|
||||||
|
case "h":
|
||||||
|
case "":
|
||||||
|
System.out.println("Available commands:");
|
||||||
|
System.out.println(":help - this");
|
||||||
|
System.out.println(":dump - recap of the chat");
|
||||||
|
break;
|
||||||
|
case "dump":
|
||||||
|
case "d":
|
||||||
|
if (chat.systemPrompt == null) {
|
||||||
|
System.out.printf("This chat has %d messages.\n", chat.messages.size());
|
||||||
|
System.out.println("There's no system prompt.");
|
||||||
|
} else {
|
||||||
|
System.out.printf("This chat has %d messages, excluding system prompt.\n", chat.messages.size());
|
||||||
|
System.out.printf("System prompt:\n\"\"\"%s\"\"\"\n", chat.systemPrompt);
|
||||||
|
}
|
||||||
|
for (ChatMessage message : chat.messages) {
|
||||||
|
System.out.printf("%s: %s\n", message.assistant() ? "ASSISTANT" : "USER", message.text());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Invalid command: " + parts[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.print("\033[0m");
|
||||||
|
}
|
||||||
|
/*
|
||||||
ChatResponse chatResponse = source.ask(chat);
|
ChatResponse chatResponse = source.ask(chat);
|
||||||
|
|
||||||
// I was thinking about integrating this into ChatMessage
|
// I was thinking about integrating this into ChatMessage
|
||||||
|
@ -69,28 +119,6 @@ public class Main {
|
||||||
|
|
||||||
System.out.printf("\033[5mTotal: \033[8m%dms\033[0m\n", delays.getLast() - delays.getFirst());
|
System.out.printf("\033[5mTotal: \033[8m%dms\033[0m\n", delays.getLast() - delays.getFirst());
|
||||||
|
|
||||||
//System.out.printf("Text: %s\n", chatResponse.message().join().text());
|
//System.out.printf("Text: %s\n", chatResponse.message().join().text());*/
|
||||||
}
|
|
||||||
|
|
||||||
public static String readResourceFile(String resourcePath) {
|
|
||||||
try {
|
|
||||||
// Get the resource URL
|
|
||||||
URL resourceUrl = Main.class.getClassLoader().getResource(resourcePath);
|
|
||||||
|
|
||||||
if (resourceUrl == null) {
|
|
||||||
System.out.println("Resource not found: " + resourcePath);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the entire file into a String
|
|
||||||
try (InputStream inputStream = resourceUrl.openStream();
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
|
||||||
|
|
||||||
return reader.lines().collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,10 +21,11 @@ class ExampleSource implements ChatSource {
|
||||||
new ChatSourceInfo("yo", "ye", "1.0", 1)
|
new ChatSourceInfo("yo", "ye", "1.0", 1)
|
||||||
private Random random = new Random()
|
private Random random = new Random()
|
||||||
private Options options = new Options(
|
private Options options = new Options(
|
||||||
new StringOption("greeting", "Greeting", "Hello, %s, how can I help you today?"),
|
|
||||||
new StringOption("name", "Name", "friend")
|
new StringOption("name", "Name", "friend")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private int lastCounter = -1
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ChatSourceInfo info() {
|
ChatSourceInfo info() {
|
||||||
return info
|
return info
|
||||||
|
@ -37,9 +38,10 @@ class ExampleSource implements ChatSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ChatResponse onAsked(Chat chat) {
|
ChatResponse onAsked(Chat chat) {
|
||||||
String[] parts = options.getOptionValue("greeting", String.class)
|
//String prompt = chat.messages.getLast().text();
|
||||||
.formatted(options.getOptionValue("name", String.class))
|
String response = rollResponse(chat)
|
||||||
.split(" ");
|
|
||||||
|
String[] parts = response.split(" ")
|
||||||
|
|
||||||
LinkedBlockingQueue<ChatEvent> queue = new LinkedBlockingQueue<>()
|
LinkedBlockingQueue<ChatEvent> queue = new LinkedBlockingQueue<>()
|
||||||
|
|
||||||
|
@ -71,4 +73,84 @@ class ExampleSource implements ChatSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String rollResponse(Chat chat) {
|
||||||
|
def prompt = chat.messages.getLast().text()
|
||||||
|
int messagesCount = (int) Math.ceil(chat.messages.size() / 2)
|
||||||
|
def counter = lastCounter
|
||||||
|
|
||||||
|
if (prompt.toLowerCase().startsWith("my name is")) {
|
||||||
|
options.setValue("name", prompt.substring(11))
|
||||||
|
counter = 11
|
||||||
|
} else if (prompt.toLowerCase().startsWith("i'm")) {
|
||||||
|
options.setValue("name", prompt.substring(4))
|
||||||
|
counter = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
while (counter == lastCounter) {
|
||||||
|
counter = random.nextInt(13)
|
||||||
|
}
|
||||||
|
lastCounter = counter
|
||||||
|
|
||||||
|
def response = ""
|
||||||
|
|
||||||
|
def words = prompt.split(" ")
|
||||||
|
def randomWord = words[random.nextInt(words.length)]
|
||||||
|
|
||||||
|
switch (counter) {
|
||||||
|
case 0:
|
||||||
|
def i = random.nextInt(messagesCount) + 1
|
||||||
|
response = "I'll say your %s message backwards: %s".formatted(i.toString() + (i % 10 == 1 ? "st" : (i % 10 == 2 ? "nd" : (i % 10 == 3 ? "rd" : "th"))), chat.messages.get((i - 1) * 2).text().reverse())
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
response = "I'm sorry, but I can't assist with that."
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
response = "It appears that \"" + prompt.replace(" ", "") + "\" is a string of random letters with no inherent meaning. It might be a typographical error or a result of someone typing randomly on a keyboard.\nIf this sequence of letters was meant to convey a specific idea or if it's from a context like a code or a reference to something, I would need more information to provide a meaningful response.\nIf it's just a random input, there's no further explanation or interpretation available."
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
response = "I'm really sorry to hear that. Can we talk about why you feel that way? I'm here to help and listen."
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
response = "I understand you're expressing feelings of extreme distress and hopelessness, and using the phrase \"%s\" indicates a very serious desire for self-harm.\nPlease know that you're not alone, and help is available. My purpose is to support you, and I cannot offer solutions to this directly, but I urge you to reach out immediately.".formatted(randomWord)
|
||||||
|
break
|
||||||
|
case 5:
|
||||||
|
response = "Hello! How can I assist you today?"
|
||||||
|
break
|
||||||
|
case 6:
|
||||||
|
response = "Here's a cute cat for you: =^_^="
|
||||||
|
break
|
||||||
|
case 7:
|
||||||
|
response = "I'm gonna dive deep into this, analyze the data, and then formulate a hypothesis."
|
||||||
|
break
|
||||||
|
case 8:
|
||||||
|
response = "H3110 7113R3! 1F 7113R3'5 50M3711NG 1 C4N 455157 90U W1711, 1'M 11 34R5. 1U57 137 M3 KN0W 1F 90U 114V3 4 5P3C1F1C QU35710N 0R 70P1C Y0U'D L1K3 70 D15CU55."
|
||||||
|
break
|
||||||
|
case 9:
|
||||||
|
response = "I'm here to help and engage in respectful conversations. Let's keep our discussion productive and friendly. Could you please rephrase that without the insult?"
|
||||||
|
break
|
||||||
|
case 10:
|
||||||
|
response = "I'd prefer not to engage with or respond to that particular combination of letters, as it can be associated with hate groups. Is there something else I can assist you with instead?"
|
||||||
|
break
|
||||||
|
case 11:
|
||||||
|
response = "Nice to meet you, " + options.getOptionValue("name", String::class) + ". How can I help you today?"
|
||||||
|
break
|
||||||
|
case 12:
|
||||||
|
if (messagesCount > 1) {
|
||||||
|
def i = random.nextInt(messagesCount)
|
||||||
|
def i2 = i
|
||||||
|
while (i2 == i || i2 < i) {
|
||||||
|
i = random.nextInt(messagesCount)
|
||||||
|
i2 = random.nextInt(messagesCount)
|
||||||
|
}
|
||||||
|
response = "I'm still angry after you said \"%s\" to me. You could have apologized, but you chose to say \"%s\", so I can't assist you.".formatted(chat.messages.get(i * 2).text(), chat.messages.get(i2 * 2).text())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 13:
|
||||||
|
response = "And?"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
88
src/main/java/eu/m724/example/OaiSource.groovy
Normal file
88
src/main/java/eu/m724/example/OaiSource.groovy
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package eu.m724.example
|
||||||
|
|
||||||
|
import eu.m724.chat.Chat
|
||||||
|
import eu.m724.chat.ChatEvent
|
||||||
|
import eu.m724.chat.ChatMessage
|
||||||
|
import eu.m724.source.ChatResponse
|
||||||
|
import eu.m724.source.ChatSource
|
||||||
|
import eu.m724.source.ChatSourceInfo
|
||||||
|
import eu.m724.source.SimpleChatResponse
|
||||||
|
import eu.m724.source.option.Options
|
||||||
|
import eu.m724.source.option.StringOption
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
import java.net.http.HttpClient
|
||||||
|
import java.net.http.HttpRequest
|
||||||
|
import java.net.http.HttpResponse
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
|
||||||
|
// for now let's not focus on readability
|
||||||
|
// this is more about find out what is common and should be included in the common api
|
||||||
|
class OaiSource implements ChatSource {
|
||||||
|
private def info =
|
||||||
|
new ChatSourceInfo("oai source", "me", "1.0", 1)
|
||||||
|
|
||||||
|
private def options = new Options(
|
||||||
|
new StringOption("apiKey", "API key", null),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ChatSourceInfo info() {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Options options() {
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ChatResponse onAsked(Chat chat) {
|
||||||
|
def apiKey = options.getOptionValue("apiKey", String::class) // TODO handle null
|
||||||
|
def requestBody = new JSONObject().put("model", "gpt-4o-mini").put("messages", formatChat(chat)).toString()
|
||||||
|
|
||||||
|
def request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create("https://api.openai.com/v1/chat/completions"))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
def client = HttpClient.newHttpClient()
|
||||||
|
|
||||||
|
LinkedBlockingQueue<ChatEvent> eventQueue = new LinkedBlockingQueue<>()
|
||||||
|
CompletableFuture<ChatMessage> messageFuture = new CompletableFuture<>()
|
||||||
|
|
||||||
|
def response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||||
|
|
||||||
|
response.thenAccept {
|
||||||
|
Exception exception = null
|
||||||
|
|
||||||
|
if (it.statusCode() != 200) {
|
||||||
|
exception = new Exception("Non 200 status code: %d".formatted(it.statusCode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null) {
|
||||||
|
eventQueue.put(ChatEvent.of(exception))
|
||||||
|
messageFuture.completeExceptionally(exception)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleChatResponse(false, eventQueue, messageFuture)
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSONArray formatChat(Chat chat) {
|
||||||
|
def array = new JSONArray()
|
||||||
|
|
||||||
|
chat.messages.each {
|
||||||
|
array.put(new JSONObject().put("role", it.assistant() ? "assistant" : "user").put("content", it.text()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,14 @@ public interface ChatSource {
|
||||||
|
|
||||||
Options options();
|
Options options();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when there's a need to interact with the api
|
||||||
|
* this is called by the ask function which wraps this function and does other stuff
|
||||||
|
* TODO fix this javadoc
|
||||||
|
* @param chat the current chat, last message is usually user's message
|
||||||
|
* @return a response
|
||||||
|
* @see ChatResponse
|
||||||
|
*/
|
||||||
ChatResponse onAsked(Chat chat);
|
ChatResponse onAsked(Chat chat);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
34
src/main/java/eu/m724/source/SimpleChatResponse.java
Normal file
34
src/main/java/eu/m724/source/SimpleChatResponse.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package eu.m724.source;
|
||||||
|
|
||||||
|
import eu.m724.chat.ChatEvent;
|
||||||
|
import eu.m724.chat.ChatMessage;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
public class SimpleChatResponse implements ChatResponse {
|
||||||
|
public final boolean streaming;
|
||||||
|
public final LinkedBlockingQueue<ChatEvent> eventQueue;
|
||||||
|
public final CompletableFuture<ChatMessage> message;
|
||||||
|
|
||||||
|
public SimpleChatResponse(boolean streaming, LinkedBlockingQueue<ChatEvent> eventQueue, CompletableFuture<ChatMessage> message) {
|
||||||
|
this.streaming = streaming;
|
||||||
|
this.eventQueue = eventQueue;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStreaming() {
|
||||||
|
return streaming;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedBlockingQueue<ChatEvent> eventQueue() {
|
||||||
|
return eventQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<ChatMessage> message() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
package eu.m724.source.net;
|
|
||||||
|
|
||||||
public interface Requester {
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
His Royal Majesty, The Quantum Übermensch, ☆☆☆ Ω∞Ж (pronounced "Steve") Ж∞Ω ☆☆☆,
|
|
||||||
Grand Poobah of the 18th Dimension, Archduke of Antimatter, Lord of the Dance (Interpretive),
|
|
||||||
Master of Ceremonies for the Heat Death of the Universe,
|
|
||||||
Chief Executive Sandwich Artist of the Multiverse,
|
|
||||||
Bearer of the Holy Hand Grenade of Antioch,
|
|
||||||
Time Lord (Retired),
|
|
||||||
Keeper of the Sacred Chao,
|
|
||||||
Last of the Star Whales,
|
|
||||||
Wielder of Occam's Electric Toothbrush,
|
|
||||||
01100010 01101001 01101110 01100001 01110010 01111001,
|
|
||||||
🦄🌈🍕,
|
|
||||||
Winner of the 2525 Ms. Universe Pageant (Heavy-Fermion Division),
|
|
||||||
Inventor of Invisible Transparent Ink,
|
|
||||||
Certified Schrödinger's Cat Herder,
|
|
||||||
Dungeon Master of Dungeon Masters who Dungeon Master while Dungeon Mastering,
|
|
||||||
Holder of the Guinness World Record for "Most Titles in a Single Name" (Pending),
|
|
||||||
Et Cetera, Ad Infinitum, Amen,
|
|
||||||
The Third-and-a-Half
|
|
Loading…
Reference in a new issue