token streaming & reasoning display: live SSE tokens frontend to back
Nightly Build / build (push) Successful in 6m59s
Nightly Build / build (push) Successful in 6m59s
- Add StreamDelta(SseDecoder) framing shared by OpenAI/Anthropic - OpenAiClient: stream=true + reasoning_content deltas, index-based tool_calls accumulation, usage from final chunk - AnthropicClient: message_start/content_block_*/message_delta events, thinking_delta->reasoning, input_json_delta->tool input - TokenDelta ServerEvent variant wired through ChatHub + WS broadcast - Frontend throttled flush (~15 Hz), pending bubble mutate-in-place, reasoning as collapsed-by-default <details> - Drop streaming bubble on error/llm_failed/model_fallback - i18n: chat.reasoning key added to en/fr/it
This commit is contained in:
@@ -507,6 +507,21 @@ export function renderAttachmentChips(host, attachments, { removable = false } =
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapsible chain-of-thought block: small, muted, collapsed by default so it
|
||||
* never weighs on the UI. A native <details> — Lit keeps the element stable
|
||||
* across re-renders, so a user-expanded block stays open while tokens stream
|
||||
* into it (live) and in past history items alike.
|
||||
*/
|
||||
function renderReasoning(msg) {
|
||||
if (!msg.reasoning) return nothing;
|
||||
return html`
|
||||
<details class="reasoning-block ${msg.streaming ? 'reasoning-block--live' : ''}">
|
||||
<summary>${t('chat.reasoning')}</summary>
|
||||
<div class="reasoning-content">${msg.reasoning}</div>
|
||||
</details>`;
|
||||
}
|
||||
|
||||
export function renderMsg(host, msg) {
|
||||
try {
|
||||
switch (msg.kind) {
|
||||
@@ -516,6 +531,7 @@ export function renderMsg(host, msg) {
|
||||
return html`
|
||||
<div class="copilot-msg assistant copilot-markdown ${msg.failed ? 'copilot-msg--failed' : ''}">
|
||||
${msg.failed ? failedBadge() : nothing}
|
||||
${renderReasoning(msg)}
|
||||
${unsafeHTML(renderMarkdown(msg.content))}
|
||||
${msg.input_tokens != null ? html`<div class="copilot-token-count">↑${msg.input_tokens.toLocaleString()} tok ↓${msg.output_tokens?.toLocaleString()} tok</div>` : nothing}
|
||||
</div>`;
|
||||
@@ -523,8 +539,10 @@ export function renderMsg(host, msg) {
|
||||
return html`
|
||||
<div class="copilot-msg assistant copilot-markdown ${msg.failed ? 'copilot-msg--failed' : ''}">
|
||||
${msg.failed ? failedBadge() : nothing}
|
||||
${renderReasoning(msg)}
|
||||
${unsafeHTML(renderMarkdown(msg.content))}
|
||||
${msg.input_tokens != null ? html`<div class="copilot-token-count">↑${msg.input_tokens.toLocaleString()} tok ↓${msg.output_tokens?.toLocaleString()} tok</div>` : nothing}
|
||||
${msg.streaming ? html`<span class="stream-caret"></span>` : nothing}
|
||||
${msg.input_tokens != null && !msg.streaming ? html`<div class="copilot-token-count">↑${msg.input_tokens.toLocaleString()} tok ↓${msg.output_tokens?.toLocaleString()} tok</div>` : nothing}
|
||||
</div>`;
|
||||
case 'error':
|
||||
return html`
|
||||
|
||||
Reference in New Issue
Block a user