Save progress

This commit is contained in:
Tyler 2025-08-16 20:46:23 -04:00
parent 04b52792d3
commit aa593433bc
Signed by: tyler
GPG Key ID: 03B27509E17EFDC8
28 changed files with 513 additions and 11 deletions

1
.gitignore vendored
View File

@ -64,3 +64,4 @@ buildNumber.properties
# JDT-specific (Eclipse Java Development Tools)
.classpath
/agent/data/agent.db

9
.idea/Agent-Java.iml generated
View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

22
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Annotation profile for agent" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.38/lombok-1.18.38.jar" />
</processorPath>
<module name="agent" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="agent" options="-parameters" />
</option>
</component>
</project>

39
.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="agent.db" uuid="e748f0a0-6da5-4df7-ad15-d313f949230c">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/agent/src/main/resources/application.yml</remarks>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="agent.db" uuid="2d94ac6a-09f6-41f0-be54-1a576bdb0bfd">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>$PROJECT_DIR$/agent/src/main/resources/application.yml</remarks>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="jdbc:sqlite:./data/agent.db [DEBUG]" group="AgentApplication" uuid="11ef3d28-185a-421c-8470-fdbfcb98953e">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="jdbc:sqlite:./data/agent.db [DEBUG]" group="AgentApplication (1)" uuid="a1194a9f-8452-4ed5-99db-9b8813a9b174">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

6
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/agent/src/main/java" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

10
.idea/misc.xml generated
View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/agent/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

1
.idea/modules.xml generated
View File

@ -3,6 +3,7 @@
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Agent-Java.iml" filepath="$PROJECT_DIR$/.idea/Agent-Java.iml" />
<module fileurl="file://$PROJECT_DIR$/agent/agent.iml" filepath="$PROJECT_DIR$/agent/agent.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="AgentApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="corretto-21" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<module name="agent" />
<option name="SPRING_BOOT_MAIN_CLASS" value="com.clortox.agent.AgentApplication" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.clortox.agent.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

7
.idea/sqldialects.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/agent/src/main/resources/db/migration/V0__placeholder.sql" dialect="GenericSQL" />
<file url="PROJECT" dialect="SQLite" />
</component>
</project>

38
AGENTS.md Normal file
View File

@ -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.

View File

@ -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`

1
agent/.sdkmanrc Normal file
View File

@ -0,0 +1 @@
java=21.0.4-tem

View File

@ -35,10 +35,32 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Ensure core Spring Context is present for IDE builds -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- SQLite JDBC driver -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.0.0</version>
</dependency>
<!-- Hibernate community dialects (includes SQLiteDialect) -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- OpenAPI/Swagger UI via springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
@ -63,6 +85,48 @@
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.7.9</version>
</dependency>
<!-- LLM and agent orchestration -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
<version>1.3.0-beta9</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>1.3.0-beta9</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
<version>1.3.0-beta9</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.3.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.bsc.langgraph4j</groupId>-->
<!-- <artifactId>langgraph4j-core</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
@ -78,6 +142,27 @@
<build>
<plugins>
<!-- Enforce building with JDK 21 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[21,)</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>

View File

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

View File

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

View File

@ -0,0 +1,4 @@
package com.clortox.agent.agent;
public interface IAgent {
}

View File

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

View File

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

View File

@ -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<ChatMessage> getMessages(Object memoryId) {
return List.of();
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
}
@Override
public void deleteMessages(Object memoryId) {
}
}

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
spring.application.name=agent

View File

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

View File

@ -0,0 +1,5 @@
CREATE TABLE message (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
conversationId VARCHAR(32) NOT NULL,
content VARCHAR(4096) NOT NULL
)

View File

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

BIN
data/agent.db Normal file

Binary file not shown.