use async_openai::types::chat::*; use chrono::Duration; use serde::{Deserialize, Serialize}; use sqlite::OpenFlags; use crate::prediction::{GeneratedResponses, PossibleResponse}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ConversationEntry { User(String), Eva(String), ShipComputer(String), StageDirection(String), SystemMessage(String) } impl TryInto for ConversationEntry { fn try_into(self) -> Result { match self { ConversationEntry::User(text) => Ok(ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage { content: text.into(), ..Default::default()})), ConversationEntry::Eva(text) => Ok(ChatCompletionRequestMessage::Assistant(ChatCompletionRequestAssistantMessage { content: Some(text.into()), ..Default::default()})), ConversationEntry::ShipComputer(text) => Ok(ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: text.into(), name: Some("ship-computer".into()), ..Default::default() })), ConversationEntry::StageDirection(text) => Ok(ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: text.into(), name: Some("stage-direction".into()), ..Default::default() })), ConversationEntry::SystemMessage(_) => Err(()) } } type Error = (); } impl TryInto for ChatCompletionRequestMessage { fn try_into(self) -> Result { match self { ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage { content: ChatCompletionRequestUserMessageContent::Text(msg), ..}) => Ok(ConversationEntry::User(msg)), ChatCompletionRequestMessage::Assistant(ChatCompletionRequestAssistantMessage { content: Some(ChatCompletionRequestAssistantMessageContent::Text(msg)), ..}) => Ok(ConversationEntry::Eva(msg)), ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: ChatCompletionRequestSystemMessageContent::Text(msg), name: Some(name), ..}) => { match name.as_str() { "ship-computer" => Ok(ConversationEntry::ShipComputer(msg)), "stage-direction" => Ok(ConversationEntry::StageDirection(msg)), _ => Err(()) } }, _ => Err(()) } } type Error = (); } #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct StageDirection { pub episode_number: u32, pub time_remaining: Duration, pub narrative: String, pub artifacts: Vec, pub current_playlist: Vec } #[derive(Debug)] pub enum MixxxError { Sql(sqlite::Error) } impl From for MixxxError { fn from(value: sqlite::Error) -> Self { Self::Sql(value) } } impl StageDirection { pub fn reload_mixxx_playlist(&mut self) -> Result<(), MixxxError> { self.current_playlist.clear(); let connection = sqlite::Connection::open_thread_safe_with_flags("mixxxdb.sqlite", OpenFlags::new().with_read_only())?; let query = "SELECT id FROM Playlists WHERE name = ? ORDER BY id DESC LIMIT 1"; let mut statement = connection.prepare(query)?; statement.bind((1, format!("BFF.fm - Episode {}", self.episode_number).as_str()))?; statement.next()?; let latest_id = statement.read::("id").unwrap(); let query = "SELECT title, artist, album, comment, url, bpm FROM library LEFT JOIN PlaylistTracks ON PlaylistTracks.track_id = library.id WHERE PlaylistTracks.playlist_id = ? ORDER BY position"; for track in connection.prepare(query).unwrap().into_iter().bind((1, latest_id)).unwrap().map(|row| row.unwrap()) { let title = track.try_read::<&str, _>("title").unwrap_or("Untitled Track"); let artist = track.try_read::<&str, _>("artist").unwrap_or("Unknown Artist"); let album = track.try_read::<&str, _>("album").unwrap_or("Unknown Album"); let bpm = track.try_read::("bpm").unwrap_or(0.); self.current_playlist.push(PlaylistEntry { artist: artist.into(), album: album.into(), title: title.into(), bpm }); } Ok(()) } } #[derive(Debug, Default, Clone)] pub struct StageActions { pub direction: StageDirection, pub additions: Vec } #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct PlaylistEntry { pub artist: String, pub album: String, pub title: String, pub bpm: f64 } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Scene { reply_options: GeneratedResponses, conversation: Vec, } impl Scene { pub fn new(reply_options: GeneratedResponses, conversation: Vec) -> Self { Self { reply_options, conversation, } } pub fn conversation(&self) -> &Vec { &self.conversation } pub fn conversation_mut(&mut self) -> &mut Vec { &mut self.conversation } pub fn reply_options(&self) -> &Vec { &self.reply_options.responses } pub fn reply_options_mut(&mut self) -> &mut Vec { &mut self.reply_options.responses } }