Add reasoning streaming
This commit is contained in:
@@ -585,6 +585,8 @@ type StreamMessage struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
// Delta is a text fragment from the assistant response.
|
||||
Delta string `json:"delta,omitempty"`
|
||||
// ReasoningDelta is a reasoning text fragment from the assistant response.
|
||||
ReasoningDelta string `json:"reasoning_delta,omitempty"`
|
||||
// Message is the chat completion message.
|
||||
Message *llm.Message `json:"message,omitempty"`
|
||||
// Provider is the LLM provider used for the response.
|
||||
@@ -709,9 +711,10 @@ func (svc *Service) chatStream(w http.ResponseWriter, r *http.Request) {
|
||||
// Consume events and send as SSE.
|
||||
for evt := range events {
|
||||
msg := StreamMessage{
|
||||
Delta: evt.Delta,
|
||||
Provider: provider,
|
||||
Model: model,
|
||||
Delta: evt.Delta,
|
||||
ReasoningDelta: evt.ReasoningDelta,
|
||||
Provider: provider,
|
||||
Model: model,
|
||||
}
|
||||
if evt.Message.Role != "" {
|
||||
msg.Message = &evt.Message
|
||||
|
||||
@@ -5,6 +5,7 @@ const PROVIDER_KEY = 'odidere_provider';
|
||||
const STORAGE_KEY = 'odidere_history';
|
||||
const STREAM_ENDPOINT = '/v1/chat/voice/stream';
|
||||
const StreamEventDelta = 'delta';
|
||||
const StreamEventReasoningDelta = 'reasoning_delta';
|
||||
const StreamEventDone = 'done';
|
||||
const StreamEventMessage = 'message';
|
||||
const SYSTEM_MESSAGE_KEY = 'odidere_system_message';
|
||||
@@ -1583,7 +1584,10 @@ class Odidere {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eventType === StreamEventDelta) {
|
||||
if (
|
||||
eventType === StreamEventDelta ||
|
||||
eventType === StreamEventReasoningDelta
|
||||
) {
|
||||
if (!streamingMessage) {
|
||||
const message = {
|
||||
role: 'assistant',
|
||||
@@ -1598,8 +1602,21 @@ class Odidere {
|
||||
streamingMessage = { message: appended[0], $el };
|
||||
}
|
||||
|
||||
streamingMessage.message.content[0].text += event.delta || '';
|
||||
this.#appendStreamingDelta(streamingMessage.$el, event.delta || '');
|
||||
if (eventType === StreamEventDelta) {
|
||||
streamingMessage.message.content[0].text += event.delta || '';
|
||||
this.#appendStreamingDelta(
|
||||
streamingMessage.$el,
|
||||
event.delta || '',
|
||||
);
|
||||
} else {
|
||||
streamingMessage.message.reasoning_content = `${
|
||||
streamingMessage.message.reasoning_content || ''
|
||||
}${event.reasoning_delta || ''}`;
|
||||
this.#appendStreamingReasoningDelta(
|
||||
streamingMessage.$el,
|
||||
event.reasoning_delta || '',
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2263,6 +2280,24 @@ class Odidere {
|
||||
this.#scrollToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* #appendStreamingReasoningDelta appends reasoning text to a streaming
|
||||
* assistant message.
|
||||
* @param {HTMLElement} $msg
|
||||
* @param {string} delta
|
||||
*/
|
||||
#appendStreamingReasoningDelta($msg, delta) {
|
||||
let $reasoning = $msg.querySelector('.collapsible--reasoning');
|
||||
if (!$reasoning) {
|
||||
$reasoning = this.#createCollapsibleBlock('reasoning', 'Reasoning', '');
|
||||
const $content = $msg.querySelector('.message__content');
|
||||
$msg.querySelector('.message__body').insertBefore($reasoning, $content);
|
||||
}
|
||||
|
||||
$reasoning.querySelector('pre').textContent += delta;
|
||||
this.#scrollToBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* #finalizeStreamingAssistantMessage replaces a streaming placeholder with
|
||||
* a fully bound assistant message.
|
||||
|
||||
Reference in New Issue
Block a user