feat(session): implement streaming state with text delta and tool‑call diff handling
- Introduce `StreamingMessageState` to track full text, last tool calls, and completion. - Add `StreamDiff`, `TextDelta`, and `TextDeltaKind` for describing incremental changes. - SessionController now maintains a `stream_states` map keyed by response IDs. - `apply_stream_chunk` uses the new state to emit append/replace text deltas and tool‑call updates, handling final chunks and cleanup. - `Conversation` gains `set_stream_content` to replace streaming content and manage metadata. - Ensure stream state is cleared on cancel, conversation reset, and controller clear.
This commit is contained in:
@@ -190,6 +190,46 @@ impl ConversationManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace the current streaming content for a message.
|
||||
pub fn set_stream_content(
|
||||
&mut self,
|
||||
message_id: Uuid,
|
||||
content: impl Into<String>,
|
||||
is_final: bool,
|
||||
) -> Result<()> {
|
||||
let index = self
|
||||
.message_index
|
||||
.get(&message_id)
|
||||
.copied()
|
||||
.ok_or_else(|| crate::Error::Unknown(format!("Unknown message id: {message_id}")))?;
|
||||
|
||||
let conversation = self.active_mut();
|
||||
if let Some(message) = conversation.messages.get_mut(index) {
|
||||
message.content = content.into();
|
||||
message.metadata.remove(PLACEHOLDER_FLAG);
|
||||
message.timestamp = std::time::SystemTime::now();
|
||||
let millis = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_millis() as u64;
|
||||
message.metadata.insert(
|
||||
LAST_CHUNK_TS.to_string(),
|
||||
Value::Number(Number::from(millis)),
|
||||
);
|
||||
|
||||
if is_final {
|
||||
message
|
||||
.metadata
|
||||
.insert(STREAMING_FLAG.to_string(), Value::Bool(false));
|
||||
self.streaming.remove(&message_id);
|
||||
} else if let Some(info) = self.streaming.get_mut(&message_id) {
|
||||
info.last_update = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set placeholder text for a streaming message
|
||||
pub fn set_stream_placeholder(
|
||||
&mut self,
|
||||
@@ -254,7 +294,11 @@ impl ConversationManager {
|
||||
.ok_or_else(|| crate::Error::Unknown(format!("Unknown message id: {message_id}")))?;
|
||||
|
||||
if let Some(message) = self.active_mut().messages.get_mut(index) {
|
||||
message.tool_calls = Some(tool_calls);
|
||||
if tool_calls.is_empty() {
|
||||
message.tool_calls = None;
|
||||
} else {
|
||||
message.tool_calls = Some(tool_calls);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user