use std::{any::Any, fmt, sync::Arc};
use crate::conversation::{AssistantTurnItem, RawJson, ToolCallId, ToolName};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TurnRole {
System,
Developer,
User,
Assistant,
}
pub struct ToolCallItemView<'a> {
pub id: &'a ToolCallId,
pub name: &'a ToolName,
pub arguments: &'a RawJson,
}
pub struct ToolResultItemView<'a> {
pub id: &'a ToolCallId,
pub name: &'a ToolName,
pub arguments: &'a RawJson,
pub result: &'a RawJson,
}
pub trait ItemView {
fn as_text(&self) -> Option<&str>;
fn as_reasoning(&self) -> Option<&str>;
fn as_refusal(&self) -> Option<&str>;
fn as_tool_call(&self) -> Option<ToolCallItemView<'_>>;
fn as_tool_result(&self) -> Option<ToolResultItemView<'_>>;
}
pub trait TurnView: fmt::Debug + Send + Sync {
fn role(&self) -> TurnRole;
fn item_count(&self) -> usize;
fn item_at(&self, index: usize) -> Option<&dyn ItemView>;
fn as_any(&self) -> &dyn Any;
}
impl dyn TurnView {
pub fn downcast_ref<T: TurnView + 'static>(&self) -> Option<&T> {
self.as_any().downcast_ref::<T>()
}
}
pub struct TurnItemIter<'a> {
turn: &'a dyn TurnView,
index: usize,
}
impl<'a> TurnItemIter<'a> {
pub fn new(turn: &'a dyn TurnView) -> Self {
Self { turn, index: 0 }
}
}
impl<'a> Iterator for TurnItemIter<'a> {
type Item = &'a dyn ItemView;
fn next(&mut self) -> Option<Self::Item> {
let item = self.turn.item_at(self.index)?;
self.index += 1;
Some(item)
}
}
#[derive(Debug)]
pub struct AssistantTurnView {
items: Vec<CoreAssistantItemView>,
}
impl AssistantTurnView {
pub fn from_items(items: &[AssistantTurnItem]) -> Self {
Self {
items: items.iter().map(CoreAssistantItemView::from_item).collect(),
}
}
}
impl TurnView for AssistantTurnView {
fn role(&self) -> TurnRole {
TurnRole::Assistant
}
fn item_count(&self) -> usize {
self.items.len()
}
fn item_at(&self, index: usize) -> Option<&dyn ItemView> {
self.items.get(index).map(|v| v as &dyn ItemView)
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
enum CoreAssistantItemKind {
Text(String),
Reasoning(String),
Refusal(String),
ToolCall {
id: ToolCallId,
name: ToolName,
arguments: RawJson,
},
}
#[derive(Debug)]
struct CoreAssistantItemView {
kind: CoreAssistantItemKind,
}
impl CoreAssistantItemView {
fn from_item(item: &AssistantTurnItem) -> Self {
let kind = match item {
AssistantTurnItem::Text(t) => CoreAssistantItemKind::Text(t.clone()),
AssistantTurnItem::Reasoning(t) => CoreAssistantItemKind::Reasoning(t.clone()),
AssistantTurnItem::Refusal(t) => CoreAssistantItemKind::Refusal(t.clone()),
AssistantTurnItem::ToolCall {
id,
name,
arguments,
} => CoreAssistantItemKind::ToolCall {
id: id.clone(),
name: name.clone(),
arguments: arguments.clone(),
},
};
Self { kind }
}
}
impl ItemView for CoreAssistantItemView {
fn as_text(&self) -> Option<&str> {
match &self.kind {
CoreAssistantItemKind::Text(t) => Some(t),
_ => None,
}
}
fn as_reasoning(&self) -> Option<&str> {
match &self.kind {
CoreAssistantItemKind::Reasoning(t) => Some(t),
_ => None,
}
}
fn as_refusal(&self) -> Option<&str> {
match &self.kind {
CoreAssistantItemKind::Refusal(t) => Some(t),
_ => None,
}
}
fn as_tool_call(&self) -> Option<ToolCallItemView<'_>> {
match &self.kind {
CoreAssistantItemKind::ToolCall {
id,
name,
arguments,
} => Some(ToolCallItemView {
id,
name,
arguments,
}),
_ => None,
}
}
fn as_tool_result(&self) -> Option<ToolResultItemView<'_>> {
None }
}
pub type CommittedTurn = Arc<dyn TurnView + Send + Sync>;