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.ChatMessage;
|
||||
import eu.m724.example.ExampleSource;
|
||||
import eu.m724.responsesource.ChatResponse;
|
||||
import eu.m724.responsesource.ChatResponseSource;
|
||||
import groovy.lang.GroovyShell;
|
||||
import eu.m724.source.ChatResponse;
|
||||
import eu.m724.source.ChatSource;
|
||||
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
public class Main {
|
||||
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.messages.add(new ChatMessage(false, "hello"));
|
||||
|
@ -24,6 +32,12 @@ public class Main {
|
|||
List<String> tokens = 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");
|
||||
ChatEvent token;
|
||||
|
||||
|
@ -40,12 +54,43 @@ public class Main {
|
|||
System.out.printf("Tokens: %d\n", tokens.size());
|
||||
|
||||
long time = delays.getFirst();
|
||||
for (int i=0; i<tokens.size()-1; i++) {
|
||||
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("\"%s\"", tokens.getFirst());
|
||||
|
||||
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.ChatEvent
|
||||
import eu.m724.chat.ChatMessage
|
||||
import eu.m724.responsesource.ChatResponse
|
||||
import eu.m724.responsesource.ChatResponseSource
|
||||
import eu.m724.responsesource.ChatResponseSourceInfo
|
||||
import eu.m724.source.ChatResponse
|
||||
import eu.m724.source.ChatSource
|
||||
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.Executors
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
|
||||
/**
|
||||
* an example chatresponsesource chatresponsesource ChatResponseSource CHATRESPONSESOURCE CAHTSERREPOSNECSOURCE
|
||||
* note to self: rename that already...
|
||||
*/
|
||||
class ExampleSource implements ChatResponseSource {
|
||||
private ChatResponseSourceInfo info =
|
||||
new ChatResponseSourceInfo("yo", "ye", "1.0", 1)
|
||||
class ExampleSource implements ChatSource {
|
||||
private ChatSourceInfo info =
|
||||
new ChatSourceInfo("yo", "ye", "1.0", 1)
|
||||
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
|
||||
ChatResponseSourceInfo info() {
|
||||
ChatSourceInfo info() {
|
||||
return info
|
||||
}
|
||||
|
||||
@Override
|
||||
Options options() {
|
||||
return options
|
||||
}
|
||||
|
||||
@Override
|
||||
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<>()
|
||||
|
||||
|
@ -36,7 +47,7 @@ class ExampleSource implements ChatResponseSource {
|
|||
for (int i=0; i<parts.length; i++) {
|
||||
String token = (i > 0 ? " " : "") + parts[i]
|
||||
queue.put(ChatEvent.of(token));
|
||||
Thread.sleep(random.nextInt(200, 500))
|
||||
Thread.sleep(random.nextInt(50, 200))
|
||||
}
|
||||
|
||||
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.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