From 5d6218c0a40bb3ab42909c87d209dcb7b9d05545 Mon Sep 17 00:00:00 2001 From: Colton Pierson Date: Mon, 1 Jul 2024 13:33:38 -0700 Subject: [PATCH] wip on dispatch fixes --- .idea/toolchain.iml | 4 + toolchain/Cargo.lock | 185 ++++++++++++++++++ toolchain/Cargo.toml | 2 +- .../examples/core10_concurrency/core.md | 1 + .../examples/core10_concurrency/main.rs | 12 ++ .../core9_multi_agent_simulation/core.md | 4 + toolchain/chidori-core/src/cells/code_cell.rs | 6 +- .../chidori-core/src/cells/code_gen_cell.rs | 5 +- .../chidori-core/src/cells/embedding_cell.rs | 4 +- .../chidori-core/src/cells/llm_prompt_cell.rs | 5 +- .../chidori-core/src/cells/memory_cell.rs | 16 +- toolchain/chidori-core/src/cells/mod.rs | 13 ++ .../chidori-core/src/cells/template_cell.rs | 7 +- toolchain/chidori-core/src/cells/web_cell.rs | 4 +- .../execution/execution/execution_graph.rs | 29 +++ .../execution/execution/execution_state.rs | 32 ++- .../src/execution/primitives/operation.rs | 6 + .../src/library/std/scheduling/local/mod.rs | 5 +- .../src/library/std/webserver/mod.rs | 13 +- toolchain/chidori-core/src/sdk/entry.rs | 155 ++++++++------- toolchain/chidori-debugger/Cargo.toml | 3 + toolchain/chidori-debugger/src/chidori.rs | 184 ++++++++++------- toolchain/chidori-debugger/src/code.rs | 132 +++++++------ toolchain/chidori-debugger/src/graph.rs | 143 +++++++++++--- toolchain/chidori-debugger/src/main.rs | 31 ++- toolchain/chidori-optimizer-dsp/Cargo.toml | 10 + toolchain/chidori-optimizer-dsp/src/main.rs | 3 + toolchain/chidori-static-analysis/Cargo.toml | 1 + toolchain/chidori-tsne/Cargo.toml | 12 ++ toolchain/chidori-tsne/src/main.rs | 141 +++++++++++++ 30 files changed, 896 insertions(+), 272 deletions(-) create mode 100644 toolchain/chidori-core/examples/core10_concurrency/core.md create mode 100644 toolchain/chidori-core/examples/core10_concurrency/main.rs create mode 100644 toolchain/chidori-optimizer-dsp/Cargo.toml create mode 100644 toolchain/chidori-optimizer-dsp/src/main.rs create mode 100644 toolchain/chidori-tsne/Cargo.toml create mode 100644 toolchain/chidori-tsne/src/main.rs diff --git a/.idea/toolchain.iml b/.idea/toolchain.iml index 0655acd..15aa699 100644 --- a/.idea/toolchain.iml +++ b/.idea/toolchain.iml @@ -31,6 +31,9 @@ + + + @@ -40,6 +43,7 @@ + diff --git a/toolchain/Cargo.lock b/toolchain/Cargo.lock index 7017f6b..5ba070e 100644 --- a/toolchain/Cargo.lock +++ b/toolchain/Cargo.lock @@ -2106,6 +2106,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cbc" version = "0.1.2" @@ -2302,6 +2314,7 @@ dependencies = [ "image", "notify-debouncer-full", "num", + "objc", "once_cell", "petgraph", "petgraph-gen", @@ -2312,6 +2325,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "serde_yaml 0.8.26", "syntect", "target-lexicon", "thread_local", @@ -2321,6 +2335,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "webbrowser", + "winit", ] [[package]] @@ -2346,6 +2361,10 @@ dependencies = [ "version_check", ] +[[package]] +name = "chidori-optimizer-dsp" +version = "0.2.1" + [[package]] name = "chidori-prompt-format" version = "0.1.35" @@ -2376,6 +2395,7 @@ dependencies = [ "rustpython-parser", "serde", "serde-wasm-bindgen", + "serde_yaml 0.9.34+deprecated", "swc_common", "swc_ecma_ast 0.111.1", "swc_ecma_parser 0.142.2", @@ -2384,6 +2404,10 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "chidori-tsne" +version = "0.2.1" + [[package]] name = "chrono" version = "0.4.37" @@ -10843,6 +10867,19 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sctk-adwaita" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.4", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "seahash" version = "4.1.0" @@ -11312,6 +11349,31 @@ dependencies = [ "version_check", ] +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.5.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.4", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + [[package]] name = "smol_str" version = "0.2.1" @@ -13608,6 +13670,115 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34e9e6b6d4a2bb4e7e69433e0b35c7923b95d4dc8503a84d25ec917a4bbfdf07" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e63801c85358a431f986cffa74ba9599ff571fc5774ac113ed3b490c19a1133" +dependencies = [ + "bitflags 2.5.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.5.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a206e8b2b53b1d3fcb9428fec72bc278ce539e2fa81fe2bfc1ab27703d5187b9" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67da50b9f80159dec0ea4c11c13e24ef9e7574bd6ce24b01860a175010cea565" +dependencies = [ + "proc-macro2 1.0.79", + "quick-xml", + "quote 1.0.36", +] + +[[package]] +name = "wayland-sys" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -14194,6 +14365,7 @@ version = "0.29.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" dependencies = [ + "ahash 0.8.11", "android-activity", "atomic-waker", "bitflags 2.5.0", @@ -14207,6 +14379,7 @@ dependencies = [ "js-sys", "libc", "log", + "memmap2 0.9.4", "ndk", "ndk-sys", "objc2 0.4.1", @@ -14216,10 +14389,16 @@ dependencies = [ "raw-window-handle 0.6.1", "redox_syscall 0.3.5", "rustix", + "sctk-adwaita", + "smithay-client-toolkit", "smol_str", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", "web-sys", "web-time 0.2.4", "windows-sys 0.48.0", @@ -14373,6 +14552,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + [[package]] name = "xdg-home" version = "1.1.0" diff --git a/toolchain/Cargo.toml b/toolchain/Cargo.toml index 4604222..0458bd1 100644 --- a/toolchain/Cargo.toml +++ b/toolchain/Cargo.toml @@ -4,7 +4,7 @@ members = [ "chidori-prompt-format", "chidori-im-rs", "chidori-static-analysis", - "chidori-debugger", + "chidori-debugger", "chidori-tsne", "chidori-optimizer-dsp", ] resolver = "2" diff --git a/toolchain/chidori-core/examples/core10_concurrency/core.md b/toolchain/chidori-core/examples/core10_concurrency/core.md new file mode 100644 index 0000000..a19cdf9 --- /dev/null +++ b/toolchain/chidori-core/examples/core10_concurrency/core.md @@ -0,0 +1 @@ +# Explaining and demonstrating our concurrency support \ No newline at end of file diff --git a/toolchain/chidori-core/examples/core10_concurrency/main.rs b/toolchain/chidori-core/examples/core10_concurrency/main.rs new file mode 100644 index 0000000..0a98cf5 --- /dev/null +++ b/toolchain/chidori-core/examples/core10_concurrency/main.rs @@ -0,0 +1,12 @@ +use std::path::Path; +use chidori_core::sdk::entry::{Chidori}; +fn main() { + let current_file = env!("CARGO_MANIFEST_DIR"); + let current_file_path = Path::new(current_file); + let relative_path = current_file_path.join("./"); + + let mut env = Chidori::new(); + env.load_md_directory(&relative_path); + let mut s = env.get_instance().unwrap(); + s.run(); +} \ No newline at end of file diff --git a/toolchain/chidori-core/examples/core9_multi_agent_simulation/core.md b/toolchain/chidori-core/examples/core9_multi_agent_simulation/core.md index fb5d8d1..b0f3b22 100644 --- a/toolchain/chidori-core/examples/core9_multi_agent_simulation/core.md +++ b/toolchain/chidori-core/examples/core9_multi_agent_simulation/core.md @@ -4,6 +4,7 @@ This example is based on [this crewAI example](https://github.com/joaomdmoura/cr ```prompt (researcher) --- +model: gpt-3.5-turbo fn: scrape_and_summarize_website_researcher --- Role: Principal Researcher @@ -87,6 +88,7 @@ def search_internet(query): ```prompt (researcher) --- +model: gpt-3.5-turbo import: - search_internet - scrape_and_summarize_website @@ -98,6 +100,7 @@ Backstory: An expert in analyzing travel data to pick ideal destinations ```prompt (local_expert) --- +model: gpt-3.5-turbo import: - search_internet - scrape_and_summarize_website @@ -110,6 +113,7 @@ Backstory: A knowledgable local guide with extensive information about the city, ```prompt (travel_concierge) --- +model: gpt-3.5-turbo import: - search_internet - scrape_and_summarize_website diff --git a/toolchain/chidori-core/src/cells/code_cell.rs b/toolchain/chidori-core/src/cells/code_cell.rs index 1189174..6c4cd32 100644 --- a/toolchain/chidori-core/src/cells/code_cell.rs +++ b/toolchain/chidori-core/src/cells/code_cell.rs @@ -1,12 +1,13 @@ use futures_util::FutureExt; use chidori_static_analysis::language::Report; use crate::cells::{CodeCell, SupportedLanguage, TextRange}; +use crate::execution::execution::execution_graph::ExecutionNodeId; use crate::execution::execution::ExecutionState; use crate::execution::primitives::operation::{InputItemConfiguration, InputSignature, InputType, OperationFnOutput, OperationNode, OutputItemConfiguration, OutputSignature}; /// Code cells allow notebooks to evaluate source code in a variety of languages. #[tracing::instrument] -pub fn code_cell(cell: &CodeCell, range: &TextRange) -> anyhow::Result { +pub fn code_cell(execution_state_id: ExecutionNodeId, cell: &CodeCell, range: &TextRange) -> anyhow::Result { match cell.language { SupportedLanguage::PyO3 => { let paths = @@ -20,6 +21,7 @@ pub fn code_cell(cell: &CodeCell, range: &TextRange) -> anyhow::Result anyhow::Result Ok(OperationNode::new( cell.name.clone(), + execution_state_id, InputSignature::new(), OutputSignature::new(), Box::new(|_, x, _, _| async move { Ok(OperationFnOutput::with_value(x)) }.boxed()), @@ -62,6 +65,7 @@ pub fn code_cell(cell: &CodeCell, range: &TextRange) -> anyhow::Result anyhow::Result { +pub fn code_gen_cell(execution_state_id: ExecutionNodeId, cell: &LLMCodeGenCell, range: &TextRange) -> anyhow::Result { let LLMCodeGenCell { configuration, name, @@ -65,6 +65,7 @@ pub fn code_gen_cell(cell: &LLMCodeGenCell, range: &TextRange) -> anyhow::Result match provider { SupportedModelProviders::OpenAI => Ok(OperationNode::new( name.clone(), + execution_state_id, input_signature, output_signature, Box::new(move |s, payload, _, _| { diff --git a/toolchain/chidori-core/src/cells/embedding_cell.rs b/toolchain/chidori-core/src/cells/embedding_cell.rs index db45373..b62d01e 100644 --- a/toolchain/chidori-core/src/cells/embedding_cell.rs +++ b/toolchain/chidori-core/src/cells/embedding_cell.rs @@ -1,10 +1,11 @@ use futures_util::FutureExt; use crate::cells::{LLMEmbeddingCell, LLMPromptCell, TextRange}; +use crate::execution::execution::execution_graph::ExecutionNodeId; use crate::execution::primitives::operation::{InputItemConfiguration, InputSignature, InputType, OperationFnOutput, OperationNode, OutputItemConfiguration, OutputSignature}; use crate::library::std::ai::llm::ai_llm_run_embedding_model; #[tracing::instrument] -pub fn llm_embedding_cell(cell: &LLMEmbeddingCell, range: &TextRange) -> anyhow::Result { +pub fn llm_embedding_cell(execution_state_id: ExecutionNodeId, cell: &LLMEmbeddingCell, range: &TextRange) -> anyhow::Result { let LLMEmbeddingCell { function_invocation, configuration, @@ -46,6 +47,7 @@ pub fn llm_embedding_cell(cell: &LLMEmbeddingCell, range: &TextRange) -> anyhow: let is_function_invocation = function_invocation.clone(); Ok(OperationNode::new( name.clone(), + execution_state_id, input_signature, output_signature, Box::new(move |s, x, _, _| { diff --git a/toolchain/chidori-core/src/cells/llm_prompt_cell.rs b/toolchain/chidori-core/src/cells/llm_prompt_cell.rs index 3a4724b..b578c37 100644 --- a/toolchain/chidori-core/src/cells/llm_prompt_cell.rs +++ b/toolchain/chidori-core/src/cells/llm_prompt_cell.rs @@ -6,6 +6,7 @@ use crate::cells::{LLMPromptCell, SupportedModelProviders, TextRange}; use crate::execution::primitives::operation::{InputItemConfiguration, InputSignature, InputType, OperationFnOutput, OperationNode, OutputItemConfiguration, OutputSignature}; use crate::execution::primitives::serialized_value::{RkyvSerializedValue as RKV, RkyvSerializedValue, serialized_value_to_json_value}; use futures_util::FutureExt; +use crate::execution::execution::execution_graph::ExecutionNodeId; use crate::execution::execution::ExecutionState; use crate::library::std::ai::llm::ai_llm_run_embedding_model; @@ -13,7 +14,7 @@ use crate::library::std::ai::llm::ai_llm_run_embedding_model; /// LLM Prompt Cells allow notebooks to invoke language models to generate text. #[tracing::instrument] -pub fn llm_prompt_cell(cell: &LLMPromptCell, range: &TextRange) -> anyhow::Result { +pub fn llm_prompt_cell(execution_state_id: ExecutionNodeId, cell: &LLMPromptCell, range: &TextRange) -> anyhow::Result { match cell { LLMPromptCell::Chat { function_invocation, @@ -82,6 +83,7 @@ pub fn llm_prompt_cell(cell: &LLMPromptCell, range: &TextRange) -> anyhow::Resul match provider { SupportedModelProviders::OpenAI => Ok(OperationNode::new( name.clone(), + execution_state_id, input_signature, output_signature, Box::new(move |s, payload, _, _| { @@ -115,6 +117,7 @@ pub fn llm_prompt_cell(cell: &LLMPromptCell, range: &TextRange) -> anyhow::Resul } LLMPromptCell::Completion { .. } => Ok(OperationNode::new( None, + execution_state_id, InputSignature::new(), OutputSignature::new(), Box::new(|_, x, _, _| async move { Ok(OperationFnOutput::with_value(x)) }.boxed()), diff --git a/toolchain/chidori-core/src/cells/memory_cell.rs b/toolchain/chidori-core/src/cells/memory_cell.rs index d12caec..32ef99e 100644 --- a/toolchain/chidori-core/src/cells/memory_cell.rs +++ b/toolchain/chidori-core/src/cells/memory_cell.rs @@ -4,13 +4,14 @@ use crate::cells::{MemoryCell, SupportedMemoryProviders, TextRange}; use crate::execution::primitives::operation::{AsyncRPCCommunication, InputItemConfiguration, InputSignature, InputType, OperationFnOutput, OperationNode, OutputItemConfiguration, OutputSignature}; use futures_util::FutureExt; use serde_json::json; +use crate::execution::execution::execution_graph::ExecutionNodeId; use crate::execution::primitives::serialized_value::{json_value_to_serialized_value, RkyvSerializedValue, serialized_value_to_json_value}; use crate::library::std::ai::memory::in_memory::InMemoryVectorDb; /// Memory cells when first executed have no inputs. They initialize the connection to the memory store. /// Once initialized they become communicated with over the functions that they provide to the workspace. #[tracing::instrument] -pub fn memory_cell(cell: &MemoryCell, range: &TextRange) -> anyhow::Result { +pub fn memory_cell(execution_state_id: ExecutionNodeId, cell: &MemoryCell, range: &TextRange) -> anyhow::Result { match cell.provider { SupportedMemoryProviders::InMemory => { let mut input_signature = InputSignature::new(); @@ -41,6 +42,7 @@ pub fn memory_cell(cell: &MemoryCell, range: &TextRange) -> anyhow::Result>, + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "fn")] pub(crate) function_name: Option, pub model: String, + + #[serde(skip_serializing_if = "Option::is_none")] pub api_url: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub frequency_penalty: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub presence_penalty: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub stop: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub temperature: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub logit_bias: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub user: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub seed: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub top_p: Option, } diff --git a/toolchain/chidori-core/src/cells/template_cell.rs b/toolchain/chidori-core/src/cells/template_cell.rs index 839e32d..ea0399b 100644 --- a/toolchain/chidori-core/src/cells/template_cell.rs +++ b/toolchain/chidori-core/src/cells/template_cell.rs @@ -5,10 +5,11 @@ use crate::execution::primitives::operation::{InputItemConfiguration, InputSigna use crate::execution::primitives::serialized_value::{RkyvSerializedValue as RKV, serialized_value_to_json_value}; use futures_util::FutureExt; +use crate::execution::execution::execution_graph::ExecutionNodeId; /// Template cells leverage the same tooling as LLM Prompt Cells, but are used for more general templating. #[tracing::instrument] -pub fn template_cell(cell: &TemplateCell, range: &TextRange) -> anyhow::Result { +pub fn template_cell(execution_state_id: ExecutionNodeId, cell: &TemplateCell, range: &TextRange) -> anyhow::Result { let schema = chidori_prompt_format::templating::templates::analyze_referenced_partials(&cell.body); @@ -39,6 +40,7 @@ pub fn template_cell(cell: &TemplateCell, range: &TextRange) -> anyhow::Result anyhow::Result anyhow::Result { +pub fn web_cell(execution_state_id: ExecutionNodeId, cell: &WebserviceCell, range: &TextRange) -> anyhow::Result { let endpoints = crate::library::std::webserver::parse_configuration_string(&cell.configuration); let mut input_signature = InputSignature::new(); @@ -25,6 +26,7 @@ pub fn web_cell(cell: &WebserviceCell, range: &TextRange) -> anyhow::Result panic!("Cannot progress an execution state that is currently executing"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), }; println!("Inserting into graph {:?}", &resulting_state_id); state_id_to_state.deref_mut().insert(resulting_state_id.clone(), new_state.clone()); @@ -331,6 +332,7 @@ impl ExecutionGraph { let previous_state = match previous_state { ExecutionStateEvaluation::Complete(state) => state, ExecutionStateEvaluation::Executing(_) => panic!("Cannot step an execution state that is currently executing"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), }; let (new_state, outputs) = previous_state.step_execution(&self.graph_mutation_sender).await?; let resulting_state_id = self.progress_graph(new_state.clone()); @@ -359,6 +361,9 @@ impl ExecutionGraph { ExecutionStateEvaluation::Executing(_) => { return Err(anyhow!("Cannot mutate a graph that is currently executing")) }, + + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), + }; let eval = ExecutionStateEvaluation::Complete(final_state.clone()); @@ -407,6 +412,7 @@ mod tests { let state_id = Uuid::nil(); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(1))) }.boxed()), @@ -414,6 +420,7 @@ mod tests { None); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(2))) }.boxed()), @@ -421,6 +428,7 @@ mod tests { None); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["a", "b"]), OutputSignature::new(), Box::new(|_, args, _, _| { @@ -487,6 +495,7 @@ mod tests { let state_id = Uuid::nil(); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(0))) }.boxed()), @@ -494,6 +503,7 @@ mod tests { None); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(1))) }.boxed()), @@ -534,6 +544,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(0))) }.boxed()), @@ -542,6 +553,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(1))) }.boxed()), @@ -550,6 +562,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(2))) }.boxed()), @@ -615,6 +628,7 @@ mod tests { for x in 0..4 { let (_, mut nstate) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(move |_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(x))) }.boxed()), @@ -678,6 +692,7 @@ mod tests { for x in 0..5 { let (_, mut nstate) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), if x == 0 { InputSignature::new() } else if x == 4 {InputSignature::from_args_list(vec!["1", "2"]) } else { InputSignature::from_args_list(vec!["1"]) }, OutputSignature::new(), Box::new(move |_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(x))) }.boxed()), @@ -748,6 +763,7 @@ mod tests { // We start with the number 1 at node 0 let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(1))) }.boxed()), @@ -771,6 +787,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["1"]), OutputSignature::new(), Box::new(f1), @@ -779,6 +796,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["1"]), OutputSignature::new(), Box::new(f1), @@ -787,6 +805,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["1"]), OutputSignature::new(), Box::new(f1), @@ -795,6 +814,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["1"]), OutputSignature::new(), Box::new(f1), @@ -803,6 +823,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["1"]), OutputSignature::new(), Box::new(f1), @@ -908,6 +929,7 @@ mod tests { // We start with the number 1 at node 0 let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(1))) }.boxed()), @@ -933,6 +955,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(f_side_effect), @@ -940,6 +963,7 @@ mod tests { None); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(f_side_effect), @@ -996,6 +1020,7 @@ mod tests { // We start with the number 0 at node 0 let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::new(), OutputSignature::new(), Box::new(|_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(0))) }.boxed()), @@ -1032,6 +1057,7 @@ mod tests { let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(f_v1), @@ -1039,6 +1065,7 @@ mod tests { None); let (_, mut state) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(f_v1), @@ -1070,6 +1097,7 @@ mod tests { if let ExecutionStateEvaluation::Complete(x_state) = x_state { let (_, mut state) = x_state.upsert_operation(OperationNode::new( None, + Uuid::nil(), InputSignature::from_args_list(vec!["0"]), OutputSignature::new(), Box::new(f_v2), @@ -1109,6 +1137,7 @@ mod tests { for x in 0..5 { let (_, mut nstate) = state.upsert_operation(OperationNode::new( None, + Uuid::nil(), if x == 0 { InputSignature::new() } else if x == 4 {InputSignature::from_args_list(vec!["1", "2"]) } else { InputSignature::from_args_list(vec!["1"]) }, OutputSignature::new(), Box::new(move |_, _args, _, _| async move { Ok(OperationFnOutput::with_value(RSV::Number(x))) }.boxed()), diff --git a/toolchain/chidori-core/src/execution/execution/execution_state.rs b/toolchain/chidori-core/src/execution/execution/execution_state.rs index 525b8fc..461f349 100644 --- a/toolchain/chidori-core/src/execution/execution/execution_state.rs +++ b/toolchain/chidori-core/src/execution/execution/execution_state.rs @@ -74,6 +74,7 @@ impl Future for FutureExecutionState { #[derive(Clone)] pub enum ExecutionStateEvaluation { + Error, Complete(ExecutionState), Executing(Arc) } @@ -83,6 +84,7 @@ impl ExecutionStateEvaluation { match self { ExecutionStateEvaluation::Complete(ref state) => state.state_get(operation_id), ExecutionStateEvaluation::Executing(ref future_state) => unreachable!("Cannot get state from a future state"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), } } @@ -90,6 +92,7 @@ impl ExecutionStateEvaluation { match self { ExecutionStateEvaluation::Complete(ref state) => state.state_get(operation_id).map(|o| &o.output), ExecutionStateEvaluation::Executing(ref future_state) => unreachable!("Cannot get state from a future state"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), } } } @@ -99,6 +102,7 @@ impl Debug for ExecutionStateEvaluation { match self { ExecutionStateEvaluation::Complete(ref state) => f.debug_tuple("Complete").field(state).finish(), ExecutionStateEvaluation::Executing(ref future_state) => f.debug_tuple("Executing").field(&format!("Future state evaluating")).finish(), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), } } } @@ -356,13 +360,13 @@ impl ExecutionState { op_id: Option, ) -> anyhow::Result<(ExecutionState, usize)> { let mut op = match &cell { - CellTypes::Code(c, r) => crate::cells::code_cell::code_cell(c, r), - CellTypes::Prompt(c, r) => crate::cells::llm_prompt_cell::llm_prompt_cell(c, r), - CellTypes::Embedding(c, r) => crate::cells::embedding_cell::llm_embedding_cell(c, r), - CellTypes::Web(c, r) => crate::cells::web_cell::web_cell(c, r), - CellTypes::Template(c, r) => crate::cells::template_cell::template_cell(c, r), - CellTypes::Memory(c, r) => crate::cells::memory_cell::memory_cell(c, r), - CellTypes::CodeGen(c, r) => crate::cells::code_gen_cell::code_gen_cell(c, r), + CellTypes::Code(c, r) => crate::cells::code_cell::code_cell(self.id.clone(), c, r), + CellTypes::Prompt(c, r) => crate::cells::llm_prompt_cell::llm_prompt_cell(self.id.clone(), c, r), + CellTypes::Embedding(c, r) => crate::cells::embedding_cell::llm_embedding_cell(self.id.clone(), c, r), + CellTypes::Web(c, r) => crate::cells::web_cell::web_cell(self.id.clone(), c, r), + CellTypes::Template(c, r) => crate::cells::template_cell::template_cell(self.id.clone(), c, r), + CellTypes::Memory(c, r) => crate::cells::memory_cell::memory_cell(self.id.clone(), c, r), + CellTypes::CodeGen(c, r) => crate::cells::code_gen_cell::code_gen_cell(self.id.clone(), c, r), }?; op.attach_cell(cell.clone()); let new_state = self.clone(); @@ -426,12 +430,6 @@ impl ExecutionState { #[tracing::instrument] fn get_possible_dependencies(new_state: &ExecutionState) -> anyhow::Result<(HashMap, HashMap)> { - // TODO: when there is a dependency on a function invocation we need to - // instantiate a new instance of the function operation node. - // It itself is not part of the call graph until it has such a dependency. - - // TODO: Store trigger-able functions that may be passed as values as well - let mut available_values = HashMap::new(); let mut available_functions = HashMap::new(); @@ -571,22 +569,22 @@ impl ExecutionState { let mut c = c.clone(); c.function_invocation = Some(clone_function_name.to_string()); - crate::cells::code_cell::code_cell(&c, r)? + crate::cells::code_cell::code_cell(Uuid::nil(), &c, r)? } CellTypes::Prompt(c, r) => { let mut c = c.clone(); match c { LLMPromptCell::Chat{ref mut function_invocation, ..} => { *function_invocation = true; - crate::cells::llm_prompt_cell::llm_prompt_cell(&c, r)? + crate::cells::llm_prompt_cell::llm_prompt_cell(Uuid::nil(), &c, r)? } _ => { - crate::cells::llm_prompt_cell::llm_prompt_cell(&c, r)? + crate::cells::llm_prompt_cell::llm_prompt_cell(Uuid::nil(), &c, r)? } } } CellTypes::Embedding(c, r) => { - crate::cells::embedding_cell::llm_embedding_cell(&c, r)? + crate::cells::embedding_cell::llm_embedding_cell(Uuid::nil(), &c, r)? } _ => { unreachable!("Unsupported cell type"); diff --git a/toolchain/chidori-core/src/execution/primitives/operation.rs b/toolchain/chidori-core/src/execution/primitives/operation.rs index 9f6d0dd..16938ee 100644 --- a/toolchain/chidori-core/src/execution/primitives/operation.rs +++ b/toolchain/chidori-core/src/execution/primitives/operation.rs @@ -10,6 +10,7 @@ use std::sync::mpsc::{Receiver, Sender}; use tokio::sync::oneshot; use futures_util::FutureExt; use tracing::{Level, span}; +use uuid::Uuid; use crate::cells::{CellTypes, CodeCell, SupportedLanguage, TextRange}; use crate::execution::execution::execution_graph::ExecutionNodeId; use crate::execution::execution::ExecutionState; @@ -297,6 +298,7 @@ pub type OperationFn = dyn Fn( pub struct OperationNode { pub(crate) id: usize, pub(crate) name: Option, + pub created_at_state_id: ExecutionNodeId, /// Should this operation run in a background thread pub is_long_running_background_thread: bool, @@ -366,6 +368,7 @@ impl Default for OperationNode { OperationNode { id: 0, name: None, + created_at_state_id: Uuid::nil(), is_long_running_background_thread: false, cell: CellTypes::Code(CodeCell { name: None, @@ -389,11 +392,13 @@ impl Default for OperationNode { impl OperationNode { pub(crate) fn new( name: Option, + created_at: ExecutionNodeId, input_signature: InputSignature, output_signature: OutputSignature, f: Box, ) -> Self { let mut node = OperationNode::default(); + node.created_at_state_id = created_at; node.signature.input_signature = input_signature; node.signature.output_signature = output_signature; node.operation = f; @@ -456,6 +461,7 @@ mod tests { async fn test_async_communication_rpc() { let (async_rpc_communication, rpc_sender, callable_interface_receiver) = AsyncRPCCommunication::new(); let op = OperationNode { + created_at_state_id: Uuid::nil(), id: 0, name: None, is_long_running_background_thread: false, diff --git a/toolchain/chidori-core/src/library/std/scheduling/local/mod.rs b/toolchain/chidori-core/src/library/std/scheduling/local/mod.rs index f2a2293..f7f6d33 100644 --- a/toolchain/chidori-core/src/library/std/scheduling/local/mod.rs +++ b/toolchain/chidori-core/src/library/std/scheduling/local/mod.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, HashSet}; use tokio_cron_scheduler::{Job, JobScheduler, JobSchedulerError}; +use uuid::Uuid; use crate::cells::{CellTypes, CodeCell, ScheduleCell}; use crate::execution::execution::ExecutionState; use crate::execution::primitives::serialized_value::{RkyvObjectBuilder, RkyvSerializedValue}; @@ -56,10 +57,10 @@ pub async fn run_cron( let mut c = c.clone(); c.function_invocation = Some(function_name.clone()); - crate::cells::code_cell::code_cell(&c, r) + crate::cells::code_cell::code_cell(Uuid::nil(), &c, r) } CellTypes::Prompt(c, r) => { - crate::cells::llm_prompt_cell::llm_prompt_cell(&c, r) + crate::cells::llm_prompt_cell::llm_prompt_cell(Uuid::nil(), &c, r) } _ => { unreachable!("Unsupported cell type"); diff --git a/toolchain/chidori-core/src/library/std/webserver/mod.rs b/toolchain/chidori-core/src/library/std/webserver/mod.rs index c40239d..8d675e6 100644 --- a/toolchain/chidori-core/src/library/std/webserver/mod.rs +++ b/toolchain/chidori-core/src/library/std/webserver/mod.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use tokio::task::JoinHandle; use tonic::IntoRequest; +use uuid::Uuid; use crate::cells::{CellTypes, CodeCell, WebserviceCell, WebserviceCellEndpoint}; use crate::execution::execution::ExecutionState; use crate::execution::primitives::serialized_value::{json_value_to_serialized_value, RkyvObjectBuilder, RkyvSerializedValue, serialized_value_to_json_value}; @@ -96,13 +97,13 @@ pub async fn run_webservice( let mut c = c.clone(); c.function_invocation = Some(function_name.clone()); - crate::cells::code_cell::code_cell(&c, r) + crate::cells::code_cell::code_cell(Uuid::nil(), &c, r) } CellTypes::Prompt(c, r) => { - crate::cells::llm_prompt_cell::llm_prompt_cell(&c, r) + crate::cells::llm_prompt_cell::llm_prompt_cell(Uuid::nil(), &c, r) } CellTypes::Template(c, r) => { - crate::cells::template_cell::template_cell(&c, r) + crate::cells::template_cell::template_cell(Uuid::nil(), &c, r) } _ => { unreachable!("Unsupported cell type"); @@ -149,13 +150,13 @@ pub async fn run_webservice( let mut c = c.clone(); c.function_invocation = Some(function_name.clone()); - crate::cells::code_cell::code_cell(&c, r) + crate::cells::code_cell::code_cell(Uuid::nil(), &c, r) }, CellTypes::Prompt(c, r) => { - crate::cells::llm_prompt_cell::llm_prompt_cell(&c, r) + crate::cells::llm_prompt_cell::llm_prompt_cell(Uuid::nil(), &c, r) }, CellTypes::Template(c, r) => { - crate::cells::template_cell::template_cell(&c, r) + crate::cells::template_cell::template_cell(Uuid::nil(), &c, r) } _ => { unreachable!("Unsupported cell type"); diff --git a/toolchain/chidori-core/src/sdk/entry.rs b/toolchain/chidori-core/src/sdk/entry.rs index e473dec..f93a23a 100644 --- a/toolchain/chidori-core/src/sdk/entry.rs +++ b/toolchain/chidori-core/src/sdk/entry.rs @@ -15,6 +15,7 @@ use std::path::Path; use std::sync::{Arc, mpsc, Mutex, MutexGuard}; use std::sync::mpsc::{Receiver, Sender}; use std::thread::sleep; +use std::time::Duration; use chumsky::prelude::any; use petgraph::graphmap::DiGraphMap; use serde::ser::SerializeMap; @@ -26,6 +27,10 @@ use crate::utils::telemetry::{init_internal_telemetry, TraceEvents}; /// This is an SDK for building execution graphs. It is designed to be used interactively. +/// + + +const USER_INTERACTION_RECEIVER_TIMEOUT_MS: u64 = 500; type Func = fn(RKV) -> RKV; @@ -92,14 +97,14 @@ impl InstancedEnvironment { ids.push(self.upsert_cell(cell_holder.cell.clone(), cell_holder.op_id)?); } else { // TODO: remove these unwraps and handle this better - ids.push((cell_holder.applied_at.unwrap(), cell_holder.op_id.unwrap())); + ids.push((cell_holder.applied_at, cell_holder.op_id.unwrap())); } } let mut shared_state = self.shared_state.lock().unwrap(); for (i, cell) in shared_state.editor_cells.iter_mut().enumerate() { let (applied_at, op_id) = ids[i]; - cell.applied_at = Some(applied_at); + cell.applied_at = applied_at.clone(); cell.op_id = Some(op_id); cell.needs_update = false; } @@ -142,18 +147,20 @@ impl InstancedEnvironment { println!("Looping UserInteraction"); // let closure_span = tracing::span!(parent: ¤t_span_id, tracing::Level::INFO, "execution_instance_loop"); // let _enter = closure_span.enter(); - if let Ok(message) = self.env_rx.try_recv() { + + + if let Ok(message) = self.env_rx.recv_timeout(Duration::from_millis(USER_INTERACTION_RECEIVER_TIMEOUT_MS)) { println!("Received message from user: {:?}", message); match message { UserInteractionMessage::Step => { self.playback_state = PlaybackState::Step; }, UserInteractionMessage::Play => { - self.get_state().render_dependency_graph(); + self.get_state_at_current_execution_head().render_dependency_graph(); self.playback_state = PlaybackState::Running; }, UserInteractionMessage::Pause => { - self.get_state().render_dependency_graph(); + self.get_state_at_current_execution_head().render_dependency_graph(); self.playback_state = PlaybackState::Paused; }, UserInteractionMessage::ReloadCells => { @@ -177,7 +184,12 @@ impl InstancedEnvironment { // TODO: keep a separate mapping of cells so we don't need to lock operations for k in state.operation_by_id.values() { let op = k.lock().unwrap(); - cells.push(op.cell.clone()); + cells.push(CellHolder { + cell: op.cell.clone(), + op_id: Some(op.id.clone()), + applied_at: op.created_at_state_id.clone(), + needs_update: false, + }); } let mut ss = self.shared_state.lock().unwrap(); ss.at_execution_state_cells = cells.clone(); @@ -223,14 +235,16 @@ impl InstancedEnvironment { pub fn get_state_at(&self, id: ExecutionNodeId) -> ExecutionState { match self.db.get_state_at_id(id).unwrap() { ExecutionStateEvaluation::Complete(s) => s, - ExecutionStateEvaluation::Executing(_) => panic!("get_state_at, failed, still executing") + ExecutionStateEvaluation::Executing(_) => panic!("get_state_at, failed, still executing"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), } } - pub fn get_state(&self) -> ExecutionState { + pub fn get_state_at_current_execution_head(&self) -> ExecutionState { match self.db.get_state_at_id(self.execution_head_state_id).unwrap() { ExecutionStateEvaluation::Complete(s) => s, - ExecutionStateEvaluation::Executing(_) => panic!("get_state, failed, still executing") + ExecutionStateEvaluation::Executing(_) => panic!("get_state, failed, still executing"), + ExecutionStateEvaluation::Error => unreachable!("Cannot get state from a future state"), } } @@ -243,7 +257,12 @@ impl InstancedEnvironment { // TODO: keep a separate mapping of cells so we don't need to lock operations for k in s.operation_by_id.values() { let op = k.lock().unwrap(); - cells.push(op.cell.clone()); + cells.push(CellHolder { + cell: op.cell.clone(), + op_id: Some(op.id.clone()), + applied_at: op.created_at_state_id.clone(), + needs_update: false, + }); } sender.send(EventsFromRuntime::ExecutionStateCellsViewUpdated(cells)).unwrap(); } @@ -322,14 +341,14 @@ pub enum EventsFromRuntime { StateAtId(ExecutionNodeId, ExecutionState), UpdateExecutionHead(ExecutionNodeId), ReceivedChatMessage(String), - ExecutionStateCellsViewUpdated(Vec), + ExecutionStateCellsViewUpdated(Vec), } #[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] pub struct CellHolder { pub cell: CellTypes, pub op_id: Option, - pub applied_at: Option, + pub applied_at: ExecutionNodeId, needs_update: bool } @@ -339,7 +358,7 @@ pub struct SharedState { execution_state_head_id: ExecutionNodeId, latest_state: Option, editor_cells: Vec, - at_execution_state_cells: Vec, + at_execution_state_cells: Vec, } @@ -471,7 +490,7 @@ impl Chidori { if prev_cell.cell != cell { new_cells_state.push(CellHolder { cell, - applied_at: None, + applied_at: Uuid::nil(), op_id: prev_cell.op_id, needs_update: true }); @@ -481,7 +500,7 @@ impl Chidori { } else { new_cells_state.push(CellHolder { cell, - applied_at: None, + applied_at: Uuid::nil(), op_id: None, needs_update: true }); @@ -578,18 +597,18 @@ mod tests { None)?; assert_eq!(op_id_y, 1); // env.resolve_dependencies_from_input_signature(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); env.step().await; assert_eq!( - env.get_state().state_get_value(&op_id_x), + env.get_state_at_current_execution_head().state_get_value(&op_id_x), Some(&RkyvObjectBuilder::new().insert_number("x", 20).build()) ); - assert_eq!(env.get_state().state_get_value(&op_id_y), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&op_id_y), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&op_id_x), - Some(&RkyvObjectBuilder::new().insert_number("x", 20).build())); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&op_id_x), + Some(&RkyvObjectBuilder::new().insert_number("x", 20).build())); assert_eq!( - env.get_state().state_get_value(&op_id_y), + env.get_state_at_current_execution_head().state_get_value(&op_id_y), Some(&RkyvObjectBuilder::new().insert_number("y", 21).build()) ); Ok(()) @@ -626,7 +645,7 @@ mod tests { }, TextRange::default()), None)?; assert_eq!(op_id_y, 1); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; assert_eq!( out.as_ref().unwrap().first().unwrap().0, @@ -639,7 +658,7 @@ mod tests { .build() ); assert_eq!( - env.get_state().state_get_value(&op_id_x), + env.get_state_at_current_execution_head().state_get_value(&op_id_x), Some( &RkyvObjectBuilder::new() .insert_string("x", "Here is a sample string".to_string()) @@ -658,7 +677,7 @@ mod tests { .build() ); assert_eq!( - env.get_state().state_get_value(&op_id_y), + env.get_state_at_current_execution_head().state_get_value(&op_id_y), Some(&RkyvObjectBuilder::new() .insert_string("example", "Here".to_string()) .build()) @@ -692,17 +711,17 @@ mod tests { }, TextRange::default()), None)?; assert_eq!(op_id_y, 1); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); dbg!(env.step().await); assert_eq!( - env.get_state().state_get_value(&op_id_x), + env.get_state_at_current_execution_head().state_get_value(&op_id_x), Some(&RkyvObjectBuilder::new().insert_number("x", 20).build()) ); - assert_eq!(env.get_state().state_get_value(&op_id_y), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&op_id_y), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&op_id_x), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&op_id_x), None); assert_eq!( - env.get_state().state_get_value(&op_id_y), + env.get_state_at_current_execution_head().state_get_value(&op_id_y), Some(&RkyvObjectBuilder::new().insert_number("y", 21).build()) ); Ok(()) @@ -734,20 +753,20 @@ mod tests { }, TextRange::default()), None)?; assert_eq!(id, 1); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); env.step().await; // Empty object from the function declaration assert_eq!( - env.get_state().state_get_value(&0), + env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new().insert_string("add", String::from("function")).build()) ); - assert_eq!(env.get_state().state_get_value(&1), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&1), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&0), + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new().insert_string("add", String::from("function")).build()) ); assert_eq!( - env.get_state().state_get_value(&1), + env.get_state_at_current_execution_head().state_get_value(&1), Some(&RkyvObjectBuilder::new().insert_number("y", 5).build()) ); env.shutdown().await; @@ -779,21 +798,21 @@ mod tests { }, TextRange::default()), None)?; assert_eq!(id, 1); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); env.step().await; // Function declaration cell assert_eq!( - env.get_state().state_get_value(&0), + env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new().insert_string("add", String::from("function")).build()) ); - assert_eq!(env.get_state().state_get_value(&1), + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&1), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&0), + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new().insert_string("add", String::from("function")).build()) ); assert_eq!( - env.get_state().state_get_value(&1), + env.get_state_at_current_execution_head().state_get_value(&1), Some(&RkyvObjectBuilder::new().insert_number("y", 5).build()) ); env.shutdown().await; @@ -818,23 +837,23 @@ mod tests { }).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); env.step().await; // Function declaration cell assert_eq!( - env.get_state().state_get_value(&0), + env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new() .insert_number("v", 40) .insert_string("squared_value", "function".to_string()) .build()) ); - assert_eq!(env.get_state().state_get_value(&1), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&1), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&0), - Some(&RkyvObjectBuilder::new().insert_number("v", 40).build()) + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&0), + Some(&RkyvObjectBuilder::new().insert_number("v", 40).build()) ); assert_eq!( - env.get_state().state_get_value(&1), + env.get_state_at_current_execution_head().state_get_value(&1), Some(&RkyvObjectBuilder::new().insert_number("z", 640000).insert_number("y", 800).build()) ); env.shutdown().await; @@ -863,20 +882,20 @@ mod tests { "# }).unwrap(); let mut env = ee.get_instance().unwrap(); - let s = env.get_state(); + let s = env.get_state_at_current_execution_head(); env.reload_cells(); s.render_dependency_graph(); env.step().await; // Function declaration cell assert_eq!( - env.get_state().state_get_value(&0), + env.get_state_at_current_execution_head().state_get_value(&0), Some(&RkyvObjectBuilder::new().build()) ); - assert_eq!(env.get_state().state_get_value(&1), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&1), None); env.step().await; - assert_eq!(env.get_state().state_get_value(&0), None); + assert_eq!(env.get_state_at_current_execution_head().state_get_value(&0), None); assert_eq!( - env.get_state().state_get_value(&1), + env.get_state_at_current_execution_head().state_get_value(&1), Some(&RkyvObjectBuilder::new().insert_number("y", 5).build()) ); } @@ -903,7 +922,7 @@ mod tests { }).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); // This will initialize the service env.step().await; @@ -946,7 +965,7 @@ mod tests { }).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); // This will initialize the service env.step().await; @@ -974,7 +993,7 @@ mod tests { ee.load_md_directory(Path::new("./examples/core1_simple_math")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await?; assert_eq!(out[0].1.output, RkyvObjectBuilder::new().insert_number("x", 20).build()); let out = env.step().await?; @@ -990,7 +1009,7 @@ mod tests { ee.load_md_directory(Path::new("./examples/core2_marshalling")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let mut out = env.step().await?; assert_eq!(out[0].0, 0); assert_eq!(out[0].1.output, RkyvObjectBuilder::new() @@ -1055,7 +1074,7 @@ mod tests { ee.load_md_directory(Path::new("./examples/core3_function_invocations")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let mut out = env.step().await?; assert_eq!(out[0].0, 0); assert_eq!(out[0].1.output, RkyvObjectBuilder::new().insert_string("add_two", "function".to_string()).build()); @@ -1069,7 +1088,7 @@ mod tests { let mut out = env.step().await?; assert_eq!(out[0].0, 3); assert_eq!(out[0].1.stderr.contains(&"OK".to_string()), true); - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); env.shutdown().await; Ok(()) } @@ -1080,7 +1099,7 @@ mod tests { ee.load_md_directory(Path::new("./examples/core4_async_function_invocations")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let mut out = env.step().await?; assert_eq!(out[0].0, 0); assert_eq!(out[0].1.output, RkyvObjectBuilder::new().insert_string("add_two", "function".to_string()).build()); @@ -1094,7 +1113,7 @@ mod tests { let mut out = env.step().await?; assert_eq!(out[0].0, 3); assert_eq!(out[0].1.stderr.contains(&"OK".to_string()), true); - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); env.shutdown().await; Ok(()) } @@ -1106,7 +1125,7 @@ mod tests { ee.load_md_directory(Path::new("./examples/core5_prompts_invoked_as_functions")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; dbg!(out); let out = env.step().await; @@ -1114,7 +1133,7 @@ mod tests { let out = env.step().await; dbg!(out); let out = env.step().await; - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); env.shutdown().await; Ok(()) } @@ -1125,13 +1144,13 @@ mod tests { ee.load_md_directory(Path::new("./examples/core6_prompts_leveraging_function_calling")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; let out = env.step().await; let out = env.step().await; let out = env.step().await; let out = env.step().await; - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); } #[tokio::test(flavor = "multi_thread", worker_threads = 4)] @@ -1140,12 +1159,12 @@ mod tests { ee.load_md_directory(Path::new("./examples/core7_rag_stateful_memory_cells")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; let out = env.step().await; let out = env.step().await; let out = env.step().await; - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); } #[tokio::test(flavor = "multi_thread", worker_threads = 4)] @@ -1154,12 +1173,12 @@ mod tests { ee.load_md_directory(Path::new("./examples/core8_prompt_code_generation_and_execution")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; let out = env.step().await; let out = env.step().await; let out = env.step().await; - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); } #[tokio::test(flavor = "multi_thread", worker_threads = 4)] @@ -1168,12 +1187,12 @@ mod tests { ee.load_md_directory(Path::new("./examples/core9_multi_agent_simulation")).unwrap(); let mut env = ee.get_instance().unwrap(); env.reload_cells(); - env.get_state().render_dependency_graph(); + env.get_state_at_current_execution_head().render_dependency_graph(); let out = env.step().await; let out = env.step().await; let out = env.step().await; let out = env.step().await; - assert_eq!(env.get_state().have_all_operations_been_set_at_least_once(), true); + assert_eq!(env.get_state_at_current_execution_head().have_all_operations_been_set_at_least_once(), true); } } diff --git a/toolchain/chidori-debugger/Cargo.toml b/toolchain/chidori-debugger/Cargo.toml index 0bb3f7e..5613c88 100644 --- a/toolchain/chidori-debugger/Cargo.toml +++ b/toolchain/chidori-debugger/Cargo.toml @@ -8,6 +8,8 @@ homepage.workspace = true repository.workspace = true [dependencies] +objc = "0.2" +winit = "=0.29.15" bevy = "0.13.2" egui = { version = "0.27.2", default-features = false, features = ["bytemuck"] } image = { version = "0.24", features = ["jpeg", "png"] } @@ -45,6 +47,7 @@ crossbeam-channel = "0.5.12" wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" webbrowser = { version = "0.8.2", optional = true } +serde_yaml = "0.8.26" [target.'cfg(not(any(target_arch = "wasm32", target_os = "android")))'.dependencies] arboard = { version = "3.2.0", optional = true } diff --git a/toolchain/chidori-debugger/src/chidori.rs b/toolchain/chidori-debugger/src/chidori.rs index 795e735..3697911 100644 --- a/toolchain/chidori-debugger/src/chidori.rs +++ b/toolchain/chidori-debugger/src/chidori.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use std::sync::mpsc::TryRecvError; +use std::sync::mpsc::{RecvTimeoutError, TryRecvError}; use std::time::Duration; use bevy::app::{App, Startup, Update}; use bevy::input::ButtonInput; -use bevy::prelude::{Commands, default, KeyCode, NextState, Res, ResMut, Resource}; +use bevy::prelude::{Commands, default, KeyCode, Local, NextState, Res, ResMut, Resource}; use crate::bevy_egui::{EguiContexts}; use egui; -use egui::{Color32, Frame, Id, Margin, Response, RichText, vec2, Visuals, Widget}; +use egui::{Color32, FontFamily, Frame, Id, Margin, Response, RichText, vec2, Visuals, Widget}; use egui::panel::TopBottomSide; use egui_tiles::{Tile, TileId}; use notify_debouncer_full::{ @@ -34,6 +34,8 @@ use chidori_core::utils::telemetry::TraceEvents; use crate::{GameState, tokio_tasks}; +const RECV_RUNTIME_EVENT_TIMEOUT_MS: u64 = 100; + #[derive(Debug)] pub struct Pane { pub tile_id: Option, @@ -236,7 +238,7 @@ pub struct ChidoriLogMessages { #[derive(Resource)] pub struct ChidoriCells { pub editor_cells: Vec, - pub state_cells: Vec, + pub state_cells: Vec, } #[derive(Resource)] @@ -423,7 +425,7 @@ fn setup(mut commands: Commands, runtime: ResMut runtime.spawn_background_task(|mut ctx| async move { loop { - match runtime_event_receiver.try_recv() { + match runtime_event_receiver.recv_timeout(Duration::from_millis(RECV_RUNTIME_EVENT_TIMEOUT_MS)) { Ok(msg) => { println!("Received from runtime: {:?}", &msg); match msg { @@ -498,8 +500,8 @@ fn setup(mut commands: Commands, runtime: ResMut } } Err(e) => match e { - TryRecvError::Empty => {} - TryRecvError::Disconnected => { + RecvTimeoutError::Timeout => {} + RecvTimeoutError::Disconnected => { println!("Runtime channel disconnected"); break; } @@ -549,15 +551,118 @@ pub fn update_gui( runtime: ResMut, mut internal_state: ResMut, mut state: ResMut>, + mut displayed_example_desc: Local> ) { if internal_state.display_example_modal { - render_example_selection_modal(&mut contexts, &mut internal_state); + let mut contexts1 = &mut contexts; + let mut internal_state1 = &mut internal_state; + egui::CentralPanel::default() + .frame( + Frame::default() + .fill(Color32::from_hex("#222222").unwrap()) + .inner_margin(16.0) + .outer_margin(100.0) + .rounding(16.0), + ) + .show(contexts1.ctx_mut(), |ui| { + ui.add_space(12.0); + let mut frame = egui::Frame::default().inner_margin(16.0).begin(ui); + { + let mut ui = &mut frame.content_ui; + ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 12.0); + // Add widgets inside the frame + ui.horizontal(|ui| { + ui.vertical(|ui| { + ui.label("Load Example:"); + ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 8.0); + let buttons_text_load = vec![ + ("Core 1: Simple Math", EXAMPLES_CORE1, "Demonstrates simple arithmetic between cells, and that values can be passed between Python and JavaScript runtimes."), + ("Core 2: Marshalling", EXAMPLES_CORE2, "All of the types that we can successfully pass between runtimes and that are preserved by our execution engine."), + ("Core 3: Function Invocations", EXAMPLES_CORE3, "Demonstrates what function execution looks like when using Chidori. Explore how states are preserved and the ability to revert between them with re-execution."), + ("Core 4: Async Function Invocations", EXAMPLES_CORE4, "Function invocations default to being asynchronous."), + ("Core 5: Prompts Invoked as Functions", EXAMPLES_CORE5, "We treat prompts as first class resources, this demonstrates how prompts are invokable as functions."), + ( + "Core 6: Prompts Leveraging Function Calling", + EXAMPLES_CORE6, "Prompts may import functions and invoke those in order to accomplish their instructions." + + ), + ("Core 7: Rag Stateful Memory Cells", EXAMPLES_CORE7, "Cells preserve their internal state, we provide a specialized API for embeddings which demonstrates this behavior, exposing functions for interacting with that state."), + ( + "Core 8: Prompt Code Generation and Execution", + EXAMPLES_CORE8, "Chidori is designed for L4-L5 agents, new behaviors can be generated on the fly via code generation." + ), + ("Core 9: Multi-Agent Simulation", EXAMPLES_CORE9, "desc"), + ]; + let mut is_a_button_hovered = false; + for button in buttons_text_load { + let res = with_cursor(ui.button(button.0)); + if res.hovered() { + is_a_button_hovered = true; + *displayed_example_desc = Some((button.0.to_string(), button.1.to_string(), button.2.to_string())); + } + if res.clicked() { + internal_state1.load_string(button.1); + } + } + if is_a_button_hovered == false { + *displayed_example_desc = None; + } + }); + + if let Some((title, code, desc)) = &*displayed_example_desc { + ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { + let mut frame = egui::Frame::default().outer_margin(Margin::symmetric(64.0, 0.0)).inner_margin(64.0).rounding(6.0).fill(Color32::from_hex("#111111").unwrap()).begin(ui); + { + let mut ui = &mut frame.content_ui; + let mut code_mut = code.to_string(); + ui.set_max_width(800.0); + ui.label(title); + ui.add_space(16.0); + ui.label(desc); + ui.add_space(16.0); + egui::ScrollArea::new([false, true]) // Horizontal: false, Vertical: true + .max_width(800.0) + .max_height(600.0) + .show(ui, |ui| { + ui.add( + egui::TextEdit::multiline(&mut code_mut) + .font(egui::FontId::new(14.0, FontFamily::Monospace)) + .code_editor() + .lock_focus(true) + .desired_width(f32::INFINITY) + .margin(Margin::symmetric(8.0, 8.0)) + + ); + }); + } + frame.end(ui); + }); + } + + // ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { + // ui.add_space(ui.available_height() / 2.0 - 256.0); // Center vertically + // egui::Image::new(egui::include_image!("../assets/images/tblogo-white.png")) + // .fit_to_exact_size(vec2(512.0, 512.0)) + // .rounding(5.0) + // .ui(ui); + // }); + + // ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + // egui::Image::new(egui::include_image!("../assets/images/tblogo-white.png")) + // .fit_to_exact_size(vec2(512.0, 512.0)) + // .rounding(5.0) + // .ui(ui); + // }); + }); + } + frame.end(ui); + }); } else { egui::CentralPanel::default() .frame(egui::Frame::default().outer_margin(Margin { left: 0.0, right: 0.0, - top: 40.0, + top: 48.0, bottom: 0.0, })) .show(contexts.ctx_mut(), |ui| { @@ -573,7 +678,7 @@ pub fn update_gui( // ui.text_edit_multiline(&mut text); // a simple button opening the dialog let mut frame = egui::Frame::default() - .inner_margin(Margin::symmetric(8.0, 4.0)) + .inner_margin(Margin::symmetric(8.0, 8.0)) .begin(ui); { let mut ui = &mut frame.content_ui; @@ -591,6 +696,7 @@ pub fn update_gui( } }); } + ui.add_space(8.0); ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 8.0); if with_cursor(ui.button("Run")).clicked() { @@ -603,69 +709,13 @@ pub fn update_gui( internal_state.step(); } }); + } frame.end(ui); }, ); } -fn render_example_selection_modal(mut contexts: &mut EguiContexts, mut internal_state: &mut ResMut) { - egui::CentralPanel::default() - .frame( - Frame::default() - .fill(Color32::from_hex("#222222").unwrap()) - .inner_margin(16.0) - .outer_margin(100.0) - .rounding(5.0), - ) - .show(contexts.ctx_mut(), |ui| { - ui.heading("Chidori"); - ui.add_space(12.0); - let mut frame = egui::Frame::default().inner_margin(16.0).begin(ui); - { - let mut ui = &mut frame.content_ui; - ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 12.0); - // Add widgets inside the frame - ui.horizontal(|ui| { - ui.vertical(|ui| { - ui.label("Load Example:"); - ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 8.0); - let buttons_text_load = vec![ - ("Core 1: Simple Math", EXAMPLES_CORE1), - ("Core 2: Marshalling", EXAMPLES_CORE2), - ("Core 3: Function Invocations", EXAMPLES_CORE3), - ("Core 4: Async Function Invocations", EXAMPLES_CORE4), - ("Core 5: Prompts Invoked as Functions", EXAMPLES_CORE5), - ( - "Core 6: Prompts Leveraging Function Calling", - EXAMPLES_CORE6, - ), - ("Core 7: Rag Stateful Memory Cells", EXAMPLES_CORE7), - ( - "Core 8: Prompt Code Generation and Execution", - EXAMPLES_CORE8, - ), - ("Core 9: Multi-Agent Simulation", EXAMPLES_CORE9), - ]; - for button in buttons_text_load { - let res = with_cursor(ui.button(button.0)); - if res.clicked() { - internal_state.load_string(button.1); - } - } - }); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - egui::Image::new(egui::include_image!("../assets/images/tblogo-white.png")) - .fit_to_exact_size(vec2(512.0, 512.0)) - .rounding(5.0) - .ui(ui); - }); - }); - } - frame.end(ui); - }); -} - pub fn chidori_plugin(app: &mut App) { app.init_resource::() .init_resource::() diff --git a/toolchain/chidori-debugger/src/code.rs b/toolchain/chidori-debugger/src/code.rs index f3d8d31..ce374d5 100644 --- a/toolchain/chidori-debugger/src/code.rs +++ b/toolchain/chidori-debugger/src/code.rs @@ -24,7 +24,7 @@ fn editor_update( mut execution_state: Res, mut tree: ResMut, mut cells: ResMut, - mut state_vs_editor_cells: Local + mut viewing_watched_file_cells: Local ) { let window = q_window.single(); let mut hide_all = false; @@ -34,7 +34,7 @@ fn editor_update( right: 0.0, top: 0.0, bottom: 0.0, - }); + }).inner_margin(16.0); if let Some(code_tile) = tree_identities.code_tile { if let Some(tile) = tree.tree.tiles.get(code_tile) { match tile { @@ -70,43 +70,54 @@ fn editor_update( egui::CentralPanel::default().frame(container_frame).show(contexts.ctx_mut(), |ui| { egui::ScrollArea::vertical().show(ui, |ui| { let mut theme = egui_extras::syntax_highlighting::CodeTheme::dark(); - let state_vs_editor_cells_c = state_vs_editor_cells.clone(); - ui.toggle_value(&mut state_vs_editor_cells, if state_vs_editor_cells_c == false { "View Editor Cells" } else { "View Cells at Current State"}); - let cells: Vec<(Option, &CellTypes)> = if !*state_vs_editor_cells { - cells.editor_cells.iter().map(|c| (c.op_id, &c.cell)).collect() + let cells = if *viewing_watched_file_cells { + cells.editor_cells.iter() } else { - cells.state_cells.iter().map(|c| (Some(0), c)).collect() + cells.state_cells.iter() }; - for (op_id, cell) in cells { - match &cell { - CellTypes::Code(CodeCell { name, source_code, language, .. }, _) => { - let language_string = match language { - SupportedLanguage::PyO3 => "python", - SupportedLanguage::Starlark => "starlark", - SupportedLanguage::Deno => "javascript/typescript" - }; - let mut layouter = |ui: &egui::Ui, text_string: &str, wrap_width: f32| { - let syntax_language = match language { - SupportedLanguage::PyO3 => "py", - SupportedLanguage::Starlark => "py", - SupportedLanguage::Deno => "js" + + ui.horizontal(|ui| { + if ui.radio(*viewing_watched_file_cells, "View Editor Cells").clicked() { + *viewing_watched_file_cells = true; + } + if ui.radio(!*viewing_watched_file_cells, "View Cells at Current State").clicked() { + *viewing_watched_file_cells = false; + } + }); + + for cell_holder in cells { + let op_id = cell_holder.op_id; + + let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(Margin::symmetric(8.0, 16.0)).inner_margin(16.0).rounding(6.0).begin(ui); + { + let mut ui = &mut frame.content_ui; + + match &cell_holder.cell { + CellTypes::Code(CodeCell { name, source_code, language, ..}, _) => { + let language_string = match language { + SupportedLanguage::PyO3 => "python", + SupportedLanguage::Starlark => "starlark", + SupportedLanguage::Deno => "javascript/typescript" }; - let mut layout_job = - egui_extras::syntax_highlighting::highlight(ui.ctx(), &theme, text_string, syntax_language); - layout_job.wrap.max_width = wrap_width; + let mut layouter = |ui: &egui::Ui, text_string: &str, wrap_width: f32| { + let syntax_language = match language { + SupportedLanguage::PyO3 => "py", + SupportedLanguage::Starlark => "py", + SupportedLanguage::Deno => "js" + }; + let mut layout_job = + egui_extras::syntax_highlighting::highlight(ui.ctx(), &theme, text_string, syntax_language); + layout_job.wrap.max_width = wrap_width; - // Fix font size - for mut section in &mut layout_job.sections { - section.format.font_id = egui::FontId::new(14.0, FontFamily::Monospace); - } + // Fix font size + for mut section in &mut layout_job.sections { + section.format.font_id = egui::FontId::new(14.0, FontFamily::Monospace); + } - ui.fonts(|f| f.layout_job(layout_job)) - }; + ui.fonts(|f| f.layout_job(layout_job)) + }; - let mut s = source_code.clone(); - let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(16.0).inner_margin(16.0).rounding(6.0).begin(ui); - { - let mut ui = &mut frame.content_ui; + let mut s = source_code.clone(); ui.horizontal(|ui| { egui_label(ui, "Code"); egui_label(ui, language_string); @@ -151,13 +162,8 @@ fn editor_update( } }); } - frame.end(ui); - } - CellTypes::CodeGen(LLMCodeGenCell { name, req, .. }, _) => { - let mut s = req.clone(); - let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(16.0).inner_margin(16.0).rounding(6.0).begin(ui); - { - let mut ui = &mut frame.content_ui; + CellTypes::CodeGen(LLMCodeGenCell { name, req, .. }, _) => { + let mut s = req.clone(); ui.horizontal(|ui| { egui_label(ui, "Prompt"); if let Some(name) = name { @@ -200,14 +206,10 @@ fn editor_update( } }); } - frame.end(ui); - } - CellTypes::Prompt(LLMPromptCell::Completion { .. }, _) => {} - CellTypes::Prompt(LLMPromptCell::Chat { name, configuration, req, .. }, _) => { - let mut s = req.clone(); - let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(16.0).inner_margin(16.0).begin(ui); - { - let mut ui = &mut frame.content_ui; + CellTypes::Prompt(LLMPromptCell::Completion { .. }, _) => {} + CellTypes::Prompt(LLMPromptCell::Chat { name, configuration, req, .. }, _) => { + let mut s = req.clone(); + let mut cfg = serde_yaml::to_string(&configuration.clone()).unwrap(); ui.horizontal(|ui| { egui_label(ui, "Prompt"); if let Some(name) = name { @@ -222,6 +224,15 @@ fn editor_update( }); // Add widgets inside the frame ui.vertical(|ui| { + ui.add( + egui::TextEdit::multiline(&mut cfg) + .font(egui::FontId::new(14.0, FontFamily::Monospace)) + .code_editor() + .lock_focus(true) + .desired_width(f32::INFINITY) + .margin(Margin::symmetric(8.0, 8.0)) + ); + ui.add_space(10.0); ui.add( egui::TextEdit::multiline(&mut s) .code_editor() @@ -250,14 +261,9 @@ fn editor_update( } }); } - frame.end(ui); - } - CellTypes::Embedding(LLMEmbeddingCell { .. }, _) => {} - CellTypes::Web(WebserviceCell { name, configuration, .. }, _) => { - let mut s = configuration.clone(); - let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(16.0).inner_margin(16.0).begin(ui); - { - let mut ui = &mut frame.content_ui; + CellTypes::Embedding(LLMEmbeddingCell { .. }, _) => {} + CellTypes::Web(WebserviceCell { name, configuration, .. }, _) => { + let mut s = configuration.clone(); ui.horizontal(|ui| { egui_label(ui, "Prompt"); if let Some(name) = name { @@ -298,13 +304,8 @@ fn editor_update( } }); } - frame.end(ui); - } - CellTypes::Template(TemplateCell { name, body }, _) => { - let mut s = body.clone(); - let mut frame = egui::Frame::default().fill(Color32::from_hex("#222222").unwrap()).outer_margin(16.0).inner_margin(16.0).begin(ui); - { - let mut ui = &mut frame.content_ui; + CellTypes::Template(TemplateCell { name, body }, _) => { + let mut s = body.clone(); ui.horizontal(|ui| { egui_label(ui, "Prompt"); if let Some(name) = name { @@ -346,10 +347,11 @@ fn editor_update( } }); } - frame.end(ui); + CellTypes::Memory(MemoryCell { .. }, _) => {} } - CellTypes::Memory(MemoryCell { .. }, _) => {} + } + frame.end(ui); } }); }); diff --git a/toolchain/chidori-debugger/src/graph.rs b/toolchain/chidori-debugger/src/graph.rs index f3625b3..1b2ab50 100644 --- a/toolchain/chidori-debugger/src/graph.rs +++ b/toolchain/chidori-debugger/src/graph.rs @@ -87,7 +87,8 @@ struct GraphMainCamera; struct GraphMinimapCamera; enum CameraStateValue { - Locked, + LockedOnSelection, + LockedOnExecHead, Free } @@ -110,18 +111,96 @@ struct InteractionLock { // TODO: support graph traversal by id in the graph -fn keyboard_navigate_to_parent( + +#[derive(Resource, Default)] +struct SelectedNode(Option); + + +#[derive(Default)] +struct KeyboardNavigationState { + last_move: f32, + move_cooldown: f32, +} + +fn keyboard_navigate_graph( + time: Res