use std::path::PathBuf;
use tokio::sync::mpsc;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SessionContext {
pub chat_id: i64,
pub message_thread_id: Option<i32>,
}
impl std::fmt::Display for SessionContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.message_thread_id {
Some(tid) => write!(f, "{}::{}", self.chat_id, tid),
None => write!(f, "{}", self.chat_id),
}
}
}
#[derive(Debug, Clone)]
pub struct SessionInfo {
pub session_id: String,
pub session_path: PathBuf,
pub workspace: PathBuf,
pub model: Option<String>,
pub session_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct PromptResponse {
pub text: String,
pub tool_calls: Vec<ToolCallRecord>,
}
#[derive(Debug, Clone)]
pub struct ToolCallRecord {
pub tool_name: String,
pub tool_call_id: String,
pub output: Option<String>,
pub is_error: bool,
}
#[derive(Debug, Clone)]
pub struct SessionStats {
pub session_id: String,
pub total_messages: usize,
pub tokens_in: u64,
pub tokens_out: u64,
pub cost: f64,
}
#[derive(Debug, Clone)]
pub enum PiEvent {
ThinkingDelta { delta: String },
TextDelta { delta: String },
ToolStart { tool_name: String, tool_call_id: String },
ToolOutput { tool_call_id: String, output: String, is_error: bool },
ToolEnd { tool_call_id: String },
Usage { tokens_in: u64, tokens_out: u64, cost: f64, model: String },
TurnEnd { text: String },
Error { message: String },
}
#[async_trait::async_trait]
pub trait PiSession: Send + Sync {
fn info(&self) -> SessionInfo;
async fn stats(&self) -> SessionStats;
async fn prompt(&self, text: &str) -> crate::error::Result<PromptResponse>;
async fn prompt_with_images(
&self,
text: &str,
images: &[PathBuf],
) -> crate::error::Result<PromptResponse>;
async fn prompt_streaming(
&self,
text: &str,
tx: mpsc::Sender<PiEvent>,
) -> crate::error::Result<PromptResponse>;
async fn abort(&self) -> crate::error::Result<()>;
async fn set_model(&self, model: &str) -> crate::error::Result<()>;
async fn dispose(&self) -> crate::error::Result<()>;
}