Compare commits

..

7 Commits

Author SHA1 Message Date
aa63e67c17 workflows (#2)
All checks were successful
Gitea Actions Demo / build (push) Successful in 50s
Reviewed-on: #2
2024-08-21 15:50:12 +02:00
fb92f678f8 This adds gitea workflows (#1)
Some checks failed
Gitea Actions Demo / build (push) Failing after 8s
Cause they work now

Reviewed-on: #1
2024-08-21 15:29:23 +02:00
0eec2a9756 Arguments 2024-08-21 14:55:53 +02:00
14d13a7d76 Working spring project 2024-08-21 11:32:12 +02:00
3ccc0dd265 First attempt at spring boot autoconfiguration 2024-08-20 20:25:25 +02:00
e051ca803c Update gitignore 2024-08-20 19:22:07 +02:00
b86ff87127 Add support for maven publishing 2024-08-20 19:21:40 +02:00
30 changed files with 413 additions and 84 deletions

View File

@@ -0,0 +1,18 @@
name: Gitea Actions Demo
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
- name: Check out
uses: actions/checkout@v4
- name: Build
run: ./gradlew jar

View File

@@ -0,0 +1,29 @@
name: Gitea Actions Demo
on:
push:
branches:
- master
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
- name: Check out
uses: actions/checkout@v4
- name: Build
run: ./gradlew jar
- name: Publish
run: ./gradlew publish
env:
ORG_GRADLE_PROJECT_GiteaUsername: ${{secrets.username}}
ORG_GRADLE_PROJECT_GiteaPassword: ${{secrets.password}}

5
.gitignore vendored
View File

@@ -5,10 +5,7 @@ build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea
*.iws
*.iml
*.ipr

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

11
.idea/gradle.xml generated
View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="/usr/share/java/gradle" />
</GradleProjectSettings>
</option>
</component>
</project>

7
.idea/misc.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,23 +1,18 @@
plugins {
id 'java'
id 'io.freefair.lombok' version '8.10'
id 'maven-publish'
}
group = 'be.seeseemelk'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
allprojects {
group = 'be.seeseemelk'
version = '0.1-SNAPSHOT'
}
dependencies {
implementation 'io.github.ollama4j:ollama4j:1.0.82'
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.hamcrest:hamcrest:3.0'
}
test {
useJUnitPlatform()
publishing {
repositories {
maven {
name = "Gitea"
url = uri("https://gitea.seeseepuff.be/api/packages/llamascript/maven")
credentials(PasswordCredentials)
}
}
}

View File

@@ -0,0 +1,34 @@
plugins {
id 'java-library'
id 'io.freefair.lombok' version '8.10'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'io.github.ollama4j:ollama4j:1.0.82'
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.hamcrest:hamcrest:3.0'
}
test {
useJUnitPlatform()
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
libllamascript(MavenPublication) {
from components.java
artifactId 'libllamascript'
}
}
}

View File

@@ -1,37 +1,33 @@
package be.seeseemelk.llamascript;
import be.seeseemelk.llamascript.arguments.ArgumentBag;
import be.seeseemelk.llamascript.arguments.ListArgumentBag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
@Slf4j
@RequiredArgsConstructor
public class LlamaScript {
private final LlamaScriptWorker worker;
public <T> T eval(String prompt, Class<T> returnType, Object... arguments) {
var argumentMap = new HashMap<Integer, Object>();
for (Object argument : arguments) {
argumentMap.put(argumentMap.size(), argument);
}
public <T> T eval(String prompt, Class<T> returnType, ArgumentBag arguments) {
if (returnType.equals(Integer.class)) {
//noinspection unchecked
return (T) evalInt(prompt, argumentMap);
return (T) evalInt(prompt, arguments);
} else if (returnType.equals(String.class)) {
//noinspection unchecked
return (T) evalString(prompt, argumentMap);
return (T) evalString(prompt, arguments);
} else if (returnType.equals(Boolean.class)) {
//noinspection unchecked
return (T) evalBool(prompt, argumentMap);
return (T) evalBool(prompt, arguments);
} else {
throw new LLamaScriptException("Unsupported return type");
}
}
public String evalString(String prompt, HashMap<Integer, Object> argumentMap) {
var result = worker.eval(prompt, primitiveOutputType("a string"), argumentMap);
public String evalString(String prompt, ArgumentBag arguments) {
var result = worker.eval(prompt, primitiveOutputType("a string"), arguments);
if (result instanceof String string)
return string;
else if (result != null)
@@ -41,11 +37,11 @@ public class LlamaScript {
}
public String evalString(String prompt, Object... arguments) {
return eval(prompt, String.class, arguments);
return eval(prompt, String.class, ListArgumentBag.of(arguments));
}
public Integer evalInt(String prompt, HashMap<Integer, Object> argumentMap) {
var result = worker.eval(prompt, primitiveOutputType("an integer number"), argumentMap);
public Integer evalInt(String prompt, ArgumentBag arguments) {
var result = worker.eval(prompt, primitiveOutputType("an integer number"), arguments);
if (result instanceof Integer integer)
return integer;
else if (result instanceof String string) {
@@ -60,11 +56,11 @@ public class LlamaScript {
}
public int evalInt(String prompt, Object... arguments) {
return eval(prompt, Integer.class, arguments);
return eval(prompt, Integer.class, ListArgumentBag.of(arguments));
}
public Boolean evalBool(String prompt, HashMap<Integer, Object> argumentMap) {
var result = worker.eval(prompt, primitiveOutputType("a boolean value"), argumentMap);
public Boolean evalBool(String prompt, ArgumentBag arguments) {
var result = worker.eval(prompt, primitiveOutputType("a boolean value"), arguments);
if (result instanceof Boolean bool)
return bool;
else if (result instanceof String string)
@@ -74,7 +70,7 @@ public class LlamaScript {
}
public boolean evalBool(String prompt, Object... arguments) {
return eval(prompt, Boolean.class, arguments);
return eval(prompt, Boolean.class, ListArgumentBag.of(arguments));
}
private static String primitiveOutputType(String description) {

View File

@@ -1,5 +1,7 @@
package be.seeseemelk.llamascript;
import be.seeseemelk.llamascript.arguments.ArgumentBag;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.ollama4j.OllamaAPI;
import io.github.ollama4j.exceptions.OllamaBaseException;
@@ -11,7 +13,6 @@ import lombok.Builder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@Builder
public class LlamaScriptWorker {
@@ -19,24 +20,23 @@ public class LlamaScriptWorker {
private final String model;
private final ObjectMapper mapper = new ObjectMapper();
public Object eval(String prompt, String outputType, HashMap<Integer, Object> argumentMap) {
public Object eval(String prompt, String outputType, ArgumentBag arguments) {
try {
var optionsBuilder = new OptionsBuilder();
optionsBuilder.setSeed(20); // Chosen with 1d20. Yes, a nat 20! Let's go baby!
optionsBuilder.setTemperature(0f);
optionsBuilder.setTemperature(0);
var options = optionsBuilder.build();
var fullPrompt = new StringBuilder("""
You will receive a prompt, and will have to consider the question in the prompt with regards to the inputs you will receive.
Your output must be a JSON object containing the single value 'result'.
Your answer must be correct. Do not make mistakes
%s
The prompt: %s
Arguments:
""".formatted(outputType, prompt));
for (var entry : argumentMap.entrySet()) {
fullPrompt.append(" - ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
arguments.writeTo(fullPrompt);
System.out.printf("Prompt is: %s\n", fullPrompt);
var messages = new ArrayList<OllamaChatMessage>();
@@ -53,6 +53,7 @@ public class LlamaScriptWorker {
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
private static class Result {
public Object result;
}

View File

@@ -0,0 +1,12 @@
package be.seeseemelk.llamascript.arguments;
/**
* A bag of arguments that can be added to an evaluation request.
*/
public interface ArgumentBag {
/**
* Writes the values inside the argument bag to a StringBuilder.
* @param builder The StringBuilder to write to.
*/
void writeTo(StringBuilder builder);
}

View File

@@ -0,0 +1,43 @@
package be.seeseemelk.llamascript.arguments;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* A bag of arguments that will keep the order in which the arguments were added.
* The arguments are added with an index, which starts at one for the first argument.
*/
public class ListArgumentBag implements ArgumentBag {
private final List<Object> values = new ArrayList<>();
/**
* Adds an argument to the bag.
* @param value The argument to add.
* @return This instance.
*/
public ListArgumentBag add(Object value) {
values.add(value);
return this;
}
@Override
public void writeTo(StringBuilder builder) {
for (int i = 0; i < values.size(); i++) {
builder.append("[").append(i + 1).append("] -> ").append(Objects.toString(values.get(i))).append('\n');
}
}
/**
* Creates an argument bag from an array of values.
* @param values The values to add.
* @return The bag with the arguments added.
*/
public static ListArgumentBag of(Object... values) {
var bag = new ListArgumentBag();
for (var value : values) {
bag.add(value);
}
return bag;
}
}

View File

@@ -0,0 +1,31 @@
package be.seeseemelk.llamascript.arguments;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* A bag of arguments that will keep the order in which the arguments were added.
* The arguments are added with an index, which starts at one for the first argument.
*/
public class NamedArgumentBag implements ArgumentBag {
private final Map<String, Object> values = new HashMap<>();
/**
* Adds an argument to the bag.
* @param value The argument to add.
* @return This instance.
*/
public NamedArgumentBag set(String name, Object value) {
values.put(name, value);
return this;
}
@Override
public void writeTo(StringBuilder builder) {
for (var entry : values.entrySet()) {
var string = Objects.toString(entry.getValue());
builder.append(entry.getKey()).append(" -> ").append(string).append('\n');
}
}
}

View File

@@ -0,0 +1,26 @@
package be.seeseemelk.llamascript;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
public class BooleanTests extends AbstractLlamaTest {
@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3, 4, 5})
void isEven(int number) {
boolean isEven = llama.evalBool("Return true if the number is even, false if it's odd. (btw, zero is even)", number);
assertThat(isEven, equalTo(number % 2 == 0));
}
@Test
void isABuilding() {
boolean isBuilding = llama.evalBool("Return true if the argument is a building or skyscraper", "Dalai Lama");
assertThat(isBuilding, equalTo(false));
isBuilding = llama.evalBool("Return true if the argument is a building or skyscraper", "Burj Khalifa");
assertThat(isBuilding, equalTo(true));
}
}

View File

@@ -0,0 +1,44 @@
package be.seeseemelk.llamascript;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
public class StringTests extends AbstractLlamaTest {
@Test
void canUppercase() {
String value = llama.evalString("Return the string in uppercase", "Cat");
assertThat(value, equalTo("CAT"));
}
@Test
void canLowercase() {
String value = llama.evalString("Return the string in lowercase", "Cat");
assertThat(value, equalTo("cat"));
}
@Test
void canInvert() {
String value = llama.evalString("Return the string backwards. Do not change the capitalisation of any letters. E.g.: Za becomes aZ", "Cat");
assertThat(value, equalTo("taC"));
}
@Test
void canSelectLongest() {
String value = llama.evalString("Return the longest string", "Cat", "Dog", "Horse");
assertThat(value, equalTo("Horse"));
}
@Test
void canDoWeirdStuff() {
String value = llama.evalString("Return the string that does not fit", "Cat", "Dog", "Horse", "Bicycle");
assertThat(value, equalTo("Bicycle"));
}
@Test
void canDoReallyWeirdStuff() {
String value = llama.evalString("Sort the letters alphabetically", "horse");
assertThat(value, equalTo("ehors"));
}
}

View File

@@ -1,2 +1,4 @@
rootProject.name = 'libllamascript'
rootProject.name = 'llamascript'
include('libllamascript')
include('spring-llamascript-starter')

View File

@@ -0,0 +1,38 @@
plugins {
id 'java-library'
id 'io.freefair.lombok' version '8.10'
}
repositories {
mavenCentral()
maven{ url 'https://gitea.seeseepuff.be/api/packages/llamascript/maven' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot:3.3.2'
implementation 'org.springframework.boot:spring-boot-autoconfigure:3.3.2'
implementation 'org.springframework.boot:spring-boot-configuration-processor:3.3.2'
api project(':libllamascript')
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.hamcrest:hamcrest:3.0'
}
test {
useJUnitPlatform()
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications {
springLlamascriptStarter(MavenPublication) {
from components.java
artifactId 'spring-llamascript-starter'
}
}
}

View File

@@ -0,0 +1,31 @@
package be.seeseemelk.llamascript.autoconfigure;
import be.seeseemelk.llamascript.LLamaScriptFactory;
import be.seeseemelk.llamascript.LlamaScript;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(LlamaScriptProperties.class)
public class LlamaScriptAutoConfiguration {
@Autowired
private LlamaScriptProperties properties;
@Bean
@ConditionalOnMissingBean
public LLamaScriptFactory lLamaScriptFactory() {
var factory = new LLamaScriptFactory();
factory.setHost(properties.getHost());
factory.setModel(properties.getModel());
return factory;
}
@Bean
@ConditionalOnMissingBean
public LlamaScript llamaScript(LLamaScriptFactory factory) {
return factory.build();
}
}

View File

@@ -0,0 +1,13 @@
package be.seeseemelk.llamascript.autoconfigure;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ConfigurationProperties(prefix = "llamascript")
public class LlamaScriptProperties {
private String host = "http://localhost:11434";
private String model = "llama3.1:8b";
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
be.seeseemelk.llamascript.autoconfigure.LlamaScriptAutoConfiguration

View File

@@ -0,0 +1,12 @@
package be.seeseemelk.llamascript;
import org.junit.jupiter.api.BeforeEach;
public class AbstractLlamaTest {
protected LlamaScript llama;
@BeforeEach
void setUp() {
llama = new LLamaScriptFactory().build();
}
}

View File

@@ -0,0 +1,41 @@
package be.seeseemelk.llamascript;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
public class IntegerTests extends AbstractLlamaTest {
@Test
void canIncrementNumber() {
int value = llama.evalInt("Increment the number", 4);
assertThat(value, equalTo(5));
}
@Test
void canMultiply() {
int value = llama.evalInt("Multiply the numbers", 2, 3);
assertThat(value, equalTo(2*3));
}
@Test
void canMax() {
int value = llama.evalInt("Select the largest number", -2, 8);
assertThat(value, equalTo(8));
}
@Test
void canDoWeirdStuff() {
int value = llama.evalInt("Select the number with the most '5's in it.", 12, 5, 1023978, 158525);
assertThat(value, equalTo(158525));
}
@Test
void givePositivity() {
int value = llama.evalInt("If the string is something positive, return 1. Else, return 0.", "I like rainbows");
assertThat(value, equalTo(1));
value = llama.evalInt("If the string is something positive, return 1. Else, return 0.", "Death to all");
assertThat(value, equalTo(0));
}
}

View File

@@ -1,4 +0,0 @@
package be.seeseemelk.llamascript.arguments;
public abstract class ArgumentBag {
}