use async_openai::types::chat::*; use chrono::Duration; use schemars::schema_for; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::GeneratedResponses; const SYSTEM_PROMPT: &str = include_str!("system-prompt.txt"); #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ConversationEntry { User(String), Eva(String), ShipComputer(String), StageDirection(String), SystemMessage(String) } #[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, 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 { pub conversation: Vec, pub direction: StageDirection } impl Scene { pub fn insert_conversation(&mut self, entry: ConversationEntry) { self.conversation.push(entry); } } impl Into for Scene { fn into(self) -> CreateChatCompletionRequest { let mut messages = vec![ ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: SYSTEM_PROMPT.into(), ..Default::default()}), ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: serde_json::to_string(&self.direction).unwrap().into(), ..Default::default()}), ]; messages.extend(self.conversation.into_iter().filter(|x| if let ConversationEntry::SystemMessage(_) = x { false } else { true }).map(|entry| { match entry { ConversationEntry::User(text) => ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage { content: text.into(), ..Default::default()}), ConversationEntry::Eva(text) => ChatCompletionRequestMessage::Assistant(ChatCompletionRequestAssistantMessage { content: Some(text.into()), ..Default::default()}), ConversationEntry::ShipComputer(text) => ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: text.into(), name: Some("ship-computer".into()), ..Default::default() }), ConversationEntry::StageDirection(text) => ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage { content: text.into(), name: Some("stage-direction".into()), ..Default::default() }), ConversationEntry::SystemMessage(_) => unreachable!() } })); let response_schema: Value = schema_for!(GeneratedResponses).into(); CreateChatCompletionRequest { model: "gpt-5.4".into(), messages: messages, max_completion_tokens: Some(350), response_format: Some(ResponseFormat::JsonSchema { json_schema: ResponseFormatJsonSchema { description: None, name: "responses".into(), schema: response_schema, strict: None } }), ..Default::default() } } }