Clean up code, Per chat text input box state, Improve UI, Add launch config
This commit is contained in:
parent
db80840c14
commit
4ae2dc55d8
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'icedtest'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=icedtest",
|
||||||
|
"--package=icedtest"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "icedtest",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'icedtest'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=icedtest",
|
||||||
|
"--package=icedtest"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "icedtest",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -29,7 +29,7 @@ pub struct OllamaChat {
|
|||||||
pub messages: Vec<ChatMessage>,
|
pub messages: Vec<ChatMessage>,
|
||||||
|
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub current_message: Option<Arc<RwLock<String>>>,
|
pub generating_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
@ -79,7 +79,7 @@ impl OllamaAPI {
|
|||||||
api_url: self.api_url.clone(),
|
api_url: self.api_url.clone(),
|
||||||
model: String::from(model),
|
model: String::from(model),
|
||||||
messages: Vec::new(),
|
messages: Vec::new(),
|
||||||
current_message: None,
|
generating_message: None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
self.chats.push(chat.clone());
|
self.chats.push(chat.clone());
|
||||||
@ -115,8 +115,7 @@ impl OllamaChat {
|
|||||||
let body = serde_json::to_string(chat.deref())?;
|
let body = serde_json::to_string(chat.deref())?;
|
||||||
println!("Sending: {}", body);
|
println!("Sending: {}", body);
|
||||||
|
|
||||||
let msg = Arc::new(RwLock::new(String::from("")));
|
chat.generating_message = Some(String::new());
|
||||||
chat.current_message = Some(msg.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let chat = chat.read().await;
|
let chat = chat.read().await;
|
||||||
|
97
src/main.rs
97
src/main.rs
@ -3,7 +3,6 @@ mod api;
|
|||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
ops::Deref,
|
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{Receiver, Sender},
|
mpsc::{Receiver, Sender},
|
||||||
Arc,
|
Arc,
|
||||||
@ -14,8 +13,10 @@ use api::{ChatMessage, OllamaAPI};
|
|||||||
use iced::{
|
use iced::{
|
||||||
alignment::{Horizontal, Vertical},
|
alignment::{Horizontal, Vertical},
|
||||||
futures::StreamExt,
|
futures::StreamExt,
|
||||||
|
subscription,
|
||||||
widget::{button, column, container, row, scrollable, text, text_input},
|
widget::{button, column, container, row, scrollable, text, text_input},
|
||||||
window, Application, Color, Command, Length, Settings, Subscription, Theme,
|
window::{self},
|
||||||
|
Application, Color, Command, Event, Length, Settings, Subscription, Theme,
|
||||||
};
|
};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
@ -24,13 +25,13 @@ use crate::api::{OllamaChat, Role};
|
|||||||
enum UI {
|
enum UI {
|
||||||
Error(String),
|
Error(String),
|
||||||
Chats(UIState),
|
Chats(UIState),
|
||||||
|
Closing,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UIState {
|
struct UIState {
|
||||||
ollama_api: Arc<RwLock<OllamaAPI>>,
|
ollama_api: Arc<RwLock<OllamaAPI>>,
|
||||||
active_chat: Option<usize>,
|
active_chat: Option<usize>,
|
||||||
chat_input: String,
|
chat_input: String,
|
||||||
busy: bool,
|
|
||||||
send: Sender<UIMessage>,
|
send: Sender<UIMessage>,
|
||||||
recv: RefCell<Option<Receiver<UIMessage>>>,
|
recv: RefCell<Option<Receiver<UIMessage>>>,
|
||||||
}
|
}
|
||||||
@ -44,8 +45,7 @@ impl UIState {
|
|||||||
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::new(),
|
||||||
busy: false,
|
|
||||||
send,
|
send,
|
||||||
recv: RefCell::new(Some(recv)),
|
recv: RefCell::new(Some(recv)),
|
||||||
})
|
})
|
||||||
@ -55,6 +55,7 @@ impl UIState {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum UIMessage {
|
enum UIMessage {
|
||||||
Nop,
|
Nop,
|
||||||
|
EventOccurred(Event),
|
||||||
CreateChat,
|
CreateChat,
|
||||||
OpenChat(usize),
|
OpenChat(usize),
|
||||||
ChatInput(String),
|
ChatInput(String),
|
||||||
@ -63,10 +64,9 @@ enum UIMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
println!("Making request");
|
|
||||||
|
|
||||||
let (send, recv) = std::sync::mpsc::channel();
|
let (send, recv) = std::sync::mpsc::channel();
|
||||||
let mut settings = Settings::with_flags(UIFlags { send, recv });
|
let mut settings = Settings::with_flags(UIFlags { send, recv });
|
||||||
|
settings.exit_on_close_request = false;
|
||||||
settings.window = window::Settings {
|
settings.window = window::Settings {
|
||||||
size: (720, 480),
|
size: (720, 480),
|
||||||
..window::Settings::default()
|
..window::Settings::default()
|
||||||
@ -101,10 +101,19 @@ impl Application for UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
String::from("Hello World")
|
String::from("AIGUI")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
|
||||||
|
if let UIMessage::EventOccurred(event) = &message {
|
||||||
|
if let Event::Window(window::Event::CloseRequested) = event {
|
||||||
|
*self = UI::Closing;
|
||||||
|
return window::close();
|
||||||
|
} else {
|
||||||
|
return Command::none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
UI::Chats(state) => match message {
|
UI::Chats(state) => match message {
|
||||||
UIMessage::CreateChat => {
|
UIMessage::CreateChat => {
|
||||||
@ -132,8 +141,7 @@ impl Application for UI {
|
|||||||
|
|
||||||
if let Some(index) = state.active_chat {
|
if let Some(index) = state.active_chat {
|
||||||
let content = state.chat_input.clone();
|
let content = state.chat_input.clone();
|
||||||
state.chat_input = String::from("");
|
state.chat_input = String::new();
|
||||||
state.busy = true;
|
|
||||||
let send = state.send.clone();
|
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(
|
||||||
@ -146,8 +154,7 @@ 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 =
|
chat.generating_message = Some(String::new());
|
||||||
Some(Arc::new(RwLock::new(String::from(""))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//let mut chat = chat.write().await;
|
//let mut chat = chat.write().await;
|
||||||
@ -163,10 +170,10 @@ impl Application for UI {
|
|||||||
let content = content.unwrap();
|
let content = content.unwrap();
|
||||||
|
|
||||||
let chat_arc = chat.clone();
|
let chat_arc = chat.clone();
|
||||||
let chat = chat_arc.read().await;
|
let mut chat = chat_arc.write().await;
|
||||||
let msg = chat.current_message.clone();
|
let msg = &mut chat.generating_message;
|
||||||
if let Some(msg) = msg {
|
if let Some(msg) = msg {
|
||||||
msg.write().await.push_str(&content);
|
msg.push_str(&content);
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("{}", content);
|
print!("{}", content);
|
||||||
@ -176,14 +183,12 @@ impl Application for UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut chat = chat.write().await;
|
let mut chat = chat.write().await;
|
||||||
let msg = chat.current_message.clone();
|
let msg = &chat.generating_message.clone();
|
||||||
if let Some(msg) = msg {
|
if let Some(msg) = msg {
|
||||||
let msg = msg.write().await;
|
chat.send_assistant(&msg);
|
||||||
chat.send_assistant(msg.deref());
|
chat.generating_message = None;
|
||||||
chat.current_message = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//state.ollama_api.complete(&x);
|
|
||||||
},
|
},
|
||||||
|_| UIMessage::DoneGenerating,
|
|_| UIMessage::DoneGenerating,
|
||||||
);
|
);
|
||||||
@ -191,13 +196,11 @@ impl Application for UI {
|
|||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
UIMessage::DoneGenerating => {
|
UIMessage::DoneGenerating => Command::none(),
|
||||||
state.busy = false;
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
_ => Command::none(),
|
_ => Command::none(),
|
||||||
},
|
},
|
||||||
UI::Error(_) => Command::none(),
|
UI::Error(_) => Command::none(),
|
||||||
|
UI::Closing => Command::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,16 +213,13 @@ impl Application for UI {
|
|||||||
.horizontal_alignment(Horizontal::Center)
|
.horizontal_alignment(Horizontal::Center)
|
||||||
.into(),
|
.into(),
|
||||||
UI::Chats(state) => {
|
UI::Chats(state) => {
|
||||||
println!("Blocking");
|
|
||||||
let api = state.ollama_api.blocking_read();
|
let api = state.ollama_api.blocking_read();
|
||||||
println!("Blocing done!");
|
|
||||||
|
|
||||||
let mut chats: Vec<iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>>> =
|
let mut chats: Vec<iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>>> =
|
||||||
api.chats
|
api.chats
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|c| {
|
.map(|c| {
|
||||||
button("amogus")
|
button("Chat")
|
||||||
.on_press(UIMessage::OpenChat(c.0))
|
.on_press(UIMessage::OpenChat(c.0))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
@ -234,13 +234,14 @@ impl Application for UI {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let chats_list = container(column(chats).padding(10.0).spacing(10))
|
let chats_list = scrollable(container(column(chats).padding(10.0).spacing(10)))
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.width(Length::Fixed(300.0));
|
.width(Length::Fixed(300.0));
|
||||||
|
|
||||||
let mut messages: Vec<
|
let mut messages: Vec<
|
||||||
iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>>,
|
iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>>,
|
||||||
>;
|
>;
|
||||||
|
let mut input_box = text_input("Your message", &state.chat_input);
|
||||||
if let Some(chat_index) = state.active_chat {
|
if let Some(chat_index) = state.active_chat {
|
||||||
let chat = &api.chats[chat_index].blocking_read();
|
let chat = &api.chats[chat_index].blocking_read();
|
||||||
messages = chat
|
messages = chat
|
||||||
@ -249,19 +250,22 @@ impl Application for UI {
|
|||||||
.map(|x| self.render_message(x))
|
.map(|x| self.render_message(x))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(msg) = &chat.current_message {
|
if let Some(msg) = &chat.generating_message {
|
||||||
messages.push(text(msg.blocking_read()).into());
|
let mut msg = msg.clone();
|
||||||
}
|
msg.push_str("...");
|
||||||
|
let in_progress_message = ChatMessage {
|
||||||
|
role: Role::Assistant,
|
||||||
|
content: msg,
|
||||||
|
};
|
||||||
|
messages.push(self.render_message(&in_progress_message));
|
||||||
} else {
|
} else {
|
||||||
messages = Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut input_box = text_input("Your message", &state.chat_input);
|
|
||||||
if !state.busy {
|
|
||||||
input_box = input_box
|
input_box = input_box
|
||||||
.on_input(|x| UIMessage::ChatInput(x))
|
.on_input(|x| UIMessage::ChatInput(x))
|
||||||
.on_submit(UIMessage::ChatSubmit)
|
.on_submit(UIMessage::ChatSubmit)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
messages = Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
let active_chat = container(
|
let active_chat = container(
|
||||||
column![
|
column![
|
||||||
@ -277,14 +281,8 @@ impl Application for UI {
|
|||||||
.height(Length::Fill);
|
.height(Length::Fill);
|
||||||
|
|
||||||
container(row![chats_list, active_chat]).into()
|
container(row![chats_list, active_chat]).into()
|
||||||
|
|
||||||
/*scrollable(
|
|
||||||
container(column![content, amogus].spacing(20).max_width(480))
|
|
||||||
.padding(40)
|
|
||||||
.center_x(),
|
|
||||||
)
|
|
||||||
.into()*/
|
|
||||||
}
|
}
|
||||||
|
UI::Closing => text("Closing").into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,8 +291,10 @@ impl Application for UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> iced::Subscription<Self::Message> {
|
fn subscription(&self) -> iced::Subscription<Self::Message> {
|
||||||
|
let event_subscription = subscription::events().map(UIMessage::EventOccurred);
|
||||||
match self {
|
match self {
|
||||||
UI::Chats(state) => iced::subscription::unfold(
|
UI::Chats(state) => {
|
||||||
|
let recv_subscription = iced::subscription::unfold(
|
||||||
"external messages",
|
"external messages",
|
||||||
state.recv.take(),
|
state.recv.take(),
|
||||||
move |recv| async move {
|
move |recv| async move {
|
||||||
@ -307,8 +307,11 @@ impl Application for UI {
|
|||||||
let msg = msg.unwrap();
|
let msg = msg.unwrap();
|
||||||
(msg, recv)
|
(msg, recv)
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
_ => Subscription::none(),
|
|
||||||
|
Subscription::batch(vec![event_subscription, recv_subscription])
|
||||||
|
}
|
||||||
|
_ => event_subscription,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user