use vw_shared::session::ui_types as session_ui;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct UiMessageId(String);
impl UiMessageId {
pub(crate) fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub(crate) fn local(seed: impl Into<String>) -> Self {
Self(format!("local:{}", seed.into()))
}
pub(crate) fn gateway(seed: impl Into<String>) -> Self {
Self(format!("gateway:{}", seed.into()))
}
pub(crate) fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiMessageBase {
pub(crate) id: UiMessageId,
pub(crate) parent_id: Option<UiMessageId>,
pub(crate) session_id: Option<String>,
pub(crate) created_ms: Option<u64>,
}
impl UiMessageBase {
pub(crate) fn new(id: UiMessageId) -> Self {
Self { id, parent_id: None, session_id: None, created_ms: None }
}
pub(crate) fn with_parent_id(mut self, parent_id: UiMessageId) -> Self {
self.parent_id = Some(parent_id);
self
}
pub(crate) fn with_session_id(mut self, session_id: impl Into<String>) -> Self {
self.session_id = Some(session_id.into());
self
}
pub(crate) fn with_created_ms(mut self, created_ms: u64) -> Self {
self.created_ms = Some(created_ms);
self
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct UiTokenUsage {
pub(crate) input_tokens: i64,
pub(crate) output_tokens: i64,
pub(crate) cached_tokens: i64,
pub(crate) reasoning_tokens: i64,
}
impl From<session_ui::TokenUsage> for UiTokenUsage {
fn from(value: session_ui::TokenUsage) -> Self {
Self {
input_tokens: value.input_tokens,
output_tokens: value.output_tokens,
cached_tokens: value.cached_tokens,
reasoning_tokens: value.reasoning_tokens,
}
}
}
impl From<&session_ui::TokenUsage> for UiTokenUsage {
fn from(value: &session_ui::TokenUsage) -> Self {
Self {
input_tokens: value.input_tokens,
output_tokens: value.output_tokens,
cached_tokens: value.cached_tokens,
reasoning_tokens: value.reasoning_tokens,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiThinkingTiming {
pub(crate) start_ms: u64,
pub(crate) end_ms: Option<u64>,
pub(crate) last_update_ms: u64,
}
impl From<session_ui::ThinkTiming> for UiThinkingTiming {
fn from(value: session_ui::ThinkTiming) -> Self {
Self {
start_ms: value.start_ms,
end_ms: value.end_ms,
last_update_ms: value.last_update_ms,
}
}
}
impl From<&session_ui::ThinkTiming> for UiThinkingTiming {
fn from(value: &session_ui::ThinkTiming) -> Self {
Self {
start_ms: value.start_ms,
end_ms: value.end_ms,
last_update_ms: value.last_update_ms,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) enum UiTurnTerminal {
#[default]
Pending,
Streaming,
Done {
finish_reason: Option<String>,
},
Cancelled {
reason: Option<String>,
},
TimedOut {
message: String,
},
Error {
message: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) enum UiToolCallState {
#[default]
Queued,
Running,
Complete,
Failed,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) enum UiStepState {
#[default]
Pending,
Running,
Complete,
Cancelled,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum UiSystemMessageLevel {
#[default]
Info,
Warning,
Success,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiUserMessage {
pub(crate) base: UiMessageBase,
pub(crate) text: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiAssistantMessage {
pub(crate) base: UiMessageBase,
pub(crate) text: String,
pub(crate) usage: UiTokenUsage,
pub(crate) step_count: usize,
pub(crate) terminal: UiTurnTerminal,
pub(crate) model: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiToolCall {
pub(crate) base: UiMessageBase,
pub(crate) call_id: Option<String>,
pub(crate) tool_name: String,
pub(crate) summary: Option<String>,
pub(crate) arguments: Option<String>,
pub(crate) state: UiToolCallState,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiToolResult {
pub(crate) base: UiMessageBase,
pub(crate) call_id: Option<String>,
pub(crate) tool_name: String,
pub(crate) content: String,
pub(crate) is_error: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiThinkingBlock {
pub(crate) base: UiMessageBase,
pub(crate) summary: Option<String>,
pub(crate) content: String,
pub(crate) timing: Vec<UiThinkingTiming>,
pub(crate) collapsed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiStep {
pub(crate) base: UiMessageBase,
pub(crate) step_index: u32,
pub(crate) started_ms: u64,
pub(crate) finished_ms: Option<u64>,
pub(crate) usage: UiTokenUsage,
pub(crate) finish_reason: Option<String>,
pub(crate) model: Option<String>,
pub(crate) state: UiStepState,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiSystemMessage {
pub(crate) base: UiMessageBase,
pub(crate) text: String,
pub(crate) level: UiSystemMessageLevel,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct UiErrorMessage {
pub(crate) base: UiMessageBase,
pub(crate) message: String,
pub(crate) recoverable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum UiMessage {
User(UiUserMessage),
Assistant(UiAssistantMessage),
ToolCall(UiToolCall),
ToolResult(UiToolResult),
Thinking(UiThinkingBlock),
Step(UiStep),
System(UiSystemMessage),
Error(UiErrorMessage),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum UiMessageKind {
User,
Assistant,
ToolCall,
ToolResult,
Thinking,
Step,
System,
Error,
}
impl UiMessage {
pub(crate) fn base(&self) -> &UiMessageBase {
match self {
Self::User(message) => &message.base,
Self::Assistant(message) => &message.base,
Self::ToolCall(message) => &message.base,
Self::ToolResult(message) => &message.base,
Self::Thinking(message) => &message.base,
Self::Step(message) => &message.base,
Self::System(message) => &message.base,
Self::Error(message) => &message.base,
}
}
pub(crate) fn kind(&self) -> UiMessageKind {
match self {
Self::User(_) => UiMessageKind::User,
Self::Assistant(_) => UiMessageKind::Assistant,
Self::ToolCall(_) => UiMessageKind::ToolCall,
Self::ToolResult(_) => UiMessageKind::ToolResult,
Self::Thinking(_) => UiMessageKind::Thinking,
Self::Step(_) => UiMessageKind::Step,
Self::System(_) => UiMessageKind::System,
Self::Error(_) => UiMessageKind::Error,
}
}
pub(crate) fn id(&self) -> &UiMessageId {
&self.base().id
}
}