Skip to content

Commit 7cd1cfb

Browse files
authored
Merge pull request #1 from muhrifqii/dev/update
Major Update
2 parents 19ba82a + 8aea46f commit 7cd1cfb

File tree

64 files changed

+1264
-446
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1264
-446
lines changed

.editorconfig

+378
Large diffs are not rendered by default.

.github/FUNDING.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
patreon: muhrifqii
2+
ko_fi: muhrifqii

.github/workflows/validates.yml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Validates
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
7+
branches:
8+
- master
9+
10+
jobs:
11+
build:
12+
name: Build
13+
runs-on: ubuntu-24.04
14+
permissions:
15+
contents: read
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-java@v4
19+
with:
20+
java-version: 22
21+
distribution: temurin
22+
cache: gradle
23+
24+
- name: Compile
25+
run: ./gradlew clean build --no-daemon

.sdkmanrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Enable auto-env through the sdkman_auto_env config
2+
# Add key=value pairs of SDKs to use below
3+
java=22-graal

Makefile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
up: dev-env dev-run ## Startup / Spinup Docker Compose
3+
down: docker-stop ## Stop Docker
4+
destroy: docker-teardown clean ## Teardown (removes volumes, tmp files, etc...)
5+
6+
dev-env: ## Bootstrap Environment (with a Docker-Compose help).
7+
docker-compose up -d --build db
8+
9+
dev-run: dev-env
10+
./gradlew bootRun
11+
12+
docker-stop:
13+
docker-compose down
14+
15+
docker-teardown:
16+
docker-compose down --remove-orphans -v
17+

README.md

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
# LLM Ollama - Spring AI, WebFlux, R2DBC - SlakingSpringBed Sample
1+
# LLM Ollama - Spring AI, WebFlux, R2DBC
22

3-
The stack:
4-
- Ollama
5-
- Spring
6-
- Spring AI
3+
Sample project to showcase a java spring boot application with Generative AI powered using Spring AI
4+
5+
## The Stack:
6+
- Java 22
7+
- GraalVM
78
- Spring WebFlux
89
- Spring Data R2DBC
9-
- Docker
10-
- Flyway
10+
- Docker/Podman
11+
- Flyway
12+
- [Ollama](https://ollama.com)
13+

build.gradle.kts

+12-43
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,18 @@
11
plugins {
22
java
3-
id("org.springframework.boot") version "3.2.1"
4-
id("io.spring.dependency-management") version "1.1.4"
3+
alias(libs.plugins.spring.boot) apply false
4+
alias(libs.plugins.spring.dependencies) apply false
5+
alias(libs.plugins.graalvm) apply false
56
}
67

7-
group = "com.muhrifqii"
8-
version = "0.0.1-SNAPSHOT"
8+
allprojects {
9+
repositories {
10+
mavenCentral()
11+
maven(url = "https://repo.spring.io/milestone")
12+
maven(url = "https://repo.spring.io/snapshot")
13+
}
914

10-
java {
11-
sourceCompatibility = JavaVersion.VERSION_17
12-
}
13-
14-
configurations {
15-
compileOnly {
16-
extendsFrom(configurations.annotationProcessor.get())
17-
}
18-
}
19-
20-
repositories {
21-
mavenCentral()
22-
maven(url = "https://repo.spring.io/milestone")
23-
maven(url = "https://repo.spring.io/snapshot")
24-
}
25-
26-
dependencies {
27-
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
28-
implementation("org.springframework.boot:spring-boot-starter-webflux")
29-
implementation("org.flywaydb:flyway-core")
30-
implementation("org.springframework:spring-jdbc")
31-
32-
implementation("org.springframework.ai:spring-ai-ollama-spring-boot-starter:0.8.0-SNAPSHOT")
33-
34-
compileOnly("org.projectlombok:lombok")
35-
36-
runtimeOnly("org.postgresql:postgresql")
37-
runtimeOnly("org.postgresql:r2dbc-postgresql")
38-
39-
annotationProcessor("org.projectlombok:lombok")
40-
41-
testImplementation("org.springframework.boot:spring-boot-starter-test")
42-
testImplementation("org.springframework.boot:spring-boot-testcontainers")
43-
testImplementation("org.testcontainers:junit-jupiter")
44-
testImplementation("io.projectreactor:reactor-test")
45-
}
46-
47-
tasks.withType<Test> {
48-
useJUnitPlatform()
15+
tasks.withType<Test> {
16+
useJUnitPlatform()
17+
}
4918
}

docker-compose.yml

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
version: "3.5"
1+
version: "3.7"
2+
3+
volumes:
4+
postgres_data:
25

3-
services:
46

5-
postgres:
6-
container_name: slaking-springbed-ai-postgres
7-
image: postgres:13.13
7+
services:
8+
db:
9+
container_name: postgres_db
10+
image: postgres:13.15
811
restart: always
912
environment:
1013
- POSTGRES_USER=postgres
1114
- POSTGRES_PASSWORD=postgres
12-
- POSTGRES_DB=slakingspringbed
15+
- POSTGRES_DB=gh_slaking_springai
1316
ports:
14-
- "5432:5432"
17+
- "5431:5432"
1518
volumes:
16-
- postgres_data:/data/db
17-
18-
volumes:
19-
postgres_data:
19+
- postgres_data:/var/lib/postgresql/data
20+
healthcheck:
21+
test: [ "CMD", "pg_isready", "-U", "postgres" ]
22+
interval: 30s
23+
timeout: 20s
24+
retries: 5

gradle.properties

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
org.gradle.caching=true
2+
org.gradle.parallel=true
3+
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8

gradle/build-logic/build.gradle.kts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
`kotlin-dsl`
3+
}
4+
5+
java {
6+
sourceCompatibility = JavaVersion.VERSION_22
7+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
dependencyResolutionManagement {
2+
repositories {
3+
gradlePluginPortal()
4+
}
5+
6+
versionCatalogs {
7+
create("libs") {
8+
from(files("../libs.versions.toml"))
9+
}
10+
}
11+
}
12+
13+
rootProject.name = "build-logic"

gradle/libs.versions.toml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html
2+
[versions]
3+
spring-boot = "3.3.3"
4+
spring-ai = "1.0.0-SNAPSHOT"
5+
lombok = "1.18.34"
6+
reactor-core = "3.6.9"
7+
graalvm = "0.10.2"
8+
flyway = "10.12.0"
9+
10+
[libraries]
11+
lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" }
12+
13+
spring-boot-web = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "spring-boot" }
14+
spring-boot-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "spring-boot" }
15+
spring-boot-r2dbc = { module = "org.springframework.boot:spring-boot-starter-data-r2dbc", version.ref = "spring-boot" }
16+
spring-boot-devtools = { module = "org.springframework.boot:spring-boot-devtools", version.ref = "spring-boot" }
17+
spring-boot-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" }
18+
19+
spring-ai-bom = { group = "org.springframework.ai", name = "spring-ai-bom", version.ref = "spring-ai" }
20+
spring-ai-ollama = { group = "org.springframework.ai", name = "spring-ai-ollama-spring-boot-starter" }
21+
22+
reactor-core = { module = "io.projectreactor:reactor-core", version.ref = "reactor-core" }
23+
reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "reactor-core" }
24+
25+
flyway-core = { module = "org.flywaydb:flyway-core", version.ref = "flyway" }
26+
flyway-postgresql = { module = "org.flywaydb:flyway-database-postgresql", version.ref = "flyway" }
27+
postgresql = { module = "org.postgresql:postgresql", version = "42.7.3" }
28+
postgresql-r2dbc = { module = "org.postgresql:r2dbc-postgresql", version = "1.0.5.RELEASE" }
29+
30+
[plugins]
31+
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
32+
spring-dependencies = { id = "io.spring.dependency-management", version = "1.1.6" }
33+
graalvm = { id = "org.graalvm.buildtools.native", version.ref = "graalvm" }
34+
35+
[bundles]

gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

gradlew

100644100755
File mode changed.

llm/api/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Domain layer contract module

llm/api/build.gradle.kts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
plugins {
2+
java
3+
}
4+
5+
java {
6+
sourceCompatibility = JavaVersion.VERSION_22
7+
toolchain {
8+
languageVersion.set(JavaLanguageVersion.of(22))
9+
}
10+
}
11+
12+
configurations {
13+
compileOnly {
14+
extendsFrom(configurations.annotationProcessor.get())
15+
}
16+
}
17+
18+
dependencies {
19+
implementation(libs.reactor.core)
20+
compileOnly(libs.lombok)
21+
annotationProcessor(libs.lombok)
22+
}
23+
24+
tasks.test {
25+
useJUnitPlatform()
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.muhrifqii.llm.api.datamodels;
2+
3+
public record ErrorResponse(String message) {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.muhrifqii.llm.api.datamodels;
2+
3+
import lombok.experimental.StandardException;
4+
5+
@StandardException
6+
public abstract sealed class HandledError
7+
extends RuntimeException
8+
permits HandledError.NotFoundError, HandledError.InvalidArgsError {
9+
10+
@StandardException
11+
public static final class NotFoundError extends HandledError {
12+
}
13+
14+
@StandardException
15+
public static final class InvalidArgsError extends HandledError {
16+
}
17+
18+
public static HandledError notFound(String message) {
19+
return new NotFoundError(message);
20+
}
21+
22+
public static HandledError invalidArgs(String message) {
23+
return new InvalidArgsError(message);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.muhrifqii.llm.api.datamodels.conversations;
2+
3+
import lombok.Builder;
4+
5+
@Builder
6+
public record Conversation(
7+
String id, String name, String description, String createdAt, String updatedAt) {
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.muhrifqii.llm.api.datamodels.conversations;
2+
3+
public record ConversationRequest(String message) {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.muhrifqii.llm.api.datamodels.conversations;
2+
3+
import lombok.Builder;
4+
5+
@Builder
6+
public record Message(
7+
String id,
8+
String conversationId,
9+
String content,
10+
String createdAt) {
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.muhrifqii.llm.api.datamodels.conversations;
2+
3+
public record UserMessage(
4+
String content,
5+
UserMultimodalityMedia media) {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.muhrifqii.llm.api.datamodels.conversations;
2+
3+
import lombok.NonNull;
4+
5+
/**
6+
* Wrapper for multimodality media
7+
*/
8+
public record UserMultimodalityMedia(@NonNull String mimeType, byte[] content) {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Conversation related data models
3+
*/
4+
package com.muhrifqii.llm.api.datamodels.conversations;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Provides data models that used between domain layer and data layer (Clean
3+
* Architecture Pattern).
4+
*/
5+
package com.muhrifqii.llm.api.datamodels;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.muhrifqii.llm.api.usecases;
2+
3+
import com.muhrifqii.llm.api.datamodels.conversations.Message;
4+
import com.muhrifqii.llm.api.datamodels.conversations.UserMessage;
5+
6+
import reactor.core.publisher.Flux;
7+
import reactor.core.publisher.Mono;
8+
9+
public interface PromptModelUsecase {
10+
11+
Mono<Message> chat(String conversationID, UserMessage message);
12+
13+
Flux<Message> streamChat(String conversationID, UserMessage message);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.muhrifqii.llm.api.usecases;
2+
3+
import reactor.core.publisher.Flux;
4+
5+
public interface SemanticSearchUsecase<T> {
6+
7+
Flux<T> search(String query);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.muhrifqii.llm.api.usecases;
2+
3+
import reactor.core.publisher.Mono;
4+
5+
public interface SummarizerUsecase {
6+
Mono<String> makeTitle(String text);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Provides contracts for the the domain layer (Clean
3+
* Architecture Pattern).
4+
*/
5+
package com.muhrifqii.llm.api.usecases;

0 commit comments

Comments
 (0)