initial commit

This commit is contained in:
MrLetsplay 2024-02-01 22:36:54 +01:00
commit adec57e1b0
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
6 changed files with 3994 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

3743
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "icedtest"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.79"
iced = "0.9.0"
iced_web = "0.4.0"
reqwest = { version = "0.11.24", features = ["stream"] }
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
tokio = { version = "1.35.1", features = ["full"] }
tokio-stream = { version = "0.1.14", features = ["io-util"] }
tokio-util = "0.7.10"

18
index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tour - Iced</title>
</head>
<body>
<script type="module">
import init from "./tour/tour.js";
init('./tour/tour_bg.wasm');
</script>
</body>
</html>

123
src/api.rs Normal file
View File

@ -0,0 +1,123 @@
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tokio::io::AsyncBufReadExt;
use tokio_stream::wrappers::LinesStream;
use tokio_stream::{Stream, StreamExt};
use tokio_util::io::StreamReader;
const API_URL: &'static str = "http://localhost:11434";
pub struct OllamaAPI {
api_url: &'static str,
client: Client,
}
#[derive(Serialize)]
pub struct OllamaChat<'a> {
#[serde(skip_serializing)]
api: &'a OllamaAPI,
model: String,
#[serde(skip_serializing)]
history_size: u32,
messages: Vec<ChatMessage>,
}
#[derive(Serialize, Deserialize)]
pub enum Role {
#[serde(rename = "system")]
System,
#[serde(rename = "user")]
User,
#[serde(rename = "assistant")]
Assistant,
}
#[derive(Serialize, Deserialize)]
pub struct ChatMessage {
role: Role,
content: String,
}
#[derive(Deserialize)]
struct OllamaResponse {
message: ChatMessage,
}
fn deserialize_chunk(chunk: Result<String, std::io::Error>) -> anyhow::Result<String> {
match chunk {
Err(err) => Err(anyhow::Error::new(err)),
Ok(str) => {
let response = serde_json::from_str::<OllamaResponse>(&str)?;
Ok(response.message.content)
}
}
}
impl OllamaAPI {
pub async fn complete<'a>(
&self,
chat: &OllamaChat<'a>,
) -> anyhow::Result<impl Stream<Item = anyhow::Result<String>>> {
let body = serde_json::to_string(&chat)?;
println!("Sending: {}", body);
let request = self
.client
.post(String::from(API_URL) + "/api/chat")
.body(serde_json::to_string(&chat)?)
.build()?;
let stream = self
.client
.execute(request)
.await?
.bytes_stream()
.map(|x| match x {
Err(err) => Err(std::io::Error::new(std::io::ErrorKind::Other, err)),
Ok(bytes) => Ok(bytes),
});
let lines = StreamReader::new(stream).lines();
Ok(LinesStream::new(lines).map(deserialize_chunk))
}
pub fn create(api_url: &'static str) -> anyhow::Result<Self> {
let client = Client::builder().build()?;
Ok(Self {
api_url: api_url,
client,
})
}
pub fn create_chat(&self, model: &str) -> OllamaChat {
OllamaChat {
api: &self,
history_size: 0,
model: String::from(model),
messages: Vec::new(),
}
}
}
impl<'a> OllamaChat<'a> {
pub fn send_message(&mut self, role: Role, content: &str) {
self.messages.push(ChatMessage {
role,
content: String::from(content),
})
}
pub fn send_system(&mut self, content: &str) {
self.send_message(Role::System, content);
}
pub fn send_user(&mut self, content: &str) {
self.send_message(Role::User, content);
}
pub fn send_assistant(&mut self, content: &str) {
self.send_message(Role::Assistant, content);
}
pub async fn complete(&self) -> anyhow::Result<impl Stream<Item = anyhow::Result<String>>> {
self.api.complete(&self).await
}
}

92
src/main.rs Normal file
View File

@ -0,0 +1,92 @@
mod api;
use api::OllamaAPI;
use iced::{
futures::StreamExt,
widget::{button, column, container, row, scrollable, text},
window::{self},
Application, Command, Settings, Theme,
};
enum UI {
Loading,
Loaded,
}
#[derive(Debug, Clone)]
enum UIMessage {
LoadDone,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("Making request");
let api = OllamaAPI::create("http://localhost:11434")?;
let mut chat = api.create_chat("dolphin-mixtral");
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?;
while let Some(v) = a.next().await {
println!("GOT = {:?}", v);
}
UI::run(Settings {
window: window::Settings {
size: (720, 480),
..window::Settings::default()
},
..Settings::default()
})?;
Ok(())
}
impl Application for UI {
type Executor = iced::executor::Default;
type Message = UIMessage;
type Theme = Theme;
type Flags = ();
fn new(_flags: Self::Flags) -> (Self, iced::Command<Self::Message>) {
(
UI::Loading,
Command::perform(async {}, |_| UIMessage::LoadDone),
)
}
fn title(&self) -> String {
String::from("Hello World")
}
fn update(&mut self, message: Self::Message) -> iced::Command<Self::Message> {
match message {
UIMessage::LoadDone => {
*self = UI::Loaded;
Command::none()
}
}
}
fn view(&self) -> iced::Element<'_, Self::Message, iced::Renderer<Self::Theme>> {
let content = match self {
UI::Loading => row![text("This is amogus sussy baka!")],
UI::Loaded => row![button(text("Hello Wordl!")).on_press(UIMessage::LoadDone)],
};
let amogus = text("Hello!");
scrollable(
container(column![content, amogus].spacing(20).max_width(480))
.padding(40)
.center_x(),
)
.into()
}
fn theme(&self) -> Self::Theme {
Theme::Dark
}
}