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:
@@ -34,6 +34,16 @@ pub struct GlobalEvent {
|
||||
|
||||
// ── Server → Client ───────────────────────────────────────────────────────────
|
||||
|
||||
/// Which token stream a [`ServerEvent::TokenDelta`] belongs to.
|
||||
#[derive(Clone, Copy, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenDeltaKind {
|
||||
/// Visible answer text.
|
||||
Content,
|
||||
/// Model chain-of-thought (reasoning/thinking tokens).
|
||||
Reasoning,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ServerEvent {
|
||||
@@ -117,6 +127,10 @@ pub enum ServerEvent {
|
||||
content: String,
|
||||
input_tokens: Option<u32>,
|
||||
output_tokens: Option<u32>,
|
||||
/// Chain-of-thought, when the model produced any. Lets non-streaming
|
||||
/// providers surface the reasoning block live, not just from history.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
reasoning_content: Option<String>,
|
||||
},
|
||||
/// A fatal error occurred processing the request.
|
||||
Error {
|
||||
@@ -132,6 +146,17 @@ pub enum ServerEvent {
|
||||
content: String,
|
||||
input_tokens: Option<u32>,
|
||||
output_tokens: Option<u32>,
|
||||
/// Chain-of-thought of this tool-call round, when produced.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
reasoning_content: Option<String>,
|
||||
},
|
||||
/// One incremental token while the assistant response (or its reasoning)
|
||||
/// is being generated. Best-effort: deltas ride the lossy broadcast bus and
|
||||
/// a lagging client may miss some — the final `Done` is always authoritative
|
||||
/// and carries the complete content.
|
||||
TokenDelta {
|
||||
kind: TokenDeltaKind,
|
||||
delta: String,
|
||||
},
|
||||
/// A write operation requires user approval before executing (shows a diff).
|
||||
PendingWrite {
|
||||
@@ -279,6 +304,7 @@ impl ServerEvent {
|
||||
Self::Done { .. } => "done",
|
||||
Self::Error { .. } => "error",
|
||||
Self::Thinking { .. } => "thinking",
|
||||
Self::TokenDelta { .. } => "token_delta",
|
||||
Self::PendingWrite { .. } => "pending_write",
|
||||
Self::ApprovalRequired { .. } => "approval_required",
|
||||
Self::AgentQuestion { .. } => "agent_question",
|
||||
|
||||
Reference in New Issue
Block a user