From 9d09c1764cf2752ad41aa5c8e527aee5002e4735 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Tue, 2 Jul 2024 02:06:37 -0700 Subject: [PATCH 1/6] Adding better UX for playground * Better render envvars buttons * Show docs links in playground * Use dropdowns to select test cases / functions --- engine/baml-lib/baml-core/src/ir/repr.rs | 11 +- engine/baml-lib/baml-core/src/ir/walker.rs | 4 + engine/baml-runtime/src/lib.rs | 3 + .../baml-schema-wasm/src/runtime_wasm/mod.rs | 61 +++- .../fiddle-examples/images/image.baml | 2 +- integ-tests/python/baml_client/inlinedbaml.py | 2 +- integ-tests/ruby/baml_client/inlined.rb | 2 +- .../typescript/baml_client/inlinedbaml.ts | 2 +- .../[project_id]/_components/ProjectView.tsx | 13 +- typescript/playground-common/package.json | 1 + .../src/components/ui/breadcrumb.tsx | 115 +++++++ .../src/components/ui/toggle-group.tsx | 49 +++ typescript/playground-common/src/index.ts | 2 +- .../playground-common/src/lib/searchbar.tsx | 6 +- .../src/shared/CheckboxHeader.tsx | 5 + .../playground-common/src/shared/Link.tsx | 7 +- .../src/shared/Selectors.tsx | 286 ++++++++++++++---- .../src/shared/SettingsDialog.tsx | 26 +- typescript/pnpm-lock.yaml | 35 ++- .../packages/language-server/src/server.ts | 8 + .../packages/vscode/src/extension.ts | 4 +- .../vscode/src/panels/StatusBarPanel.ts | 46 +++ .../vscode/src/panels/WebPanelView.ts | 3 +- .../src/plugins/language-server/index.ts | 11 + .../packages/web-panel/package.json | 3 +- .../vscode-ext/packages/web-panel/src/App.tsx | 24 +- 26 files changed, 608 insertions(+), 123 deletions(-) create mode 100644 typescript/playground-common/src/components/ui/breadcrumb.tsx create mode 100644 typescript/playground-common/src/components/ui/toggle-group.tsx create mode 100644 typescript/vscode-ext/packages/vscode/src/panels/StatusBarPanel.ts diff --git a/engine/baml-lib/baml-core/src/ir/repr.rs b/engine/baml-lib/baml-core/src/ir/repr.rs index ea80d810f..070fa4b39 100644 --- a/engine/baml-lib/baml-core/src/ir/repr.rs +++ b/engine/baml-lib/baml-core/src/ir/repr.rs @@ -10,8 +10,7 @@ use internal_baml_parser_database::{ ClassWalker, ClientWalker, ConfigurationWalker, EnumValueWalker, EnumWalker, FieldWalker, FunctionWalker, TemplateStringWalker, VariantWalker, }, - ParserDatabase, PromptAst, RetryPolicyStrategy, ToStringAttributes, - WithStaticRenames, + ParserDatabase, PromptAst, RetryPolicyStrategy, ToStringAttributes, WithStaticRenames, }; use internal_baml_schema_ast::ast::{self, FieldArity, WithName, WithSpan}; @@ -1102,6 +1101,14 @@ pub struct TestCase { } impl WithRepr for ConfigurationWalker<'_> { + fn attributes(&self, _db: &ParserDatabase) -> NodeAttributes { + NodeAttributes { + meta: IndexMap::new(), + overrides: IndexMap::new(), + span: Some(self.span().clone()), + } + } + fn repr(&self, db: &ParserDatabase) -> Result { Ok(TestCase { name: self.name().to_string(), diff --git a/engine/baml-lib/baml-core/src/ir/walker.rs b/engine/baml-lib/baml-core/src/ir/walker.rs index c769c878f..3b0bd80a9 100644 --- a/engine/baml-lib/baml-core/src/ir/walker.rs +++ b/engine/baml-lib/baml-core/src/ir/walker.rs @@ -265,6 +265,10 @@ impl<'a> Walker<'a, (&'a Function, &'a TestCase)> { &self.item.1.elem } + pub fn span(&self) -> Option<&crate::Span> { + self.item.1.attributes.span.as_ref() + } + pub fn test_case_params( &self, env_values: &HashMap, diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index a6cbc5660..92ae4ad47 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -50,6 +50,9 @@ pub use internal_baml_jinja::{ChatMessagePart, RenderedPrompt}; #[cfg(feature = "internal")] pub use runtime_interface::InternalRuntimeInterface; +#[cfg(feature = "internal")] +pub use internal_baml_core as internal_core; + #[cfg(not(feature = "internal"))] pub(crate) use internal_baml_jinja::{ChatMessagePart, RenderedPrompt}; #[cfg(not(feature = "internal"))] diff --git a/engine/baml-schema-wasm/src/runtime_wasm/mod.rs b/engine/baml-schema-wasm/src/runtime_wasm/mod.rs index 95c8b8f25..a84141794 100644 --- a/engine/baml-schema-wasm/src/runtime_wasm/mod.rs +++ b/engine/baml-schema-wasm/src/runtime_wasm/mod.rs @@ -257,6 +257,8 @@ pub struct WasmFunction { pub test_cases: Vec, #[wasm_bindgen(readonly)] pub test_snippet: String, + #[wasm_bindgen(readonly)] + pub signature: String, } #[wasm_bindgen(getter_with_clone, inspectable)] @@ -268,6 +270,31 @@ pub struct WasmSpan { pub start: usize, #[wasm_bindgen(readonly)] pub end: usize, + #[wasm_bindgen(readonly)] + pub start_line: usize, +} + +impl From<&baml_runtime::internal_core::internal_baml_diagnostics::Span> for WasmSpan { + fn from(span: &baml_runtime::internal_core::internal_baml_diagnostics::Span) -> Self { + let (start, end) = span.line_and_column(); + WasmSpan { + file_path: span.file.path().to_string(), + start: span.start, + end: span.end, + start_line: start.0, + } + } +} + +impl Default for WasmSpan { + fn default() -> Self { + WasmSpan { + file_path: "".to_string(), + start: 0, + end: 0, + start_line: 0, + } + } } #[wasm_bindgen(getter_with_clone, inspectable)] @@ -279,6 +306,8 @@ pub struct WasmTestCase { pub inputs: Vec, #[wasm_bindgen(readonly)] pub error: Option, + #[wasm_bindgen(readonly)] + pub span: WasmSpan, } #[wasm_bindgen(getter_with_clone, inspectable)] @@ -743,21 +772,27 @@ impl WasmRuntime { ); let wasm_span = match f.span() { - Some(span) => WasmSpan { - file_path: span.file.path().to_string(), - start: span.start, - end: span.end, - }, - None => WasmSpan { - file_path: "".to_string(), - start: 0, - end: 0, - }, + Some(span) => span.into(), + None => WasmSpan::default(), }; WasmFunction { name: f.name().to_string(), span: wasm_span, + signature: { + let inputs = f + .inputs() + .right() + .map(|func_params| { + func_params + .iter() + .map(|(k, t)| format!("{}: {}", k, t)) + .collect::>() + .join(", ") + }) + .unwrap_or_default(); + format!("({}) -> {}", inputs, f.output().to_string()) + }, test_snippet: snippet, test_cases: f .walk_tests() @@ -812,10 +847,16 @@ impl WasmRuntime { } }); + let wasm_span = match tc.span() { + Some(span) => span.into(), + None => WasmSpan::default(), + }; + WasmTestCase { name: tc.test_case().name.clone(), inputs: params, error, + span: wasm_span, } }) .collect(), diff --git a/integ-tests/baml_src/fiddle-examples/images/image.baml b/integ-tests/baml_src/fiddle-examples/images/image.baml index 1efc63d2d..0c42e0877 100644 --- a/integ-tests/baml_src/fiddle-examples/images/image.baml +++ b/integ-tests/baml_src/fiddle-examples/images/image.baml @@ -65,6 +65,6 @@ function DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string { test TestName { functions [DescribeImage] args { - img { url "https://imgs.xkcd.com/comics/standards.png"} + img { url "http://imgs.xkcd.com/comics/standards.png" } } } diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index c5fbb0df8..c4951beae 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -22,7 +22,7 @@ "fiddle-examples/classify-message.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "fiddle-examples/extract-names.baml": "function ExtractNames(input: string) -> string[] {\n client GPT4\n prompt #\"\n Extract the names from this INPUT:\n \n INPUT:\n ---\n {{ input }}\n ---\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}\n", "fiddle-examples/extract-receipt-info.baml": "class ReceiptItem {\n name string\n description string?\n quantity int\n price float\n}\n\nclass ReceiptInfo {\n items ReceiptItem[]\n total_cost float?\n}\n\nfunction ExtractReceiptInfo(email: string) -> ReceiptInfo {\n client GPT4o\n prompt #\"\n Given the receipt below:\n\n ```\n {{email}}\n ```\n\n {{ ctx.output_format }}\n \"#\n}\n\n", - "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"https://imgs.xkcd.com/comics/standards.png\"}\n }\n}\n", + "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"http://imgs.xkcd.com/comics/standards.png\" }\n }\n}\n", "fiddle-examples/symbol-tuning.baml": "enum Category3 {\n Refund @alias(\"k1\")\n @description(\"Customer wants to refund a product\")\n\n CancelOrder @alias(\"k2\")\n @description(\"Customer wants to cancel an order\")\n\n TechnicalSupport @alias(\"k3\")\n @description(\"Customer needs help with a technical issue unrelated to account creation or login\")\n\n AccountIssue @alias(\"k4\")\n @description(\"Specifically relates to account-login or account-creation\")\n\n Question @alias(\"k5\")\n @description(\"Customer has a question\")\n}\n\nfunction ClassifyMessage3(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\ngenerator lang_ruby {\n output_type ruby/sorbet\n output_dir \"../ruby\"\n}\n", "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb index 3afa00b71..946e96f44 100644 --- a/integ-tests/ruby/baml_client/inlined.rb +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -22,7 +22,7 @@ module Inlined "fiddle-examples/classify-message.baml" => "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "fiddle-examples/extract-names.baml" => "function ExtractNames(input: string) -> string[] {\n client GPT4\n prompt #\"\n Extract the names from this INPUT:\n \n INPUT:\n ---\n {{ input }}\n ---\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}\n", "fiddle-examples/extract-receipt-info.baml" => "class ReceiptItem {\n name string\n description string?\n quantity int\n price float\n}\n\nclass ReceiptInfo {\n items ReceiptItem[]\n total_cost float?\n}\n\nfunction ExtractReceiptInfo(email: string) -> ReceiptInfo {\n client GPT4o\n prompt #\"\n Given the receipt below:\n\n ```\n {{email}}\n ```\n\n {{ ctx.output_format }}\n \"#\n}\n\n", - "fiddle-examples/images/image.baml" => "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"https://imgs.xkcd.com/comics/standards.png\"}\n }\n}\n", + "fiddle-examples/images/image.baml" => "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"http://imgs.xkcd.com/comics/standards.png\" }\n }\n}\n", "fiddle-examples/symbol-tuning.baml" => "enum Category3 {\n Refund @alias(\"k1\")\n @description(\"Customer wants to refund a product\")\n\n CancelOrder @alias(\"k2\")\n @description(\"Customer wants to cancel an order\")\n\n TechnicalSupport @alias(\"k3\")\n @description(\"Customer needs help with a technical issue unrelated to account creation or login\")\n\n AccountIssue @alias(\"k4\")\n @description(\"Specifically relates to account-login or account-creation\")\n\n Question @alias(\"k5\")\n @description(\"Customer has a question\")\n}\n\nfunction ClassifyMessage3(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "main.baml" => "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\ngenerator lang_ruby {\n output_type ruby/sorbet\n output_dir \"../ruby\"\n}\n", "test-files/aliases/classes.baml" => "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index 4d3eabe3d..0327a16fc 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -23,7 +23,7 @@ const fileMap = { "fiddle-examples/classify-message.baml": "// This will be available as an enum in your Python and Typescript code.\nenum Category {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "fiddle-examples/extract-names.baml": "function ExtractNames(input: string) -> string[] {\n client GPT4\n prompt #\"\n Extract the names from this INPUT:\n \n INPUT:\n ---\n {{ input }}\n ---\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}\n", "fiddle-examples/extract-receipt-info.baml": "class ReceiptItem {\n name string\n description string?\n quantity int\n price float\n}\n\nclass ReceiptInfo {\n items ReceiptItem[]\n total_cost float?\n}\n\nfunction ExtractReceiptInfo(email: string) -> ReceiptInfo {\n client GPT4o\n prompt #\"\n Given the receipt below:\n\n ```\n {{email}}\n ```\n\n {{ ctx.output_format }}\n \"#\n}\n\n", - "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"https://imgs.xkcd.com/comics/standards.png\"}\n }\n}\n", + "fiddle-examples/images/image.baml": "function DescribeImage(img: image) -> string {\n client AwsBedrock\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 20 words:\n {{ img }}\n \"#\n\n}\n\nclass FakeImage {\n url string\n}\n\nclass ClassWithImage {\n myImage image\n param2 string\n fake_image FakeImage\n}\n\n// chat role user present\nfunction DescribeImage2(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n You should return 2 answers that answer the following commands.\n\n 1. Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n 2. Also tell me what's happening here in one sentence:\n {{ img2 }}\n \"#\n}\n\n// no chat role\nfunction DescribeImage3(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\n\n// system prompt and chat prompt\nfunction DescribeImage4(classWithImage: ClassWithImage, img2: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"system\")}}\n\n Describe this in 5 words:\n {{ classWithImage.myImage }}\n\n Tell me also what's happening here in one sentence and relate it to the word {{ classWithImage.param2 }}:\n {{ img2 }}\n \"#\n}\n\ntest TestName {\n functions [DescribeImage]\n args {\n img { url \"http://imgs.xkcd.com/comics/standards.png\" }\n }\n}\n", "fiddle-examples/symbol-tuning.baml": "enum Category3 {\n Refund @alias(\"k1\")\n @description(\"Customer wants to refund a product\")\n\n CancelOrder @alias(\"k2\")\n @description(\"Customer wants to cancel an order\")\n\n TechnicalSupport @alias(\"k3\")\n @description(\"Customer needs help with a technical issue unrelated to account creation or login\")\n\n AccountIssue @alias(\"k4\")\n @description(\"Specifically relates to account-login or account-creation\")\n\n Question @alias(\"k5\")\n @description(\"Customer has a question\")\n}\n\nfunction ClassifyMessage3(input: string) -> Category {\n client GPT4\n\n prompt #\"\n Classify the following INPUT into ONE\n of the following categories:\n\n INPUT: {{ input }}\n\n {{ ctx.output_format }}\n\n Response:\n \"#\n}", "main.baml": "generator lang_python {\n output_type python/pydantic\n output_dir \"../python\"\n}\n\ngenerator lang_typescript {\n output_type typescript\n output_dir \"../typescript\"\n}\n\ngenerator lang_ruby {\n output_type ruby/sorbet\n output_dir \"../ruby\"\n}\n", "test-files/aliases/classes.baml": "class TestClassAlias {\n key string @alias(\"key-dash\") @description(#\"\n This is a description for key\n af asdf\n \"#)\n key2 string @alias(\"key21\")\n key3 string @alias(\"key with space\")\n key4 string //unaliased\n key5 string @alias(\"key.with.punctuation/123\")\n}\n\nfunction FnTestClassAlias(input: string) -> TestClassAlias {\n client GPT35\n prompt #\"\n {{ctx.output_format}}\n \"#\n}\n\ntest FnTestClassAlias {\n functions [FnTestClassAlias]\n args {\n input \"example input\"\n }\n}\n", diff --git a/typescript/fiddle-frontend/app/[project_id]/_components/ProjectView.tsx b/typescript/fiddle-frontend/app/[project_id]/_components/ProjectView.tsx index 6b3984c21..bd2ba448a 100644 --- a/typescript/fiddle-frontend/app/[project_id]/_components/ProjectView.tsx +++ b/typescript/fiddle-frontend/app/[project_id]/_components/ProjectView.tsx @@ -12,7 +12,6 @@ import { CustomErrorBoundary, EventListener, FunctionPanel, - FunctionSelector, //useSelections, } from '@baml/playground-common' import { updateFileAtom } from '@baml/playground-common/baml_wasm_web/EventListener' @@ -42,10 +41,11 @@ import { CodeMirrorEditor } from './CodeMirrorEditor' import { GithubStars } from './GithubStars' import { InitialTour, PostTestRunTour } from './Tour' -import SettingsDialog, { ShowSettingsButton, showSettingsAtom } from '@baml/playground-common/shared/SettingsDialog' +import SettingsDialog, { ShowSettingsButton } from '@baml/playground-common/shared/SettingsDialog' import FileViewer from './Tree/FileViewer' import { AppStateProvider } from '@baml/playground-common/shared/AppStateContext' // Import the AppStateProvider +import { ViewSelector } from '@baml/playground-common/shared/Selectors' const ProjectViewImpl = ({ project }: { project: BAMLProject }) => { const setEditorFiles = useSetAtom(updateFileAtom) @@ -323,13 +323,10 @@ const PlaygroundView = () => {
- -
- +
+
+
{/* */} diff --git a/typescript/playground-common/package.json b/typescript/playground-common/package.json index 50488216c..8c628ce54 100644 --- a/typescript/playground-common/package.json +++ b/typescript/playground-common/package.json @@ -83,6 +83,7 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toggle": "^1.0.3", + "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.0.7", "@rjsf/core": "^5.15.0", "@rjsf/utils": "^5.15.0", diff --git a/typescript/playground-common/src/components/ui/breadcrumb.tsx b/typescript/playground-common/src/components/ui/breadcrumb.tsx new file mode 100644 index 000000000..26560efcc --- /dev/null +++ b/typescript/playground-common/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons" +import { Slot } from "@radix-ui/react-slot" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>
)) )} diff --git a/typescript/playground-common/src/shared/CheckboxHeader.tsx b/typescript/playground-common/src/shared/CheckboxHeader.tsx index 4a16e000b..d6a7ba619 100644 --- a/typescript/playground-common/src/shared/CheckboxHeader.tsx +++ b/typescript/playground-common/src/shared/CheckboxHeader.tsx @@ -1,5 +1,9 @@ import { useAppState } from './AppStateContext' import { Checkbox } from '../components/ui/checkbox' +import { useAtomValue } from 'jotai' +import { selectedTestCaseAtom } from '../baml_wasm_web/EventListener' +import Link from './Link' +import { ShowSettingsButton } from './SettingsDialog' const PromptCheckbox = ({ children, @@ -32,6 +36,7 @@ export const CheckboxHeader = () => { Raw cURL +
) } diff --git a/typescript/playground-common/src/shared/Link.tsx b/typescript/playground-common/src/shared/Link.tsx index 0ca46ee72..14d641587 100644 --- a/typescript/playground-common/src/shared/Link.tsx +++ b/typescript/playground-common/src/shared/Link.tsx @@ -2,15 +2,18 @@ import type { StringSpan } from '@baml/common' import { VSCodeLink } from '@vscode/webview-ui-toolkit/react' import { cn } from '../lib/utils' import { vscode } from '../utils/vscode' +import { File } from 'lucide-react' const Link: React.FC<{ item: StringSpan; display?: string; className?: string }> = ({ item, display, className }) => ( { vscode.postMessage({ command: 'jumpToFile', data: item }) }} > - {display ?? item.value} +
+ {display ?? item.value} +
) diff --git a/typescript/playground-common/src/shared/Selectors.tsx b/typescript/playground-common/src/shared/Selectors.tsx index c63a331f6..9529d9e3e 100644 --- a/typescript/playground-common/src/shared/Selectors.tsx +++ b/typescript/playground-common/src/shared/Selectors.tsx @@ -1,16 +1,39 @@ import type { SFunction } from '@baml/common' import { VSCodeDropdown, VSCodeLink, VSCodeOption } from '@vscode/webview-ui-toolkit/react' import { useAtom, useAtomValue } from 'jotai' -import { Check, ChevronsUpDown } from 'lucide-react' +import { + BookTextIcon, + Check, + ChevronDown, + ChevronRight, + ChevronsDown, + ChevronsUpDown, + Compass, + Gamepad2, + Gamepad2Icon, + ListChecksIcon, + SettingsIcon, + SlashIcon, +} from 'lucide-react' import type React from 'react' import { useContext, useState } from 'react' -import { availableFunctionsAtom, selectedFunctionAtom } from '../baml_wasm_web/EventListener' +import { availableFunctionsAtom, selectedFunctionAtom, selectedTestCaseAtom } from '../baml_wasm_web/EventListener' import { Button } from '../components/ui/button' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '../components/ui/popover' import SearchBarWithSelector from '../lib/searchbar' - +import { ToggleGroup, ToggleGroupItem } from '../components/ui/toggle-group' +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from '../components/ui/breadcrumb' import Link from './Link' +import { Dialog, DialogContent, DialogTrigger } from '../components/ui/dialog' +import { Snippets } from './Snippets' const FunctionDropdown: React.FC = () => { const [open, setOpen] = useState(false) @@ -19,69 +42,216 @@ const FunctionDropdown: React.FC = () => { const functionName = selected?.name + if (functions.length === 0) { + return <>Create a function + } + return ( - - - - - - ({ - value: func.name, - label: func.test_cases.length > 0 ? `${func.name} (${func.test_cases.length} tests)` : undefined, - }))} - onChange={(value) => { - setSelected(value) - setOpen(false) - }} - /> - - +
+ + Function + {selected && } + + + +
+ {functionName ?? 'Select a function...'} + +
+
+ + ({ + value: func.name, + label: func.test_cases.length > 0 ? `${func.name} (${func.test_cases.length} tests)` : undefined, + content: ( +
+ {func.signature} +
+ ), + }))} + onChange={(value) => { + setSelected(value) + setOpen(false) + }} + /> +
+
+
) } -export const FunctionSelector: React.FC = () => { +const TestDropdown: React.FC = () => { + const [open, setOpen] = useState(false) + const tests = useAtomValue(selectedFunctionAtom)?.test_cases + const [selected, setSelected] = useAtom(selectedTestCaseAtom) + + if (tests === undefined) { + return null + } + + if (tests.length === 0) { + return <>Create a test + } + + if (!selected) { + return <>Select a test... + } + return ( -
-
- {/* */} +
+ + Test + {selected && } + + + +
+ {selected.name} + +
+
+ + ({ + value: test.name, + content: ( +
+ {test.inputs.map((i) => ( +
+ {i.name} + {i.value} +
+ ))} +
+ ), + }))} + onChange={(value) => { + setSelected(value) + setOpen(false) + }} + /> +
+
+
+ ) +} + +const JumpToFunction: React.FC = () => { + const selected = useAtomValue(selectedFunctionAtom) + + if (!selected) { + return null + } + + return ( + .baml'}:${selected.span.start_line + 1}`, + }} + className='text-xs text-muted-foreground decoration-0' + /> + ) +} + +const JumpToTestCase: React.FC = () => { + const selected = useAtomValue(selectedTestCaseAtom) + + if (!selected) { + return null + } + + return ( + .baml'}:${selected.span.start_line + 1}`, + }} + className='text-xs text-muted-foreground decoration-0' + /> + ) +} + +export const ViewSelector: React.FC = () => { + return ( +
+
- {/* Function - '} - onChange={(event) => - setSelection( - undefined, - (event as React.FormEvent).currentTarget.value, - undefined, - undefined, - undefined, - ) - } - > - {function_names.map((func) => ( - - {func} - - ))} - */} + + +
+
+ + + + + + + +
- {/* {func && ( -
- - {'('} - {') → '}{' '} - {func.output.arg_type === 'positional' && } -
- )} */}
+ + // + // + // + // + // + // + // + // + // + // ) } + +// export const FunctionSelector: React.FC = () => { +// return ( +//
+//
+// {/* */} + +// +// {/* Function +// '} +// onChange={(event) => +// setSelection( +// undefined, +// (event as React.FormEvent).currentTarget.value, +// undefined, +// undefined, +// undefined, +// ) +// } +// > +// {function_names.map((func) => ( +// +// {func} +// +// ))} +// */} +//
+// {/* {func && ( +//
+// +// {'('} +// {') → '}{' '} +// {func.output.arg_type === 'positional' && } +//
+// )} */} +//
+// ) +// } diff --git a/typescript/playground-common/src/shared/SettingsDialog.tsx b/typescript/playground-common/src/shared/SettingsDialog.tsx index d6ca003be..4b3284ca4 100644 --- a/typescript/playground-common/src/shared/SettingsDialog.tsx +++ b/typescript/playground-common/src/shared/SettingsDialog.tsx @@ -170,10 +170,7 @@ const EnvvarInput: React.FC<{ envvar: EnvVar }> = ({ envvar }) => { ) } -export const ShowSettingsButton: React.FC<{ buttonClassName: string; iconClassName: string }> = ({ - buttonClassName, - iconClassName, -}) => { +export const ShowSettingsButton: React.FC<{ iconClassName: string }> = ({ iconClassName }) => { const setShowSettings = useSetAtom(showSettingsAtom) const requiredButUnset = useAtomValue(requiredButUnsetAtom) const requiredButUnsetCount = requiredButUnset.length @@ -183,10 +180,13 @@ export const ShowSettingsButton: React.FC<{ buttonClassName: string; iconClassNa } const button = ( - - - - - - - -
-
- +
+ +
From 8222f4a41f13644771537ab13666a4ca5ca2e7ba Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Tue, 2 Jul 2024 02:09:22 -0700 Subject: [PATCH 2/6] formatting --- .../src/components/ui/breadcrumb.tsx | 129 +++++++----------- 1 file changed, 52 insertions(+), 77 deletions(-) diff --git a/typescript/playground-common/src/components/ui/breadcrumb.tsx b/typescript/playground-common/src/components/ui/breadcrumb.tsx index 26560efcc..7744607ba 100644 --- a/typescript/playground-common/src/components/ui/breadcrumb.tsx +++ b/typescript/playground-common/src/components/ui/breadcrumb.tsx @@ -1,108 +1,83 @@ -import * as React from "react" -import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons" -import { Slot } from "@radix-ui/react-slot" +import * as React from 'react' +import { ChevronRightIcon, DotsHorizontalIcon } from '@radix-ui/react-icons' +import { Slot } from '@radix-ui/react-slot' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Breadcrumb = React.forwardRef< HTMLElement, - React.ComponentPropsWithoutRef<"nav"> & { + React.ComponentPropsWithoutRef<'nav'> & { separator?: React.ReactNode } ->(({ ...props }, ref) =>
diff --git a/typescript/playground-common/src/shared/ImplPanel.tsx b/typescript/playground-common/src/shared/ImplPanel.tsx index 2bbc8c08b..eccac2a51 100644 --- a/typescript/playground-common/src/shared/ImplPanel.tsx +++ b/typescript/playground-common/src/shared/ImplPanel.tsx @@ -98,7 +98,6 @@ const CodeLine: React.FC<{ const isTokenized = Array.isArray(line[0]) if (Array.isArray(line)) { - console.log('line', line) return (
{lineNumberSpan} diff --git a/typescript/playground-common/src/shared/Link.tsx b/typescript/playground-common/src/shared/Link.tsx index 14d641587..9581d7a8c 100644 --- a/typescript/playground-common/src/shared/Link.tsx +++ b/typescript/playground-common/src/shared/Link.tsx @@ -6,13 +6,13 @@ import { File } from 'lucide-react' const Link: React.FC<{ item: StringSpan; display?: string; className?: string }> = ({ item, display, className }) => ( { vscode.postMessage({ command: 'jumpToFile', data: item }) }} > -
- {display ?? item.value} +
+ {display ?? item.value}
) diff --git a/typescript/playground-common/src/shared/Selectors.tsx b/typescript/playground-common/src/shared/Selectors.tsx index 9529d9e3e..447ae8101 100644 --- a/typescript/playground-common/src/shared/Selectors.tsx +++ b/typescript/playground-common/src/shared/Selectors.tsx @@ -1,36 +1,11 @@ -import type { SFunction } from '@baml/common' -import { VSCodeDropdown, VSCodeLink, VSCodeOption } from '@vscode/webview-ui-toolkit/react' import { useAtom, useAtomValue } from 'jotai' -import { - BookTextIcon, - Check, - ChevronDown, - ChevronRight, - ChevronsDown, - ChevronsUpDown, - Compass, - Gamepad2, - Gamepad2Icon, - ListChecksIcon, - SettingsIcon, - SlashIcon, -} from 'lucide-react' +import { ChevronDown, ChevronRight, Compass } from 'lucide-react' import type React from 'react' -import { useContext, useState } from 'react' +import { useState } from 'react' import { availableFunctionsAtom, selectedFunctionAtom, selectedTestCaseAtom } from '../baml_wasm_web/EventListener' import { Button } from '../components/ui/button' -import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '../components/ui/popover' import SearchBarWithSelector from '../lib/searchbar' -import { ToggleGroup, ToggleGroupItem } from '../components/ui/toggle-group' -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - BreadcrumbList, - BreadcrumbPage, - BreadcrumbSeparator, -} from '../components/ui/breadcrumb' import Link from './Link' import { Dialog, DialogContent, DialogTrigger } from '../components/ui/dialog' import { Snippets } from './Snippets' @@ -47,14 +22,14 @@ const FunctionDropdown: React.FC = () => { } return ( -
- +
+ Function {selected && } -
+
{functionName ?? 'Select a function...'}
@@ -99,15 +74,15 @@ const TestDropdown: React.FC = () => { } return ( -
- +
+ Test {selected && } -
+
{selected.name}
@@ -153,7 +128,7 @@ const JumpToFunction: React.FC = () => { source_file: selected.span.file_path, value: `${selected.span.file_path.split('/').pop() ?? '.baml'}:${selected.span.start_line + 1}`, }} - className='text-xs text-muted-foreground decoration-0' + className='text-xs text-muted-foreground decoration-0 py-0' /> ) } @@ -183,7 +158,9 @@ export const ViewSelector: React.FC = () => {
- +
+ +
From 0d2d4eaef257e3e6d615f11021ac1231a2860eaf Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Tue, 2 Jul 2024 22:21:32 -0700 Subject: [PATCH 4/6] Include fixes for font size on file view --- .../_components/Tree/FileViewer.tsx | 8 ++++---- .../app/[project_id]/_components/Tree/Node.tsx | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/typescript/fiddle-frontend/app/[project_id]/_components/Tree/FileViewer.tsx b/typescript/fiddle-frontend/app/[project_id]/_components/Tree/FileViewer.tsx index 78529e39d..96cb6f698 100644 --- a/typescript/fiddle-frontend/app/[project_id]/_components/Tree/FileViewer.tsx +++ b/typescript/fiddle-frontend/app/[project_id]/_components/Tree/FileViewer.tsx @@ -137,8 +137,8 @@ const FileViewer = () => { ) return ( -
-
{createFileFolder}
+
+
{createFileFolder}
{/* { value={term} onChange={(e) => setTerm(e.target.value)} /> */} -
+
) => { style={style} ref={dragHandle} > -
node.isInternal && node.toggle()}> +
node.isInternal && node.toggle()}> {node.isLeaf ? ( <> - + {renderIcon(node.id)} ) : ( <> - {node.isOpen ? : } + + {node.isOpen ? : } + {/* */} @@ -137,7 +139,12 @@ const Node = ({ node, style, dragHandle, tree }: NodeRendererProps) => { autoFocus /> ) : ( - + {node.data.name} )} @@ -146,7 +153,7 @@ const Node = ({ node, style, dragHandle, tree }: NodeRendererProps) => { {node.id !== 'baml_src' && (
-
+