Skip to content

Commit

Permalink
jlama-sentiment added -> thank you for attending airhacks.live
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamBien committed Dec 19, 2024
1 parent e7c867d commit 8fe6f58
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 0 deletions.
1 change: 1 addition & 0 deletions samples/jlama-sentiment-v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
models/*
2 changes: 2 additions & 0 deletions samples/jlama-sentiment-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Models are located in the `models` directory and will be automatically downloaded.
The model files are larger than 1GB.
79 changes: 79 additions & 0 deletions samples/jlama-sentiment-v2/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>airhacks</groupId>
<artifactId>jlama-sentiment</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.7.1</version>
<configuration>
<finalName>sentimental</finalName>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>airhacks.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>package-everything</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-core</artifactId>
<version>0.8.3</version>
</dependency>
<dependency>
<groupId>com.github.tjake</groupId>
<artifactId>jlama-native</artifactId>
<version>0.8.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>2.0.16</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<java.version>21</java.version>
</properties>
</project>
16 changes: 16 additions & 0 deletions samples/jlama-sentiment-v2/src/main/java/airhacks/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package airhacks;

import airhacks.sentimental.boundary.SentimentAnalysis;

/**
*
* @author airhacks.com
*/
interface App {

static void main(String... args) {
var message = args.length > 0 ? args[0]: "java is great";
var result = SentimentAnalysis.analyze(message);
System.out.println(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package airhacks.logging.control;

public interface Log {

public static void info(String message){
System.out.println(message);
}

public static void info(Object message){
info(message.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package airhacks.sentimental.boundary;

import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;

import airhacks.logging.control.Log;
import airhacks.sentimental.control.ModelLoader;
import airhacks.sentimental.entity.Result;

public interface SentimentAnalysis {

String systemPrompt = """
Your task is to analyze whether a given statement is positive or negative.
Only respond with either: "positive", "negative" or "neutral".
Analyze the following statement:
""";

static String invoke(String message) throws IOException {
var model = ModelLoader.load();
var promptContext = model.promptSupport()
.get()
.builder()
.addSystemMessage(systemPrompt)
.addUserMessage(message)
.build();

var response = model.generate(UUID.randomUUID(), promptContext, 0.0f, 256, (s, f) -> {});
var duration = response.promptTimeMs;
Log.info("responded in: %d ms".formatted(duration));
return response.responseText;
}

public static Result analyze(String message){
var start = Instant.now();
String response;
try {
response = invoke(message);

} catch (IOException e) {
throw new RuntimeException("cannot invoke model. Reason: " + e);
}
var duration = Duration.between(start, Instant.now());
return Result.fromLLMResponse(duration,response);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package airhacks.sentimental.control;

import java.io.IOException;

import com.github.tjake.jlama.model.AbstractModel;
import com.github.tjake.jlama.model.ModelSupport;
import com.github.tjake.jlama.safetensors.DType;
import com.github.tjake.jlama.safetensors.SafeTensorSupport;

public interface ModelLoader {
enum Model{
TINY("tjake/TinyLlama-1.1B-Chat-v1.0-Jlama-Q4"),
MISTRAL7B("tjake/Mistral-7B-Instruct-v0.3-JQ4"),
MIXTRAL("tjake/Mixtral-8x7B-Instruct-v0.1-JQ4"),
LLAMA("tjake/Llama-3.2-1B-Instruct-Jlama-Q4");

final String modelName;

private Model(String modelName) {
this.modelName = modelName;
}

public String modelName(){
return this.modelName;
}


}
String workingDirectory = "./models";


static AbstractModel load() throws IOException{
var localModelPath = SafeTensorSupport
.maybeDownloadModel(workingDirectory, Model.LLAMA.modelName());
return ModelSupport.loadModel(localModelPath, DType.F32, DType.I8);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package airhacks.sentimental.entity;

public class HallucinationException extends IllegalStateException{

public HallucinationException(String response) {
super("unexpected: " + response);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package airhacks.sentimental.entity;

import java.time.Duration;

public record Result(Duration duration,Sentiment sentiment) {
public enum Sentiment {
POSITIVE, NEGATIVE, NEUTRAL;
}

public static Result fromLLMResponse(Duration duration,String answer) {
var normalized = answer
.trim()
.toUpperCase();
try{
var sentiment = Sentiment.valueOf(normalized);
return new Result(duration,sentiment);
}catch(IllegalArgumentException ex){
throw new HallucinationException(answer);
}
}

public boolean isPositive(){
return Sentiment.POSITIVE.equals(this.sentiment);
}

public boolean isNegative(){
return Sentiment.NEGATIVE.equals(this.sentiment);
}

public boolean isNeutral(){
return Sentiment.NEUTRAL.equals(this.sentiment);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package airhacks.sentimental.boundary;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;

import org.junit.jupiter.api.Test;

import airhacks.logging.control.Log;

public class SentimentAnalysisTest {

@Test
void analyze() throws IOException {
var result = SentimentAnalysis.analyze("java is hot and tastes really good");
Log.info(result);
assertThat(result.isPositive()).isTrue();

result = SentimentAnalysis.analyze("python is dangerous, unpredictable and slow");
Log.info(result);
assertThat(result.isNegative()).isTrue();

result = SentimentAnalysis.analyze("old rusty engine");
Log.info(result);
assertThat(result.isNegative()).isTrue();

result = SentimentAnalysis.analyze("where should I GO?");
Log.info(result);
assertThat(result.isNeutral()).isTrue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package airhacks.sentimental.entity;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

public class ResultTest {
@Test
void hallucination() {
var exception = assertThrows(HallucinationException.class, ()-> Result.fromLLMResponse(null, "incredible"));
assertThat(exception).hasMessageContaining("incredible");

}

@Test
void positive() {
var result = Result.fromLLMResponse(null, "positive");
assertThat(result.isPositive()).isTrue();

}

@Test
void negative() {
var result = Result.fromLLMResponse(null, "negative");
assertThat(result.isNegative()).isTrue();
}
}

0 comments on commit 8fe6f58

Please sign in to comment.