use std::pin::Pin;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use futures::Stream;
use uuid::Uuid;
use crate::error::ChannelError;
#[derive(Debug, Clone)]
pub struct IncomingMessage {
pub id: Uuid,
pub channel: String,
pub user_id: String,
pub user_name: Option<String>,
pub content: String,
pub thread_id: Option<String>,
pub received_at: DateTime<Utc>,
pub metadata: serde_json::Value,
}
impl IncomingMessage {
pub fn new(
channel: impl Into<String>,
user_id: impl Into<String>,
content: impl Into<String>,
) -> Self {
Self {
id: Uuid::new_v4(),
channel: channel.into(),
user_id: user_id.into(),
user_name: None,
content: content.into(),
thread_id: None,
received_at: Utc::now(),
metadata: serde_json::Value::Null,
}
}
pub fn with_thread(mut self, thread_id: impl Into<String>) -> Self {
self.thread_id = Some(thread_id.into());
self
}
pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
self.metadata = metadata;
self
}
pub fn with_user_name(mut self, name: impl Into<String>) -> Self {
self.user_name = Some(name.into());
self
}
}
pub type MessageStream = Pin<Box<dyn Stream<Item = IncomingMessage> + Send>>;
#[derive(Debug, Clone)]
pub struct OutgoingResponse {
pub content: String,
pub thread_id: Option<String>,
pub metadata: serde_json::Value,
}
impl OutgoingResponse {
pub fn text(content: impl Into<String>) -> Self {
Self {
content: content.into(),
thread_id: None,
metadata: serde_json::Value::Null,
}
}
pub fn in_thread(mut self, thread_id: impl Into<String>) -> Self {
self.thread_id = Some(thread_id.into());
self
}
}
#[derive(Debug, Clone)]
pub enum StatusUpdate {
Thinking(String),
ToolStarted { name: String },
ToolCompleted { name: String, success: bool },
ToolResult { name: String, preview: String },
StreamChunk(String),
Status(String),
JobStarted {
job_id: String,
title: String,
browse_url: String,
},
ApprovalNeeded {
request_id: String,
tool_name: String,
description: String,
parameters: serde_json::Value,
},
AuthRequired {
extension_name: String,
instructions: Option<String>,
auth_url: Option<String>,
setup_url: Option<String>,
},
AuthCompleted {
extension_name: String,
success: bool,
message: String,
},
}
#[async_trait]
pub trait Channel: Send + Sync {
fn name(&self) -> &str;
async fn start(&self) -> Result<MessageStream, ChannelError>;
async fn respond(
&self,
msg: &IncomingMessage,
response: OutgoingResponse,
) -> Result<(), ChannelError>;
async fn send_status(
&self,
_status: StatusUpdate,
_metadata: &serde_json::Value,
) -> Result<(), ChannelError> {
Ok(())
}
async fn broadcast(
&self,
_user_id: &str,
_response: OutgoingResponse,
) -> Result<(), ChannelError> {
Ok(())
}
async fn health_check(&self) -> Result<(), ChannelError>;
async fn shutdown(&self) -> Result<(), ChannelError> {
Ok(())
}
}