From 6472bec231b581076ee7edefaab2e7979b2bf336 Mon Sep 17 00:00:00 2001 From: Samuel Lijin Date: Fri, 7 Jun 2024 11:47:04 -0700 Subject: [PATCH] feat: make ruby work again (#650) - ruby now supports streaming and tracing - ruby does _not_ yet support: custom trace decorators, images, or dynamic types - ruby compilation is not baked into the default `cargo build` - too much stuff requires ruby-specific compiler flags - `baml` gem 0.1.10 has been released and contains the latest stuff - `release_language_client_ruby` now works - ruby codegen has been updated, and needs to be shipped in the vscode extension - fix: codegen for the default `baml_client` used to automatically happen in `baml_src`, it now happens in `baml_src/..` - chore: delete integ-tests2 --- .envrc | 2 + .../release_language_client_ruby.yaml | 35 +- .gitignore | 1 + .mise.toml | 2 +- engine/Cargo.lock | 4 +- engine/Cargo.toml | 6 + .../src/jsonish/parser/markdown_parser.rs | 6 +- engine/baml-runtime/src/cli/generate.rs | 14 +- engine/language-client-codegen/askama.toml | 1 - engine/language-client-codegen/src/lib.rs | 4 +- .../src/python/templates/inlinedbaml.py.j2 | 4 +- .../src/ruby/field_type.rs | 2 +- .../src/ruby/generate_types.rs | 160 +- .../language-client-codegen/src/ruby/mod.rs | 48 +- .../src/ruby/ruby_language_features.rs | 2 +- .../src/ruby/templates/class.rb.j2 | 24 - .../ruby/templates/class_forward_decl.rb.j2 | 6 - .../src/ruby/templates/client.rb.j2 | 59 +- .../src/ruby/templates/enum.rb.j2 | 12 - .../src/ruby/templates/inlined.rb.j2 | 9 + .../src/ruby/templates/partial-types.rb.j2 | 26 + .../src/ruby/templates/types.rb.j2 | 45 +- .../python_src/baml_py/__init__.py | 2 +- .../{async_context_vars.py => ctx_manager.py} | 0 .../src/types/function_result_stream.rs | 3 - engine/language_client_ruby/Gemfile | 1 + engine/language_client_ruby/Gemfile.lock | 17 +- engine/language_client_ruby/baml.gemspec | 7 +- engine/language_client_ruby/exe/baml-cli | 4 + .../ext/ruby_ffi/Cargo.toml | 7 +- .../ext/ruby_ffi/src/function_result.rs | 70 + .../ruby_ffi/src/function_result_stream.rs | 66 + .../ext/ruby_ffi/src/lib.rs | 206 +- .../ext/ruby_ffi/src/ruby_to_json.rs | 137 +- .../ext/ruby_ffi/src/ruby_types.rs | 61 - .../ext/ruby_ffi/src/runtime_ctx_manager.rs | 21 + .../ext/ruby_ffi/src/tokio_demo.rs | 103 - engine/language_client_ruby/lib/baml.rb | 4 + engine/language_client_ruby/lib/stream.rb | 70 + engine/language_client_ruby/test/async.rb | 29 - .../language_client_ruby/test/async_test.rb | 37 + engine/language_client_ruby/test/bench.rb | 35 - .../language_client_ruby/test/runtime_test.rb | 16 + engine/language_client_ruby/test/sorbet.rb | 49 - .../language_client_ruby/test/sorbet_test.rb | 99 + integ-tests/baml_src/main.baml | 5 + .../test-files/functions/output/unions.baml | 2 +- .../test-files/testing_pipeline/resume.baml | 6 +- integ-tests/python/README.md | 4 +- integ-tests/python/app/test_functions.py | 10 +- integ-tests/python/baml_client/client.py | 4 +- integ-tests/python/baml_client/inlinedbaml.py | 6 +- .../python/baml_client/partial_types.py | 2 +- integ-tests/python/baml_client/types.py | 2 +- integ-tests/ruby/Gemfile | 11 + integ-tests/ruby/Gemfile.lock | 36 + integ-tests/ruby/README.md | 24 + integ-tests/ruby/Rakefile | 14 + integ-tests/ruby/baml_client/client.rb | 1938 ++++++++++++----- integ-tests/ruby/baml_client/inlined.rb | 65 + integ-tests/ruby/baml_client/partial-types.rb | 199 ++ integ-tests/ruby/baml_client/types.rb | 420 +--- integ-tests/ruby/test.rb | 58 - integ-tests/ruby/test_functions.rb | 149 ++ integ-tests/typescript/baml_client/client.ts | 8 +- .../typescript/baml_client/inlinedbaml.ts | 6 +- integ-tests/typescript/baml_client/types.ts | 2 +- integ-tests2/python/main.py | 33 - root.code-workspace | 3 +- tools/build | 31 +- 70 files changed, 2925 insertions(+), 1629 deletions(-) delete mode 100644 engine/language-client-codegen/src/ruby/templates/class.rb.j2 delete mode 100644 engine/language-client-codegen/src/ruby/templates/class_forward_decl.rb.j2 delete mode 100644 engine/language-client-codegen/src/ruby/templates/enum.rb.j2 create mode 100644 engine/language-client-codegen/src/ruby/templates/inlined.rb.j2 create mode 100644 engine/language-client-codegen/src/ruby/templates/partial-types.rb.j2 rename engine/language_client_python/python_src/baml_py/{async_context_vars.py => ctx_manager.py} (100%) create mode 100755 engine/language_client_ruby/exe/baml-cli create mode 100644 engine/language_client_ruby/ext/ruby_ffi/src/function_result.rs create mode 100644 engine/language_client_ruby/ext/ruby_ffi/src/function_result_stream.rs delete mode 100644 engine/language_client_ruby/ext/ruby_ffi/src/ruby_types.rs create mode 100644 engine/language_client_ruby/ext/ruby_ffi/src/runtime_ctx_manager.rs delete mode 100644 engine/language_client_ruby/ext/ruby_ffi/src/tokio_demo.rs create mode 100644 engine/language_client_ruby/lib/stream.rb delete mode 100644 engine/language_client_ruby/test/async.rb create mode 100644 engine/language_client_ruby/test/async_test.rb delete mode 100644 engine/language_client_ruby/test/bench.rb create mode 100644 engine/language_client_ruby/test/runtime_test.rb delete mode 100644 engine/language_client_ruby/test/sorbet.rb create mode 100644 engine/language_client_ruby/test/sorbet_test.rb create mode 100644 integ-tests/ruby/Gemfile create mode 100644 integ-tests/ruby/Gemfile.lock create mode 100644 integ-tests/ruby/README.md create mode 100644 integ-tests/ruby/Rakefile create mode 100644 integ-tests/ruby/baml_client/inlined.rb create mode 100644 integ-tests/ruby/baml_client/partial-types.rb delete mode 100644 integ-tests/ruby/test.rb create mode 100644 integ-tests/ruby/test_functions.rb delete mode 100644 integ-tests2/python/main.py diff --git a/.envrc b/.envrc index 77cf37ba3..b49cd049f 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,3 @@ PATH_add tools/ + +eval "$(mise activate bash)" diff --git a/.github/workflows/release_language_client_ruby.yaml b/.github/workflows/release_language_client_ruby.yaml index 8ec6b78a7..986928442 100644 --- a/.github/workflows/release_language_client_ruby.yaml +++ b/.github/workflows/release_language_client_ruby.yaml @@ -2,14 +2,15 @@ name: Release engine/language_client_ruby on: workflow_dispatch: {} - #push: - # branches: - # - sam/rust-llm permissions: contents: read id-token: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: strategy: @@ -23,6 +24,9 @@ jobs: #- x64-mingw-ucrt runs-on: ubuntu-latest + defaults: + run: + working-directory: engine/language_client_ruby steps: - uses: rubygems/configure-rubygems-credentials@main with: @@ -31,8 +35,6 @@ jobs: - uses: actions/checkout@v4 - - run: cd engine/language_client_ruby - - uses: oxidize-rb/actions/setup-ruby-and-rust@main with: rubygems: latest @@ -103,13 +105,10 @@ jobs: gem install rb_sys -v $version fi - - run: | - echo "Switching to top-level workspace - we need to be able to resolve the root-level Cargo.toml" - cd .. - pwd - - name: Build gem shell: bash + working-directory: engine + if: ${{ matrix.platform == 'x86_64-linux' }} run: | : Compile gem echo "Docker Working Directory: $(pwd)" @@ -123,6 +122,22 @@ jobs: --build \ -- sudo yum install -y perl-IPC-Cmd + - name: Build gem + shell: bash + working-directory: engine + if: ${{ matrix.platform != 'x86_64-linux' }} + run: | + : Compile gem + echo "Docker Working Directory: $(pwd)" + set -x + + rb-sys-dock \ + --platform ${{ matrix.platform }} \ + --mount-toolchains \ + --directory language_client_ruby \ + --ruby-versions 3.3,3.2,3.1,3.0,2.7 \ + --build + ################################################################################################################# # # END: these steps are copied from https://github.com/oxidize-rb/actions/blob/main/cross-gem/action.yml diff --git a/.gitignore b/.gitignore index 05dc4fb4d..15e8215d2 100644 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,7 @@ $RECYCLE.BIN/ /dist /node_modules /out/ +engine/language_client_ruby/**/*.bundle engine/target/ Cargo.lock Icon diff --git a/.mise.toml b/.mise.toml index b0964abea..bb001a086 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,2 +1,2 @@ [tools] -ruby = "3.3.0" +ruby = "3.1" diff --git a/engine/Cargo.lock b/engine/Cargo.lock index e575b1304..d05df2e40 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -2059,9 +2059,9 @@ dependencies = [ [[package]] name = "magnus" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc7a31fb0b64761e3cd09a6975577601fccc5f08b8fc9245064fc4f71ed6a9d" +checksum = "b1597ef40aa8c36be098249e82c9a20cf7199278ac1c1a1a995eeead6a184479" dependencies = [ "magnus-macros", "rb-sys", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index e4fb50750..251b1646b 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -66,5 +66,11 @@ license-file = "LICENSE" [workspace.metadata.workspaces] allow_branch = "canary" +[profile.dev] +lto = false + +[profile.dev2] +inherits = "dev" + [profile.release] lto = true diff --git a/engine/baml-lib/jsonish/src/jsonish/parser/markdown_parser.rs b/engine/baml-lib/jsonish/src/jsonish/parser/markdown_parser.rs index 1578e651e..ba9670822 100644 --- a/engine/baml-lib/jsonish/src/jsonish/parser/markdown_parser.rs +++ b/engine/baml-lib/jsonish/src/jsonish/parser/markdown_parser.rs @@ -12,8 +12,10 @@ pub fn parse<'a>(str: &'a str, options: &ParseOptions) -> Result) - let md_tag_start = regex::Regex::new(r"```([a-zA-Z0-9 ]+)(?:\n|$)").expect("Invalid regex"); - let md_tag_end = regex::Regex::new(r"```(?:\n|$)").expect("Invalid regex"); + let md_tag_start = regex::Regex::new(r"```([a-zA-Z0-9 ]+)(?:\n|$)") + .map_err(|e| anyhow::Error::from(e).context("Failed to build regex for md-tag-start"))?; + let md_tag_end = regex::Regex::new(r"```(?:\n|$)") + .map_err(|e| anyhow::Error::from(e).context("Failed to build regex for md-tag-end"))?; let mut should_loop = true; diff --git a/engine/baml-runtime/src/cli/generate.rs b/engine/baml-runtime/src/cli/generate.rs index b97537933..3a52996b3 100644 --- a/engine/baml-runtime/src/cli/generate.rs +++ b/engine/baml-runtime/src/cli/generate.rs @@ -30,16 +30,19 @@ impl GenerateArgs { if generated.is_empty() { let client_type = caller_type.into(); // Normally `baml_client` is added via the generator, but since we're not running the generator, we need to add it manually. - let output_dir = src_dir.join("..").join("baml_client"); + let output_dir_relative_to_baml_src = PathBuf::from(".."); let generate_output = runtime.generate_client( &client_type, &internal_baml_codegen::GeneratorArgs::new( - &output_dir, + &output_dir_relative_to_baml_src.join("baml_client"), &self.from, all_files.iter(), )?, )?; - println!("Generated 1 baml_client at {}", output_dir.display()); + println!( + "Generated 1 baml_client at {}", + generate_output.output_dir.display() + ); println!( r#" @@ -47,9 +50,10 @@ You can automatically generate a client by adding the following to any one of yo generator my_client {{ output_type "{}" - output_dir "../" + output_dir "{}" }}"#, - generate_output.client_type.to_string() + generate_output.client_type.to_string(), + output_dir_relative_to_baml_src.join("").display(), ); } else { println!("Generated {} baml_client", generated.len()); diff --git a/engine/language-client-codegen/askama.toml b/engine/language-client-codegen/askama.toml index 04668d9b8..8c040ea16 100644 --- a/engine/language-client-codegen/askama.toml +++ b/engine/language-client-codegen/askama.toml @@ -7,4 +7,3 @@ dirs = [ # whitespace can be either preserve, suppress, or minimize # suppress and minimize are both too aggressive for us whitespace = "preserve" -escape = "none" diff --git a/engine/language-client-codegen/src/lib.rs b/engine/language-client-codegen/src/lib.rs index b66147116..cfe1d5cff 100644 --- a/engine/language-client-codegen/src/lib.rs +++ b/engine/language-client-codegen/src/lib.rs @@ -31,7 +31,7 @@ fn relative_path_to_baml_src(path: &PathBuf, baml_src: &PathBuf) -> Result( - output_dir: impl Into, + output_dir_relative_to_baml_src: impl Into, baml_src_dir: impl Into, input_files: impl IntoIterator, ) -> Result { @@ -42,7 +42,7 @@ impl GeneratorArgs { .collect::>()?; Ok(Self { - output_dir_relative_to_baml_src: output_dir.into(), + output_dir_relative_to_baml_src: output_dir_relative_to_baml_src.into(), baml_src_dir: baml_src.clone(), // for the key, whhich is the name, just get the filename input_file_map, diff --git a/engine/language-client-codegen/src/python/templates/inlinedbaml.py.j2 b/engine/language-client-codegen/src/python/templates/inlinedbaml.py.j2 index 2b96fb8d3..60e2183d3 100644 --- a/engine/language-client-codegen/src/python/templates/inlinedbaml.py.j2 +++ b/engine/language-client-codegen/src/python/templates/inlinedbaml.py.j2 @@ -1,7 +1,7 @@ file_map = { - {% for k in file_map %} - {{ k.0 }}: {{ k.1 }}, + {% for (path, contents) in file_map %} + {{ path }}: {{ contents }}, {%- endfor %} } diff --git a/engine/language-client-codegen/src/ruby/field_type.rs b/engine/language-client-codegen/src/ruby/field_type.rs index fcb8edde9..7171fa81b 100644 --- a/engine/language-client-codegen/src/ruby/field_type.rs +++ b/engine/language-client-codegen/src/ruby/field_type.rs @@ -20,7 +20,7 @@ impl ToRuby for FieldType { TypeValue::String => "String".to_string(), TypeValue::Null => "NilClass".to_string(), // TODO: Create Baml::Types::Image - TypeValue::Image => "Baml::Types::Image".to_string(), + TypeValue::Image => "Baml::Image".to_string(), }, FieldType::Union(inner) => format!( // https://sorbet.org/docs/union-types diff --git a/engine/language-client-codegen/src/ruby/generate_types.rs b/engine/language-client-codegen/src/ruby/generate_types.rs index 9c6b21d62..4c22306cf 100644 --- a/engine/language-client-codegen/src/ruby/generate_types.rs +++ b/engine/language-client-codegen/src/ruby/generate_types.rs @@ -1,75 +1,51 @@ use anyhow::Result; -use askama::Template; use super::ruby_language_features::ToRuby; -use internal_baml_core::ir::{repr::IntermediateRepr, ClassWalker, EnumWalker}; +use internal_baml_core::ir::{repr::IntermediateRepr, ClassWalker, EnumWalker, FieldType}; #[derive(askama::Template)] #[template(path = "types.rb.j2", escape = "none")] -pub(crate) struct RubyTypes { - enums: Vec, - forward_decls: Vec, - classes: Vec, +pub(crate) struct RubyTypes<'ir> { + enums: Vec>, + classes: Vec>, } -#[derive(askama::Template)] -#[template(path = "enum.rb.j2")] -struct RubyEnum<'a> { - pub name: &'a str, - pub values: Vec<&'a str>, +struct RubyEnum<'ir> { + pub name: &'ir str, + pub values: Vec<&'ir str>, } -#[derive(askama::Template)] -#[template(path = "class_forward_decl.rb.j2")] -struct RubyForwardDecl<'a> { - name: &'a str, +struct RubyStruct<'ir> { + name: &'ir str, + fields: Vec<(&'ir str, String)>, } #[derive(askama::Template)] -#[template(path = "class.rb.j2")] -struct RubyStruct<'a> { - name: &'a str, - fields: Vec<(&'a str, String)>, +#[template(path = "partial-types.rb.j2", escape = "none")] +pub(crate) struct RubyStreamTypes<'ir> { + partial_classes: Vec>, } -impl TryFrom<&IntermediateRepr> for RubyTypes { +/// The Python class corresponding to Partial +struct PartialRubyStruct<'ir> { + name: &'ir str, + // the name, and the type of the field + fields: Vec<(&'ir str, String)>, +} + +impl<'ir> TryFrom<(&'ir IntermediateRepr, &'ir crate::GeneratorArgs)> for RubyTypes<'ir> { type Error = anyhow::Error; - fn try_from(ir: &IntermediateRepr) -> Result { + fn try_from((ir, _): (&'ir IntermediateRepr, &'ir crate::GeneratorArgs)) -> Result { Ok(RubyTypes { - enums: ir - .walk_enums() - .map(|e| { - Into::::into(&e) - .render() - .unwrap_or(format!("# Error rendering enum {}", e.name())) - }) - .collect(), - forward_decls: ir - .walk_classes() - .map(|c| { - RubyForwardDecl { name: c.name() } - .render() - .unwrap_or(format!( - "# Error rendering forward decl for class {}", - c.name() - )) - }) - .collect(), - classes: ir - .walk_classes() - .map(|c| { - Into::::into(&c) - .render() - .unwrap_or(format!("# Error rendering class {}", c.name())) - }) - .collect(), + enums: ir.walk_enums().map(|e| e.into()).collect(), + classes: ir.walk_classes().map(|c| c.into()).collect(), }) } } -impl<'ir> From<&EnumWalker<'ir>> for RubyEnum<'ir> { - fn from(e: &EnumWalker<'ir>) -> RubyEnum<'ir> { +impl<'ir> From> for RubyEnum<'ir> { + fn from(e: EnumWalker<'ir>) -> RubyEnum<'ir> { RubyEnum { name: e.name(), values: e @@ -83,8 +59,8 @@ impl<'ir> From<&EnumWalker<'ir>> for RubyEnum<'ir> { } } -impl<'ir> From<&ClassWalker<'ir>> for RubyStruct<'ir> { - fn from(c: &ClassWalker<'ir>) -> RubyStruct<'ir> { +impl<'ir> From> for RubyStruct<'ir> { + fn from(c: ClassWalker<'ir>) -> RubyStruct<'ir> { RubyStruct { name: c.name(), fields: c @@ -92,8 +68,86 @@ impl<'ir> From<&ClassWalker<'ir>> for RubyStruct<'ir> { .elem .static_fields .iter() - .map(|f| (f.elem.name.as_str(), f.elem.r#type.elem.to_ruby())) + .map(|f| (f.elem.name.as_str(), f.elem.r#type.elem.to_type_ref())) + .collect(), + } + } +} + +impl<'ir> TryFrom<(&'ir IntermediateRepr, &'ir crate::GeneratorArgs)> for RubyStreamTypes<'ir> { + type Error = anyhow::Error; + + fn try_from((ir, _): (&'ir IntermediateRepr, &'ir crate::GeneratorArgs)) -> Result { + Ok(RubyStreamTypes { + partial_classes: ir.walk_classes().map(|c| c.into()).collect(), + }) + } +} + +impl<'ir> From> for PartialRubyStruct<'ir> { + fn from(c: ClassWalker<'ir>) -> PartialRubyStruct<'ir> { + PartialRubyStruct { + name: c.name(), + fields: c + .item + .elem + .static_fields + .iter() + .map(|f| { + ( + f.elem.name.as_str(), + f.elem.r#type.elem.to_partial_type_ref(), + ) + }) .collect(), } } } + +pub(super) trait ToTypeReferenceInTypeDefinition { + fn to_type_ref(&self) -> String; + fn to_partial_type_ref(&self) -> String; +} + +impl ToTypeReferenceInTypeDefinition for FieldType { + fn to_type_ref(&self) -> String { + use ToRuby; + self.to_ruby() + } + + fn to_partial_type_ref(&self) -> String { + match self { + FieldType::Class(name) => format!("Baml::PartialTypes::{}", name.clone()), + FieldType::Enum(name) => format!("T.nilable(Baml::Types::{})", name.clone()), + // https://sorbet.org/docs/stdlib-generics + FieldType::List(inner) => format!("T::Array[{}]", inner.to_partial_type_ref()), + FieldType::Map(key, value) => { + format!( + "T::Hash[{}, {}]", + key.to_type_ref(), + value.to_partial_type_ref() + ) + } + FieldType::Primitive(_) => format!("T.nilable({})", self.to_type_ref()), + FieldType::Union(inner) => format!( + // https://sorbet.org/docs/union-types + "T.nilable(T.any({}))", + inner + .iter() + .map(|t| t.to_partial_type_ref()) + .collect::>() + .join(", ") + ), + FieldType::Tuple(inner) => format!( + // https://sorbet.org/docs/tuples + "T.nilable([{}])", + inner + .iter() + .map(|t| t.to_partial_type_ref()) + .collect::>() + .join(", ") + ), + FieldType::Optional(inner) => inner.to_partial_type_ref(), + } + } +} diff --git a/engine/language-client-codegen/src/ruby/mod.rs b/engine/language-client-codegen/src/ruby/mod.rs index 7f50ae25a..a6e85fae5 100644 --- a/engine/language-client-codegen/src/ruby/mod.rs +++ b/engine/language-client-codegen/src/ruby/mod.rs @@ -3,10 +3,9 @@ mod field_type; mod generate_types; mod ruby_language_features; -use std::path::PathBuf; +use std::{collections::BTreeMap, path::PathBuf}; use anyhow::Result; -use askama::Template; use indexmap::IndexMap; use ruby_language_features::ToRuby; @@ -16,7 +15,8 @@ use internal_baml_core::ir::repr::IntermediateRepr; use crate::dir_writer::FileCollector; -use self::ruby_language_features::RubyLanguageFeatures; +use generate_types::ToTypeReferenceInTypeDefinition; +use ruby_language_features::RubyLanguageFeatures; #[derive(askama::Template)] #[template(path = "client.rb.j2", escape = "none")] @@ -25,39 +25,36 @@ struct RubyClient { } struct RubyFunction { name: String, + partial_return_type: String, return_type: String, args: Vec<(String, String)>, } +#[derive(askama::Template)] +#[template(path = "inlined.rb.j2", escape = "none")] +struct InlinedBaml { + file_map: Vec<(String, String)>, +} + pub(crate) fn generate( ir: &IntermediateRepr, generator: &crate::GeneratorArgs, ) -> Result> { let mut collector = FileCollector::::new(); - collector.add_file( - "types.rb", - TryInto::::try_into(ir) - .map_err(|e| e.context("Error while building types.rb"))? - .render() - .map_err(|e| anyhow::Error::from(e).context("Error while rendering types.rb"))?, - ); - - collector.add_file( - "client.rb", - TryInto::::try_into(ir) - .map_err(|e| e.context("Error while building client.rb"))? - .render() - .map_err(|e| anyhow::Error::from(e).context("Error while rendering client.rb"))?, - ); + collector + .add_template::("partial-types.rb", (ir, generator))?; + collector.add_template::("types.rb", (ir, generator))?; + collector.add_template::("client.rb", (ir, generator))?; + collector.add_template::("inlined.rb", (ir, generator))?; collector.commit(&generator.output_dir()) } -impl TryFrom<&IntermediateRepr> for RubyClient { +impl<'ir> TryFrom<(&'ir IntermediateRepr, &'ir crate::GeneratorArgs)> for RubyClient { type Error = anyhow::Error; - fn try_from(ir: &IntermediateRepr) -> Result { + fn try_from((ir, _): (&'ir IntermediateRepr, &'ir crate::GeneratorArgs)) -> Result { let functions = ir .walk_functions() .map(|f| { @@ -69,6 +66,7 @@ impl TryFrom<&IntermediateRepr> for RubyClient { let (_function, _impl_) = c.item; Ok(RubyFunction { name: f.name().to_string(), + partial_return_type: f.elem().output().to_partial_type_ref(), return_type: f.elem().output().to_ruby(), args: match f.inputs() { either::Either::Left(_args) => anyhow::bail!("Ruby codegen does not support unnamed args: please add names to all arguments of BAML function '{}'", f.name().to_string()), @@ -88,3 +86,13 @@ impl TryFrom<&IntermediateRepr> for RubyClient { Ok(RubyClient { funcs: functions }) } } + +impl TryFrom<(&'_ IntermediateRepr, &'_ crate::GeneratorArgs)> for InlinedBaml { + type Error = anyhow::Error; + + fn try_from((_ir, args): (&IntermediateRepr, &crate::GeneratorArgs)) -> Result { + Ok(InlinedBaml { + file_map: args.file_map()?, + }) + } +} diff --git a/engine/language-client-codegen/src/ruby/ruby_language_features.rs b/engine/language-client-codegen/src/ruby/ruby_language_features.rs index f0d91147e..b4855b471 100644 --- a/engine/language-client-codegen/src/ruby/ruby_language_features.rs +++ b/engine/language-client-codegen/src/ruby/ruby_language_features.rs @@ -11,7 +11,7 @@ impl LanguageFeatures for RubyLanguageFeatures { # # Welcome to Baml! To use this generated code, please run the following: # -# $ bundle add baml sorbet-runtime sorbet-coerce sorbet-struct-comparable +# $ bundle add baml sorbet-runtime sorbet-struct-comparable # ############################################################################### diff --git a/engine/language-client-codegen/src/ruby/templates/class.rb.j2 b/engine/language-client-codegen/src/ruby/templates/class.rb.j2 deleted file mode 100644 index 6b36e2336..000000000 --- a/engine/language-client-codegen/src/ruby/templates/class.rb.j2 +++ /dev/null @@ -1,24 +0,0 @@ -{#- module Baml #} - {#- module Types #} - {#- https://sorbet.org/docs/tstruct #} - class {{name}} < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - {%- for (name, type) in fields %} - const :{{name}}, {{type}} - {%- endfor %} - - {#- The below is part of a proof-of-concept serialization impl, see struct_to_map_via_to_hash #} - {#- - def to_hash - { - {%- for (name, _) in fields %} - :{{name}} => self.{{name}}, - {%- endfor %} - } - end - #} - end - {#- end #} -{#- end #} \ No newline at end of file diff --git a/engine/language-client-codegen/src/ruby/templates/class_forward_decl.rb.j2 b/engine/language-client-codegen/src/ruby/templates/class_forward_decl.rb.j2 deleted file mode 100644 index 192f739cb..000000000 --- a/engine/language-client-codegen/src/ruby/templates/class_forward_decl.rb.j2 +++ /dev/null @@ -1,6 +0,0 @@ -{#- module Baml #} - {#- module Types #} - {#- https://sorbet.org/docs/tstruct #} - class {{name}} < T::Struct; end - {#- end #} -{#- end #} \ No newline at end of file diff --git a/engine/language-client-codegen/src/ruby/templates/client.rb.j2 b/engine/language-client-codegen/src/ruby/templates/client.rb.j2 index 0a90c4f06..8afe8d4bd 100644 --- a/engine/language-client-codegen/src/ruby/templates/client.rb.j2 +++ b/engine/language-client-codegen/src/ruby/templates/client.rb.j2 @@ -1,35 +1,51 @@ -# typed: strict +# typed: false require "baml" require "sorbet-runtime" +require_relative "inlined" +require_relative "partial-types" require_relative "types" module Baml + @instance = nil + + def self.Client + if @instance.nil? + @instance = BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_files("baml_src", Baml::Inlined::FILE_MAP, ENV)) + end + + @instance + end class BamlClient extend T::Sig - sig { returns(UnstableBamlClient) } - attr_reader :unstable + sig { returns(BamlStreamClient) } + attr_reader :stream sig {params(runtime: Baml::Ffi::BamlRuntime).void} def initialize(runtime:) @runtime = runtime - @unstable = T.let(UnstableBamlClient.new(runtime: runtime), UnstableBamlClient) + @ctx_manager = runtime.create_context_manager() + @stream = BamlStreamClient.new(runtime: @runtime, ctx_manager: @ctx_manager) end sig {params(path: String).returns(BamlClient)} def self.from_directory(path) - BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_directory(path)) + BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_directory(path, ENV)) end {% for fn in funcs -%} sig { + {% if fn.args.len() > 0 %} params( {% for (name, type) in fn.args -%} {{name}}: {{type}}, {%- endfor %} ).returns({{ fn.return_type }}) + {% else %} + returns({{ fn.return_type }}) + {% endif %} } def {{fn.name}}( {% for (name, _) in fn.args -%} @@ -37,26 +53,28 @@ module Baml {%- endfor %} ) raw = @runtime.call_function( - function_name: "{{fn.name}}", - args: { + "{{fn.name}}", + { {% for (name, _) in fn.args -%} "{{name}}" => {{name}}, {%- endfor %} - } + }, + @ctx_manager, ) - Baml::convert_to({{ fn.return_type }}).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end {% endfor %} end - class UnstableBamlClient + class BamlStreamClient extend T::Sig - sig {params(runtime: Baml::Ffi::BamlRuntime).void} - def initialize(runtime:) + sig {params(runtime: Baml::Ffi::BamlRuntime, ctx_manager: Baml::Ffi::RuntimeContextManager).void} + def initialize(runtime:, ctx_manager:) @runtime = runtime + @ctx_manager = ctx_manager end {% for fn in funcs -%} @@ -65,24 +83,25 @@ module Baml {% for (name, type) in fn.args -%} {{name}}: {{type}}, {%- endfor %} - ).returns(Baml::Unstable::FunctionResult[{{ fn.return_type }}]) + ).returns(Baml::BamlStream[{{ fn.return_type }}]) } def {{fn.name}}( {% for (name, _) in fn.args -%} {{name}}:{% if !loop.last %},{% endif %} {%- endfor %} ) - raw = @runtime.call_function( - function_name: "{{fn.name}}", - args: { + raw = @runtime.stream_function( + "{{fn.name}}", + { {% for (name, _) in fn.args -%} "{{name}}" => {{name}}, {%- endfor %} - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[{{ fn.return_type }}].new( - inner: raw, - parsed: Baml::convert_to({{ fn.return_type }}).from(raw.parsed) + Baml::BamlStream[{{fn.partial_return_type}}, {{fn.return_type}}].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end diff --git a/engine/language-client-codegen/src/ruby/templates/enum.rb.j2 b/engine/language-client-codegen/src/ruby/templates/enum.rb.j2 deleted file mode 100644 index 70f22a3f7..000000000 --- a/engine/language-client-codegen/src/ruby/templates/enum.rb.j2 +++ /dev/null @@ -1,12 +0,0 @@ -{#- module Baml #} - {#- module Types #} - {#- https://sorbet.org/docs/tenum #} - class {{name}} < T::Enum - enums do - {%- for value in values %} - {{ value }} = new("{{ value }}") - {%- endfor %} - end - end - {#- end #} -{#- end #} \ No newline at end of file diff --git a/engine/language-client-codegen/src/ruby/templates/inlined.rb.j2 b/engine/language-client-codegen/src/ruby/templates/inlined.rb.j2 new file mode 100644 index 000000000..e9029f86f --- /dev/null +++ b/engine/language-client-codegen/src/ruby/templates/inlined.rb.j2 @@ -0,0 +1,9 @@ +module Baml + module Inlined + FILE_MAP = { + {% for (path, contents) in file_map %} + {{ path }} => {{ contents }}, + {%- endfor %} + } + end +end diff --git a/engine/language-client-codegen/src/ruby/templates/partial-types.rb.j2 b/engine/language-client-codegen/src/ruby/templates/partial-types.rb.j2 new file mode 100644 index 000000000..0d729cbfc --- /dev/null +++ b/engine/language-client-codegen/src/ruby/templates/partial-types.rb.j2 @@ -0,0 +1,26 @@ +# typed: false +require "sorbet-runtime" +require "sorbet-struct-comparable" + +require_relative "types" + +module Baml + {# Baml::PartialTypes is reserved for generated types. #} + module PartialTypes + {#- Forward declarations for types #} + {%- for cls in partial_classes %} + class {{cls.name}} < T::Struct; end + {%- endfor %} + + {#- https://sorbet.org/docs/tstruct #} + {%- for cls in partial_classes %} + class {{cls.name}} < T::Struct + include T::Struct::ActsAsComparable + + {%- for (name, type) in cls.fields %} + const :{{name}}, {{type}} + {%- endfor %} + end + {%- endfor %} + end +end \ No newline at end of file diff --git a/engine/language-client-codegen/src/ruby/templates/types.rb.j2 b/engine/language-client-codegen/src/ruby/templates/types.rb.j2 index 187227c68..d962f4c62 100644 --- a/engine/language-client-codegen/src/ruby/templates/types.rb.j2 +++ b/engine/language-client-codegen/src/ruby/templates/types.rb.j2 @@ -1,38 +1,37 @@ # typed: false -require "delegate" -require "sorbet-coerce" -require "sorbet-struct-comparable" require "sorbet-runtime" +require "sorbet-struct-comparable" module Baml {# Baml::Types is reserved for generated types. #} module Types + {#- https://sorbet.org/docs/tenum #} {%- for enum in enums %} -{{ enum }} - {%- endfor %} - {%- for forward_decl in forward_decls %} - {{ forward_decl }} + class {{ enum.name }} < T::Enum + {%- if enum.values.len() > 0 %} + enums do + {%- for value in enum.values %} + {{ value }} = new("{{ value }}") + {%- endfor %} + end + {%- endif %} + end {%- endfor %} + + {#- Forward declarations for types #} {%- for cls in classes %} -{{ cls }} + class {{cls.name}} < T::Struct; end {%- endfor %} - end - - module Unstable - class FunctionResult < SimpleDelegator - extend T::Sig - extend T::Generic - - ParsedType = type_member - sig { returns(ParsedType) } - attr_reader :parsed + {#- https://sorbet.org/docs/tstruct #} + {%- for cls in classes %} + class {{cls.name}} < T::Struct + include T::Struct::ActsAsComparable - sig { params(inner: Baml::Ffi::FunctionResult, parsed: ParsedType).void } - def initialize(inner:, parsed:) - @inner = inner - @parsed = parsed - end + {%- for (name, type) in cls.fields %} + const :{{name}}, {{type}} + {%- endfor %} end + {%- endfor %} end end \ No newline at end of file diff --git a/engine/language_client_python/python_src/baml_py/__init__.py b/engine/language_client_python/python_src/baml_py/__init__.py index 7db4c7d63..1b6ff8636 100644 --- a/engine/language_client_python/python_src/baml_py/__init__.py +++ b/engine/language_client_python/python_src/baml_py/__init__.py @@ -9,7 +9,7 @@ invoke_runtime_cli, ) from .stream import BamlStream -from .async_context_vars import CtxManager as BamlCtxManager +from .ctx_manager import CtxManager as BamlCtxManager __all__ = [ "BamlRuntime", diff --git a/engine/language_client_python/python_src/baml_py/async_context_vars.py b/engine/language_client_python/python_src/baml_py/ctx_manager.py similarity index 100% rename from engine/language_client_python/python_src/baml_py/async_context_vars.py rename to engine/language_client_python/python_src/baml_py/ctx_manager.py diff --git a/engine/language_client_python/src/types/function_result_stream.rs b/engine/language_client_python/src/types/function_result_stream.rs index 2b442729d..face146b2 100644 --- a/engine/language_client_python/src/types/function_result_stream.rs +++ b/engine/language_client_python/src/types/function_result_stream.rs @@ -5,9 +5,6 @@ use crate::BamlError; use super::function_results::FunctionResult; use super::runtime_ctx_manager::RuntimeContextManager; -use super::type_builder::TypeBuilder; -use super::BamlRuntime; -use baml_runtime::InternalRuntimeInterface; crate::lang_wrapper!( FunctionResultStream, diff --git a/engine/language_client_ruby/Gemfile b/engine/language_client_ruby/Gemfile index 70fed42c0..debb9be7b 100644 --- a/engine/language_client_ruby/Gemfile +++ b/engine/language_client_ruby/Gemfile @@ -11,6 +11,7 @@ gem "rake-compiler", "~> 1.2" gem "rb_sys", "=0.9.97" group :test do + gem "async" gem "minitest-reporters" gem "sorbet-coerce" gem "sorbet-runtime" diff --git a/engine/language_client_ruby/Gemfile.lock b/engine/language_client_ruby/Gemfile.lock index a953f961d..c6e15488f 100644 --- a/engine/language_client_ruby/Gemfile.lock +++ b/engine/language_client_ruby/Gemfile.lock @@ -1,13 +1,27 @@ PATH remote: . specs: - baml (0.1.6) + baml (0.1.9) GEM remote: https://rubygems.org/ specs: ansi (1.5.0) + async (2.12.0) + console (~> 1.25, >= 1.25.2) + fiber-annotation + io-event (~> 1.6) builder (3.2.4) + console (1.25.2) + fiber-annotation + fiber-local (~> 1.1) + json + fiber-annotation (0.2.0) + fiber-local (1.1.0) + fiber-storage + fiber-storage (0.1.1) + io-event (1.6.0) + json (2.7.2) minitest (5.22.3) minitest-reporters (1.6.1) ansi @@ -34,6 +48,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + async baml! minitest-reporters rake (~> 13.0) diff --git a/engine/language_client_ruby/baml.gemspec b/engine/language_client_ruby/baml.gemspec index c61d66978..9da3852f0 100644 --- a/engine/language_client_ruby/baml.gemspec +++ b/engine/language_client_ruby/baml.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "baml" - spec.version = "0.1.7" + spec.version = "0.1.10" spec.authors = ["BoundaryML"] spec.email = ["contact@boundaryml.com"] @@ -14,9 +14,10 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir["lib/**/*.rb", "ext/**/*.{rs,toml,lock,rb}"] + spec.files = Dir["exe/*", "lib/**/*.rb", "ext/**/*.{rs,toml,lock,rb}"] spec.bindir = "exe" - spec.executables = [] + # TODO: make sure this is invoke-able from an installed gem + spec.executables = ["baml-cli"] spec.require_paths = ["lib"] spec.extensions = ["ext/ruby_ffi/extconf.rb"] diff --git a/engine/language_client_ruby/exe/baml-cli b/engine/language_client_ruby/exe/baml-cli new file mode 100755 index 000000000..b11ea96e2 --- /dev/null +++ b/engine/language_client_ruby/exe/baml-cli @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require "baml" + +Baml::Ffi::invoke_runtime_cli(Process.argv0, ARGV) \ No newline at end of file diff --git a/engine/language_client_ruby/ext/ruby_ffi/Cargo.toml b/engine/language_client_ruby/ext/ruby_ffi/Cargo.toml index bfe1d8324..e295928b7 100644 --- a/engine/language_client_ruby/ext/ruby_ffi/Cargo.toml +++ b/engine/language_client_ruby/ext/ruby_ffi/Cargo.toml @@ -17,13 +17,14 @@ env_logger.workspace = true futures.workspace = true indexmap.workspace = true log.workspace = true -magnus = { version = "0.6", features = ["rb-sys"] } +magnus = { version = "0.6.4", default = false, features = ["rb-sys"] } # Must be kept in sync with ../../Gemfile rb-sys = { version = "0.9.97", features = [ "global-allocator", - #"bindgen-rbimpls", - #"bindgen-deprecated-types", + "bindgen-rbimpls", + "bindgen-deprecated-types", "stable-api", + "stable-api-compiled-fallback", ] } serde.workspace = true serde_json.workspace = true diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/function_result.rs b/engine/language_client_ruby/ext/ruby_ffi/src/function_result.rs new file mode 100644 index 000000000..a3f23c7b1 --- /dev/null +++ b/engine/language_client_ruby/ext/ruby_ffi/src/function_result.rs @@ -0,0 +1,70 @@ +use baml_types::BamlValue; +use magnus::{ + class, exception::runtime_error, method, prelude::*, value::Value, Error, RModule, Ruby, +}; + +use crate::ruby_to_json; +use crate::Result; + +#[magnus::wrap(class = "Baml::Ffi::FunctionResult", free_immediately, size)] +pub struct FunctionResult { + inner: baml_runtime::FunctionResult, +} + +impl FunctionResult { + pub fn new(inner: baml_runtime::FunctionResult) -> Self { + Self { inner } + } + + #[allow(dead_code)] + fn to_s(&self) -> String { + format!("{}", self.inner) + } + + #[allow(dead_code)] + fn raw(&self) -> Result { + match self.inner.content() { + Ok(content) => Ok(content.to_string()), + Err(_) => Err(Error::new( + runtime_error(), + format!("No LLM response: {}", self.inner), + )), + } + } + + pub fn parsed_using_types( + ruby: &Ruby, + rb_self: &FunctionResult, + types: RModule, + ) -> Result { + match rb_self.inner.parsed_content() { + Ok(parsed) => { + ruby_to_json::RubyToJson::serialize_baml(ruby, types, &BamlValue::from(parsed)) + .map_err(|e| { + magnus::Error::new( + ruby.exception_type_error(), + format!("failing inside parsed_using_types: {:?}", e), + ) + }) + } + Err(_) => Err(Error::new( + ruby.exception_runtime_error(), + format!("Failed to parse LLM response: {}", rb_self.inner), + )), + } + } + + /// For usage in magnus::init + /// + /// TODO: use traits and macros to implement this + pub fn define_in_ruby(module: &RModule) -> Result<()> { + let cls = module.define_class("FunctionResult", class::object())?; + + cls.define_method( + "parsed_using_types", + method!(FunctionResult::parsed_using_types, 1), + )?; + + Ok(()) + } +} diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/function_result_stream.rs b/engine/language_client_ruby/ext/ruby_ffi/src/function_result_stream.rs new file mode 100644 index 000000000..0eb6329a1 --- /dev/null +++ b/engine/language_client_ruby/ext/ruby_ffi/src/function_result_stream.rs @@ -0,0 +1,66 @@ +use std::cell::RefCell; +use std::sync::Arc; + +use magnus::{block::Proc, class, method, Module, RModule, Ruby}; + +use crate::function_result::FunctionResult; +use crate::runtime_ctx_manager::RuntimeContextManager; +use crate::Error; +use crate::Result; + +#[magnus::wrap(class = "Baml::Ffi::FunctionResultStream", free_immediately, size)] +pub struct FunctionResultStream { + inner: RefCell, + t: Arc, +} + +impl FunctionResultStream { + pub(super) fn new( + inner: baml_runtime::FunctionResultStream, + t: Arc, + ) -> Self { + Self { + inner: RefCell::new(inner), + t, + } + } + + fn done( + ruby: &Ruby, + rb_self: &FunctionResultStream, + ctx: &RuntimeContextManager, + ) -> Result { + let on_event = if ruby.block_given() { + let proc = ruby.block_proc()?; + Some(move |event: baml_runtime::FunctionResult| { + // ignore errors if they happen + let _ = proc.call::<_, magnus::Value>((FunctionResult::new(event),)); + () + }) + } else { + None + }; + + match rb_self + .t + .block_on(rb_self.inner.borrow_mut().run(on_event, &ctx.inner, None)) + { + (Ok(res), _) => Ok(FunctionResult::new(res)), + (Err(e), _) => Err(Error::new( + ruby.exception_runtime_error(), + format!("{:?}", e), + )), + } + } + + /// For usage in magnus::init + /// + /// TODO: use traits and macros to implement this + pub fn define_in_ruby(module: &RModule) -> Result<()> { + let cls = module.define_class("FunctionResultStream", class::object())?; + + cls.define_method("done", method!(FunctionResultStream::done, 1))?; + + Ok(()) + } +} diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/lib.rs b/engine/language_client_ruby/ext/ruby_ffi/src/lib.rs index 8a1ed57bd..7f0f264b8 100644 --- a/engine/language_client_ruby/ext/ruby_ffi/src/lib.rs +++ b/engine/language_client_ruby/ext/ruby_ffi/src/lib.rs @@ -1,49 +1,65 @@ use baml_runtime::{BamlRuntime, RuntimeContext}; +use baml_types::BamlValue; use indexmap::IndexMap; +use magnus::block::Proc; use magnus::IntoValue; use magnus::{ class, error::RubyUnavailableError, exception::runtime_error, function, method, prelude::*, scan_args::get_kwargs, Error, RHash, Ruby, }; -use std::cell::RefCell; use std::collections::HashMap; +use std::ops::Deref; use std::path::PathBuf; +use std::sync::Arc; +use function_result::FunctionResult; +use function_result_stream::FunctionResultStream; +use runtime_ctx_manager::RuntimeContextManager; + +mod function_result; +mod function_result_stream; mod ruby_to_json; -mod ruby_types; -mod tokio_demo; +mod runtime_ctx_manager; type Result = std::result::Result; // must be kept in sync with rb.define_class in the init() fn #[magnus::wrap(class = "Baml::Ffi::BamlRuntime", free_immediately, size)] struct BamlRuntimeFfi { - internal: RefCell, - t: tokio::runtime::Runtime, + inner: Arc, + t: Arc, } -impl BamlRuntimeFfi { - fn try_lock_gvl() -> Result { - match Ruby::get() { - Ok(ruby) => Ok(ruby), - Err(e) => match e { - // TODO(sam): this error handling code doesn't feel right to me - calling `runtime_error()` will - // panic from a non-Ruby thread - but I'm not sure what the right way to handle this is - RubyUnavailableError::GvlUnlocked => Err(Error::new( - runtime_error(), - "Failed to access Ruby runtime: GVL is unlocked", - )), - RubyUnavailableError::NonRubyThread => Err(Error::new( - runtime_error(), - "Failed to access Ruby runtime: calling from a non-Ruby thread", - )), - }, +impl Drop for BamlRuntimeFfi { + fn drop(&mut self) { + use baml_runtime::runtime_interface::ExperimentalTracingInterface; + match self.inner.flush() { + Ok(_) => log::info!("Flushed BAML log events"), + Err(e) => log::error!("Error while flushing BAML log events: {:?}", e), } } +} - pub fn from_directory(directory: PathBuf, env_vars: HashMap) -> Result { - let ruby = BamlRuntimeFfi::try_lock_gvl()?; +impl BamlRuntimeFfi { + fn make_tokio_runtime(ruby: &Ruby) -> Result { + // NB: libruby will panic if called from a non-Ruby thread, so we stick to the current thread + // to avoid causing issues + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| { + Error::new( + ruby.exception_runtime_error(), + format!("Failed to start tokio runtime because:\n{:?}", e), + ) + }) + } + pub fn from_directory( + ruby: &Ruby, + directory: PathBuf, + env_vars: HashMap, + ) -> Result { let baml_runtime = match BamlRuntime::from_directory(&directory, env_vars) { Ok(br) => br, Err(e) => { @@ -54,51 +70,73 @@ impl BamlRuntimeFfi { } }; - // NB: libruby will panic if called from a non-Ruby thread, so we stick to the current thread - // to avoid causing issues - let Ok(tokio_runtime) = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - else { - return Err(Error::new( - ruby.exception_runtime_error(), - "Failed to start tokio runtime", - )); + let rt = BamlRuntimeFfi { + inner: Arc::new(baml_runtime), + t: Arc::new(Self::make_tokio_runtime(ruby)?), + }; + + Ok(rt) + } + + pub fn from_files( + ruby: &Ruby, + root_path: String, + files: HashMap, + env_vars: HashMap, + ) -> Result { + let baml_runtime = match BamlRuntime::from_file_content(&root_path, &files, env_vars) { + Ok(br) => br, + Err(e) => { + return Err(Error::new( + ruby.exception_runtime_error(), + format!("{:?}", e.context("Failed to initialize BAML runtime")), + )) + } + }; + + let rt = BamlRuntimeFfi { + inner: Arc::new(baml_runtime), + t: Arc::new(Self::make_tokio_runtime(ruby)?), }; - Ok(Self { - internal: RefCell::new(baml_runtime), - t: tokio_runtime, - }) + Ok(rt) + } + + pub fn create_context_manager(&self) -> RuntimeContextManager { + RuntimeContextManager { + inner: self + .inner + .create_ctx_manager(BamlValue::String("ruby".to_string())), + } } pub fn call_function( - &self, + ruby: &Ruby, + rb_self: &BamlRuntimeFfi, function_name: String, args: RHash, ctx: &RuntimeContextManager, - ) -> Result { - let ruby = BamlRuntimeFfi::try_lock_gvl()?; - + ) -> Result { let args = match ruby_to_json::RubyToJson::convert_hash_to_json(args) { Ok(args) => args.into_iter().collect(), Err(e) => { return Err(Error::new( ruby.exception_syntax_error(), - format!("error while parsing keyword 'args' as JSON:\n{}", e), + format!("error while parsing call_function args:\n{}", e), )); } }; - log::debug!("Calling {function_name} with:\nargs: {args:#?}\nctx (where env is envvar overrides): {ctx:#?}"); + log::debug!("Calling {function_name} with:\nargs: {args:#?}\nctx ???"); - let retval = match self.t.block_on(self.internal.borrow().call_function( + let retval = match rb_self.t.block_on(rb_self.inner.call_function( function_name.clone(), &args, &ctx.inner, + None, )) { - Ok(res) => Ok(ruby_types::FunctionResult::new(res)), - Err(e) => Err(Error::new( + (Ok(res), _) => Ok(FunctionResult::new(res)), + (Err(e), _) => Err(Error::new( ruby.exception_runtime_error(), format!( "{:?}", @@ -109,10 +147,60 @@ impl BamlRuntimeFfi { retval } + + fn stream_function( + ruby: &Ruby, + rb_self: &BamlRuntimeFfi, + function_name: String, + args: RHash, + ctx: &RuntimeContextManager, + ) -> Result { + let args = match ruby_to_json::RubyToJson::convert_hash_to_json(args) { + Ok(args) => args.into_iter().collect(), + Err(e) => { + return Err(Error::new( + ruby.exception_syntax_error(), + format!("error while parsing stream_function args:\n{}", e), + )); + } + }; + + log::debug!("Streaming {function_name} with:\nargs: {args:#?}\nctx ???"); + + let retval = + match rb_self + .inner + .stream_function(function_name.clone(), &args, &ctx.inner, None) + { + Ok(res) => Ok(FunctionResultStream::new(res, rb_self.t.clone())), + Err(e) => Err(Error::new( + ruby.exception_runtime_error(), + format!( + "{:?}", + e.context(format!("error while calling {function_name}")) + ), + )), + }; + + retval + } +} + +fn invoke_runtime_cli(ruby: &Ruby, argv0: String, argv: Vec) -> Result<()> { + baml_runtime::BamlRuntime::run_cli( + std::iter::once(argv0).chain(argv.into_iter()).collect(), + baml_runtime::CallerType::Ruby, + ) + .map_err(|e| { + Error::new( + ruby.exception_runtime_error(), + format!("{:?}", e.context(format!("error while invoking baml-cli"))), + ) + }) } #[magnus::init(name = "ruby_ffi")] -fn init() -> Result<()> { +fn init(ruby: &Ruby) -> Result<()> { if let Err(e) = env_logger::try_init_from_env( env_logger::Env::new() .filter("BAML_LOG") @@ -121,9 +209,9 @@ fn init() -> Result<()> { eprintln!("Failed to initialize BAML logger: {:#}", e); }; - let rb = BamlRuntimeFfi::try_lock_gvl()?; + let module = ruby.define_module("Baml")?.define_module("Ffi")?; - let module = rb.define_module("Baml")?.define_module("Ffi")?; + module.define_module_function("invoke_runtime_cli", function!(invoke_runtime_cli, 2))?; // must be kept in sync with the magnus::wrap annotation let runtime_class = module.define_class("BamlRuntime", class::object())?; @@ -131,16 +219,32 @@ fn init() -> Result<()> { "from_directory", function!(BamlRuntimeFfi::from_directory, 2), )?; + runtime_class + .define_singleton_method("from_files", function!(BamlRuntimeFfi::from_files, 3))?; + //runtime_class.define_method("call_function", method!(BamlRuntimeFfi::call_function, 2))?; + runtime_class.define_method( + "create_context_manager", + method!(BamlRuntimeFfi::create_context_manager, 0), + )?; runtime_class.define_method("call_function", method!(BamlRuntimeFfi::call_function, 3))?; + runtime_class.define_method( + "stream_function", + method!(BamlRuntimeFfi::stream_function, 3), + )?; - ruby_types::define_types(&module)?; + FunctionResult::define_in_ruby(&module)?; + FunctionResultStream::define_in_ruby(&module)?; + RuntimeContextManager::define_in_ruby(&module)?; // everything below this is for our own testing purposes - tokio_demo::TokioDemo::define_in_ruby(&module)?; module.define_module_function( "roundtrip", function!(ruby_to_json::RubyToJson::roundtrip, 1), )?; + module.define_module_function( + "serialize", + function!(ruby_to_json::RubyToJson::serialize, 2), + )?; Ok(()) } diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs b/engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs index 24a83ea2c..97e66403c 100644 --- a/engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs +++ b/engine/language_client_ruby/ext/ruby_ffi/src/ruby_to_json.rs @@ -1,9 +1,12 @@ +use baml_types::{BamlMap, BamlValue}; +use core::hash; +use indexmap::IndexMap; use magnus::{ class, error::RubyUnavailableError, exception::runtime_error, function, method, prelude::*, - scan_args::get_kwargs, value::Value, Error, Float, Integer, RArray, RHash, RString, Ruby, - Symbol, + scan_args::get_kwargs, value::Value, Error, Float, Integer, IntoValue, RArray, RClass, RHash, + RModule, RString, Ruby, Symbol, }; -use std::result::Result; +use std::{collections::HashMap, result::Result}; struct SerializationError { position: Vec, @@ -20,6 +23,49 @@ impl<'rb> RubyToJson<'rb> { serde_magnus::serialize(&json) } + pub fn serialize_baml(ruby: &Ruby, types: RModule, from: &BamlValue) -> crate::Result { + match from { + BamlValue::Class(class_name, class_fields) => { + let class_type: RClass = types.const_get(class_name.as_str())?; + let hash = ruby.hash_new(); + for (k, v) in class_fields.iter() { + let k = ruby.sym_new(k.as_str()); + let v = RubyToJson::serialize_baml(ruby, types, v)?; + hash.aset(k, v)?; + } + class_type.funcall("new", (hash,)) + } + BamlValue::Enum(enum_name, enum_value) => { + let enum_type: RClass = types.const_get(enum_name.as_str())?; + let enum_value = ruby.str_new(enum_value); + enum_type.funcall("deserialize", (enum_value,)) + } + BamlValue::Map(m) => { + let hash = ruby.hash_new(); + for (k, v) in m.iter() { + let k = ruby.str_new(k); + let v = RubyToJson::serialize_baml(ruby, types, v)?; + hash.aset(k, v)?; + } + Ok(hash.into_value_with(ruby)) + } + BamlValue::List(l) => { + let arr = ruby.ary_new(); + for v in l.iter() { + let v = RubyToJson::serialize_baml(ruby, types, v)?; + arr.push(v)?; + } + Ok(arr.into_value_with(ruby)) + } + _ => serde_magnus::serialize(from), + } + } + + pub fn serialize(ruby: &Ruby, types: RModule, from: Value) -> crate::Result { + let json = RubyToJson::convert(from)?; + RubyToJson::serialize_baml(ruby, types, &json) + } + /// Convert a Ruby object to a JSON object. /// /// We have to implement this ourselves instead of relying on Serde, because in the codegen, @@ -27,7 +73,7 @@ impl<'rb> RubyToJson<'rb> { /// fields do not serialize correctly, see https://sorbet.org/docs/tstruct#serialize-gotchas) /// /// We do still rely on :serialize for enums though. - pub fn convert(from: Value) -> crate::Result { + pub fn convert(from: Value) -> crate::Result { let ruby = Ruby::get_with(from); let result = RubyToJson { ruby: &ruby }.to_json(from, vec![]); @@ -50,9 +96,7 @@ impl<'rb> RubyToJson<'rb> { } } - pub fn convert_hash_to_json( - from: RHash, - ) -> crate::Result> { + pub fn convert_hash_to_json(from: RHash) -> crate::Result> { let ruby = Ruby::get_with(from); let result = RubyToJson { ruby: &ruby }.hash_to_map(from, vec![]); @@ -79,28 +123,17 @@ impl<'rb> RubyToJson<'rb> { &self, any: Value, field_pos: Vec, - ) -> Result> { - // done - self.ruby.class_float(); - // done - self.ruby.class_integer(); - // done - self.ruby.class_true_class(); - // done - self.ruby.class_false_class(); - // done - self.ruby.class_nil_class(); - // done - self.ruby.class_array(); - // done - self.ruby.class_string(); - // done? - self.ruby.class_hash(); - // enums - // done - structs - + ) -> Result> { if any.is_nil() { - return Ok(serde_json::Value::Null); + return Ok(BamlValue::Null); } if any.is_kind_of(self.ruby.class_true_class()) { - return Ok(serde_json::Value::Bool(true)); + return Ok(BamlValue::Bool(true)); } if any.is_kind_of(self.ruby.class_false_class()) { - return Ok(serde_json::Value::Bool(false)); + return Ok(BamlValue::Bool(false)); } if let Some(any) = magnus::Integer::from_value(any) { @@ -112,9 +145,7 @@ impl<'rb> RubyToJson<'rb> { } if let Some(any) = RString::from_value(any) { - return self - .to_string(any, field_pos) - .map(serde_json::Value::String); + return self.to_string(any, field_pos).map(BamlValue::String); } if let Some(any) = RArray::from_value(any) { @@ -122,9 +153,7 @@ impl<'rb> RubyToJson<'rb> { } if let Some(any) = RHash::from_value(any) { - return self - .hash_to_map(any, field_pos) - .map(serde_json::Value::Object); + return self.hash_to_map(any, field_pos).map(BamlValue::Map); } if let Ok(superclass) = any.class().superclass() { @@ -152,32 +181,19 @@ impl<'rb> RubyToJson<'rb> { &self, any: Integer, field_pos: Vec, - ) -> Result> { + ) -> Result> { if let Ok(any) = any.to_i64() { - return Ok(serde_json::Value::Number(serde_json::Number::from(any))); - } - if let Ok(any) = any.to_u64() { - return Ok(serde_json::Value::Number(serde_json::Number::from(any))); + return Ok(BamlValue::Int(any)); } return Err(vec![SerializationError { position: field_pos, - message: format!("failed to convert {:?} to i64 or u64", any), + message: format!("failed to convert {:?} to i64", any), }]); } - fn to_float( - &self, - any: Float, - field_pos: Vec, - ) -> Result> { - let Some(as_json) = serde_json::Number::from_f64(any.to_f64()) else { - return Err(vec![SerializationError { - position: field_pos, - message: format!("failed to convert {:?} to float", any), - }]); - }; - return Ok(serde_json::Value::Number(as_json)); + fn to_float(&self, any: Float, _: Vec) -> Result> { + return Ok(BamlValue::Float(any.to_f64())); } fn to_string( @@ -198,7 +214,7 @@ impl<'rb> RubyToJson<'rb> { &self, any: RArray, field_pos: Vec, - ) -> Result> { + ) -> Result> { let mut errs = vec![]; let mut arr = vec![]; @@ -225,7 +241,7 @@ impl<'rb> RubyToJson<'rb> { return Err(errs); } - return Ok(serde_json::Value::Array(arr)); + return Ok(BamlValue::List(arr)); } fn hash_key_to_string( @@ -270,11 +286,11 @@ impl<'rb> RubyToJson<'rb> { &self, any: RHash, field_pos: Vec, - ) -> Result, Vec> { + ) -> Result, Vec> { use magnus::r_hash::ForEach; let mut errs = vec![]; - let mut map = serde_json::Map::new(); + let mut map = BamlMap::new(); if any .foreach(|k: Value, v: Value| { let k = match self.hash_key_to_string(k, field_pos.clone()) { @@ -315,7 +331,7 @@ impl<'rb> RubyToJson<'rb> { &self, any: Value, field_pos: Vec, - ) -> Result> { + ) -> Result> { // https://ruby-doc.org/3.0.4/Module.html#method-i-instance_methods let fields = match any .class() @@ -377,7 +393,7 @@ impl<'rb> RubyToJson<'rb> { }; let mut errs = vec![]; - let mut map = serde_json::Map::new(); + let mut map = BamlMap::new(); for field in fields.as_slice() { let mut field_pos = field_pos.clone(); field_pos.push(field.clone()); @@ -406,7 +422,14 @@ impl<'rb> RubyToJson<'rb> { return Err(errs); } - Ok(serde_json::Value::Object(map)) + let fully_qualified_class_name = unsafe { any.class().name() }.into_owned(); + let class_name = match fully_qualified_class_name.rsplit_once("::") { + Some((_, class_name)) => class_name.to_string(), + None => fully_qualified_class_name, + }; + Ok(BamlValue::Class(class_name, map)) + + //Ok(BamlValue::Map(map)) } // This codepath is not used right now - it was implemented as a proof-of-concept @@ -417,7 +440,7 @@ impl<'rb> RubyToJson<'rb> { &self, any: Result, field_pos: Vec, - ) -> Result> { + ) -> Result> { let any = match any { Ok(any) => any, Err(e) => { @@ -429,9 +452,7 @@ impl<'rb> RubyToJson<'rb> { }; if let Some(any) = RHash::from_value(any) { - return self - .hash_to_map(any, field_pos) - .map(serde_json::Value::Object); + return self.hash_to_map(any, field_pos).map(BamlValue::Map); } return Err(vec![SerializationError { @@ -447,7 +468,7 @@ impl<'rb> RubyToJson<'rb> { &self, any: Value, field_pos: Vec, - ) -> Result> { + ) -> Result> { match any.check_funcall("serialize", ()) { None => { return Err(vec![SerializationError { diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/ruby_types.rs b/engine/language_client_ruby/ext/ruby_ffi/src/ruby_types.rs deleted file mode 100644 index 7307c1fa4..000000000 --- a/engine/language_client_ruby/ext/ruby_ffi/src/ruby_types.rs +++ /dev/null @@ -1,61 +0,0 @@ -use baml_types::BamlValue; -use magnus::{ - class, exception::runtime_error, method, prelude::*, value::Value, Error, RModule, Ruby, -}; - -use crate::Result; - -#[magnus::wrap(class = "Baml::Ffi::FunctionResult", free_immediately, size)] -pub struct FunctionResult { - inner: baml_runtime::FunctionResult, -} - -impl FunctionResult { - pub fn new(inner: baml_runtime::FunctionResult) -> Self { - Self { inner } - } - - pub fn to_s(&self) -> String { - format!("{}", self.inner) - } - - pub fn raw(&self) -> Result { - match self.inner.content() { - Ok(content) => Ok(content.to_string()), - Err(e) => Err(Error::new( - runtime_error(), - format!("No LLM response: {}", self.inner), - )), - } - } - - pub fn parsed(&self) -> Result { - match self.inner.parsed_content() { - Ok(parsed) => serde_magnus::serialize(&BamlValue::from(parsed)), - Err(e) => Err(Error::new( - //rb.exception_runtime_error(), - runtime_error(), - format!("Failed to parse LLM response: {}", self.inner), - )), - } - } - - /// For usage in magnus::init - /// - /// TODO: use traits and macros to implement this - pub fn define_in_ruby(module: &RModule) -> Result<()> { - let cls = module.define_class("FunctionResult", class::object())?; - - cls.define_method("to_s", method!(FunctionResult::to_s, 0))?; - cls.define_method("raw", method!(FunctionResult::raw, 0))?; - cls.define_method("parsed", method!(FunctionResult::parsed, 0))?; - - Ok(()) - } -} - -pub fn define_types(rmod: &RModule) -> Result<()> { - FunctionResult::define_in_ruby(rmod)?; - - Ok(()) -} diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/runtime_ctx_manager.rs b/engine/language_client_ruby/ext/ruby_ffi/src/runtime_ctx_manager.rs new file mode 100644 index 000000000..18d816e43 --- /dev/null +++ b/engine/language_client_ruby/ext/ruby_ffi/src/runtime_ctx_manager.rs @@ -0,0 +1,21 @@ +use baml_types::BamlValue; +use magnus::{ + class, exception::runtime_error, method, prelude::*, value::Value, Error, RModule, Ruby, +}; + +use crate::Result; + +#[magnus::wrap(class = "Baml::Ffi::RuntimeContextManager", free_immediately, size)] +pub struct RuntimeContextManager { + pub inner: baml_runtime::RuntimeContextManager, +} +impl RuntimeContextManager { + pub fn define_in_ruby(module: &RModule) -> Result<()> { + module.define_class("RuntimeContextManager", class::object())?; + + //cls.define_method("upsert_tags", method!(RuntimeContextManager::upsert_tags, 1))?; + //cls.define_method("deep_clone", method!(RuntimeContextManager::deep_clone, 0))?; + + Ok(()) + } +} diff --git a/engine/language_client_ruby/ext/ruby_ffi/src/tokio_demo.rs b/engine/language_client_ruby/ext/ruby_ffi/src/tokio_demo.rs deleted file mode 100644 index 5be1a2e83..000000000 --- a/engine/language_client_ruby/ext/ruby_ffi/src/tokio_demo.rs +++ /dev/null @@ -1,103 +0,0 @@ -use magnus::{ - class, exception::runtime_error, function, method, prelude::*, Error, IntoValue, RModule, -}; -#[cfg(ruby_have_ruby_fiber_scheduler_h)] -use rb_sys::bindings::uncategorized::{ - rb_fiber_current, rb_fiber_scheduler_block, rb_fiber_scheduler_current, rb_fiber_scheduler_get, - rb_fiber_scheduler_kernel_sleep, rb_fiber_scheduler_unblock, -}; -use tokio::time::{sleep, Duration}; - -use crate::Result; - -async fn async_fn() -> String { - let duration = Duration::from_secs(2); - println!("async-BEGIN- sleeping for {duration:#?}"); - sleep(duration).await; - println!("async-END- slept for {duration:#?}"); - "async-retval".to_string() -} - -#[magnus::wrap(class = "Baml::Ffi::TokioDemo", free_immediately, size)] -/// For testing how to implement tokio in a Ruby extension -pub struct TokioDemo { - t: tokio::runtime::Runtime, -} - -impl TokioDemo { - fn new() -> Result { - let Ok(tokio_runtime) = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - else { - return Err(Error::new(runtime_error(), "Failed to start tokio runtime")); - }; - - Ok(Self { t: tokio_runtime }) - } - - #[cfg(ruby_have_ruby_fiber_scheduler_h)] - fn does_this_yield(&self) { - let rb_qnil = rb_sys::special_consts::Qnil; - println!("build2 qnil {}", Into::::into(rb_qnil)); - let rb_scheduler = unsafe { rb_fiber_scheduler_get() }; - println!("current scheduler {}", Into::::into(rb_scheduler)); - let rb_curr_fiber = unsafe { rb_fiber_current() }; - println!( - " fiber={} going to sleep", - Into::::into(rb_curr_fiber) - ); - unsafe { - let rb_duration = magnus::Integer::from_i64(10).into_value(); - //rb_fiber_scheduler_kernel_sleep(rb_scheduler, rb_duration.as_raw()); - } - let fut = self.t.spawn(async move { - async_fn().await; - println!(" fiber={} done sleeping, pls wake up", rb_curr_fiber); - unsafe { - let rb_qnil = rb_sys::special_consts::Qnil; - if rb_scheduler != Into::::into(rb_qnil) { - rb_fiber_scheduler_unblock(rb_scheduler, rb_qnil.into(), rb_curr_fiber); - } - } - }); - println!( - " fiber={} signalling that we're going to block", - Into::::into(rb_curr_fiber) - ); - unsafe { - if rb_scheduler != Into::::into(rb_qnil) { - rb_fiber_scheduler_block( - rb_scheduler, - rb_qnil.into(), - // In theory, according to rb_fiber_scheduler_make_timeout, qnil blocks indefinitely - /*timeout:*/ - rb_qnil.into(), - ); - } - } - println!( - " fiber={} blocking until woken up", - Into::::into(rb_curr_fiber) - ); - self.t.block_on(fut); - } - - fn tokio_test(&self) { - let f0 = self.t.spawn(async_fn()); - let f1 = self.t.spawn(async_fn()); - let f2 = self.t.spawn(async_fn()); - } - - /// For usage in magnus::init - /// - /// TODO: use traits and macros to implement this - pub fn define_in_ruby(module: &RModule) -> Result<()> { - let tokio_demo = module.define_class("TokioDemo", class::object())?; - tokio_demo.define_singleton_method("new", function!(TokioDemo::new, 0))?; - #[cfg(ruby_have_ruby_fiber_scheduler_h)] - tokio_demo.define_method("does_this_yield", method!(TokioDemo::does_this_yield, 0))?; - - Ok(()) - } -} diff --git a/engine/language_client_ruby/lib/baml.rb b/engine/language_client_ruby/lib/baml.rb index 32cef43f1..3d380610c 100644 --- a/engine/language_client_ruby/lib/baml.rb +++ b/engine/language_client_ruby/lib/baml.rb @@ -4,8 +4,12 @@ rescue LoadError require_relative "baml/ruby_ffi" end +require_relative "stream" module Baml + # TODO: implement image support + class Image; end + # Dynamically + idempotently define Baml::TypeConverter # NB: this does not respect raise_coercion_error = false def self.convert_to(type) diff --git a/engine/language_client_ruby/lib/stream.rb b/engine/language_client_ruby/lib/stream.rb new file mode 100644 index 000000000..caea9a262 --- /dev/null +++ b/engine/language_client_ruby/lib/stream.rb @@ -0,0 +1,70 @@ +require "sorbet-runtime" + +module Baml + # class BamlStream + # include Enumerable + + # def initialize(raw_stream:) + # @raw_stream = raw_stream + # end + + # def each(&block) + # @raw_stream.each do |raw_msg| + # yield Message.from(raw_msg) + # end + # end + # end + + class BamlStream + extend T::Sig + extend T::Generic + + include Enumerable + + PartialType = type_member + FinalType = type_member + + def initialize( + ffi_stream:, + ctx_manager: + ) + @ffi_stream = ffi_stream + @ctx_manager = ctx_manager + + @final_response = nil + end + + # Calls the given block once for each event in the stream, where event is a parsed + # partial response. Returns `self` to enable chaining `.get_final_response`. + # + # Must be called with a block. + # + # @yieldparam [PartialType] event the parsed partial response + # @return [BamlStream] self + sig { params(block: T.proc.params(event: PartialType).void).returns(BamlStream)} + def each(&block) + # Implementing this and include-ing Enumerable allows users to treat this as a Ruby + # collection: https://ruby-doc.org/3.1.6/Enumerable.html#module-Enumerable-label-Usage + if @final_response == nil + @final_response = @ffi_stream.done(@ctx_manager) do |event| + block.call event.parsed_using_types(Baml::PartialTypes) + end + end + + self + end + + + # Gets the final response from the stream. + # + # @return [FinalType] the parsed final response + sig {returns(FinalType)} + def get_final_response + if @final_response == nil + @final_response = @ffi_stream.done(@ctx_manager) + end + + @final_response.parsed_using_types(Baml::Types) + end + end +end \ No newline at end of file diff --git a/engine/language_client_ruby/test/async.rb b/engine/language_client_ruby/test/async.rb deleted file mode 100644 index d0fb96b7c..000000000 --- a/engine/language_client_ruby/test/async.rb +++ /dev/null @@ -1,29 +0,0 @@ -require "async" -require_relative "../lib/baml" - -start = Time.now - -t = Baml::TokioDemo.new - -puts "BEGIN sync sleep" -t.does_this_yield -t.does_this_yield -puts "END sync sleep" -puts - -puts "BEGIN async sleep" -Async do |task| - - Fiber.schedule do - t.does_this_yield - end - Fiber.schedule do - t.does_this_yield - end - #task.async do - # t.does_this_yield - #end -end -puts "END async sleep" - -puts "Duration: #{Time.now - start}" diff --git a/engine/language_client_ruby/test/async_test.rb b/engine/language_client_ruby/test/async_test.rb new file mode 100644 index 000000000..597278e1a --- /dev/null +++ b/engine/language_client_ruby/test/async_test.rb @@ -0,0 +1,37 @@ +require "async" +require_relative "../lib/baml" + +start = Time.now + +#t = Baml::Ffi::TokioDemo.new + +#puts "BEGIN sync sleep" +#t.does_this_yield +#t.does_this_yield +#puts "END sync sleep" +#puts + +puts "BEGIN async sleep" +#Async do |task| +# +# Fiber.schedule do +# t.does_this_yield +# end +# Fiber.schedule do +# t.does_this_yield +# end +# Fiber.schedule do +# t.does_this_yield +# end +# Fiber.schedule do +# t.does_this_yield +# end +# #task.async do +# # t.does_this_yield +# #end +#end +puts "END async sleep" + +#t.shutdown + +puts "Duration: #{Time.now - start}" diff --git a/engine/language_client_ruby/test/bench.rb b/engine/language_client_ruby/test/bench.rb deleted file mode 100644 index 6bc70ca48..000000000 --- a/engine/language_client_ruby/test/bench.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "benchmark" -require_relative "../lib/baml" - -n = 1_000_000 -Benchmark.bmbm do |x| - x.report("empty") do - n.times {"".blank?} - end - - x.report("blank") do - n.times {" ".blank?} - end - - x.report("present") do - n.times {"x".blank?} - end - - x.report("lots of spaces blank") do - n.times {" ".blank?} - end - - x.report("lots of spaces present") do - n.times {" x".blank?} - end - - x.report("blank US-ASCII") do - s = " ".encode("US-ASCII") - n.times {s.blank?} - end - - x.report("blank non-utf-8") do - s = " ".encode("UTF-16LE") - n.times {s.blank?} - end -end diff --git a/engine/language_client_ruby/test/runtime_test.rb b/engine/language_client_ruby/test/runtime_test.rb new file mode 100644 index 000000000..343595a2e --- /dev/null +++ b/engine/language_client_ruby/test/runtime_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative "../lib/baml" + +require 'minitest/autorun' +require 'minitest/reporters' + +describe "runtime sanity check" do + it "can build runtime" do + baml = Baml::Ffi::BamlRuntime.from_directory("/Users/sam/baml/integ-tests/baml_src", {}) + # assert_equal(baml.always_error("input"), "0.1.0") + + end +end + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new diff --git a/engine/language_client_ruby/test/sorbet.rb b/engine/language_client_ruby/test/sorbet.rb deleted file mode 100644 index 9b59053b4..000000000 --- a/engine/language_client_ruby/test/sorbet.rb +++ /dev/null @@ -1,49 +0,0 @@ -require_relative "../lib/baml" -require "sorbet-runtime" -require "pp" - -class Color < T::Enum - enums do - RED = new('RED') - BLUE = new - end -end - -class Foo < T::Struct - const :foo, Integer - #def to_hash - # {:foo => self.foo} - #end -end -class Bar < T::Struct - const :bar, String - #def to_hash - # {:bar => self.bar} - #end -end - -class Top < T::Struct - const :foo_or_bar, T.any(Foo, Bar) - const :color, Color - - #def to_hash - # { - # :foo_or_bar => self.foo_or_bar, - # :foo_or_bar2 => self.foo_or_bar, - # "foo" => Foo.new(foo: 23), - # :bar => Bar.new(bar: "hello"), - # :color => Color::RED, - # } - #end -end - -foo_top = Top.new( - foo_or_bar: Foo.new(foo: 12), - color: Color::RED - ) - -#puts foo_top.to_hash - -pp Baml::Ffi::roundtrip(foo_top) -#puts "instance methods" -#puts foo_top.class.instance_methods(false) \ No newline at end of file diff --git a/engine/language_client_ruby/test/sorbet_test.rb b/engine/language_client_ruby/test/sorbet_test.rb new file mode 100644 index 000000000..ecc8c54ba --- /dev/null +++ b/engine/language_client_ruby/test/sorbet_test.rb @@ -0,0 +1,99 @@ +require 'minitest/autorun' +require 'minitest/reporters' +require "sorbet-runtime" +require "pp" + + +class Color < T::Enum + enums do + RED = new('rojo') + BLUE = new('azul') + end +end + +module Identifiable + def identify + "i am a #{self.name}" + end +end + +class Vehicle + include Identifiable + + def name + "vehicle" + end +end + +module Baml + module Serializable + def serialize + Baml::Ffi::serialize(Baml::Types, self) + end + end + + module Types + class Foo < T::Struct + prepend Baml::Serializable + + const :foo, Integer + end + + class Bar < T::Struct + const :bar, String + end + + class Top < T::Struct + const :foo_or_bar, T.any(Foo, Bar) + const :color, Color + end + end +end + + +describe "learning ruby and sorbet" do + it "defines an enum without warnings" do + assert_equal(Color::RED.serialize, "rojo") + assert_equal(Color.deserialize("rojo"), Color::RED) + end + + it "uses include correctly" do + v = Vehicle.new + assert_equal(v.identify, "i am a vehicle") + end + + it "serializes a basic struct without warnings" do + foo = Baml::Types::Foo.new(foo: 1) + assert_equal(foo.serialize.foo, 1) + end + + it "forwards each correctly" do + class FakeStream + include Enumerable + + def initialize + @data = [1, 2, 3] + @final = nil + end + + def each(&block) + @data.each(&block) + @final = 'final' + end + + def final + @final + end + end + + f = FakeStream.new + + assert_nil(f.final) + f.each_with_index do |e, i| + assert_equal(e, i + 1) + end + assert_equal(f.final, 'final') + end +end + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new diff --git a/integ-tests/baml_src/main.baml b/integ-tests/baml_src/main.baml index 02a0c72b9..ae4d603bb 100644 --- a/integ-tests/baml_src/main.baml +++ b/integ-tests/baml_src/main.baml @@ -7,3 +7,8 @@ generator lang_typescript { output_type typescript output_dir "../typescript" } + +generator lang_ruby { + output_type ruby + output_dir "../ruby" +} diff --git a/integ-tests/baml_src/test-files/functions/output/unions.baml b/integ-tests/baml_src/test-files/functions/output/unions.baml index 4f0f00858..213999d53 100644 --- a/integ-tests/baml_src/test-files/functions/output/unions.baml +++ b/integ-tests/baml_src/test-files/functions/output/unions.baml @@ -1,7 +1,7 @@ class UnionTest_ReturnType { prop1 string | bool prop2 (float | bool)[] - prop3 (float[] | bool[]) + prop3 (bool[] | int[]) } function UnionTest_Function(input: string | bool) -> UnionTest_ReturnType { diff --git a/integ-tests/baml_src/test-files/testing_pipeline/resume.baml b/integ-tests/baml_src/test-files/testing_pipeline/resume.baml index e73e5ec61..9e48a5fae 100644 --- a/integ-tests/baml_src/test-files/testing_pipeline/resume.baml +++ b/integ-tests/baml_src/test-files/testing_pipeline/resume.baml @@ -22,8 +22,8 @@ template_string AddRole(foo: string) #" {{ _.role('user') }} "# -function ExtractResume(resume: string, img: image) -> Resume { - client GPT4 +function ExtractResume(resume: string, img: image?) -> Resume { + client GPT35 prompt #" {{ AddRole("Software Engineer") }} @@ -50,7 +50,7 @@ test sam_resume { } resume #" Sam Lijin - he/him | jobs@sxlijin.com | sxlijin.github.io | sxlijin | sxlijin + he/him | jobs@sxlijin.com | sxlijin.github.io | 111-222-3333 | sxlijin | sxlijin Experience Trunk diff --git a/integ-tests/python/README.md b/integ-tests/python/README.md index fca6d1eca..9a89befd3 100644 --- a/integ-tests/python/README.md +++ b/integ-tests/python/README.md @@ -1,8 +1,10 @@ Run the tests like this: -infisical run --env=devasen -- poetry run pytest app/test_functions.py +``` +infisical run --env=dev -- poetry run pytest app/test_functions.py env -u CONDA_PREFIX poetry run maturin develop --manifest-path ../../engine/language_client_python/Cargo.toml && poetry run baml-cli generate --from ../baml_src BAML_LOG=baml_events infisical run --env=dev -- poetry run pytest app/test_functions.py -s +``` diff --git a/integ-tests/python/app/test_functions.py b/integ-tests/python/app/test_functions.py index 04fa1e981..afb26a647 100644 --- a/integ-tests/python/app/test_functions.py +++ b/integ-tests/python/app/test_functions.py @@ -8,10 +8,6 @@ import datetime -class MyCustomClass(NamedArgsSingleClass): - date: datetime.datetime - - @pytest.mark.asyncio async def test_should_work_for_all_inputs(): res = await b.TestFnNamedArgsSingleBool(True) @@ -55,8 +51,12 @@ async def test_should_work_for_all_inputs(): assert "3566" in res +class MyCustomClass(NamedArgsSingleClass): + date: datetime.datetime + + @pytest.mark.asyncio -async def test_custom_types(): +async def accepts_subclass_of_baml_type(): print("calling with class") res = await b.TestFnNamedArgsSingleClass( myArg=MyCustomClass( diff --git a/integ-tests/python/baml_client/client.py b/integ-tests/python/baml_client/client.py index 1a2465fdd..0266e1a69 100644 --- a/integ-tests/python/baml_client/client.py +++ b/integ-tests/python/baml_client/client.py @@ -277,7 +277,7 @@ async def ExtractPeople( async def ExtractResume( self, - resume: str,img: baml_py.Image, + resume: str,img: Optional[baml_py.Image], baml_options: BamlCallOptions = {}, ) -> types.Resume: __tb__ = baml_options.get("tb", None) @@ -1534,7 +1534,7 @@ def ExtractPeople( def ExtractResume( self, - resume: str,img: baml_py.Image, + resume: str,img: Optional[baml_py.Image], baml_options: BamlCallOptions = {}, ) -> baml_py.BamlStream[partial_types.Resume, types.Resume]: __tb__ = baml_options.get("tb", None) diff --git a/integ-tests/python/baml_client/inlinedbaml.py b/integ-tests/python/baml_client/inlinedbaml.py index 13161b458..bba85b3ef 100644 --- a/integ-tests/python/baml_client/inlinedbaml.py +++ b/integ-tests/python/baml_client/inlinedbaml.py @@ -23,7 +23,7 @@ "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/images/image.baml": "function DescribeImage(img: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 5 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}", "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", + "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\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", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", @@ -51,7 +51,7 @@ "test-files/functions/output/optional-class.baml": "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/optional.baml": "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/string-list.baml": "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", - "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (float[] | bool[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", @@ -59,7 +59,7 @@ "test-files/strategies/fallback.baml": "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", "test-files/strategies/roundrobin.baml": "", - "test-files/testing_pipeline/resume.baml": "class Resume {\n name string\n email string\n phone string\n experience Education[]\n education string[]\n skills string[]\n}\n\nclass Education {\n institution string\n location string\n degree string\n major string[]\n graduation_date string?\n}\n\ntemplate_string AddRole(foo: string) #\"\n {{ _.role('system')}}\n You are a {{ foo }}. be nice\n\n {{ _.role('user') }}\n\"#\n\nfunction ExtractResume(resume: string, img: image) -> Resume {\n client GPT4\n prompt #\"\n {{ AddRole(\"Software Engineer\") }}\n\n Extract data:\n \n\n <<<<\n {{ resume }}\n <<<<\n\n {% if img %}\n {{img}}\n {% endif %}\n\n {{ ctx.output_format }}\n \"#\n}\n\ntest sam_resume {\n functions [ExtractResume]\n input {\n img {\n url \"https://avatars.githubusercontent.com/u/1016595?v=4\"\n }\n resume #\"\n Sam Lijin\n he/him | jobs@sxlijin.com | sxlijin.github.io | sxlijin | sxlijin\n\n Experience\n Trunk\n | July 2021 - current\n Trunk Check | Senior Software Engineer | Services TL, Mar 2023 - current | IC, July 2021 - Feb 2023\n Proposed, designed, and led a team of 3 to build a web experience for Check (both a web-only onboarding flow and SaaS offerings)\n Proposed and built vulnerability scanning into Check, enabling it to compete with security products such as Snyk\n Helped grow Check from <1K users to 90K+ users by focusing on product-led growth\n Google | Sept 2017 - June 2021\n User Identity SRE | Senior Software Engineer | IC, Mar 2021 - June 2021\n Designed an incremental key rotation system to limit the global outage risk to Google SSO\n Discovered and severed an undocumented Gmail serving dependency on Identity-internal systems\n Cloud Firestore | Senior Software Engineer | EngProd TL, Aug 2019 - Feb 2021 | IC, Sept 2017 - July 2019\n Metadata TTL system: backlog of XX trillion records, sustained 1M ops/sec, peaking at 3M ops/sec\n\n Designed and implemented a logging system with novel observability and privacy requirements\n Designed and implemented Jepsen-style testing to validate correctness guarantees\n Datastore Migration: zero downtime, xM RPS and xxPB of data over xM customers and 36 datacenters\n\n Designed composite index migration, queue processing migration, progressive rollout, fast rollback, and disk stockout mitigations; implemented transaction log replay, state transitions, and dark launch process\n Designed and implemented end-to-end correctness and performance testing\n Velocity improvements for 60-eng org\n\n Proposed and implemented automated rollbacks: got us out of a 3-month release freeze and prevented 5 outages over the next 6 months\n Proposed and implemented new development and release environments spanning 30+ microservices\n Incident response for API proxy rollback affecting every Google Cloud service\n\n Google App Engine Memcache | Software Engineer | EngProd TL, Apr 2019 - July 2019\n Proposed and led execution of test coverage improvement strategy for a new control plane: reduced rollbacks and ensured strong consistency of a distributed cache serving xxM QPS\n Designed and implemented automated performance regression testing for two critical serving paths\n Used to validate Google-wide rollout of AMD CPUs, by proving a 50p latency delta of <10µs\n Implemented on shared Borg (i.e. vulnerable to noisy neighbors) with <12% variance\n Miscellaneous | Sept 2017 - June 2021\n Redesigned the Noogler training on Google-internal storage technologies & trained 2500+ Nooglers\n Landed multiple google3-wide refactorings, each spanning xxK files (e.g. SWIG to CLIF)\n Education\n Vanderbilt University (Nashville, TN) | May 2017 | B.S. in Computer Science, Mathematics, and Political Science\n\n Stuyvesant HS (New York, NY) | 2013\n\n Skills\n C++, Java, Typescript, Javascript, Python, Bash; light experience with Rust, Golang, Scheme\n gRPC, Bazel, React, Linux\n Hobbies: climbing, skiing, photography\n \"#\n }\n}\n\ntest vaibhav_resume {\n functions [ExtractResume]\n input {\n resume #\"\n Vaibhav Gupta\n linkedin/vaigup\n (972) 400-5279\n vaibhavtheory@gmail.com\n EXPERIENCE\n Google,\n Software Engineer\n Dec 2018-Present\n Seattle, WA\n •\n Augmented Reality,\n Depth Team\n •\n Technical Lead for on-device optimizations\n •\n Optimized and designed front\n facing depth algorithm\n on Pixel 4\n •\n Focus: C++ and SIMD on custom silicon\n \n \n EDUCATION\n University of Texas at Austin\n Aug 2012-May 2015\n Bachelors of Engineering, Integrated Circuits\n Bachelors of Computer Science\n \"#\n }\n}", + "test-files/testing_pipeline/resume.baml": "class Resume {\n name string\n email string\n phone string\n experience Education[]\n education string[]\n skills string[]\n}\n\nclass Education {\n institution string\n location string\n degree string\n major string[]\n graduation_date string?\n}\n\ntemplate_string AddRole(foo: string) #\"\n {{ _.role('system')}}\n You are a {{ foo }}. be nice\n\n {{ _.role('user') }}\n\"#\n\nfunction ExtractResume(resume: string, img: image?) -> Resume {\n client GPT35\n prompt #\"\n {{ AddRole(\"Software Engineer\") }}\n\n Extract data:\n \n\n <<<<\n {{ resume }}\n <<<<\n\n {% if img %}\n {{img}}\n {% endif %}\n\n {{ ctx.output_format }}\n \"#\n}\n\ntest sam_resume {\n functions [ExtractResume]\n input {\n img {\n url \"https://avatars.githubusercontent.com/u/1016595?v=4\"\n }\n resume #\"\n Sam Lijin\n he/him | jobs@sxlijin.com | sxlijin.github.io | 111-222-3333 | sxlijin | sxlijin\n\n Experience\n Trunk\n | July 2021 - current\n Trunk Check | Senior Software Engineer | Services TL, Mar 2023 - current | IC, July 2021 - Feb 2023\n Proposed, designed, and led a team of 3 to build a web experience for Check (both a web-only onboarding flow and SaaS offerings)\n Proposed and built vulnerability scanning into Check, enabling it to compete with security products such as Snyk\n Helped grow Check from <1K users to 90K+ users by focusing on product-led growth\n Google | Sept 2017 - June 2021\n User Identity SRE | Senior Software Engineer | IC, Mar 2021 - June 2021\n Designed an incremental key rotation system to limit the global outage risk to Google SSO\n Discovered and severed an undocumented Gmail serving dependency on Identity-internal systems\n Cloud Firestore | Senior Software Engineer | EngProd TL, Aug 2019 - Feb 2021 | IC, Sept 2017 - July 2019\n Metadata TTL system: backlog of XX trillion records, sustained 1M ops/sec, peaking at 3M ops/sec\n\n Designed and implemented a logging system with novel observability and privacy requirements\n Designed and implemented Jepsen-style testing to validate correctness guarantees\n Datastore Migration: zero downtime, xM RPS and xxPB of data over xM customers and 36 datacenters\n\n Designed composite index migration, queue processing migration, progressive rollout, fast rollback, and disk stockout mitigations; implemented transaction log replay, state transitions, and dark launch process\n Designed and implemented end-to-end correctness and performance testing\n Velocity improvements for 60-eng org\n\n Proposed and implemented automated rollbacks: got us out of a 3-month release freeze and prevented 5 outages over the next 6 months\n Proposed and implemented new development and release environments spanning 30+ microservices\n Incident response for API proxy rollback affecting every Google Cloud service\n\n Google App Engine Memcache | Software Engineer | EngProd TL, Apr 2019 - July 2019\n Proposed and led execution of test coverage improvement strategy for a new control plane: reduced rollbacks and ensured strong consistency of a distributed cache serving xxM QPS\n Designed and implemented automated performance regression testing for two critical serving paths\n Used to validate Google-wide rollout of AMD CPUs, by proving a 50p latency delta of <10µs\n Implemented on shared Borg (i.e. vulnerable to noisy neighbors) with <12% variance\n Miscellaneous | Sept 2017 - June 2021\n Redesigned the Noogler training on Google-internal storage technologies & trained 2500+ Nooglers\n Landed multiple google3-wide refactorings, each spanning xxK files (e.g. SWIG to CLIF)\n Education\n Vanderbilt University (Nashville, TN) | May 2017 | B.S. in Computer Science, Mathematics, and Political Science\n\n Stuyvesant HS (New York, NY) | 2013\n\n Skills\n C++, Java, Typescript, Javascript, Python, Bash; light experience with Rust, Golang, Scheme\n gRPC, Bazel, React, Linux\n Hobbies: climbing, skiing, photography\n \"#\n }\n}\n\ntest vaibhav_resume {\n functions [ExtractResume]\n input {\n resume #\"\n Vaibhav Gupta\n linkedin/vaigup\n (972) 400-5279\n vaibhavtheory@gmail.com\n EXPERIENCE\n Google,\n Software Engineer\n Dec 2018-Present\n Seattle, WA\n •\n Augmented Reality,\n Depth Team\n •\n Technical Lead for on-device optimizations\n •\n Optimized and designed front\n facing depth algorithm\n on Pixel 4\n •\n Focus: C++ and SIMD on custom silicon\n \n \n EDUCATION\n University of Texas at Austin\n Aug 2012-May 2015\n Bachelors of Engineering, Integrated Circuits\n Bachelors of Computer Science\n \"#\n }\n}", } def get_baml_files(): diff --git a/integ-tests/python/baml_client/partial_types.py b/integ-tests/python/baml_client/partial_types.py index 64d161d27..d965ab82a 100644 --- a/integ-tests/python/baml_client/partial_types.py +++ b/integ-tests/python/baml_client/partial_types.py @@ -199,7 +199,7 @@ class UnionTest_ReturnType(BaseModel): prop1: Optional[Union[Optional[str], Optional[bool]]] = None prop2: List[Optional[Union[Optional[float], Optional[bool]]]] - prop3: Optional[Union[List[Optional[float]], List[Optional[bool]]]] = None + prop3: Optional[Union[List[Optional[bool]], List[Optional[int]]]] = None class WithReasoning(BaseModel): diff --git a/integ-tests/python/baml_client/types.py b/integ-tests/python/baml_client/types.py index 59ea7aaf6..1be2600cc 100644 --- a/integ-tests/python/baml_client/types.py +++ b/integ-tests/python/baml_client/types.py @@ -290,7 +290,7 @@ class UnionTest_ReturnType(BaseModel): prop1: Union[str, bool] prop2: List[Union[float, bool]] - prop3: Union[List[float], List[bool]] + prop3: Union[List[bool], List[int]] class WithReasoning(BaseModel): diff --git a/integ-tests/ruby/Gemfile b/integ-tests/ruby/Gemfile new file mode 100644 index 000000000..c24e9aac4 --- /dev/null +++ b/integ-tests/ruby/Gemfile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "rake", "~> 13.0" +gem "sorbet-runtime" +gem "sorbet-struct-comparable" + +group :test do + gem "minitest-reporters" +end diff --git a/integ-tests/ruby/Gemfile.lock b/integ-tests/ruby/Gemfile.lock new file mode 100644 index 000000000..f7d0cacd6 --- /dev/null +++ b/integ-tests/ruby/Gemfile.lock @@ -0,0 +1,36 @@ +GEM + remote: https://rubygems.org/ + specs: + ansi (1.5.0) + builder (3.2.4) + minitest (5.23.1) + minitest-reporters (1.6.1) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + polyfill (1.9.0) + rake (13.2.1) + ruby-progressbar (1.13.0) + safe_type (1.1.1) + sorbet-coerce (0.7.0) + polyfill (~> 1.8) + safe_type (~> 1.1, >= 1.1.1) + sorbet-runtime (>= 0.4.4704) + sorbet-runtime (0.5.11409) + sorbet-struct-comparable (1.3.0) + sorbet-runtime (>= 0.5) + +PLATFORMS + arm64-darwin-23 + ruby + +DEPENDENCIES + minitest-reporters + rake (~> 13.0) + sorbet-coerce + sorbet-runtime + sorbet-struct-comparable + +BUNDLED WITH + 2.5.11 diff --git a/integ-tests/ruby/README.md b/integ-tests/ruby/README.md new file mode 100644 index 000000000..35289a58f --- /dev/null +++ b/integ-tests/ruby/README.md @@ -0,0 +1,24 @@ +To install Ruby: + +``` +# this is it- Ruby is versioned by baml/.mise.toml +brew install mise +``` + + +Run the tests like this: + +``` +(cd ../../engine/language_client_ruby && mise exec -- rake compile) + +mise exec -- bundle install + +infisical run --env=test -- mise exec -- rake test +``` + +If you want to just run `bundle` or `rake` directly, add mise shims to your shell ([mise docs](https://mise.jdx.dev/getting-started.html#_2a-activate-mise)): + +``` +echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc +echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc +``` \ No newline at end of file diff --git a/integ-tests/ruby/Rakefile b/integ-tests/ruby/Rakefile new file mode 100644 index 000000000..3f97ada70 --- /dev/null +++ b/integ-tests/ruby/Rakefile @@ -0,0 +1,14 @@ +require "rake/testtask" + +task :generate do + sh "ruby", \ + "-I../../engine/language_client_ruby/lib", \ + "../../engine/language_client_ruby/exe/baml-cli", \ + "generate", "--from", "../baml_src" +end + +Rake::TestTask.new do |t| + t.libs << "../../engine/language_client_ruby/lib" + t.libs << "baml_client" + t.test_files = FileList["test_*.rb"] +end \ No newline at end of file diff --git a/integ-tests/ruby/baml_client/client.rb b/integ-tests/ruby/baml_client/client.rb index fd4ae0284..c128867f6 100644 --- a/integ-tests/ruby/baml_client/client.rb +++ b/integ-tests/ruby/baml_client/client.rb @@ -2,7 +2,7 @@ # # Welcome to Baml! To use this generated code, please run the following: # -# $ bundle add baml sorbet-runtime sorbet-coerce sorbet-struct-comparable +# $ bundle add baml sorbet-runtime sorbet-struct-comparable # ############################################################################### @@ -12,1409 +12,2179 @@ # frozen_string_literal: true # rubocop: disable # formatter:off -# typed: strict +# typed: false require "baml" require "sorbet-runtime" +require_relative "inlined" +require_relative "partial-types" require_relative "types" module Baml + @instance = nil + + def self.Client + if @instance.nil? + @instance = BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_files("baml_src", Baml::Inlined::FILE_MAP, ENV)) + end + + @instance + end class BamlClient extend T::Sig - sig { returns(UnstableBamlClient) } - attr_reader :unstable + sig { returns(BamlStreamClient) } + attr_reader :stream sig {params(runtime: Baml::Ffi::BamlRuntime).void} def initialize(runtime:) @runtime = runtime - @unstable = T.let(UnstableBamlClient.new(runtime: runtime), UnstableBamlClient) + @ctx_manager = runtime.create_context_manager() + @stream = BamlStreamClient.new(runtime: @runtime, ctx_manager: @ctx_manager) end sig {params(path: String).returns(BamlClient)} def self.from_directory(path) - BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_directory(path)) + BamlClient.new(runtime: Baml::Ffi::BamlRuntime.from_directory(path, ENV)) end sig { + params( input: String, ).returns(Baml::Types::Category) + } def ClassifyMessage( input: ) raw = @runtime.call_function( - function_name: "ClassifyMessage", - args: { + "ClassifyMessage", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::Category).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, ).returns(Baml::Types::Category) + } def ClassifyMessage2( input: ) raw = @runtime.call_function( - function_name: "ClassifyMessage2", - args: { + "ClassifyMessage2", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::Category).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, ).returns(Baml::Types::Category) + } def ClassifyMessage3( input: ) raw = @runtime.call_function( - function_name: "ClassifyMessage3", - args: { + "ClassifyMessage3", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::Category).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - img: Baml::Types::Image, + img: Baml::Image, ).returns(String) + } def DescribeImage( img: ) raw = @runtime.call_function( - function_name: "DescribeImage", - args: { + "DescribeImage", + { "img" => img, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, ).returns(String) + } def DescribeImage2( classWithImage:,img2: ) raw = @runtime.call_function( - function_name: "DescribeImage2", - args: { + "DescribeImage2", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, ).returns(String) + } def DescribeImage3( classWithImage:,img2: ) raw = @runtime.call_function( - function_name: "DescribeImage3", - args: { + "DescribeImage3", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, ).returns(String) + } def DescribeImage4( classWithImage:,img2: ) raw = @runtime.call_function( - function_name: "DescribeImage4", - args: { + "DescribeImage4", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + + params( + input: Baml::Types::DynamicClassOne, + ).returns(Baml::Types::DynamicClassTwo) + + } + def DynamicFunc( + input: + ) + raw = @runtime.call_function( + "DynamicFunc", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + params( input: String, ).returns(T::Array[String]) + } def ExtractNames( input: ) raw = @runtime.call_function( - function_name: "ExtractNames", - args: { + "ExtractNames", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Array[String]).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - resume: String, + text: String, + ).returns(T::Array[Baml::Types::Person]) + + } + def ExtractPeople( + text: + ) + raw = @runtime.call_function( + "ExtractPeople", + { + "text" => text, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + resume: String,img: T.nilable(Baml::Image), ).returns(Baml::Types::Resume) + } def ExtractResume( - resume: + resume:,img: ) raw = @runtime.call_function( - function_name: "ExtractResume", - args: { - "resume" => resume, - } + "ExtractResume", + { + "resume" => resume,"img" => img, + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::Resume).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( resume: String, ).returns(Baml::Types::Resume) + } def ExtractResume2( resume: ) raw = @runtime.call_function( - function_name: "ExtractResume2", - args: { + "ExtractResume2", + { "resume" => resume, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::Resume).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(T.nilable(Baml::Types::ClassOptionalOutput2v2)) + ).returns(T.nilable(Baml::Types::ClassOptionalOutput)) + } - def FnClassOptionalOutput2_V2( + def FnClassOptionalOutput( input: ) raw = @runtime.call_function( - function_name: "FnClassOptionalOutput2_V2", - args: { + "FnClassOptionalOutput", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T.nilable(Baml::Types::ClassOptionalOutput2v2)).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(Baml::Types::TestClassWithEnum2) + ).returns(T.nilable(Baml::Types::ClassOptionalOutput2)) + } - def FnOutputClassWithEnum_V2( + def FnClassOptionalOutput2( input: ) raw = @runtime.call_function( - function_name: "FnOutputClassWithEnum_V2", - args: { + "FnClassOptionalOutput2", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::TestClassWithEnum2).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - text: String, - ).returns(Baml::Types::RaysData) + input: String, + ).returns(T::Array[Baml::Types::EnumOutput]) + } - def GetDataType( - text: + def FnEnumListOutput( + input: ) raw = @runtime.call_function( - function_name: "GetDataType", - args: { - "text" => text, - } + "FnEnumListOutput", + { + "input" => input, + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::RaysData).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - email: Baml::Types::Email, - ).returns(Baml::Types::OrderInfo) + input: String, + ).returns(Baml::Types::EnumOutput) + } - def GetOrderInfo( - email: + def FnEnumOutput( + input: ) raw = @runtime.call_function( - function_name: "GetOrderInfo", - args: { - "email" => email, - } + "FnEnumOutput", + { + "input" => input, + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::OrderInfo).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - query: String, - ).returns(Baml::Types::SearchParams) + myString: T.nilable(String), + ).returns(String) + } - def GetQuery( - query: + def FnNamedArgsSingleStringOptional( + myString: ) raw = @runtime.call_function( - function_name: "GetQuery", - args: { - "query" => query, - } + "FnNamedArgsSingleStringOptional", + { + "myString" => myString, + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::SearchParams).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(T::Array[T.nilable(Baml::Types::OptionalTest_ReturnTypev2)]) + ).returns(T::Boolean) + } - def OptionalTest_Function_V2( + def FnOutputBool( input: ) raw = @runtime.call_function( - function_name: "OptionalTest_Function_V2", - args: { + "FnOutputBool", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Array[T.nilable(Baml::Types::OptionalTest_ReturnTypev2)]).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - input: T.nilable(Baml::Types::OptionalClassv2), - ).returns(String) + input: String, + ).returns(Baml::Types::TestOutputClass) + } - def V2_FnClassOptional( + def FnOutputClass( input: ) raw = @runtime.call_function( - function_name: "V2_FnClassOptional", - args: { + "FnOutputClass", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - input: Baml::Types::ClassOptionalFieldsv2, - ).returns(String) + input: String, + ).returns(T::Array[Baml::Types::TestOutputClass]) + } - def V2_FnClassOptional2( + def FnOutputClassList( input: ) raw = @runtime.call_function( - function_name: "V2_FnClassOptional2", - args: { + "FnOutputClassList", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(T::Array[Baml::Types::EnumOutput]) + ).returns(Baml::Types::TestClassWithEnum) + } - def V2_FnEnumListOutput( + def FnOutputClassWithEnum( input: ) raw = @runtime.call_function( - function_name: "V2_FnEnumListOutput", - args: { + "FnOutputClassWithEnum", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Array[Baml::Types::EnumOutput]).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(Baml::Types::EnumOutput2) + ).returns(Baml::Types::TestOutputClassNested) + } - def V2_FnEnumOutput( + def FnOutputNestedClass( input: ) raw = @runtime.call_function( - function_name: "V2_FnEnumOutput", - args: { + "FnOutputNestedClass", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::EnumOutput2).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - myString: T.nilable(String), + input: String, + ).returns(T::Array[String]) + + } + def FnOutputStringList( + input: + ) + raw = @runtime.call_function( + "FnOutputStringList", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + input: String, + ).returns(Baml::Types::TestEnum) + + } + def FnTestAliasedEnumOutput( + input: + ) + raw = @runtime.call_function( + "FnTestAliasedEnumOutput", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + input: String, + ).returns(Baml::Types::TestClassAlias) + + } + def FnTestClassAlias( + input: + ) + raw = @runtime.call_function( + "FnTestClassAlias", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + myArg: Baml::Types::NamedArgsSingleEnum, ).returns(String) + } - def V2_FnNamedArgsSingleStringOptional( - myString: + def FnTestNamedArgsSingleEnum( + myArg: ) raw = @runtime.call_function( - function_name: "V2_FnNamedArgsSingleStringOptional", - args: { - "myString" => myString, - } + "FnTestNamedArgsSingleEnum", + { + "myArg" => myArg, + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + + params( + text: String, + ).returns(Baml::Types::RaysData) + + } + def GetDataType( + text: + ) + raw = @runtime.call_function( + "GetDataType", + { + "text" => text, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + email: Baml::Types::Email, + ).returns(Baml::Types::OrderInfo) + + } + def GetOrderInfo( + email: + ) + raw = @runtime.call_function( + "GetOrderInfo", + { + "email" => email, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + query: String, + ).returns(Baml::Types::SearchParams) + + } + def GetQuery( + query: + ) + raw = @runtime.call_function( + "GetQuery", + { + "query" => query, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + params( input: String, - ).returns(T::Boolean) + ).returns(Baml::Types::DynamicOutput) + } - def V2_FnOutputBool( + def MyFunc( input: ) raw = @runtime.call_function( - function_name: "V2_FnOutputBool", - args: { + "MyFunc", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Boolean).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(Baml::Types::TestOutputClass2) + ).returns(T::Array[T.nilable(Baml::Types::OptionalTest_ReturnType)]) + } - def V2_FnOutputClass( + def OptionalTest_Function( input: ) raw = @runtime.call_function( - function_name: "V2_FnOutputClass", - args: { + "OptionalTest_Function", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(Baml::Types::TestOutputClass2).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(T::Array[Baml::Types::TestOutputClass]) + ).returns(String) + } - def V2_FnOutputClassList( + def PromptTestClaude( input: ) raw = @runtime.call_function( - function_name: "V2_FnOutputClassList", - args: { + "PromptTestClaude", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Array[Baml::Types::TestOutputClass]).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: String, - ).returns(T::Array[String]) + ).returns(String) + } - def V2_FnOutputStringList( + def PromptTestClaudeChat( input: ) raw = @runtime.call_function( - function_name: "V2_FnOutputStringList", - args: { + "PromptTestClaudeChat", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T::Array[String]).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - input: T.nilable(String), + input: String, ).returns(String) + } - def V2_FnStringOptional( + def PromptTestClaudeChatNoSystem( input: ) raw = @runtime.call_function( - function_name: "V2_FnStringOptional", - args: { + "PromptTestClaudeChatNoSystem", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - myArg: Baml::Types::NamedArgsSingleEnum2, + input: String, ).returns(String) + } - def V2_FnTestNamedArgsSingleEnum( - myArg: + def PromptTestOpenAI( + input: ) raw = @runtime.call_function( - function_name: "V2_FnTestNamedArgsSingleEnum", - args: { - "myArg" => myArg, - } + "PromptTestOpenAI", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + input: String, + ).returns(String) + + } + def PromptTestOpenAIChat( + input: + ) + raw = @runtime.call_function( + "PromptTestOpenAIChat", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + input: String, + ).returns(String) + + } + def PromptTestOpenAIChatNoSystem( + input: + ) + raw = @runtime.call_function( + "PromptTestOpenAIChatNoSystem", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + returns(String) + + } + def TestFallbackClient( + + ) + raw = @runtime.call_function( + "TestFallbackClient", + { + + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( myBool: T::Boolean, ).returns(String) + } - def V2_TestFnNamedArgsSingleBool( + def TestFnNamedArgsSingleBool( myBool: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleBool", - args: { + "TestFnNamedArgsSingleBool", + { "myBool" => myBool, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - myArg: Baml::Types::NamedArgsSingleClass2, + myArg: Baml::Types::NamedArgsSingleClass, ).returns(String) + } - def V2_TestFnNamedArgsSingleClass( + def TestFnNamedArgsSingleClass( myArg: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleClass", - args: { + "TestFnNamedArgsSingleClass", + { "myArg" => myArg, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - myArg: T::Array[Baml::Types::NamedArgsSingleEnumList2], + myArg: T::Array[Baml::Types::NamedArgsSingleEnumList], ).returns(String) + } - def V2_TestFnNamedArgsSingleEnumList( + def TestFnNamedArgsSingleEnumList( myArg: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleEnumList", - args: { + "TestFnNamedArgsSingleEnumList", + { "myArg" => myArg, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( myFloat: Float, ).returns(String) + } - def V2_TestFnNamedArgsSingleFloat( + def TestFnNamedArgsSingleFloat( myFloat: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleFloat", - args: { + "TestFnNamedArgsSingleFloat", + { "myFloat" => myFloat, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( myInt: Integer, ).returns(String) + } - def V2_TestFnNamedArgsSingleInt( + def TestFnNamedArgsSingleInt( myInt: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleInt", - args: { + "TestFnNamedArgsSingleInt", + { "myInt" => myInt, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( myString: String, ).returns(String) + } - def V2_TestFnNamedArgsSingleString( + def TestFnNamedArgsSingleString( myString: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleString", - args: { + "TestFnNamedArgsSingleString", + { "myString" => myString, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( myStringArray: T::Array[String], ).returns(String) + } - def V2_TestFnNamedArgsSingleStringArray( + def TestFnNamedArgsSingleStringArray( myStringArray: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleStringArray", - args: { + "TestFnNamedArgsSingleStringArray", + { "myStringArray" => myStringArray, - } + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - myArg: T::Array[Baml::Types::NamedArgsSingleClassList2], + myArg: T::Array[String], ).returns(String) + } - def V2_TestFnNamedArgsSingleStringList( + def TestFnNamedArgsSingleStringList( myArg: ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleStringList", - args: { + "TestFnNamedArgsSingleStringList", + { "myArg" => myArg, - } + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + img: Baml::Image, + ).returns(String) + + } + def TestImageInput( + img: + ) + raw = @runtime.call_function( + "TestImageInput", + { + "img" => img, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + params( + myArg: Baml::Types::NamedArgsSingleClass,myArg2: Baml::Types::NamedArgsSingleClass, + ).returns(String) + + } + def TestMulticlassNamedArgs( + myArg:,myArg2: + ) + raw = @runtime.call_function( + "TestMulticlassNamedArgs", + { + "myArg" => myArg,"myArg2" => myArg2, + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( - var: String,var_with_underscores: String, + input: String, ).returns(String) + + } + def TestOllama( + input: + ) + raw = @runtime.call_function( + "TestOllama", + { + "input" => input, + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + returns(String) + + } + def TestRetryConstant( + + ) + raw = @runtime.call_function( + "TestRetryConstant", + { + + }, + @ctx_manager, + ) + (raw.parsed_using_types(Baml::Types)) + end + + sig { + + returns(String) + } - def V2_TestFnNamedArgsSyntax( - var:,var_with_underscores: + def TestRetryExponential( + ) raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSyntax", - args: { - "var" => var,"var_with_underscores" => var_with_underscores, - } + "TestRetryExponential", + { + + }, + @ctx_manager, ) - Baml::convert_to(String).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end sig { + params( input: T.any(String, T::Boolean), - ).returns(T.any(Baml::Types::UnionTest_ReturnTypev2, Baml::Types::DataType)) + ).returns(Baml::Types::UnionTest_ReturnType) + } - def V2_UnionTest_Function( + def UnionTest_Function( input: ) raw = @runtime.call_function( - function_name: "V2_UnionTest_Function", - args: { + "UnionTest_Function", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::convert_to(T.any(Baml::Types::UnionTest_ReturnTypev2, Baml::Types::DataType)).from(raw.parsed) + (raw.parsed_using_types(Baml::Types)) end end - class UnstableBamlClient + class BamlStreamClient extend T::Sig - sig {params(runtime: Baml::Ffi::BamlRuntime).void} - def initialize(runtime:) + sig {params(runtime: Baml::Ffi::BamlRuntime, ctx_manager: Baml::Ffi::RuntimeContextManager).void} + def initialize(runtime:, ctx_manager:) @runtime = runtime + @ctx_manager = ctx_manager end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::Category]) + ).returns(Baml::BamlStream[Baml::Types::Category]) } def ClassifyMessage( input: ) - raw = @runtime.call_function( - function_name: "ClassifyMessage", - args: { + raw = @runtime.stream_function( + "ClassifyMessage", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::Category].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::Category).from(raw.parsed) + Baml::BamlStream[T.nilable(Baml::Types::Category), Baml::Types::Category].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::Category]) + ).returns(Baml::BamlStream[Baml::Types::Category]) } def ClassifyMessage2( input: ) - raw = @runtime.call_function( - function_name: "ClassifyMessage2", - args: { + raw = @runtime.stream_function( + "ClassifyMessage2", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::Category].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::Category).from(raw.parsed) + Baml::BamlStream[T.nilable(Baml::Types::Category), Baml::Types::Category].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::Category]) + ).returns(Baml::BamlStream[Baml::Types::Category]) } def ClassifyMessage3( input: ) - raw = @runtime.call_function( - function_name: "ClassifyMessage3", - args: { + raw = @runtime.stream_function( + "ClassifyMessage3", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::Category].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::Category).from(raw.parsed) + Baml::BamlStream[T.nilable(Baml::Types::Category), Baml::Types::Category].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - img: Baml::Types::Image, - ).returns(Baml::Unstable::FunctionResult[String]) + img: Baml::Image, + ).returns(Baml::BamlStream[String]) } def DescribeImage( img: ) - raw = @runtime.call_function( - function_name: "DescribeImage", - args: { + raw = @runtime.stream_function( + "DescribeImage", + { "img" => img, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, - ).returns(Baml::Unstable::FunctionResult[String]) + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, + ).returns(Baml::BamlStream[String]) } def DescribeImage2( classWithImage:,img2: ) - raw = @runtime.call_function( - function_name: "DescribeImage2", - args: { + raw = @runtime.stream_function( + "DescribeImage2", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, - ).returns(Baml::Unstable::FunctionResult[String]) + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, + ).returns(Baml::BamlStream[String]) } def DescribeImage3( classWithImage:,img2: ) - raw = @runtime.call_function( - function_name: "DescribeImage3", - args: { + raw = @runtime.stream_function( + "DescribeImage3", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - classWithImage: Baml::Types::ClassWithImage,img2: Baml::Types::Image, - ).returns(Baml::Unstable::FunctionResult[String]) + classWithImage: Baml::Types::ClassWithImage,img2: Baml::Image, + ).returns(Baml::BamlStream[String]) } def DescribeImage4( classWithImage:,img2: ) - raw = @runtime.call_function( - function_name: "DescribeImage4", - args: { + raw = @runtime.stream_function( + "DescribeImage4", + { "classWithImage" => classWithImage,"img2" => img2, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: Baml::Types::DynamicClassOne, + ).returns(Baml::BamlStream[Baml::Types::DynamicClassTwo]) + } + def DynamicFunc( + input: + ) + raw = @runtime.stream_function( + "DynamicFunc", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[Baml::PartialTypes::DynamicClassTwo, Baml::Types::DynamicClassTwo].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Array[String]]) + ).returns(Baml::BamlStream[T::Array[String]]) } def ExtractNames( input: ) - raw = @runtime.call_function( - function_name: "ExtractNames", - args: { + raw = @runtime.stream_function( + "ExtractNames", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Array[String]].new( - inner: raw, - parsed: Baml::convert_to(T::Array[String]).from(raw.parsed) + Baml::BamlStream[T::Array[T.nilable(String)], T::Array[String]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - resume: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::Resume]) + text: String, + ).returns(Baml::BamlStream[T::Array[Baml::Types::Person]]) + } + def ExtractPeople( + text: + ) + raw = @runtime.stream_function( + "ExtractPeople", + { + "text" => text, + }, + @ctx_manager, + ) + Baml::BamlStream[T::Array[Baml::PartialTypes::Person], T::Array[Baml::Types::Person]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + resume: String,img: T.nilable(Baml::Image), + ).returns(Baml::BamlStream[Baml::Types::Resume]) } def ExtractResume( - resume: + resume:,img: ) - raw = @runtime.call_function( - function_name: "ExtractResume", - args: { - "resume" => resume, - } + raw = @runtime.stream_function( + "ExtractResume", + { + "resume" => resume,"img" => img, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::Resume].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::Resume).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::Resume, Baml::Types::Resume].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( resume: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::Resume]) + ).returns(Baml::BamlStream[Baml::Types::Resume]) } def ExtractResume2( resume: ) - raw = @runtime.call_function( - function_name: "ExtractResume2", - args: { + raw = @runtime.stream_function( + "ExtractResume2", + { "resume" => resume, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::Resume].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::Resume).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::Resume, Baml::Types::Resume].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T.nilable(Baml::Types::ClassOptionalOutput2v2)]) + ).returns(Baml::BamlStream[T.nilable(Baml::Types::ClassOptionalOutput)]) } - def FnClassOptionalOutput2_V2( + def FnClassOptionalOutput( input: ) - raw = @runtime.call_function( - function_name: "FnClassOptionalOutput2_V2", - args: { + raw = @runtime.stream_function( + "FnClassOptionalOutput", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T.nilable(Baml::Types::ClassOptionalOutput2v2)].new( - inner: raw, - parsed: Baml::convert_to(T.nilable(Baml::Types::ClassOptionalOutput2v2)).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::ClassOptionalOutput, T.nilable(Baml::Types::ClassOptionalOutput)].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::TestClassWithEnum2]) + ).returns(Baml::BamlStream[T.nilable(Baml::Types::ClassOptionalOutput2)]) } - def FnOutputClassWithEnum_V2( + def FnClassOptionalOutput2( input: ) - raw = @runtime.call_function( - function_name: "FnOutputClassWithEnum_V2", - args: { + raw = @runtime.stream_function( + "FnClassOptionalOutput2", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::TestClassWithEnum2].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::TestClassWithEnum2).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::ClassOptionalOutput2, T.nilable(Baml::Types::ClassOptionalOutput2)].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - text: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::RaysData]) + input: String, + ).returns(Baml::BamlStream[T::Array[Baml::Types::EnumOutput]]) } - def GetDataType( - text: + def FnEnumListOutput( + input: ) - raw = @runtime.call_function( - function_name: "GetDataType", - args: { - "text" => text, - } + raw = @runtime.stream_function( + "FnEnumListOutput", + { + "input" => input, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::RaysData].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::RaysData).from(raw.parsed) + Baml::BamlStream[T::Array[T.nilable(Baml::Types::EnumOutput)], T::Array[Baml::Types::EnumOutput]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - email: Baml::Types::Email, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::OrderInfo]) + input: String, + ).returns(Baml::BamlStream[Baml::Types::EnumOutput]) } - def GetOrderInfo( - email: + def FnEnumOutput( + input: ) - raw = @runtime.call_function( - function_name: "GetOrderInfo", - args: { - "email" => email, - } + raw = @runtime.stream_function( + "FnEnumOutput", + { + "input" => input, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::OrderInfo].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::OrderInfo).from(raw.parsed) + Baml::BamlStream[T.nilable(Baml::Types::EnumOutput), Baml::Types::EnumOutput].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - query: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::SearchParams]) + myString: T.nilable(String), + ).returns(Baml::BamlStream[String]) } - def GetQuery( - query: + def FnNamedArgsSingleStringOptional( + myString: ) - raw = @runtime.call_function( - function_name: "GetQuery", - args: { - "query" => query, - } + raw = @runtime.stream_function( + "FnNamedArgsSingleStringOptional", + { + "myString" => myString, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::SearchParams].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::SearchParams).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Array[T.nilable(Baml::Types::OptionalTest_ReturnTypev2)]]) + ).returns(Baml::BamlStream[T::Boolean]) } - def OptionalTest_Function_V2( + def FnOutputBool( input: ) - raw = @runtime.call_function( - function_name: "OptionalTest_Function_V2", - args: { + raw = @runtime.stream_function( + "FnOutputBool", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Array[T.nilable(Baml::Types::OptionalTest_ReturnTypev2)]].new( - inner: raw, - parsed: Baml::convert_to(T::Array[T.nilable(Baml::Types::OptionalTest_ReturnTypev2)]).from(raw.parsed) + Baml::BamlStream[T.nilable(T::Boolean), T::Boolean].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - input: T.nilable(Baml::Types::OptionalClassv2), - ).returns(Baml::Unstable::FunctionResult[String]) + input: String, + ).returns(Baml::BamlStream[Baml::Types::TestOutputClass]) } - def V2_FnClassOptional( + def FnOutputClass( input: ) - raw = @runtime.call_function( - function_name: "V2_FnClassOptional", - args: { + raw = @runtime.stream_function( + "FnOutputClass", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::TestOutputClass, Baml::Types::TestOutputClass].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - input: Baml::Types::ClassOptionalFieldsv2, - ).returns(Baml::Unstable::FunctionResult[String]) + input: String, + ).returns(Baml::BamlStream[T::Array[Baml::Types::TestOutputClass]]) } - def V2_FnClassOptional2( + def FnOutputClassList( input: ) - raw = @runtime.call_function( - function_name: "V2_FnClassOptional2", - args: { + raw = @runtime.stream_function( + "FnOutputClassList", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T::Array[Baml::PartialTypes::TestOutputClass], T::Array[Baml::Types::TestOutputClass]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Array[Baml::Types::EnumOutput]]) + ).returns(Baml::BamlStream[Baml::Types::TestClassWithEnum]) } - def V2_FnEnumListOutput( + def FnOutputClassWithEnum( input: ) - raw = @runtime.call_function( - function_name: "V2_FnEnumListOutput", - args: { + raw = @runtime.stream_function( + "FnOutputClassWithEnum", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Array[Baml::Types::EnumOutput]].new( - inner: raw, - parsed: Baml::convert_to(T::Array[Baml::Types::EnumOutput]).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::TestClassWithEnum, Baml::Types::TestClassWithEnum].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::EnumOutput2]) + ).returns(Baml::BamlStream[Baml::Types::TestOutputClassNested]) } - def V2_FnEnumOutput( + def FnOutputNestedClass( input: ) - raw = @runtime.call_function( - function_name: "V2_FnEnumOutput", - args: { + raw = @runtime.stream_function( + "FnOutputNestedClass", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::EnumOutput2].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::EnumOutput2).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::TestOutputClassNested, Baml::Types::TestOutputClassNested].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - myString: T.nilable(String), - ).returns(Baml::Unstable::FunctionResult[String]) + input: String, + ).returns(Baml::BamlStream[T::Array[String]]) } - def V2_FnNamedArgsSingleStringOptional( - myString: + def FnOutputStringList( + input: ) - raw = @runtime.call_function( - function_name: "V2_FnNamedArgsSingleStringOptional", - args: { - "myString" => myString, - } + raw = @runtime.stream_function( + "FnOutputStringList", + { + "input" => input, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T::Array[T.nilable(String)], T::Array[String]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Boolean]) + ).returns(Baml::BamlStream[Baml::Types::TestEnum]) } - def V2_FnOutputBool( + def FnTestAliasedEnumOutput( input: ) - raw = @runtime.call_function( - function_name: "V2_FnOutputBool", - args: { + raw = @runtime.stream_function( + "FnTestAliasedEnumOutput", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Boolean].new( - inner: raw, - parsed: Baml::convert_to(T::Boolean).from(raw.parsed) + Baml::BamlStream[T.nilable(Baml::Types::TestEnum), Baml::Types::TestEnum].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[Baml::Types::TestOutputClass2]) + ).returns(Baml::BamlStream[Baml::Types::TestClassAlias]) } - def V2_FnOutputClass( + def FnTestClassAlias( input: ) - raw = @runtime.call_function( - function_name: "V2_FnOutputClass", - args: { + raw = @runtime.stream_function( + "FnTestClassAlias", + { "input" => input, - } + }, + @ctx_manager, + ) + Baml::BamlStream[Baml::PartialTypes::TestClassAlias, Baml::Types::TestClassAlias].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + myArg: Baml::Types::NamedArgsSingleEnum, + ).returns(Baml::BamlStream[String]) + } + def FnTestNamedArgsSingleEnum( + myArg: + ) + raw = @runtime.stream_function( + "FnTestNamedArgsSingleEnum", + { + "myArg" => myArg, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + text: String, + ).returns(Baml::BamlStream[Baml::Types::RaysData]) + } + def GetDataType( + text: + ) + raw = @runtime.stream_function( + "GetDataType", + { + "text" => text, + }, + @ctx_manager, + ) + Baml::BamlStream[Baml::PartialTypes::RaysData, Baml::Types::RaysData].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + email: Baml::Types::Email, + ).returns(Baml::BamlStream[Baml::Types::OrderInfo]) + } + def GetOrderInfo( + email: + ) + raw = @runtime.stream_function( + "GetOrderInfo", + { + "email" => email, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[Baml::Types::TestOutputClass2].new( - inner: raw, - parsed: Baml::convert_to(Baml::Types::TestOutputClass2).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::OrderInfo, Baml::Types::OrderInfo].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + query: String, + ).returns(Baml::BamlStream[Baml::Types::SearchParams]) + } + def GetQuery( + query: + ) + raw = @runtime.stream_function( + "GetQuery", + { + "query" => query, + }, + @ctx_manager, + ) + Baml::BamlStream[Baml::PartialTypes::SearchParams, Baml::Types::SearchParams].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Array[Baml::Types::TestOutputClass]]) + ).returns(Baml::BamlStream[Baml::Types::DynamicOutput]) } - def V2_FnOutputClassList( + def MyFunc( input: ) - raw = @runtime.call_function( - function_name: "V2_FnOutputClassList", - args: { + raw = @runtime.stream_function( + "MyFunc", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Array[Baml::Types::TestOutputClass]].new( - inner: raw, - parsed: Baml::convert_to(T::Array[Baml::Types::TestOutputClass]).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::DynamicOutput, Baml::Types::DynamicOutput].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: String, - ).returns(Baml::Unstable::FunctionResult[T::Array[String]]) + ).returns(Baml::BamlStream[T::Array[T.nilable(Baml::Types::OptionalTest_ReturnType)]]) } - def V2_FnOutputStringList( + def OptionalTest_Function( input: ) - raw = @runtime.call_function( - function_name: "V2_FnOutputStringList", - args: { + raw = @runtime.stream_function( + "OptionalTest_Function", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T::Array[String]].new( - inner: raw, - parsed: Baml::convert_to(T::Array[String]).from(raw.parsed) + Baml::BamlStream[T::Array[Baml::PartialTypes::OptionalTest_ReturnType], T::Array[T.nilable(Baml::Types::OptionalTest_ReturnType)]].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - input: T.nilable(String), - ).returns(Baml::Unstable::FunctionResult[String]) + input: String, + ).returns(Baml::BamlStream[String]) } - def V2_FnStringOptional( + def PromptTestClaude( input: ) - raw = @runtime.call_function( - function_name: "V2_FnStringOptional", - args: { + raw = @runtime.stream_function( + "PromptTestClaude", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - myArg: Baml::Types::NamedArgsSingleEnum2, - ).returns(Baml::Unstable::FunctionResult[String]) + input: String, + ).returns(Baml::BamlStream[String]) } - def V2_FnTestNamedArgsSingleEnum( - myArg: + def PromptTestClaudeChat( + input: ) - raw = @runtime.call_function( - function_name: "V2_FnTestNamedArgsSingleEnum", - args: { - "myArg" => myArg, - } + raw = @runtime.stream_function( + "PromptTestClaudeChat", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def PromptTestClaudeChatNoSystem( + input: + ) + raw = @runtime.stream_function( + "PromptTestClaudeChatNoSystem", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def PromptTestOpenAI( + input: + ) + raw = @runtime.stream_function( + "PromptTestOpenAI", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def PromptTestOpenAIChat( + input: + ) + raw = @runtime.stream_function( + "PromptTestOpenAIChat", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def PromptTestOpenAIChatNoSystem( + input: + ) + raw = @runtime.stream_function( + "PromptTestOpenAIChatNoSystem", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + + ).returns(Baml::BamlStream[String]) + } + def TestFallbackClient( + + ) + raw = @runtime.stream_function( + "TestFallbackClient", + { + + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( myBool: T::Boolean, - ).returns(Baml::Unstable::FunctionResult[String]) + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleBool( + def TestFnNamedArgsSingleBool( myBool: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleBool", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleBool", + { "myBool" => myBool, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - myArg: Baml::Types::NamedArgsSingleClass2, - ).returns(Baml::Unstable::FunctionResult[String]) + myArg: Baml::Types::NamedArgsSingleClass, + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleClass( + def TestFnNamedArgsSingleClass( myArg: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleClass", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleClass", + { "myArg" => myArg, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - myArg: T::Array[Baml::Types::NamedArgsSingleEnumList2], - ).returns(Baml::Unstable::FunctionResult[String]) + myArg: T::Array[Baml::Types::NamedArgsSingleEnumList], + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleEnumList( + def TestFnNamedArgsSingleEnumList( myArg: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleEnumList", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleEnumList", + { "myArg" => myArg, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( myFloat: Float, - ).returns(Baml::Unstable::FunctionResult[String]) + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleFloat( + def TestFnNamedArgsSingleFloat( myFloat: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleFloat", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleFloat", + { "myFloat" => myFloat, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( myInt: Integer, - ).returns(Baml::Unstable::FunctionResult[String]) + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleInt( + def TestFnNamedArgsSingleInt( myInt: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleInt", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleInt", + { "myInt" => myInt, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( myString: String, - ).returns(Baml::Unstable::FunctionResult[String]) + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleString( + def TestFnNamedArgsSingleString( myString: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleString", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleString", + { "myString" => myString, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( myStringArray: T::Array[String], - ).returns(Baml::Unstable::FunctionResult[String]) + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleStringArray( + def TestFnNamedArgsSingleStringArray( myStringArray: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleStringArray", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleStringArray", + { "myStringArray" => myStringArray, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - myArg: T::Array[Baml::Types::NamedArgsSingleClassList2], - ).returns(Baml::Unstable::FunctionResult[String]) + myArg: T::Array[String], + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSingleStringList( + def TestFnNamedArgsSingleStringList( myArg: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSingleStringList", - args: { + raw = @runtime.stream_function( + "TestFnNamedArgsSingleStringList", + { "myArg" => myArg, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( - var: String,var_with_underscores: String, - ).returns(Baml::Unstable::FunctionResult[String]) + img: Baml::Image, + ).returns(Baml::BamlStream[String]) } - def V2_TestFnNamedArgsSyntax( - var:,var_with_underscores: + def TestImageInput( + img: ) - raw = @runtime.call_function( - function_name: "V2_TestFnNamedArgsSyntax", - args: { - "var" => var,"var_with_underscores" => var_with_underscores, - } + raw = @runtime.stream_function( + "TestImageInput", + { + "img" => img, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + myArg: Baml::Types::NamedArgsSingleClass,myArg2: Baml::Types::NamedArgsSingleClass, + ).returns(Baml::BamlStream[String]) + } + def TestMulticlassNamedArgs( + myArg:,myArg2: + ) + raw = @runtime.stream_function( + "TestMulticlassNamedArgs", + { + "myArg" => myArg,"myArg2" => myArg2, + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[String].new( - inner: raw, - parsed: Baml::convert_to(String).from(raw.parsed) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + input: String, + ).returns(Baml::BamlStream[String]) + } + def TestOllama( + input: + ) + raw = @runtime.stream_function( + "TestOllama", + { + "input" => input, + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + + ).returns(Baml::BamlStream[String]) + } + def TestRetryConstant( + + ) + raw = @runtime.stream_function( + "TestRetryConstant", + { + + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager + ) + end + + sig { + params( + + ).returns(Baml::BamlStream[String]) + } + def TestRetryExponential( + + ) + raw = @runtime.stream_function( + "TestRetryExponential", + { + + }, + @ctx_manager, + ) + Baml::BamlStream[T.nilable(String), String].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end sig { params( input: T.any(String, T::Boolean), - ).returns(Baml::Unstable::FunctionResult[T.any(Baml::Types::UnionTest_ReturnTypev2, Baml::Types::DataType)]) + ).returns(Baml::BamlStream[Baml::Types::UnionTest_ReturnType]) } - def V2_UnionTest_Function( + def UnionTest_Function( input: ) - raw = @runtime.call_function( - function_name: "V2_UnionTest_Function", - args: { + raw = @runtime.stream_function( + "UnionTest_Function", + { "input" => input, - } + }, + @ctx_manager, ) - Baml::Unstable::FunctionResult[T.any(Baml::Types::UnionTest_ReturnTypev2, Baml::Types::DataType)].new( - inner: raw, - parsed: Baml::convert_to(T.any(Baml::Types::UnionTest_ReturnTypev2, Baml::Types::DataType)).from(raw.parsed) + Baml::BamlStream[Baml::PartialTypes::UnionTest_ReturnType, Baml::Types::UnionTest_ReturnType].new( + ffi_stream: raw, + ctx_manager: @ctx_manager ) end diff --git a/integ-tests/ruby/baml_client/inlined.rb b/integ-tests/ruby/baml_client/inlined.rb new file mode 100644 index 000000000..e0d38a37d --- /dev/null +++ b/integ-tests/ruby/baml_client/inlined.rb @@ -0,0 +1,65 @@ +############################################################################### +# +# Welcome to Baml! To use this generated code, please run the following: +# +# $ bundle add baml sorbet-runtime sorbet-struct-comparable +# +############################################################################### + +# This file was generated by BAML: please do not edit it. Instead, edit the +# BAML files and re-generate this code. +# +# frozen_string_literal: true +# rubocop: disable +# formatter:off +module Baml + module Inlined + FILE_MAP = { + + "clients.baml" => "retry_policy Bar {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Foo {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient GPT4 {\n provider baml-openai-chat\n options {\n model gpt-4\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4o {\n provider baml-openai-chat\n options {\n model gpt-4o\n api_key env.OPENAI_API_KEY\n }\n} \n\n\nclient GPT4Turbo {\n retry_policy Bar\n provider baml-openai-chat\n options {\n model gpt-4-turbo\n api_key env.OPENAI_API_KEY\n }\n} \n\nclient GPT35 {\n provider baml-openai-chat\n options {\n model \"gpt-3.5-turbo\"\n api_key env.OPENAI_API_KEY\n }\n}\n\nclient Ollama {\n provider ollama\n options {\n model llama2\n api_key \"\"\n }\n}\n\nclient GPT35Azure {\n provider azure-openai\n options {\n resource_name \"west-us-azure-baml\"\n deployment_id \"gpt-35-turbo-default\"\n // base_url \"https://west-us-azure-baml.openai.azure.com/openai/deployments/gpt-35-turbo-default\"\n api_version \"2024-02-01\"\n api_key env.AZURE_OPENAI_API_KEY\n }\n}\n\n\nclient Claude {\n provider anthropic\n options {\n model claude-3-haiku-20240307\n api_key env.ANTHROPIC_API_KEY\n max_tokens 1000\n }\n}\n\nclient Resilient_SimpleSyntax {\n retry_policy Foo\n provider baml-fallback\n options {\n strategy [\n GPT4Turbo\n GPT35\n Lottery_SimpleSyntax\n ]\n }\n} \n \nclient Lottery_SimpleSyntax {\n provider baml-round-robin\n options {\n start 0\n strategy [\n GPT35\n Claude\n ]\n }\n}\n", + "fiddle-examples/chain-of-thought.baml" => "class Email {\n subject string\n body string\n from_address string\n}\n\nenum OrderStatus {\n ORDERED\n SHIPPED\n DELIVERED\n CANCELLED\n}\n\nclass OrderInfo {\n order_status OrderStatus\n tracking_number string?\n estimated_arrival_date string?\n}\n\nfunction GetOrderInfo(email: Email) -> OrderInfo {\n client GPT4\n prompt #\"\n Given the email below:\n\n ```\n from: {{email.from_address}}\n Email Subject: {{email.subject}}\n Email Body: {{email.body}}\n ```\n\n Extract this info from the email in JSON format:\n {{ ctx.output_format }}\n\n Before you output the JSON, please explain your\n reasoning step-by-step. Here is an example on how to do this:\n 'If we think step by step we can see that ...\n therefore the output JSON is:\n {\n ... the json schema ...\n }'\n \"#\n}", + "fiddle-examples/chat-roles.baml" => "// This will be available as an enum in your Python and Typescript code.\nenum Category2 {\n Refund\n CancelOrder\n TechnicalSupport\n AccountIssue\n Question\n}\n\nfunction ClassifyMessage2(input: string) -> Category {\n client GPT4\n\n prompt #\"\n {{ _.role(\"system\") }}\n // You can use _.role(\"system\") to indicate that this text should be a system message\n\n Classify the following INPUT into ONE\n of the following categories:\n\n {{ ctx.output_format }}\n\n {{ _.role(\"user\") }}\n // And _.role(\"user\") to indicate that this text should be a user message\n\n INPUT: {{ input }}\n\n Response:\n \"#\n}", + "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/images/image.baml" => "function DescribeImage(img: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 5 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}", + "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\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", + "test-files/aliases/enums.baml" => "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", + "test-files/comments/comments.baml" => "// add some functions, classes, enums etc with comments all over.", + "test-files/dynamic/dynamic.baml" => "class DynamicClassOne {\n @@dynamic\n}\n\nenum DynEnumOne {\n @@dynamic\n}\n\nenum DynEnumTwo {\n @@dynamic\n}\n\nclass SomeClassNestedDynamic {\n hi string\n @@dynamic\n\n}\n\nclass DynamicClassTwo {\n hi string\n some_class SomeClassNestedDynamic\n status DynEnumOne\n @@dynamic\n}\n\nfunction DynamicFunc(input: DynamicClassOne) -> DynamicClassTwo {\n client GPT35\n prompt #\"\n Please extract the schema from \n {{ input }}\n\n {{ ctx.output_format }}\n \"#\n}\n\nclass DynamicOutput {\n @@dynamic\n}\n \nfunction MyFunc(input: string) -> DynamicOutput {\n client GPT4\n prompt #\"\n Given a string, extract info using the schema:\n\n {{ input}}\n\n {{ ctx.output_format }}\n \"#\n}", + "test-files/functions/input/named-args/single/named-boolean.baml" => "\n\nfunction TestFnNamedArgsSingleBool(myBool: bool) -> string{\n client GPT35\n prompt #\"\n Return this value back to me: {{myBool}}\n \"#\n}\n\ntest TestFnNamedArgsSingleBool {\n functions [TestFnNamedArgsSingleBool]\n args {\n myBool true\n }\n}", + "test-files/functions/input/named-args/single/named-class-list.baml" => "\n\n\nfunction TestFnNamedArgsSingleStringList(myArg: string[]) -> string{\n client GPT35\n prompt #\"\n Return this value back to me: {{myArg}}\n \"#\n}\n\ntest TestFnNamedArgsSingleStringList {\n functions [TestFnNamedArgsSingleStringList]\n args {\n myArg [\"hello\", \"world\"]\n }\n}", + "test-files/functions/input/named-args/single/named-class.baml" => "class NamedArgsSingleClass {\n key string\n key_two bool\n key_three int\n // TODO: doesn't work with keys with numbers\n // key2 bool\n // key3 int\n}\n\nfunction TestFnNamedArgsSingleClass(myArg: NamedArgsSingleClass) -> string {\n client GPT35\n prompt #\"\n Print these values back to me:\n {{myArg.key}}\n {{myArg.key_two}}\n {{myArg.key_three}}\n \"#\n}\n\ntest TestFnNamedArgsSingleClass {\n functions [TestFnNamedArgsSingleClass]\n args {\n myArg {\n key \"example\",\n key_two true,\n key_three 42\n }\n }\n}\n\nfunction TestMulticlassNamedArgs(myArg: NamedArgsSingleClass, myArg2: NamedArgsSingleClass) -> string {\n client GPT35\n prompt #\"\n Print these values back to me:\n {{myArg.key}}\n {{myArg.key_two}}\n {{myArg.key_three}}\n {{myArg2.key}}\n {{myArg2.key_two}}\n {{myArg2.key_three}}\n \"#\n}", + "test-files/functions/input/named-args/single/named-enum-list.baml" => "enum NamedArgsSingleEnumList {\n ONE\n TWO\n}\n\nfunction TestFnNamedArgsSingleEnumList(myArg: NamedArgsSingleEnumList[]) -> string {\n client GPT35\n prompt #\"\n Print these values back to me:\n {{myArg}}\n \"#\n}\n\ntest TestFnNamedArgsSingleEnumList {\n functions [TestFnNamedArgsSingleEnumList]\n args {\n myArg [ONE, TWO]\n }\n}", + "test-files/functions/input/named-args/single/named-enum.baml" => "enum NamedArgsSingleEnum {\n ONE\n TWO\n}\n\nfunction FnTestNamedArgsSingleEnum(myArg: NamedArgsSingleEnum) -> string {\n client GPT35\n prompt #\"\n Print these values back to me:\n {{myArg}}\n \"#\n}\n\ntest FnTestNamedArgsSingleEnum {\n functions [FnTestNamedArgsSingleEnum]\n args {\n myArg ONE\n }\n}", + "test-files/functions/input/named-args/single/named-float.baml" => "function TestFnNamedArgsSingleFloat(myFloat: float) -> string {\n client GPT35\n prompt #\"\n Return this value back to me: {{myFloat}}\n \"#\n}\n\ntest TestFnNamedArgsSingleFloat {\n functions [TestFnNamedArgsSingleFloat]\n args {\n myFloat 3.14\n }\n}\n", + "test-files/functions/input/named-args/single/named-image.baml" => "function TestImageInput(img: image) -> string{\n client GPT4o\n prompt #\"\n {{ _.role(\"user\") }}\n\n Describe this in 4 words {{img}}\n \"#\n}\n\ntest TestImageInput {\n functions [TestImageInput]\n args {\n img {\n url \"https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png\"\n }\n }\n}\n\ntest shrek {\n functions [TestImageInput]\n args {\n img {\n url \"https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png\"\n }\n }\n}\n\n\n// double check this before adding it. Probably n ot right.\n// function TestImageInputAnthropic(img: image) -> string{\n// client GPT4o\n// prompt #\"\n// {{ _.role(\"user\") }}\n\n// Describe this in 4 words {{img}}\n// \"#\n// }\n\n// test TestImageInputAnthropic {\n// functions [TestImageInputAnthropic]\n// args {\n// img {\n// base64 iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=\n// media_type \"png\"\n// }\n// }\n// }", + "test-files/functions/input/named-args/single/named-int.baml" => "// test for int\nfunction TestFnNamedArgsSingleInt(myInt: int) -> string {\n client GPT35\n prompt #\"\n Return this value back to me: {{myInt}}\n \"#\n}\n\ntest TestFnNamedArgsSingleInt {\n functions [TestFnNamedArgsSingleInt]\n args {\n myInt 42\n }\n}\n", + "test-files/functions/input/named-args/single/named-string-list.baml" => "// string[]\nfunction TestFnNamedArgsSingleStringArray(myStringArray: string[]) -> string {\n client GPT35\n prompt #\"\n Return this value back to me: {{myStringArray}}\n \"#\n}\n\ntest TestFnNamedArgsSingleStringArray {\n functions [TestFnNamedArgsSingleStringArray]\n args {\n myStringArray [\"example1\", \"example2\", \"example3\"]\n }\n}\n", + "test-files/functions/input/named-args/single/named-string-optional.baml" => "\n\n // string[]\nfunction FnNamedArgsSingleStringOptional(myString: string?) -> string {\n client GPT35\n prompt #\"\n Return this value back to me: {{myString}}\n \"#\n}\n\ntest FnNamedArgsSingleStringOptional {\n functions [FnNamedArgsSingleStringOptional]\n args {\n myString \"example string\"\n }\n}\n\ntest FnNamedArgsSingleStringOptional2 {\n functions [FnNamedArgsSingleStringOptional]\n args {\n \n }\n}\n", + "test-files/functions/input/named-args/single/named-string.baml" => "// test string\nfunction TestFnNamedArgsSingleString(myString: string) -> string {\n client GPT35\n prompt #\"\n Return this value back to me: {{myString}}\n \"#\n}\n\ntest TestFnNamedArgsSingleString {\n functions [TestFnNamedArgsSingleString]\n args {\n myString \"example string\"\n }\n}\n", + "test-files/functions/input/named-args/syntax.baml" => "function TestFnNamedArgsSyntax {\n input (myVar: string, var_with_underscores: string)\n output string\n}\n// TODO: we don't support numbers in named args yet!\n// TODO: we also allow dashes but python fails.", + "test-files/functions/output/boolean.baml" => "function FnOutputBool(input: string) -> bool {\n client GPT35\n prompt #\"\n Return a true: {{ ctx.output_format}}\n \"#\n}\n\ntest FnOutputBool {\n functions [FnOutputBool]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/class-dynamic.baml" => "class Person {\n name string?\n hair_color Color?\n\n @@dynamic\n}\n\nenum Color {\n RED\n BLUE\n GREEN\n YELLOW\n BLACK\n WHITE\n\n @@dynamic\n}\n\nfunction ExtractPeople(text: string) -> Person[] {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\t\t You are an expert extraction algorithm. Only extract relevant information from the text. If you do not know the value of an attribute asked to extract, return null for the attribute's value.\n\t\t \n\t\t {# This is a special macro that prints out the output schema of the function #}\n\t\t {{ ctx.output_format }} \n\t\t \n\t\t {{ _.role('user') }}\n\t\t {{text}}\n \"#\n}\n\nenum Hobby {\n SPORTS\n MUSIC\n READING\n\n @@dynamic\n}\n", + "test-files/functions/output/class-list.baml" => "function FnOutputClassList(input: string) -> TestOutputClass[] {\n client GPT35\n prompt #\"\n Return a JSON array that follows this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnOutputClassList {\n functions [FnOutputClassList]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/class-with-enum.baml" => "enum EnumInClass {\n ONE\n TWO\n}\n\nclass TestClassWithEnum {\n prop1 string\n prop2 EnumInClass\n}\n\nfunction FnOutputClassWithEnum(input: string) -> TestClassWithEnum {\n client GPT35\n prompt #\"\n Return a made up json blob that matches this schema:\n {{ctx.output_format}}\n ---\n\n JSON:\n \"#\n}\n\ntest FnOutputClassWithEnum {\n functions [FnOutputClassWithEnum]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/class.baml" => "class TestOutputClass {\n prop1 string\n prop2 int\n}\n\nfunction FnOutputClass(input: string) -> TestOutputClass {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n For the prop2, always return a 540\n\n JSON:\n \"#\n}\n\ntest TestClass {\n functions [FnOutputClass, FnOutputNestedClass]\n args {\n input \"example input\"\n }\n}\n\n \n\nclass TestOutputClassNested {\n prop1 string\n prop2 int\n prop3 TestOutputClass\n}\n\nfunction FnOutputNestedClass(input: string) -> TestOutputClassNested {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}", + "test-files/functions/output/enum-list.baml" => "function FnEnumListOutput(input: string) -> EnumOutput[] {\n client GPT35\n prompt #\"\n Print out two of these values randomly selected from the list below in a json array.\n\n {{ctx.output_format}}\n\n Answer:\n \"#\n} \n\ntest FnEnumListOutput {\n functions [FnEnumListOutput]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/enum.baml" => "enum EnumOutput {\n ONE\n TWO\n THREE\n\n @@alias(\"VALUE_ENUM\")\n}\n\nfunction FnEnumOutput(input: string) -> EnumOutput {\n client GPT35\n prompt #\"\n Choose one of these values randomly. Before you give the answer, write out an unrelated haiku about the ocean.\n\n {{ctx.output_format(prefix=null)}}\n \"#\n}\n\ntest FnEnumOutput {\n functions [FnEnumOutput]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/int.baml" => " ", + "test-files/functions/output/optional-class.baml" => "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/optional.baml" => "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/string-list.baml" => "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/unions.baml" => "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/prompts/no-chat-messages.baml" => "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", + "test-files/functions/prompts/with-chat-messages.baml" => "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", + "test-files/functions/v2/basic.baml" => "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", + "test-files/providers/providers.baml" => "\n\nfunction TestOllama(input: string) -> string {\n client Ollama\n prompt #\"\n Write a nice haiku about {{ input }}\n \"#\n}\n\ntest TestProvider {\n functions [TestOllama]\n args {\n input \"the moon\"\n }\n}\n", + "test-files/strategies/fallback.baml" => "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", + "test-files/strategies/retry.baml" => "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", + "test-files/strategies/roundrobin.baml" => "", + "test-files/testing_pipeline/resume.baml" => "class Resume {\n name string\n email string\n phone string\n experience Education[]\n education string[]\n skills string[]\n}\n\nclass Education {\n institution string\n location string\n degree string\n major string[]\n graduation_date string?\n}\n\ntemplate_string AddRole(foo: string) #\"\n {{ _.role('system')}}\n You are a {{ foo }}. be nice\n\n {{ _.role('user') }}\n\"#\n\nfunction ExtractResume(resume: string, img: image?) -> Resume {\n client GPT35\n prompt #\"\n {{ AddRole(\"Software Engineer\") }}\n\n Extract data:\n \n\n <<<<\n {{ resume }}\n <<<<\n\n {% if img %}\n {{img}}\n {% endif %}\n\n {{ ctx.output_format }}\n \"#\n}\n\ntest sam_resume {\n functions [ExtractResume]\n input {\n img {\n url \"https://avatars.githubusercontent.com/u/1016595?v=4\"\n }\n resume #\"\n Sam Lijin\n he/him | jobs@sxlijin.com | sxlijin.github.io | 111-222-3333 | sxlijin | sxlijin\n\n Experience\n Trunk\n | July 2021 - current\n Trunk Check | Senior Software Engineer | Services TL, Mar 2023 - current | IC, July 2021 - Feb 2023\n Proposed, designed, and led a team of 3 to build a web experience for Check (both a web-only onboarding flow and SaaS offerings)\n Proposed and built vulnerability scanning into Check, enabling it to compete with security products such as Snyk\n Helped grow Check from <1K users to 90K+ users by focusing on product-led growth\n Google | Sept 2017 - June 2021\n User Identity SRE | Senior Software Engineer | IC, Mar 2021 - June 2021\n Designed an incremental key rotation system to limit the global outage risk to Google SSO\n Discovered and severed an undocumented Gmail serving dependency on Identity-internal systems\n Cloud Firestore | Senior Software Engineer | EngProd TL, Aug 2019 - Feb 2021 | IC, Sept 2017 - July 2019\n Metadata TTL system: backlog of XX trillion records, sustained 1M ops/sec, peaking at 3M ops/sec\n\n Designed and implemented a logging system with novel observability and privacy requirements\n Designed and implemented Jepsen-style testing to validate correctness guarantees\n Datastore Migration: zero downtime, xM RPS and xxPB of data over xM customers and 36 datacenters\n\n Designed composite index migration, queue processing migration, progressive rollout, fast rollback, and disk stockout mitigations; implemented transaction log replay, state transitions, and dark launch process\n Designed and implemented end-to-end correctness and performance testing\n Velocity improvements for 60-eng org\n\n Proposed and implemented automated rollbacks: got us out of a 3-month release freeze and prevented 5 outages over the next 6 months\n Proposed and implemented new development and release environments spanning 30+ microservices\n Incident response for API proxy rollback affecting every Google Cloud service\n\n Google App Engine Memcache | Software Engineer | EngProd TL, Apr 2019 - July 2019\n Proposed and led execution of test coverage improvement strategy for a new control plane: reduced rollbacks and ensured strong consistency of a distributed cache serving xxM QPS\n Designed and implemented automated performance regression testing for two critical serving paths\n Used to validate Google-wide rollout of AMD CPUs, by proving a 50p latency delta of <10µs\n Implemented on shared Borg (i.e. vulnerable to noisy neighbors) with <12% variance\n Miscellaneous | Sept 2017 - June 2021\n Redesigned the Noogler training on Google-internal storage technologies & trained 2500+ Nooglers\n Landed multiple google3-wide refactorings, each spanning xxK files (e.g. SWIG to CLIF)\n Education\n Vanderbilt University (Nashville, TN) | May 2017 | B.S. in Computer Science, Mathematics, and Political Science\n\n Stuyvesant HS (New York, NY) | 2013\n\n Skills\n C++, Java, Typescript, Javascript, Python, Bash; light experience with Rust, Golang, Scheme\n gRPC, Bazel, React, Linux\n Hobbies: climbing, skiing, photography\n \"#\n }\n}\n\ntest vaibhav_resume {\n functions [ExtractResume]\n input {\n resume #\"\n Vaibhav Gupta\n linkedin/vaigup\n (972) 400-5279\n vaibhavtheory@gmail.com\n EXPERIENCE\n Google,\n Software Engineer\n Dec 2018-Present\n Seattle, WA\n •\n Augmented Reality,\n Depth Team\n •\n Technical Lead for on-device optimizations\n •\n Optimized and designed front\n facing depth algorithm\n on Pixel 4\n •\n Focus: C++ and SIMD on custom silicon\n \n \n EDUCATION\n University of Texas at Austin\n Aug 2012-May 2015\n Bachelors of Engineering, Integrated Circuits\n Bachelors of Computer Science\n \"#\n }\n}", + } + end +end \ No newline at end of file diff --git a/integ-tests/ruby/baml_client/partial-types.rb b/integ-tests/ruby/baml_client/partial-types.rb new file mode 100644 index 000000000..90fdea5a2 --- /dev/null +++ b/integ-tests/ruby/baml_client/partial-types.rb @@ -0,0 +1,199 @@ +############################################################################### +# +# Welcome to Baml! To use this generated code, please run the following: +# +# $ bundle add baml sorbet-runtime sorbet-struct-comparable +# +############################################################################### + +# This file was generated by BAML: please do not edit it. Instead, edit the +# BAML files and re-generate this code. +# +# frozen_string_literal: true +# rubocop: disable +# formatter:off +# typed: false +require "sorbet-runtime" +require "sorbet-struct-comparable" + +require_relative "types" + +module Baml + + module PartialTypes + class Blah < T::Struct; end + class ClassOptionalOutput < T::Struct; end + class ClassOptionalOutput2 < T::Struct; end + class ClassWithImage < T::Struct; end + class DynamicClassOne < T::Struct; end + class DynamicClassTwo < T::Struct; end + class DynamicOutput < T::Struct; end + class Education < T::Struct; end + class Email < T::Struct; end + class Event < T::Struct; end + class FakeImage < T::Struct; end + class NamedArgsSingleClass < T::Struct; end + class OptionalTest_Prop1 < T::Struct; end + class OptionalTest_ReturnType < T::Struct; end + class OrderInfo < T::Struct; end + class Person < T::Struct; end + class RaysData < T::Struct; end + class Resume < T::Struct; end + class SearchParams < T::Struct; end + class SomeClassNestedDynamic < T::Struct; end + class TestClassAlias < T::Struct; end + class TestClassWithEnum < T::Struct; end + class TestOutputClass < T::Struct; end + class TestOutputClassNested < T::Struct; end + class UnionTest_ReturnType < T::Struct; end + class WithReasoning < T::Struct; end + class Blah < T::Struct + include T::Struct::ActsAsComparable + const :prop4, T.nilable(String) + end + class ClassOptionalOutput < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(String) + const :prop2, T.nilable(String) + end + class ClassOptionalOutput2 < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(String) + const :prop2, T.nilable(String) + const :prop3, Baml::PartialTypes::Blah + end + class ClassWithImage < T::Struct + include T::Struct::ActsAsComparable + const :myImage, T.nilable(Baml::Image) + const :param2, T.nilable(String) + const :fake_image, Baml::PartialTypes::FakeImage + end + class DynamicClassOne < T::Struct + include T::Struct::ActsAsComparable + end + class DynamicClassTwo < T::Struct + include T::Struct::ActsAsComparable + const :hi, T.nilable(String) + const :some_class, Baml::PartialTypes::SomeClassNestedDynamic + const :status, T.nilable(Baml::Types::DynEnumOne) + end + class DynamicOutput < T::Struct + include T::Struct::ActsAsComparable + end + class Education < T::Struct + include T::Struct::ActsAsComparable + const :institution, T.nilable(String) + const :location, T.nilable(String) + const :degree, T.nilable(String) + const :major, T::Array[T.nilable(String)] + const :graduation_date, T.nilable(String) + end + class Email < T::Struct + include T::Struct::ActsAsComparable + const :subject, T.nilable(String) + const :body, T.nilable(String) + const :from_address, T.nilable(String) + end + class Event < T::Struct + include T::Struct::ActsAsComparable + const :title, T.nilable(String) + const :date, T.nilable(String) + const :location, T.nilable(String) + const :description, T.nilable(String) + end + class FakeImage < T::Struct + include T::Struct::ActsAsComparable + const :url, T.nilable(String) + end + class NamedArgsSingleClass < T::Struct + include T::Struct::ActsAsComparable + const :key, T.nilable(String) + const :key_two, T.nilable(T::Boolean) + const :key_three, T.nilable(Integer) + end + class OptionalTest_Prop1 < T::Struct + include T::Struct::ActsAsComparable + const :omega_a, T.nilable(String) + const :omega_b, T.nilable(Integer) + end + class OptionalTest_ReturnType < T::Struct + include T::Struct::ActsAsComparable + const :omega_1, Baml::PartialTypes::OptionalTest_Prop1 + const :omega_2, T.nilable(String) + const :omega_3, T::Array[T.nilable(Baml::Types::OptionalTest_CategoryType)] + end + class OrderInfo < T::Struct + include T::Struct::ActsAsComparable + const :order_status, T.nilable(Baml::Types::OrderStatus) + const :tracking_number, T.nilable(String) + const :estimated_arrival_date, T.nilable(String) + end + class Person < T::Struct + include T::Struct::ActsAsComparable + const :name, T.nilable(String) + const :hair_color, T.nilable(Baml::Types::Color) + end + class RaysData < T::Struct + include T::Struct::ActsAsComparable + const :dataType, T.nilable(Baml::Types::DataType) + const :value, T.nilable(T.any(Baml::PartialTypes::Resume, Baml::PartialTypes::Event)) + end + class Resume < T::Struct + include T::Struct::ActsAsComparable + const :name, T.nilable(String) + const :email, T.nilable(String) + const :phone, T.nilable(String) + const :experience, T::Array[Baml::PartialTypes::Education] + const :education, T::Array[T.nilable(String)] + const :skills, T::Array[T.nilable(String)] + end + class SearchParams < T::Struct + include T::Struct::ActsAsComparable + const :dateRange, T.nilable(Integer) + const :location, T::Array[T.nilable(String)] + const :jobTitle, Baml::PartialTypes::WithReasoning + const :company, Baml::PartialTypes::WithReasoning + const :description, T::Array[Baml::PartialTypes::WithReasoning] + const :tags, T::Array[T.nilable(T.any(T.nilable(Baml::Types::Tag), T.nilable(String)))] + end + class SomeClassNestedDynamic < T::Struct + include T::Struct::ActsAsComparable + const :hi, T.nilable(String) + end + class TestClassAlias < T::Struct + include T::Struct::ActsAsComparable + const :key, T.nilable(String) + const :key2, T.nilable(String) + const :key3, T.nilable(String) + const :key4, T.nilable(String) + const :key5, T.nilable(String) + end + class TestClassWithEnum < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(String) + const :prop2, T.nilable(Baml::Types::EnumInClass) + end + class TestOutputClass < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(String) + const :prop2, T.nilable(Integer) + end + class TestOutputClassNested < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(String) + const :prop2, T.nilable(Integer) + const :prop3, Baml::PartialTypes::TestOutputClass + end + class UnionTest_ReturnType < T::Struct + include T::Struct::ActsAsComparable + const :prop1, T.nilable(T.any(T.nilable(String), T.nilable(T::Boolean))) + const :prop2, T::Array[T.nilable(T.any(T.nilable(Float), T.nilable(T::Boolean)))] + const :prop3, T.nilable(T.any(T::Array[T.nilable(T::Boolean)], T::Array[T.nilable(Integer)])) + end + class WithReasoning < T::Struct + include T::Struct::ActsAsComparable + const :value, T.nilable(String) + const :reasoning, T.nilable(String) + end + end +end \ No newline at end of file diff --git a/integ-tests/ruby/baml_client/types.rb b/integ-tests/ruby/baml_client/types.rb index 8c21c8c61..140092802 100644 --- a/integ-tests/ruby/baml_client/types.rb +++ b/integ-tests/ruby/baml_client/types.rb @@ -2,7 +2,7 @@ # # Welcome to Baml! To use this generated code, please run the following: # -# $ bundle add baml sorbet-runtime sorbet-coerce sorbet-struct-comparable +# $ bundle add baml sorbet-runtime sorbet-struct-comparable # ############################################################################### @@ -13,15 +13,12 @@ # rubocop: disable # formatter:off # typed: false -require "delegate" -require "sorbet-coerce" -require "sorbet-struct-comparable" require "sorbet-runtime" +require "sorbet-struct-comparable" module Baml module Types - class Category < T::Enum enums do Refund = new("Refund") @@ -31,7 +28,6 @@ class Category < T::Enum Question = new("Question") end end - class Category2 < T::Enum enums do Refund = new("Refund") @@ -41,7 +37,6 @@ class Category2 < T::Enum Question = new("Question") end end - class Category3 < T::Enum enums do Refund = new("Refund") @@ -51,28 +46,32 @@ class Category3 < T::Enum Question = new("Question") end end - + class Color < T::Enum + enums do + RED = new("RED") + BLUE = new("BLUE") + GREEN = new("GREEN") + YELLOW = new("YELLOW") + BLACK = new("BLACK") + WHITE = new("WHITE") + end + end class DataType < T::Enum enums do Resume = new("Resume") Event = new("Event") end end - - class EnumInClass < T::Enum - enums do - ONE = new("ONE") - TWO = new("TWO") - end + class DynEnumOne < T::Enum end - - class EnumInClass2 < T::Enum + class DynEnumTwo < T::Enum + end + class EnumInClass < T::Enum enums do ONE = new("ONE") TWO = new("TWO") end end - class EnumOutput < T::Enum enums do ONE = new("ONE") @@ -80,43 +79,25 @@ class EnumOutput < T::Enum THREE = new("THREE") end end - - class EnumOutput2 < T::Enum + class Hobby < T::Enum enums do - ONE = new("ONE") - TWO = new("TWO") - THREE = new("THREE") + SPORTS = new("SPORTS") + MUSIC = new("MUSIC") + READING = new("READING") end end - class NamedArgsSingleEnum < T::Enum enums do ONE = new("ONE") TWO = new("TWO") end end - - class NamedArgsSingleEnum2 < T::Enum - enums do - ONE = new("ONE") - TWO = new("TWO") - end - end - class NamedArgsSingleEnumList < T::Enum enums do ONE = new("ONE") TWO = new("TWO") end end - - class NamedArgsSingleEnumList2 < T::Enum - enums do - ONE = new("ONE") - TWO = new("TWO") - end - end - class OptionalTest_CategoryType < T::Enum enums do Aleph = new("Aleph") @@ -124,15 +105,6 @@ class OptionalTest_CategoryType < T::Enum Gamma = new("Gamma") end end - - class OptionalTest_CategoryTypev2 < T::Enum - enums do - Aleph = new("Aleph") - Beta = new("Beta") - Gamma = new("Gamma") - end - end - class OrderStatus < T::Enum enums do ORDERED = new("ORDERED") @@ -141,14 +113,6 @@ class OrderStatus < T::Enum CANCELLED = new("CANCELLED") end end - - class OverrideEnum < T::Enum - enums do - ONE = new("ONE") - TWO = new("TWO") - end - end - class Tag < T::Enum enums do Security = new("Security") @@ -156,7 +120,6 @@ class Tag < T::Enum Blockchain = new("Blockchain") end end - class TestEnum < T::Enum enums do A = new("A") @@ -168,305 +131,134 @@ class TestEnum < T::Enum G = new("G") end end - class Blah < T::Struct; end - - class Blah2 < T::Struct; end - - class ClassOptionalFields < T::Struct; end - - class ClassOptionalFieldsv2 < T::Struct; end - class ClassOptionalOutput < T::Struct; end - class ClassOptionalOutput2 < T::Struct; end - - class ClassOptionalOutput2v2 < T::Struct; end - class ClassWithImage < T::Struct; end - - class DynamicPropsClass < T::Struct; end - + class DynamicClassOne < T::Struct; end + class DynamicClassTwo < T::Struct; end + class DynamicOutput < T::Struct; end + class Education < T::Struct; end class Email < T::Struct; end - class Event < T::Struct; end - class FakeImage < T::Struct; end - - class ModifiedOutput < T::Struct; end - class NamedArgsSingleClass < T::Struct; end - - class NamedArgsSingleClass2 < T::Struct; end - - class NamedArgsSingleClassList2 < T::Struct; end - - class OptionalClass < T::Struct; end - - class OptionalClassv2 < T::Struct; end - class OptionalTest_Prop1 < T::Struct; end - - class OptionalTest_Prop1v2 < T::Struct; end - class OptionalTest_ReturnType < T::Struct; end - - class OptionalTest_ReturnTypev2 < T::Struct; end - class OrderInfo < T::Struct; end - - class OverrideClass < T::Struct; end - + class Person < T::Struct; end class RaysData < T::Struct; end - class Resume < T::Struct; end - class SearchParams < T::Struct; end - - class SomeClass2 < T::Struct; end - + class SomeClassNestedDynamic < T::Struct; end class TestClassAlias < T::Struct; end - class TestClassWithEnum < T::Struct; end - - class TestClassWithEnum2 < T::Struct; end - class TestOutputClass < T::Struct; end - - class TestOutputClass2 < T::Struct; end - + class TestOutputClassNested < T::Struct; end class UnionTest_ReturnType < T::Struct; end - - class UnionTest_ReturnTypev2 < T::Struct; end - class WithReasoning < T::Struct; end - class Blah < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop4, T.nilable(String) end - - class Blah2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop4, T.nilable(String) - end - - class ClassOptionalFields < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, T.nilable(String) - const :prop2, T.nilable(String) - end - - class ClassOptionalFieldsv2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, T.nilable(String) - const :prop2, T.nilable(String) - end - class ClassOptionalOutput < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop1, String const :prop2, String end - class ClassOptionalOutput2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop1, T.nilable(String) const :prop2, T.nilable(String) const :prop3, T.nilable(Baml::Types::Blah) end - - class ClassOptionalOutput2v2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, T.nilable(String) - const :prop2, T.nilable(String) - const :prop3, T.nilable(Baml::Types::Blah2) - end - class ClassWithImage < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :myImage, Baml::Types::Image + include T::Struct::ActsAsComparable + const :myImage, Baml::Image const :param2, String const :fake_image, Baml::Types::FakeImage end - - class DynamicPropsClass < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, String - const :prop3, Integer + class DynamicClassOne < T::Struct + include T::Struct::ActsAsComparable + end + class DynamicClassTwo < T::Struct + include T::Struct::ActsAsComparable + const :hi, String + const :some_class, Baml::Types::SomeClassNestedDynamic + const :status, Baml::Types::DynEnumOne + end + class DynamicOutput < T::Struct + include T::Struct::ActsAsComparable + end + class Education < T::Struct + include T::Struct::ActsAsComparable + const :institution, String + const :location, String + const :degree, String + const :major, T::Array[String] + const :graduation_date, T.nilable(String) end - class Email < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :subject, String const :body, String const :from_address, String end - class Event < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :title, String const :date, String const :location, String const :description, String end - class FakeImage < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :url, String end - - class ModifiedOutput < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :reasoning, String - const :answer, String - end - class NamedArgsSingleClass < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :key, String const :key_two, T::Boolean const :key_three, Integer end - - class NamedArgsSingleClass2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :key, String - const :key_two, T::Boolean - const :key_three, Integer - end - - class NamedArgsSingleClassList2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :key, String - const :key_two, T::Boolean - const :key_three, Integer - end - - class OptionalClass < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, String - end - - class OptionalClassv2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, String - end - class OptionalTest_Prop1 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :omega_a, String const :omega_b, Integer end - - class OptionalTest_Prop1v2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :omega_a, String - const :omega_b, Integer - end - class OptionalTest_ReturnType < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :omega_1, T.nilable(Baml::Types::OptionalTest_Prop1) const :omega_2, T.nilable(String) const :omega_3, T::Array[T.nilable(Baml::Types::OptionalTest_CategoryType)] end - - class OptionalTest_ReturnTypev2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :omega_1, T.nilable(Baml::Types::OptionalTest_Prop1v2) - const :omega_2, T.nilable(String) - const :omega_3, T::Array[T.nilable(Baml::Types::OptionalTest_CategoryTypev2)] - end - class OrderInfo < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :order_status, Baml::Types::OrderStatus const :tracking_number, T.nilable(String) const :estimated_arrival_date, T.nilable(String) end - - class OverrideClass < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, String + class Person < T::Struct + include T::Struct::ActsAsComparable + const :name, T.nilable(String) + const :hair_color, T.nilable(Baml::Types::Color) end - class RaysData < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :dataType, Baml::Types::DataType const :value, T.any(Baml::Types::Resume, Baml::Types::Event) end - class Resume < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :name, String const :email, String const :phone, String - const :experience, T::Array[String] + const :experience, T::Array[Baml::Types::Education] const :education, T::Array[String] const :skills, T::Array[String] end - class SearchParams < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :dateRange, T.nilable(Integer) const :location, T::Array[String] const :jobTitle, T.nilable(Baml::Types::WithReasoning) @@ -474,100 +266,44 @@ class SearchParams < T::Struct const :description, T::Array[Baml::Types::WithReasoning] const :tags, T::Array[T.any(Baml::Types::Tag, String)] end - - class SomeClass2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, String + class SomeClassNestedDynamic < T::Struct + include T::Struct::ActsAsComparable + const :hi, String end - class TestClassAlias < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :key, String const :key2, String const :key3, String const :key4, String const :key5, String end - class TestClassWithEnum < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop1, String const :prop2, Baml::Types::EnumInClass end - - class TestClassWithEnum2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, String - const :prop2, Baml::Types::EnumInClass - end - class TestOutputClass < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop1, String const :prop2, Integer end - - class TestOutputClass2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + class TestOutputClassNested < T::Struct + include T::Struct::ActsAsComparable const :prop1, String const :prop2, Integer + const :prop3, Baml::Types::TestOutputClass end - class UnionTest_ReturnType < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end - const :prop1, T.any(String, T::Boolean) - const :prop2, T::Array[T.any(Float, T::Boolean)] - const :prop3, T.any(T::Array[Float], T::Array[T::Boolean]) - end - - class UnionTest_ReturnTypev2 < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :prop1, T.any(String, T::Boolean) const :prop2, T::Array[T.any(Float, T::Boolean)] - const :prop3, T.any(T::Array[Float], T::Array[T::Boolean]) + const :prop3, T.any(T::Array[T::Boolean], T::Array[Integer]) end - class WithReasoning < T::Struct - if defined?(T::Struct::ActsAsComparable) - include T::Struct::ActsAsComparable - end + include T::Struct::ActsAsComparable const :value, String const :reasoning, String end end - - module Unstable - class FunctionResult < SimpleDelegator - extend T::Sig - extend T::Generic - - ParsedType = type_member - - sig { returns(ParsedType) } - attr_reader :parsed - - sig { params(inner: Baml::Ffi::FunctionResult, parsed: ParsedType).void } - def initialize(inner:, parsed:) - @inner = inner - @parsed = parsed - end - end - end end \ No newline at end of file diff --git a/integ-tests/ruby/test.rb b/integ-tests/ruby/test.rb deleted file mode 100644 index 3ed234e7a..000000000 --- a/integ-tests/ruby/test.rb +++ /dev/null @@ -1,58 +0,0 @@ -require_relative "baml_client/client" - -b = Baml::BamlClient.from_directory("integ-tests/baml_src") - -puts "Example 0: round-tripping data (nullable is object)" -input = Baml::Types::RoundtripObject.new( - my_int: 4, - my_float: 3.14, - my_string: "hello", - greek_letter: Baml::Types::GreekLetter::ALPHA, - nullable: Baml::Types::FizzBuzz.new(beer: "IPA", wine: "merlot"), - string_list: ["a", "b", "c"], - primitive_union: 4, - object_union: [Baml::Types::ChoiceBar.new( - bar: "hello", - hebrew_letter: Baml::Types::HebrewLetter::ALEPH, - )] -) -output = b.RoundtripMyData(input: input) -pp output -puts - -puts "Example 1: round-tripping data (nullable is string)" -input = Baml::Types::RoundtripObject.new( - my_int: 4, - my_float: 3.14, - my_string: "hello", - greek_letter: Baml::Types::GreekLetter::ALPHA, - #nullable: Baml::Types::FizzBuzz.new(beer: "IPA", wine: "merlot"), - nullable: "hello", - string_list: ["a", "b", "c"], - primitive_union: 4, - object_union: [Baml::Types::ChoiceBar.new( - bar: "hello", - hebrew_letter: Baml::Types::HebrewLetter::ALEPH, - )] -) -output = b.RoundtripMyData(input: input) -pp output -puts - -puts "Example 2: round-tripping data (nullable is null)" -input = Baml::Types::RoundtripObject.new( - my_int: 4, - my_float: 3.14, - my_string: "hello", - greek_letter: Baml::Types::GreekLetter::ALPHA, - nullable: nil, - string_list: ["a", "b", "c"], - primitive_union: 4, - object_union: [Baml::Types::ChoiceBar.new( - bar: "hello", - hebrew_letter: Baml::Types::HebrewLetter::ALEPH, - )] -) -output = b.RoundtripMyData(input: input) -pp output -puts \ No newline at end of file diff --git a/integ-tests/ruby/test_functions.rb b/integ-tests/ruby/test_functions.rb new file mode 100644 index 000000000..23159d155 --- /dev/null +++ b/integ-tests/ruby/test_functions.rb @@ -0,0 +1,149 @@ +require 'minitest/autorun' +require 'minitest/reporters' + +require_relative "baml_client/client" + + +b = Baml.Client + +describe "ruby<->baml integration tests" do + it "works with all inputs" do + res = b.TestFnNamedArgsSingleBool(myBool: true) + assert_equal res, "true" + + res = b.TestFnNamedArgsSingleStringList(myArg: ["a", "b", "c"]) + assert_includes res, "a" + assert_includes res, "b" + assert_includes res, "c" + + res = b.TestFnNamedArgsSingleClass( + myArg: Baml::Types::NamedArgsSingleClass.new( + key: "key", + key_two: true, + key_three: 52, + ) + ) + assert_includes res, "52" + + res = b.TestMulticlassNamedArgs( + myArg: Baml::Types::NamedArgsSingleClass.new( + key: "key", + key_two: true, + key_three: 52, + ), + myArg2: Baml::Types::NamedArgsSingleClass.new( + key: "key", + key_two: true, + key_three: 64, + ), + ) + assert_includes res, "52" + assert_includes res, "64" + + res = b.TestFnNamedArgsSingleEnumList(myArg: [Baml::Types::NamedArgsSingleEnumList::TWO]) + assert_includes res, "TWO" + + res = b.TestFnNamedArgsSingleFloat(myFloat: 3.12) + assert_includes res, "3.12" + + res = b.TestFnNamedArgsSingleInt(myInt: 3566) + assert_includes res, "3566" + end + + it "accepts subclass of baml type" do + # no-op- T::Struct cannot be subclassed + end + + it "works with all outputs" do + res = b.FnOutputBool(input: "a") + assert_equal true, res + + list = b.FnOutputClassList(input: "a") + assert list.size > 0 + assert list.first.prop1.size > 0 + + classWEnum = b.FnOutputClassWithEnum(input: "a") + assert classWEnum.prop2.serialize, "prop2" + assert_includes ["ONE", "TWO"], classWEnum.prop2.serialize + + classs = b.FnOutputClass(input: "a") + refute_nil classs.prop1 + assert_equal 540, classs.prop2 + + enumList = b.FnEnumListOutput(input: "a") + assert_equal 2, enumList.size + + myEnum = b.FnEnumOutput(input: "a") + refute_nil myEnum + end + + #it "should work with image" do + # res = b.TestImageInput( + # img: Baml::Image.from_url("https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png") + # ) + # assert_includes res.downcase, "green" + #end + + it "works with unions" do + res = b.UnionTest_Function(input: "a") + assert_includes res.inspect, "prop1" + assert_includes res.inspect, "prop2" + assert_includes res.inspect, "prop3" + end + + it "works with retries" do + assert_raises Exception do + # calls a client that doesn't set api key correctly + b.TestRetryExponential() + end + end + + it "works with fallbacks" do + res = b.TestFallbackClient() + assert res.size > 0 + end + + it "allows calling claude" do + res = b.PromptTestClaude(input: "Mt Rainier is tall") + assert res.size > 0 + end + + it "allows streaming" do + stream = b.stream.PromptTestOpenAI(input: "Programming languages are fun to create") + msgs = [] + stream.each do |msg| + msgs << msg + end + final = stream.get_final_response + + assert final.size > 0, "Expected non-empty final but got empty." + assert msgs.size > 0, "Expected at least one streamed response but got none." + + msgs.each_cons(2) do |prev_msg, msg| + assert msg.start_with?(prev_msg), "Expected messages to be continuous, but prev was #{prev_msg} and next was #{msg}" + end + assert msgs.last == final, "Expected last stream message to match final response." + end + + it "allows uniterated streaming" do + final = b.stream.PromptTestOpenAI(input: "The color blue makes me sad").get_final_response + assert final.size > 0, "Expected non-empty final but got empty." + end + + it "allows streaming with claude" do + stream = b.stream.PromptTestClaude(input: "Mt Rainier is tall") + msgs = [] + stream.each do |msg| + msgs << msg + end + final = stream.get_final_response + + assert final.size > 0, "Expected non-empty final but got empty." + assert msgs.size > 0, "Expected at least one streamed response but got none." + + msgs.each_cons(2) do |prev_msg, msg| + assert msg.start_with?(prev_msg), "Expected messages to be continuous, but prev was #{prev_msg} and next was #{msg}" + end + assert msgs.last == final, "Expected last stream message to match final response." + end +end diff --git a/integ-tests/typescript/baml_client/client.ts b/integ-tests/typescript/baml_client/client.ts index dc0f14741..f65f0db37 100644 --- a/integ-tests/typescript/baml_client/client.ts +++ b/integ-tests/typescript/baml_client/client.ts @@ -182,13 +182,13 @@ export class BamlClient { } async ExtractResume( - resume: string,img: Image, + resume: string,img?: Image | null, __baml_options__?: { tb?: TypeBuilder } ): Promise { const raw = await this.runtime.callFunction( "ExtractResume", { - "resume": resume,"img": img + "resume": resume,"img": img?? null }, this.ctx_manager.get(), __baml_options__?.tb?.__tb(), @@ -1038,13 +1038,13 @@ class BamlStreamClient { } ExtractResume( - resume: string,img: Image, + resume: string,img?: Image | null, __baml_options__?: { tb?: TypeBuilder } ): BamlStream<(Partial | null), Resume> { const raw = this.runtime.streamFunction( "ExtractResume", { - "resume": resume,"img": img + "resume": resume,"img": img ?? null }, undefined, this.ctx_manager.get(), diff --git a/integ-tests/typescript/baml_client/inlinedbaml.ts b/integ-tests/typescript/baml_client/inlinedbaml.ts index 32810307e..1b53c31ec 100644 --- a/integ-tests/typescript/baml_client/inlinedbaml.ts +++ b/integ-tests/typescript/baml_client/inlinedbaml.ts @@ -24,7 +24,7 @@ const fileMap = { "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/images/image.baml": "function DescribeImage(img: image) -> string {\n client GPT4Turbo\n prompt #\"\n {{ _.role(\"user\") }}\n\n\n Describe the image below in 5 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}", "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", + "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\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", "test-files/aliases/enums.baml": "enum TestEnum {\n A @alias(\"k1\") @description(#\"\n User is angry\n \"#)\n B @alias(\"k22\") @description(#\"\n User is happy\n \"#)\n // tests whether k1 doesnt incorrectly get matched with k11\n C @alias(\"k11\") @description(#\"\n User is sad\n \"#)\n D @alias(\"k44\") @description(\n User is confused\n )\n E @description(\n User is excited\n )\n F @alias(\"k5\") // only alias\n \n G @alias(\"k6\") @description(#\"\n User is bored\n With a long description\n \"#)\n \n @@alias(\"Category\")\n}\n\nfunction FnTestAliasedEnumOutput(input: string) -> TestEnum {\n client GPT35\n prompt #\"\n Classify the user input into the following category\n \n {{ ctx.output_format }}\n\n {{ _.role('user') }}\n {{input}}\n\n {{ _.role('assistant') }}\n Category ID:\n \"#\n}\n\ntest FnTestAliasedEnumOutput {\n functions [FnTestAliasedEnumOutput]\n args {\n input \"mehhhhh\"\n }\n}", "test-files/comments/comments.baml": "// add some functions, classes, enums etc with comments all over.", @@ -52,7 +52,7 @@ const fileMap = { "test-files/functions/output/optional-class.baml": "class ClassOptionalOutput {\n prop1 string\n prop2 string\n}\n\nfunction FnClassOptionalOutput(input: string) -> ClassOptionalOutput? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\n\nclass Blah {\n prop4 string?\n}\n\nclass ClassOptionalOutput2 {\n prop1 string?\n prop2 string?\n prop3 Blah?\n}\n\nfunction FnClassOptionalOutput2(input: string) -> ClassOptionalOutput2? {\n client GPT35\n prompt #\"\n Return a json blob for the following input:\n {{input}}\n\n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest FnClassOptionalOutput2 {\n functions [FnClassOptionalOutput2, FnClassOptionalOutput]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/optional.baml": "class OptionalTest_Prop1 {\n omega_a string\n omega_b int\n}\n\nenum OptionalTest_CategoryType {\n Aleph\n Beta\n Gamma\n}\n \nclass OptionalTest_ReturnType {\n omega_1 OptionalTest_Prop1?\n omega_2 string?\n omega_3 (OptionalTest_CategoryType?)[]\n} \n \nfunction OptionalTest_Function(input: string) -> (OptionalTest_ReturnType?)[]\n{\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest OptionalTest_Function {\n functions [OptionalTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/output/string-list.baml": "function FnOutputStringList(input: string) -> string[] {\n client GPT35\n prompt #\"\n Return a list of strings in json format like [\"string1\", \"string2\", \"string3\"].\n\n JSON:\n \"#\n}\n\ntest FnOutputStringList {\n functions [FnOutputStringList]\n args {\n input \"example input\"\n }\n}\n", - "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (float[] | bool[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", + "test-files/functions/output/unions.baml": "class UnionTest_ReturnType {\n prop1 string | bool\n prop2 (float | bool)[]\n prop3 (bool[] | int[])\n}\n\nfunction UnionTest_Function(input: string | bool) -> UnionTest_ReturnType {\n client GPT35\n prompt #\"\n Return a JSON blob with this schema: \n {{ctx.output_format}}\n\n JSON:\n \"#\n}\n\ntest UnionTest_Function {\n functions [UnionTest_Function]\n args {\n input \"example input\"\n }\n}\n", "test-files/functions/prompts/no-chat-messages.baml": "\n\nfunction PromptTestClaude(input: string) -> string {\n client Claude\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAI(input: string) -> string {\n client GPT35\n prompt #\"\n Tell me a haiku about {{ input }}\n \"#\n}", "test-files/functions/prompts/with-chat-messages.baml": "\nfunction PromptTestOpenAIChat(input: string) -> string {\n client GPT35\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestOpenAIChatNoSystem(input: string) -> string {\n client GPT35\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChat(input: string) -> string {\n client Claude\n prompt #\"\n {{ _.role(\"system\") }}\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\nfunction PromptTestClaudeChatNoSystem(input: string) -> string {\n client Claude\n prompt #\"\n You are an assistant that always responds in a very excited way with emojis and also outputs this word 4 times after giving a response: {{ input }}\n \n {{ _.role(\"user\") }}\n Tell me a haiku about {{ input }}\n \"#\n}\n\ntest PromptTestOpenAIChat {\n functions [PromptTestClaude, PromptTestOpenAI, PromptTestOpenAIChat, PromptTestOpenAIChatNoSystem, PromptTestClaudeChat, PromptTestClaudeChatNoSystem]\n args {\n input \"cats\"\n }\n}\n\ntest TestClaude {\n functions [PromptTestClaudeChatNoSystem]\n args {\n input \"lion\"\n }\n}", "test-files/functions/v2/basic.baml": "\n\nfunction ExtractResume2(resume: string) -> Resume {\n client GPT4\n prompt #\"\n {{ _.role('system') }}\n\n Extract the following information from the resume:\n\n Resume:\n <<<<\n {{ resume }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}\n\n\nclass WithReasoning {\n value string\n reasoning string @description(#\"\n Why the value is a good fit.\n \"#)\n}\n\n\nclass SearchParams {\n dateRange int? @description(#\"\n In ISO duration format, e.g. P1Y2M10D.\n \"#)\n location string[]\n jobTitle WithReasoning? @description(#\"\n An exact job title, not a general category.\n \"#)\n company WithReasoning? @description(#\"\n The exact name of the company, not a product or service.\n \"#)\n description WithReasoning[] @description(#\"\n Any specific projects or features the user is looking for.\n \"#)\n tags (Tag | string)[]\n}\n\nenum Tag {\n Security\n AI\n Blockchain\n}\n\nfunction GetQuery(query: string) -> SearchParams {\n client GPT4\n prompt #\"\n Extract the following information from the query:\n\n Query:\n <<<<\n {{ query }}\n <<<<\n\n OUTPUT_JSON_SCHEMA:\n {{ ctx.output_format }}\n\n Before OUTPUT_JSON_SCHEMA, list 5 intentions the user may have.\n --- EXAMPLES ---\n 1. \n 2. \n 3. \n 4. \n 5. \n\n {\n ... // OUTPUT_JSON_SCHEMA\n }\n \"#\n}\n\nclass RaysData {\n dataType DataType\n value Resume | Event\n}\n\nenum DataType {\n Resume\n Event\n}\n\nclass Event {\n title string\n date string\n location string\n description string\n}\n\nfunction GetDataType(text: string) -> RaysData {\n client GPT4\n prompt #\"\n Extract the relevant info.\n\n Text:\n <<<<\n {{ text }}\n <<<<\n\n Output JSON schema:\n {{ ctx.output_format }}\n\n JSON:\n \"#\n}", @@ -60,7 +60,7 @@ const fileMap = { "test-files/strategies/fallback.baml": "\nclient FaultyClient {\n provider openai\n options {\n model unknown-model\n api_key env.OPENAI_API_KEY\n }\n}\n\n\nclient FallbackClient {\n provider fallback\n options {\n // first 2 clients are expected to fail.\n strategy [\n FaultyClient,\n RetryClientConstant,\n GPT35\n ]\n }\n}\n\nfunction TestFallbackClient() -> string {\n client FallbackClient\n // TODO make it return the client name instead\n prompt #\"\n Say a haiku about mexico.\n \"#\n}", "test-files/strategies/retry.baml": "\nretry_policy Exponential {\n max_retries 3\n strategy {\n type exponential_backoff\n }\n}\n\nretry_policy Constant {\n max_retries 3\n strategy {\n type constant_delay\n delay_ms 100\n }\n}\n\nclient RetryClientConstant {\n provider openai\n retry_policy Constant\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blah\"\n }\n}\n\nclient RetryClientExponential {\n provider openai\n retry_policy Exponential\n options {\n model \"gpt-3.5-turbo\"\n api_key \"blahh\"\n }\n}\n\nfunction TestRetryConstant() -> string {\n client RetryClientConstant\n prompt #\"\n Say a haiku\n \"#\n}\n\nfunction TestRetryExponential() -> string {\n client RetryClientExponential\n prompt #\"\n Say a haiku\n \"#\n}\n", "test-files/strategies/roundrobin.baml": "", - "test-files/testing_pipeline/resume.baml": "class Resume {\n name string\n email string\n phone string\n experience Education[]\n education string[]\n skills string[]\n}\n\nclass Education {\n institution string\n location string\n degree string\n major string[]\n graduation_date string?\n}\n\ntemplate_string AddRole(foo: string) #\"\n {{ _.role('system')}}\n You are a {{ foo }}. be nice\n\n {{ _.role('user') }}\n\"#\n\nfunction ExtractResume(resume: string, img: image) -> Resume {\n client GPT4\n prompt #\"\n {{ AddRole(\"Software Engineer\") }}\n\n Extract data:\n \n\n <<<<\n {{ resume }}\n <<<<\n\n {% if img %}\n {{img}}\n {% endif %}\n\n {{ ctx.output_format }}\n \"#\n}\n\ntest sam_resume {\n functions [ExtractResume]\n input {\n img {\n url \"https://avatars.githubusercontent.com/u/1016595?v=4\"\n }\n resume #\"\n Sam Lijin\n he/him | jobs@sxlijin.com | sxlijin.github.io | sxlijin | sxlijin\n\n Experience\n Trunk\n | July 2021 - current\n Trunk Check | Senior Software Engineer | Services TL, Mar 2023 - current | IC, July 2021 - Feb 2023\n Proposed, designed, and led a team of 3 to build a web experience for Check (both a web-only onboarding flow and SaaS offerings)\n Proposed and built vulnerability scanning into Check, enabling it to compete with security products such as Snyk\n Helped grow Check from <1K users to 90K+ users by focusing on product-led growth\n Google | Sept 2017 - June 2021\n User Identity SRE | Senior Software Engineer | IC, Mar 2021 - June 2021\n Designed an incremental key rotation system to limit the global outage risk to Google SSO\n Discovered and severed an undocumented Gmail serving dependency on Identity-internal systems\n Cloud Firestore | Senior Software Engineer | EngProd TL, Aug 2019 - Feb 2021 | IC, Sept 2017 - July 2019\n Metadata TTL system: backlog of XX trillion records, sustained 1M ops/sec, peaking at 3M ops/sec\n\n Designed and implemented a logging system with novel observability and privacy requirements\n Designed and implemented Jepsen-style testing to validate correctness guarantees\n Datastore Migration: zero downtime, xM RPS and xxPB of data over xM customers and 36 datacenters\n\n Designed composite index migration, queue processing migration, progressive rollout, fast rollback, and disk stockout mitigations; implemented transaction log replay, state transitions, and dark launch process\n Designed and implemented end-to-end correctness and performance testing\n Velocity improvements for 60-eng org\n\n Proposed and implemented automated rollbacks: got us out of a 3-month release freeze and prevented 5 outages over the next 6 months\n Proposed and implemented new development and release environments spanning 30+ microservices\n Incident response for API proxy rollback affecting every Google Cloud service\n\n Google App Engine Memcache | Software Engineer | EngProd TL, Apr 2019 - July 2019\n Proposed and led execution of test coverage improvement strategy for a new control plane: reduced rollbacks and ensured strong consistency of a distributed cache serving xxM QPS\n Designed and implemented automated performance regression testing for two critical serving paths\n Used to validate Google-wide rollout of AMD CPUs, by proving a 50p latency delta of <10µs\n Implemented on shared Borg (i.e. vulnerable to noisy neighbors) with <12% variance\n Miscellaneous | Sept 2017 - June 2021\n Redesigned the Noogler training on Google-internal storage technologies & trained 2500+ Nooglers\n Landed multiple google3-wide refactorings, each spanning xxK files (e.g. SWIG to CLIF)\n Education\n Vanderbilt University (Nashville, TN) | May 2017 | B.S. in Computer Science, Mathematics, and Political Science\n\n Stuyvesant HS (New York, NY) | 2013\n\n Skills\n C++, Java, Typescript, Javascript, Python, Bash; light experience with Rust, Golang, Scheme\n gRPC, Bazel, React, Linux\n Hobbies: climbing, skiing, photography\n \"#\n }\n}\n\ntest vaibhav_resume {\n functions [ExtractResume]\n input {\n resume #\"\n Vaibhav Gupta\n linkedin/vaigup\n (972) 400-5279\n vaibhavtheory@gmail.com\n EXPERIENCE\n Google,\n Software Engineer\n Dec 2018-Present\n Seattle, WA\n •\n Augmented Reality,\n Depth Team\n •\n Technical Lead for on-device optimizations\n •\n Optimized and designed front\n facing depth algorithm\n on Pixel 4\n •\n Focus: C++ and SIMD on custom silicon\n \n \n EDUCATION\n University of Texas at Austin\n Aug 2012-May 2015\n Bachelors of Engineering, Integrated Circuits\n Bachelors of Computer Science\n \"#\n }\n}", + "test-files/testing_pipeline/resume.baml": "class Resume {\n name string\n email string\n phone string\n experience Education[]\n education string[]\n skills string[]\n}\n\nclass Education {\n institution string\n location string\n degree string\n major string[]\n graduation_date string?\n}\n\ntemplate_string AddRole(foo: string) #\"\n {{ _.role('system')}}\n You are a {{ foo }}. be nice\n\n {{ _.role('user') }}\n\"#\n\nfunction ExtractResume(resume: string, img: image?) -> Resume {\n client GPT35\n prompt #\"\n {{ AddRole(\"Software Engineer\") }}\n\n Extract data:\n \n\n <<<<\n {{ resume }}\n <<<<\n\n {% if img %}\n {{img}}\n {% endif %}\n\n {{ ctx.output_format }}\n \"#\n}\n\ntest sam_resume {\n functions [ExtractResume]\n input {\n img {\n url \"https://avatars.githubusercontent.com/u/1016595?v=4\"\n }\n resume #\"\n Sam Lijin\n he/him | jobs@sxlijin.com | sxlijin.github.io | 111-222-3333 | sxlijin | sxlijin\n\n Experience\n Trunk\n | July 2021 - current\n Trunk Check | Senior Software Engineer | Services TL, Mar 2023 - current | IC, July 2021 - Feb 2023\n Proposed, designed, and led a team of 3 to build a web experience for Check (both a web-only onboarding flow and SaaS offerings)\n Proposed and built vulnerability scanning into Check, enabling it to compete with security products such as Snyk\n Helped grow Check from <1K users to 90K+ users by focusing on product-led growth\n Google | Sept 2017 - June 2021\n User Identity SRE | Senior Software Engineer | IC, Mar 2021 - June 2021\n Designed an incremental key rotation system to limit the global outage risk to Google SSO\n Discovered and severed an undocumented Gmail serving dependency on Identity-internal systems\n Cloud Firestore | Senior Software Engineer | EngProd TL, Aug 2019 - Feb 2021 | IC, Sept 2017 - July 2019\n Metadata TTL system: backlog of XX trillion records, sustained 1M ops/sec, peaking at 3M ops/sec\n\n Designed and implemented a logging system with novel observability and privacy requirements\n Designed and implemented Jepsen-style testing to validate correctness guarantees\n Datastore Migration: zero downtime, xM RPS and xxPB of data over xM customers and 36 datacenters\n\n Designed composite index migration, queue processing migration, progressive rollout, fast rollback, and disk stockout mitigations; implemented transaction log replay, state transitions, and dark launch process\n Designed and implemented end-to-end correctness and performance testing\n Velocity improvements for 60-eng org\n\n Proposed and implemented automated rollbacks: got us out of a 3-month release freeze and prevented 5 outages over the next 6 months\n Proposed and implemented new development and release environments spanning 30+ microservices\n Incident response for API proxy rollback affecting every Google Cloud service\n\n Google App Engine Memcache | Software Engineer | EngProd TL, Apr 2019 - July 2019\n Proposed and led execution of test coverage improvement strategy for a new control plane: reduced rollbacks and ensured strong consistency of a distributed cache serving xxM QPS\n Designed and implemented automated performance regression testing for two critical serving paths\n Used to validate Google-wide rollout of AMD CPUs, by proving a 50p latency delta of <10µs\n Implemented on shared Borg (i.e. vulnerable to noisy neighbors) with <12% variance\n Miscellaneous | Sept 2017 - June 2021\n Redesigned the Noogler training on Google-internal storage technologies & trained 2500+ Nooglers\n Landed multiple google3-wide refactorings, each spanning xxK files (e.g. SWIG to CLIF)\n Education\n Vanderbilt University (Nashville, TN) | May 2017 | B.S. in Computer Science, Mathematics, and Political Science\n\n Stuyvesant HS (New York, NY) | 2013\n\n Skills\n C++, Java, Typescript, Javascript, Python, Bash; light experience with Rust, Golang, Scheme\n gRPC, Bazel, React, Linux\n Hobbies: climbing, skiing, photography\n \"#\n }\n}\n\ntest vaibhav_resume {\n functions [ExtractResume]\n input {\n resume #\"\n Vaibhav Gupta\n linkedin/vaigup\n (972) 400-5279\n vaibhavtheory@gmail.com\n EXPERIENCE\n Google,\n Software Engineer\n Dec 2018-Present\n Seattle, WA\n •\n Augmented Reality,\n Depth Team\n •\n Technical Lead for on-device optimizations\n •\n Optimized and designed front\n facing depth algorithm\n on Pixel 4\n •\n Focus: C++ and SIMD on custom silicon\n \n \n EDUCATION\n University of Texas at Austin\n Aug 2012-May 2015\n Bachelors of Engineering, Integrated Circuits\n Bachelors of Computer Science\n \"#\n }\n}", } export const getBamlFiles = () => { return fileMap; diff --git a/integ-tests/typescript/baml_client/types.ts b/integ-tests/typescript/baml_client/types.ts index 98bcbed00..fb0d6d69e 100644 --- a/integ-tests/typescript/baml_client/types.ts +++ b/integ-tests/typescript/baml_client/types.ts @@ -285,7 +285,7 @@ export interface TestOutputClassNested { export interface UnionTest_ReturnType { prop1: string | boolean prop2: (number | boolean)[] - prop3: number[] | boolean[] + prop3: boolean[] | number[] } diff --git a/integ-tests2/python/main.py b/integ-tests2/python/main.py deleted file mode 100644 index 490880ab1..000000000 --- a/integ-tests2/python/main.py +++ /dev/null @@ -1,33 +0,0 @@ -import asyncio -import time -from baml_client import b -from baml_client.tracing import trace - - -@trace -async def main2(): - # single = await b.ExtractResume(resume="Lee Hsien Loong[a] SPMJ DK (born 10 February 1952) is a Singaporean politician and former brigadier-general who has been serving as Senior Minister of Singapore since 2024, having previously served as Prime Minister of Singapore from 2004 to 2024. He has been the Member of Parliament (MP) for the Teck Ghee division of Ang Mo Kio GRC since 1991, and previously Teck Ghee SMC between 1984 and 1991, as well as Secretary-General of the People's Action Party (PAP) since 2004.") - # print(single) - # return - # retval = await b.stream(lambda d, e, f: print(f"cb arg: {f}")) - print("starting stream") - # stream = b.stream.ExtractResume(resume="Lee Hsien Loong[a] SPMJ DK (born 10 February 1952) is a Singaporean politician and former brigadier-general who has been serving as Senior Minister of Singapore since 2024, having previously served as Prime Minister of Singapore from 2004 to 2024. He has been the Member of Parliament (MP) for the Teck Ghee division of Ang Mo Kio GRC since 1991, and previously Teck Ghee SMC between 1984 and 1991, as well as Secretary-General of the People's Action Party (PAP) since 2004.") - stream = b.stream.ExtractNames( - input="We would like to thank Qianqian Wang, Justin Kerr, Brent Yi, David McAllister, Matthew Tancik, Evonne Ng, Anjali Thakrar, Christian Foley, Abhishek Kar, Georgios Pavlakos, the Nerfstudio team, and the KAIR lab for discussions, feedback, and technical support. We also thank Ian Mitchell and Roland Jose for helping to label points." - ) - - async for event in stream: - print(f"baml-async-for: {event}") - - final_message = await stream.done() - - print("baml-final-message:", final_message) - - -start_time = time.perf_counter() - -asyncio.run(main2()) - -end_time = time.perf_counter() -elapsed_time = end_time - start_time -print(f"Elapsed time: {elapsed_time:.2f} seconds") diff --git a/root.code-workspace b/root.code-workspace index 65fbf334e..948357542 100644 --- a/root.code-workspace +++ b/root.code-workspace @@ -92,7 +92,8 @@ "charliermarsh.ruff", "matangover.mypy", "samuelcolvin.jinjahtml", - "biomejs.biome" + "biomejs.biome", + "boundary.baml-extension" ] } } \ No newline at end of file diff --git a/tools/build b/tools/build index 1b2f6769f..b66d41103 100755 --- a/tools/build +++ b/tools/build @@ -87,12 +87,17 @@ case "$_path" in ;; /engine/language_client_ruby | /engine/language_client_ruby/* ) - #command="rake compile; date" - command="cargo build" + command="RB_SYS_CARGO_PROFILE=dev2 rake compile; date" + + if [ "$_test_mode" -eq 1 ]; then + #command="wasm-pack test --chrome . --features=wasm" + #command="wasm-pack test --node . --features=wasm" + command="${command} && rake test" + fi if [ "$_watch_mode" -eq 1 ]; then npx nodemon \ - --ext rs,hb,hbs,toml,baml,rb \ + --ext rs,j2,toml,baml,rb,gemspec,Gemfile \ --watch "${_repo_root}/engine" \ --ignore 'target' \ --ignore 'tmp' \ @@ -142,7 +147,7 @@ case "$_path" in /engine | /engine/* ) command="" if [ "$_test_mode" -eq 1 ]; then - command="cargo test ${command} jsonish::parser" + command="cargo test ${command}" else command="cargo build ${command}" fi @@ -261,6 +266,24 @@ case "$_path" in fi ;; + /integ-tests/ruby | /integ-tests/ruby/* ) + command="rake generate" + if [ "$_test_mode" -eq 1 ]; then + command="${command} && infisical run --env=dev -- rake test" + fi + if [ "$_watch_mode" -eq 1 ]; then + npx nodemon \ + --ext bundle,rb \ + --watch "${_repo_root}/engine/language_client_ruby/lib/" \ + --watch . \ + --ignore baml_client \ + --exec "${command}" + else + eval "${command}" + date + fi + ;; + /typescript/codemirror-lang-baml | /typescript/codemirror-lang-baml/* ) if [ "$_watch_mode" -eq 1 ]; then npx nodemon \