Add simple user interface

This commit is contained in:
Pablo de la Torre 2025-06-28 10:45:42 +02:00
parent 2aad09df7d
commit f84714a726
7 changed files with 147 additions and 60 deletions

View File

@ -1 +1 @@
[1751004438] warming up the model with an empty run [1751008457] warming up the model with an empty run

97
pom.xml
View File

@ -1,46 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<modelVersion>4.0.0</modelVersion> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
<parent> https://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>org.springframework.boot</groupId> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.pablotj</groupId>
<artifactId>ia-chat-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ia-chat-boot</name>
<description>Project IA chat boot</description>
<properties> <parent>
<java.version>17</java.version> <groupId>org.springframework.boot</groupId>
</properties> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/>
</parent>
<dependencies> <groupId>com.pablotj</groupId>
<dependency> <artifactId>ia-chat-boot</artifactId>
<groupId>de.kherud</groupId> <version>0.0.1-SNAPSHOT</version>
<artifactId>llama</artifactId> <name>ia-chat-boot</name>
<version>3.4.1</version> <description>Project IA chat boot</description>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build> <properties>
<plugins> <java.version>17</java.version>
<plugin> </properties>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <dependencies>
</plugin> <!-- Spring Boot Web -->
</plugins> <dependency>
</build> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf template engine -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- llama-java (IA offline) -->
<dependency>
<groupId>de.kherud</groupId>
<artifactId>llama</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Test support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -1,21 +1,35 @@
package com.pablotj.ia.chat.boot; package com.pablotj.ia.chat.boot;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;
@RestController import java.util.ArrayList;
@RequestMapping("/chat") import java.util.List;
@Controller
@RequestMapping("/")
public class ChatController { public class ChatController {
private final List<ChatMessage> messages = new ArrayList<>();
private final LlamaService llamaService; private final LlamaService llamaService;
public ChatController(LlamaService llamaService) { public ChatController(LlamaService llamaService) {
this.llamaService = llamaService; this.llamaService = llamaService;
} }
@GetMapping @GetMapping("/chat")
public String chat(@RequestParam String prompt) { public String showChat(Model model) {
return llamaService.chat(prompt); model.addAttribute("messages", messages);
return "chat";
}
@PostMapping("/chat")
public String handleChat(@RequestParam("prompt") String prompt, Model model) {
messages.add(new ChatMessage("user", prompt));
String reply = llamaService.chat(prompt);
messages.add(new ChatMessage("bot", reply));
model.addAttribute("messages", messages);
return "chat";
} }
} }

View File

@ -0,0 +1,19 @@
package com.pablotj.ia.chat.boot;
public class ChatMessage {
private String role; // "user" o "bot"
private String text;
public ChatMessage(String role, String text) {
this.role = role;
this.text = text;
}
public String getRole() {
return role;
}
public String getText() {
return text;
}
}

View File

@ -15,7 +15,14 @@ public class LlamaService implements AutoCloseable {
public void init() { public void init() {
try { try {
ModelParameters params = new ModelParameters() ModelParameters params = new ModelParameters()
.setModelFilePath("models/openchat-3.5-0106.Q4_K_M.gguf"); .setModelFilePath("models/ggml-model-q3_k.gguf")
.setSeed(42)
.setNThreads(8) // usa 8 hilos CPU (ajusta según tu CPU)
.setNGpuLayers(0) // no usar GPU
.setMainGpu(-1) // deshabilitar GPU principal
.setNoKvOffload(true) // no descargar KV, evitar errores GPU
.setUseMmap(true) // mejorar gestión memoria
.setNPredict(128);
model = new LlamaModel(params); model = new LlamaModel(params);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error cargando el modelo", e); throw new RuntimeException("Error cargando el modelo", e);
@ -33,9 +40,11 @@ public class LlamaService implements AutoCloseable {
String finalPrompt = chat.build(); String finalPrompt = chat.build();
InferenceParameters inf = new InferenceParameters(finalPrompt) InferenceParameters inf = new InferenceParameters(finalPrompt)
.setTemperature(0.7f) .setNPredict(200)
.setTopP(0.9f) .setTemperature(0.7f)
.setTopK(40); .setTopP(0.9f)
.setTopK(40)
.setUseChatTemplate(true);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View File

@ -13,21 +13,23 @@ public class PromptBuilder {
} }
public void user(String message) { public void user(String message) {
turns.add("GPT4 Correct User: " + message + "<|end_of_turn|>"); turns.add("<|im_start|>user\n" + message + "\n<|im_end|>");
} }
public void assistant(String message) { public void assistant(String message) {
turns.add("GPT4 Correct Assistant: " + message + "<|end_of_turn|>"); turns.add("<|im_start|>assistant\n" + message + "\n<|im_end|>");
} }
public String build() { public String build() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(systemPrompt).append("<|end_of_turn|>\n"); sb.append("<|im_start|>system\n")
.append(systemPrompt)
.append("\n<|im_end|>\n");
for (String turn : turns) { for (String turn : turns) {
sb.append(turn).append("\n"); sb.append(turn).append("\n");
} }
// Deja el último turno preparado para que el modelo continúe como "Assistant" // Deja listo para que el modelo continúe como assistant generando respuesta:
sb.append("GPT4 Correct Assistant: "); sb.append("<|im_start|>assistant\n");
return sb.toString(); return sb.toString();
} }
} }

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="es">
<head>
<meta charset="UTF-8">
<title>Chat con IA Offline</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 2rem auto; }
.bubble { margin-bottom: 1rem; }
.user { color: #003366; font-weight: bold; }
.bot { color: #006600; }
textarea { width: 100%; height: 4em; margin-top: 1rem; }
button { padding: 0.5rem 1rem; margin-top: 0.5rem; }
</style>
</head>
<body>
<h1>Chat con IA Offline</h1>
<div th:each="msg : ${messages}" class="bubble">
<div th:if="${msg.role} == 'user'" class="user">🧑‍💻: <span th:text="${msg.text}"></span></div>
<div th:if="${msg.role} == 'bot'" class="bot">🤖: <span th:text="${msg.text}"></span></div>
</div>
<form method="post" th:action="@{/chat}">
<textarea name="prompt" placeholder="Escribe tu mensaje aquí..."></textarea><br>
<button type="submit">Enviar</button>
</form>
</body>
</html>