diff --git a/.gitignore b/.gitignore
index 264b524..49852a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,3 +64,4 @@ buildNumber.properties
# JDT-specific (Eclipse Java Development Tools)
.classpath
+/agent/data/agent.db
diff --git a/.idea/Agent-Java.iml b/.idea/Agent-Java.iml
deleted file mode 100644
index d6ebd48..0000000
--- a/.idea/Agent-Java.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..688d071
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..494566a
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ sqlite.xerial
+ true
+ true
+ $PROJECT_DIR$/agent/src/main/resources/application.yml
+ org.sqlite.JDBC
+ jdbc:sqlite:./data/agent.db
+ $ProjectFileDir$
+
+
+ sqlite.xerial
+ true
+ true
+ $PROJECT_DIR$/agent/src/main/resources/application.yml
+ org.sqlite.JDBC
+ jdbc:sqlite:./data/agent.db
+ $ProjectFileDir$
+
+
+ sqlite.xerial
+ true
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:./data/agent.db
+ $ProjectFileDir$
+
+
+ sqlite.xerial
+ true
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:./data/agent.db
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..608ba93
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6f29fee..76a772f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,14 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 0230b78..417e50d 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,6 +3,7 @@
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/AgentApplication.xml b/.idea/runConfigurations/AgentApplication.xml
new file mode 100644
index 0000000..369831b
--- /dev/null
+++ b/.idea/runConfigurations/AgentApplication.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..39b8559
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..8ecd757
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,38 @@
+# Repository Guidelines
+
+## Project Structure & Modules
+- `src/main/java/com/clortox/agent`: Spring Boot app code (`AgentApplication`, support classes).
+- `src/main/resources`: Config (e.g., `application.yml`). SQLite DB path: `./data/agent.db`.
+- `src/test/java/com/clortox/agent`: JUnit 5 tests (Spring test support).
+- `target/`: Build outputs. Not checked in.
+- `data/`: Created at startup for local DB. Do not commit.
+
+## Build, Test, and Run
+- Build: `./mvnw clean verify` — compile, run tests, package the app.
+- Package (skip tests): `./mvnw -DskipTests package` — produces JAR in `target/`.
+- Run locally: `./mvnw spring-boot:run` — starts the API with SQLite.
+- Run JAR: `java -jar target/agent-0.0.1-SNAPSHOT.jar` — same as above.
+- Java version: JDK 21 is enforced by the build (enforcer plugin).
+
+## Coding Style & Naming
+- Language: Java 21. Indentation: 4 spaces; 120-col soft wrap.
+- Packages: `com.clortox.agent.*`. Class names are PascalCase; methods/fields camelCase.
+- Spring components: annotate appropriately (`@RestController`, `@Service`, `@Entity`). Favor constructor injection.
+- Lombok: allowed where present (e.g., `@Getter`, `@RequiredArgsConstructor`). Avoid overuse that obscures intent.
+
+## Testing Guidelines
+- Frameworks: JUnit 5 with Spring Boot test starter; Modulith test starter is available.
+- Naming: `*Tests.java` per class or feature (e.g., `AgentApplicationTests`).
+- Run tests: `./mvnw test`. Prefer slice tests where possible; use `@SpringBootTest` only when needed.
+- Aim for meaningful coverage on domain logic; avoid brittle tests against logs or timing.
+
+## Commit & Pull Requests
+- Commits: imperative, concise subject (≤72 chars). Example: `Add SQLite dialect and datasource config`.
+- Include context in body: rationale, notable trade-offs, follow-up TODOs.
+- PRs: clear description, linked issues (`Fixes #123`), test evidence (logs or screenshots), and manual run steps.
+- Ensure: `./mvnw clean verify` passes locally and no files added to `data/` or `target/`.
+
+## Configuration Tips
+- DB location: `application.yml` points to `jdbc:sqlite:./data/agent.db`. Override via CLI:
+ `./mvnw spring-boot:run -Dspring-boot.run.arguments="--spring.datasource.url=jdbc:sqlite:/tmp/agent.db"`.
+- The app creates `./data` at startup; avoid hardcoding absolute paths.
diff --git a/README.md b/README.md
index df95abc..c7c05bb 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,10 @@
# Agent-Java
+- Build and run the app from `agent/`.
+- Swagger UI is available once running.
+
+Quick start:
+
+- Run: `cd agent && ./mvnw spring-boot:run`
+- Swagger UI: `http://localhost:8080/swagger-ui`
+- OpenAPI JSON: `http://localhost:8080/v3/api-docs`
diff --git a/agent/.sdkmanrc b/agent/.sdkmanrc
new file mode 100644
index 0000000..2b8a854
--- /dev/null
+++ b/agent/.sdkmanrc
@@ -0,0 +1 @@
+java=21.0.4-tem
diff --git a/agent/pom.xml b/agent/pom.xml
index a864481..485cccb 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -35,10 +35,32 @@
org.springframework.boot
spring-boot-starter-data-jpa
+
+
+ org.springframework
+ spring-context
+
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.46.0.0
+
+
+
+ org.hibernate.orm
+ hibernate-community-dialects
+
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.6.0
+
org.springframework.modulith
spring-modulith-starter-core
@@ -63,6 +85,48 @@
spring-modulith-starter-test
test
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
+
+
+ io.projectreactor
+ reactor-core
+ 3.7.9
+
+
+
+
+ dev.langchain4j
+ langchain4j-ollama-spring-boot-starter
+ 1.3.0-beta9
+
+
+ dev.langchain4j
+ langchain4j-spring-boot-starter
+ 1.3.0-beta9
+
+
+ dev.langchain4j
+ langchain4j-reactor
+ 1.3.0-beta9
+
+
+ dev.langchain4j
+ langchain4j
+ 1.3.0
+
+
+
+
+
+
+ dev.langchain4j
+ langchain4j-ollama
+ 1.3.0
+
@@ -78,6 +142,27 @@
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.5.0
+
+
+ enforce-java
+
+ enforce
+
+
+
+
+ [21,)
+
+
+
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
diff --git a/agent/src/main/java/com/clortox/agent/AgentApplication.java b/agent/src/main/java/com/clortox/agent/AgentApplication.java
index 2924406..b69ef6e 100644
--- a/agent/src/main/java/com/clortox/agent/AgentApplication.java
+++ b/agent/src/main/java/com/clortox/agent/AgentApplication.java
@@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class AgentApplication {
public static void main(String[] args) {
+ DataDirectoryInitializer.createDataDirectory();
SpringApplication.run(AgentApplication.class, args);
}
diff --git a/agent/src/main/java/com/clortox/agent/DataDirectoryInitializer.java b/agent/src/main/java/com/clortox/agent/DataDirectoryInitializer.java
new file mode 100644
index 0000000..0a8a390
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/DataDirectoryInitializer.java
@@ -0,0 +1,29 @@
+package com.clortox.agent;
+
+import java.io.UncheckedIOException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Ensures the application's data directory exists before Spring Boot starts.
+ */
+public final class DataDirectoryInitializer {
+
+ private static final String DATA_DIR = "data";
+
+ private DataDirectoryInitializer() {}
+
+ public static void createDataDirectory() {
+ Path path = Paths.get(DATA_DIR);
+ try {
+ if (Files.notExists(path)) {
+ Files.createDirectories(path);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to create data directory: " + path.toAbsolutePath(), e);
+ }
+ }
+}
+
diff --git a/agent/src/main/java/com/clortox/agent/agent/IAgent.java b/agent/src/main/java/com/clortox/agent/agent/IAgent.java
new file mode 100644
index 0000000..6f560ea
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/IAgent.java
@@ -0,0 +1,4 @@
+package com.clortox.agent.agent;
+
+public interface IAgent {
+}
diff --git a/agent/src/main/java/com/clortox/agent/agent/controllers/AgentController.java b/agent/src/main/java/com/clortox/agent/agent/controllers/AgentController.java
new file mode 100644
index 0000000..dc40643
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/controllers/AgentController.java
@@ -0,0 +1,17 @@
+package com.clortox.agent.agent.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("Agent")
+public class AgentController {
+
+
+ @GetMapping(path = "/assistant")
+ public ResponseEntity> get(String message) {
+ return ResponseEntity.status(HttpStatus.OK).body("lol");
+ }
+}
diff --git a/agent/src/main/java/com/clortox/agent/agent/llm/LLMConfiguration.java b/agent/src/main/java/com/clortox/agent/agent/llm/LLMConfiguration.java
new file mode 100644
index 0000000..29feefa
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/llm/LLMConfiguration.java
@@ -0,0 +1,49 @@
+package com.clortox.agent.agent.llm;
+
+import dev.langchain4j.http.client.HttpClientBuilder;
+import dev.langchain4j.http.client.spring.restclient.SpringRestClient;
+import dev.langchain4j.http.client.spring.restclient.SpringRestClientBuilder;
+import dev.langchain4j.model.chat.Capability;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.ollama.OllamaChatModel;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.http.client.HttpClientSettings;
+import org.springframework.boot.http.client.JdkHttpClientBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestClient;
+
+import java.net.http.HttpClient;
+import java.util.Set;
+
+@Configuration
+public class LLMConfiguration {
+
+ @Bean
+ public ChatModel getOllama(
+ @Value("${agent.ollama.baseUrl}") String baseUrl,
+ @Value("${agent.ollama.model}") String model,
+ @Value("${agent.ollama.temp}") Double temp
+ ) {
+
+ RestClient.Builder restClientBuilder = RestClient.builder()
+ .requestFactory(new HttpComponentsClientHttpRequestFactory());
+
+ SpringRestClientBuilder springRestClientBuilder = SpringRestClient.builder()
+ .restClientBuilder(restClientBuilder)
+ .streamingRequestExecutor(new VirtualThreadTaskExecutor());
+
+ return OllamaChatModel.builder()
+ .baseUrl(baseUrl)
+ .httpClientBuilder(springRestClientBuilder)
+ .modelName(model)
+ .temperature(temp)
+ .maxRetries(3)
+ .returnThinking(false)
+ .logResponses(true)
+ .logResponses(true)
+ .build();
+ }
+}
diff --git a/agent/src/main/java/com/clortox/agent/agent/memory/PersistentChatMemoryStore.java b/agent/src/main/java/com/clortox/agent/agent/memory/PersistentChatMemoryStore.java
new file mode 100644
index 0000000..43eb967
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/memory/PersistentChatMemoryStore.java
@@ -0,0 +1,24 @@
+package com.clortox.agent.agent.memory;
+
+import dev.langchain4j.data.message.ChatMessage;
+import dev.langchain4j.store.memory.chat.ChatMemoryStore;
+
+import java.util.List;
+
+public class PersistentChatMemoryStore implements ChatMemoryStore {
+
+ @Override
+ public List getMessages(Object memoryId) {
+ return List.of();
+ }
+
+ @Override
+ public void updateMessages(Object memoryId, List messages) {
+
+ }
+
+ @Override
+ public void deleteMessages(Object memoryId) {
+
+ }
+}
diff --git a/agent/src/main/java/com/clortox/agent/agent/memory/persistence/Message.java b/agent/src/main/java/com/clortox/agent/agent/memory/persistence/Message.java
new file mode 100644
index 0000000..489bf2d
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/memory/persistence/Message.java
@@ -0,0 +1,30 @@
+package com.clortox.agent.agent.memory.persistence;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.*;
+
+@Entity
+@Table(name = "message")
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Message {
+
+ @Id
+ @Getter
+ @Setter
+ @Column(name = "ID", nullable = false, updatable = false)
+ private Long id;
+
+ @Column(name = "conversationId", nullable = false, updatable = false)
+ private String conversationId;
+
+ @Column(name = "content", nullable = false, updatable = false)
+ private String content;
+
+
+
+}
diff --git a/agent/src/main/java/com/clortox/agent/agent/service/Agent.java b/agent/src/main/java/com/clortox/agent/agent/service/Agent.java
new file mode 100644
index 0000000..6a1ab0d
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/agent/service/Agent.java
@@ -0,0 +1,19 @@
+package com.clortox.agent.agent.service;
+
+import dev.langchain4j.model.chat.ChatModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class Agent {
+
+ private final ChatModel model;
+
+ @Autowired
+ public Agent(
+ ChatModel model
+ ) {
+
+ this.model = model;
+ }
+}
diff --git a/agent/src/main/java/com/clortox/agent/starters/StarterListener.java b/agent/src/main/java/com/clortox/agent/starters/StarterListener.java
new file mode 100644
index 0000000..2ef14a7
--- /dev/null
+++ b/agent/src/main/java/com/clortox/agent/starters/StarterListener.java
@@ -0,0 +1,32 @@
+package com.clortox.agent.starters;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class StarterListener {
+
+ @EventListener
+ public void onApplicationReady(ApplicationReadyEvent event) {
+
+ printBanner();
+
+ }
+
+ private void printBanner() {
+ log.info(" █████╗ ██████╗ ███████╗███╗ ██╗████████╗");
+ log.info("██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝");
+ log.info("███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║");
+ log.info("██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║");
+ log.info("██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║");
+ log.info("╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝");
+
+ log.info("Welcome to the local agent!");
+
+ log.info("View documentation at http://localhost:{}/swagger-ui/index.html", 8080);
+ }
+}
diff --git a/agent/src/main/resources/application.properties b/agent/src/main/resources/application.properties
deleted file mode 100644
index 0b1a488..0000000
--- a/agent/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.application.name=agent
diff --git a/agent/src/main/resources/application.yml b/agent/src/main/resources/application.yml
new file mode 100644
index 0000000..3198954
--- /dev/null
+++ b/agent/src/main/resources/application.yml
@@ -0,0 +1,37 @@
+spring:
+ datasource:
+ url: jdbc:sqlite:${SQLITE_DB_LOCATION:./data/agent.db}
+ driver-class-name: org.sqlite.JDBC
+ jpa:
+ hibernate:
+ ddl-auto: none
+ properties:
+ hibernate:
+ dialect: org.hibernate.community.dialect.SQLiteDialect
+ format_sql: true
+ open-in-view: false
+
+logging:
+ level:
+ org.hibernate.SQL: warn
+ org.hibernate.type.descriptor.sql.BasicBinder: warn
+
+springdoc:
+ swagger-ui:
+ path: /swagger-ui
+
+
+agent:
+ ollama:
+ baseUrl: "${OLLAMA_BASEURL:http://10.0.3.2:8080}"
+ model: "${OLLAMA_MODEL:llama3.2}"
+ temp: "${OLLAMA_TEMP:0.2}"
+
+#langchain4j:
+# ollama:
+# chat-model:
+# base-url: "${OLLAMA_BASEURL:http://10.0.3.2:8080}"
+# model-name: "${OLLAMA_MODEL:llama3.2}"
+# log-requests: true
+# log-responses: true
+# return-thinking: false
diff --git a/agent/src/main/resources/db/migration/V0__placeholder.sql b/agent/src/main/resources/db/migration/V0__placeholder.sql
new file mode 100644
index 0000000..e7bd515
--- /dev/null
+++ b/agent/src/main/resources/db/migration/V0__placeholder.sql
@@ -0,0 +1,5 @@
+CREATE TABLE message (
+ ID INTEGER PRIMARY KEY AUTOINCREMENT,
+ conversationId VARCHAR(32) NOT NULL,
+ content VARCHAR(4096) NOT NULL
+)
\ No newline at end of file
diff --git a/agent/src/test/java/com/clortox/agent/ModularityTest.java b/agent/src/test/java/com/clortox/agent/ModularityTest.java
new file mode 100644
index 0000000..9743b12
--- /dev/null
+++ b/agent/src/test/java/com/clortox/agent/ModularityTest.java
@@ -0,0 +1,12 @@
+package com.clortox.agent;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.modulith.core.ApplicationModules;
+
+public class ModularityTest {
+
+ @Test
+ public void verify() {
+ ApplicationModules.of(AgentApplication.class).verify();
+ }
+}
diff --git a/data/agent.db b/data/agent.db
new file mode 100644
index 0000000..b7c0e37
Binary files /dev/null and b/data/agent.db differ