make it work for real this time
This commit is contained in:
parent
6ce5453de0
commit
1b1393da2c
14 changed files with 285 additions and 64 deletions
|
@ -4,16 +4,24 @@ import eu.m724.chat.Chat;
|
||||||
import eu.m724.chat.ChatEvent;
|
import eu.m724.chat.ChatEvent;
|
||||||
import eu.m724.chat.ChatMessage;
|
import eu.m724.chat.ChatMessage;
|
||||||
import eu.m724.example.ExampleSource;
|
import eu.m724.example.ExampleSource;
|
||||||
import eu.m724.responsesource.ChatResponse;
|
import eu.m724.source.ChatResponse;
|
||||||
import eu.m724.responsesource.ChatResponseSource;
|
import eu.m724.source.ChatSource;
|
||||||
import groovy.lang.GroovyShell;
|
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
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 {
|
||||||
ChatResponseSource source = new ExampleSource();
|
ChatSource source = new ExampleSource();
|
||||||
|
source.options().setValue("name", readResourceFile("name.txt"));
|
||||||
|
|
||||||
Chat chat = new Chat();
|
Chat chat = new Chat();
|
||||||
chat.messages.add(new ChatMessage(false, "hello"));
|
chat.messages.add(new ChatMessage(false, "hello"));
|
||||||
|
@ -24,6 +32,12 @@ public class Main {
|
||||||
List<String> tokens = new ArrayList<>();
|
List<String> tokens = new ArrayList<>();
|
||||||
List<Long> delays = new ArrayList<>();
|
List<Long> delays = new ArrayList<>();
|
||||||
|
|
||||||
|
System.out.printf("%s has %d options: %s\n\n",
|
||||||
|
source.getClass().getName(),
|
||||||
|
source.options().count(),
|
||||||
|
source.options().getOptions().values().stream().map(o -> "%s (%s)".formatted(o.label, o.getType().getName())).collect(Collectors.joining(", "))
|
||||||
|
);
|
||||||
|
|
||||||
System.out.println("Streaming response now\n");
|
System.out.println("Streaming response now\n");
|
||||||
ChatEvent token;
|
ChatEvent token;
|
||||||
|
|
||||||
|
@ -40,12 +54,43 @@ public class Main {
|
||||||
System.out.printf("Tokens: %d\n", tokens.size());
|
System.out.printf("Tokens: %d\n", tokens.size());
|
||||||
|
|
||||||
long time = delays.getFirst();
|
long time = delays.getFirst();
|
||||||
for (int i=0; i<tokens.size()-1; i++) {
|
System.out.printf("\"%s\"", tokens.getFirst());
|
||||||
System.out.printf("\"%s\" + %dms, ", tokens.get(i), delays.get(i+1) - time);
|
|
||||||
time = delays.get(i+1);
|
|
||||||
}
|
|
||||||
System.out.printf("\"%s\"\n\n", tokens.getLast());
|
|
||||||
|
|
||||||
System.out.printf("Text: %s\n", chatResponse.message().join().text());
|
for (int i = 1; i < tokens.size(); i++) {
|
||||||
|
System.out.print(i % 2 == 1 ? "\033[1m" : "\033[0m");
|
||||||
|
System.out.printf(" + %dms + \"%s\"", delays.get(i) - time, tokens.get(i).replace("\n", "\\n"));
|
||||||
|
time = delays.get(i);
|
||||||
|
|
||||||
|
if (i % 5 == 0 && i != tokens.size() - 1) {
|
||||||
|
System.out.println(" +\033[0m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("\033[0m\n");
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,32 +3,43 @@ package eu.m724.example
|
||||||
import eu.m724.chat.Chat
|
import eu.m724.chat.Chat
|
||||||
import eu.m724.chat.ChatEvent
|
import eu.m724.chat.ChatEvent
|
||||||
import eu.m724.chat.ChatMessage
|
import eu.m724.chat.ChatMessage
|
||||||
import eu.m724.responsesource.ChatResponse
|
import eu.m724.source.ChatResponse
|
||||||
import eu.m724.responsesource.ChatResponseSource
|
import eu.m724.source.ChatSource
|
||||||
import eu.m724.responsesource.ChatResponseSourceInfo
|
import eu.m724.source.ChatSourceInfo
|
||||||
|
import eu.m724.source.option.Options
|
||||||
|
import eu.m724.source.option.StringOption
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.LinkedBlockingDeque
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* an example chatresponsesource chatresponsesource ChatResponseSource CHATRESPONSESOURCE CAHTSERREPOSNECSOURCE
|
* an example chatresponsesource chatresponsesource ChatResponseSource CHATRESPONSESOURCE CAHTSERREPOSNECSOURCE
|
||||||
* note to self: rename that already...
|
* note to self: rename that already...
|
||||||
*/
|
*/
|
||||||
class ExampleSource implements ChatResponseSource {
|
class ExampleSource implements ChatSource {
|
||||||
private ChatResponseSourceInfo info =
|
private ChatSourceInfo info =
|
||||||
new ChatResponseSourceInfo("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(
|
||||||
|
new StringOption("greeting", "Greeting", "Hello, %s, how can I help you today?"),
|
||||||
|
new StringOption("name", "Name", "friend")
|
||||||
|
)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ChatResponseSourceInfo info() {
|
ChatSourceInfo info() {
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Options options() {
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ChatResponse onAsked(Chat chat) {
|
ChatResponse onAsked(Chat chat) {
|
||||||
String[] parts = "hello how can I assist you today".split(" ")
|
String[] parts = options.getOptionValue("greeting", String.class)
|
||||||
|
.formatted(options.getOptionValue("name", String.class))
|
||||||
|
.split(" ");
|
||||||
|
|
||||||
LinkedBlockingQueue<ChatEvent> queue = new LinkedBlockingQueue<>()
|
LinkedBlockingQueue<ChatEvent> queue = new LinkedBlockingQueue<>()
|
||||||
|
|
||||||
|
@ -36,7 +47,7 @@ class ExampleSource implements ChatResponseSource {
|
||||||
for (int i=0; i<parts.length; i++) {
|
for (int i=0; i<parts.length; i++) {
|
||||||
String token = (i > 0 ? " " : "") + parts[i]
|
String token = (i > 0 ? " " : "") + parts[i]
|
||||||
queue.put(ChatEvent.of(token));
|
queue.put(ChatEvent.of(token));
|
||||||
Thread.sleep(random.nextInt(200, 500))
|
Thread.sleep(random.nextInt(50, 200))
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.put(ChatEvent.finished("stop"))
|
queue.put(ChatEvent.finished("stop"))
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package eu.m724.responsesource;
|
|
||||||
|
|
||||||
import eu.m724.chat.Chat;
|
|
||||||
|
|
||||||
public interface ChatResponseSource {
|
|
||||||
ChatResponseSourceInfo info();
|
|
||||||
|
|
||||||
ChatResponse onAsked(Chat chat);
|
|
||||||
|
|
||||||
default ChatResponse ask(Chat chat) {
|
|
||||||
ChatResponse chatResponse = onAsked(chat);
|
|
||||||
|
|
||||||
// TODO make sure it works in parallel
|
|
||||||
chatResponse.message().thenAccept(chat::addMessage);
|
|
||||||
|
|
||||||
return chatResponse;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package eu.m724.responsesource;
|
|
||||||
|
|
||||||
public class ChatResponseSourceInfo {
|
|
||||||
public final String name;
|
|
||||||
public final String author;
|
|
||||||
public final String versionName;
|
|
||||||
public final int version;
|
|
||||||
|
|
||||||
public ChatResponseSourceInfo(
|
|
||||||
String name,
|
|
||||||
String author,
|
|
||||||
String versionName,
|
|
||||||
int version
|
|
||||||
) {
|
|
||||||
this.name = name;
|
|
||||||
this.author = author;
|
|
||||||
this.versionName = versionName;
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
package eu.m724.responsesource.net;
|
|
||||||
|
|
||||||
public interface Requester {
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.m724.responsesource;
|
package eu.m724.source;
|
||||||
|
|
||||||
import eu.m724.chat.ChatEvent;
|
import eu.m724.chat.ChatEvent;
|
||||||
import eu.m724.chat.ChatMessage;
|
import eu.m724.chat.ChatMessage;
|
33
src/main/java/eu/m724/source/ChatSource.java
Normal file
33
src/main/java/eu/m724/source/ChatSource.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package eu.m724.source;
|
||||||
|
|
||||||
|
import eu.m724.chat.Chat;
|
||||||
|
import eu.m724.source.option.Option;
|
||||||
|
import eu.m724.source.option.Options;
|
||||||
|
|
||||||
|
public interface ChatSource {
|
||||||
|
/**
|
||||||
|
* gets information about this ChatSource
|
||||||
|
* @return information ({@link ChatSourceInfo}) about this {@link ChatSource}
|
||||||
|
* @see ChatSourceInfo
|
||||||
|
*/
|
||||||
|
ChatSourceInfo info();
|
||||||
|
|
||||||
|
Options options();
|
||||||
|
|
||||||
|
ChatResponse onAsked(Chat chat);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ask for chat completion
|
||||||
|
* @param chat the current chat, it's recommended that the last message is user's message
|
||||||
|
* @return a response
|
||||||
|
* @see ChatResponse
|
||||||
|
*/
|
||||||
|
default ChatResponse ask(Chat chat) {
|
||||||
|
ChatResponse chatResponse = onAsked(chat);
|
||||||
|
|
||||||
|
// TODO make sure it works in parallel
|
||||||
|
chatResponse.message().thenAccept(chat::addMessage);
|
||||||
|
|
||||||
|
return chatResponse;
|
||||||
|
}
|
||||||
|
}
|
4
src/main/java/eu/m724/source/ChatSourceInfo.java
Normal file
4
src/main/java/eu/m724/source/ChatSourceInfo.java
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package eu.m724.source;
|
||||||
|
|
||||||
|
public record ChatSourceInfo(String name, String author, String versionName, int version) {
|
||||||
|
}
|
4
src/main/java/eu/m724/source/net/Requester.java
Normal file
4
src/main/java/eu/m724/source/net/Requester.java
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package eu.m724.source.net;
|
||||||
|
|
||||||
|
public interface Requester {
|
||||||
|
}
|
21
src/main/java/eu/m724/source/option/NumberOption.java
Normal file
21
src/main/java/eu/m724/source/option/NumberOption.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package eu.m724.source.option;
|
||||||
|
|
||||||
|
public class NumberOption extends Option<Integer> {
|
||||||
|
private int minValue = Integer.MIN_VALUE;
|
||||||
|
private int maxValue = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
public NumberOption(String id, String label, Integer value) {
|
||||||
|
super(id, label, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberOption(String id, String label, Integer value, int minValue, int maxValue) {
|
||||||
|
super(id, label, value);
|
||||||
|
this.minValue = minValue;
|
||||||
|
this.maxValue = maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isValid(Integer value) {
|
||||||
|
return value >= minValue && value <= maxValue;
|
||||||
|
}
|
||||||
|
}
|
53
src/main/java/eu/m724/source/option/Option.java
Normal file
53
src/main/java/eu/m724/source/option/Option.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package eu.m724.source.option;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* represents an option that is a text label and value of any type
|
||||||
|
* @param <T> what type is the option
|
||||||
|
*/
|
||||||
|
public abstract class Option<T> {
|
||||||
|
public final String id;
|
||||||
|
public final String label;
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
public Option(String id, String label, T value) {
|
||||||
|
this.id = id;
|
||||||
|
this.label = label;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set value of this option
|
||||||
|
* @param valueObject the value as an object, it will be converted
|
||||||
|
* @throws IllegalArgumentException if value doesn't fit constraints
|
||||||
|
* @throws ClassCastException if type is wrong
|
||||||
|
*/
|
||||||
|
public void setValue(Object valueObject) {
|
||||||
|
Class<T> type = getType();
|
||||||
|
|
||||||
|
if (!type.isInstance(valueObject)) {
|
||||||
|
throw new ClassCastException("Invalid type %s, expected %s".formatted(valueObject.getClass().getName(), getType().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
T value = (T) valueObject;
|
||||||
|
|
||||||
|
if (!isValid(value))
|
||||||
|
throw new IllegalArgumentException("Value invalid");
|
||||||
|
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<T> getType() {
|
||||||
|
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO I'm not a fan of that, probably should address the warnings
|
||||||
|
|
||||||
|
abstract boolean isValid(T value);
|
||||||
|
}
|
48
src/main/java/eu/m724/source/option/Options.java
Normal file
48
src/main/java/eu/m724/source/option/Options.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package eu.m724.source.option;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Options {
|
||||||
|
private final Map<String, Option<?>> options;
|
||||||
|
|
||||||
|
public Options(Option<?>... options) {
|
||||||
|
this.options = Arrays.stream(options).collect(
|
||||||
|
Collectors.toMap(o -> o.id, Function.identity())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int count() {
|
||||||
|
return options.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] keys() {
|
||||||
|
return options.keySet().toArray(String[]::new); // TODO I should think whether it's a good idea to cast when I don't have to
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Option<?>> getOptions() {
|
||||||
|
return options;
|
||||||
|
} // TODO remove that
|
||||||
|
|
||||||
|
public Option<?> getOption(String id) {
|
||||||
|
return options.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Object getOptionValue(String id, T type) {
|
||||||
|
return (T) options.get(id).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a value of an option
|
||||||
|
* @param id the option id
|
||||||
|
* @param value the value
|
||||||
|
* @throws IllegalArgumentException if value doesn't fit constraints
|
||||||
|
* @throws ClassCastException if type is wrong
|
||||||
|
* @throws NullPointerException if no such option
|
||||||
|
*/
|
||||||
|
public void setValue(String id, Object value) {
|
||||||
|
options.get(id).setValue(value);
|
||||||
|
}
|
||||||
|
}
|
26
src/main/java/eu/m724/source/option/StringOption.java
Normal file
26
src/main/java/eu/m724/source/option/StringOption.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package eu.m724.source.option;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class StringOption extends Option<String> {
|
||||||
|
private Pattern pattern = null;
|
||||||
|
|
||||||
|
public StringOption(String id, String label, String value) {
|
||||||
|
super(id, label, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringOption(
|
||||||
|
String id,
|
||||||
|
String label,
|
||||||
|
String value,
|
||||||
|
String pattern
|
||||||
|
) {
|
||||||
|
super(id, label, value);
|
||||||
|
this.pattern = Pattern.compile(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isValid(String value) {
|
||||||
|
return pattern == null || pattern.matcher(value).matches();
|
||||||
|
}
|
||||||
|
}
|
18
src/main/resources/name.txt
Normal file
18
src/main/resources/name.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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