Потоковая передача WebSocket Не работает в Django + Next.js — Получаем только первое и последнее сообщение
Я создаю приложение для чата с искусственным интеллектом, используя Django (каналы) для серверной части и Next.js для интерфейса. Цель состоит в том, чтобы передавать сгенерированные ИИ ответы по частям через соединение WebSocket - аналогично тому, как работает REST API с ReadableStream. Однако на интерфейсе отображается только первый фрагмент, а затем окончательное завершенное сообщение, а не промежуточные потоковые фрагменты.
Вот упрощенная версия моей настройки:
Серверная часть (Django WebSocket Consumer): Я использую асинхронный генератор (handle_chat_request) для создания фрагментов сообщений.
async def receive(self, text_data):
logging.info(f"WebSocket message received: {text_data}")
try:
data = json.loads(text_data)
message_type = data.get("type")
if message_type == "message":
jwt_token = data.get("token")
if not jwt_token:
await self.send(json.dumps({"type": "error", "message": "Missing Authorization token."}))
await self.close(code=4001)
return
user_message = data.get("content")
user_id = data.get("user_id")
combined_response = ""
# Stream response chunks
async for chunk in handle_chat_request(user_message, user_id, jwt_token):
combined_response += chunk
await self.send(json.dumps({"type": "ai_response_chunk", "content": chunk}))
await asyncio.sleep(0) # Yield control to allow chunk sending
# Send final complete message
await self.send(json.dumps({"type": "ai_response_complete", "content": combined_response}))
except Exception as e:
logging.error(f"WebSocket error: {e}")
await self.send(json.dumps({"type": "error", "message": "An error occurred."}))
Интерфейс (Next.js Клиент WebSocket): Я прослушиваю входящие фрагменты и пытаюсь добавить их к последнему сообщению бота:
const handleWebSocketMessage = (event) => {
try {
const data = JSON.parse(event.data);
switch (data.type) {
case "ai_response_chunk":
setMessages((messages) => {
const updatedMessages = [...messages];
const lastMessageIndex = updatedMessages.findLastIndex(
(m) => m.role === "bot"
);
if (lastMessageIndex !== -1) {
updatedMessages[lastMessageIndex] = {
...updatedMessages[lastMessageIndex],
content: updatedMessages[lastMessageIndex].content + data.content,
};
} else {
updatedMessages.push({ role: "bot", content: data.content });
}
return updatedMessages;
});
break;
case "ai_response_complete":
//some other functionality
break;
case "error":
setError(data.message);
setLoading(false);
break;
}
} catch (error) {
console.error("Error parsing WebSocket message:", error);
setLoading(false);
}
};
Проблема: "ai_response_chunk" работает так, как и ожидалось от пользовательского интерфейса, отображает только первый фрагмент, а затем конечное содержимое сообщения, когда оно будет завершено, но я не вижу дополнительных обновлений, как в случае с реализацией REST API.
Реализация Rest:
fetch(`/api/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ message: userMessage }),
}).then(async (response) => {
if (!response.ok) throw new Error("Chat API failed");
if (!response.body) throw new Error("ReadableStream not supported");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let done = false;
let botMessage = "";
// Add bot message placeholder
setMessages((messages) => [
...messages,
{ content: "", role: "bot" },
]);
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
const chunk = decoder.decode(value, { stream: true });
botMessage += chunk;
setMessages((messages) => {
const updatedMessages = [...messages];
const lastMessageIndex = updatedMessages.findLastIndex(
(m) => m.role === "bot"
);
if (lastMessageIndex !== -1) {
updatedMessages[lastMessageIndex] = {
...updatedMessages[lastMessageIndex],
content: updatedMessages[lastMessageIndex].content + chunk,
};
}
return updatedMessages;
});
}
```
Вы можете добавить небольшую задержку внутри цикла отправки фрагментов! Например, использование asyncio.sleep поможет контролировать поток.
for chunk in self.sync_stream_chat_response(user_message, user_id, jwt_token):
chunk_count += 1
print(f"Sending chunk #{chunk_count} to WebSocket")
await self.send(json.dumps({"type": "ai_response_chunk", "content": chunk}))
# Add a small delay (e.g., 50ms)
await asyncio.sleep(0.05)
await self.send(json.dumps({"type": "ai_response_complete"}))
await asyncio.sleep(0.05) → Пауза в 50 миллисекунд между каждым фрагментом.