Add getting-started module: ChatClient fluent API, system/user prompts, streaming, and provider switching via spring.ai.model.chat
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
package com.ankurm.gettingstarted;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class GettingStartedApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GettingStartedApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.ankurm.gettingstarted.config;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* The one bean this module exists to explain. {@link ChatClient.Builder} arrives
|
||||
* autoconfigured and already pointed at whichever {@code ChatModel} bean
|
||||
* {@code spring.ai.model.chat} selected — this class never mentions OpenAI or Ollama
|
||||
* by name, and that is the whole point of the properties-based switch.
|
||||
*/
|
||||
@Configuration
|
||||
public class ChatClientConfig {
|
||||
|
||||
@Bean
|
||||
ChatClient chatClient(ChatClient.Builder builder) {
|
||||
return builder
|
||||
.defaultSystem("""
|
||||
You are a terse Java and Spring assistant. Answer in at most two \
|
||||
sentences unless the user asks for code.""")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.ankurm.gettingstarted.web;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* Three endpoints, three corners of the {@link ChatClient} fluent API:
|
||||
* a plain call, a call with a templated system prompt, and a streamed call.
|
||||
* See {@code docs} on ankurm.com for the walkthrough — the endpoints
|
||||
* themselves carry no comment beyond what the method name says, deliberately,
|
||||
* since the interesting part is the one-line body.
|
||||
*/
|
||||
@RestController
|
||||
public class ChatController {
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public ChatController(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@GetMapping("/api/chat")
|
||||
public String chat(@RequestParam String message) {
|
||||
return this.chatClient.prompt()
|
||||
.user(message)
|
||||
.call()
|
||||
.content();
|
||||
}
|
||||
|
||||
@GetMapping("/api/chat/as")
|
||||
public String chatAs(@RequestParam String voice, @RequestParam String message) {
|
||||
return this.chatClient.prompt()
|
||||
.system(s -> s.text("Answer in the voice of a {voice}, still in two sentences.")
|
||||
.param("voice", voice))
|
||||
.user(message)
|
||||
.call()
|
||||
.content();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/api/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<String> chatStream(@RequestParam String message) {
|
||||
return this.chatClient.prompt()
|
||||
.user(message)
|
||||
.stream()
|
||||
.content();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
spring:
|
||||
application:
|
||||
name: getting-started
|
||||
ai:
|
||||
# Which provider's ChatModel bean gets built. Flip this one line, keep the code
|
||||
# in ChatClientConfig and ChatController unchanged, and add the matching provider
|
||||
# properties below.
|
||||
model:
|
||||
chat: ${CHAT_PROVIDER:openai}
|
||||
openai:
|
||||
api-key: ${OPENAI_API_KEY:}
|
||||
chat:
|
||||
model: gpt-4o
|
||||
temperature: 0.1
|
||||
ollama:
|
||||
base-url: ${OLLAMA_BASE_URL:http://localhost:11434}
|
||||
chat:
|
||||
model: llama3.2
|
||||
Reference in New Issue
Block a user