More UI stuff

This commit is contained in:
MrLetsplay 2024-02-07 22:02:11 +01:00
parent d90151ee5f
commit 47e17e4d42
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg

View File

@ -1,8 +1,12 @@
mod api; mod api;
use std::{ use std::{
cell::RefCell,
io::{self, Write}, io::{self, Write},
sync::Arc, sync::{
mpsc::{Receiver, Sender},
Arc,
},
}; };
use api::OllamaAPI; use api::OllamaAPI;
@ -10,15 +14,13 @@ use iced::{
alignment::{Horizontal, Vertical}, alignment::{Horizontal, Vertical},
futures::StreamExt, futures::StreamExt,
widget::{button, column, container, row, scrollable, text, text_input}, widget::{button, column, container, row, scrollable, text, text_input},
window::{self}, window, Application, Command, Length, Settings, Subscription, Theme,
Application, Command, Length, Settings, Theme,
}; };
use tokio::sync::RwLock; use tokio::sync::RwLock;
use crate::api::OllamaChat; use crate::api::OllamaChat;
enum UI { enum UI {
Loading,
Error(String), Error(String),
Chats(UIState), Chats(UIState),
} }
@ -28,15 +30,23 @@ struct UIState {
active_chat: Option<usize>, active_chat: Option<usize>,
chat_input: String, chat_input: String,
busy: bool, busy: bool,
send: Sender<UIMessage>,
recv: RefCell<Option<Receiver<UIMessage>>>,
} }
impl UIState { impl UIState {
fn create(api_url: &str) -> anyhow::Result<Self> { fn create(
api_url: &str,
send: Sender<UIMessage>,
recv: Receiver<UIMessage>,
) -> anyhow::Result<Self> {
Ok(Self { Ok(Self {
ollama_api: Arc::new(RwLock::new(OllamaAPI::create(api_url)?)), ollama_api: Arc::new(RwLock::new(OllamaAPI::create(api_url)?)),
active_chat: None, active_chat: None,
chat_input: String::from(""), chat_input: String::from(""),
busy: false, busy: false,
send,
recv: RefCell::new(Some(recv)),
}) })
} }
} }
@ -44,7 +54,6 @@ impl UIState {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum UIMessage { enum UIMessage {
Nop, Nop,
LoadDone,
CreateChat, CreateChat,
OpenChat(usize), OpenChat(usize),
ChatInput(String), ChatInput(String),
@ -55,42 +64,39 @@ enum UIMessage {
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
println!("Making request"); println!("Making request");
UI::run(Settings { let (send, recv) = std::sync::mpsc::channel();
window: window::Settings { let mut settings = Settings::with_flags(UIFlags { send, recv });
size: (720, 480), settings.window = window::Settings {
..window::Settings::default() size: (720, 480),
}, ..window::Settings::default()
..Settings::default() };
})?;
UI::run(settings)?;
Ok(()) Ok(())
} }
struct UIFlags {
send: Sender<UIMessage>,
recv: Receiver<UIMessage>,
}
impl Application for UI { impl Application for UI {
type Executor = iced::executor::Default; type Executor = iced::executor::Default;
type Message = UIMessage; type Message = UIMessage;
type Theme = Theme; type Theme = Theme;
type Flags = (); type Flags = UIFlags;
fn new(_flags: Self::Flags) -> (Self, iced::Command<Self::Message>) { fn new(flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
( let state: UI;
UI::Loading, let state_result = UIState::create("http://localhost:11434", flags.send, flags.recv);
Command::perform( if let Ok(s) = state_result {
async { state = UI::Chats(s);
/*let api = OllamaAPI::create("http://localhost:11434").unwrap(); } else {
let mut chat = api.create_chat("dolphin-mixtral"); state = UI::Error(String::from("Failed to initialize Ollama API"));
chat.send_system("You are a bot that only replies with \"Hello\" and nothing else. You must never reply with anything other than \"Hello\""); }
chat.send_user("Who are you?");
// println!("{}", amogus.get().await?);
let mut a = chat.complete().await.unwrap(); (state, Command::none())
while let Some(v) = a.next().await {
println!("GOT = {:?}", v);
}*/
},
|_| UIMessage::LoadDone,
),
)
} }
fn title(&self) -> String { fn title(&self) -> String {
@ -99,18 +105,6 @@ impl Application for UI {
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> { fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
match self { match self {
UI::Loading => match message {
UIMessage::LoadDone => {
let state = UIState::create("http://localhost:11434");
if let Ok(state) = state {
*self = UI::Chats(state);
} else {
*self = UI::Error(String::from("Failed to initialize Ollama API"));
}
Command::none()
}
_ => Command::none(),
},
UI::Chats(state) => match message { UI::Chats(state) => match message {
UIMessage::CreateChat => { UIMessage::CreateChat => {
let chat = state let chat = state
@ -139,6 +133,7 @@ impl Application for UI {
let content = state.chat_input.clone(); let content = state.chat_input.clone();
state.chat_input = String::from(""); state.chat_input = String::from("");
state.busy = true; state.busy = true;
let send = state.send.clone();
let ollama_api = state.ollama_api.clone(); let ollama_api = state.ollama_api.clone();
return Command::perform( return Command::perform(
async move { async move {
@ -150,6 +145,8 @@ impl Application for UI {
chat = api.chats[index].clone(); chat = api.chats[index].clone();
let mut chat = chat.write().await; let mut chat = chat.write().await;
chat.send_user(&content); chat.send_user(&content);
chat.current_message =
Some(Arc::new(RwLock::new(String::from(""))));
} }
//let mut chat = chat.write().await; //let mut chat = chat.write().await;
@ -164,18 +161,17 @@ impl Application for UI {
let content = content.unwrap(); let content = content.unwrap();
let chat = chat.clone(); let chat_arc = chat.clone();
let mut chat = chat.write().await; let chat = chat_arc.read().await;
let msg = chat.current_message.clone(); let msg = chat.current_message.clone();
if let Some(msg) = msg { if let Some(msg) = msg {
msg.write().await.push_str(&content); msg.write().await.push_str(&content);
} else {
chat.current_message =
Some(Arc::new(RwLock::new(String::from(&content))));
} }
print!("{}", content); print!("{}", content);
let _ = io::stdout().flush(); let _ = io::stdout().flush();
let _ = send.send(UIMessage::Nop);
} }
} }
//state.ollama_api.complete(&x); //state.ollama_api.complete(&x);
@ -198,12 +194,6 @@ impl Application for UI {
fn view(&self) -> iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>> { fn view(&self) -> iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>> {
match self { match self {
UI::Loading => text("Loading")
.width(Length::Fill)
.height(Length::Fill)
.vertical_alignment(Vertical::Center)
.horizontal_alignment(Horizontal::Center)
.into(),
UI::Error(message) => text(String::from("Error: ") + message) UI::Error(message) => text(String::from("Error: ") + message)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
@ -293,4 +283,24 @@ impl Application for UI {
fn theme(&self) -> Self::Theme { fn theme(&self) -> Self::Theme {
Theme::Dark Theme::Dark
} }
fn subscription(&self) -> iced::Subscription<Self::Message> {
match self {
UI::Chats(state) => iced::subscription::unfold(
"external messages",
state.recv.take(),
move |recv| async move {
let msg = recv.as_ref().unwrap().recv();
if let Err(_) = msg {
return (UIMessage::Nop, recv);
}
let msg = msg.unwrap();
(msg, recv)
},
),
_ => Subscription::none(),
}
}
} }