Enhance state direction command with ship computer outputs, and report token burn on the UI
This commit is contained in:
+13
-53
@@ -13,7 +13,7 @@ use futures::{StreamExt, future::FutureExt};
|
||||
use ratatui::prelude::*;
|
||||
use tui_skeleton::{AnimationMode, SkeletonText};
|
||||
|
||||
use crate::{audio::{AudioInputControl, start_audio_input}, prediction::{BandcampResult, PossibleResponse}, scene::{ConversationEntry, Scene, StageActions, StageDirection}, transcription::TranscriptionControl, tts::{TtsControl, start_tts}};
|
||||
use crate::{audio::{AudioInputControl, start_audio_input}, prediction::{BandcampResult, PossibleResponse}, scene::{ConversationEntry, Scene, Scenery, StageActions, StageDirection}, transcription::TranscriptionControl, tts::{TtsControl, start_tts}};
|
||||
|
||||
mod scene;
|
||||
mod events;
|
||||
@@ -94,24 +94,6 @@ enum FocusState {
|
||||
UserInput
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum BandcampError {
|
||||
Json(serde_json::Error),
|
||||
Api(bandcamp::Error)
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for BandcampError {
|
||||
fn from(value: serde_json::Error) -> Self {
|
||||
BandcampError::Json(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bandcamp::Error> for BandcampError {
|
||||
fn from(value: bandcamp::Error) -> Self {
|
||||
BandcampError::Api(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(prediction_request_sink: watch::Sender<StageActions>, audio: AudioInputControl, transcription: TranscriptionControl, tts: TtsControl, sys_message_sink: mpsc::Sender<String>, initial_direction: StageDirection) -> Self {
|
||||
Self {
|
||||
@@ -292,6 +274,9 @@ impl App {
|
||||
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||
Span::from(format!("Time Remaining: {}", formatted_time)).style(time_style),
|
||||
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||
Span::from(format!("{} artifacts recorded", self.scene.scenery().artifacts.len())).style(ratatui::style::Color::LightBlue),
|
||||
Span::from(" | ").style(ratatui::style::Color::DarkGray),
|
||||
Span::from(format!("{} tokens sacrificed", self.scene.tokens_consumed)).style(ratatui::style::Color::LightCyan),
|
||||
]);
|
||||
frame.render_widget(status_line, area);
|
||||
}
|
||||
@@ -411,15 +396,7 @@ impl App {
|
||||
let command = parts.next().unwrap();
|
||||
let arg = parts.next().unwrap_or("");
|
||||
match command {
|
||||
"/bandcamp" => {
|
||||
if let Err(err) = self.add_bandcamp_artifact(arg).await {
|
||||
self.sys_message_sink.send(format!("Failed to fetch artifact: {:?}", err)).await.unwrap();
|
||||
} else {
|
||||
self.sys_message_sink.send(format!("Added Bandcamp artifact from {}", arg)).await.unwrap();
|
||||
self.next_actions.push(ConversationEntry::ShipComputer(format!("Incoming transmission from {}", arg)));
|
||||
self.regenerate_responses();
|
||||
}
|
||||
},
|
||||
// FIXME: Need some new kind of /bandcamp command to force loading of specific urls
|
||||
"/episode" => {
|
||||
if let Ok(episode_number) = arg.trim().parse::<u32>() {
|
||||
self.direction.episode_number = episode_number;
|
||||
@@ -438,21 +415,10 @@ impl App {
|
||||
self.sys_message_sink.send("Invalid timer format. Use /timer [minutes]".into()).await.unwrap();
|
||||
}
|
||||
},
|
||||
"/clear" => {
|
||||
match arg.trim() {
|
||||
"artifacts" => {
|
||||
self.direction.artifacts.clear();
|
||||
self.sys_message_sink.send("Cleared artifacts.".into()).await.unwrap();
|
||||
},
|
||||
_ => {
|
||||
self.sys_message_sink.send("Unknown clear command. Use /clear [artifacts]".into()).await.unwrap();
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
"/narrative" => {
|
||||
self.direction.narrative = arg.to_string();
|
||||
self.sys_message_sink.send(format!("Updated stage direction: {}", self.direction.narrative)).await.unwrap();
|
||||
self.regenerate_responses();
|
||||
},
|
||||
"/event" => {
|
||||
self.next_actions.push(ConversationEntry::StageDirection(arg.to_string()));
|
||||
@@ -463,7 +429,7 @@ impl App {
|
||||
self.regenerate_responses();
|
||||
},
|
||||
_ => {
|
||||
self.sys_message_sink.send("Unknown command. Available commands: /bandcamp [url], /episode [number], /narrative [text], /reset".into()).await.unwrap();
|
||||
self.sys_message_sink.send("Unknown command. Available commands: /episode [number], /narrative [text], /event [text], /computer [text], /timer [minutes]".into()).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,6 +442,7 @@ impl App {
|
||||
KeyCode::Tab => {
|
||||
self.focus_state = FocusState::UserInput;
|
||||
self.conversation_state.select(None);
|
||||
self.reply_state.select_first();
|
||||
},
|
||||
KeyCode::PageUp => self.conversation_state.scroll_down_by(5),
|
||||
KeyCode::PageDown => self.conversation_state.scroll_up_by(5),
|
||||
@@ -489,6 +456,7 @@ impl App {
|
||||
self.tts.speak(text.clone()).await;
|
||||
self.focus_state = FocusState::UserInput;
|
||||
self.conversation_state.select(None);
|
||||
self.reply_state.select_first();
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
@@ -499,6 +467,7 @@ impl App {
|
||||
KeyCode::Tab => {
|
||||
self.focus_state = FocusState::Conversation;
|
||||
self.conversation_state.select_first();
|
||||
self.reply_state.select(None);
|
||||
},
|
||||
KeyCode::Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => self.regenerate_responses(),
|
||||
KeyCode::Char('x') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
@@ -536,16 +505,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_bandcamp_artifact(&mut self, url: &str) -> Result<(), BandcampError> {
|
||||
let album = bandcamp::album_from_url(url).await?;
|
||||
let result: BandcampResult = album.into();
|
||||
let json = serde_json::to_string(&result)?;
|
||||
self.direction.artifacts.push(json);
|
||||
self.sys_message_sink.send("Added bandcamp album".into()).await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn regenerate_responses(&mut self) {
|
||||
let actions = StageActions {
|
||||
direction: self.direction.clone(),
|
||||
@@ -570,7 +529,8 @@ impl App {
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct SaveData {
|
||||
pub direction: StageDirection,
|
||||
pub messages: Vec<ChatCompletionRequestMessage>
|
||||
pub messages: Vec<ChatCompletionRequestMessage>,
|
||||
pub scenery: Scenery
|
||||
}
|
||||
|
||||
impl SaveData {
|
||||
@@ -619,7 +579,7 @@ async fn main() {
|
||||
|
||||
let (audio_ctrl, mic_stream, tts_output) = start_audio_input(&sys_message_sink).await;
|
||||
let tts_ctrl = start_tts(tts_output).await;
|
||||
let (prediction_request_in, mut prediction_out) = prediction::start_prediction(sys_message_src, saved_session.messages).await;
|
||||
let (prediction_request_in, mut prediction_out) = prediction::start_prediction(sys_message_src, saved_session.messages, saved_session.scenery).await;
|
||||
let transcription_ctrl = transcription::start_transcription(mic_stream).await;
|
||||
|
||||
let mut app = App::new(prediction_request_in, audio_ctrl, transcription_ctrl, tts_ctrl, sys_message_sink, saved_session.direction);
|
||||
|
||||
Reference in New Issue
Block a user