initial commit
This commit is contained in:
commit
adec57e1b0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
3743
Cargo.lock
generated
Normal file
3743
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal 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
18
index.html
Normal 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
123
src/api.rs
Normal 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
92
src/main.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user