From 0a6c7b7827d235863f7c1adf28db8fda24a5e69a Mon Sep 17 00:00:00 2001 From: dezoito Date: Sun, 1 Dec 2024 16:05:35 -0300 Subject: [PATCH 01/11] feature/sql: Experiments are randonmly named and stored in DB --- src-tauri/Cargo.toml | 1 + src-tauri/src/commands/llm.rs | 12 +++++- src-tauri/src/lib.rs | 70 +++++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 58699d9..3440f98 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -27,6 +27,7 @@ ollama-rs = { version = "0.2.0", default-features = false, features = ["rustls"] chrono = "0.4.38" reqwest = {version = "0.12.4", features = ["blocking", "json", "rustls-tls"], default-features = false } sqlx = { version = "0.8.1", features = ["runtime-tokio", "sqlite", "chrono"] } +eff-wordlist = "1.0.3" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/src-tauri/src/commands/llm.rs b/src-tauri/src/commands/llm.rs index 552fac1..b114cb4 100644 --- a/src-tauri/src/commands/llm.rs +++ b/src-tauri/src/commands/llm.rs @@ -1,3 +1,4 @@ +use crate::db::DatabaseState; use reqwest::Client; use serde_json::json; use tokio::time::{self, Duration}; @@ -62,6 +63,7 @@ pub async fn get_ollama_version(config: IDefaultConfigs) -> Result, config: IDefaultConfigs, params: TParamIteration, app_handle: tauri::AppHandle, @@ -189,9 +191,17 @@ pub async fn get_inference( let app_data_dir_str = app_data_dir.to_string_lossy(); // Log the experiment if it's successful + let pool = &state.0; match res { Ok(generation_response) => { - log_experiment(&config, ¶ms, &generation_response, &app_data_dir_str).await?; + log_experiment( + pool, + &config, + ¶ms, + &generation_response, + &app_data_dir_str, + ) + .await?; Ok(generation_response) } Err(err) => Err(Error::StringError(err.to_string())), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 882f2de..8262784 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -13,15 +13,15 @@ https://jonaskruckenberg.github.io/tauri-docs-wip/development/inter-process-comm The Error enum, therefore, has to implement a variant for "OllamaError" */ - -use std::collections::HashMap; - use chrono::Utc; use ollama_rs::error::OllamaError; use ollama_rs::generation::completion::GenerationResponse; use serde::{Deserialize, Serialize}; use serde_json::json; use serde_json::Value; +use sqlx::Pool; +use sqlx::Sqlite; +use std::collections::HashMap; use std::fs; use std::time::SystemTime; use std::{ @@ -32,6 +32,7 @@ use std::{ use thiserror::Error; use url::{ParseError, Url}; +use eff_wordlist::short::random_word; use sqlx::Error as SqlxError; use tokio::time::{sleep, Duration}; @@ -68,6 +69,38 @@ pub struct ExperimentFile { pub contents: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Experiment { + pub id: Option, + pub name: String, + pub contents: String, + pub experiment_uuid: String, + pub is_favorite: bool, + pub created: String, + pub date_created: Option, +} + +impl Experiment { + /// Create a new experiment + pub fn new( + name: String, + contents: String, + experiment_uuid: String, + created: String, + is_favorite: bool, + ) -> Self { + Self { + id: None, // Will be set by database on insertion + name, + contents, + experiment_uuid, + is_favorite, + created, + date_created: None, // Will be set by database on insertion + } + } +} + // Use thiserror::Error to implement serializable errors // that are returned by commands #[derive(Debug, Error)] @@ -101,6 +134,16 @@ impl serde::Serialize for Error { } } +pub fn create_experiment_name() -> String { + // uses eff_short_wordlist to create a name + // composed of three random words for an experiment + let mut word_vec = vec![]; + for _ in 0..3 { + word_vec.push(random_word()); + } + word_vec.join("-") +} + #[allow(unused)] pub async fn wait_and_return(duration_seconds: u64) -> String { // Convert seconds to Duration @@ -129,6 +172,7 @@ pub fn split_host_port(url: &str) -> Result<(String, u16), ParseError> { } pub async fn log_experiment( + pool: &Pool, config: &IDefaultConfigs, params: &TParamIteration, res: &GenerationResponse, @@ -180,5 +224,25 @@ pub async fn log_experiment( println!("Experiment logged at: {}", log_file_path); + // Create a database record for the experiment + let experiment = Experiment::new( + create_experiment_name(), + serde_json::to_string(&log_data)?.to_string(), + experiment_uuid.to_string(), + Utc::now().to_string(), + false, + ); + + // let pool = &state.0; + let stmt = "INSERT INTO experiments (name, contents, experiment_uuid, created, is_favorite) VALUES ($1, $2, $3, $4, $5)"; + sqlx::query(stmt) + .bind(experiment.name) + .bind(experiment.contents) + .bind(experiment.experiment_uuid) + .bind(experiment.created) + .bind(experiment.is_favorite) + .execute(pool) + .await?; + Ok(()) } From 24677562ca498120fd1b9d0c3f7babbb588598a8 Mon Sep 17 00:00:00 2001 From: dezoito Date: Sun, 1 Dec 2024 17:20:12 -0300 Subject: [PATCH 02/11] feature/sql: Fixed most of the UI code, but experiments fail when there are multiple generations because we are not checking if the UUID already exists --- src-tauri/Cargo.toml | 2 +- src-tauri/src/commands/experiment.rs | 68 ++++++++++++++++++---------- src-tauri/src/lib.rs | 9 ++-- src-tauri/tauri.conf.json | 3 ++ 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3440f98..436ab19 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" tauri-build = { version = "1.5", features = [] } [dependencies] -tauri = { version = "1.6", features = [ "fs-all", "dialog-all", "shell-open"] } +tauri = { version = "1.6", features = [ "path-all", "fs-all", "dialog-all", "shell-open"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" anyhow = "1.0.79" diff --git a/src-tauri/src/commands/experiment.rs b/src-tauri/src/commands/experiment.rs index efa0503..b80e095 100644 --- a/src-tauri/src/commands/experiment.rs +++ b/src-tauri/src/commands/experiment.rs @@ -1,31 +1,53 @@ use grid_search_desktop::{Error, ExperimentFile}; use std::fs; +use crate::db::DatabaseState; + #[tauri::command] -pub fn get_experiments(app_handle: tauri::AppHandle) -> Result, Error> { - let binding = app_handle.path_resolver().app_data_dir().unwrap(); - let app_data_dir = binding.to_str().unwrap(); - let mut files: Vec = fs::read_dir(app_data_dir)? - .filter_map(Result::ok) - .filter_map(|entry| { - let path = entry.path(); - if path.extension().unwrap_or_default() != "json" { - return None; - } - let metadata = fs::metadata(&path).ok()?; - let created = metadata.created().ok()?; - let contents = fs::read_to_string(&path).ok()?; - Some(ExperimentFile { - name: path.file_name()?.to_string_lossy().into_owned(), - created, - contents, - }) - }) - .collect(); +pub async fn get_experiments( + state: tauri::State<'_, DatabaseState>, + // app_handle: tauri::AppHandle, +) -> Result, Error> { + let stmt = r#" + SELECT + name, + created, + contents + FROM experiments + ORDER BY id DESC + "#; + + let query = sqlx::query_as::<_, ExperimentFile>(stmt); + let pool = &state.0; + let experiments = query.fetch_all(pool).await?; + + println!("\nRetrieved {} experiments:", experiments.len()); + dbg!(&experiments); + Ok(experiments) + + // let binding = app_handle.path_resolver().app_data_dir().unwrap(); + // let app_data_dir = binding.to_str().unwrap(); + // let mut files: Vec = fs::read_dir(app_data_dir)? + // .filter_map(Result::ok) + // .filter_map(|entry| { + // let path = entry.path(); + // if path.extension().unwrap_or_default() != "json" { + // return None; + // } + // let metadata = fs::metadata(&path).ok()?; + // let created = metadata.created().ok()?; + // let contents = fs::read_to_string(&path).ok()?; + // Some(ExperimentFile { + // name: path.file_name()?.to_string_lossy().into_owned(), + // created, + // contents, + // }) + // }) + // .collect(); - files.sort_by_key(|file| file.created); - files.reverse(); - Ok(files) + // files.sort_by_key(|file| file.created); + // files.reverse(); + // Ok(files) } // --- diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 8262784..8bd57dd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -19,11 +19,12 @@ use ollama_rs::generation::completion::GenerationResponse; use serde::{Deserialize, Serialize}; use serde_json::json; use serde_json::Value; +use sqlx::prelude::FromRow; use sqlx::Pool; use sqlx::Sqlite; use std::collections::HashMap; use std::fs; -use std::time::SystemTime; +// use std::time::SystemTime; use std::{ fs::File, io::{BufReader, BufWriter}, @@ -62,14 +63,14 @@ pub struct IDefaultConfigs { pub default_options: HashMap, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, FromRow)] pub struct ExperimentFile { pub name: String, - pub created: SystemTime, + pub created: String, pub contents: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Experiment { pub id: Option, pub name: String, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6c35204..75fe039 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -12,6 +12,9 @@ "tauri": { "allowlist": { "all": false, + "path": { + "all": true + }, "shell": { "all": false, "open": true From 372812a0ffc5fa9000a4c888f3fad774a00edf95 Mon Sep 17 00:00:00 2001 From: dezoito Date: Sun, 1 Dec 2024 17:26:18 -0300 Subject: [PATCH 03/11] feature/sql: Fixed most of the UI code, but experiments fail when there are multiple generations because we are not checking if the UUID already exists --- src-tauri/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 8bd57dd..5eeafff 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -179,6 +179,9 @@ pub async fn log_experiment( res: &GenerationResponse, app_data_dir: &str, ) -> Result<(), Error> { + //TODO: rewrite so that... if the experiment does NOT exist, create it as is + // if it exists, just update the contents field, adding data from the latest prompt + // (eww... this is gross) let experiment_uuid = ¶ms.experiment_uuid; let log_file_path = format!("{}/{}.json", app_data_dir, experiment_uuid); From 295fe259feea94dc8244f14b85582ca0991bdd40 Mon Sep 17 00:00:00 2001 From: dezoito Date: Sun, 1 Dec 2024 17:27:03 -0300 Subject: [PATCH 04/11] Feature/experiments: update notes --- src/components/Selectors/logs-selector.tsx | 22 +++++++++++++--------- src/components/experiment-data-dialog.tsx | 12 ++++-------- src/issue_with_sqlx.md | 7 +++++++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/Selectors/logs-selector.tsx b/src/components/Selectors/logs-selector.tsx index 8c2b973..4d14dae 100644 --- a/src/components/Selectors/logs-selector.tsx +++ b/src/components/Selectors/logs-selector.tsx @@ -2,6 +2,9 @@ import { IExperimentFile, TFormValues } from "@/Interfaces"; import { Button } from "@/components/ui/button"; import { formValuesAtom } from "@/Atoms"; +import { ExperimentDataDialog } from "@/components/experiment-data-dialog"; +import { get_experiments } from "@/components/queries"; +import { useConfirm } from "@/components/ui/alert-dialog-provider"; import { Sheet, SheetClose, @@ -12,7 +15,7 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; -import { convertEpochToDateTime } from "@/lib"; +import { toast } from "@/components/ui/use-toast"; import { CrossCircledIcon, DownloadIcon, @@ -26,10 +29,6 @@ import { invoke } from "@tauri-apps/api/tauri"; import { saveAs } from "file-saver"; import { useAtom } from "jotai"; import { useState } from "react"; -import { ExperimentDataDialog } from "@/components/experiment-data-dialog"; -import { get_experiments } from "@/components/queries"; -import { useConfirm } from "@/components/ui/alert-dialog-provider"; -import { toast } from "@/components/ui/use-toast"; function processExperimentData(logContent: string): TFormValues { const logData = JSON.parse(logContent); @@ -178,7 +177,7 @@ export function LogsSelector() { actionButton: "Delete!", }) ) { - console.log("deleteing experiment", fileName); + console.log("D eleting experiment", fileName); await invoke("delete_experiment_files", { fileName: fileName, @@ -248,10 +247,13 @@ export function LogsSelector() { >
- {convertEpochToDateTime(exp.created.secs_since_epoch)} + {exp.name} + {/* {convertEpochToDateTime(exp.created.secs_since_epoch)} */}
-
{exp.name}
+
+ {exp.created.toString()} +
{/* Buttons to inspect and download */} @@ -268,7 +270,9 @@ export function LogsSelector() { diff --git a/src/components/experiment-data-dialog.tsx b/src/components/experiment-data-dialog.tsx index f043a29..2046e30 100644 --- a/src/components/experiment-data-dialog.tsx +++ b/src/components/experiment-data-dialog.tsx @@ -11,13 +11,12 @@ import { import { IExperimentFile } from "@/Interfaces"; import { - convertEpochToDateTime, convertNanosecondsToTime, - convertToUTCString, formatInterval, tokensPerSecond, } from "@/lib"; import { useState } from "react"; +import { convertToUTCString } from "../lib/index"; import { Separator } from "./ui/separator"; interface IProps { @@ -38,7 +37,7 @@ export function ExperimentDataDialog(props: IProps) { - Experiment Data + Experiment: {experiment.name} {/* {convertEpochToDateTime(experiment.created.secs_since_epoch)} */} @@ -49,11 +48,8 @@ export function ExperimentDataDialog(props: IProps) {
Metadata
-
ID: {experiment.name.slice(0, -5)}
-
- Date:{" "} - {convertEpochToDateTime(experiment.created.secs_since_epoch)} -
+
ID: {JSON.parse(experiment.contents).experiment_uuid}
+
Date: {experiment.created.toString()}
diff --git a/src/issue_with_sqlx.md b/src/issue_with_sqlx.md index 08b5947..aa74a02 100644 --- a/src/issue_with_sqlx.md +++ b/src/issue_with_sqlx.md @@ -157,3 +157,10 @@ https://www.youtube.com/watch?v=TCERYbgvbq0&t=511s ============ Forget using macros such as query! and quey_as! . just use the non macro functions and skip runing `cargo sqlx prepare` and copying db files and all the nonsense + +================= +When inserting experiments into the DB +1- we really should separate an experiment from its generations +2- we are not going to do it, though, but we must check if an experiment exists before trying to add a generation to its `contents` field!!!! + +3- (we also have to handle deleting existing experiments now) From 02f22ef5fd869258d894c85ccf606b0dd66ecd3a Mon Sep 17 00:00:00 2001 From: dezoito Date: Fri, 6 Dec 2024 14:32:01 -0300 Subject: [PATCH 05/11] feature/sql: Fixed creation of database record for experiments with multiple inferences + UI updates --- src-tauri/src/lib.rs | 91 ++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5eeafff..f12b50a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -179,9 +179,6 @@ pub async fn log_experiment( res: &GenerationResponse, app_data_dir: &str, ) -> Result<(), Error> { - //TODO: rewrite so that... if the experiment does NOT exist, create it as is - // if it exists, just update the contents field, adding data from the latest prompt - // (eww... this is gross) let experiment_uuid = ¶ms.experiment_uuid; let log_file_path = format!("{}/{}.json", app_data_dir, experiment_uuid); @@ -197,7 +194,6 @@ pub async fn log_experiment( serde_json::from_reader(reader)? } else { // Create new log data - // Log version does NOT have to match the app version json!({ "experiment_uuid": experiment_uuid, "datetime": Utc::now().to_string(), @@ -214,7 +210,7 @@ pub async fn log_experiment( }); if let Some(inferences) = log_data["inferences"].as_array_mut() { - inferences.push(inference_entry); + inferences.push(inference_entry.clone()); } else { return Err(Error::StringError( "Failed to access inferences array".to_string(), @@ -228,25 +224,72 @@ pub async fn log_experiment( println!("Experiment logged at: {}", log_file_path); - // Create a database record for the experiment - let experiment = Experiment::new( - create_experiment_name(), - serde_json::to_string(&log_data)?.to_string(), - experiment_uuid.to_string(), - Utc::now().to_string(), - false, - ); - - // let pool = &state.0; - let stmt = "INSERT INTO experiments (name, contents, experiment_uuid, created, is_favorite) VALUES ($1, $2, $3, $4, $5)"; - sqlx::query(stmt) - .bind(experiment.name) - .bind(experiment.contents) - .bind(experiment.experiment_uuid) - .bind(experiment.created) - .bind(experiment.is_favorite) - .execute(pool) - .await?; + // query the database to see if the experiment already exists + let stmt = "SELECT * FROM experiments WHERE experiment_uuid = $1"; + + // Use a more idiomatic approach to handle the optional result + match sqlx::query_as::<_, Experiment>(stmt) + .bind(experiment_uuid) + .fetch_optional(pool) + .await + .map_err(Error::Database)? + { + Some(existing_experiment) => { + // Update the existing experiment + let mut contents: Value = serde_json::from_str(&existing_experiment.contents) + .map_err(|e| Error::StringError(e.to_string()))?; + + contents["inferences"] + .as_array_mut() + .unwrap() + .push(inference_entry); + + let stmt = r#" + UPDATE experiments + SET contents = $1, + WHERE experiment_uuid = $4 + "#; + sqlx::query(stmt) + .bind(serde_json::to_string(&contents)?.to_string()) + .bind(Utc::now().to_string()) + .execute(pool) + .await?; + } + None => { + // Create a new experiment record + let experiment = Experiment::new( + create_experiment_name(), + serde_json::to_string(&log_data)?.to_string(), + experiment_uuid.to_string(), + Utc::now().to_string(), + false, + ); + + let stmt = r#" + INSERT INTO experiments ( + name, + contents, + experiment_uuid, + created, + is_favorite + ) VALUES ( + $1, + $2, + $3, + $4, + $5 + ) + "#; + sqlx::query(stmt) + .bind(experiment.name) + .bind(experiment.contents) + .bind(experiment.experiment_uuid) + .bind(experiment.created) + .bind(experiment.is_favorite) + .execute(pool) + .await?; + } + } Ok(()) } From c1c025b7da524b392c05860f66223331ec1d88c6 Mon Sep 17 00:00:00 2001 From: dezoito Date: Fri, 6 Dec 2024 15:34:01 -0300 Subject: [PATCH 06/11] feature/sql: Fixed SQL Bug, renamed component, moved scrollbar in LogSelector --- src-tauri/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f12b50a..ecd56f7 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -246,7 +246,7 @@ pub async fn log_experiment( let stmt = r#" UPDATE experiments - SET contents = $1, + SET contents = $1 WHERE experiment_uuid = $4 "#; sqlx::query(stmt) From 06594097aa3f29eda9899e6ba860995c1f307cb2 Mon Sep 17 00:00:00 2001 From: dezoito Date: Fri, 6 Dec 2024 15:34:40 -0300 Subject: [PATCH 07/11] feature/sql: Fixed SQL Bug, renamed component, moved scrollbar in LogSelector --- src/Layout.tsx | 2 +- .../{logs-selector.tsx => LogSelector.tsx} | 94 ++++++++++--------- .../autocomplete-with-command.tsx.example | 91 ------------------ src/components/results/iteration-result.tsx | 7 +- 4 files changed, 55 insertions(+), 139 deletions(-) rename src/components/Selectors/{logs-selector.tsx => LogSelector.tsx} (78%) delete mode 100644 src/components/Selectors/autocomplete-with-command.tsx.example diff --git a/src/Layout.tsx b/src/Layout.tsx index 35dac1f..6bd74f2 100644 --- a/src/Layout.tsx +++ b/src/Layout.tsx @@ -1,5 +1,5 @@ import { PromptArchiveDialog } from "@/components/Prompt/prompt-archive-dialog"; -import { LogsSelector } from "@/components/Selectors/logs-selector"; +import { LogsSelector } from "@/components/Selectors/LogSelector"; import FormGridParams from "@/components/form-grid-params"; import { ModeToggle } from "@/components/mode-toggle"; import GridResultsPane from "@/components/results/grid-results-pane"; diff --git a/src/components/Selectors/logs-selector.tsx b/src/components/Selectors/LogSelector.tsx similarity index 78% rename from src/components/Selectors/logs-selector.tsx rename to src/components/Selectors/LogSelector.tsx index 4d14dae..a674f19 100644 --- a/src/components/Selectors/logs-selector.tsx +++ b/src/components/Selectors/LogSelector.tsx @@ -217,7 +217,7 @@ export function LogsSelector() { Inspect, re-run, download or delete your experiment files. -
+
{query.isLoading && (
Loading...
@@ -239,57 +239,59 @@ export function LogsSelector() {
)} - {query.data && - query.data.map((exp: IExperimentFile) => ( -
-
-
- {exp.name} - {/* {convertEpochToDateTime(exp.created.secs_since_epoch)} */} -
+
+ {query.data && + query.data.map((exp: IExperimentFile) => ( +
+
+
+ {exp.name} + {/* {convertEpochToDateTime(exp.created.secs_since_epoch)} */} +
-
- {exp.created.toString()} +
+ {exp.created.toString()} +
-
- {/* Buttons to inspect and download */} -
- - + {/* Buttons to inspect and download */} +
+ + - + - {/* delete log file */} - + {/* delete log file */} + +
-
- ))} + ))} +
 
diff --git a/src/components/Selectors/autocomplete-with-command.tsx.example b/src/components/Selectors/autocomplete-with-command.tsx.example deleted file mode 100644 index 47f871a..0000000 --- a/src/components/Selectors/autocomplete-with-command.tsx.example +++ /dev/null @@ -1,91 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import * as React from "react"; -import { get_all_prompts } from "../queries"; - -import { IPrompt } from "@/Interfaces"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "../ui/command"; - -export interface IProps { - trigger: boolean; - index: number; - inputText?: string; - onSelect: (value: string) => void; -} - -export function Autocomplete({ - trigger, - index, - inputText = "", - onSelect, -}: IProps) { - const [filteredPrompts, setFilteredPrompts] = React.useState([]); - const commandRef = React.useRef(null); - - const promptQuery = useQuery({ - queryKey: ["get_all_prompts"], - queryFn: (): Promise => get_all_prompts(), - refetchInterval: 1000 * 30 * 1, - refetchOnWindowFocus: "always", - staleTime: 0, - }); - - React.useEffect(() => { - if (trigger && promptQuery.data) { - const filterText = inputText.slice(1).toLowerCase(); - const filtered = promptQuery.data.filter((prompt) => - prompt.slug.toLowerCase().startsWith(filterText), - ); - setFilteredPrompts(filtered); - - // Focus the command menu when it appears - if (filtered.length > 0) { - setTimeout(() => { - commandRef.current?.focus(); - }, 0); - } - } else { - setFilteredPrompts([]); - } - }, [trigger, promptQuery.data, inputText]); - - if (promptQuery.isLoading) { - return <>; - } - - return ( -
- {filteredPrompts.length > 0 && ( - - - No prompts found. - - - {filteredPrompts.map((prompt) => ( - onSelect(prompt.prompt)} - className="flex flex-col items-start py-2" - > - {`/${prompt.slug}`} - - {prompt.name} - - - ))} - - - - )} -
- ); -} - -export default Autocomplete; diff --git a/src/components/results/iteration-result.tsx b/src/components/results/iteration-result.tsx index 38e9d51..9a5b428 100644 --- a/src/components/results/iteration-result.tsx +++ b/src/components/results/iteration-result.tsx @@ -10,7 +10,7 @@ import { import { ClipboardCopyIcon, ReloadIcon } from "@radix-ui/react-icons"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAtom } from "jotai"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { get_inference } from "../queries"; import { Button } from "../ui/button"; import { CollapsibleItem } from "../ui/collapsible-item"; @@ -82,6 +82,11 @@ export default function IterationResult(props: IProps) { setEnabled(false); }; + // Updates the list of experiments + useEffect(() => { + queryClient.refetchQueries({ queryKey: ["get_experiments"] }); + }, [query.data]); + return (
Date: Fri, 6 Dec 2024 15:45:28 -0300 Subject: [PATCH 08/11] feature/sql: Styles scrollbar in LogSelector (since ScrollArea won't work) --- bun.lockb | Bin 146370 -> 146784 bytes package.json | 1 + src/components/Selectors/LogSelector.tsx | 2 +- tailwind.config.js | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index 5113783d393ad371d1cb833f8e3a5a6de24f3d43..0a839d980e2980cfefdad9ff8bc77839f4e883b1 100755 GIT binary patch delta 27545 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 JTw_Oi{2zm}KyCm4 delta 27464 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
)} -
+
{query.data && query.data.map((exp: IExperimentFile) => (
Date: Fri, 6 Dec 2024 15:50:58 -0300 Subject: [PATCH 09/11] feature/sql: More styling --- src/components/Selectors/LogSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Selectors/LogSelector.tsx b/src/components/Selectors/LogSelector.tsx index cd1e579..040dc18 100644 --- a/src/components/Selectors/LogSelector.tsx +++ b/src/components/Selectors/LogSelector.tsx @@ -239,7 +239,7 @@ export function LogsSelector() {
)} -
+
{query.data && query.data.map((exp: IExperimentFile) => (
Date: Sat, 7 Dec 2024 12:01:08 -0300 Subject: [PATCH 10/11] feature/sql: Completely stores experiments in the DB, stops using files --- src-tauri/src/commands/experiment.rs | 99 ++++-------------------- src-tauri/src/commands/llm.rs | 17 +--- src-tauri/src/lib.rs | 75 +++++------------- src-tauri/src/main.rs | 2 +- src/components/Selectors/LogSelector.tsx | 18 +++-- 5 files changed, 46 insertions(+), 165 deletions(-) diff --git a/src-tauri/src/commands/experiment.rs b/src-tauri/src/commands/experiment.rs index b80e095..8f47579 100644 --- a/src-tauri/src/commands/experiment.rs +++ b/src-tauri/src/commands/experiment.rs @@ -1,12 +1,10 @@ use grid_search_desktop::{Error, ExperimentFile}; -use std::fs; use crate::db::DatabaseState; #[tauri::command] pub async fn get_experiments( state: tauri::State<'_, DatabaseState>, - // app_handle: tauri::AppHandle, ) -> Result, Error> { let stmt = r#" SELECT @@ -22,94 +20,23 @@ pub async fn get_experiments( let experiments = query.fetch_all(pool).await?; println!("\nRetrieved {} experiments:", experiments.len()); - dbg!(&experiments); Ok(experiments) - - // let binding = app_handle.path_resolver().app_data_dir().unwrap(); - // let app_data_dir = binding.to_str().unwrap(); - // let mut files: Vec = fs::read_dir(app_data_dir)? - // .filter_map(Result::ok) - // .filter_map(|entry| { - // let path = entry.path(); - // if path.extension().unwrap_or_default() != "json" { - // return None; - // } - // let metadata = fs::metadata(&path).ok()?; - // let created = metadata.created().ok()?; - // let contents = fs::read_to_string(&path).ok()?; - // Some(ExperimentFile { - // name: path.file_name()?.to_string_lossy().into_owned(), - // created, - // contents, - // }) - // }) - // .collect(); - - // files.sort_by_key(|file| file.created); - // files.reverse(); - // Ok(files) } -// --- -// Example below connects to the database -// --- - -// #[tauri::command] -// pub async fn get_experiments( -// state: tauri::State<'_, DatabaseState>, -// ) -> Result, Error> { -// // Access the database pool -// let pool = &state.0; - -// // Example query -// let experiments = -// sqlx::query!("SELECT name, created, contents FROM experiments ORDER BY created DESC") -// .fetch_all(pool) -// .await -// .map_err(|e| Error::StringError(e.to_string()))?; - -// // Convert to your ExperimentFile struct -// let files: Vec = experiments -// .into_iter() -// .map(|row| ExperimentFile { -// name: row.name, -// created: SystemTime::from(chrono::DateTime::parse_from_rfc3339(&row.created).unwrap()), -// contents: row.contents, -// }) -// .collect(); +#[tauri::command] +pub async fn delete_experiments( + state: tauri::State<'_, DatabaseState>, + uuid: String, +) -> Result<(), Error> { + let pool = &state.0; -// Ok(files) -// } + let stmt: &str = match uuid.as_str() { + "*" => "DELETE FROM experiments", + _ => "DELETE FROM experiments WHERE experiment_uuid = $1", + }; -#[tauri::command] -pub fn delete_experiment_files( - app_handle: tauri::AppHandle, - file_name: String, -) -> Result<(), String> { - let app_data_dir = app_handle - .path_resolver() - .app_data_dir() - .ok_or_else(|| "Failed to get application data directory".to_string())?; + let _ = sqlx::query(stmt).bind(&uuid).execute(pool).await?; + print!("Deleted experiment with UUID: {}", uuid); - if file_name == "*" { - // Delete all JSON files in the directory - for entry in fs::read_dir(&app_data_dir).map_err(|e| e.to_string())? { - let entry = entry.map_err(|e| e.to_string())?; - let path = entry.path(); - if path.is_file() && path.extension().map(|e| e == "json").unwrap_or(false) { - fs::remove_file(path).map_err(|e| e.to_string())?; - } - } - Ok(()) - } else { - // Delete a specific file - let file_path = app_data_dir.join(file_name); - if file_path.exists() && file_path.is_file() { - fs::remove_file(file_path).map_err(|e| e.to_string())?; - Ok(()) - } else { - // File doesn't exist, do nothing - Ok(()) - } - } + Ok(()) } diff --git a/src-tauri/src/commands/llm.rs b/src-tauri/src/commands/llm.rs index b114cb4..b034167 100644 --- a/src-tauri/src/commands/llm.rs +++ b/src-tauri/src/commands/llm.rs @@ -182,26 +182,13 @@ pub async fn get_inference( dbg!(&res); println!("---------------------------------------------"); - // sets the base path for storing logs - // see https://github.com/tauri-apps/tauri/discussions/5557 - let app_data_dir = app_handle - .path_resolver() - .app_data_dir() - .unwrap_or_else(|| panic!("Failed to get application data directory")); - let app_data_dir_str = app_data_dir.to_string_lossy(); + // Log the experiment if it's successful let pool = &state.0; match res { Ok(generation_response) => { - log_experiment( - pool, - &config, - ¶ms, - &generation_response, - &app_data_dir_str, - ) - .await?; + log_experiment(pool, &config, ¶ms, &generation_response).await?; Ok(generation_response) } Err(err) => Err(Error::StringError(err.to_string())), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ecd56f7..26fdce7 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -23,13 +23,6 @@ use sqlx::prelude::FromRow; use sqlx::Pool; use sqlx::Sqlite; use std::collections::HashMap; -use std::fs; -// use std::time::SystemTime; -use std::{ - fs::File, - io::{BufReader, BufWriter}, - path::Path, -}; use thiserror::Error; use url::{ParseError, Url}; @@ -177,53 +170,23 @@ pub async fn log_experiment( config: &IDefaultConfigs, params: &TParamIteration, res: &GenerationResponse, - app_data_dir: &str, ) -> Result<(), Error> { let experiment_uuid = ¶ms.experiment_uuid; - let log_file_path = format!("{}/{}.json", app_data_dir, experiment_uuid); - // Create the logs directory if it doesn't exist - if !Path::new(&app_data_dir).exists() { - fs::create_dir(app_data_dir)?; - } - - let mut log_data = if Path::new(&log_file_path).exists() { - // Load existing log file - let file = File::open(&log_file_path)?; - let reader = BufReader::new(file); - serde_json::from_reader(reader)? - } else { - // Create new log data - json!({ - "experiment_uuid": experiment_uuid, - "datetime": Utc::now().to_string(), - "log_version": "0.5.0", - "config": config, - "inferences": [] - }) - }; - - // Add the new inference entry - let inference_entry = json!({ - "parameters": params, - "result": res + // Create a log data JSON structure + let log_data = json!({ + "experiment_uuid": experiment_uuid, + "datetime": Utc::now().to_string(), + "log_version": "0.5.0", + "config": config, + "inferences": [ + { + "parameters": params, + "result": res + } + ] }); - if let Some(inferences) = log_data["inferences"].as_array_mut() { - inferences.push(inference_entry.clone()); - } else { - return Err(Error::StringError( - "Failed to access inferences array".to_string(), - )); - } - - // Write the updated log data back to the file - let file = File::create(&log_file_path)?; - let writer = BufWriter::new(file); - serde_json::to_writer_pretty(writer, &log_data)?; - - println!("Experiment logged at: {}", log_file_path); - // query the database to see if the experiment already exists let stmt = "SELECT * FROM experiments WHERE experiment_uuid = $1"; @@ -239,19 +202,21 @@ pub async fn log_experiment( let mut contents: Value = serde_json::from_str(&existing_experiment.contents) .map_err(|e| Error::StringError(e.to_string()))?; - contents["inferences"] - .as_array_mut() - .unwrap() - .push(inference_entry); + contents["inferences"].as_array_mut().unwrap().push(json!({ + "parameters": params, + "result": res + })); let stmt = r#" UPDATE experiments - SET contents = $1 - WHERE experiment_uuid = $4 + SET contents = $1, created = $2 + WHERE experiment_uuid = $3 "#; + sqlx::query(stmt) .bind(serde_json::to_string(&contents)?.to_string()) .bind(Utc::now().to_string()) + .bind(experiment_uuid) .execute(pool) .await?; } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a3ead96..3a291bb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -50,7 +50,7 @@ fn main() { commands::get_inference, commands::get_experiments, commands::get_ollama_version, - commands::delete_experiment_files, + commands::delete_experiments, commands::get_all_prompts, commands::create_prompt, commands::update_prompt, diff --git a/src/components/Selectors/LogSelector.tsx b/src/components/Selectors/LogSelector.tsx index 040dc18..c8ac47c 100644 --- a/src/components/Selectors/LogSelector.tsx +++ b/src/components/Selectors/LogSelector.tsx @@ -168,7 +168,7 @@ export function LogsSelector() { setSheetOpen(false); } - async function deleteExperimentFiles(fileName: string) { + async function deleteExperiments(experiment_uuid: string) { if ( await confirm({ title: "Sanity Check", @@ -177,10 +177,10 @@ export function LogsSelector() { actionButton: "Delete!", }) ) { - console.log("D eleting experiment", fileName); + console.log("Deleting experiment", experiment_uuid); - await invoke("delete_experiment_files", { - fileName: fileName, + await invoke("delete_experiments", { + uuid: experiment_uuid, }); toast({ variant: "warning", @@ -214,7 +214,7 @@ export function LogsSelector() { Experiments - Inspect, re-run, download or delete your experiment files. + Inspect, re-run, download or delete your experiments.
@@ -230,7 +230,7 @@ export function LogsSelector() { variant="ghost" size="sm" onClick={async () => { - await deleteExperimentFiles("*"); + await deleteExperiments("*"); }} className="flex items-center space-x-2 text-red-500" > @@ -239,7 +239,7 @@ export function LogsSelector() {
)} -
+
{query.data && query.data.map((exp: IExperimentFile) => (
{ - await deleteExperimentFiles(exp.name); + await deleteExperiments( + JSON.parse(exp.contents).experiment_uuid, + ); }} > From 3bf7e59331888abd6c4902e42f9cfaf83464a980 Mon Sep 17 00:00:00 2001 From: dezoito Date: Sun, 8 Dec 2024 10:29:36 -0300 Subject: [PATCH 11/11] feature/experiment: Removes unused code --- src-tauri/src/commands/llm.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src-tauri/src/commands/llm.rs b/src-tauri/src/commands/llm.rs index b034167..8e40650 100644 --- a/src-tauri/src/commands/llm.rs +++ b/src-tauri/src/commands/llm.rs @@ -66,7 +66,6 @@ pub async fn get_inference( state: tauri::State<'_, DatabaseState>, config: IDefaultConfigs, params: TParamIteration, - app_handle: tauri::AppHandle, ) -> Result { // println!("----------------------------------------------------------"); // println!("Config and Params"); @@ -182,8 +181,6 @@ pub async fn get_inference( dbg!(&res); println!("---------------------------------------------"); - - // Log the experiment if it's successful let pool = &state.0; match res {