use crate::adapter::RunError;
use crate::repository::{ExportedRepository, MessageChanges, MessageEntry, RepositoryError};
use crate::subscribe::Subscription;
use crate::types::{AppendMessage, MessageId, RunConfig, SubmittedFeedback, ThreadMessage};
use crate::Composer;
use serde_json::Value;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
pub type RunFuture = Pin<Box<dyn Future<Output = Result<(), RunError>>>>;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct StartRunConfig {
pub parent_id: Option<MessageId>,
pub source_id: Option<MessageId>,
pub run_config: RunConfig,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ToolResult {
pub message_id: MessageId,
pub tool_call_id: String,
pub result: Value,
pub is_error: bool,
pub artifact: Option<Value>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RuntimeCapabilities {
pub switch_to_branch: bool,
pub edit: bool,
pub reload: bool,
pub cancel: bool,
pub copy: bool,
pub feedback: bool,
pub answer_tool_call: bool,
}
impl RuntimeCapabilities {
pub const ALL: Self = Self {
switch_to_branch: true,
edit: true,
reload: true,
cancel: true,
copy: true,
feedback: true,
answer_tool_call: true,
};
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RuntimeError {
Repository(RepositoryError),
NotAssistantMessage(MessageId),
ToolCallNotFound {
message_id: MessageId,
tool_call_id: String,
},
RunInProgress,
EditInProgress(MessageId),
ApprovalNotFound(String),
Unsupported(&'static str),
Disposed,
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Repository(error) => error.fmt(f),
Self::NotAssistantMessage(id) => write!(f, "message {id} is not an assistant message"),
Self::ToolCallNotFound {
message_id,
tool_call_id,
} => {
write!(f, "message {message_id} has no tool call {tool_call_id}")
}
Self::RunInProgress => f.write_str("a run is in progress"),
Self::EditInProgress(id) => write!(f, "message {id} is already being edited"),
Self::ApprovalNotFound(id) => write!(f, "no pending approval {id}"),
Self::Unsupported(what) => write!(f, "this runtime does not support {what}"),
Self::Disposed => f.write_str("the runtime was dropped"),
}
}
}
impl std::error::Error for RuntimeError {}
impl From<RepositoryError> for RuntimeError {
fn from(error: RepositoryError) -> Self {
Self::Repository(error)
}
}
pub trait ThreadRuntime {
fn messages(&self) -> Arc<[Arc<ThreadMessage>]>;
fn message(&self, id: &MessageId) -> Result<MessageEntry, RuntimeError>;
fn is_running(&self) -> bool;
fn capabilities(&self) -> RuntimeCapabilities;
fn branches(&self, id: &MessageId) -> Result<Vec<MessageId>, RuntimeError>;
fn branch_position(&self, id: &MessageId) -> Result<(usize, usize), RuntimeError> {
let branches = self.branches(id)?;
let position = branches.iter().position(|b| b == id).map_or(1, |p| p + 1);
Ok((position, branches.len().max(1)))
}
fn structure_version(&self) -> Option<u64> {
None
}
fn message_changes_since(&self, _version: Option<u64>) -> Option<MessageChanges> {
None
}
fn switch_to_branch(&self, id: &MessageId) -> Result<(), RuntimeError>;
fn append(&self, message: AppendMessage) -> Result<RunFuture, RuntimeError>;
fn reload(&self, config: StartRunConfig) -> Result<RunFuture, RuntimeError>;
fn cancel_run(&self);
fn add_tool_result(&self, result: ToolResult) -> Result<Option<RunFuture>, RuntimeError>;
fn respond_to_tool_approval(
&self,
message_id: &MessageId,
approval_id: &str,
approved: bool,
reason: Option<String>,
) -> Result<Option<RunFuture>, RuntimeError>;
fn submit_feedback(
&self,
id: &MessageId,
feedback: Option<SubmittedFeedback>,
) -> Result<(), RuntimeError>;
fn composer(&self) -> Composer;
fn begin_edit(&self, id: &MessageId) -> Result<Composer, RuntimeError>;
fn edit_composer(&self, id: &MessageId) -> Option<Composer>;
fn subscribe_dyn(&self, callback: Box<dyn Fn()>) -> Subscription;
fn export(&self) -> ExportedRepository;
fn import(&self, exported: ExportedRepository) -> Result<(), RuntimeError>;
}
pub trait ComposerHost {
fn last_message_id(&self) -> Option<MessageId>;
fn append(&self, message: AppendMessage) -> Result<RunFuture, RuntimeError>;
fn cancel_run(&self);
fn is_running(&self) -> bool;
fn end_edit(&self, id: &MessageId);
}