Files
eva-pwm-cohost/src/scene.rs
T
2026-06-08 10:26:50 +02:00

142 lines
5.4 KiB
Rust

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<ChatCompletionRequestMessage> for ConversationEntry {
fn try_into(self) -> Result<ChatCompletionRequestMessage, Self::Error> {
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<ConversationEntry> for ChatCompletionRequestMessage {
fn try_into(self) -> Result<ConversationEntry, Self::Error> {
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<String>,
pub current_playlist: Vec<PlaylistEntry>
}
#[derive(Debug)]
pub enum MixxxError {
Sql(sqlite::Error)
}
impl From<sqlite::Error> 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::<i64, _>("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::<f64, _>("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<ConversationEntry>
}
#[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<ConversationEntry>,
}
impl Scene {
pub fn new(reply_options: GeneratedResponses, conversation: Vec<ConversationEntry>) -> Self {
Self {
reply_options,
conversation,
}
}
pub fn conversation(&self) -> &Vec<ConversationEntry> {
&self.conversation
}
pub fn conversation_mut(&mut self) -> &mut Vec<ConversationEntry> {
&mut self.conversation
}
pub fn reply_options(&self) -> &Vec<PossibleResponse> {
&self.reply_options.responses
}
pub fn reply_options_mut(&mut self) -> &mut Vec<PossibleResponse> {
&mut self.reply_options.responses
}
}