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

29
pom.xml
View File

@ -1,13 +1,17 @@
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath/>
</parent>
<groupId>com.pablotj</groupId>
<artifactId>ia-chat-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
@ -19,15 +23,26 @@
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Test support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@ -1,21 +1,35 @@
package com.pablotj.ia.chat.boot;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/chat")
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/")
public class ChatController {
private final List<ChatMessage> messages = new ArrayList<>();
private final LlamaService llamaService;
public ChatController(LlamaService llamaService) {
this.llamaService = llamaService;
}
@GetMapping
public String chat(@RequestParam String prompt) {
return llamaService.chat(prompt);
@GetMapping("/chat")
public String showChat(Model model) {
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() {
try {
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);
} catch (Exception e) {
throw new RuntimeException("Error cargando el modelo", e);
@ -33,9 +40,11 @@ public class LlamaService implements AutoCloseable {
String finalPrompt = chat.build();
InferenceParameters inf = new InferenceParameters(finalPrompt)
.setNPredict(200)
.setTemperature(0.7f)
.setTopP(0.9f)
.setTopK(40);
.setTopK(40)
.setUseChatTemplate(true);
StringBuilder sb = new StringBuilder();

View File

@ -13,21 +13,23 @@ public class PromptBuilder {
}
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) {
turns.add("GPT4 Correct Assistant: " + message + "<|end_of_turn|>");
turns.add("<|im_start|>assistant\n" + message + "\n<|im_end|>");
}
public String build() {
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) {
sb.append(turn).append("\n");
}
// Deja el último turno preparado para que el modelo continúe como "Assistant"
sb.append("GPT4 Correct Assistant: ");
// Deja listo para que el modelo continúe como assistant generando respuesta:
sb.append("<|im_start|>assistant\n");
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>