use serde::{Deserialize, Serialize};
use crate::cancellation::CancellationToken;
use crate::inbox::{InboxReceiver, InboxSender};
pub type TaskId = String;
pub const BACKGROUND_TASKS_PLUGIN_ID: &str = "background_tasks";
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TaskStatus {
#[default]
Running,
Completed,
Failed,
Cancelled,
}
impl TaskStatus {
pub fn as_str(self) -> &'static str {
match self {
Self::Running => "running",
Self::Completed => "completed",
Self::Failed => "failed",
Self::Cancelled => "cancelled",
}
}
pub fn is_terminal(self) -> bool {
!matches!(self, Self::Running)
}
}
#[derive(Debug, Clone)]
pub enum TaskResult {
Success(serde_json::Value),
Failed(String),
Cancelled,
}
impl TaskResult {
pub fn status(&self) -> TaskStatus {
match self {
Self::Success(_) => TaskStatus::Completed,
Self::Failed(_) => TaskStatus::Failed,
Self::Cancelled => TaskStatus::Cancelled,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct TaskParentContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub task_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub run_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub call_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_id: Option<String>,
}
impl TaskParentContext {
pub fn is_empty(&self) -> bool {
self.task_id.is_none()
&& self.run_id.is_none()
&& self.call_id.is_none()
&& self.agent_id.is_none()
}
}
pub struct AgentTaskContext {
pub task_id: TaskId,
pub cancel_token: CancellationToken,
pub inbox_sender: InboxSender,
pub inbox_receiver: InboxReceiver,
}
#[derive(Clone)]
pub struct TaskContext {
pub task_id: TaskId,
pub cancel_token: CancellationToken,
pub(crate) inbox: Option<InboxSender>,
}
impl TaskContext {
pub fn emit(&self, event_type: &str, payload: serde_json::Value) -> bool {
let event = TaskEvent::Custom {
task_id: self.task_id.clone(),
event_type: event_type.to_string(),
payload,
};
match &self.inbox {
Some(s) => {
s.send(serde_json::to_value(&event).expect("TaskEvent serialization is infallible"))
}
None => false,
}
}
pub async fn cancelled(&self) {
self.cancel_token.cancelled().await;
}
pub fn is_cancelled(&self) -> bool {
self.cancel_token.is_cancelled()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum TaskEvent {
Completed {
task_id: TaskId,
result: Option<serde_json::Value>,
},
Failed { task_id: TaskId, error: String },
Cancelled { task_id: TaskId },
Custom {
task_id: TaskId,
event_type: String,
payload: serde_json::Value,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskSummary {
pub task_id: TaskId,
pub task_type: String,
pub description: String,
pub status: TaskStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
pub created_at_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completed_at_ms: Option<u64>,
#[serde(default, skip_serializing_if = "TaskParentContext::is_empty")]
pub parent_context: TaskParentContext,
}