Skip to content

Commit c9ee1b2

Browse files
handling stream chat feature
1 parent 7131582 commit c9ee1b2

File tree

8 files changed

+234
-134
lines changed

8 files changed

+234
-134
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.example.treaders.LLM;
2+
3+
public class ChatStructure {
4+
private String model;
5+
private String prompt;
6+
private Boolean stream;
7+
8+
public String getModel() {
9+
return model;
10+
}
11+
12+
public void setModel(String model) {
13+
this.model = model;
14+
}
15+
16+
public String getPrompt() {
17+
return prompt;
18+
}
19+
20+
public void setPrompt(String prompt) {
21+
this.prompt = prompt;
22+
}
23+
24+
public Boolean getStream() {
25+
return stream;
26+
}
27+
28+
public void setStream(Boolean stream) {
29+
this.stream = stream;
30+
}
31+
}

src/main/java/com/example/treaders/LLM/LlamaService.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,51 @@
22

33
import org.springframework.ai.ollama.OllamaChatClient;
44
import org.springframework.stereotype.Service;
5-
5+
import org.springframework.web.reactive.function.BodyInserters;
6+
import org.springframework.web.reactive.function.client.WebClient;
7+
import reactor.core.publisher.Flux;
68

79

810
@Service
911
public class LlamaService {
12+
private final WebClient webClient;
13+
14+
public LlamaService(WebClient.Builder webClientBuilder) {
15+
this.webClient = webClientBuilder.baseUrl("http://localhost:11434").build();
16+
}
17+
18+
public Flux<String> getResponse(String prompt) {
19+
// Directly embedding the prompt in the JSON request body
20+
// String requestBody = "{\"model\": \"llama3\", \"prompt\": \"Introduce yourself in 10 words\", \"stream\": true}";
21+
ChatStructure requestBody = new ChatStructure();
22+
requestBody.setModel("llama3");
23+
requestBody.setPrompt(prompt);
24+
requestBody.setStream(true);
1025

11-
private OllamaChatClient chatClient;
26+
Flux<String> responseFlux = webClient.post()
27+
.uri("/api/generate")
28+
.header("Content-Type", "application/json")
29+
.body(BodyInserters.fromValue(requestBody))
30+
.retrieve()
31+
.bodyToFlux(String.class)
32+
.map(this::extractContent)
33+
.filter(content -> content != null && !content.isEmpty())
34+
.map(content -> content.replaceAll("\\\\n", "\n"));
1235

13-
public LlamaService(OllamaChatClient chatClient) {
14-
this.chatClient = chatClient;
36+
return responseFlux;
1537
}
1638

17-
public String getResponse(String input) {
18-
String response = chatClient.call(input);
19-
return response; // Simulated response
39+
private String extractContent(String response) {
40+
int startIndex = response.indexOf("\"response\":");
41+
if (startIndex != -1) { // Check if "content" is found
42+
startIndex = response.indexOf("\"", startIndex + "\"response\":".length()); // Find the opening quote after "content"
43+
if (startIndex != -1) {
44+
int endIndex = response.indexOf("\"", startIndex + 1); // Find the closing quote after content value
45+
if (endIndex != -1) {
46+
return response.substring(startIndex + 1, endIndex); // Add 1 to startIndex to remove the opening quote
47+
}
48+
}
49+
}
50+
return null; // Return null if content is not found or if the indexes are invalid
2051
}
2152
}

src/main/java/com/example/treaders/controller/AllController.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,10 @@ public String logout(){
122122
}
123123

124124
@GetMapping("/chat")
125-
public String index(Model model) {
125+
public String showChatPage() {
126126
if(!UserLoggedIn){
127127
return "redirect:/";
128128
}
129-
model.addAttribute("inputForm", new InputForm());
130-
return "ChatPage";
131-
}
132-
133-
@PostMapping("/chat")
134-
public String processString(InputForm inputForm,Model model) {
135-
String userInput = inputForm.getInputString();
136-
String llamaResponse = llamaService.getResponse(userInput); // Get the response from Llama
137-
inputForm.setResponseString(llamaResponse); // Set the response in the inputForm
138-
model.addAttribute("inputForm", inputForm); // Update the model with the inputForm containing the response
139129
return "ChatPage";
140130
}
141131

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example.treaders.controller;
2+
3+
import com.example.treaders.LLM.LlamaService;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.http.MediaType;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.RequestParam;
8+
import org.springframework.web.bind.annotation.RestController;
9+
import reactor.core.publisher.Flux;
10+
11+
@RestController
12+
public class AllRestControllers {
13+
14+
@Autowired
15+
private LlamaService llamaService;
16+
17+
@GetMapping(value = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
18+
public Flux<String> askQuestion(@RequestParam("question") String question) {
19+
return llamaService.getResponse(question);
20+
}
21+
}

src/main/resources/static/css/ChatPage.css

Lines changed: 75 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,108 @@
1+
/* General body styling */
12
body {
23
font-family: Arial, sans-serif;
3-
background-color: #000; /* Black background */
44
margin: 0;
55
padding: 0;
6-
color: #fff; /* White text */
6+
background-color: #222; /* Set page background color to medium black */
7+
color: white; /* Set default text color to white */
78
}
89

9-
.container {
10-
position: relative; /* Positioning context for absolute positioning */
11-
max-width: 600px;
12-
margin: 50px auto;
13-
padding: 20px;
14-
background-color: #1a1a1a; /* Slightly lighter black for container */
15-
border-radius: 8px;
16-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
17-
text-align: center;
18-
}
19-
20-
h1 {
21-
color: #fff; /* White color for the heading */
22-
margin-bottom: 20px;
23-
}
24-
25-
.input-container {
10+
/* Chat container and box styling */
11+
#chat-container {
2612
display: flex;
2713
justify-content: center;
28-
margin-bottom: 20px;
14+
align-items: center;
15+
height: 100vh; /* Full height of the viewport */
2916
}
3017

31-
input[type="text"] {
32-
width: 70%;
18+
#chat-box {
19+
background-color: black; /* Chat box background color */
20+
color: white; /* Text color inside the chat box */
21+
padding: 20px;
22+
border-radius: 10px;
23+
max-width: 600px;
24+
width: 100%;
25+
}
26+
27+
/* Response container styling */
28+
#response-container {
29+
height: 240px; /* Height of the response container */
30+
overflow-y: auto; /* Enable scrolling */
31+
border: 1px solid #ccc;
3332
padding: 10px;
34-
font-size: 16px;
35-
border: none;
36-
border-radius: 5px;
37-
background-color: #2b2b2b; /* Darker background for the input */
38-
color: #fff; /* White text color */
39-
margin-right: 10px;
33+
margin-bottom: 10px; /* Space between response container and button */
4034
}
4135

42-
button {
36+
/* Stop button styling */
37+
#stop-button {
38+
background: linear-gradient(45deg, #ff6b6b, #f06595);
39+
border: none;
40+
border-radius: 25px;
41+
color: white;
4342
padding: 10px 20px;
4443
font-size: 16px;
45-
border: none;
46-
background-color: #444; /* Darker shade for the button */
47-
color: #fff; /* White text */
48-
border-radius: 5px;
4944
cursor: pointer;
45+
transition: background 0.3s ease, transform 0.2s ease;
5046
}
5147

52-
button:hover {
53-
background-color: #555; /* Slightly lighter on hover */
48+
#stop-button:hover {
49+
background: linear-gradient(45deg, #f06595, #ff6b6b);
50+
transform: scale(1.05);
5451
}
5552

56-
.output-container {
57-
max-height: 300px;
58-
overflow-y: auto;
59-
padding: 15px;
60-
border: 1px solid #333; /* Dark border */
61-
border-radius: 5px;
62-
background-color: #1f1f1f; /* Dark background for the output container */
63-
text-align: left;
64-
margin-top: 20px;
53+
#stop-button:focus {
54+
outline: none;
55+
box-shadow: 0 0 10px rgba(255, 107, 107, 0.6);
56+
}
57+
58+
/* Form styling */
59+
#question-form {
60+
display: flex;
61+
gap: 10px; /* Space between input and button */
62+
margin-bottom: 15px; /* Space below the form */
6563
}
6664

67-
.output-text {
68-
white-space: pre-wrap; /* Ensures the text preserves whitespace and line breaks */
69-
background-color: #2b2b2b; /* Slightly lighter background for the chat bubble */
70-
border: 1px solid #333; /* Dark border for the chat bubble */
65+
#question-input {
66+
flex: 1; /* Take up available space */
7167
padding: 10px;
68+
border: 1px solid #ccc;
7269
border-radius: 5px;
73-
color: #fff; /* White text color */
70+
background-color: #333; /* Darker background for the input */
71+
color: white; /* Input text color */
7472
}
7573

76-
.loading-screen {
77-
display: none; /* Initially hidden */
78-
position: fixed;
79-
top: 0;
80-
left: 0;
81-
width: 100%;
82-
height: 100%;
83-
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */
84-
z-index: 9999; /* Ensure it appears above other content */
85-
color: #fff; /* White text */
86-
font-size: 24px;
87-
text-align: center;
88-
padding-top: 50vh; /* Center vertically */
74+
#question-input::placeholder {
75+
color: #aaa; /* Placeholder text color */
76+
}
77+
78+
#question-form button {
79+
background: linear-gradient(45deg, #4facfe, #00f2fe);
80+
/* Change the background color to a darker shade */
81+
background: linear-gradient(45deg, #00688b, #004356);
82+
border: none;
83+
border-radius: 5px;
84+
color: white;
85+
padding: 10px 20px;
86+
font-size: 16px;
87+
cursor: pointer;
88+
transition: background 0.3s ease, transform 0.2s ease;
89+
}
90+
91+
#question-form button:hover {
92+
/* Change the hover background color to a darker shade */
93+
background: linear-gradient(45deg, #004356, #00688b);
94+
transform: scale(1.05);
8995
}
9096

97+
#question-form button:focus {
98+
outline: none;
99+
box-shadow: 0 0 10px rgba(0, 242, 254, 0.6);
100+
}
101+
/* Home button styling */
91102
.home-button {
92103
position: absolute;
93-
top: 20px; /* Adjust as needed */
94-
right: 20px; /* Adjust as needed */
104+
top: 150px; /* Adjust as needed */
105+
right: 470px; /* Adjust as needed */
95106
padding: 10px 20px;
96107
font-size: 16px;
97108
background-color: #444;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
document.addEventListener('DOMContentLoaded', () => {
2+
const form = document.getElementById('question-form');
3+
const responseContainer = document.getElementById('response-container');
4+
const stopButton = document.getElementById('stop-button');
5+
6+
let eventSource;
7+
let completeResponse = '';
8+
9+
form.addEventListener('submit', function(event) {
10+
event.preventDefault(); // Prevent the default form submission
11+
12+
// Clear any previous responses
13+
responseContainer.innerHTML = '';
14+
completeResponse = '';
15+
16+
// Get the user's question from the input field
17+
const question = document.getElementById('question-input').value;
18+
19+
// Close any existing EventSource to avoid multiple open connections
20+
if (eventSource) {
21+
eventSource.close();
22+
}
23+
24+
// Create a new EventSource to receive streaming responses
25+
eventSource = new EventSource(`/stream-chat?question=${encodeURIComponent(question)}`);
26+
27+
eventSource.onmessage = function(event) {
28+
if (event.data === null) {
29+
// Close the event source if no more data
30+
eventSource.close();
31+
return;
32+
}
33+
34+
// Process the received data and replace line breaks with HTML tags
35+
let newData = event.data.replace(/\\n/g, '<br>');
36+
completeResponse += " " + newData;
37+
responseContainer.innerHTML = completeResponse; // Use innerHTML to render HTML tags
38+
responseContainer.scrollTop = responseContainer.scrollHeight; // Scroll to the latest message
39+
};
40+
41+
eventSource.onerror = function(event) {
42+
console.error("EventSource failed:", event);
43+
eventSource.close();
44+
};
45+
});
46+
47+
// Handle stopping the stream
48+
stopButton.addEventListener('click', function() {
49+
if (eventSource) {
50+
eventSource.close();
51+
responseContainer.innerHTML += '<p>Streaming stopped.</p>';
52+
}
53+
});
54+
});

src/main/resources/static/js/LodingScreen.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)