make it work for real this time

This commit is contained in:
Minecon724 2024-08-29 19:50:58 +02:00
parent 6ce5453de0
commit 1b1393da2c
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
14 changed files with 285 additions and 64 deletions

View file

@ -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;
}
}
}

View file

@ -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"))

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -1,4 +0,0 @@
package eu.m724.responsesource.net;
public interface Requester {
}

View file

@ -1,4 +1,4 @@
package eu.m724.responsesource;
package eu.m724.source;
import eu.m724.chat.ChatEvent;
import eu.m724.chat.ChatMessage;

View 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;
}
}

View file

@ -0,0 +1,4 @@
package eu.m724.source;
public record ChatSourceInfo(String name, String author, String versionName, int version) {
}

View file

@ -0,0 +1,4 @@
package eu.m724.source.net;
public interface Requester {
}

View 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;
}
}

View 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);
}

View 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);
}
}

View 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();
}
}

View 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