Добавлена поддержка стриминга сообщений через yield токены. 'Деактивирован' метод generate_stream в ollamaapi
This commit is contained in:
Binary file not shown.
+10
-10
@@ -1,4 +1,4 @@
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from flask import Flask, Response, render_template, request, jsonify
|
||||
from core.agent.agent import Agent
|
||||
from core.llm.ollamaapi import OllamaProvider
|
||||
from core.character.character import Character
|
||||
@@ -39,18 +39,18 @@ def init():
|
||||
#@ui.route("/stream", methods=["POST"])
|
||||
def chat():
|
||||
|
||||
data = request.json
|
||||
global last_prompt
|
||||
last_prompt = request.json["message"]
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
user_message = data["message"]
|
||||
|
||||
response = agent.respond(user_message)
|
||||
@ui.route("/stream")
|
||||
def stream():
|
||||
|
||||
def generate():
|
||||
for content in agent.stream_responce(last_prompt):
|
||||
yield f"data: {content}\n\n"
|
||||
agent.save_memory()
|
||||
|
||||
return jsonify({
|
||||
"response": response
|
||||
})
|
||||
|
||||
return Response(generate(), mimetype="text/event-stream")
|
||||
|
||||
#список персонажей
|
||||
@ui.route("/characters", methods=["GET"])
|
||||
|
||||
+142
-21
@@ -1,15 +1,47 @@
|
||||
const messages = document.getElementById("messages");
|
||||
const input = document.getElementById("user-input");
|
||||
const sendBtn = document.getElementById("send-btn");
|
||||
loadChat();
|
||||
sendBtn.addEventListener("click", async () => {
|
||||
|
||||
const input = document.getElementById("user-input");
|
||||
const text = input.value;
|
||||
let userName = "Alex";
|
||||
|
||||
const messages = document.getElementById("messages");
|
||||
// текущий SSE поток (ВАЖНО: чтобы не плодить соединения)
|
||||
let eventSource = null;
|
||||
|
||||
messages.innerHTML += `<p><b>You:</b> ${text}</p>`; //=> получаем имя пользователя
|
||||
|
||||
const response = await fetch("/chat", {
|
||||
|
||||
// ===============================
|
||||
// 1. ЗАГРУЗКА ИСТОРИИ
|
||||
// ===============================
|
||||
async function loadHistory() {
|
||||
const res = await fetch("/init");
|
||||
const data = await res.json();
|
||||
|
||||
userName = data.user_name || "User";
|
||||
|
||||
data.messages.forEach(msg => {
|
||||
renderMessage(msg.role, msg.content);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// 2. ОТПРАВКА СООБЩЕНИЯ
|
||||
// ===============================
|
||||
async function sendMessage() {
|
||||
|
||||
const text = input.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
renderMessage("user", text);
|
||||
input.value = "";
|
||||
|
||||
// закрываем старый stream если есть
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
}
|
||||
|
||||
// отправляем сообщение на backend (он сохраняет state)
|
||||
await fetch("/chat", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
@@ -19,21 +51,110 @@ sendBtn.addEventListener("click", async () => {
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
// создаём ПУСТОЕ сообщение для стрима
|
||||
const aiMessageDiv = createAIMessagePlaceholder();
|
||||
|
||||
messages.innerHTML += `<p><b>AI:</b> ${data.response}</p>`;
|
||||
// запускаем stream
|
||||
startStream(aiMessageDiv);
|
||||
}
|
||||
|
||||
input.value = "";
|
||||
|
||||
// ===============================
|
||||
// 3. SSE STREAM
|
||||
// ===============================
|
||||
function startStream(aiMessageDiv) {
|
||||
|
||||
eventSource = new EventSource("/stream");
|
||||
|
||||
let fullText = "";
|
||||
|
||||
eventSource.onmessage = function(event) {
|
||||
|
||||
const token = event.data;
|
||||
|
||||
// иногда Ollama может слать [DONE]
|
||||
if (token === "[DONE]") {
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
return;
|
||||
}
|
||||
|
||||
fullText += token;
|
||||
|
||||
aiMessageDiv.innerHTML = `<b>AI:</b> ${formatText(fullText)}`;
|
||||
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
};
|
||||
|
||||
eventSource.onerror = function(err) {
|
||||
console.error("Stream error:", err);
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// 4. РЕНДЕР СООБЩЕНИЙ
|
||||
// ===============================
|
||||
function renderMessage(role, text) {
|
||||
|
||||
const div = document.createElement("div");
|
||||
|
||||
if (role === "user") {
|
||||
div.className = "user-msg";
|
||||
div.innerHTML = `<b>${userName}:</b> ${text}`;
|
||||
}
|
||||
|
||||
if (role === "assistant") {
|
||||
div.className = "ai-msg";
|
||||
div.innerHTML = `<b>AI:</b> ${formatText(text)}`;
|
||||
}
|
||||
|
||||
messages.appendChild(div);
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// 5. ПУСТОЙ КОНТЕЙНЕР ДЛЯ STREAM
|
||||
// ===============================
|
||||
function createAIMessagePlaceholder() {
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "ai-msg";
|
||||
div.innerHTML = `<b>AI:</b> `;
|
||||
|
||||
messages.appendChild(div);
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// 6. ФОРМАТИРОВАНИЕ (*actions*)
|
||||
// ===============================
|
||||
function formatText(text) {
|
||||
|
||||
// действия *...*
|
||||
return text.replace(/\*(.*?)\*/g, '<span class="action">*$1*</span>');
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// 7. EVENTS
|
||||
// ===============================
|
||||
sendBtn.addEventListener("click", sendMessage);
|
||||
|
||||
input.addEventListener("keypress", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
async function loadChat() {
|
||||
|
||||
const res = await fetch("/init");
|
||||
const data = await res.json();
|
||||
|
||||
const messages = document.getElementById("messages");
|
||||
|
||||
data.messages.forEach(msg => {
|
||||
messages.innerHTML += `<p><b>${msg.role}:</b> ${msg.content}</p>`; //добавить проверку role == user => получаем имя пользователя
|
||||
});
|
||||
}
|
||||
// ===============================
|
||||
// 8. INIT
|
||||
// ===============================
|
||||
loadHistory();
|
||||
Reference in New Issue
Block a user