use std::pin::Pin;
use async_trait::async_trait;
use futures::Stream;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::message::Message;
use super::suspension::ToolCallResume;
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum LiveControlError {
#[error("live control subscribe failed: {0}")]
Subscribe(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum LiveRunCommand {
Messages(Vec<Message>),
PendingBoundaryWake,
Cancel,
Decision(Vec<(String, ToolCallResume)>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LiveRunTarget {
pub thread_id: String,
pub run_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dispatch_id: Option<String>,
}
impl LiveRunTarget {
#[must_use]
pub fn new(thread_id: impl Into<String>, run_id: impl Into<String>) -> Self {
Self {
thread_id: thread_id.into(),
run_id: run_id.into(),
dispatch_id: None,
}
}
#[must_use]
pub fn with_dispatch_id(mut self, dispatch_id: impl Into<String>) -> Self {
self.dispatch_id = Some(dispatch_id.into());
self
}
}
pub trait LiveCommandReceipt: Send + Sync {
fn ack(self: Box<Self>);
}
pub struct LiveRunCommandEntry {
pub command: LiveRunCommand,
pub receipt: Box<dyn LiveCommandReceipt>,
}
impl std::fmt::Debug for LiveRunCommandEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LiveRunCommandEntry")
.field("command", &self.command)
.finish_non_exhaustive()
}
}
pub type LiveRunCommandStream = Pin<Box<dyn Stream<Item = LiveRunCommandEntry> + Send>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LiveDeliveryOutcome {
Delivered,
NoSubscriber,
}
#[async_trait]
pub trait LiveRunCommandSource: Send + Sync {
async fn open_live_channel_for(
&self,
target: &LiveRunTarget,
) -> Result<LiveRunCommandStream, LiveControlError>;
}