feat(tui): implement status bar with connection and turn state indicators feat(tui): create workflow panel for agent status and progress visualization feat(web): introduce web frontend interface with static file serving feat(ws): add WebSocket interface for real-time communication and session management
89 lines
3.1 KiB
Rust
89 lines
3.1 KiB
Rust
//! In-memory conversation state: message history plus the system prompt and
|
||
//! model parameters used to drive the LLM.
|
||
//!
|
||
//! # Flow
|
||
//!
|
||
//! [`Conversation::new`] → [`push`](Conversation::push) to add messages →
|
||
//! [`to_api_messages`](Conversation::to_api_messages) to format for the LLM
|
||
//! API (system prompt prepended).
|
||
//!
|
||
//! # Components
|
||
//!
|
||
//! - `Conversation` — message vector + session metadata + generation params
|
||
//! - `push` / `rebuild_system` — mutation helpers
|
||
//! - `to_api_messages` — formats messages for API consumption
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
use super::message::{ChatMessage, Role};
|
||
|
||
/// A single conversation's message history and generation settings.
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct Conversation {
|
||
/// Ordered list of chat messages (user, assistant, tool, system).
|
||
pub messages: Vec<ChatMessage>,
|
||
/// System prompt prepended at request time (see `to_api_messages`).
|
||
pub system_prompt: String,
|
||
/// Foreign key referencing the owning session.
|
||
pub session_id: String,
|
||
/// Model identifier string, e.g. `"anthropic/claude-opus-4-8"`.
|
||
pub model: String,
|
||
/// Optional cap on output tokens.
|
||
pub max_tokens: Option<u32>,
|
||
/// Optional temperature (0.0 – 2.0).
|
||
pub temperature: Option<f32>,
|
||
}
|
||
|
||
impl Conversation {
|
||
/// Create an empty conversation with the given system prompt and
|
||
/// session id, using default model/token/temperature settings.
|
||
pub fn new(system_prompt: String, session_id: String) -> Self {
|
||
Conversation {
|
||
messages: Vec::new(),
|
||
system_prompt,
|
||
session_id,
|
||
model: "anthropic/claude-opus-4-8".to_string(),
|
||
max_tokens: None,
|
||
temperature: None,
|
||
}
|
||
}
|
||
|
||
/// Append a message to the conversation history.
|
||
pub fn push(&mut self, msg: ChatMessage) {
|
||
self.messages.push(msg);
|
||
}
|
||
|
||
/// Replace the system prompt and strip any prior `System`-role
|
||
/// messages from history.
|
||
///
|
||
/// Why: the system prompt is re-injected fresh at request time via
|
||
/// `to_api_messages`, so stale `System` messages in `self.messages`
|
||
/// would be redundant/conflicting if left in place.
|
||
pub fn rebuild_system(&mut self, new_prompt: String) {
|
||
self.system_prompt = new_prompt;
|
||
self.messages.retain(|m| !matches!(m.role, Role::System));
|
||
}
|
||
|
||
/// Build the message list to send to the LLM API, with the system
|
||
/// prompt prepended.
|
||
///
|
||
/// Return: a new `Vec` (clone of history) with a synthesized system
|
||
/// message at index 0.
|
||
pub fn to_api_messages(&self) -> Vec<ChatMessage> {
|
||
let mut msgs = Vec::with_capacity(self.messages.len() + 1);
|
||
msgs.push(ChatMessage::system(&self.system_prompt));
|
||
msgs.extend(self.messages.iter().cloned());
|
||
msgs
|
||
}
|
||
|
||
/// Number of messages in the conversation history (excluding the
|
||
/// synthesized system message).
|
||
pub fn len(&self) -> usize {
|
||
self.messages.len()
|
||
}
|
||
|
||
/// Returns `true` if the conversation has no messages.
|
||
pub fn is_empty(&self) -> bool {
|
||
self.messages.is_empty()
|
||
}
|
||
}
|