Add ai-services and spring-boot-ai-service modules: prompt templates, per-id chat memory, tools, RAG, the @MemoryId trap, the message-window probe, and @AiService on Spring Boot 4.1.1, each with a captured transcript
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01G8ikz8xdWuTP5yun8DZ1hk
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
Spring Boot version: 4.1.1
|
||||
Bean of the interface type exists: [bootApp.Greeter]
|
||||
Messages sent to the model:
|
||||
SYSTEM: You greet people briefly.
|
||||
USER: Greet Ankur
|
||||
Reply: Reply built from 2 message(s).
|
||||
@@ -0,0 +1,52 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.ankurm.langchain4j</groupId>
|
||||
<artifactId>langchain4j-demo</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-boot-ai-service</artifactId>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>4.1.1</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ankurm.langchain4j</groupId>
|
||||
<artifactId>ai-services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.langchain4j</groupId>
|
||||
<artifactId>langchain4j-spring-boot-starter</artifactId>
|
||||
<version>${langchain4j.beta.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ankurm.lc4j.boot;
|
||||
|
||||
import com.ankurm.lc4j.aiservices.ScriptedChatModel;
|
||||
import dev.langchain4j.data.message.AiMessage;
|
||||
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
|
||||
import dev.langchain4j.model.chat.ChatModel;
|
||||
import dev.langchain4j.service.spring.AiService;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BootApp {
|
||||
|
||||
/** Stand-in for an OpenAI or Ollama model bean. @AiService finds the single ChatModel in the context. */
|
||||
@Bean
|
||||
ScriptedChatModel chatModel() {
|
||||
return new ScriptedChatModel(r -> AiMessage.from("Reply built from " + r.messages().size() + " message(s)."));
|
||||
}
|
||||
|
||||
@Bean
|
||||
dev.langchain4j.memory.ChatMemory chatMemory() {
|
||||
return MessageWindowChatMemory.withMaxMessages(10);
|
||||
}
|
||||
|
||||
/** The whole declaration. No @Bean method, no builder: the scanner creates and registers it. */
|
||||
@AiService
|
||||
public interface Greeter {
|
||||
|
||||
@SystemMessage("You greet people briefly.")
|
||||
@UserMessage("Greet {{it}}")
|
||||
String greet(String name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.ankurm.lc4j.boot;
|
||||
|
||||
import com.ankurm.lc4j.aiservices.ScriptedChatModel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
class BootAiServiceTest {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
BootApp.Greeter greeter;
|
||||
|
||||
@Autowired
|
||||
ScriptedChatModel model;
|
||||
|
||||
@Test
|
||||
void aiServiceInterfaceIsRegisteredAsABeanAndWorks() throws IOException {
|
||||
String reply = greeter.greet("Ankur");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Spring Boot version: ").append(org.springframework.boot.SpringBootVersion.getVersion()).append('\n');
|
||||
sb.append("Bean of the interface type exists: ").append(context.getBeansOfType(BootApp.Greeter.class).keySet()).append('\n');
|
||||
sb.append("Messages sent to the model:\n").append(ScriptedChatModel.render(model.lastRequest().messages()));
|
||||
sb.append("Reply: ").append(reply).append('\n');
|
||||
Files.createDirectories(Path.of("output"));
|
||||
Files.writeString(Path.of("output/01-boot-context.txt"), sb.toString());
|
||||
|
||||
assertThat(reply).startsWith("Reply built from");
|
||||
assertThat(context.getBeansOfType(BootApp.Greeter.class)).hasSize(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user