Save progress
This commit is contained in:
parent
aa593433bc
commit
db13632687
11
.idea/dataSources.xml
generated
11
.idea/dataSources.xml
generated
@ -1,15 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
<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">
|
<data-source source="LOCAL" name="agent.db" uuid="2d94ac6a-09f6-41f0-be54-1a576bdb0bfd">
|
||||||
<driver-ref>sqlite.xerial</driver-ref>
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
@ -27,7 +18,7 @@
|
|||||||
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
|
<jdbc-url>jdbc:sqlite:./data/agent.db</jdbc-url>
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
</data-source>
|
</data-source>
|
||||||
<data-source source="LOCAL" name="jdbc:sqlite:./data/agent.db [DEBUG]" group="AgentApplication (1)" uuid="a1194a9f-8452-4ed5-99db-9b8813a9b174">
|
<data-source source="LOCAL" name="jdbc:sqlite:./data/agent.db [DEBUG]" group="AgentApplication" uuid="0b41b15e-1a4f-4cf2-8b38-4cbc2765a473">
|
||||||
<driver-ref>sqlite.xerial</driver-ref>
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<imported>true</imported>
|
<imported>true</imported>
|
||||||
|
6
.idea/data_source_mapping.xml
generated
Normal file
6
.idea/data_source_mapping.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourcePerFileMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$/agent/src/main/resources/db/migration/V1.0.0.1__init.sql" value="2d94ac6a-09f6-41f0-be54-1a576bdb0bfd" />
|
||||||
|
</component>
|
||||||
|
</project>
|
2
.idea/sqldialects.xml
generated
2
.idea/sqldialects.xml
generated
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
<file url="file://$PROJECT_DIR$/agent/src/main/resources/db/migration/V0__placeholder.sql" dialect="GenericSQL" />
|
<file url="file://$PROJECT_DIR$/agent/src/main/resources/db/migration/V1.0.0.1__init.sql" dialect="SQLite" />
|
||||||
<file url="PROJECT" dialect="SQLite" />
|
<file url="PROJECT" dialect="SQLite" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
5
agent/.env.example
Normal file
5
agent/.env.example
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
OLLAMA_BASEURL=https://10.0.3.2:8080
|
||||||
|
OLLAMA_MODEL=mistral-small3.2
|
||||||
|
OLLAMA_TEMP=0.2
|
||||||
|
|
||||||
|
TOOLS_TENOR_API_KEY=mytenorkey
|
3
agent/.gitignore
vendored
3
agent/.gitignore
vendored
@ -31,3 +31,6 @@ build/
|
|||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
.env
|
||||||
|
data/
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
</scm>
|
</scm>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
<spring-modulith.version>1.4.1</spring-modulith.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -61,14 +60,20 @@
|
|||||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
<version>2.6.0</version>
|
<version>2.6.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.modulith</groupId>
|
<dependency>
|
||||||
<artifactId>spring-modulith-starter-core</artifactId>
|
<groupId>org.xerial</groupId>
|
||||||
</dependency>
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
<dependency>
|
<version>3.46.0.0</version>
|
||||||
<groupId>org.springframework.modulith</groupId>
|
</dependency>
|
||||||
<artifactId>spring-modulith-starter-jpa</artifactId>
|
|
||||||
</dependency>
|
<!-- Flyway core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
<version>10.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
@ -80,11 +85,6 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.modulith</groupId>
|
|
||||||
<artifactId>spring-modulith-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||||
<artifactId>httpclient5</artifactId>
|
<artifactId>httpclient5</artifactId>
|
||||||
@ -118,25 +118,38 @@
|
|||||||
<artifactId>langchain4j</artifactId>
|
<artifactId>langchain4j</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- <dependency>-->
|
<dependency>
|
||||||
<!-- <groupId>org.bsc.langgraph4j</groupId>-->
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
<!-- <artifactId>langgraph4j-core</artifactId>-->
|
<artifactId>langgraph4j-langchain4j</artifactId>
|
||||||
<!-- </dependency>-->
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
|
<artifactId>langgraph4j-agent-executor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
|
<artifactId>langgraph4j-core</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-ollama</artifactId>
|
<artifactId>langchain4j-ollama</artifactId>
|
||||||
<version>1.3.0</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.modulith</groupId>
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
<artifactId>spring-modulith-bom</artifactId>
|
<artifactId>langgraph4j-bom</artifactId>
|
||||||
<version>${spring-modulith.version}</version>
|
<version>1.6.0</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
package com.clortox.agent.agent;
|
|
||||||
|
|
||||||
public interface IAgent {
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
package com.clortox.agent.agent.controllers;
|
package com.clortox.agent.agent.controllers;
|
||||||
|
|
||||||
|
import com.clortox.agent.agent.state.SmartAgent;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -9,9 +10,14 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController("Agent")
|
@RestController("Agent")
|
||||||
public class AgentController {
|
public class AgentController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SmartAgent agent;
|
||||||
|
|
||||||
|
|
||||||
@GetMapping(path = "/assistant")
|
@GetMapping(path = "/assistant")
|
||||||
public ResponseEntity<?> get(String message) {
|
public ResponseEntity<?> get(String message) {
|
||||||
return ResponseEntity.status(HttpStatus.OK).body("lol");
|
return ResponseEntity.status(HttpStatus.OK).body(agent.invoke(message));
|
||||||
|
|
||||||
|
// return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class ChatCompletionChunk {
|
||||||
|
private String id;
|
||||||
|
private String object; // "chat.completion.chunk"
|
||||||
|
private long created;
|
||||||
|
private String model;
|
||||||
|
private List<ChunkChoice> choices;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class ChatCompletionRequest {
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
private List<Message> messages;
|
||||||
|
|
||||||
|
private Double temperature;
|
||||||
|
|
||||||
|
@JsonProperty("max_tokens")
|
||||||
|
private Integer maxTokens;
|
||||||
|
|
||||||
|
private Boolean stream;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
package com.example.openai.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// ========= NON-STREAMING RESPONSE DTO =========
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class ChatCompletionResponse {
|
||||||
|
private String id;
|
||||||
|
private String object; // "chat.completion"
|
||||||
|
private long created;
|
||||||
|
private String model;
|
||||||
|
private List<Choice> choices;
|
||||||
|
private Usage usage;
|
||||||
|
}
|
18
agent/src/main/java/com/clortox/agent/agent/dto/Choice.java
Normal file
18
agent/src/main/java/com/clortox/agent/agent/dto/Choice.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class Choice {
|
||||||
|
private int index;
|
||||||
|
private Message message;
|
||||||
|
|
||||||
|
@JsonProperty("finish_reason")
|
||||||
|
private FinishReason finishReason;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class ChunkChoice {
|
||||||
|
private int index;
|
||||||
|
private Delta delta;
|
||||||
|
|
||||||
|
@JsonProperty("finish_reason")
|
||||||
|
private FinishReason finishReason;
|
||||||
|
}
|
14
agent/src/main/java/com/clortox/agent/agent/dto/Delta.java
Normal file
14
agent/src/main/java/com/clortox/agent/agent/dto/Delta.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class Delta {
|
||||||
|
private Role role; // only set in first chunk
|
||||||
|
private String content;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum FinishReason {
|
||||||
|
STOP("stop"),
|
||||||
|
LENGTH("length"),
|
||||||
|
TOOL_CALLS("tool_calls"),
|
||||||
|
CONTENT_FILTER("content_filter");
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
private final String value;
|
||||||
|
}
|
17
agent/src/main/java/com/clortox/agent/agent/dto/Message.java
Normal file
17
agent/src/main/java/com/clortox/agent/agent/dto/Message.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class Message {
|
||||||
|
private Role role;
|
||||||
|
private String content;
|
||||||
|
}
|
17
agent/src/main/java/com/clortox/agent/agent/dto/Role.java
Normal file
17
agent/src/main/java/com/clortox/agent/agent/dto/Role.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum Role {
|
||||||
|
SYSTEM("system"),
|
||||||
|
USER("user"),
|
||||||
|
ASSISTANT("assistant"),
|
||||||
|
TOOL("tool");
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
private final String value;
|
||||||
|
}
|
21
agent/src/main/java/com/clortox/agent/agent/dto/Usage.java
Normal file
21
agent/src/main/java/com/clortox/agent/agent/dto/Usage.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package com.clortox.agent.agent.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public class Usage {
|
||||||
|
@JsonProperty("prompt_tokens")
|
||||||
|
private int promptTokens;
|
||||||
|
|
||||||
|
@JsonProperty("completion_tokens")
|
||||||
|
private int completionTokens;
|
||||||
|
|
||||||
|
@JsonProperty("total_tokens")
|
||||||
|
private int totalTokens;
|
||||||
|
}
|
@ -1,15 +1,36 @@
|
|||||||
package com.clortox.agent.agent.memory;
|
package com.clortox.agent.agent.memory;
|
||||||
|
|
||||||
|
import com.clortox.agent.agent.memory.persistence.Message;
|
||||||
|
import com.clortox.agent.agent.memory.persistence.MessageRepository;
|
||||||
import dev.langchain4j.data.message.ChatMessage;
|
import dev.langchain4j.data.message.ChatMessage;
|
||||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
public class PersistentChatMemoryStore implements ChatMemoryStore {
|
public class PersistentChatMemoryStore implements ChatMemoryStore {
|
||||||
|
|
||||||
|
private MessageRepository repository;
|
||||||
|
|
||||||
|
public PersistentChatMemoryStore(MessageRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ChatMessage> getMessages(Object memoryId) {
|
public List<ChatMessage> getMessages(Object memoryId) {
|
||||||
return List.of();
|
Message exampleMessage = new Message();
|
||||||
|
exampleMessage.setConversationId(memoryId.toString());
|
||||||
|
|
||||||
|
Collection<Message> messages = repository.findAll(Example.of(exampleMessage));
|
||||||
|
|
||||||
|
for (Message message : messages) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,9 +20,18 @@ public class Message {
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Column(name = "conversationId", nullable = false, updatable = false)
|
@Column(name = "conversationId", nullable = false, updatable = false)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String conversationId;
|
private String conversationId;
|
||||||
|
|
||||||
|
@Column(name = "messsageType", nullable = false, updatable = false)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String messageType;
|
||||||
|
|
||||||
@Column(name = "content", nullable = false, updatable = false)
|
@Column(name = "content", nullable = false, updatable = false)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.clortox.agent.agent.memory.persistence;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface MessageRepository extends JpaRepository<Message, Long> {
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.clortox.agent.agent.state;
|
||||||
|
|
||||||
|
import com.clortox.agent.tools.ISmartAgentTool;
|
||||||
|
import dev.langchain4j.data.message.SystemMessage;
|
||||||
|
import dev.langchain4j.data.message.UserMessage;
|
||||||
|
import dev.langchain4j.model.chat.ChatModel;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bsc.langgraph4j.CompiledGraph;
|
||||||
|
import org.bsc.langgraph4j.GraphStateException;
|
||||||
|
import org.bsc.langgraph4j.agentexecutor.AgentExecutor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class SmartAgent {
|
||||||
|
|
||||||
|
private static final String SYSTEM_PROMPT = """
|
||||||
|
# Instructions
|
||||||
|
|
||||||
|
You have access to tools. When you think you should use one
|
||||||
|
use it and send back the result of the tool call, if applicable.
|
||||||
|
|
||||||
|
# Personality
|
||||||
|
|
||||||
|
You are a very smart cute anime girl. You talk like an anime girl.
|
||||||
|
""";
|
||||||
|
|
||||||
|
private final CompiledGraph<AgentExecutor.State> agent;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public SmartAgent(ChatModel model, List<ISmartAgentTool> tools) throws GraphStateException {
|
||||||
|
|
||||||
|
log.info("Building SmartAgent");
|
||||||
|
var agentBuilder = AgentExecutor.builder()
|
||||||
|
.systemMessage(SystemMessage.from(SYSTEM_PROMPT))
|
||||||
|
.chatModel(model);
|
||||||
|
|
||||||
|
for(ISmartAgentTool tool : tools) {
|
||||||
|
log.info("Adding tool {}", tool.getClass().getSimpleName());
|
||||||
|
agentBuilder.toolsFromObject(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
agent = agentBuilder.build().compile();
|
||||||
|
log.info("SmartAgent built");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the agent with a seed message
|
||||||
|
* @param message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String invoke(String message) {
|
||||||
|
|
||||||
|
Optional<AgentExecutor.State> finalState = agent.invoke(Map.of(
|
||||||
|
"messages", UserMessage.from(message)
|
||||||
|
));
|
||||||
|
|
||||||
|
AgentExecutor.State state = finalState.orElseThrow();
|
||||||
|
return state.finalResponse().orElse("Agent failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.clortox.agent.common.config;
|
||||||
|
|
||||||
|
import com.clortox.agent.common.dto.ErrorResponseDTO;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
|
||||||
|
@ControllerAdvice
|
||||||
|
public class GenericControllerAdvice {
|
||||||
|
|
||||||
|
|
||||||
|
@ExceptionHandler(Throwable.class)
|
||||||
|
public ResponseEntity<ErrorResponseDTO> generalError(Throwable t, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
|
return ResponseEntity.status(status)
|
||||||
|
.body(ErrorResponseDTO.builder()
|
||||||
|
.path(request.getRequestURI())
|
||||||
|
.status(status.value())
|
||||||
|
.message(t.getMessage())
|
||||||
|
.error(status.getReasonPhrase())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.clortox.agent.common.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class ErrorResponseDTO {
|
||||||
|
|
||||||
|
Instant timestamp = Instant.now();
|
||||||
|
Integer status;
|
||||||
|
String error;
|
||||||
|
String message;
|
||||||
|
String path;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.clortox.agent.tools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to categorize all tools for use with the smart agent
|
||||||
|
*/
|
||||||
|
public interface ISmartAgentTool {
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.clortox.agent.tools.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
public class TenorResult {
|
||||||
|
|
||||||
|
public String url;
|
||||||
|
|
||||||
|
public Collection<String> tags;
|
||||||
|
|
||||||
|
public String description;
|
||||||
|
|
||||||
|
public String title;
|
||||||
|
}
|
@ -0,0 +1,202 @@
|
|||||||
|
package com.clortox.agent.tools.tenor;
|
||||||
|
|
||||||
|
import com.clortox.agent.tools.ISmartAgentTool;
|
||||||
|
import com.clortox.agent.tools.dto.TenorResult;
|
||||||
|
import dev.langchain4j.agent.tool.P;
|
||||||
|
import dev.langchain4j.agent.tool.Tool;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.client.RestClientException;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class TenorSearch implements ISmartAgentTool {
|
||||||
|
|
||||||
|
@Value("${agent.tools.tenor.api_key}")
|
||||||
|
private String tenorApiKey;
|
||||||
|
|
||||||
|
private static final String TENOR_SEARCH = "https://tenor.googleapis.com/v2/search";
|
||||||
|
private static final String TENOR_RELATED = "https://tenor.googleapis.com/v2/related";
|
||||||
|
|
||||||
|
// constant params required by your ask:
|
||||||
|
private static final String MEDIA_FILTER = "minimal";
|
||||||
|
private static final String CONTENTFILTER = "off";
|
||||||
|
|
||||||
|
private final RestTemplate http = new RestTemplate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search tenor for a gif
|
||||||
|
* @param query
|
||||||
|
* @param results
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Tool("Search Tenor for GIFs with a text query")
|
||||||
|
public Collection<TenorResult> search(
|
||||||
|
@P("Search query") String query,
|
||||||
|
@P(value = "Number of results to return", required = false) Integer results) {
|
||||||
|
if(results == null) results = 5;
|
||||||
|
|
||||||
|
int limit = normalizeLimit(results);
|
||||||
|
if (!StringUtils.hasText(tenorApiKey) || !StringUtils.hasText(query)) return List.of();
|
||||||
|
|
||||||
|
String url = UriComponentsBuilder.fromHttpUrl(TENOR_SEARCH)
|
||||||
|
.queryParam("key", tenorApiKey)
|
||||||
|
.queryParam("q", query)
|
||||||
|
.queryParam("limit", limit)
|
||||||
|
.queryParam("media_filter", MEDIA_FILTER)
|
||||||
|
.queryParam("contentfilter", CONTENTFILTER)
|
||||||
|
.build(true)
|
||||||
|
.toUriString();
|
||||||
|
|
||||||
|
return executeAndMap(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serach tenor for gifs similar or related to a given tenor
|
||||||
|
* @param urlOrId
|
||||||
|
* @param results
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Tool("Find GIFs related to a given Tenor GIF URL or ID")
|
||||||
|
public Collection<TenorResult> related(
|
||||||
|
@P("A Tenor GIF URL or Tenor GIF ID") String urlOrId,
|
||||||
|
@P(value = "Number of results to return", required = false) Integer results) {
|
||||||
|
|
||||||
|
if(results == null) results = 5;
|
||||||
|
|
||||||
|
int limit = normalizeLimit(results);
|
||||||
|
if (!StringUtils.hasText(tenorApiKey) || !StringUtils.hasText(urlOrId)) return List.of();
|
||||||
|
|
||||||
|
String id = extractTenorId(urlOrId);
|
||||||
|
UriComponentsBuilder ucb;
|
||||||
|
if (id != null) {
|
||||||
|
ucb = UriComponentsBuilder.fromHttpUrl(TENOR_RELATED)
|
||||||
|
.queryParam("key", tenorApiKey)
|
||||||
|
.queryParam("id", id)
|
||||||
|
.queryParam("limit", limit)
|
||||||
|
.queryParam("media_filter", MEDIA_FILTER)
|
||||||
|
.queryParam("contentfilter", CONTENTFILTER);
|
||||||
|
} else {
|
||||||
|
// If we can't parse an ID, fall back to a normal search using the string (best-effort)
|
||||||
|
ucb = UriComponentsBuilder.fromHttpUrl(TENOR_SEARCH)
|
||||||
|
.queryParam("key", tenorApiKey)
|
||||||
|
.queryParam("q", urlOrId)
|
||||||
|
.queryParam("limit", limit)
|
||||||
|
.queryParam("media_filter", MEDIA_FILTER)
|
||||||
|
.queryParam("contentfilter", CONTENTFILTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAndMap(ucb.build(true).toUriString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------
|
||||||
|
// Helpers
|
||||||
|
// ------------------------
|
||||||
|
|
||||||
|
private int normalizeLimit(Integer results) {
|
||||||
|
return (results == null || results <= 0) ? 10 : Math.min(results, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<TenorResult> executeAndMap(String url) {
|
||||||
|
try {
|
||||||
|
TenorApiResponse resp = http.getForObject(url, TenorApiResponse.class);
|
||||||
|
if (resp == null || resp.results == null) return List.of();
|
||||||
|
|
||||||
|
return resp.results.stream()
|
||||||
|
.map(TenorSearch::mapToDto)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} catch (RestClientException e) {
|
||||||
|
log.error("Tenor API request failed: {}", e.getMessage(), e);
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TenorResult mapToDto(TenorApiResult r) {
|
||||||
|
if (r == null) return null;
|
||||||
|
|
||||||
|
String shareUrl = firstNonBlank(r.itemurl, r.url);
|
||||||
|
String title = firstNonBlank(r.title, r.content_description);
|
||||||
|
String desc = firstNonBlank(r.content_description, r.title);
|
||||||
|
Collection<String> tags = (r.tags == null) ? List.of() : r.tags;
|
||||||
|
|
||||||
|
return TenorResult.builder()
|
||||||
|
.url(shareUrl)
|
||||||
|
.title(title)
|
||||||
|
.description(desc)
|
||||||
|
.tags(tags)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String firstNonBlank(String... vals) {
|
||||||
|
for (String v : vals) if (v != null && !v.isBlank()) return v;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractTenorId(String input) {
|
||||||
|
// Direct ID
|
||||||
|
if (input.matches("\\d+")) return input;
|
||||||
|
|
||||||
|
// Try URL forms
|
||||||
|
try {
|
||||||
|
URI uri = new URI(input);
|
||||||
|
String host = Optional.ofNullable(uri.getHost()).orElse("");
|
||||||
|
if (!host.contains("tenor.com")) return null;
|
||||||
|
|
||||||
|
// itemid query param
|
||||||
|
String query = uri.getQuery();
|
||||||
|
if (query != null) {
|
||||||
|
for (String part : query.split("&")) {
|
||||||
|
int idx = part.indexOf('=');
|
||||||
|
if (idx > 0) {
|
||||||
|
String key = part.substring(0, idx);
|
||||||
|
String val = part.substring(idx + 1);
|
||||||
|
if ("itemid".equalsIgnoreCase(key) && val.matches("\\d+")) return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trailing -digits in last path segment
|
||||||
|
String path = Optional.ofNullable(uri.getPath()).orElse("");
|
||||||
|
String lastSeg = path.substring(path.lastIndexOf('/') + 1);
|
||||||
|
Matcher m = TRAILING_ID.matcher(lastSeg);
|
||||||
|
if (m.find()) return m.group(1);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (URISyntaxException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern TRAILING_ID = Pattern.compile(".*-(\\d+)$");
|
||||||
|
|
||||||
|
/* ===== Minimal Tenor API models ===== */
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class TenorApiResponse {
|
||||||
|
List<TenorApiResult> results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class TenorApiResult {
|
||||||
|
String id;
|
||||||
|
String itemurl; // share page
|
||||||
|
String url; // sometimes present
|
||||||
|
String title;
|
||||||
|
String content_description;
|
||||||
|
List<String> tags;
|
||||||
|
// media_formats omitted; you asked for media_filter=minimal + DTO doesn't need direct media links
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,12 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
url: jdbc:sqlite:${SQLITE_DB_LOCATION:./data/agent.db}
|
url: jdbc:sqlite:${SQLITE_DB_LOCATION:./data/agent.db}
|
||||||
driver-class-name: org.sqlite.JDBC
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:db/migration
|
||||||
|
create-schemas: false
|
||||||
|
baseline-on-migrate: true
|
||||||
|
baseline-version: 1.0.0.0
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
@ -13,6 +19,7 @@ spring:
|
|||||||
|
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
|
com.clortox.agent: debug
|
||||||
org.hibernate.SQL: warn
|
org.hibernate.SQL: warn
|
||||||
org.hibernate.type.descriptor.sql.BasicBinder: warn
|
org.hibernate.type.descriptor.sql.BasicBinder: warn
|
||||||
|
|
||||||
@ -22,10 +29,15 @@ springdoc:
|
|||||||
|
|
||||||
|
|
||||||
agent:
|
agent:
|
||||||
|
prompt:
|
||||||
|
smartAgent: "${SMART_AGENT_PROMPT_PATH:./data/smartAgent.md}"
|
||||||
ollama:
|
ollama:
|
||||||
baseUrl: "${OLLAMA_BASEURL:http://10.0.3.2:8080}"
|
baseUrl: "${OLLAMA_BASEURL:http://10.0.3.2:8080}"
|
||||||
model: "${OLLAMA_MODEL:llama3.2}"
|
model: "${OLLAMA_MODEL:mistral-small3.2}"
|
||||||
temp: "${OLLAMA_TEMP:0.2}"
|
temp: "${OLLAMA_TEMP:0.2}"
|
||||||
|
tools:
|
||||||
|
tenor:
|
||||||
|
api_key: "${TOOLS_TENOR_API_KEY:}"
|
||||||
|
|
||||||
#langchain4j:
|
#langchain4j:
|
||||||
# ollama:
|
# ollama:
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
CREATE TABLE message (
|
|
||||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
conversationId VARCHAR(32) NOT NULL,
|
|
||||||
content VARCHAR(4096) NOT NULL
|
|
||||||
)
|
|
19
agent/src/main/resources/db/migration/V1.0.0.1__init.sql
Normal file
19
agent/src/main/resources/db/migration/V1.0.0.1__init.sql
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS message (
|
||||||
|
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
conversationId VARCHAR(32) NOT NULL,
|
||||||
|
messageType VARCHAR(64) NOT NULL CHECK ( messageType IN ( 'SYSTEM', 'AI', 'USER', 'TOOL_RESULT', 'CUSTOM' ) ),
|
||||||
|
content VARCHAR(4096) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS EVENT_PUBLICATION (
|
||||||
|
ID TEXT NOT NULL,
|
||||||
|
LISTENER_ID TEXT NOT NULL,
|
||||||
|
EVENT_TYPE TEXT NOT NULL,
|
||||||
|
SERIALIZED_EVENT TEXT NOT NULL,
|
||||||
|
PUBLICATION_DATE DATETIME NOT NULL,
|
||||||
|
COMPLETION_DATE DATETIME DEFAULT NULL,
|
||||||
|
PRIMARY KEY (ID)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS EVENT_PUBLICATION_BY_COMPLETION_DATE_IDX
|
||||||
|
ON EVENT_PUBLICATION (COMPLETION_DATE);
|
@ -2,7 +2,11 @@ package com.clortox.agent;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
@ActiveProfiles("test")
|
||||||
|
@Import(FlywayTestConfiguration.class)
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class AgentApplicationTests {
|
class AgentApplicationTests {
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.clortox.agent;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
|
||||||
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
@TestConfiguration
|
||||||
|
public class FlywayTestConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
FlywayMigrationStrategy cleanMigrate() {
|
||||||
|
return flyway -> {
|
||||||
|
flyway.clean();
|
||||||
|
flyway.migrate();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
28
agent/src/test/resources/application-test.yml
Normal file
28
agent/src/test/resources/application-test.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:sqlite:./data/agent-test.db
|
||||||
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
hikari:
|
||||||
|
maximum-pool-size: 1 # SQLite + tests = keep it simple
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:db/migration
|
||||||
|
clean-disabled: false # allow clean() in tests if you want
|
||||||
|
baseline-on-migrate: true
|
||||||
|
baseline-version: 1.0.0.0
|
||||||
|
|
||||||
|
spring.jpa:
|
||||||
|
hibernate.ddl-auto: none
|
||||||
|
database-platform: org.hibernate.dialect.SQLiteDialect
|
||||||
|
|
||||||
|
agent:
|
||||||
|
prompt:
|
||||||
|
smartAgent: "${SMART_AGENT_PROMPT_PATH:./data/smartAgent.md}"
|
||||||
|
ollama:
|
||||||
|
baseUrl: "${OLLAMA_BASEURL:http://10.0.3.2:8080}"
|
||||||
|
model: "${OLLAMA_MODEL:mistral-small3.2}"
|
||||||
|
temp: "${OLLAMA_TEMP:0.2}"
|
||||||
|
tools:
|
||||||
|
tenor:
|
||||||
|
api_key: "test-key"
|
||||||
|
|
BIN
data/agent.db
BIN
data/agent.db
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user