From a55b2ea1e8cf5a09419129a692b7f8d4116f3aea Mon Sep 17 00:00:00 2001 From: Swarnim Arun Date: Fri, 3 Nov 2023 16:53:24 +0530 Subject: [PATCH 01/31] add: handle edge cases & tests for macos (#477) --- src-tauri/src/utils.rs | 83 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index 4cacfcd2..43a2fd71 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -44,17 +44,90 @@ pub fn get_binary_url(binaries_url: &HashMap>) -> Result< err!("Unsupported OS") } else if is_macos() { if is_x86_64() { - binaries_url.get("x86_64-apple-darwin") + binaries_url.get("x86_64-apple-darwin").cloned().flatten() } else if is_aarch64() { - binaries_url.get("aarch64-apple-darwin") + binaries_url.get("aarch64-apple-darwin").cloned().flatten() } else { err!("Unsupported architecture") } - .or_else(|| binaries_url.get("universal-apple-darwin")) - .with_context(|| "No binary available for platform")? - .clone() + .or_else(|| { + binaries_url + .get("universal-apple-darwin") + .cloned() + .flatten() + }) .with_context(|| "Service not supported on your platform") } else { err!("Unsupported platform") } } + +#[cfg(all(test, target_os = "macos"))] +mod tests { + use super::get_binary_url; + use std::collections::HashMap; + + #[test] + fn binary_url_test_universal_only() { + let url = get_binary_url(&HashMap::from_iter([( + "universal-apple-darwin".to_string(), + Some("randome_url.com".to_string()), + )])) + .unwrap(); + assert_eq!(url, "randome_url.com"); + } + + #[test] + fn binary_url_test_universal_only_option() { + let url = get_binary_url(&HashMap::from_iter([ + ( + "universal-apple-darwin".to_string(), + Some("randome_url.com".to_string()), + ), + ("aarch64-apple-darwin".to_string(), None), + ("x86_64-apple-darwin".to_string(), None), + ])) + .unwrap(); + assert_eq!(url, "randome_url.com"); + } + + #[test] + fn binary_url_test_with_non_universal() { + let url = get_binary_url(&HashMap::from_iter([ + ( + "universal-apple-darwin".to_string(), + Some("randome_url.com//universal".to_string()), + ), + ( + "aarch64-apple-darwin".to_string(), + Some("randome_url.com//not_universal".to_string()), + ), + ( + "x86_64-apple-darwin".to_string(), + Some("randome_url.com//not_universal".to_string()), + ), + ])) + .unwrap(); + assert_eq!(url, "randome_url.com//not_universal"); + } + + #[test] + #[should_panic] + fn binary_url_test_empty() { + let url = get_binary_url(&HashMap::from_iter([])).unwrap(); + assert_eq!(url, "randome_url.com"); + } + + #[test] + fn binary_url_all_none() { + let err = get_binary_url(&HashMap::from_iter([ + ("universal-apple-darwin".to_string(), None), + ("aarch64-apple-darwin".to_string(), None), + ("x86_64-apple-darwin".to_string(), None), + ])); + assert_eq!( + err.err().map(|err| err.to_string()).unwrap_or_default(), + "Service not supported on your platform" + ); + } +} From a29c188cac1b950a19f1edae27f1feb12596d757 Mon Sep 17 00:00:00 2001 From: tiero <3596602+tiero@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:33:42 +0100 Subject: [PATCH 02/31] switch registry dev & v1 based on debug or release Signed-off-by: tiero <3596602+tiero@users.noreply.github.com> --- src-tauri/src/main.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c86dcec5..f5f8f4c7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -256,8 +256,17 @@ fn main() { }) .setup(|app| { tauri::async_runtime::block_on(async move { + // Determine the URL based on whether the app is in debug or release mode + let url = if cfg!(debug_assertions) { + // Debug mode URL + "https://raw.githubusercontent.com/premAI-io/prem-registry/dev/manifests.json" + } else { + // Release mode URL + "https://raw.githubusercontent.com/premAI-io/prem-registry/v1/manifests.json" + }; + utils::fetch_services_manifests( - "https://raw.githubusercontent.com/premAI-io/prem-registry/v1/manifests.json", + url, app.state::>().deref().clone(), ) .await From 4c358805da4b5af210dfb164907d9841887e899e Mon Sep 17 00:00:00 2001 From: Biswaroop Date: Sat, 4 Nov 2023 14:14:19 +0530 Subject: [PATCH 03/31] petals models local support using python env (#435) * added support for swarm mode * stable petals release fixed version * Fix style + add PETALS tag * updated small fix readme * show swarm mode only on macos * added num_blocks parameter; start petals does not work * clean up * bugfix * added hardcoded model selection * added model selection * added username from whomai (with prem-app as fallback) * removed max val for num blocks * build fixed * added public name parameter (default prem-app) * moved petals swarm to sidecar * fix: supports Apple Silicon and little UI tweaks * moved petals sidecar script to scripts; bugfix in SwarmMode * added get_username tauri command * added swarm-mode only for aarch64 * renamed petals binary from petals-aarch64-apple-darwin to petals-universal-apple-darwin * CI: workflow tidy * build_petals: parameterise for arches * moving swarm mode from sidecar to miniconda env * bugfix in swarm command * removed sidecar * added env deletion script * Async run_swarm_mode Co-authored-by: Casper da Costa-Luis * added spinner while creating the environment * replaced wget with user wget * added fix for including path vars * tidy miniconda installation script * more tidy & use curl * use miniforge, more tidy * update delete_env script * fix exe perms * fixed script for conda env; swarm mode for linux and macos; added tooltips; added modal; updated public name with username * minor fixes * moved swarm commands to module * swarm persistent store draft * swarm persistent store bugfix * removed shadow in swarm settings; addded label for swarm settings * added label on swarm environment creation * added clickable link for explorer * Update src/shared/helpers/utils.ts * improve copy * updated conda processes filter Co-authored-by: Casper da Costa-Luis * fmt * add: petals models local support using python env * fix: typo while resolving merge conflict * chore: removed todos * fix: lint * update: changes on create environment due to swarm mode changes * fix: lint * chore: use dev registry --------- Co-authored-by: Filippo Pedrazzini Co-authored-by: Janaka-Steph Co-authored-by: nsosio Co-authored-by: tiero <3596602+tiero@users.noreply.github.com> Co-authored-by: Casper da Costa-Luis --- src-tauri/src/controller_binaries.rs | 21 ++++++++++++++++++++- src-tauri/src/main.rs | 1 + src-tauri/src/swarm.rs | 8 ++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/controller_binaries.rs b/src-tauri/src/controller_binaries.rs index 93934085..a2754715 100644 --- a/src-tauri/src/controller_binaries.rs +++ b/src-tauri/src/controller_binaries.rs @@ -5,7 +5,9 @@ use crate::{ download::Downloader, err, errors::{Context, Result}, - logerr, Service, SharedState, + logerr, + swarm::{create_environment, Config}, + Service, SharedState, }; use std::path::PathBuf; @@ -58,6 +60,7 @@ pub async fn download_service( #[tauri::command(async)] pub async fn start_service( + handle: tauri::AppHandle, service_id: String, state: State<'_, Arc>, app_handle: AppHandle, @@ -97,6 +100,11 @@ pub async fn start_service( ) })? .as_str(); + let is_petals_model = services_guard + .get(&service_id) + .with_context(|| format!("service_id {} doesn't exist in registry", service_id))? + .petals + .unwrap_or_default(); log::info!("serve_command: {}", serve_command); let serve_command_vec: Vec<&str> = serve_command.split_whitespace().collect(); @@ -123,9 +131,20 @@ pub async fn start_service( }) .collect(); log::info!("args: {:?}", args); + + let config = Config::new(); + let mut env_vars = HashMap::new(); + env_vars.insert("PREM_APPDIR".to_string(), config.app_data_dir); + env_vars.insert("PREM_PYTHON".to_string(), config.python); + + if is_petals_model { + create_environment(handle); + } + let child = Command::new(&binary_path) .current_dir(service_dir) .args(args) + .envs(env_vars) .stdout(std::process::Stdio::from( log_file .try_clone() diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f5f8f4c7..cf451d40 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -54,6 +54,7 @@ pub struct Service { running_port: Option, #[serde(rename = "serviceType")] service_type: Option, + petals: Option, version: Option, #[serde(rename = "weightsDirectoryUrl")] weights_directory_url: Option, diff --git a/src-tauri/src/swarm.rs b/src-tauri/src/swarm.rs index 8f08e459..9cb1b184 100644 --- a/src-tauri/src/swarm.rs +++ b/src-tauri/src/swarm.rs @@ -25,13 +25,13 @@ pub fn is_swarm_supported() -> bool { } } -struct Config { - app_data_dir: String, - python: String, +pub struct Config { + pub app_data_dir: String, + pub python: String, } impl Config { - fn new() -> Self { + pub fn new() -> Self { let mut app_data_dir = tauri::api::path::home_dir().expect("🙈 Failed to get app data directory"); app_data_dir.push(".config/prem"); From e17feaecd2d597a9f265ea3e4b490c10adc82f6c Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Sat, 4 Nov 2023 19:44:49 +0100 Subject: [PATCH 04/31] chore: cargo fix (#488) --- src-tauri/src/controller_binaries.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/controller_binaries.rs b/src-tauri/src/controller_binaries.rs index a2754715..efc34f69 100644 --- a/src-tauri/src/controller_binaries.rs +++ b/src-tauri/src/controller_binaries.rs @@ -470,7 +470,7 @@ pub async fn get_system_stats() -> Result> { } #[tauri::command(async)] -pub async fn get_service_stats(service_id: String) -> Result> { +pub async fn get_service_stats(_service_id: String) -> Result> { Ok(HashMap::new()) } #[tauri::command(async)] From de172fda28c4842bec421dabd30d9a4e28a1ef1f Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Sat, 4 Nov 2023 19:45:34 +0100 Subject: [PATCH 05/31] feat: improve service Card (#490) --- src/modules/service/components/Docker.tsx | 7 +++++ src/modules/service/components/Local.tsx | 7 +++++ src/modules/service/components/PeerToPeer.tsx | 7 +++++ src/modules/service/components/Petals.tsx | 7 ----- .../service/components/PremNetwork.tsx | 7 +++++ .../service/components/ServiceCard.tsx | 27 ++++++++++++++----- 6 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 src/modules/service/components/Docker.tsx create mode 100644 src/modules/service/components/Local.tsx create mode 100644 src/modules/service/components/PeerToPeer.tsx delete mode 100644 src/modules/service/components/Petals.tsx create mode 100644 src/modules/service/components/PremNetwork.tsx diff --git a/src/modules/service/components/Docker.tsx b/src/modules/service/components/Docker.tsx new file mode 100644 index 00000000..15bb91d7 --- /dev/null +++ b/src/modules/service/components/Docker.tsx @@ -0,0 +1,7 @@ +import clsx from "clsx"; + +const Docker = ({ className }: React.ButtonHTMLAttributes) => { + return 🐳 DOCKER; +}; + +export default Docker; diff --git a/src/modules/service/components/Local.tsx b/src/modules/service/components/Local.tsx new file mode 100644 index 00000000..8d52e49f --- /dev/null +++ b/src/modules/service/components/Local.tsx @@ -0,0 +1,7 @@ +import clsx from "clsx"; + +const Local = ({ className }: React.ButtonHTMLAttributes) => { + return 🖥️ MY COMPUTER; +}; + +export default Local; diff --git a/src/modules/service/components/PeerToPeer.tsx b/src/modules/service/components/PeerToPeer.tsx new file mode 100644 index 00000000..bcae9b0d --- /dev/null +++ b/src/modules/service/components/PeerToPeer.tsx @@ -0,0 +1,7 @@ +import clsx from "clsx"; + +const PeerToPeer = ({ className }: React.ButtonHTMLAttributes) => { + return 🍐 PEER TO PEER; +}; + +export default PeerToPeer; diff --git a/src/modules/service/components/Petals.tsx b/src/modules/service/components/Petals.tsx deleted file mode 100644 index e99f76ca..00000000 --- a/src/modules/service/components/Petals.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import clsx from "clsx"; - -const Petals = ({ className }: React.ButtonHTMLAttributes) => { - return PETALS; -}; - -export default Petals; diff --git a/src/modules/service/components/PremNetwork.tsx b/src/modules/service/components/PremNetwork.tsx new file mode 100644 index 00000000..7c170288 --- /dev/null +++ b/src/modules/service/components/PremNetwork.tsx @@ -0,0 +1,7 @@ +import clsx from "clsx"; + +const PremNetwork = ({ className }: React.ButtonHTMLAttributes) => { + return 📡 PREM NETWORK; +}; + +export default PremNetwork; diff --git a/src/modules/service/components/ServiceCard.tsx b/src/modules/service/components/ServiceCard.tsx index 8613e344..0127ee81 100644 --- a/src/modules/service/components/ServiceCard.tsx +++ b/src/modules/service/components/ServiceCard.tsx @@ -11,7 +11,10 @@ import useWarningModal from "../hooks/useWarningModal"; import type { ServiceCardProps } from "../types"; import Beta from "./Beta"; -import Petals from "./Petals"; +import Docker from "./Docker"; +import Local from "./Local"; +import PeerToPeer from "./PeerToPeer"; +import PremNetwork from "./PremNetwork"; import ServiceActions from "./ServiceActions"; const ServiceCard = ({ className, service }: ServiceCardProps) => { @@ -56,11 +59,23 @@ const ServiceCard = ({ className, service }: ServiceCardProps) => { isWarningModalOpen={isWarningModalOpen} /> -

- {title} - {service.beta && } - {service.petals && } -

+
+

+ {title} + {service.beta && } +

+ {service.petals ? ( +

+ + +

+ ) : ( +

+ + {status === "docker_only" && } +

+ )} +
); }; From 97a0b6ed987cc038f59c39dbf17a96c345823fb6 Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Sat, 4 Nov 2023 20:07:14 +0100 Subject: [PATCH 06/31] feat: add Network link (#493) --- src/assets/images/network.png | Bin 0 -> 24809 bytes src/shared/components/DashboardSidebar.tsx | 10 +++++++++- src/shared/components/NetworkIcon.tsx | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/assets/images/network.png create mode 100644 src/shared/components/NetworkIcon.tsx diff --git a/src/assets/images/network.png b/src/assets/images/network.png new file mode 100644 index 0000000000000000000000000000000000000000..f331088605ccbdd24c180b6841ad7303b1b9045d GIT binary patch literal 24809 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 E2YQ=BqW}N^ literal 0 HcmV?d00001 diff --git a/src/shared/components/DashboardSidebar.tsx b/src/shared/components/DashboardSidebar.tsx index be193c97..1f950839 100644 --- a/src/shared/components/DashboardSidebar.tsx +++ b/src/shared/components/DashboardSidebar.tsx @@ -6,6 +6,7 @@ import DashboardIcon from "./DashboardIcon"; import DocumentationIcon from "./DocumentationIcon"; import NavLinkContainer from "./NavLinkContainer"; import NavLinkItem from "./NavLinkItem"; +import NetworkIcon from "./NetworkIcon"; import SettingIcon from "./SettingIcon"; const DashboardSidebar = () => { @@ -23,10 +24,17 @@ const DashboardSidebar = () => {
+ + } + /> } /> { + return ; +}; + +export default NetworkIcon; From 2bdf101cf9adf8c5a2d0087412083d31300f12f9 Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:12:25 +0000 Subject: [PATCH 07/31] v0.2.0-rc.0 --- latest.json | 18 +++++++++++++----- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 8 ++++---- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/latest.json b/latest.json index 14086336..10abebac 100644 --- a/latest.json +++ b/latest.json @@ -1,11 +1,19 @@ { - "version": "0.1.2", + "version": "0.2.0-rc.0", "notes": "See the assets to download and install this version.", - "pub_date": "2023-10-26T17:02:17.290Z", + "pub_date": "2023-11-06T09:12:24.149Z", "platforms": { - "linux-x86_64": { - "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTck91dEJzS1kvNnhxSDVuUGVZQ3diN0owNUd1YWd0UldlVmRTWjJsNWFWVHZMbFNxNDE1M2k2Qnd5aEMyNUJJZFlqbFc5VEdQaDlKMzFRejlMc2ZhM25Eb2ozbTJtWXdRPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjk4MzM5MjA0CWZpbGU6cHJlbV8wLjEuMl9hbWQ2NC5BcHBJbWFnZS50YXIuZ3oKWkt6RE5sc3F3Y0dSS1dGdGx2dzR6N3JrVzdkODliQ1l5QVkyTnJSbzZpdkNLSE1Xa08zeXNQaVNTTkpleWk1dkQvdHdLV2wvVmxaalovZHRqSXR3REE9PQo=", - "url": "https://github.com/premAI-io/prem-app/releases/download/v0.1.2/prem_0.1.2_amd64.AppImage.tar.gz" + "darwin-aarch64": { + "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTck91dEJzS1kvNjdTZjQzVDUxajBWSkh0QWhkNU9hL2J5RDJ2NzdabS9ucUNLZGlXeU9zZzlhTytldmtaaXk3OEdyWlJudmJHL2dsWE55ZmNzbGxPT0paM1ZEMEZZcFFjPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjk5MjYxNTgzCWZpbGU6UHJlbS5hcHAudGFyLmd6CkMxYlM4U1B1THNCY0VPS00rc2dCSS9pM002VDFhNG1KbFF5TWsxWEdyaFFxNEJTVlhzcDRsYUZzR2RRNGN4bmxRQWU2dWNxYVU2bjg1Z3pIcmR2MENBPT0K", + "url": "https://github.com/premAI-io/prem-app/releases/download/v0.2.0-rc.0/Prem_universal.app.tar.gz" + }, + "darwin-x86_64": { + "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTck91dEJzS1kvNjdTZjQzVDUxajBWSkh0QWhkNU9hL2J5RDJ2NzdabS9ucUNLZGlXeU9zZzlhTytldmtaaXk3OEdyWlJudmJHL2dsWE55ZmNzbGxPT0paM1ZEMEZZcFFjPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjk5MjYxNTgzCWZpbGU6UHJlbS5hcHAudGFyLmd6CkMxYlM4U1B1THNCY0VPS00rc2dCSS9pM002VDFhNG1KbFF5TWsxWEdyaFFxNEJTVlhzcDRsYUZzR2RRNGN4bmxRQWU2dWNxYVU2bjg1Z3pIcmR2MENBPT0K", + "url": "https://github.com/premAI-io/prem-app/releases/download/v0.2.0-rc.0/Prem_universal.app.tar.gz" + }, + "darwin-universal": { + "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTck91dEJzS1kvNjdTZjQzVDUxajBWSkh0QWhkNU9hL2J5RDJ2NzdabS9ucUNLZGlXeU9zZzlhTytldmtaaXk3OEdyWlJudmJHL2dsWE55ZmNzbGxPT0paM1ZEMEZZcFFjPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNjk5MjYxNTgzCWZpbGU6UHJlbS5hcHAudGFyLmd6CkMxYlM4U1B1THNCY0VPS00rc2dCSS9pM002VDFhNG1KbFF5TWsxWEdyaFFxNEJTVlhzcDRsYUZzR2RRNGN4bmxRQWU2dWNxYVU2bjg1Z3pIcmR2MENBPT0K", + "url": "https://github.com/premAI-io/prem-app/releases/download/v0.2.0-rc.0/Prem_universal.app.tar.gz" } } } \ No newline at end of file diff --git a/package.json b/package.json index 266216f0..8a55409f 100644 --- a/package.json +++ b/package.json @@ -74,5 +74,5 @@ "tauri": "tauri" }, "type": "module", - "version": "0.1.2" + "version": "0.2.0-rc.0" } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b7f34784..3f7fe169 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2496,7 +2496,7 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prem-app" -version = "0.1.2" +version = "0.2.0-rc.0" dependencies = [ "chrono", "ctrlc", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 142d29b6..21d58f66 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -46,4 +46,4 @@ license = "" name = "prem-app" repository = "" - version = "0.1.2" + version = "0.2.0-rc.0" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ae5a8a57..b8fb5b2b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "Prem", - "version": "0.1.2" + "version": "0.2.0-rc.0" }, "tauri": { "allowlist": { @@ -27,9 +27,6 @@ }, "bundle": { "active": true, - "resources": [ - "petals/*" - ], "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -38,6 +35,9 @@ "icons/icon.ico" ], "identifier": "io.premai.prem-app", + "resources": [ + "petals/*" + ], "targets": "all" }, "security": { From 39ebc7a3e9616443075752fe6b6499de5540bf9a Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:03:39 +0100 Subject: [PATCH 08/31] Merge v1 into main (#497) * use v1 branch of prem registry * Fix chat history (#439) * Available tag (#433) * Available tag * filter available + rm appId * rm unused var * gha: workflow_dispatch with manual version and branch name * gha: debug * gha: use version as number * v0.1.3 * remove mp3 (#432) * Update to TanStack Query v5 (#443) * fix: binary_url precedence logic (#450) * fix: binary_url precedence logic * fix: lint * Remove isFetching (#460) * fix: match premd routes with slash in docker controller (#455) * fix: gracefully handle process kill (#442) * GHA: split Tauri and Docker in two workflows (#462) * chore: add docker-compose to test app, daemon and gateway * docker-compose.gateway point to stable premd * v0.1.4 * fix: buggy state fetch (#471) * v0.1.5 --------- Co-authored-by: Janaka-Steph Co-authored-by: Biswaroop Co-authored-by: Swarnim Arun --- package-lock.json | 4 ++-- src/modules/service/components/Service.tsx | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f8605eb..8d8ad00b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prem-app", - "version": "0.1.2", + "version": "0.1.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "prem-app", - "version": "0.1.2", + "version": "0.1.4", "dependencies": { "@microsoft/fetch-event-source": "^2.0.1", "@tanstack/react-query": "^5.4.3", diff --git a/src/modules/service/components/Service.tsx b/src/modules/service/components/Service.tsx index a1e14ed9..f0c0804d 100644 --- a/src/modules/service/components/Service.tsx +++ b/src/modules/service/components/Service.tsx @@ -42,6 +42,17 @@ const Service = () => { setAppsAugmented(_apps ?? []); }, [apps]); + useEffect(() => { + const _apps = apps?.concat({ + id: "available", + name: "Available", + playground: false, + documentation: "", + icon: "https://raw.githubusercontent.com/astrit/css.gg/master/icons/svg/smile-mouth-open.svg", + }); + setAppsAugmented(_apps ?? []); + }, [apps]); + const filteredApps = useMemo(() => { if (filters.size === 0) return appsAugmented; if (![...filters.values()].includes(true)) return appsAugmented; From 5f95f38c963f4532764f98ecb74c4dffd43d1094 Mon Sep 17 00:00:00 2001 From: Janaka-Steph Date: Mon, 6 Nov 2023 14:51:36 +0100 Subject: [PATCH 09/31] Skip healthcheck (#483) * Skip healthcheck * Skip healthcheck --- src-tauri/src/controller_binaries.rs | 59 +++++++++++++++++----------- src-tauri/src/main.rs | 2 + 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src-tauri/src/controller_binaries.rs b/src-tauri/src/controller_binaries.rs index efc34f69..4a51ffd0 100644 --- a/src-tauri/src/controller_binaries.rs +++ b/src-tauri/src/controller_binaries.rs @@ -154,31 +154,42 @@ pub async fn start_service( .spawn() .map_err(|e| format!("Failed to spawn child process: {}", e))?; - // Check if the service is running calling /v1 endpoint every 500ms - let interval_duration = Duration::from_millis(500); - let mut interval = interval(interval_duration); - loop { - interval.tick().await; - let base_url = get_base_url(&services_guard[&service_id])?; - let url = format!("{}/v1", base_url); - let client = reqwest::Client::new(); - let res = client.get(&url).send().await; - match res { - Ok(response) => { - // If /v1 is not implemented by the service, it will return 400 Bad Request, consider it as success - if response.status().is_success() - || response.status() == reqwest::StatusCode::BAD_REQUEST - { - let mut running_services_guard = state.running_services.lock().await; - running_services_guard.insert(service_id.clone(), child); - log::info!("Service started: {}", service_id); - break; - } else { - log::error!("Service failed to start: {}", service_id); + let skip_service_check = services_guard + .get(&service_id) + .map(|service| service.skip_health_check.unwrap_or(false)) + .unwrap(); + + if skip_service_check { + let mut running_services_guard = state.running_services.lock().await; + running_services_guard.insert(service_id.clone(), child); + log::info!("Service started: {}", service_id); + } else { + // Check if the service is running calling /v1 endpoint every 500ms + let interval_duration = Duration::from_millis(500); + let mut interval = interval(interval_duration); + loop { + interval.tick().await; + let base_url = get_base_url(&services_guard[&service_id])?; + let url = format!("{}/v1", base_url); + let client = reqwest::Client::new(); + let res = client.get(&url).send().await; + match res { + Ok(response) => { + // If /v1 is not implemented by the service, it will return 400 Bad Request, consider it as success + if response.status().is_success() + || response.status() == reqwest::StatusCode::BAD_REQUEST + { + let mut running_services_guard = state.running_services.lock().await; + running_services_guard.insert(service_id.clone(), child); + log::info!("Service started: {}", service_id); + break; + } else { + log::error!("Service failed to start: {}", service_id); + } + } + Err(e) => { + log::error!("Failed to send request: {}", e); } - } - Err(e) => { - log::error!("Failed to send request: {}", e); } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index cf451d40..36bcbd72 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -55,6 +55,8 @@ pub struct Service { #[serde(rename = "serviceType")] service_type: Option, petals: Option, + #[serde(rename = "skipHealthCheck")] + skip_health_check: Option, version: Option, #[serde(rename = "weightsDirectoryUrl")] weights_directory_url: Option, From 45a152ddec3fb9f6ccd3e038eb3682da38d7ec0d Mon Sep 17 00:00:00 2001 From: Janaka-Steph Date: Mon, 6 Nov 2023 15:05:57 +0100 Subject: [PATCH 10/31] Autosize textarea (#486) --- src/assets/css/prem-chat.css | 30 ++++++++---- src/modules/prem-chat/components/InputBox.tsx | 29 ------------ .../components/PremChatContainer.tsx | 47 ++++++++++++------- src/shared/hooks/useAutosizeTextarea.ts | 18 +++++++ tailwind.config.js | 3 ++ 5 files changed, 71 insertions(+), 56 deletions(-) delete mode 100644 src/modules/prem-chat/components/InputBox.tsx create mode 100644 src/shared/hooks/useAutosizeTextarea.ts diff --git a/src/assets/css/prem-chat.css b/src/assets/css/prem-chat.css index 9c0969b3..9cb7e5ae 100644 --- a/src/assets/css/prem-chat.css +++ b/src/assets/css/prem-chat.css @@ -35,25 +35,37 @@ @apply bg-transparent cursor-pointer border border-grey-400 text-grey-200 w-full z-10 px-4 py-[6px] appearance-none; } -.prem-chat-input input { - @apply bg-grey-700 text-white md:text-sm text-[13px] outline-none rounded-[29px] h-[57px] mt-[14px] py-2 md:pl-[33px] md:pr-16 pr-12 pl-4 w-full; +.autosize-textarea { + @apply bg-grey-700 text-white leading-6 text-sm outline-none rounded-[29px] h-14 mt-[14px] py-2 md:pl-[33px] md:pr-16 pr-12 pl-4 w-full min-h-14 max-h-40; } -.prem-chat-input input:-webkit-autofill, -.prem-chat-input input:-webkit-autofill:hover, -.prem-chat-input input:-webkit-autofill:active, -.prem-chat-input input:-webkit-autofill:focus { +.autosize-textarea:empty { + line-height: 38px; +} + +.autosize-textarea::placeholder { + line-height: 38px; +} + +.autosize-textarea:-webkit-autofill, +.autosize-textarea:-webkit-autofill:hover, +.autosize-textarea:-webkit-autofill:active, +.autosize-textarea:-webkit-autofill:focus { -webkit-text-fill-color: #302f32; -webkit-box-shadow: 0 0 0 1000px #302f32 inset; transition: background-color 5000s ease-in-out 0s; } -.prem-chat-input img { +.autosize-textarea-container { + @apply relative; +} + +.autosize-textarea-container img { @apply bg-primary-light p-2 w-[35px] h-[35px] rounded-full stroke-white; } -.prem-chat-input button { - @apply absolute md:right-[21px] right-[12px] top-[25px]; +.autosize-textarea-container button { + @apply absolute md:right-[21px] right-[12px] bottom-4; } .prem-chat-bottom { diff --git a/src/modules/prem-chat/components/InputBox.tsx b/src/modules/prem-chat/components/InputBox.tsx deleted file mode 100644 index e6dc59bc..00000000 --- a/src/modules/prem-chat/components/InputBox.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import Send from "assets/images/send.svg"; -import type { ForwardedRef } from "react"; -import { forwardRef } from "react"; - -import type { InputBoxProps } from "../types"; - -const InputBox = forwardRef((props: InputBoxProps, ref: ForwardedRef) => { - const { question, setQuestion, disabled, placeholder } = props; - return ( -
- setQuestion(e.target.value)} - disabled={disabled} - placeholder={placeholder} - ref={ref} - autoFocus - /> - -
- ); -}); - -export default InputBox; diff --git a/src/modules/prem-chat/components/PremChatContainer.tsx b/src/modules/prem-chat/components/PremChatContainer.tsx index 797d5813..9a6ea49e 100644 --- a/src/modules/prem-chat/components/PremChatContainer.tsx +++ b/src/modules/prem-chat/components/PremChatContainer.tsx @@ -1,3 +1,4 @@ +import Send from "assets/images/send.svg"; import clsx from "clsx"; import { useEffect, useRef, useState } from "react"; import BotReply from "shared/components/BotReply"; @@ -5,10 +6,10 @@ import UserReply from "shared/components/UserReply"; import usePremChatStream from "shared/hooks/usePremChatStream"; import { useMediaQuery, useWindowSize } from "usehooks-ts"; +import useAutosizeTextArea from "../../../shared/hooks/useAutosizeTextarea"; import type { Message, PremChatContainerProps } from "../types"; import Header from "./Header"; -import InputBox from "./InputBox"; import PremChatSidebar from "./PremChatSidebar"; import RegenerateButton from "./RegenerateButton"; import RightSidebar from "./RightSidebar"; @@ -23,7 +24,7 @@ const PremChatContainer = ({ const [rightSidebar, setRightSidebar] = useState(false); const [hamburgerMenuOpen, setHamburgerMenu] = useState(true); const chatMessageListRef = useRef(null); - const inputRef = useRef(null); + const textAreaRef = useRef(null); const { height } = useWindowSize(); const responsiveMatches = useMediaQuery("(min-width: 768px)"); @@ -40,6 +41,8 @@ const PremChatContainer = ({ abort, } = usePremChatStream(serviceId, serviceType, chatId || null); + useAutosizeTextArea(textAreaRef.current, question); + useEffect(() => { if (chatMessageListRef.current) { chatMessageListRef.current.scrollTop = chatMessageListRef.current.scrollHeight; @@ -47,8 +50,8 @@ const PremChatContainer = ({ }, [chatMessages]); useEffect(() => { - if (!isLoading && inputRef.current) { - inputRef.current.focus(); + if (!isLoading && textAreaRef.current) { + textAreaRef.current.focus(); } }, [isLoading]); @@ -103,20 +106,28 @@ const PremChatContainer = ({ )} -
- + +
+