switch to flume for 🚀

master
Tymon 1 year ago
parent 8794e519cf
commit d1a11ac5e2

54
Cargo.lock generated

@ -126,6 +126,7 @@ dependencies = [
"chrono",
"eframe",
"egui-notify",
"flume",
"once_cell",
"reqwest",
"ron",
@ -959,6 +960,19 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "flume"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
dependencies = [
"futures-core",
"futures-sink",
"nanorand",
"pin-project",
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1080,8 +1094,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@ -1550,6 +1566,15 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "nanorand"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
]
[[package]]
name = "native-tls"
version = "0.2.11"
@ -1880,6 +1905,26 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pin-project"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -2345,6 +2390,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "spin"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef"
dependencies = [
"lock_api",
]
[[package]]
name = "static_assertions"
version = "1.1.0"

@ -10,6 +10,7 @@ anyhow = "1.0.70"
chrono = { version = "0.4.24", features = ["serde"] }
eframe = "0.21.3"
egui-notify = "0.6.0"
flume = "0.10.14"
once_cell = "1.17.1"
reqwest = { version = "0.11.15", features = ["json"] }
ron = "0.8.0"

@ -1,8 +1,9 @@
use std::{collections::HashMap, sync::mpsc::Sender};
use std::collections::HashMap;
use crate::config::{ParamType, AI};
use anyhow::{Context, Result};
use flume::Sender;
use once_cell::sync::Lazy;
use reqwest::Client;
use serde::{Deserialize, Serialize};

@ -1,241 +1,240 @@
use std::{
sync::mpsc::{self, Receiver, Sender},
time::Duration,
};
use chrono::Local;
use eframe::{
egui::{
self, CentralPanel, ComboBox, DragValue, Grid, Layout, ScrollArea, Sense, Slider, TextEdit,
Window,
},
emath::Align,
epaint::Vec2,
};
use egui_notify::Toasts;
use crate::{
ai::{generate_with_mpsc, Params, Response, ResponseType},
config::{AI, CONFIG},
history::{read_all_history, write_history, HistoryEntry},
};
pub struct App {
toasts: Toasts,
output: String,
selected_ai: AI,
params: Params,
history_open: bool,
history: Vec<HistoryEntry>,
open_history_entry: Option<HistoryEntry>,
tx: Sender<Response>,
rx: Receiver<Response>,
}
impl Default for App {
fn default() -> Self {
let (tx, rx) = mpsc::channel();
Self {
toasts: Default::default(),
output: "Awaiting generation...".to_string(),
selected_ai: CONFIG.ai[0].clone(),
params: Default::default(),
history_open: false,
history: vec![],
open_history_entry: None,
tx,
rx,
}
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if let Ok(recieved) = self.rx.try_recv() {
if recieved.response_type == ResponseType::Success {
self.output = recieved.output.trim().to_string();
} else {
self.output = "Failed to generate!".to_string();
self.toasts
.error("Failed to generate! Error printed to console.");
eprintln!("{}", recieved.output);
}
match write_history(HistoryEntry {
date: Local::now(),
selected_ai: self.selected_ai.name.clone(),
params: self.params.clone(),
result: self.output.clone(),
}) {
Ok(_) => {}
Err(error) => {
self.toasts
.error(format!("Failed to write history! {}", error));
}
}
}
CentralPanel::default().show(ctx, |ui| {
Window::new("History")
.open(&mut self.history_open)
.collapsible(false)
.hscroll(true)
.show(ctx, |ui| {
if let Some(entry) = self.open_history_entry.clone() {
ui.set_max_width(580.0);
ui.horizontal(|ui| {
// maybe make these param indicators look more like the main ui somehow
ui.label(format!("{} - AI", entry.selected_ai));
ui.separator();
ui.label(format!(
"{} - Temperature",
entry.params.temperature.to_string()
));
ui.separator();
ui.label(format!(
"{} - Max Tokens",
entry.params.max_tokens.to_string()
));
ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
if ui.button("Back").clicked() {
self.open_history_entry = None;
}
});
});
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
ScrollArea::vertical()
.id_source("history_prompt_scroll")
.show(ui, |ui| {
ui.add(TextEdit::multiline(&mut entry.params.prompt.as_str()));
});
ScrollArea::vertical()
.id_source("history_output_scroll")
.show(ui, |ui| {
ui.add(TextEdit::multiline(&mut entry.result.as_str()));
});
})
.response
.rect
.width();
} else {
ScrollArea::vertical()
.id_source("history_scroll")
.show(ui, |ui| {
Grid::new("history_grid").num_columns(1).show(ui, |ui| {
for entry in &self.history {
let response = ui
.horizontal(|ui| {
ui.label(&entry.selected_ai);
ui.label(
entry
.date
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
)
})
.response;
let clickable =
ui.allocate_rect(response.rect, Sense::click());
if clickable.clicked() {
self.open_history_entry = Some(entry.clone());
}
ui.end_row()
}
});
});
}
});
ui.vertical(|ui| {
if ui.button("Generate!").clicked() {
self.output = "Generating...".to_string();
generate_with_mpsc(
self.selected_ai.clone(),
self.params.clone(),
self.tx.clone(),
)
}
ui.horizontal(|ui| {
ComboBox::from_label("AI")
.selected_text(&self.selected_ai.name)
.show_ui(ui, |ui| {
for ai in &CONFIG.ai {
ui.selectable_value(&mut self.selected_ai, ai.clone(), &ai.name);
}
});
ui.separator();
ui.add(
Slider::new(&mut self.params.temperature, 0.0..=2.0)
.text("Temperature")
.show_value(true),
);
ui.separator();
ui.add(DragValue::new(&mut self.params.max_tokens));
ui.label("Max Tokens");
ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
ui.add_visible_ui(CONFIG.app.history_dir.is_some(), |ui| {
if ui.button("History").clicked() {
match read_all_history() {
Ok(history) => self.history = history,
Err(error) => {
self.toasts.error(error.to_string());
}
}
self.history_open = true;
}
});
});
});
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
let size = ui.available_size();
ScrollArea::vertical()
.id_source("prompt_scroll")
.show(ui, |ui| {
ui.add_sized(
Vec2::new(size.x / 2.0, size.y),
TextEdit::multiline(&mut self.params.prompt),
);
});
ScrollArea::vertical()
.id_source("output_scroll")
.show(ui, |ui| {
ui.add_sized(
Vec2::new(size.x / 2.0, size.y),
TextEdit::multiline(&mut self.output.as_str()),
);
});
});
});
self.toasts.show(ctx);
ctx.request_repaint_after(Duration::from_secs(1))
});
}
}
use std::time::Duration;
use flume::{self, Receiver, Sender};
use chrono::Local;
use eframe::{
egui::{
self, CentralPanel, ComboBox, DragValue, Grid, Layout, ScrollArea, Sense, Slider, TextEdit,
Window,
},
emath::Align,
epaint::Vec2,
};
use egui_notify::Toasts;
use crate::{
ai::{generate_with_mpsc, Params, Response, ResponseType},
config::{AI, CONFIG},
history::{read_all_history, write_history, HistoryEntry},
};
pub struct App {
toasts: Toasts,
output: String,
selected_ai: AI,
params: Params,
history_open: bool,
history: Vec<HistoryEntry>,
open_history_entry: Option<HistoryEntry>,
tx: Sender<Response>,
rx: Receiver<Response>,
}
impl Default for App {
fn default() -> Self {
let (tx, rx) = flume::unbounded();
Self {
toasts: Default::default(),
output: "Awaiting generation...".to_string(),
selected_ai: CONFIG.ai[0].clone(),
params: Default::default(),
history_open: false,
history: vec![],
open_history_entry: None,
tx,
rx,
}
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if let Ok(recieved) = self.rx.try_recv() {
if recieved.response_type == ResponseType::Success {
self.output = recieved.output.trim().to_string();
} else {
self.output = "Failed to generate!".to_string();
self.toasts
.error("Failed to generate! Error printed to console.");
eprintln!("{}", recieved.output);
}
match write_history(HistoryEntry {
date: Local::now(),
selected_ai: self.selected_ai.name.clone(),
params: self.params.clone(),
result: self.output.clone(),
}) {
Ok(_) => {}
Err(error) => {
self.toasts
.error(format!("Failed to write history! {}", error));
}
}
}
CentralPanel::default().show(ctx, |ui| {
Window::new("History")
.open(&mut self.history_open)
.collapsible(false)
.hscroll(true)
.show(ctx, |ui| {
if let Some(entry) = self.open_history_entry.clone() {
ui.set_max_width(580.0);
ui.horizontal(|ui| {
// maybe make these param indicators look more like the main ui somehow
ui.label(format!("{} - AI", entry.selected_ai));
ui.separator();
ui.label(format!(
"{} - Temperature",
entry.params.temperature.to_string()
));
ui.separator();
ui.label(format!(
"{} - Max Tokens",
entry.params.max_tokens.to_string()
));
ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
if ui.button("Back").clicked() {
self.open_history_entry = None;
}
});
});
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
ScrollArea::vertical()
.id_source("history_prompt_scroll")
.show(ui, |ui| {
ui.add(TextEdit::multiline(&mut entry.params.prompt.as_str()));
});
ScrollArea::vertical()
.id_source("history_output_scroll")
.show(ui, |ui| {
ui.add(TextEdit::multiline(&mut entry.result.as_str()));
});
})
.response
.rect
.width();
} else {
ScrollArea::vertical()
.id_source("history_scroll")
.show(ui, |ui| {
Grid::new("history_grid").num_columns(1).show(ui, |ui| {
for entry in &self.history {
let response = ui
.horizontal(|ui| {
ui.label(&entry.selected_ai);
ui.label(
entry
.date
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
)
})
.response;
let clickable =
ui.allocate_rect(response.rect, Sense::click());
if clickable.clicked() {
self.open_history_entry = Some(entry.clone());
}
ui.end_row()
}
});
});
}
});
ui.vertical(|ui| {
if ui.button("Generate!").clicked() {
self.output = "Generating...".to_string();
generate_with_mpsc(
self.selected_ai.clone(),
self.params.clone(),
self.tx.clone(),
)
}
ui.horizontal(|ui| {
ComboBox::from_label("AI")
.selected_text(&self.selected_ai.name)
.show_ui(ui, |ui| {
for ai in &CONFIG.ai {
ui.selectable_value(&mut self.selected_ai, ai.clone(), &ai.name);
}
});
ui.separator();
ui.add(
Slider::new(&mut self.params.temperature, 0.0..=2.0)
.text("Temperature")
.show_value(true),
);
ui.separator();
ui.add(DragValue::new(&mut self.params.max_tokens));
ui.label("Max Tokens");
ui.with_layout(Layout::right_to_left(Align::Max), |ui| {
ui.add_visible_ui(CONFIG.app.history_dir.is_some(), |ui| {
if ui.button("History").clicked() {
match read_all_history() {
Ok(history) => self.history = history,
Err(error) => {
self.toasts.error(error.to_string());
}
}
self.history_open = true;
}
});
});
});
ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
let size = ui.available_size();
ScrollArea::vertical()
.id_source("prompt_scroll")
.show(ui, |ui| {
ui.add_sized(
Vec2::new(size.x / 2.0, size.y),
TextEdit::multiline(&mut self.params.prompt),
);
});
ScrollArea::vertical()
.id_source("output_scroll")
.show(ui, |ui| {
ui.add_sized(
Vec2::new(size.x / 2.0, size.y),
TextEdit::multiline(&mut self.output.as_str()),
);
});
});
});
self.toasts.show(ctx);
ctx.request_repaint_after(Duration::from_secs(1))
});
}
}