baml-cc 0.208.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/exe/baml +1 -0
- data/exe/baml-cli +4 -0
- data/ext/ruby_ffi/Cargo.toml +52 -0
- data/ext/ruby_ffi/build.rs +21 -0
- data/ext/ruby_ffi/extconf.rb +17 -0
- data/ext/ruby_ffi/src/function_result.rs +75 -0
- data/ext/ruby_ffi/src/function_result_stream.rs +63 -0
- data/ext/ruby_ffi/src/lib.rs +388 -0
- data/ext/ruby_ffi/src/ruby_to_json.rs +651 -0
- data/ext/ruby_ffi/src/types/client_registry.rs +71 -0
- data/ext/ruby_ffi/src/types/lang_wrapper.rs +60 -0
- data/ext/ruby_ffi/src/types/log_collector.rs +607 -0
- data/ext/ruby_ffi/src/types/media.rs +142 -0
- data/ext/ruby_ffi/src/types/mod.rs +8 -0
- data/ext/ruby_ffi/src/types/request.rs +107 -0
- data/ext/ruby_ffi/src/types/response.rs +48 -0
- data/ext/ruby_ffi/src/types/runtime_ctx_manager.rs +18 -0
- data/ext/ruby_ffi/src/types/type_builder.rs +334 -0
- data/lib/baml.rb +63 -0
- data/lib/checked.rb +36 -0
- data/lib/stream.rb +87 -0
- data/lib/struct.rb +66 -0
- metadata +69 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '03252379da1ea189a52782459ad23fa7ab7c90497788cfd81f13022510f9681f'
|
|
4
|
+
data.tar.gz: 4aeec84a0a78c3b15b60251e1b90bbafd5502284d900a779c4607677303f5b38
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b53db0c22a6f99c5733247b0f3e36a90d1e13333a3d51333cd1f577f156852e6483078062e396d0d3698779b713a4b377103f8cebfdcac9138d0b9804fc7ffbe
|
|
7
|
+
data.tar.gz: aa15eedabb9dbc1454c490665edc7b6912c92af824063e94b28d267094f24a3c0534a8211ebf66e184b386879d66bcd5927c67a14617b124b24b9c0e896bd4f1
|
data/exe/baml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
baml-cli
|
data/exe/baml-cli
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "ruby_ffi"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
publish = false
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
# This controls the name of the generated .so file
|
|
9
|
+
crate-type = ["cdylib"]
|
|
10
|
+
|
|
11
|
+
[lints.clippy]
|
|
12
|
+
too_many_arguments = "allow"
|
|
13
|
+
|
|
14
|
+
[dependencies]
|
|
15
|
+
anyhow.workspace = true
|
|
16
|
+
baml-cli.workspace = true
|
|
17
|
+
baml-ids.workspace = true
|
|
18
|
+
baml-log.workspace = true
|
|
19
|
+
baml-runtime = { path = "../../../baml-runtime", features = ["internal"] }
|
|
20
|
+
baml-types.workspace = true
|
|
21
|
+
base64.workspace = true
|
|
22
|
+
env_logger.workspace = true
|
|
23
|
+
futures.workspace = true
|
|
24
|
+
indexmap.workspace = true
|
|
25
|
+
internal-baml-core.workspace = true
|
|
26
|
+
jsonish = { path = "../../../baml-lib/jsonish" }
|
|
27
|
+
log.workspace = true
|
|
28
|
+
magnus = { version = "0.7.1", features = ["rb-sys"] }
|
|
29
|
+
# Must be kept in sync with ../../Gemfile
|
|
30
|
+
rb-sys = { version = "0.9.111", features = [
|
|
31
|
+
"global-allocator",
|
|
32
|
+
"bindgen-rbimpls",
|
|
33
|
+
"bindgen-deprecated-types",
|
|
34
|
+
"stable-api",
|
|
35
|
+
"stable-api-compiled-fallback",
|
|
36
|
+
] }
|
|
37
|
+
serde.workspace = true
|
|
38
|
+
serde_json.workspace = true
|
|
39
|
+
serde_magnus = { version = "0.9.0" }
|
|
40
|
+
tokio = { version = "1", features = ["full"] }
|
|
41
|
+
tracing-subscriber = { version = "0.3.18", features = [
|
|
42
|
+
"json",
|
|
43
|
+
"env-filter",
|
|
44
|
+
"valuable",
|
|
45
|
+
] }
|
|
46
|
+
|
|
47
|
+
[dev-dependencies.magnus]
|
|
48
|
+
version = "0.7.1"
|
|
49
|
+
features = ["rb-sys", "embed"]
|
|
50
|
+
|
|
51
|
+
[build-dependencies]
|
|
52
|
+
rb-sys-env = { version = "0.2.2" }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
use std::process::Command;
|
|
2
|
+
|
|
3
|
+
pub fn main() {
|
|
4
|
+
// This is derived from the rustc commands that 'rake compile' runs, e.g.
|
|
5
|
+
//
|
|
6
|
+
// cargo rustc --package ruby_ffi --manifest-path /Users/sam/baml/engine/language_client_ruby/ext/ruby_ffi/Cargo.toml --target-dir /Users/sam/baml/engine/target --lib --profile release -- -C linker=clang -L native=/Users/sam/.local/share/mise/installs/ruby/3.1.6/lib -L native=/opt/homebrew/opt/gmp/lib -C link-arg=-Wl,-undefined,dynamic_lookup
|
|
7
|
+
//
|
|
8
|
+
// You need to run 'rake compile' from language_client_ruby itself to see these paths.
|
|
9
|
+
|
|
10
|
+
match Command::new("mise").args(["where", "ruby"]).output() {
|
|
11
|
+
Ok(output) => {
|
|
12
|
+
let ruby_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
13
|
+
println!("cargo:rustc-link-search=native={ruby_path}/lib");
|
|
14
|
+
}
|
|
15
|
+
Err(e) => {
|
|
16
|
+
println!("cargo:rustc-warning=Failed to execute 'mise where ruby': {e}");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
println!("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup");
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "mkmf"
|
|
2
|
+
require "rb_sys/mkmf"
|
|
3
|
+
|
|
4
|
+
# no idea what create_rust_makefile actually takes as its arg, i thought it did
|
|
5
|
+
# the below, but i don't think that's right anymore:
|
|
6
|
+
#
|
|
7
|
+
# create_rust_makefile takes, as its argument, ${CRATE_NAME}/${GEM_NAME} where:
|
|
8
|
+
#
|
|
9
|
+
# - CRATE_NAME is the name of the crate in ext/${GEM_NAME}/Cargo.toml
|
|
10
|
+
# - GEM_NAME is the name of the gem in ext/baml
|
|
11
|
+
create_rust_makefile("ruby_ffi") do |r|
|
|
12
|
+
r.extra_cargo_args += ["--package", "ruby_ffi"]
|
|
13
|
+
r.extra_rustflags = ["--cfg=tracing_unstable"]
|
|
14
|
+
r.env = {
|
|
15
|
+
"MACOSX_DEPLOYMENT_TARGET" => "10.13",
|
|
16
|
+
}
|
|
17
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
use magnus::{
|
|
2
|
+
class, exception::runtime_error, method, prelude::*, value::Value, Error, RModule, Ruby,
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
use crate::{ruby_to_json, Result};
|
|
6
|
+
|
|
7
|
+
#[magnus::wrap(class = "Baml::Ffi::FunctionResult", free_immediately, size)]
|
|
8
|
+
pub struct FunctionResult {
|
|
9
|
+
inner: baml_runtime::FunctionResult,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
impl FunctionResult {
|
|
13
|
+
pub fn new(inner: baml_runtime::FunctionResult) -> Self {
|
|
14
|
+
Self { inner }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[allow(dead_code)]
|
|
18
|
+
fn to_s(&self) -> String {
|
|
19
|
+
format!("{}", self.inner)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[allow(dead_code)]
|
|
23
|
+
fn raw(&self) -> Result<String> {
|
|
24
|
+
match self.inner.content() {
|
|
25
|
+
Ok(content) => Ok(content.to_string()),
|
|
26
|
+
Err(_) => Err(Error::new(
|
|
27
|
+
runtime_error(),
|
|
28
|
+
format!("No LLM response: {}", self.inner),
|
|
29
|
+
)),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub fn parsed_using_types(
|
|
34
|
+
ruby: &Ruby,
|
|
35
|
+
rb_self: &FunctionResult,
|
|
36
|
+
types: RModule,
|
|
37
|
+
partial_types: RModule,
|
|
38
|
+
allow_partials: bool,
|
|
39
|
+
) -> Result<Value> {
|
|
40
|
+
let res = match rb_self.inner.result_with_constraints_content() {
|
|
41
|
+
Ok(parsed) => ruby_to_json::RubyToJson::serialize_baml(
|
|
42
|
+
ruby,
|
|
43
|
+
types,
|
|
44
|
+
partial_types,
|
|
45
|
+
allow_partials,
|
|
46
|
+
parsed.clone(),
|
|
47
|
+
)
|
|
48
|
+
.map_err(|e| {
|
|
49
|
+
magnus::Error::new(
|
|
50
|
+
ruby.exception_type_error(),
|
|
51
|
+
format!("failing inside parsed_using_types: {e:?}"),
|
|
52
|
+
)
|
|
53
|
+
}),
|
|
54
|
+
Err(_) => Err(Error::new(
|
|
55
|
+
ruby.exception_runtime_error(),
|
|
56
|
+
format!("Failed to parse LLM response: {}", rb_self.inner),
|
|
57
|
+
)),
|
|
58
|
+
};
|
|
59
|
+
res
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// For usage in magnus::init
|
|
63
|
+
///
|
|
64
|
+
/// TODO: use traits and macros to implement this
|
|
65
|
+
pub fn define_in_ruby(module: &RModule) -> Result<()> {
|
|
66
|
+
let cls = module.define_class("FunctionResult", class::object())?;
|
|
67
|
+
|
|
68
|
+
cls.define_method(
|
|
69
|
+
"parsed_using_types",
|
|
70
|
+
method!(FunctionResult::parsed_using_types, 3),
|
|
71
|
+
)?;
|
|
72
|
+
|
|
73
|
+
Ok(())
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
|
2
|
+
|
|
3
|
+
use magnus::{class, method, Module, RModule, Ruby};
|
|
4
|
+
|
|
5
|
+
use super::types::runtime_ctx_manager::RuntimeContextManager;
|
|
6
|
+
use crate::{function_result::FunctionResult, Error, Result};
|
|
7
|
+
|
|
8
|
+
#[magnus::wrap(class = "Baml::Ffi::FunctionResultStream", free_immediately, size)]
|
|
9
|
+
pub struct FunctionResultStream {
|
|
10
|
+
inner: RefCell<baml_runtime::FunctionResultStream>,
|
|
11
|
+
t: Arc<tokio::runtime::Runtime>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl FunctionResultStream {
|
|
15
|
+
pub(super) fn new(
|
|
16
|
+
inner: baml_runtime::FunctionResultStream,
|
|
17
|
+
t: Arc<tokio::runtime::Runtime>,
|
|
18
|
+
) -> Self {
|
|
19
|
+
Self {
|
|
20
|
+
inner: RefCell::new(inner),
|
|
21
|
+
t,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn done(
|
|
26
|
+
ruby: &Ruby,
|
|
27
|
+
rb_self: &FunctionResultStream,
|
|
28
|
+
ctx: &RuntimeContextManager,
|
|
29
|
+
) -> Result<FunctionResult> {
|
|
30
|
+
let on_event = if ruby.block_given() {
|
|
31
|
+
let proc = ruby.block_proc()?;
|
|
32
|
+
Some(move |event: baml_runtime::FunctionResult| {
|
|
33
|
+
// ignore errors if they happen
|
|
34
|
+
let _ = proc.call::<_, magnus::Value>((FunctionResult::new(event),));
|
|
35
|
+
})
|
|
36
|
+
} else {
|
|
37
|
+
None
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
match rb_self.t.block_on(rb_self.inner.borrow_mut().run(
|
|
41
|
+
None::<fn()>,
|
|
42
|
+
on_event,
|
|
43
|
+
&ctx.inner,
|
|
44
|
+
None,
|
|
45
|
+
None,
|
|
46
|
+
HashMap::new(),
|
|
47
|
+
)) {
|
|
48
|
+
(Ok(res), _) => Ok(FunctionResult::new(res)),
|
|
49
|
+
(Err(e), _) => Err(Error::new(ruby.exception_runtime_error(), format!("{e:?}"))),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// For usage in magnus::init
|
|
54
|
+
///
|
|
55
|
+
/// TODO: use traits and macros to implement this
|
|
56
|
+
pub fn define_in_ruby(module: &RModule) -> Result<()> {
|
|
57
|
+
let cls = module.define_class("FunctionResultStream", class::object())?;
|
|
58
|
+
|
|
59
|
+
cls.define_method("done", method!(FunctionResultStream::done, 1))?;
|
|
60
|
+
|
|
61
|
+
Ok(())
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
|
2
|
+
|
|
3
|
+
use baml_runtime::{BamlRuntime, TripWire};
|
|
4
|
+
use baml_types::BamlValue;
|
|
5
|
+
use function_result::FunctionResult;
|
|
6
|
+
use function_result_stream::FunctionResultStream;
|
|
7
|
+
use internal_baml_core::feature_flags::FeatureFlags;
|
|
8
|
+
use magnus::{class, function, method, prelude::*, Error, RArray, RHash, RModule, Ruby};
|
|
9
|
+
use types::{
|
|
10
|
+
log_collector::Collector, request::HTTPRequest, runtime_ctx_manager::RuntimeContextManager,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
mod function_result;
|
|
14
|
+
mod function_result_stream;
|
|
15
|
+
mod ruby_to_json;
|
|
16
|
+
mod types;
|
|
17
|
+
|
|
18
|
+
type Result<T> = std::result::Result<T, magnus::Error>;
|
|
19
|
+
|
|
20
|
+
// must be kept in sync with rb.define_class in the init() fn
|
|
21
|
+
#[magnus::wrap(class = "Baml::Ffi::BamlRuntime", free_immediately, size)]
|
|
22
|
+
struct BamlRuntimeFfi {
|
|
23
|
+
inner: Arc<BamlRuntime>,
|
|
24
|
+
t: Arc<tokio::runtime::Runtime>,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl Drop for BamlRuntimeFfi {
|
|
28
|
+
fn drop(&mut self) {
|
|
29
|
+
use baml_runtime::runtime_interface::ExperimentalTracingInterface;
|
|
30
|
+
match self.inner.flush() {
|
|
31
|
+
Ok(_) => log::trace!("Flushed BAML log events"),
|
|
32
|
+
Err(e) => log::error!("Error while flushing BAML log events: {e:?}"),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
impl BamlRuntimeFfi {
|
|
38
|
+
fn make_tokio_runtime(ruby: &Ruby) -> Result<tokio::runtime::Runtime> {
|
|
39
|
+
// NB: libruby will panic if called from a non-Ruby thread, so we stick to the current thread
|
|
40
|
+
// to avoid causing issues
|
|
41
|
+
tokio::runtime::Builder::new_current_thread()
|
|
42
|
+
.enable_all()
|
|
43
|
+
.build()
|
|
44
|
+
.map_err(|e| {
|
|
45
|
+
Error::new(
|
|
46
|
+
ruby.exception_runtime_error(),
|
|
47
|
+
format!("Failed to start tokio runtime because:\n{e:?}"),
|
|
48
|
+
)
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn from_directory(
|
|
53
|
+
ruby: &Ruby,
|
|
54
|
+
directory: PathBuf,
|
|
55
|
+
env_vars: HashMap<String, String>,
|
|
56
|
+
) -> Result<BamlRuntimeFfi> {
|
|
57
|
+
let baml_runtime =
|
|
58
|
+
match BamlRuntime::from_directory(&directory, env_vars, FeatureFlags::new()) {
|
|
59
|
+
Ok(br) => br,
|
|
60
|
+
Err(e) => {
|
|
61
|
+
return Err(Error::new(
|
|
62
|
+
ruby.exception_runtime_error(),
|
|
63
|
+
format!("{:?}", e.context("Failed to initialize BAML runtime")),
|
|
64
|
+
))
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
let rt = BamlRuntimeFfi {
|
|
69
|
+
inner: Arc::new(baml_runtime),
|
|
70
|
+
t: Arc::new(Self::make_tokio_runtime(ruby)?),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
Ok(rt)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn from_files(
|
|
77
|
+
ruby: &Ruby,
|
|
78
|
+
root_path: String,
|
|
79
|
+
files: HashMap<String, String>,
|
|
80
|
+
env_vars: HashMap<String, String>,
|
|
81
|
+
) -> Result<Self> {
|
|
82
|
+
let baml_runtime =
|
|
83
|
+
match BamlRuntime::from_file_content(&root_path, &files, env_vars, FeatureFlags::new())
|
|
84
|
+
{
|
|
85
|
+
Ok(br) => br,
|
|
86
|
+
Err(e) => {
|
|
87
|
+
return Err(Error::new(
|
|
88
|
+
ruby.exception_runtime_error(),
|
|
89
|
+
format!("{:?}", e.context("Failed to initialize BAML runtime")),
|
|
90
|
+
))
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let rt = BamlRuntimeFfi {
|
|
95
|
+
inner: Arc::new(baml_runtime),
|
|
96
|
+
t: Arc::new(Self::make_tokio_runtime(ruby)?),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
Ok(rt)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub fn create_context_manager(&self) -> RuntimeContextManager {
|
|
103
|
+
RuntimeContextManager {
|
|
104
|
+
inner: self
|
|
105
|
+
.inner
|
|
106
|
+
.create_ctx_manager(BamlValue::String("ruby".to_string()), None),
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pub fn call_function(
|
|
111
|
+
ruby: &Ruby,
|
|
112
|
+
rb_self: &BamlRuntimeFfi,
|
|
113
|
+
function_name: String,
|
|
114
|
+
args: RHash,
|
|
115
|
+
ctx: &RuntimeContextManager,
|
|
116
|
+
type_registry: Option<&types::type_builder::TypeBuilder>,
|
|
117
|
+
client_registry: Option<&types::client_registry::ClientRegistry>,
|
|
118
|
+
collector: RArray,
|
|
119
|
+
env_vars: HashMap<String, String>,
|
|
120
|
+
) -> Result<FunctionResult> {
|
|
121
|
+
let args = match ruby_to_json::RubyToJson::convert_hash_to_json(args) {
|
|
122
|
+
Ok(args) => args.into_iter().collect(),
|
|
123
|
+
Err(e) => {
|
|
124
|
+
return Err(Error::new(
|
|
125
|
+
ruby.exception_syntax_error(),
|
|
126
|
+
format!("error while parsing call_function args:\n{e}"),
|
|
127
|
+
));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let mut collectors = Vec::new();
|
|
132
|
+
for i in collector.into_iter() {
|
|
133
|
+
collectors.push(<&Collector>::try_convert(i)?.inner.clone());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let retval = match rb_self.t.block_on(rb_self.inner.call_function(
|
|
137
|
+
function_name.clone(),
|
|
138
|
+
&args,
|
|
139
|
+
&ctx.inner,
|
|
140
|
+
type_registry.map(|t| &t.inner),
|
|
141
|
+
client_registry.map(|c| c.inner.borrow_mut()).as_deref(),
|
|
142
|
+
Some(collectors),
|
|
143
|
+
env_vars,
|
|
144
|
+
TripWire::new(None),
|
|
145
|
+
)) {
|
|
146
|
+
(Ok(res), _) => Ok(FunctionResult::new(res)),
|
|
147
|
+
(Err(e), _) => Err(Error::new(
|
|
148
|
+
ruby.exception_runtime_error(),
|
|
149
|
+
format!(
|
|
150
|
+
"{:?}",
|
|
151
|
+
e.context(format!("error while calling {function_name}"))
|
|
152
|
+
),
|
|
153
|
+
)),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
retval
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fn stream_function(
|
|
160
|
+
ruby: &Ruby,
|
|
161
|
+
rb_self: &BamlRuntimeFfi,
|
|
162
|
+
function_name: String,
|
|
163
|
+
args: RHash,
|
|
164
|
+
ctx: &RuntimeContextManager,
|
|
165
|
+
type_registry: Option<&types::type_builder::TypeBuilder>,
|
|
166
|
+
client_registry: Option<&types::client_registry::ClientRegistry>,
|
|
167
|
+
collector: RArray,
|
|
168
|
+
env_vars: HashMap<String, String>,
|
|
169
|
+
) -> Result<FunctionResultStream> {
|
|
170
|
+
let args = match ruby_to_json::RubyToJson::convert_hash_to_json(args) {
|
|
171
|
+
Ok(args) => args.into_iter().collect(),
|
|
172
|
+
Err(e) => {
|
|
173
|
+
return Err(Error::new(
|
|
174
|
+
ruby.exception_syntax_error(),
|
|
175
|
+
format!("error while parsing stream_function args:\n{e}"),
|
|
176
|
+
));
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
let mut collectors = Vec::new();
|
|
181
|
+
for i in collector.into_iter() {
|
|
182
|
+
collectors.push(<&Collector>::try_convert(i)?.inner.clone());
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
log::debug!("Streaming {function_name} with:\nargs: {args:#?}\nctx ???");
|
|
186
|
+
|
|
187
|
+
let retval = match rb_self.inner.stream_function(
|
|
188
|
+
function_name.clone(),
|
|
189
|
+
&args,
|
|
190
|
+
&ctx.inner,
|
|
191
|
+
type_registry.map(|t| &t.inner),
|
|
192
|
+
client_registry.map(|c| c.inner.borrow_mut()).as_deref(),
|
|
193
|
+
Some(collectors),
|
|
194
|
+
env_vars,
|
|
195
|
+
TripWire::new(None),
|
|
196
|
+
) {
|
|
197
|
+
Ok(res) => Ok(FunctionResultStream::new(res, rb_self.t.clone())),
|
|
198
|
+
Err(e) => Err(Error::new(
|
|
199
|
+
ruby.exception_runtime_error(),
|
|
200
|
+
format!(
|
|
201
|
+
"{:?}",
|
|
202
|
+
e.context(format!("error while calling {function_name}"))
|
|
203
|
+
),
|
|
204
|
+
)),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
retval
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pub fn build_request(
|
|
211
|
+
ruby: &Ruby,
|
|
212
|
+
rb_self: &BamlRuntimeFfi,
|
|
213
|
+
function_name: String,
|
|
214
|
+
args: RHash,
|
|
215
|
+
ctx: &RuntimeContextManager,
|
|
216
|
+
type_registry: Option<&types::type_builder::TypeBuilder>,
|
|
217
|
+
client_registry: Option<&types::client_registry::ClientRegistry>,
|
|
218
|
+
env_vars: HashMap<String, String>,
|
|
219
|
+
stream: bool,
|
|
220
|
+
) -> Result<HTTPRequest> {
|
|
221
|
+
let args = match ruby_to_json::RubyToJson::convert_hash_to_json(args) {
|
|
222
|
+
Ok(args) => args.into_iter().collect(),
|
|
223
|
+
Err(e) => {
|
|
224
|
+
return Err(Error::new(
|
|
225
|
+
ruby.exception_syntax_error(),
|
|
226
|
+
format!("error while parsing call_function args:\n{e}"),
|
|
227
|
+
));
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
rb_self
|
|
232
|
+
.inner
|
|
233
|
+
.build_request_sync(
|
|
234
|
+
function_name.clone(),
|
|
235
|
+
&args,
|
|
236
|
+
&ctx.inner,
|
|
237
|
+
type_registry.map(|t| &t.inner),
|
|
238
|
+
client_registry.map(|c| c.inner.borrow_mut()).as_deref(),
|
|
239
|
+
stream,
|
|
240
|
+
env_vars,
|
|
241
|
+
)
|
|
242
|
+
.map(HTTPRequest::from)
|
|
243
|
+
.map_err(|e| {
|
|
244
|
+
Error::new(
|
|
245
|
+
ruby.exception_runtime_error(),
|
|
246
|
+
format!(
|
|
247
|
+
"{:?}",
|
|
248
|
+
e.context(format!(
|
|
249
|
+
"error while building HTTP request for function {function_name}"
|
|
250
|
+
))
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
pub fn parse_llm_response(
|
|
257
|
+
ruby: &Ruby,
|
|
258
|
+
rb_self: &BamlRuntimeFfi,
|
|
259
|
+
function_name: String,
|
|
260
|
+
llm_response: String,
|
|
261
|
+
types: RModule,
|
|
262
|
+
partial_types: RModule,
|
|
263
|
+
allow_partials: bool,
|
|
264
|
+
ctx: &RuntimeContextManager,
|
|
265
|
+
type_registry: Option<&types::type_builder::TypeBuilder>,
|
|
266
|
+
client_registry: Option<&types::client_registry::ClientRegistry>,
|
|
267
|
+
env_vars: HashMap<String, String>,
|
|
268
|
+
) -> Result<magnus::Value> {
|
|
269
|
+
let parsed = rb_self
|
|
270
|
+
.inner
|
|
271
|
+
.parse_llm_response(
|
|
272
|
+
function_name.clone(),
|
|
273
|
+
llm_response,
|
|
274
|
+
allow_partials,
|
|
275
|
+
&ctx.inner,
|
|
276
|
+
type_registry.map(|t| &t.inner),
|
|
277
|
+
client_registry.map(|c| c.inner.borrow_mut()).as_deref(),
|
|
278
|
+
env_vars,
|
|
279
|
+
)
|
|
280
|
+
.map_err(|e| {
|
|
281
|
+
Error::new(
|
|
282
|
+
ruby.exception_runtime_error(),
|
|
283
|
+
format!(
|
|
284
|
+
"{:?}",
|
|
285
|
+
e.context(format!(
|
|
286
|
+
"error while parsing LLM response for function {function_name}"
|
|
287
|
+
))
|
|
288
|
+
),
|
|
289
|
+
)
|
|
290
|
+
})?;
|
|
291
|
+
|
|
292
|
+
ruby_to_json::RubyToJson::serialize_baml(ruby, types, partial_types, allow_partials, parsed)
|
|
293
|
+
.map_err(|e| {
|
|
294
|
+
magnus::Error::new(
|
|
295
|
+
ruby.exception_type_error(),
|
|
296
|
+
format!("failed coercing BAML value to Ruby value: {e:?}"),
|
|
297
|
+
)
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
fn invoke_runtime_cli(ruby: &Ruby, argv0: String, argv: Vec<String>) -> Result<u32> {
|
|
303
|
+
match baml_cli::run_cli(
|
|
304
|
+
std::iter::once(argv0).chain(argv).collect(),
|
|
305
|
+
baml_runtime::RuntimeCliDefaults {
|
|
306
|
+
output_type: baml_types::GeneratorOutputType::RubySorbet,
|
|
307
|
+
},
|
|
308
|
+
) {
|
|
309
|
+
Ok(exit_code) => Ok(exit_code.into()),
|
|
310
|
+
Err(e) => Err(Error::new(
|
|
311
|
+
ruby.exception_runtime_error(),
|
|
312
|
+
format!(
|
|
313
|
+
"{:?}",
|
|
314
|
+
e.context("error while invoking baml-cli".to_string())
|
|
315
|
+
),
|
|
316
|
+
)),
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
#[magnus::init(name = "ruby_ffi")]
|
|
321
|
+
fn init(ruby: &Ruby) -> Result<()> {
|
|
322
|
+
baml_log::init().map_err(|e| {
|
|
323
|
+
Error::new(
|
|
324
|
+
ruby.exception_runtime_error(),
|
|
325
|
+
format!("Failed to initialize BAML logger: {e:#}"),
|
|
326
|
+
)
|
|
327
|
+
})?;
|
|
328
|
+
|
|
329
|
+
let module = ruby.define_module("Baml")?.define_module("Ffi")?;
|
|
330
|
+
|
|
331
|
+
module.define_module_function("invoke_runtime_cli", function!(invoke_runtime_cli, 2))?;
|
|
332
|
+
|
|
333
|
+
// must be kept in sync with the magnus::wrap annotation
|
|
334
|
+
let runtime_class = module.define_class("BamlRuntime", class::object())?;
|
|
335
|
+
runtime_class.define_singleton_method(
|
|
336
|
+
"from_directory",
|
|
337
|
+
function!(BamlRuntimeFfi::from_directory, 2),
|
|
338
|
+
)?;
|
|
339
|
+
runtime_class
|
|
340
|
+
.define_singleton_method("from_files", function!(BamlRuntimeFfi::from_files, 3))?;
|
|
341
|
+
runtime_class.define_method(
|
|
342
|
+
"create_context_manager",
|
|
343
|
+
method!(BamlRuntimeFfi::create_context_manager, 0),
|
|
344
|
+
)?;
|
|
345
|
+
runtime_class.define_method("call_function", method!(BamlRuntimeFfi::call_function, 7))?;
|
|
346
|
+
runtime_class.define_method(
|
|
347
|
+
"stream_function",
|
|
348
|
+
method!(BamlRuntimeFfi::stream_function, 7),
|
|
349
|
+
)?;
|
|
350
|
+
runtime_class.define_method("build_request", method!(BamlRuntimeFfi::build_request, 7))?;
|
|
351
|
+
runtime_class.define_method(
|
|
352
|
+
"parse_llm_response",
|
|
353
|
+
method!(BamlRuntimeFfi::parse_llm_response, 9),
|
|
354
|
+
)?;
|
|
355
|
+
|
|
356
|
+
FunctionResult::define_in_ruby(&module)?;
|
|
357
|
+
FunctionResultStream::define_in_ruby(&module)?;
|
|
358
|
+
|
|
359
|
+
RuntimeContextManager::define_in_ruby(&module)?;
|
|
360
|
+
|
|
361
|
+
types::type_builder::TypeBuilder::define_in_ruby(&module)?;
|
|
362
|
+
types::type_builder::EnumBuilder::define_in_ruby(&module)?;
|
|
363
|
+
types::type_builder::ClassBuilder::define_in_ruby(&module)?;
|
|
364
|
+
types::type_builder::EnumValueBuilder::define_in_ruby(&module)?;
|
|
365
|
+
types::type_builder::ClassPropertyBuilder::define_in_ruby(&module)?;
|
|
366
|
+
types::type_builder::FieldType::define_in_ruby(&module)?;
|
|
367
|
+
|
|
368
|
+
types::client_registry::ClientRegistry::define_in_ruby(&module)?;
|
|
369
|
+
types::media::Audio::define_in_ruby(&module)?;
|
|
370
|
+
types::media::Image::define_in_ruby(&module)?;
|
|
371
|
+
types::media::Pdf::define_in_ruby(&module)?;
|
|
372
|
+
types::media::Video::define_in_ruby(&module)?;
|
|
373
|
+
|
|
374
|
+
// Register the new log collector classes
|
|
375
|
+
types::log_collector::define_all_in_ruby(&module)?;
|
|
376
|
+
|
|
377
|
+
// everything below this is for our own testing purposes
|
|
378
|
+
module.define_module_function(
|
|
379
|
+
"roundtrip",
|
|
380
|
+
function!(ruby_to_json::RubyToJson::roundtrip, 1),
|
|
381
|
+
)?;
|
|
382
|
+
module.define_module_function(
|
|
383
|
+
"serialize",
|
|
384
|
+
function!(ruby_to_json::RubyToJson::serialize, 4),
|
|
385
|
+
)?;
|
|
386
|
+
|
|
387
|
+
Ok(())
|
|
388
|
+
}
|