From 3a03725104a919192976f8a172b1553bc62776f9 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 16 Oct 2024 16:20:24 -0700 Subject: [PATCH 01/31] add minimal test --- .../langsmith_tracing_client/Cargo.lock | 346 ++++++++++++++++++ .../langsmith_tracing_client/Cargo.toml | 8 + .../langsmith_tracing_client/src/lib.rs | 7 + .../tests/tokio_test.rs | 10 + 4 files changed, 371 insertions(+) create mode 100644 rust/crates/langsmith_tracing_client/Cargo.lock create mode 100644 rust/crates/langsmith_tracing_client/Cargo.toml create mode 100644 rust/crates/langsmith_tracing_client/src/lib.rs create mode 100644 rust/crates/langsmith_tracing_client/tests/tokio_test.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock new file mode 100644 index 000000000..11447bbb3 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -0,0 +1,346 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "langsmith_tracing_client" +version = "0.1.0" +dependencies = [ + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml new file mode 100644 index 000000000..2547843ae --- /dev/null +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "langsmith_tracing_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } + diff --git a/rust/crates/langsmith_tracing_client/src/lib.rs b/rust/crates/langsmith_tracing_client/src/lib.rs new file mode 100644 index 000000000..2728c4657 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/lib.rs @@ -0,0 +1,7 @@ +use tokio::time::{sleep, Duration}; + +pub async fn minimal_test() { + println!("Starting async task..."); + sleep(Duration::from_secs(1)).await; + println!("Async task complete!"); +} diff --git a/rust/crates/langsmith_tracing_client/tests/tokio_test.rs b/rust/crates/langsmith_tracing_client/tests/tokio_test.rs new file mode 100644 index 000000000..e762a75ea --- /dev/null +++ b/rust/crates/langsmith_tracing_client/tests/tokio_test.rs @@ -0,0 +1,10 @@ +use tokio::runtime::Runtime; +use langsmith_tracing_client::minimal_test; + +#[test] +fn test_tokio_runtime() { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + minimal_test().await; + }); +} From 0be9ec1db444821aaa37885bd275150250573fbb Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 16 Oct 2024 21:24:02 -0700 Subject: [PATCH 02/31] add struct definitions and tests --- .../langsmith_tracing_client/Cargo.lock | 241 ++++++++++++++++++ .../langsmith_tracing_client/Cargo.toml | 4 + .../src/client/run.rs | 57 +++++ .../langsmith_tracing_client/src/lib.rs | 2 + .../tests/test_run.rs | 116 +++++++++ 5 files changed, 420 insertions(+) create mode 100644 rust/crates/langsmith_tracing_client/src/client/run.rs create mode 100644 rust/crates/langsmith_tracing_client/tests/test_run.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index 11447bbb3..ff53b68a3 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -17,6 +17,21 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -44,18 +59,64 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytes" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cc" +version = "1.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.31.1" @@ -68,11 +129,53 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "langsmith_tracing_client" version = "0.1.0" dependencies = [ + "chrono", + "serde", + "serde_json", "tokio", + "uuid", ] [[package]] @@ -91,6 +194,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" @@ -118,6 +227,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.5" @@ -127,6 +245,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "parking_lot" version = "0.12.3" @@ -189,12 +313,56 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -266,12 +434,85 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 2547843ae..6e90a9feb 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -5,4 +5,8 @@ edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } +uuid = { version = "1.11.0", features = ["v4"] } +serde_json = "1.0.128" +serde = { version = "1.0.210", features = ["derive"] } +chrono = "0.4.38" diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs new file mode 100644 index 000000000..9e41da4a4 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -0,0 +1,57 @@ +use std::collections::HashMap; +use serde::{Serialize, Deserialize}; + +// Map attachment ref to tuple of filename, bytes +type Attachments = HashMap)>; + +// Must support both string (Py) and unsigned int (JS) +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +pub enum TimeValue { + String(String), + UnsignedInt(u64), +} + +#[derive(Serialize, Deserialize)] +pub struct RunCommon { + pub id: String, + pub trace_id: String, + pub dotted_order: String, + pub parent_run_id: Option, + pub extra: serde_json::Value, + pub error: Option, + pub serialized: serde_json::Value, + pub inputs: serde_json::Value, + pub events: serde_json::Value, + pub tags: serde_json::Value, +} + +#[derive(Serialize, Deserialize)] +pub struct RunCreate { + pub common: RunCommon, + pub name: String, + pub start_time: TimeValue, + pub end_time: Option, + pub outputs: serde_json::Value, + pub run_type: String, + pub session_id: Option, + pub session_name: Option, + pub reference_example_id: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct RunUpdate { + pub common: RunCommon, + pub end_time: TimeValue, + pub outputs: Option, +} + +pub struct RunCreateWithAttachments { + pub run_create: RunCreate, + pub attachments: Attachments, +} + +pub struct RunUpdateWithAttachments { + pub run_update: RunUpdate, + pub attachments: Attachments, +} diff --git a/rust/crates/langsmith_tracing_client/src/lib.rs b/rust/crates/langsmith_tracing_client/src/lib.rs index 2728c4657..6e3658edc 100644 --- a/rust/crates/langsmith_tracing_client/src/lib.rs +++ b/rust/crates/langsmith_tracing_client/src/lib.rs @@ -1,3 +1,5 @@ +pub mod client; + use tokio::time::{sleep, Duration}; pub async fn minimal_test() { diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs new file mode 100644 index 000000000..91bdd82d9 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -0,0 +1,116 @@ +use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunUpdate, TimeValue}; +use serde_json; + +#[test] +fn test_run_common() { + let run_common = RunCommon { + id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: Some(String::from("error message")), + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + }; + + let serialized = serde_json::to_string(&run_common).unwrap(); + println!("Serialized RunCommon: {}", serialized); + assert!(serialized.contains("\"dotted_order\":\"1.1\"")); +} + +#[test] +fn test_run_create_with_string_time() { + let run_common = RunCommon { + id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + }; + + let run_create = RunCreate { + common: run_common, + name: String::from("Run Name"), + start_time: TimeValue::String("2024-10-16T12:00:00Z".to_string()), + end_time: Some(TimeValue::String("2024-10-16T14:00:00Z".to_string())), + outputs: serde_json::json!({"output_key": "output_value"}), + run_type: String::from("test_run_type"), + session_id: None, + session_name: None, + reference_example_id: None, + }; + + let serialized = serde_json::to_string(&run_create).unwrap(); + println!("Serialized RunCreate (String Time): {}", serialized); + assert!(serialized.contains("\"name\":\"Run Name\"")); + assert!(serialized.contains("\"start_time\":\"2024-10-16T12:00:00Z\"")); +} + +#[test] +fn test_run_create_with_timestamp() { + let run_common = RunCommon { + id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + }; + + let run_create = RunCreate { + common: run_common, + name: String::from("Run Name"), + start_time: TimeValue::UnsignedInt(1697462400000), // Timestamp format (milliseconds since epoch) + end_time: Some(TimeValue::UnsignedInt(1697466000000)), + outputs: serde_json::json!({"output_key": "output_value"}), + run_type: String::from("test_run_type"), + session_id: None, + session_name: None, + reference_example_id: None, + }; + + let serialized = serde_json::to_string(&run_create).unwrap(); + println!("Serialized RunCreate (Timestamp): {}", serialized); + assert!(serialized.contains("\"name\":\"Run Name\"")); + assert!(serialized.contains("\"start_time\":1697462400000")); +} + +#[test] +fn test_run_update() { + let run_common = RunCommon { + id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([]), // Assuming Vec will be implemented later + tags: serde_json::json!({"tag": "value"}), + }; + + let run_update = RunUpdate { + common: run_common, + end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), // String format + outputs: Some(serde_json::json!({"output_key": "output_value"})), + }; + + let serialized = serde_json::to_string(&run_update).unwrap(); + println!("Serialized RunUpdate: {}", serialized); + assert!(serialized.contains("\"dotted_order\":\"1.1\"")); + assert!(serialized.contains("\"end_time\":\"2024-10-16T14:00:00Z\"")); +} + From b491b096d930cf548b466cc068229c4e8f216ed6 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 16 Oct 2024 21:24:41 -0700 Subject: [PATCH 03/31] lint and format --- rust/crates/langsmith_tracing_client/src/client/mod.rs | 1 + rust/crates/langsmith_tracing_client/src/client/run.rs | 2 +- rust/crates/langsmith_tracing_client/tests/test_run.rs | 5 ++--- rust/crates/langsmith_tracing_client/tests/tokio_test.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/src/client/mod.rs diff --git a/rust/crates/langsmith_tracing_client/src/client/mod.rs b/rust/crates/langsmith_tracing_client/src/client/mod.rs new file mode 100644 index 000000000..9137f27b0 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/client/mod.rs @@ -0,0 +1 @@ +pub mod run; diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 9e41da4a4..803f6d4f8 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -1,5 +1,5 @@ +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; // Map attachment ref to tuple of filename, bytes type Attachments = HashMap)>; diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index 91bdd82d9..16ed086ef 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -98,13 +98,13 @@ fn test_run_update() { error: None, serialized: serde_json::json!({"key": "value"}), inputs: serde_json::json!({"input": "value"}), - events: serde_json::json!([]), // Assuming Vec will be implemented later + events: serde_json::json!([]), // Assuming Vec will be implemented later tags: serde_json::json!({"tag": "value"}), }; let run_update = RunUpdate { common: run_common, - end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), // String format + end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), // String format outputs: Some(serde_json::json!({"output_key": "output_value"})), }; @@ -113,4 +113,3 @@ fn test_run_update() { assert!(serialized.contains("\"dotted_order\":\"1.1\"")); assert!(serialized.contains("\"end_time\":\"2024-10-16T14:00:00Z\"")); } - diff --git a/rust/crates/langsmith_tracing_client/tests/tokio_test.rs b/rust/crates/langsmith_tracing_client/tests/tokio_test.rs index e762a75ea..b905365ca 100644 --- a/rust/crates/langsmith_tracing_client/tests/tokio_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tokio_test.rs @@ -1,5 +1,5 @@ -use tokio::runtime::Runtime; use langsmith_tracing_client::minimal_test; +use tokio::runtime::Runtime; #[test] fn test_tokio_runtime() { From dac80dd9e8dcd7fb4ad0c9cf707de515ea466ef0 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 16 Oct 2024 23:05:56 -0700 Subject: [PATCH 04/31] compilation --- .../langsmith_tracing_client/Cargo.lock | 842 +++++++++++++++++- .../langsmith_tracing_client/Cargo.toml | 3 +- .../src/client/errors.rs | 19 + .../src/client/mod.rs | 3 + .../src/client/processor.rs | 188 ++++ .../src/client/run.rs | 5 + .../src/client/tracing_client.rs | 58 ++ 7 files changed, 1114 insertions(+), 4 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/src/client/errors.rs create mode 100644 rust/crates/langsmith_tracing_client/src/client/processor.rs create mode 100644 rust/crates/langsmith_tracing_client/src/client/tracing_client.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index ff53b68a3..ca2523a74 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -32,6 +32,12 @@ dependencies = [ "libc", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -53,6 +59,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.6.0" @@ -100,12 +112,122 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -123,12 +245,149 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -152,6 +411,32 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + [[package]] name = "itoa" version = "1.0.11" @@ -172,8 +457,10 @@ name = "langsmith_tracing_client" version = "0.1.0" dependencies = [ "chrono", + "reqwest", "serde", "serde_json", + "thiserror", "tokio", "uuid", ] @@ -184,6 +471,12 @@ version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -206,6 +499,22 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -224,7 +533,24 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -251,6 +577,50 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -274,12 +644,30 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "proc-macro2" version = "1.0.88" @@ -307,24 +695,167 @@ dependencies = [ "bitflags", ] +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.210" @@ -357,6 +888,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -372,6 +915,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -385,9 +937,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.79" @@ -399,6 +963,84 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -414,7 +1056,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -428,12 +1070,118 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.11.0" @@ -443,6 +1191,27 @@ dependencies = [ "getrandom", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -475,6 +1244,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.95" @@ -504,6 +1285,16 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -513,6 +1304,36 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -522,6 +1343,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -585,3 +1415,9 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 6e90a9feb..6ff07f14e 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -9,4 +9,5 @@ uuid = { version = "1.11.0", features = ["v4"] } serde_json = "1.0.128" serde = { version = "1.0.210", features = ["derive"] } chrono = "0.4.38" - +reqwest = { version = "0.12.8", features = ["multipart"] } +thiserror = "1.0" diff --git a/rust/crates/langsmith_tracing_client/src/client/errors.rs b/rust/crates/langsmith_tracing_client/src/client/errors.rs new file mode 100644 index 000000000..53a41b282 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/client/errors.rs @@ -0,0 +1,19 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum TracingClientError { + #[error("Queue is full")] + QueueFull, + + #[error("Serialization error: {0}")] + SerializationError(#[from] serde_json::Error), + + #[error("HTTP error: {0}")] + HttpError(reqwest::StatusCode), + + #[error("Request error: {0}")] + RequestError(#[from] reqwest::Error), + + #[error("Channel send error")] + ChannelSendError, +} diff --git a/rust/crates/langsmith_tracing_client/src/client/mod.rs b/rust/crates/langsmith_tracing_client/src/client/mod.rs index 9137f27b0..bb78d9c0c 100644 --- a/rust/crates/langsmith_tracing_client/src/client/mod.rs +++ b/rust/crates/langsmith_tracing_client/src/client/mod.rs @@ -1 +1,4 @@ pub mod run; +pub mod tracing_client; +pub mod errors; +pub mod processor; diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs new file mode 100644 index 000000000..ea4965250 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -0,0 +1,188 @@ +use tokio::sync::mpsc::{Receiver}; +use tokio::time::{sleep, Instant}; +use reqwest::multipart::{Form, Part}; +use serde_json::Value; +use crate::client::run::{QueuedRun}; +use crate::client::errors::TracingClientError; +use crate::client::tracing_client::ClientConfig; +use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; + +pub(crate) struct RunProcessor { + receiver: Receiver, + http_client: reqwest::Client, + config: ClientConfig, +} + +impl RunProcessor { + pub(crate) fn new(receiver: Receiver, config: ClientConfig) -> Self { + let http_client = reqwest::Client::new(); + + Self { + receiver, + http_client, + config, + } + } + + pub(crate) async fn run(mut self) -> Result<(), TracingClientError> { + let mut buffer = Vec::new(); + let mut last_send_time = Instant::now(); + + loop { + tokio::select! { + Some(queued_run) = self.receiver.recv() => { + buffer.push(queued_run); + if buffer.len() >= self.config.batch_size { + self.send_and_clear_buffer(&mut buffer).await?; + last_send_time = Instant::now(); + } + } + _ = sleep(self.config.batch_timeout) => { + if !buffer.is_empty() && last_send_time.elapsed() >= self.config.batch_timeout { + self.send_and_clear_buffer(&mut buffer).await?; + last_send_time = Instant::now(); + } + } + else => { + // Channel closed + if !buffer.is_empty() { + self.send_and_clear_buffer(&mut buffer).await?; + } + break; + } + } + } + Ok(()) + } + + async fn send_and_clear_buffer(&self, buffer: &mut Vec) -> Result<(), TracingClientError> { + if let Err(e) = self.send_batch(buffer).await { + // Handle error (e.g., log and retry logic) + eprintln!("Error sending batch: {}", e); + // Decide whether to drop the buffer or retry + } + buffer.clear(); + Ok(()) + } + + async fn send_batch(&self, batch: &[QueuedRun]) -> Result<(), TracingClientError> { + let mut form = Form::new(); + + for queued_run in batch { + match queued_run { + QueuedRun::Create(run_create_with_attachments) => { + self.add_run_create_to_form(run_create_with_attachments, &mut form)?; + } + QueuedRun::Update(run_update_with_attachments) => { + self.add_run_update_to_form(run_update_with_attachments, &mut form)?; + } + } + } + + // Send the multipart POST request + let response = self + .http_client + .post(&self.config.endpoint) + .multipart(form) + .send() + .await?; + + if response.status().is_success() { + Ok(()) + } else { + Err(TracingClientError::HttpError(response.status())) + } + } + + fn add_run_create_to_form( + &self, + run_with_attachments: &RunCreateWithAttachments, + form: &mut Form, + ) -> Result<(), TracingClientError> { + let run = &run_with_attachments.run_create; + let run_id = &run.common.id; + + // Serialize the run + let mut run_json = serde_json::to_value(run)?; + let inputs = run_json + .as_object_mut() + .and_then(|obj| obj.remove("inputs")) + .unwrap_or(Value::Null); + let outputs = run_json + .as_object_mut() + .and_then(|obj| obj.remove("outputs")) + .unwrap_or(Value::Null); + + // Add the main run data + *form = std::mem::take(form).part( + format!("post.{}", run_id), + Part::text(run_json.to_string()).mime_str("application/json")?, + ); + + // Add inputs + *form = std::mem::take(form).part( + format!("post.{}.inputs", run_id), + Part::text(inputs.to_string()).mime_str("application/json")?, + ); + + // Add outputs + *form = std::mem::take(form).part( + format!("post.{}.outputs", run_id), + Part::text(outputs.to_string()).mime_str("application/json")?, + ); + + // Add attachments + for (ref_name, (filename, data)) in &run_with_attachments.attachments { + *form = std::mem::take(form).part( + format!("post.{}.attachments.{}", run_id, ref_name), + Part::bytes(data.clone()) + .file_name(filename.clone()) + .mime_str("application/octet-stream")?, + ); + } + + Ok(()) + } + + fn add_run_update_to_form( + &self, + run_with_attachments: &RunUpdateWithAttachments, + form: &mut Form, + ) -> Result<(), TracingClientError> { + let run = &run_with_attachments.run_update; + let run_id = &run.common.id; + + // Serialize the run + let mut run_json = serde_json::to_value(run)?; + let outputs = run_json + .as_object_mut() + .and_then(|obj| obj.remove("outputs")) + .unwrap_or(Value::Null); + + // Add the main run data + *form = std::mem::take(form).part( + format!("patch.{}", run_id), + Part::text(run_json.to_string()).mime_str("application/json")?, + ); + + // Add outputs if present + if !outputs.is_null() { + *form = std::mem::take(form).part( + format!("patch.{}.outputs", run_id), + Part::text(outputs.to_string()).mime_str("application/json")?, + ); + } + + // Add attachments + for (ref_name, (filename, data)) in &run_with_attachments.attachments { + *form = std::mem::take(form).part( + format!("patch.{}.attachments.{}", run_id, ref_name), + Part::bytes(data.clone()) + .file_name(filename.clone()) + .mime_str("application/octet-stream")?, + ); + } + + Ok(()) + } +} diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 803f6d4f8..ddbfa9fac 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -55,3 +55,8 @@ pub struct RunUpdateWithAttachments { pub run_update: RunUpdate, pub attachments: Attachments, } + +pub enum QueuedRun { + Create(RunCreateWithAttachments), + Update(RunUpdateWithAttachments), +} diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs new file mode 100644 index 000000000..15598b649 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -0,0 +1,58 @@ +use tokio::sync::mpsc::{self, Sender}; +use std::time::Duration; +use crate::client::run::QueuedRun; +use crate::client::errors::TracingClientError; +use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; +use crate::client::processor::RunProcessor; + +pub struct ClientConfig { + pub endpoint: String, + pub queue_capacity: usize, + pub batch_size: usize, + pub batch_timeout: Duration, +} + +pub struct TracingClient { + sender: Sender, +} + +impl TracingClient { + pub fn new(config: ClientConfig) -> Result { + let (sender, receiver) = mpsc::channel(config.queue_capacity); + + let processor = RunProcessor::new(receiver, config); + tokio::spawn(async move { + if let Err(e) = processor.run().await { + eprintln!("RunProcessor exited with error: {}", e); + } + }); + + Ok(Self { sender }) + } + + pub async fn submit_run_create( + &self, + run: RunCreateWithAttachments, + ) -> Result<(), TracingClientError> { + let queued_run = QueuedRun::Create(run); + + // Send the run to the async queue + self.sender + .send(queued_run) + .await + .map_err(|_| TracingClientError::QueueFull) + } + + pub async fn submit_run_update( + &self, + run: RunUpdateWithAttachments, + ) -> Result<(), TracingClientError> { + let queued_run = QueuedRun::Update(run); + + // Send the run to the async queue + self.sender + .send(queued_run) + .await + .map_err(|_| TracingClientError::QueueFull) + } +} From f212ff854ca31ffe10f2895f9941346d8f4fa8a7 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Thu, 17 Oct 2024 17:40:45 -0700 Subject: [PATCH 05/31] all tests passing --- .../langsmith_tracing_client/Cargo.lock | 266 ++++++++++++++++-- .../langsmith_tracing_client/Cargo.toml | 1 + .../src/client/mod.rs | 4 +- .../src/client/processor.rs | 19 +- .../src/client/tracing_client.rs | 8 +- .../tests/tracing_client_test.rs | 145 ++++++++++ 6 files changed, 413 insertions(+), 30 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index ca2523a74..b354b05d2 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -17,6 +17,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -32,6 +41,16 @@ dependencies = [ "libc", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -56,7 +75,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -77,6 +96,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.7.2" @@ -109,7 +134,17 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", ] [[package]] @@ -316,6 +351,12 @@ version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.5.0" @@ -329,6 +370,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -457,6 +499,7 @@ name = "langsmith_tracing_client" version = "0.1.0" dependencies = [ "chrono", + "mockito", "reqwest", "serde", "serde_json", @@ -465,6 +508,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.159" @@ -536,6 +585,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockito" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -641,7 +714,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -668,6 +741,15 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.88" @@ -686,6 +768,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.5.7" @@ -695,6 +807,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "reqwest" version = "0.12.8" @@ -915,6 +1056,12 @@ dependencies = [ "libc", ] +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "slab" version = "0.4.9" @@ -1301,7 +1448,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1312,7 +1459,7 @@ checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1321,7 +1468,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1331,7 +1478,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -1340,7 +1496,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1349,7 +1505,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1358,28 +1529,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1392,30 +1581,75 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 6ff07f14e..25372eefa 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -11,3 +11,4 @@ serde = { version = "1.0.210", features = ["derive"] } chrono = "0.4.38" reqwest = { version = "0.12.8", features = ["multipart"] } thiserror = "1.0" +mockito = "1.5.0" diff --git a/rust/crates/langsmith_tracing_client/src/client/mod.rs b/rust/crates/langsmith_tracing_client/src/client/mod.rs index bb78d9c0c..ed4b7545c 100644 --- a/rust/crates/langsmith_tracing_client/src/client/mod.rs +++ b/rust/crates/langsmith_tracing_client/src/client/mod.rs @@ -1,4 +1,4 @@ -pub mod run; -pub mod tracing_client; pub mod errors; pub mod processor; +pub mod run; +pub mod tracing_client; diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index ea4965250..2c319c02c 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -1,13 +1,13 @@ -use tokio::sync::mpsc::{Receiver}; -use tokio::time::{sleep, Instant}; -use reqwest::multipart::{Form, Part}; -use serde_json::Value; -use crate::client::run::{QueuedRun}; use crate::client::errors::TracingClientError; -use crate::client::tracing_client::ClientConfig; +use crate::client::run::QueuedRun; use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; +use crate::client::tracing_client::ClientConfig; +use reqwest::multipart::{Form, Part}; +use serde_json::Value; +use tokio::sync::mpsc::Receiver; +use tokio::time::{sleep, Instant}; -pub(crate) struct RunProcessor { +pub struct RunProcessor { receiver: Receiver, http_client: reqwest::Client, config: ClientConfig, @@ -55,7 +55,10 @@ impl RunProcessor { Ok(()) } - async fn send_and_clear_buffer(&self, buffer: &mut Vec) -> Result<(), TracingClientError> { + async fn send_and_clear_buffer( + &self, + buffer: &mut Vec, + ) -> Result<(), TracingClientError> { if let Err(e) = self.send_batch(buffer).await { // Handle error (e.g., log and retry logic) eprintln!("Error sending batch: {}", e); diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs index 15598b649..b60d4b48e 100644 --- a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -1,9 +1,9 @@ -use tokio::sync::mpsc::{self, Sender}; -use std::time::Duration; -use crate::client::run::QueuedRun; use crate::client::errors::TracingClientError; -use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; use crate::client::processor::RunProcessor; +use crate::client::run::QueuedRun; +use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; +use std::time::Duration; +use tokio::sync::mpsc::{self, Sender}; pub struct ClientConfig { pub endpoint: String, diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs new file mode 100644 index 000000000..fee6ca0b9 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -0,0 +1,145 @@ +use langsmith_tracing_client::client::run::{ + RunCommon, RunCreate, RunCreateWithAttachments, RunUpdate, RunUpdateWithAttachments, TimeValue, +}; +use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; +use mockito::{Matcher, Server}; +use std::collections::HashMap; +use std::time::Duration; + +#[tokio::test] +async fn test_tracing_client_submit_run_create() { + let mut server = Server::new_async().await; + let m = server + .mock("POST", "/") + .match_header( + "content-type", + Matcher::Regex(r"multipart/form-data.*".to_string()), + ) + .match_body(Matcher::AllOf(vec![ + Matcher::Regex("post\\.test_id".to_string()), + Matcher::Regex("post\\.test_id\\.inputs".to_string()), + Matcher::Regex("post\\.test_id\\.outputs".to_string()), + Matcher::Regex("post\\.test_id\\.attachments\\.attachment_1".to_string()), + Matcher::Regex("post\\.test_id\\.attachments\\.attachment_2".to_string()), + ])) + .with_status(200) + .create_async() + .await; + + let config = ClientConfig { + endpoint: server.url(), + queue_capacity: 10, + batch_size: 1, + batch_timeout: Duration::from_secs(1), + }; + + let client = TracingClient::new(config).unwrap(); + + let mut attachments = HashMap::new(); + attachments.insert( + "attachment_1".to_string(), + ("file1.txt".to_string(), vec![1, 2, 3]), + ); + attachments.insert( + "attachment_2".to_string(), + ("file2.txt".to_string(), vec![4, 5, 6]), + ); + + let run_create = RunCreateWithAttachments { + run_create: RunCreate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + }, + name: String::from("Run Name"), + start_time: TimeValue::UnsignedInt(1697462400000), + end_time: Some(TimeValue::UnsignedInt(1697466000000)), + outputs: serde_json::json!({"output_key": "output_value"}), + run_type: String::from("test_run_type"), + session_id: None, + session_name: None, + reference_example_id: None, + }, + attachments, + }; + + let _result = client.submit_run_create(run_create).await; + // sleep for a bit to allow the mock server to receive the request + tokio::time::sleep(Duration::from_secs(1)).await; + let _ = m.assert_async().await; +} + +// now test run update + +#[tokio::test] +async fn test_tracing_client_submit_run_update() { + let mut server = Server::new_async().await; + let m = server + .mock("POST", "/") + .match_header( + "content-type", + Matcher::Regex(r"multipart/form-data.*".to_string()), + ) + .match_body(Matcher::AllOf(vec![ + Matcher::Regex("patch\\.test_id".to_string()), + Matcher::Regex("patch\\.test_id\\.outputs".to_string()), + Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_1".to_string()), + Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_2".to_string()), + ])) + .with_status(200) + .create_async() + .await; + + let config = ClientConfig { + endpoint: server.url(), + queue_capacity: 10, + batch_size: 1, + batch_timeout: Duration::from_secs(1), + }; + + let client = TracingClient::new(config).unwrap(); + + let mut attachments = HashMap::new(); + attachments.insert( + "attachment_1".to_string(), + ("file1.txt".to_string(), vec![1, 2, 3]), + ); + attachments.insert( + "attachment_2".to_string(), + ("file2.txt".to_string(), vec![4, 5, 6]), + ); + + let run_update = RunUpdateWithAttachments { + run_update: RunUpdate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + }, + end_time: TimeValue::UnsignedInt(1697462400000), + outputs: Some(serde_json::json!({"output_key": "output_value"})), + }, + attachments, + }; + + let _result = client.submit_run_update(run_update).await; + // sleep for a bit to allow the mock server to receive the request + // sleep for a bit to allow the mock server to receive the request + tokio::time::sleep(Duration::from_secs(1)).await; + let _ = m.assert_async().await; +} From 0a7d49e3f1eef85b9dccdcf1edb56b89aea21abc Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Thu, 17 Oct 2024 18:23:21 -0700 Subject: [PATCH 06/31] use shutdown method instead of janky sleep --- .../src/client/errors.rs | 3 ++ .../src/client/processor.rs | 39 ++++++++++++------- .../src/client/run.rs | 1 + .../src/client/tracing_client.rs | 22 ++++++++--- .../tests/tracing_client_test.rs | 24 ++++++------ 5 files changed, 58 insertions(+), 31 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/errors.rs b/rust/crates/langsmith_tracing_client/src/client/errors.rs index 53a41b282..62d8cde43 100644 --- a/rust/crates/langsmith_tracing_client/src/client/errors.rs +++ b/rust/crates/langsmith_tracing_client/src/client/errors.rs @@ -16,4 +16,7 @@ pub enum TracingClientError { #[error("Channel send error")] ChannelSendError, + + #[error("Unexpected shutdown")] + UnexpectedShutdown, } diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 2c319c02c..9db16b00a 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -31,27 +31,44 @@ impl RunProcessor { loop { tokio::select! { Some(queued_run) = self.receiver.recv() => { - buffer.push(queued_run); - if buffer.len() >= self.config.batch_size { - self.send_and_clear_buffer(&mut buffer).await?; - last_send_time = Instant::now(); + match queued_run { + QueuedRun::Shutdown => { + println!("shutdown signal received."); + if !buffer.is_empty() { + println!("sending remaining buffer before shutdown."); + self.send_and_clear_buffer(&mut buffer).await?; + } + break; + }, + _ => { + println!("received a queued run."); + buffer.push(queued_run); + if buffer.len() >= self.config.batch_size { + println!("batch size limit, sending batch."); + self.send_and_clear_buffer(&mut buffer).await?; + last_send_time = Instant::now(); + } + } } } _ = sleep(self.config.batch_timeout) => { if !buffer.is_empty() && last_send_time.elapsed() >= self.config.batch_timeout { + println!("batch timeout, sending batch."); self.send_and_clear_buffer(&mut buffer).await?; last_send_time = Instant::now(); } } else => { - // Channel closed + println!("channel closed."); if !buffer.is_empty() { + println!("sending remaining buffer."); self.send_and_clear_buffer(&mut buffer).await?; } break; } } } + println!("exiting loop."); Ok(()) } @@ -79,6 +96,9 @@ impl RunProcessor { QueuedRun::Update(run_update_with_attachments) => { self.add_run_update_to_form(run_update_with_attachments, &mut form)?; } + QueuedRun::Shutdown => { + return Err(TracingClientError::UnexpectedShutdown); + } } } @@ -105,7 +125,6 @@ impl RunProcessor { let run = &run_with_attachments.run_create; let run_id = &run.common.id; - // Serialize the run let mut run_json = serde_json::to_value(run)?; let inputs = run_json .as_object_mut() @@ -116,25 +135,21 @@ impl RunProcessor { .and_then(|obj| obj.remove("outputs")) .unwrap_or(Value::Null); - // Add the main run data *form = std::mem::take(form).part( format!("post.{}", run_id), Part::text(run_json.to_string()).mime_str("application/json")?, ); - // Add inputs *form = std::mem::take(form).part( format!("post.{}.inputs", run_id), Part::text(inputs.to_string()).mime_str("application/json")?, ); - // Add outputs *form = std::mem::take(form).part( format!("post.{}.outputs", run_id), Part::text(outputs.to_string()).mime_str("application/json")?, ); - // Add attachments for (ref_name, (filename, data)) in &run_with_attachments.attachments { *form = std::mem::take(form).part( format!("post.{}.attachments.{}", run_id, ref_name), @@ -155,20 +170,17 @@ impl RunProcessor { let run = &run_with_attachments.run_update; let run_id = &run.common.id; - // Serialize the run let mut run_json = serde_json::to_value(run)?; let outputs = run_json .as_object_mut() .and_then(|obj| obj.remove("outputs")) .unwrap_or(Value::Null); - // Add the main run data *form = std::mem::take(form).part( format!("patch.{}", run_id), Part::text(run_json.to_string()).mime_str("application/json")?, ); - // Add outputs if present if !outputs.is_null() { *form = std::mem::take(form).part( format!("patch.{}.outputs", run_id), @@ -176,7 +188,6 @@ impl RunProcessor { ); } - // Add attachments for (ref_name, (filename, data)) in &run_with_attachments.attachments { *form = std::mem::take(form).part( format!("patch.{}.attachments.{}", run_id, ref_name), diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index ddbfa9fac..603569d51 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -59,4 +59,5 @@ pub struct RunUpdateWithAttachments { pub enum QueuedRun { Create(RunCreateWithAttachments), Update(RunUpdateWithAttachments), + Shutdown, } diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs index b60d4b48e..42bb73cf8 100644 --- a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -4,6 +4,7 @@ use crate::client::run::QueuedRun; use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; use std::time::Duration; use tokio::sync::mpsc::{self, Sender}; +use tokio::task::JoinHandle; pub struct ClientConfig { pub endpoint: String, @@ -14,6 +15,7 @@ pub struct ClientConfig { pub struct TracingClient { sender: Sender, + handle: JoinHandle>, } impl TracingClient { @@ -21,13 +23,16 @@ impl TracingClient { let (sender, receiver) = mpsc::channel(config.queue_capacity); let processor = RunProcessor::new(receiver, config); - tokio::spawn(async move { - if let Err(e) = processor.run().await { + + let handle = tokio::spawn(async move { + let result = processor.run().await; + if let Err(e) = &result { eprintln!("RunProcessor exited with error: {}", e); } + result }); - Ok(Self { sender }) + Ok(Self { sender, handle }) } pub async fn submit_run_create( @@ -36,7 +41,6 @@ impl TracingClient { ) -> Result<(), TracingClientError> { let queued_run = QueuedRun::Create(run); - // Send the run to the async queue self.sender .send(queued_run) .await @@ -49,10 +53,18 @@ impl TracingClient { ) -> Result<(), TracingClientError> { let queued_run = QueuedRun::Update(run); - // Send the run to the async queue self.sender .send(queued_run) .await .map_err(|_| TracingClientError::QueueFull) } + + pub async fn shutdown(self) -> Result<(), TracingClientError> { + self.sender + .send(QueuedRun::Shutdown) + .await + .map_err(|_| TracingClientError::QueueFull)?; + + self.handle.await.unwrap() + } } diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index fee6ca0b9..54f398068 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -29,7 +29,7 @@ async fn test_tracing_client_submit_run_create() { let config = ClientConfig { endpoint: server.url(), queue_capacity: 10, - batch_size: 1, + batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue batch_timeout: Duration::from_secs(1), }; @@ -71,14 +71,14 @@ async fn test_tracing_client_submit_run_create() { attachments, }; - let _result = client.submit_run_create(run_create).await; - // sleep for a bit to allow the mock server to receive the request - tokio::time::sleep(Duration::from_secs(1)).await; - let _ = m.assert_async().await; + client.submit_run_create(run_create).await.unwrap(); + + // shutdown the client to ensure all messages are processed + client.shutdown().await.unwrap(); + m.assert_async().await; } // now test run update - #[tokio::test] async fn test_tracing_client_submit_run_update() { let mut server = Server::new_async().await; @@ -101,7 +101,7 @@ async fn test_tracing_client_submit_run_update() { let config = ClientConfig { endpoint: server.url(), queue_capacity: 10, - batch_size: 1, + batch_size: 1, // batch size is 1 to ensure each message is sent immediately batch_timeout: Duration::from_secs(1), }; @@ -137,9 +137,9 @@ async fn test_tracing_client_submit_run_update() { attachments, }; - let _result = client.submit_run_update(run_update).await; - // sleep for a bit to allow the mock server to receive the request - // sleep for a bit to allow the mock server to receive the request - tokio::time::sleep(Duration::from_secs(1)).await; - let _ = m.assert_async().await; + client.submit_run_update(run_update).await.unwrap(); + + // shutdown the client to ensure all messages are processed + client.shutdown().await.unwrap(); + m.assert_async().await; } From 471e654ee0ed7dd7505052b494764dcf2d8abd75 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Thu, 17 Oct 2024 18:27:19 -0700 Subject: [PATCH 07/31] comments --- rust/crates/langsmith_tracing_client/tests/test_run.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index 16ed086ef..5754d7426 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -72,7 +72,7 @@ fn test_run_create_with_timestamp() { let run_create = RunCreate { common: run_common, name: String::from("Run Name"), - start_time: TimeValue::UnsignedInt(1697462400000), // Timestamp format (milliseconds since epoch) + start_time: TimeValue::UnsignedInt(1697462400000), end_time: Some(TimeValue::UnsignedInt(1697466000000)), outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), @@ -98,13 +98,13 @@ fn test_run_update() { error: None, serialized: serde_json::json!({"key": "value"}), inputs: serde_json::json!({"input": "value"}), - events: serde_json::json!([]), // Assuming Vec will be implemented later + events: serde_json::json!([]), tags: serde_json::json!({"tag": "value"}), }; let run_update = RunUpdate { common: run_common, - end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), // String format + end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), outputs: Some(serde_json::json!({"output_key": "output_value"})), }; From b7eefed30863825e450950c0c16d994ba169f58d Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Thu, 17 Oct 2024 18:31:35 -0700 Subject: [PATCH 08/31] cleanup --- rust/crates/langsmith_tracing_client/src/client/processor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 9db16b00a..a4683885e 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -77,9 +77,8 @@ impl RunProcessor { buffer: &mut Vec, ) -> Result<(), TracingClientError> { if let Err(e) = self.send_batch(buffer).await { - // Handle error (e.g., log and retry logic) + // todo: retry logic? eprintln!("Error sending batch: {}", e); - // Decide whether to drop the buffer or retry } buffer.clear(); Ok(()) From f5d0adef99ae2bcfc720f553e90adde1fd588795 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 19 Oct 2024 14:04:02 -0700 Subject: [PATCH 09/31] move more stuff to common --- .../langsmith_tracing_client/src/client/run.rs | 4 ++-- .../langsmith_tracing_client/tests/test_run.rs | 12 ++++++++---- .../tests/tracing_client_test.rs | 7 +++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 603569d51..82d18a108 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -24,6 +24,8 @@ pub struct RunCommon { pub inputs: serde_json::Value, pub events: serde_json::Value, pub tags: serde_json::Value, + pub session_id: Option, + pub session_name: Option, } #[derive(Serialize, Deserialize)] @@ -34,8 +36,6 @@ pub struct RunCreate { pub end_time: Option, pub outputs: serde_json::Value, pub run_type: String, - pub session_id: Option, - pub session_name: Option, pub reference_example_id: Option, } diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index 5754d7426..31d3bb4ba 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -14,6 +14,8 @@ fn test_run_common() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), + session_id: Some("efghijkl-7654-3210-fedc-ba9876543210".to_string()), + session_name: None, }; let serialized = serde_json::to_string(&run_common).unwrap(); @@ -34,6 +36,8 @@ fn test_run_create_with_string_time() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: Some("Session Name".to_string()), }; let run_create = RunCreate { @@ -43,8 +47,6 @@ fn test_run_create_with_string_time() { end_time: Some(TimeValue::String("2024-10-16T14:00:00Z".to_string())), outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), - session_id: None, - session_name: None, reference_example_id: None, }; @@ -67,6 +69,8 @@ fn test_run_create_with_timestamp() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: None, }; let run_create = RunCreate { @@ -76,8 +80,6 @@ fn test_run_create_with_timestamp() { end_time: Some(TimeValue::UnsignedInt(1697466000000)), outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), - session_id: None, - session_name: None, reference_example_id: None, }; @@ -100,6 +102,8 @@ fn test_run_update() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([]), tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: None, }; let run_update = RunUpdate { diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index 54f398068..f8b553f8f 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -5,6 +5,7 @@ use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClie use mockito::{Matcher, Server}; use std::collections::HashMap; use std::time::Duration; +use serde::de::Unexpected::Option; #[tokio::test] async fn test_tracing_client_submit_run_create() { @@ -58,14 +59,14 @@ async fn test_tracing_client_submit_run_create() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: Some("Session Name".to_string()), }, name: String::from("Run Name"), start_time: TimeValue::UnsignedInt(1697462400000), end_time: Some(TimeValue::UnsignedInt(1697466000000)), outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), - session_id: None, - session_name: None, reference_example_id: None, }, attachments, @@ -130,6 +131,8 @@ async fn test_tracing_client_submit_run_update() { inputs: serde_json::json!({"input": "value"}), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), + session_name: Some("Session Name".to_string()), + session_id: None, }, end_time: TimeValue::UnsignedInt(1697462400000), outputs: Some(serde_json::json!({"output_key": "output_value"})), From 77f3dc5af64d15b96f942554f0d84c5262f8b7fe Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 19 Oct 2024 14:14:57 -0700 Subject: [PATCH 10/31] make the vec optional --- .../src/client/processor.rs | 28 +++++++++++-------- .../src/client/run.rs | 4 +-- .../tests/tokio_test.rs | 10 ------- .../tests/tracing_client_test.rs | 11 ++++---- 4 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 rust/crates/langsmith_tracing_client/tests/tokio_test.rs diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index a4683885e..1ac7aa729 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -150,12 +150,14 @@ impl RunProcessor { ); for (ref_name, (filename, data)) in &run_with_attachments.attachments { - *form = std::mem::take(form).part( - format!("post.{}.attachments.{}", run_id, ref_name), - Part::bytes(data.clone()) - .file_name(filename.clone()) - .mime_str("application/octet-stream")?, - ); + if let Some(data) = data { + *form = std::mem::take(form).part( + format!("post.{}.attachments.{}", run_id, ref_name), + Part::bytes(data.clone()) + .file_name(filename.clone()) + .mime_str("application/octet-stream")?, + ); + } } Ok(()) @@ -188,12 +190,14 @@ impl RunProcessor { } for (ref_name, (filename, data)) in &run_with_attachments.attachments { - *form = std::mem::take(form).part( - format!("patch.{}.attachments.{}", run_id, ref_name), - Part::bytes(data.clone()) - .file_name(filename.clone()) - .mime_str("application/octet-stream")?, - ); + if let Some(data) = data { + *form = std::mem::take(form).part( + format!("patch.{}.attachments.{}", run_id, ref_name), + Part::bytes(data.clone()) + .file_name(filename.clone()) + .mime_str("application/octet-stream")?, + ); + } } Ok(()) diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 82d18a108..39424390a 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; -// Map attachment ref to tuple of filename, bytes -type Attachments = HashMap)>; +// Map attachment ref to tuple of filename, optional bytes +type Attachments = HashMap>)>; // Must support both string (Py) and unsigned int (JS) #[derive(Serialize, Deserialize)] diff --git a/rust/crates/langsmith_tracing_client/tests/tokio_test.rs b/rust/crates/langsmith_tracing_client/tests/tokio_test.rs deleted file mode 100644 index b905365ca..000000000 --- a/rust/crates/langsmith_tracing_client/tests/tokio_test.rs +++ /dev/null @@ -1,10 +0,0 @@ -use langsmith_tracing_client::minimal_test; -use tokio::runtime::Runtime; - -#[test] -fn test_tokio_runtime() { - let rt = Runtime::new().unwrap(); - rt.block_on(async { - minimal_test().await; - }); -} diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index f8b553f8f..60755cc82 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -5,7 +5,6 @@ use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClie use mockito::{Matcher, Server}; use std::collections::HashMap; use std::time::Duration; -use serde::de::Unexpected::Option; #[tokio::test] async fn test_tracing_client_submit_run_create() { @@ -30,7 +29,7 @@ async fn test_tracing_client_submit_run_create() { let config = ClientConfig { endpoint: server.url(), queue_capacity: 10, - batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue + batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue batch_timeout: Duration::from_secs(1), }; @@ -39,11 +38,11 @@ async fn test_tracing_client_submit_run_create() { let mut attachments = HashMap::new(); attachments.insert( "attachment_1".to_string(), - ("file1.txt".to_string(), vec![1, 2, 3]), + ("file1.txt".to_string(), Some(vec![1, 2, 3])), ); attachments.insert( "attachment_2".to_string(), - ("file2.txt".to_string(), vec![4, 5, 6]), + ("file2.txt".to_string(), Some(vec![4, 5, 6])), ); let run_create = RunCreateWithAttachments { @@ -111,11 +110,11 @@ async fn test_tracing_client_submit_run_update() { let mut attachments = HashMap::new(); attachments.insert( "attachment_1".to_string(), - ("file1.txt".to_string(), vec![1, 2, 3]), + ("file1.txt".to_string(), Some(vec![1, 2, 3])), ); attachments.insert( "attachment_2".to_string(), - ("file2.txt".to_string(), vec![4, 5, 6]), + ("file2.txt".to_string(), Some(vec![4, 5, 6])), ); let run_update = RunUpdateWithAttachments { From 8065b3d8d9d42cc54e8316010361e3ba40bd08c3 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 19 Oct 2024 15:57:10 -0700 Subject: [PATCH 11/31] something kind of working with streaming --- .../langsmith_tracing_client/Cargo.lock | 39 ++++ .../langsmith_tracing_client/Cargo.toml | 4 +- .../src/client/errors.rs | 3 + .../src/client/processor.rs | 70 ++++++- .../src/client/run.rs | 10 +- .../tests/tracing_client_test.rs | 174 +++++++++++------- 6 files changed, 216 insertions(+), 84 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index b354b05d2..9374358e9 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -239,6 +239,23 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -258,9 +275,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -503,8 +525,10 @@ dependencies = [ "reqwest", "serde", "serde_json", + "tempfile", "thiserror", "tokio", + "tokio-util", "uuid", ] @@ -872,10 +896,12 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "windows-registry", ] @@ -1432,6 +1458,19 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.72" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 25372eefa..5ce2e0570 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -9,6 +9,8 @@ uuid = { version = "1.11.0", features = ["v4"] } serde_json = "1.0.128" serde = { version = "1.0.210", features = ["derive"] } chrono = "0.4.38" -reqwest = { version = "0.12.8", features = ["multipart"] } +reqwest = { version = "0.12.8", features = ["multipart", "stream"] } thiserror = "1.0" mockito = "1.5.0" +tokio-util = "0.7.12" +tempfile = "3.13.0" diff --git a/rust/crates/langsmith_tracing_client/src/client/errors.rs b/rust/crates/langsmith_tracing_client/src/client/errors.rs index 62d8cde43..0ad9c88d5 100644 --- a/rust/crates/langsmith_tracing_client/src/client/errors.rs +++ b/rust/crates/langsmith_tracing_client/src/client/errors.rs @@ -19,4 +19,7 @@ pub enum TracingClientError { #[error("Unexpected shutdown")] UnexpectedShutdown, + + #[error("IO error")] + IoError(String), } diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 1ac7aa729..c4cddcdb7 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -6,6 +6,7 @@ use reqwest::multipart::{Form, Part}; use serde_json::Value; use tokio::sync::mpsc::Receiver; use tokio::time::{sleep, Instant}; +use tokio_util::io::ReaderStream; pub struct RunProcessor { receiver: Receiver, @@ -90,10 +91,12 @@ impl RunProcessor { for queued_run in batch { match queued_run { QueuedRun::Create(run_create_with_attachments) => { - self.add_run_create_to_form(run_create_with_attachments, &mut form)?; + self.add_run_create_to_form(run_create_with_attachments, &mut form) + .await?; } QueuedRun::Update(run_update_with_attachments) => { - self.add_run_update_to_form(run_update_with_attachments, &mut form)?; + self.add_run_update_to_form(run_update_with_attachments, &mut form) + .await?; } QueuedRun::Shutdown => { return Err(TracingClientError::UnexpectedShutdown); @@ -116,7 +119,7 @@ impl RunProcessor { } } - fn add_run_create_to_form( + async fn add_run_create_to_form( &self, run_with_attachments: &RunCreateWithAttachments, form: &mut Form, @@ -149,21 +152,45 @@ impl RunProcessor { Part::text(outputs.to_string()).mime_str("application/json")?, ); - for (ref_name, (filename, data)) in &run_with_attachments.attachments { + for (ref_name, attachment) in &run_with_attachments.attachments { + let filename = &attachment.filename; + let data = &attachment.data; + let content_type = &attachment.content_type; + + let part_name = format!("post.{}.attachments.{}", run_id, ref_name); if let Some(data) = data { *form = std::mem::take(form).part( - format!("post.{}.attachments.{}", run_id, ref_name), + part_name, Part::bytes(data.clone()) .file_name(filename.clone()) - .mime_str("application/octet-stream")?, + .mime_str(content_type)?, ); + } else { + // Read the file from disk and stream it + let file_path = std::path::Path::new(filename); + let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { + TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) + })?; + let file_size = metadata.len(); + let file = tokio::fs::File::open(file_path).await.map_err(|e| { + TracingClientError::IoError(format!("Failed to open file: {}", e)) + })?; + let stream = ReaderStream::new(file); + let body = reqwest::Body::wrap_stream(stream); + + let part = Part::stream_with_length(body, file_size) + // TODO only take the last component of the path as the filename + .file_name(filename.clone()) + .mime_str(content_type)?; + + *form = std::mem::take(form).part(part_name, part); } } Ok(()) } - fn add_run_update_to_form( + async fn add_run_update_to_form( &self, run_with_attachments: &RunUpdateWithAttachments, form: &mut Form, @@ -189,14 +216,37 @@ impl RunProcessor { ); } - for (ref_name, (filename, data)) in &run_with_attachments.attachments { + for (ref_name, attachment) in &run_with_attachments.attachments { + let filename = &attachment.filename; + let data = &attachment.data; + let content_type = &attachment.content_type; + + let part_name = format!("patch.{}.attachments.{}", run_id, ref_name); if let Some(data) = data { *form = std::mem::take(form).part( - format!("patch.{}.attachments.{}", run_id, ref_name), + part_name, Part::bytes(data.clone()) .file_name(filename.clone()) - .mime_str("application/octet-stream")?, + .mime_str(content_type)?, ); + } else { + // Read the file from disk and stream it + let file_path = std::path::Path::new(filename); + let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { + TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) + })?; + let file_size = metadata.len(); + let file = tokio::fs::File::open(file_path).await.map_err(|e| { + TracingClientError::IoError(format!("Failed to open file: {}", e)) + })?; + let stream = ReaderStream::new(file); + let body = reqwest::Body::wrap_stream(stream); + + let part = Part::stream_with_length(body, file_size) + .file_name(filename.clone()) + .mime_str(content_type)?; + + *form = std::mem::take(form).part(part_name, part); } } diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 39424390a..c9c66f70f 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -2,7 +2,11 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; // Map attachment ref to tuple of filename, optional bytes -type Attachments = HashMap>)>; +pub struct Attachment { + pub filename: String, + pub data: Option>, + pub content_type: String, +} // Must support both string (Py) and unsigned int (JS) #[derive(Serialize, Deserialize)] @@ -48,12 +52,12 @@ pub struct RunUpdate { pub struct RunCreateWithAttachments { pub run_create: RunCreate, - pub attachments: Attachments, + pub attachments: HashMap, } pub struct RunUpdateWithAttachments { pub run_update: RunUpdate, - pub attachments: Attachments, + pub attachments: HashMap, } pub enum QueuedRun { diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index 60755cc82..f61b756cb 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -1,10 +1,15 @@ use langsmith_tracing_client::client::run::{ - RunCommon, RunCreate, RunCreateWithAttachments, RunUpdate, RunUpdateWithAttachments, TimeValue, + Attachment, RunCommon, RunCreate, RunCreateWithAttachments, RunUpdate, + RunUpdateWithAttachments, TimeValue, }; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; use mockito::{Matcher, Server}; use std::collections::HashMap; +use std::fs::File; use std::time::Duration; +// use tokio::io::AsyncWriteExt; +use std::io::{self, Write}; +use tempfile::TempDir; #[tokio::test] async fn test_tracing_client_submit_run_create() { @@ -35,14 +40,28 @@ async fn test_tracing_client_submit_run_create() { let client = TracingClient::new(config).unwrap(); + // Write a test file to disk for streaming + let tmp_dir = TempDir::new().unwrap(); + let test_file_path = tmp_dir.path().join("test_file_create.txt"); + let mut test_file = File::create(&test_file_path).unwrap(); + writeln!(test_file, "Test file content for create").unwrap(); + let mut attachments = HashMap::new(); attachments.insert( "attachment_1".to_string(), - ("file1.txt".to_string(), Some(vec![1, 2, 3])), + Attachment { + filename: "file1.txt".to_string(), + data: Some(vec![1, 2, 3]), + content_type: "application/octet-stream".to_string(), + }, ); attachments.insert( "attachment_2".to_string(), - ("file2.txt".to_string(), Some(vec![4, 5, 6])), + Attachment { + filename: test_file_path.into_os_string().into_string().unwrap(), + data: None, // This will cause the code to read from disk + content_type: "text/plain".to_string(), + }, ); let run_create = RunCreateWithAttachments { @@ -78,70 +97,85 @@ async fn test_tracing_client_submit_run_create() { m.assert_async().await; } -// now test run update -#[tokio::test] -async fn test_tracing_client_submit_run_update() { - let mut server = Server::new_async().await; - let m = server - .mock("POST", "/") - .match_header( - "content-type", - Matcher::Regex(r"multipart/form-data.*".to_string()), - ) - .match_body(Matcher::AllOf(vec![ - Matcher::Regex("patch\\.test_id".to_string()), - Matcher::Regex("patch\\.test_id\\.outputs".to_string()), - Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_1".to_string()), - Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_2".to_string()), - ])) - .with_status(200) - .create_async() - .await; - - let config = ClientConfig { - endpoint: server.url(), - queue_capacity: 10, - batch_size: 1, // batch size is 1 to ensure each message is sent immediately - batch_timeout: Duration::from_secs(1), - }; - - let client = TracingClient::new(config).unwrap(); - - let mut attachments = HashMap::new(); - attachments.insert( - "attachment_1".to_string(), - ("file1.txt".to_string(), Some(vec![1, 2, 3])), - ); - attachments.insert( - "attachment_2".to_string(), - ("file2.txt".to_string(), Some(vec![4, 5, 6])), - ); - - let run_update = RunUpdateWithAttachments { - run_update: RunUpdate { - common: RunCommon { - id: String::from("test_id"), - trace_id: String::from("trace_id"), - dotted_order: String::from("1.1"), - parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), - error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), - session_name: Some("Session Name".to_string()), - session_id: None, - }, - end_time: TimeValue::UnsignedInt(1697462400000), - outputs: Some(serde_json::json!({"output_key": "output_value"})), - }, - attachments, - }; - - client.submit_run_update(run_update).await.unwrap(); - - // shutdown the client to ensure all messages are processed - client.shutdown().await.unwrap(); - m.assert_async().await; -} +// #[tokio::test] +// async fn test_tracing_client_submit_run_update() { +// let mut server = Server::new_async().await; +// let m = server +// .mock("POST", "/") +// .match_header( +// "content-type", +// Matcher::Regex(r"multipart/form-data.*".to_string()), +// ) +// .match_body(Matcher::AllOf(vec![ +// Matcher::Regex("patch\\.test_id".to_string()), +// Matcher::Regex("patch\\.test_id\\.outputs".to_string()), +// Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_1".to_string()), +// Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_2".to_string()), +// ])) +// .with_status(200) +// .create_async() +// .await; +// +// let config = ClientConfig { +// endpoint: server.url(), +// queue_capacity: 10, +// batch_size: 1, // batch size is 1 to ensure each message is sent immediately +// batch_timeout: Duration::from_secs(1), +// }; +// +// let client = TracingClient::new(config).unwrap(); +// +// // Write a test file to disk for streaming +// let test_file_path = "test_file_update.txt"; +// let mut test_file = File::create(test_file_path).await.unwrap(); +// test_file +// .write_all(b"Test file content for update") +// .await +// .unwrap(); +// +// let mut attachments = HashMap::new(); +// attachments.insert( +// "attachment_1".to_string(), +// Attachment { +// filename: "file1.txt".to_string(), +// data: Some(vec![1, 2, 3]), +// content_type: "application/octet-stream".to_string(), +// }, +// ); +// attachments.insert( +// "attachment_2".to_string(), +// Attachment { +// filename: test_file_path.to_string(), +// data: None, // This will cause the code to read from disk +// content_type: "text/plain".to_string(), +// }, +// ); +// +// let run_update = RunUpdateWithAttachments { +// run_update: RunUpdate { +// common: RunCommon { +// id: String::from("test_id"), +// trace_id: String::from("trace_id"), +// dotted_order: String::from("1.1"), +// parent_run_id: None, +// extra: serde_json::json!({"extra_data": "value"}), +// error: None, +// serialized: serde_json::json!({"key": "value"}), +// inputs: serde_json::json!({"input": "value"}), +// events: serde_json::json!([{ "event": "event_data" }]), +// tags: serde_json::json!({"tag": "value"}), +// session_name: Some("Session Name".to_string()), +// session_id: None, +// }, +// end_time: TimeValue::UnsignedInt(1697462400000), +// outputs: Some(serde_json::json!({"output_key": "output_value"})), +// }, +// attachments, +// }; +// +// client.submit_run_update(run_update).await.unwrap(); +// +// // shutdown the client to ensure all messages are processed +// client.shutdown().await.unwrap(); +// m.assert_async().await; +// } From b7c550838cf9422c9125dceca86372799c1a4cf3 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 19 Oct 2024 16:38:18 -0700 Subject: [PATCH 12/31] inspect multipart form --- .../tests/tracing_client_test.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index f61b756cb..ca56dccf7 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -14,20 +14,20 @@ use tempfile::TempDir; #[tokio::test] async fn test_tracing_client_submit_run_create() { let mut server = Server::new_async().await; + // let mut captured_body: Vec = Vec::new(); + let m = server .mock("POST", "/") - .match_header( - "content-type", - Matcher::Regex(r"multipart/form-data.*".to_string()), - ) - .match_body(Matcher::AllOf(vec![ - Matcher::Regex("post\\.test_id".to_string()), - Matcher::Regex("post\\.test_id\\.inputs".to_string()), - Matcher::Regex("post\\.test_id\\.outputs".to_string()), - Matcher::Regex("post\\.test_id\\.attachments\\.attachment_1".to_string()), - Matcher::Regex("post\\.test_id\\.attachments\\.attachment_2".to_string()), - ])) .with_status(200) + .with_body_from_request(move |req| { + let body = req.body().unwrap(); + // print it as a utf-8 string + println!( + "Captured body: {:?}", + String::from_utf8(body.to_vec()).unwrap() + ); + vec![0] + }) .create_async() .await; @@ -59,7 +59,7 @@ async fn test_tracing_client_submit_run_create() { "attachment_2".to_string(), Attachment { filename: test_file_path.into_os_string().into_string().unwrap(), - data: None, // This will cause the code to read from disk + data: None, // this will cause the processor to read from disk content_type: "text/plain".to_string(), }, ); From f7e3e2fd39c81cd4a91dcdbeb11b383ae84c8c66 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sun, 20 Oct 2024 23:31:05 -0700 Subject: [PATCH 13/31] fix some issues --- .../langsmith_tracing_client/Cargo.lock | 598 +++++++++++++++++- .../langsmith_tracing_client/Cargo.toml | 3 + .../src/client/run.rs | 8 +- .../tests/tracing_client_test.rs | 140 +++- 4 files changed, 679 insertions(+), 70 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index 9374358e9..092a467e3 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -41,6 +41,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ascii" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14" + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -57,6 +63,15 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -78,18 +93,44 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -137,6 +178,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "chunked_transfer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "colored" version = "2.1.0" @@ -221,9 +277,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "percent-encoding", + "percent-encoding 2.3.1", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-channel" version = "0.3.31" @@ -293,7 +355,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -302,6 +364,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "groupable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32619942b8be646939eaf3db0602b39f5229b74575b67efc897811ded1db4e57" + [[package]] name = "h2" version = "0.4.6" @@ -379,6 +447,25 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.10.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" +dependencies = [ + "base64 0.9.3", + "httparse", + "language-tags", + "log 0.3.9", + "mime 0.2.6", + "num_cpus", + "time", + "traitobject", + "typeable", + "unicase 1.4.2", + "url 1.7.2", +] + [[package]] name = "hyper" version = "1.5.0" @@ -408,7 +495,7 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http", - "hyper", + "hyper 1.5.0", "hyper-util", "rustls", "rustls-pki-types", @@ -425,7 +512,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.5.0", "hyper-util", "native-tls", "tokio", @@ -444,7 +531,7 @@ dependencies = [ "futures-util", "http", "http-body", - "hyper", + "hyper 1.5.0", "pin-project-lite", "socket2", "tokio", @@ -475,6 +562,17 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -501,6 +599,22 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "iron" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6d308ca2d884650a8bf9ed2ff4cb13fbb2207b71f64cda11dc9b892067295e8" +dependencies = [ + "hyper 0.10.16", + "log 0.3.9", + "mime_guess 1.8.8", + "modifier", + "num_cpus", + "plugin", + "typemap", + "url 1.7.2", +] + [[package]] name = "itoa" version = "1.0.11" @@ -522,6 +636,7 @@ version = "0.1.0" dependencies = [ "chrono", "mockito", + "multipart", "reqwest", "serde", "serde_json", @@ -532,6 +647,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + [[package]] name = "lazy_static" version = "1.5.0" @@ -556,36 +677,72 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg", + "autocfg 1.4.0", "scopeguard", ] +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.22", +] + [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +dependencies = [ + "log 0.3.9", +] + [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "1.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3" +dependencies = [ + "mime 0.2.6", + "phf", + "phf_codegen", + "unicase 1.4.2", +] + [[package]] name = "mime_guess" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ - "mime", - "unicase", + "mime 0.3.17", + "unicase 2.7.0", ] [[package]] @@ -605,7 +762,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -622,10 +779,10 @@ dependencies = [ "http", "http-body", "http-body-util", - "hyper", + "hyper 1.5.0", "hyper-util", - "log", - "rand", + "log 0.4.22", + "rand 0.8.5", "regex", "serde_json", "serde_urlencoded", @@ -633,6 +790,44 @@ dependencies = [ "tokio", ] +[[package]] +name = "modifier" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" + +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "hyper 0.10.16", + "iron", + "log 0.4.22", + "mime 0.3.17", + "mime_guess 2.0.5", + "nickel", + "quick-error", + "rand 0.8.5", + "safemem", + "tempfile", + "tiny_http", + "twoway", +] + +[[package]] +name = "mustache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51956ef1c5d20a1384524d91e616fb44dfc7d8f249bf696d49c97dd3289ecab5" +dependencies = [ + "log 0.3.9", + "serde", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -640,7 +835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ "libc", - "log", + "log 0.4.22", "openssl", "openssl-probe", "openssl-sys", @@ -650,13 +845,44 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nickel" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5061a832728db2dacb61cefe0ce303b58f85764ec680e71d9138229640a46d9" +dependencies = [ + "groupable", + "hyper 0.10.16", + "lazy_static", + "log 0.3.9", + "modifier", + "mustache", + "plugin", + "regex", + "serde", + "serde_json", + "time", + "typemap", + "url 1.7.2", +] + [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg", + "autocfg 1.4.0", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", ] [[package]] @@ -680,7 +906,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -741,12 +967,57 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +dependencies = [ + "phf_shared", + "rand 0.6.5", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +dependencies = [ + "siphasher", + "unicase 1.4.2", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -765,6 +1036,15 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plugin" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" +dependencies = [ + "typemap", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -783,6 +1063,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.37" @@ -792,6 +1078,25 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -799,8 +1104,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", ] [[package]] @@ -810,9 +1125,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -822,13 +1152,84 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -866,7 +1267,7 @@ version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", @@ -875,18 +1276,18 @@ dependencies = [ "http", "http-body", "http-body-util", - "hyper", + "hyper 1.5.0", "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", "js-sys", - "log", - "mime", - "mime_guess", + "log 0.4.22", + "mime 0.3.17", + "mime_guess 2.0.5", "native-tls", "once_cell", - "percent-encoding", + "percent-encoding 2.3.1", "pin-project-lite", "rustls-pemfile", "serde", @@ -898,7 +1299,7 @@ dependencies = [ "tokio-native-tls", "tokio-util", "tower-service", - "url", + "url 2.5.2", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", @@ -933,7 +1334,7 @@ version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -985,6 +1386,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "schannel" version = "0.1.26" @@ -1006,7 +1413,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -1088,13 +1495,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] [[package]] @@ -1151,7 +1564,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] @@ -1199,6 +1612,30 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tiny_http" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e22cb179b63e5fc2d0b5be237dc107da072e2407809ac70a8ce85b93fe8f562" +dependencies = [ + "ascii", + "chrono", + "chunked_transfer", + "log 0.4.22", + "url 1.7.2", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -1302,19 +1739,58 @@ dependencies = [ "once_cell", ] +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +dependencies = [ + "unsafe-any", +] + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check 0.1.5", +] + [[package]] name = "unicase" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "version_check", + "version_check 0.9.5", ] [[package]] @@ -1338,12 +1814,32 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +dependencies = [ + "traitobject", +] + [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + [[package]] name = "url" version = "2.5.2" @@ -1351,8 +1847,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", - "percent-encoding", + "idna 0.5.0", + "percent-encoding 2.3.1", ] [[package]] @@ -1370,6 +1866,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.5" @@ -1385,6 +1887,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1409,7 +1917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", - "log", + "log 0.4.22", "once_cell", "proc-macro2", "quote", @@ -1481,6 +1989,28 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 5ce2e0570..237f4f981 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -14,3 +14,6 @@ thiserror = "1.0" mockito = "1.5.0" tokio-util = "0.7.12" tempfile = "3.13.0" + +[dev-dependencies] +multipart = "0.18.0" diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index c9c66f70f..5908b140f 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -9,14 +9,14 @@ pub struct Attachment { } // Must support both string (Py) and unsigned int (JS) -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] #[serde(untagged)] pub enum TimeValue { String(String), UnsignedInt(u64), } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunCommon { pub id: String, pub trace_id: String, @@ -32,7 +32,7 @@ pub struct RunCommon { pub session_name: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunCreate { pub common: RunCommon, pub name: String, @@ -43,7 +43,7 @@ pub struct RunCreate { pub reference_example_id: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunUpdate { pub common: RunCommon, pub end_time: TimeValue, diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index ca56dccf7..af5235093 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -8,9 +8,102 @@ use std::collections::HashMap; use std::fs::File; use std::time::Duration; // use tokio::io::AsyncWriteExt; -use std::io::{self, Write}; +use multipart::server::Multipart; +use std::io::{self, Read, Write}; use tempfile::TempDir; +#[derive(Debug)] +struct MultipartField { + name: String, + content_type: Option, + filename: Option, + data: String, +} + +fn handle_request(req: &mockito::Request) -> Vec { + let body = req.body().unwrap(); + + let content_type_headers = req.header("content-type"); + let content_type_str: String = content_type_headers + .iter() + .filter_map(|h| h.to_str().ok()) + .collect::>() + .join(", "); + + assert!(content_type_str.starts_with("multipart/form-data")); + + let boundary = content_type_str.split("boundary=").nth(1).unwrap(); + let mut mp = Multipart::with_body(body.as_slice(), boundary); + + let mut fields = Vec::new(); + + while let Some(mut field) = mp.read_entry().unwrap() { + let field_name = field.headers.name.to_string(); + let field_content_type = field.headers.content_type.map(|ct| ct.to_string()); + let field_filename = field.headers.filename.map(String::from); + + let mut content = String::new(); + field.data.read_to_string(&mut content).unwrap(); + + let multipart_field = MultipartField { + name: field_name, + content_type: field_content_type, + filename: field_filename, + data: content, + }; + + fields.push(multipart_field); + } + + fields +} + +fn create_run_create_with_attachments() -> RunCreateWithAttachments { + let mut attachments = HashMap::new(); + attachments.insert( + "attachment_1".to_string(), + Attachment { + filename: "file1.txt".to_string(), + data: Some(vec![1, 2, 3]), + content_type: "application/octet-stream".to_string(), + }, + ); + attachments.insert( + "attachment_2".to_string(), + Attachment { + filename: "test_file_create.txt".to_string(), + data: None, // this will cause the processor to read from disk + content_type: "text/plain".to_string(), + }, + ); + + RunCreateWithAttachments { + run_create: RunCreate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: Some("Session Name".to_string()), + }, + name: String::from("Run Name"), + start_time: TimeValue::UnsignedInt(1697462400000), + end_time: Some(TimeValue::UnsignedInt(1697466000000)), + outputs: serde_json::json!({"output_key": "output_value"}), + run_type: String::from("test_run_type"), + reference_example_id: None, + }, + attachments, + } +} + #[tokio::test] async fn test_tracing_client_submit_run_create() { let mut server = Server::new_async().await; @@ -20,12 +113,19 @@ async fn test_tracing_client_submit_run_create() { .mock("POST", "/") .with_status(200) .with_body_from_request(move |req| { - let body = req.body().unwrap(); - // print it as a utf-8 string - println!( - "Captured body: {:?}", - String::from_utf8(body.to_vec()).unwrap() - ); + let fields = handle_request(req); + + assert_eq!(fields.len(), 4); + assert_eq!(fields[0].name, "post.test_id"); + assert_eq!(fields[0].content_type, Some("application/json".to_string())); + assert_eq!(fields[0].filename, None); + // HELP ME WRITE ASSERTIONS FOR THE DATA + + let received_run: RunCreate = serde_json::from_str(&fields[0].data).unwrap(); + let expected_run = create_run_create_with_attachments(); + let expected_run_create = expected_run.run_create; + assert_eq!(received_run, expected_run_create); + vec![0] }) .create_async() @@ -64,31 +164,7 @@ async fn test_tracing_client_submit_run_create() { }, ); - let run_create = RunCreateWithAttachments { - run_create: RunCreate { - common: RunCommon { - id: String::from("test_id"), - trace_id: String::from("trace_id"), - dotted_order: String::from("1.1"), - parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), - error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), - session_id: None, - session_name: Some("Session Name".to_string()), - }, - name: String::from("Run Name"), - start_time: TimeValue::UnsignedInt(1697462400000), - end_time: Some(TimeValue::UnsignedInt(1697466000000)), - outputs: serde_json::json!({"output_key": "output_value"}), - run_type: String::from("test_run_type"), - reference_example_id: None, - }, - attachments, - }; + let run_create = create_run_create_with_attachments(); client.submit_run_create(run_create).await.unwrap(); From 85315cd8f7cefa2da5dda9f626500d200cf67920 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 00:48:24 -0700 Subject: [PATCH 14/31] use arc::mutex to capture request body --- .../tests/tracing_client_test.rs | 80 +++++++++++++------ 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index af5235093..ac8047410 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -11,6 +11,7 @@ use std::time::Duration; use multipart::server::Multipart; use std::io::{self, Read, Write}; use tempfile::TempDir; +use std::sync::{Arc, Mutex}; #[derive(Debug)] struct MultipartField { @@ -20,16 +21,7 @@ struct MultipartField { data: String, } -fn handle_request(req: &mockito::Request) -> Vec { - let body = req.body().unwrap(); - - let content_type_headers = req.header("content-type"); - let content_type_str: String = content_type_headers - .iter() - .filter_map(|h| h.to_str().ok()) - .collect::>() - .join(", "); - +fn handle_request(body: Vec, content_type_str: String) -> Vec { assert!(content_type_str.starts_with("multipart/form-data")); let boundary = content_type_str.split("boundary=").nth(1).unwrap(); @@ -107,26 +99,24 @@ fn create_run_create_with_attachments() -> RunCreateWithAttachments { #[tokio::test] async fn test_tracing_client_submit_run_create() { let mut server = Server::new_async().await; - // let mut captured_body: Vec = Vec::new(); + let captured_request: Arc, String)>> = Arc::new(Mutex::new((Vec::new(), String::new()))); + let captured_request_clone = Arc::clone(&captured_request); let m = server .mock("POST", "/") + .expect(1) .with_status(200) .with_body_from_request(move |req| { - let fields = handle_request(req); - - assert_eq!(fields.len(), 4); - assert_eq!(fields[0].name, "post.test_id"); - assert_eq!(fields[0].content_type, Some("application/json".to_string())); - assert_eq!(fields[0].filename, None); - // HELP ME WRITE ASSERTIONS FOR THE DATA - - let received_run: RunCreate = serde_json::from_str(&fields[0].data).unwrap(); - let expected_run = create_run_create_with_attachments(); - let expected_run_create = expected_run.run_create; - assert_eq!(received_run, expected_run_create); - - vec![0] + let mut request = captured_request_clone.lock().unwrap(); + request.0 = req.body().unwrap().to_vec(); + let content_type_headers = req.header("content-type"); + let content_type_str: String = content_type_headers + .iter() + .filter_map(|h| h.to_str().ok()) + .collect::>() + .join(", "); + request.1 = content_type_str; + vec![] // return empty response body }) .create_async() .await; @@ -164,13 +154,51 @@ async fn test_tracing_client_submit_run_create() { }, ); - let run_create = create_run_create_with_attachments(); + let run_create = RunCreateWithAttachments { + run_create: RunCreate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: serde_json::json!({"extra_data": "value"}), + error: None, + serialized: serde_json::json!({"key": "value"}), + inputs: serde_json::json!({"input": "value"}), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!({"tag": "value"}), + session_id: None, + session_name: Some("Session Name".to_string()), + }, + name: String::from("Run Name"), + start_time: TimeValue::UnsignedInt(1697462400000), + end_time: Some(TimeValue::UnsignedInt(1697466000000)), + outputs: serde_json::json!({"output_key": "output_value"}), + run_type: String::from("test_run_type"), + reference_example_id: None, + }, + attachments, + }; client.submit_run_create(run_create).await.unwrap(); // shutdown the client to ensure all messages are processed client.shutdown().await.unwrap(); m.assert_async().await; + + let req = captured_request.lock().unwrap().clone(); + let fields = handle_request(req.0, req.1); + + assert_eq!(fields.len(), 5); + assert_eq!(fields[0].name, "post.test_id"); + assert_eq!(fields[0].content_type, Some("application/json".to_string())); + assert_eq!(fields[0].filename, None); + + let received_run: RunCreate = serde_json::from_str(&fields[0].data).unwrap(); + let expected_run = create_run_create_with_attachments(); + let expected_run_create = expected_run.run_create; + assert_eq!(received_run, expected_run_create); + } // #[tokio::test] From a9d71ad5502b106e6fa44f968cea6269d9084639 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 10:42:34 -0700 Subject: [PATCH 15/31] use bytes --- .../src/client/processor.rs | 39 ++++++++---- .../src/client/run.rs | 19 +++--- .../tests/test_run.rs | 40 +++++++----- .../tests/tracing_client_test.rs | 62 +++---------------- 4 files changed, 72 insertions(+), 88 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index c4cddcdb7..72286e576 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -126,30 +126,45 @@ impl RunProcessor { ) -> Result<(), TracingClientError> { let run = &run_with_attachments.run_create; let run_id = &run.common.id; + let run_io = &run.common.io; + let inputs = &run_io.inputs; + let outputs = &run_io.outputs; + + let mut inputs_bytes: Vec = Vec::new(); + let mut outputs_bytes: Vec = Vec::new(); + let mut run_bytes: Vec = Vec::new(); + + serde_json::to_writer(&mut inputs_bytes, inputs)?; + serde_json::to_writer(&mut outputs_bytes, outputs)?; + serde_json::to_writer(&mut run_bytes, run)?; - let mut run_json = serde_json::to_value(run)?; - let inputs = run_json - .as_object_mut() - .and_then(|obj| obj.remove("inputs")) - .unwrap_or(Value::Null); - let outputs = run_json - .as_object_mut() - .and_then(|obj| obj.remove("outputs")) - .unwrap_or(Value::Null); + + //let mut run_json = serde_json::to_value(run)?; + // let inputs = run_json + // .as_object_mut() + // .and_then(|obj| obj.remove("inputs")) + // .unwrap_or(Value::Null); + // let outputs = run_json + // .as_object_mut() + // .and_then(|obj| obj.remove("outputs")) + // .unwrap_or(Value::Null); *form = std::mem::take(form).part( format!("post.{}", run_id), - Part::text(run_json.to_string()).mime_str("application/json")?, + Part::bytes(run_bytes) + .mime_str("application/json")?, ); *form = std::mem::take(form).part( format!("post.{}.inputs", run_id), - Part::text(inputs.to_string()).mime_str("application/json")?, + Part::bytes(inputs_bytes) + .mime_str("application/json")?, ); *form = std::mem::take(form).part( format!("post.{}.outputs", run_id), - Part::text(outputs.to_string()).mime_str("application/json")?, + Part::bytes(outputs_bytes) + .mime_str("application/json")?, ); for (ref_name, attachment) in &run_with_attachments.attachments { diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 5908b140f..9067a009a 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -16,20 +16,26 @@ pub enum TimeValue { UnsignedInt(u64), } +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct RunIO { + pub inputs: serde_json::Value, + pub outputs: Option, +} + #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunCommon { pub id: String, pub trace_id: String, pub dotted_order: String, pub parent_run_id: Option, - pub extra: serde_json::Value, + pub extra: Option, pub error: Option, - pub serialized: serde_json::Value, - pub inputs: serde_json::Value, + pub serialized: Option, pub events: serde_json::Value, pub tags: serde_json::Value, pub session_id: Option, pub session_name: Option, + pub io: RunIO, } #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -38,7 +44,6 @@ pub struct RunCreate { pub name: String, pub start_time: TimeValue, pub end_time: Option, - pub outputs: serde_json::Value, pub run_type: String, pub reference_example_id: Option, } @@ -60,8 +65,8 @@ pub struct RunUpdateWithAttachments { pub attachments: HashMap, } -pub enum QueuedRun { - Create(RunCreateWithAttachments), - Update(RunUpdateWithAttachments), +pub enum QueuedRun<'a> { + Create(&'a RunCreateWithAttachments), + Update(&'a RunUpdateWithAttachments), Shutdown, } diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index 31d3bb4ba..dc0c9042c 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -1,4 +1,4 @@ -use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunUpdate, TimeValue}; +use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunUpdate, TimeValue, RunIO}; use serde_json; #[test] @@ -8,14 +8,17 @@ fn test_run_common() { trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), dotted_order: String::from("1.1"), parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), + extra: Some(serde_json::json!({"extra_data": "value"})), error: Some(String::from("error message")), - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), + serialized: Some(serde_json::json!({"key": "value"})), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), session_id: Some("efghijkl-7654-3210-fedc-ba9876543210".to_string()), session_name: None, + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: Some(serde_json::json!({"output": "value"})), + }, }; let serialized = serde_json::to_string(&run_common).unwrap(); @@ -30,14 +33,17 @@ fn test_run_create_with_string_time() { trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), dotted_order: String::from("1.1"), parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), + extra: None, error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), + serialized: None, events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: Some("Session Name".to_string()), + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: Some(serde_json::json!({"output": "value"})), + } }; let run_create = RunCreate { @@ -45,7 +51,6 @@ fn test_run_create_with_string_time() { name: String::from("Run Name"), start_time: TimeValue::String("2024-10-16T12:00:00Z".to_string()), end_time: Some(TimeValue::String("2024-10-16T14:00:00Z".to_string())), - outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), reference_example_id: None, }; @@ -63,14 +68,17 @@ fn test_run_create_with_timestamp() { trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), dotted_order: String::from("1.1"), parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), + extra: Some(serde_json::json!({"extra_data": "value"})), error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), + serialized: Some(serde_json::json!({"key": "value"})), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: None, + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: None, + } }; let run_create = RunCreate { @@ -78,7 +86,6 @@ fn test_run_create_with_timestamp() { name: String::from("Run Name"), start_time: TimeValue::UnsignedInt(1697462400000), end_time: Some(TimeValue::UnsignedInt(1697466000000)), - outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), reference_example_id: None, }; @@ -96,14 +103,17 @@ fn test_run_update() { trace_id: String::from("fedcba98-7654-3210-fedc-ba9876543210"), dotted_order: String::from("1.1"), parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), + extra: None, error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), + serialized: None, events: serde_json::json!([]), tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: None, + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: None, + } }; let run_update = RunUpdate { diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index ac8047410..76010f73a 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -1,6 +1,6 @@ use langsmith_tracing_client::client::run::{ Attachment, RunCommon, RunCreate, RunCreateWithAttachments, RunUpdate, - RunUpdateWithAttachments, TimeValue, + RunUpdateWithAttachments, TimeValue, RunIO }; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; use mockito::{Matcher, Server}; @@ -50,52 +50,6 @@ fn handle_request(body: Vec, content_type_str: String) -> Vec RunCreateWithAttachments { - let mut attachments = HashMap::new(); - attachments.insert( - "attachment_1".to_string(), - Attachment { - filename: "file1.txt".to_string(), - data: Some(vec![1, 2, 3]), - content_type: "application/octet-stream".to_string(), - }, - ); - attachments.insert( - "attachment_2".to_string(), - Attachment { - filename: "test_file_create.txt".to_string(), - data: None, // this will cause the processor to read from disk - content_type: "text/plain".to_string(), - }, - ); - - RunCreateWithAttachments { - run_create: RunCreate { - common: RunCommon { - id: String::from("test_id"), - trace_id: String::from("trace_id"), - dotted_order: String::from("1.1"), - parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), - error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), - session_id: None, - session_name: Some("Session Name".to_string()), - }, - name: String::from("Run Name"), - start_time: TimeValue::UnsignedInt(1697462400000), - end_time: Some(TimeValue::UnsignedInt(1697466000000)), - outputs: serde_json::json!({"output_key": "output_value"}), - run_type: String::from("test_run_type"), - reference_example_id: None, - }, - attachments, - } -} - #[tokio::test] async fn test_tracing_client_submit_run_create() { let mut server = Server::new_async().await; @@ -161,19 +115,21 @@ async fn test_tracing_client_submit_run_create() { trace_id: String::from("trace_id"), dotted_order: String::from("1.1"), parent_run_id: None, - extra: serde_json::json!({"extra_data": "value"}), + extra: Some(serde_json::json!({"extra_data": "value"})), error: None, - serialized: serde_json::json!({"key": "value"}), - inputs: serde_json::json!({"input": "value"}), + serialized: Some(serde_json::json!({"key": "value"})), events: serde_json::json!([{ "event": "event_data" }]), tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: Some("Session Name".to_string()), + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: Some(serde_json::json!({"output": "value"})), + } }, name: String::from("Run Name"), start_time: TimeValue::UnsignedInt(1697462400000), end_time: Some(TimeValue::UnsignedInt(1697466000000)), - outputs: serde_json::json!({"output_key": "output_value"}), run_type: String::from("test_run_type"), reference_example_id: None, }, @@ -195,9 +151,7 @@ async fn test_tracing_client_submit_run_create() { assert_eq!(fields[0].filename, None); let received_run: RunCreate = serde_json::from_str(&fields[0].data).unwrap(); - let expected_run = create_run_create_with_attachments(); - let expected_run_create = expected_run.run_create; - assert_eq!(received_run, expected_run_create); + //assert_eq!(received_run, run_create.run_create); } From 5ba4b10481557fed918ca6870bbb58978e6b75b8 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 15:13:07 -0700 Subject: [PATCH 16/31] finish writing assertions --- .../src/client/processor.rs | 80 +++++++------ .../src/client/run.rs | 24 ++-- .../src/client/tracing_client.rs | 6 +- .../tests/test_run.rs | 19 +-- .../tests/tracing_client_test.rs | 113 +++++++++++++----- 5 files changed, 145 insertions(+), 97 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 72286e576..1ecaf9ccc 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -1,6 +1,6 @@ use crate::client::errors::TracingClientError; use crate::client::run::QueuedRun; -use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; +use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use crate::client::tracing_client::ClientConfig; use reqwest::multipart::{Form, Part}; use serde_json::Value; @@ -90,8 +90,8 @@ impl RunProcessor { for queued_run in batch { match queued_run { - QueuedRun::Create(run_create_with_attachments) => { - self.add_run_create_to_form(run_create_with_attachments, &mut form) + QueuedRun::Create(run_create_extended) => { + self.add_run_create_to_form(run_create_extended, &mut form) .await?; } QueuedRun::Update(run_update_with_attachments) => { @@ -121,53 +121,40 @@ impl RunProcessor { async fn add_run_create_to_form( &self, - run_with_attachments: &RunCreateWithAttachments, + run_create_extended: &RunCreateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { - let run = &run_with_attachments.run_create; + let run = &run_create_extended.run_create; let run_id = &run.common.id; - let run_io = &run.common.io; - let inputs = &run_io.inputs; - let outputs = &run_io.outputs; + let (inputs, outputs) = ( + &run_create_extended.io.inputs, + &run_create_extended.io.outputs, + ); - let mut inputs_bytes: Vec = Vec::new(); - let mut outputs_bytes: Vec = Vec::new(); - let mut run_bytes: Vec = Vec::new(); + let (mut inputs_bytes, mut outputs_bytes, mut run_bytes) = + (Vec::new(), Vec::new(), Vec::new()); serde_json::to_writer(&mut inputs_bytes, inputs)?; serde_json::to_writer(&mut outputs_bytes, outputs)?; serde_json::to_writer(&mut run_bytes, run)?; - - //let mut run_json = serde_json::to_value(run)?; - // let inputs = run_json - // .as_object_mut() - // .and_then(|obj| obj.remove("inputs")) - // .unwrap_or(Value::Null); - // let outputs = run_json - // .as_object_mut() - // .and_then(|obj| obj.remove("outputs")) - // .unwrap_or(Value::Null); - *form = std::mem::take(form).part( format!("post.{}", run_id), - Part::bytes(run_bytes) - .mime_str("application/json")?, + Part::bytes(run_bytes).mime_str("application/json")?, ); *form = std::mem::take(form).part( format!("post.{}.inputs", run_id), - Part::bytes(inputs_bytes) - .mime_str("application/json")?, + Part::bytes(inputs_bytes).mime_str("application/json")?, ); *form = std::mem::take(form).part( format!("post.{}.outputs", run_id), - Part::bytes(outputs_bytes) - .mime_str("application/json")?, + Part::bytes(outputs_bytes).mime_str("application/json")?, ); - for (ref_name, attachment) in &run_with_attachments.attachments { + for attachment in &run_create_extended.attachments { + let ref_name = &attachment.ref_name; let filename = &attachment.filename; let data = &attachment.data; let content_type = &attachment.content_type; @@ -193,9 +180,18 @@ impl RunProcessor { let stream = ReaderStream::new(file); let body = reqwest::Body::wrap_stream(stream); + // Extract just the last component of the file path + let file_name = file_path + .file_name() + .ok_or_else(|| { + TracingClientError::IoError( + "Failed to extract filename from path".to_string(), + ) + })? + .to_string_lossy(); + let part = Part::stream_with_length(body, file_size) - // TODO only take the last component of the path as the filename - .file_name(filename.clone()) + .file_name(file_name.into_owned()) .mime_str(content_type)?; *form = std::mem::take(form).part(part_name, part); @@ -207,7 +203,7 @@ impl RunProcessor { async fn add_run_update_to_form( &self, - run_with_attachments: &RunUpdateWithAttachments, + run_with_attachments: &RunUpdateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { let run = &run_with_attachments.run_update; @@ -231,7 +227,8 @@ impl RunProcessor { ); } - for (ref_name, attachment) in &run_with_attachments.attachments { + for attachment in &run_with_attachments.attachments { + let ref_name = &attachment.ref_name; let filename = &attachment.filename; let data = &attachment.data; let content_type = &attachment.content_type; @@ -245,7 +242,7 @@ impl RunProcessor { .mime_str(content_type)?, ); } else { - // Read the file from disk and stream it + // read the file from disk and stream it to avoid loading the entire file into memory let file_path = std::path::Path::new(filename); let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) @@ -257,8 +254,21 @@ impl RunProcessor { let stream = ReaderStream::new(file); let body = reqwest::Body::wrap_stream(stream); + // extract just the last component of the file path + let file_name = file_path + .file_name() + .ok_or_else(|| { + TracingClientError::IoError( + "Failed to extract filename from path".to_string(), + ) + })? + .to_string_lossy() + .to_string(); + + println!("file_name: {:?}", file_name); + let part = Part::stream_with_length(body, file_size) - .file_name(filename.clone()) + .file_name(file_name) .mime_str(content_type)?; *form = std::mem::take(form).part(part_name, part); diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 9067a009a..3df9ffd13 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; -use std::collections::HashMap; // Map attachment ref to tuple of filename, optional bytes pub struct Attachment { + pub ref_name: String, pub filename: String, pub data: Option>, pub content_type: String, @@ -16,7 +16,7 @@ pub enum TimeValue { UnsignedInt(u64), } -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(PartialEq, Debug)] pub struct RunIO { pub inputs: serde_json::Value, pub outputs: Option, @@ -35,11 +35,11 @@ pub struct RunCommon { pub tags: serde_json::Value, pub session_id: Option, pub session_name: Option, - pub io: RunIO, } #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunCreate { + #[serde(flatten)] pub common: RunCommon, pub name: String, pub start_time: TimeValue, @@ -50,23 +50,25 @@ pub struct RunCreate { #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct RunUpdate { + #[serde(flatten)] pub common: RunCommon, pub end_time: TimeValue, - pub outputs: Option, } -pub struct RunCreateWithAttachments { +pub struct RunCreateExtended { pub run_create: RunCreate, - pub attachments: HashMap, + pub io: RunIO, + pub attachments: Vec, } -pub struct RunUpdateWithAttachments { +pub struct RunUpdateExtended { pub run_update: RunUpdate, - pub attachments: HashMap, + pub io: RunIO, + pub attachments: Vec, } -pub enum QueuedRun<'a> { - Create(&'a RunCreateWithAttachments), - Update(&'a RunUpdateWithAttachments), +pub enum QueuedRun { + Create(RunCreateExtended), + Update(RunUpdateExtended), Shutdown, } diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs index 42bb73cf8..c210b1ebf 100644 --- a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -1,7 +1,7 @@ use crate::client::errors::TracingClientError; use crate::client::processor::RunProcessor; use crate::client::run::QueuedRun; -use crate::client::run::{RunCreateWithAttachments, RunUpdateWithAttachments}; +use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use std::time::Duration; use tokio::sync::mpsc::{self, Sender}; use tokio::task::JoinHandle; @@ -37,7 +37,7 @@ impl TracingClient { pub async fn submit_run_create( &self, - run: RunCreateWithAttachments, + run: RunCreateExtended, ) -> Result<(), TracingClientError> { let queued_run = QueuedRun::Create(run); @@ -49,7 +49,7 @@ impl TracingClient { pub async fn submit_run_update( &self, - run: RunUpdateWithAttachments, + run: RunUpdateExtended, ) -> Result<(), TracingClientError> { let queued_run = QueuedRun::Update(run); diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index dc0c9042c..c915733b2 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -1,4 +1,4 @@ -use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunUpdate, TimeValue, RunIO}; +use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunIO, RunUpdate, TimeValue}; use serde_json; #[test] @@ -15,10 +15,6 @@ fn test_run_common() { tags: serde_json::json!({"tag": "value"}), session_id: Some("efghijkl-7654-3210-fedc-ba9876543210".to_string()), session_name: None, - io: RunIO { - inputs: serde_json::json!({"input": "value"}), - outputs: Some(serde_json::json!({"output": "value"})), - }, }; let serialized = serde_json::to_string(&run_common).unwrap(); @@ -40,10 +36,6 @@ fn test_run_create_with_string_time() { tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: Some("Session Name".to_string()), - io: RunIO { - inputs: serde_json::json!({"input": "value"}), - outputs: Some(serde_json::json!({"output": "value"})), - } }; let run_create = RunCreate { @@ -75,10 +67,6 @@ fn test_run_create_with_timestamp() { tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: None, - io: RunIO { - inputs: serde_json::json!({"input": "value"}), - outputs: None, - } }; let run_create = RunCreate { @@ -110,16 +98,11 @@ fn test_run_update() { tags: serde_json::json!({"tag": "value"}), session_id: None, session_name: None, - io: RunIO { - inputs: serde_json::json!({"input": "value"}), - outputs: None, - } }; let run_update = RunUpdate { common: run_common, end_time: TimeValue::String("2024-10-16T14:00:00Z".to_string()), - outputs: Some(serde_json::json!({"output_key": "output_value"})), }; let serialized = serde_json::to_string(&run_update).unwrap(); diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index 76010f73a..d53a6c046 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -1,6 +1,6 @@ use langsmith_tracing_client::client::run::{ - Attachment, RunCommon, RunCreate, RunCreateWithAttachments, RunUpdate, - RunUpdateWithAttachments, TimeValue, RunIO + Attachment, RunCommon, RunCreate, RunCreateExtended, RunIO, RunUpdate, RunUpdateExtended, + TimeValue, }; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; use mockito::{Matcher, Server}; @@ -10,8 +10,8 @@ use std::time::Duration; // use tokio::io::AsyncWriteExt; use multipart::server::Multipart; use std::io::{self, Read, Write}; -use tempfile::TempDir; use std::sync::{Arc, Mutex}; +use tempfile::TempDir; #[derive(Debug)] struct MultipartField { @@ -53,7 +53,8 @@ fn handle_request(body: Vec, content_type_str: String) -> Vec, String)>> = Arc::new(Mutex::new((Vec::new(), String::new()))); + let captured_request: Arc, String)>> = + Arc::new(Mutex::new((Vec::new(), String::new()))); let captured_request_clone = Arc::clone(&captured_request); let m = server @@ -90,25 +91,21 @@ async fn test_tracing_client_submit_run_create() { let mut test_file = File::create(&test_file_path).unwrap(); writeln!(test_file, "Test file content for create").unwrap(); - let mut attachments = HashMap::new(); - attachments.insert( - "attachment_1".to_string(), - Attachment { - filename: "file1.txt".to_string(), - data: Some(vec![1, 2, 3]), - content_type: "application/octet-stream".to_string(), - }, - ); - attachments.insert( - "attachment_2".to_string(), - Attachment { - filename: test_file_path.into_os_string().into_string().unwrap(), - data: None, // this will cause the processor to read from disk - content_type: "text/plain".to_string(), - }, - ); + let mut attachments = Vec::new(); + attachments.push(Attachment { + ref_name: "attachment_1".to_string(), + filename: "file1.txt".to_string(), + data: Some(vec![1, 2, 3]), + content_type: "application/octet-stream".to_string(), + }); + attachments.push(Attachment { + ref_name: "attachment_2".to_string(), + filename: test_file_path.into_os_string().into_string().unwrap(), + data: None, // this will cause the processor to read from disk + content_type: "text/plain".to_string(), + }); - let run_create = RunCreateWithAttachments { + let run_create = RunCreateExtended { run_create: RunCreate { common: RunCommon { id: String::from("test_id"), @@ -119,21 +116,21 @@ async fn test_tracing_client_submit_run_create() { error: None, serialized: Some(serde_json::json!({"key": "value"})), events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), + tags: serde_json::json!([{"tag": "value"}]), session_id: None, session_name: Some("Session Name".to_string()), - io: RunIO { - inputs: serde_json::json!({"input": "value"}), - outputs: Some(serde_json::json!({"output": "value"})), - } }, name: String::from("Run Name"), start_time: TimeValue::UnsignedInt(1697462400000), end_time: Some(TimeValue::UnsignedInt(1697466000000)), - run_type: String::from("test_run_type"), + run_type: String::from("chain"), reference_example_id: None, }, attachments, + io: RunIO { + inputs: serde_json::json!({"input": "value"}), + outputs: Some(serde_json::json!({"output": "value"})), + }, }; client.submit_run_create(run_create).await.unwrap(); @@ -146,13 +143,69 @@ async fn test_tracing_client_submit_run_create() { let fields = handle_request(req.0, req.1); assert_eq!(fields.len(), 5); + + // assert run fields assert_eq!(fields[0].name, "post.test_id"); assert_eq!(fields[0].content_type, Some("application/json".to_string())); assert_eq!(fields[0].filename, None); + let received_run: serde_json::Value = serde_json::from_str(&fields[0].data).unwrap(); + assert_eq!(received_run["id"], "test_id"); + assert_eq!(received_run["trace_id"], "trace_id"); + assert_eq!(received_run["dotted_order"], "1.1"); + assert_eq!(received_run["parent_run_id"], serde_json::Value::Null); + assert_eq!( + received_run["extra"], + serde_json::json!({"extra_data": "value"}) + ); + assert_eq!(received_run["error"], serde_json::Value::Null); + assert_eq!( + received_run["serialized"], + serde_json::json!({"key": "value"}) + ); + assert_eq!( + received_run["events"], + serde_json::json!([{ "event": "event_data" }]) + ); + assert_eq!(received_run["tags"], serde_json::json!([{"tag": "value"}])); + assert_eq!(received_run["session_name"], "Session Name"); + assert_eq!(received_run["session_id"], serde_json::Value::Null); + assert_eq!(received_run["name"], "Run Name"); + assert_eq!(received_run["start_time"], 1697462400000i64); + assert_eq!(received_run["end_time"], 1697466000000i64); + assert_eq!(received_run["run_type"], "chain"); + assert_eq!( + received_run["reference_example_id"], + serde_json::Value::Null + ); - let received_run: RunCreate = serde_json::from_str(&fields[0].data).unwrap(); - //assert_eq!(received_run, run_create.run_create); + // assert inputs fields + assert_eq!(fields[1].name, "post.test_id.inputs"); + assert_eq!(fields[1].content_type, Some("application/json".to_string())); + assert_eq!(fields[1].filename, None); + let received_inputs: serde_json::Value = serde_json::from_str(&fields[1].data).unwrap(); + assert_eq!(received_inputs, serde_json::json!({"input": "value"})); + + // assert outputs fields + assert_eq!(fields[2].name, "post.test_id.outputs"); + assert_eq!(fields[2].content_type, Some("application/json".to_string())); + assert_eq!(fields[2].filename, None); + let received_outputs: serde_json::Value = serde_json::from_str(&fields[2].data).unwrap(); + assert_eq!(received_outputs, serde_json::json!({"output": "value"})); + + // assert attachment_1 fields + assert_eq!(fields[3].name, "post.test_id.attachments.attachment_1"); + assert_eq!( + fields[3].content_type, + Some("application/octet-stream".to_string()) + ); + assert_eq!(fields[3].filename, Some("file1.txt".to_string())); + assert_eq!(fields[3].data, "\u{1}\u{2}\u{3}"); + // assert attachment_2 fields + assert_eq!(fields[4].name, "post.test_id.attachments.attachment_2"); + assert_eq!(fields[4].content_type, Some("text/plain".to_string())); + assert_eq!(fields[4].filename, Some("test_file_create.txt".to_string())); + assert_eq!(fields[4].data, "Test file content for create\n"); } // #[tokio::test] From 5f5f76864deb36351720ad46142b1232b83e8d32 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 15:37:05 -0700 Subject: [PATCH 17/31] avoid excessive clones --- .../src/client/processor.rs | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 1ecaf9ccc..4ff367dcc 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -77,7 +77,7 @@ impl RunProcessor { &self, buffer: &mut Vec, ) -> Result<(), TracingClientError> { - if let Err(e) = self.send_batch(buffer).await { + if let Err(e) = self.send_batch(std::mem::take(buffer)).await { // todo: retry logic? eprintln!("Error sending batch: {}", e); } @@ -85,7 +85,7 @@ impl RunProcessor { Ok(()) } - async fn send_batch(&self, batch: &[QueuedRun]) -> Result<(), TracingClientError> { + async fn send_batch(&self, batch: Vec) -> Result<(), TracingClientError> { let mut form = Form::new(); for queued_run in batch { @@ -94,8 +94,8 @@ impl RunProcessor { self.add_run_create_to_form(run_create_extended, &mut form) .await?; } - QueuedRun::Update(run_update_with_attachments) => { - self.add_run_update_to_form(run_update_with_attachments, &mut form) + QueuedRun::Update(run_update_extended) => { + self.add_run_update_to_form(run_update_extended, &mut form) .await?; } QueuedRun::Shutdown => { @@ -121,22 +121,17 @@ impl RunProcessor { async fn add_run_create_to_form( &self, - run_create_extended: &RunCreateExtended, + run_create_extended: RunCreateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { - let run = &run_create_extended.run_create; + let run = run_create_extended.run_create; let run_id = &run.common.id; - let (inputs, outputs) = ( - &run_create_extended.io.inputs, - &run_create_extended.io.outputs, - ); - - let (mut inputs_bytes, mut outputs_bytes, mut run_bytes) = - (Vec::new(), Vec::new(), Vec::new()); + let inputs = run_create_extended.io.inputs; + let outputs = run_create_extended.io.outputs; - serde_json::to_writer(&mut inputs_bytes, inputs)?; - serde_json::to_writer(&mut outputs_bytes, outputs)?; - serde_json::to_writer(&mut run_bytes, run)?; + let inputs_bytes = serde_json::to_vec(&inputs)?; + let outputs_bytes = serde_json::to_vec(&outputs)?; + let run_bytes = serde_json::to_vec(&run)?; *form = std::mem::take(form).part( format!("post.{}", run_id), @@ -153,23 +148,23 @@ impl RunProcessor { Part::bytes(outputs_bytes).mime_str("application/json")?, ); - for attachment in &run_create_extended.attachments { - let ref_name = &attachment.ref_name; - let filename = &attachment.filename; - let data = &attachment.data; - let content_type = &attachment.content_type; + for attachment in run_create_extended.attachments { + let ref_name = attachment.ref_name; + let filename = attachment.filename; + let data = attachment.data; + let content_type = attachment.content_type; let part_name = format!("post.{}.attachments.{}", run_id, ref_name); if let Some(data) = data { *form = std::mem::take(form).part( part_name, - Part::bytes(data.clone()) - .file_name(filename.clone()) - .mime_str(content_type)?, + Part::bytes(data) + .file_name(filename) + .mime_str(&content_type)?, ); } else { - // Read the file from disk and stream it - let file_path = std::path::Path::new(filename); + // read the file from disk and stream it to avoid loading the entire file into memory + let file_path = std::path::Path::new(&filename); let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) })?; @@ -192,7 +187,7 @@ impl RunProcessor { let part = Part::stream_with_length(body, file_size) .file_name(file_name.into_owned()) - .mime_str(content_type)?; + .mime_str(&content_type)?; *form = std::mem::take(form).part(part_name, part); } @@ -203,7 +198,7 @@ impl RunProcessor { async fn add_run_update_to_form( &self, - run_with_attachments: &RunUpdateExtended, + run_with_attachments: RunUpdateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { let run = &run_with_attachments.run_update; From d8e020d602de13d47e06be9f6d4c0f030ae29377 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 15:50:11 -0700 Subject: [PATCH 18/31] remove excessive clear --- rust/crates/langsmith_tracing_client/src/client/processor.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 4ff367dcc..7471a4b04 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -81,7 +81,6 @@ impl RunProcessor { // todo: retry logic? eprintln!("Error sending batch: {}", e); } - buffer.clear(); Ok(()) } @@ -259,9 +258,6 @@ impl RunProcessor { })? .to_string_lossy() .to_string(); - - println!("file_name: {:?}", file_name); - let part = Part::stream_with_length(body, file_size) .file_name(file_name) .mime_str(content_type)?; From 2cc93f3e946208216c73893b121a100713824dd9 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 17:52:12 -0700 Subject: [PATCH 19/31] fix tests --- .../src/client/processor.rs | 218 +++++++----------- 1 file changed, 87 insertions(+), 131 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 7471a4b04..26ea55ccc 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -1,5 +1,5 @@ use crate::client::errors::TracingClientError; -use crate::client::run::QueuedRun; +use crate::client::run::{Attachment, QueuedRun}; use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use crate::client::tracing_client::ClientConfig; use reqwest::multipart::{Form, Part}; @@ -90,11 +90,11 @@ impl RunProcessor { for queued_run in batch { match queued_run { QueuedRun::Create(run_create_extended) => { - self.add_run_create_to_form(run_create_extended, &mut form) + self.consume_run_create(run_create_extended, &mut form) .await?; } QueuedRun::Update(run_update_extended) => { - self.add_run_update_to_form(run_update_extended, &mut form) + self.consume_run_update(run_update_extended, &mut form) .await?; } QueuedRun::Shutdown => { @@ -118,154 +118,110 @@ impl RunProcessor { } } - async fn add_run_create_to_form( + async fn consume_run_create( &self, run_create_extended: RunCreateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { - let run = run_create_extended.run_create; - let run_id = &run.common.id; - let inputs = run_create_extended.io.inputs; - let outputs = run_create_extended.io.outputs; - - let inputs_bytes = serde_json::to_vec(&inputs)?; - let outputs_bytes = serde_json::to_vec(&outputs)?; - let run_bytes = serde_json::to_vec(&run)?; - - *form = std::mem::take(form).part( - format!("post.{}", run_id), - Part::bytes(run_bytes).mime_str("application/json")?, - ); - - *form = std::mem::take(form).part( - format!("post.{}.inputs", run_id), - Part::bytes(inputs_bytes).mime_str("application/json")?, - ); - - *form = std::mem::take(form).part( - format!("post.{}.outputs", run_id), - Part::bytes(outputs_bytes).mime_str("application/json")?, - ); - - for attachment in run_create_extended.attachments { - let ref_name = attachment.ref_name; - let filename = attachment.filename; - let data = attachment.data; - let content_type = attachment.content_type; - - let part_name = format!("post.{}.attachments.{}", run_id, ref_name); - if let Some(data) = data { - *form = std::mem::take(form).part( - part_name, - Part::bytes(data) - .file_name(filename) - .mime_str(&content_type)?, - ); - } else { - // read the file from disk and stream it to avoid loading the entire file into memory - let file_path = std::path::Path::new(&filename); - let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { - TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) - })?; - let file_size = metadata.len(); - let file = tokio::fs::File::open(file_path).await.map_err(|e| { - TracingClientError::IoError(format!("Failed to open file: {}", e)) - })?; - let stream = ReaderStream::new(file); - let body = reqwest::Body::wrap_stream(stream); - - // Extract just the last component of the file path - let file_name = file_path - .file_name() - .ok_or_else(|| { - TracingClientError::IoError( - "Failed to extract filename from path".to_string(), - ) - })? - .to_string_lossy(); - - let part = Part::stream_with_length(body, file_size) - .file_name(file_name.into_owned()) - .mime_str(&content_type)?; - - *form = std::mem::take(form).part(part_name, part); - } + let RunCreateExtended { + run_create, + io, + attachments, + } = run_create_extended; + let run_id = &run_create.common.id; + + self.add_json_part_to_form(form, format!("post.{}", run_id), &run_create)?; + self.add_json_part_to_form(form, format!("post.{}.inputs", run_id), &io.inputs)?; + self.add_json_part_to_form(form, format!("post.{}.outputs", run_id), &io.outputs)?; + + for attachment in attachments { + self.add_attachment_to_form(form, "post", run_id, attachment) + .await?; } Ok(()) } - async fn add_run_update_to_form( + async fn consume_run_update( &self, - run_with_attachments: RunUpdateExtended, + run_update_extended: RunUpdateExtended, form: &mut Form, ) -> Result<(), TracingClientError> { - let run = &run_with_attachments.run_update; - let run_id = &run.common.id; + let RunUpdateExtended { + run_update, + io, + attachments, + } = run_update_extended; + let run_id = &run_update.common.id; + + self.add_json_part_to_form(form, format!("post.{}", run_id), &run_update)?; + self.add_json_part_to_form(form, format!("post.{}.outputs", run_id), &io.outputs)?; + + for attachment in attachments { + self.add_attachment_to_form(form, "post", run_id, attachment) + .await?; + } - let mut run_json = serde_json::to_value(run)?; - let outputs = run_json - .as_object_mut() - .and_then(|obj| obj.remove("outputs")) - .unwrap_or(Value::Null); + Ok(()) + } + fn add_json_part_to_form( + &self, + form: &mut Form, + part_name: String, + data: &impl serde::Serialize, + ) -> Result<(), TracingClientError> { + let data_bytes = serde_json::to_vec(data)?; *form = std::mem::take(form).part( - format!("patch.{}", run_id), - Part::text(run_json.to_string()).mime_str("application/json")?, + part_name, + Part::bytes(data_bytes).mime_str("application/json")?, ); + Ok(()) + } - if !outputs.is_null() { + async fn add_attachment_to_form( + &self, + form: &mut Form, + prefix: &str, + run_id: &str, + attachment: Attachment, + ) -> Result<(), TracingClientError> { + let part_name = format!("{}.{}.attachments.{}", prefix, run_id, attachment.ref_name); + if let Some(data) = attachment.data { *form = std::mem::take(form).part( - format!("patch.{}.outputs", run_id), - Part::text(outputs.to_string()).mime_str("application/json")?, + part_name, + Part::bytes(data) + .file_name(attachment.filename) + .mime_str(&attachment.content_type)?, ); + } else { + // stream the file from disk to avoid loading the entire file into memory + let file_path = std::path::Path::new(&attachment.filename); + let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { + TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) + })?; + let file_size = metadata.len(); + let file = tokio::fs::File::open(file_path) + .await + .map_err(|e| TracingClientError::IoError(format!("Failed to open file: {}", e)))?; + let stream = ReaderStream::new(file); + let body = reqwest::Body::wrap_stream(stream); + + // extract filename from path + let file_name = file_path + .file_name() + .ok_or_else(|| { + TracingClientError::IoError("Failed to extract filename from path".to_string()) + })? + .to_string_lossy() + .into_owned(); + + let part = Part::stream_with_length(body, file_size) + .file_name(file_name) + .mime_str(&attachment.content_type)?; + + *form = std::mem::take(form).part(part_name, part); } - - for attachment in &run_with_attachments.attachments { - let ref_name = &attachment.ref_name; - let filename = &attachment.filename; - let data = &attachment.data; - let content_type = &attachment.content_type; - - let part_name = format!("patch.{}.attachments.{}", run_id, ref_name); - if let Some(data) = data { - *form = std::mem::take(form).part( - part_name, - Part::bytes(data.clone()) - .file_name(filename.clone()) - .mime_str(content_type)?, - ); - } else { - // read the file from disk and stream it to avoid loading the entire file into memory - let file_path = std::path::Path::new(filename); - let metadata = tokio::fs::metadata(file_path).await.map_err(|e| { - TracingClientError::IoError(format!("Failed to read file metadata: {}", e)) - })?; - let file_size = metadata.len(); - let file = tokio::fs::File::open(file_path).await.map_err(|e| { - TracingClientError::IoError(format!("Failed to open file: {}", e)) - })?; - let stream = ReaderStream::new(file); - let body = reqwest::Body::wrap_stream(stream); - - // extract just the last component of the file path - let file_name = file_path - .file_name() - .ok_or_else(|| { - TracingClientError::IoError( - "Failed to extract filename from path".to_string(), - ) - })? - .to_string_lossy() - .to_string(); - let part = Part::stream_with_length(body, file_size) - .file_name(file_name) - .mime_str(content_type)?; - - *form = std::mem::take(form).part(part_name, part); - } - } - Ok(()) } } From 8ad8dbfa914fac93fc500cb5b2689871ede6be9c Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Mon, 21 Oct 2024 19:04:45 -0700 Subject: [PATCH 20/31] add length header --- .../src/client/processor.rs | 15 +- .../src/client/run.rs | 2 +- .../langsmith_tracing_client/src/lib.rs | 2 - .../tests/test_run.rs | 2 +- .../tests/tracing_client_test.rs | 251 +++++++++++------- 5 files changed, 165 insertions(+), 107 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 26ea55ccc..b3a76bddf 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -3,7 +3,6 @@ use crate::client::run::{Attachment, QueuedRun}; use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use crate::client::tracing_client::ClientConfig; use reqwest::multipart::{Form, Part}; -use serde_json::Value; use tokio::sync::mpsc::Receiver; use tokio::time::{sleep, Instant}; use tokio_util::io::ReaderStream; @@ -154,11 +153,11 @@ impl RunProcessor { } = run_update_extended; let run_id = &run_update.common.id; - self.add_json_part_to_form(form, format!("post.{}", run_id), &run_update)?; - self.add_json_part_to_form(form, format!("post.{}.outputs", run_id), &io.outputs)?; + self.add_json_part_to_form(form, format!("patch.{}", run_id), &run_update)?; + self.add_json_part_to_form(form, format!("patch.{}.outputs", run_id), &io.outputs)?; for attachment in attachments { - self.add_attachment_to_form(form, "post", run_id, attachment) + self.add_attachment_to_form(form, "patch", run_id, attachment) .await?; } @@ -172,9 +171,10 @@ impl RunProcessor { data: &impl serde::Serialize, ) -> Result<(), TracingClientError> { let data_bytes = serde_json::to_vec(data)?; + let part_size = data_bytes.len() as u64; *form = std::mem::take(form).part( part_name, - Part::bytes(data_bytes).mime_str("application/json")?, + Part::bytes(data_bytes).mime_str(&format!("application/json; length={}", part_size))?, ); Ok(()) } @@ -188,11 +188,12 @@ impl RunProcessor { ) -> Result<(), TracingClientError> { let part_name = format!("{}.{}.attachments.{}", prefix, run_id, attachment.ref_name); if let Some(data) = attachment.data { + let part_size = data.len() as u64; *form = std::mem::take(form).part( part_name, Part::bytes(data) .file_name(attachment.filename) - .mime_str(&attachment.content_type)?, + .mime_str(&format!("{}; length={}", &attachment.content_type, part_size))?, ); } else { // stream the file from disk to avoid loading the entire file into memory @@ -218,7 +219,7 @@ impl RunProcessor { let part = Part::stream_with_length(body, file_size) .file_name(file_name) - .mime_str(&attachment.content_type)?; + .mime_str(&format!("{}; length={}", &attachment.content_type, file_size))?; *form = std::mem::take(form).part(part_name, part); } diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index 3df9ffd13..ba6309cc4 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -18,7 +18,7 @@ pub enum TimeValue { #[derive(PartialEq, Debug)] pub struct RunIO { - pub inputs: serde_json::Value, + pub inputs: Option, pub outputs: Option, } diff --git a/rust/crates/langsmith_tracing_client/src/lib.rs b/rust/crates/langsmith_tracing_client/src/lib.rs index 6e3658edc..2ee84bec7 100644 --- a/rust/crates/langsmith_tracing_client/src/lib.rs +++ b/rust/crates/langsmith_tracing_client/src/lib.rs @@ -3,7 +3,5 @@ pub mod client; use tokio::time::{sleep, Duration}; pub async fn minimal_test() { - println!("Starting async task..."); sleep(Duration::from_secs(1)).await; - println!("Async task complete!"); } diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index c915733b2..5cf6ee68f 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -1,4 +1,4 @@ -use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunIO, RunUpdate, TimeValue}; +use langsmith_tracing_client::client::run::{RunCommon, RunCreate, RunUpdate, TimeValue}; use serde_json; #[test] diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index d53a6c046..323ba0157 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -3,14 +3,12 @@ use langsmith_tracing_client::client::run::{ TimeValue, }; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; -use mockito::{Matcher, Server}; -use std::collections::HashMap; -use std::fs::File; -use std::time::Duration; -// use tokio::io::AsyncWriteExt; +use mockito::Server; use multipart::server::Multipart; -use std::io::{self, Read, Write}; +use std::fs::File; +use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; +use std::time::Duration; use tempfile::TempDir; #[derive(Debug)] @@ -53,6 +51,8 @@ fn handle_request(body: Vec, content_type_str: String) -> Vec, String)>> = Arc::new(Mutex::new((Vec::new(), String::new()))); let captured_request_clone = Arc::clone(&captured_request); @@ -116,7 +116,7 @@ async fn test_tracing_client_submit_run_create() { error: None, serialized: Some(serde_json::json!({"key": "value"})), events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!([{"tag": "value"}]), + tags: serde_json::json!(["tag1", "tag2"]), session_id: None, session_name: Some("Session Name".to_string()), }, @@ -128,7 +128,7 @@ async fn test_tracing_client_submit_run_create() { }, attachments, io: RunIO { - inputs: serde_json::json!({"input": "value"}), + inputs: Some(serde_json::json!({"input": "value"})), outputs: Some(serde_json::json!({"output": "value"})), }, }; @@ -146,7 +146,7 @@ async fn test_tracing_client_submit_run_create() { // assert run fields assert_eq!(fields[0].name, "post.test_id"); - assert_eq!(fields[0].content_type, Some("application/json".to_string())); + assert_eq!(fields[0].content_type, Some("application/json; length=375".to_string())); assert_eq!(fields[0].filename, None); let received_run: serde_json::Value = serde_json::from_str(&fields[0].data).unwrap(); assert_eq!(received_run["id"], "test_id"); @@ -166,7 +166,7 @@ async fn test_tracing_client_submit_run_create() { received_run["events"], serde_json::json!([{ "event": "event_data" }]) ); - assert_eq!(received_run["tags"], serde_json::json!([{"tag": "value"}])); + assert_eq!(received_run["tags"], serde_json::json!(["tag1", "tag2"])); assert_eq!(received_run["session_name"], "Session Name"); assert_eq!(received_run["session_id"], serde_json::Value::Null); assert_eq!(received_run["name"], "Run Name"); @@ -180,14 +180,14 @@ async fn test_tracing_client_submit_run_create() { // assert inputs fields assert_eq!(fields[1].name, "post.test_id.inputs"); - assert_eq!(fields[1].content_type, Some("application/json".to_string())); + assert_eq!(fields[1].content_type, Some("application/json; length=17".to_string())); assert_eq!(fields[1].filename, None); let received_inputs: serde_json::Value = serde_json::from_str(&fields[1].data).unwrap(); assert_eq!(received_inputs, serde_json::json!({"input": "value"})); // assert outputs fields assert_eq!(fields[2].name, "post.test_id.outputs"); - assert_eq!(fields[2].content_type, Some("application/json".to_string())); + assert_eq!(fields[2].content_type, Some("application/json; length=18".to_string())); assert_eq!(fields[2].filename, None); let received_outputs: serde_json::Value = serde_json::from_str(&fields[2].data).unwrap(); assert_eq!(received_outputs, serde_json::json!({"output": "value"})); @@ -196,97 +196,156 @@ async fn test_tracing_client_submit_run_create() { assert_eq!(fields[3].name, "post.test_id.attachments.attachment_1"); assert_eq!( fields[3].content_type, - Some("application/octet-stream".to_string()) + Some("application/octet-stream; length=3".to_string()) ); assert_eq!(fields[3].filename, Some("file1.txt".to_string())); assert_eq!(fields[3].data, "\u{1}\u{2}\u{3}"); // assert attachment_2 fields assert_eq!(fields[4].name, "post.test_id.attachments.attachment_2"); - assert_eq!(fields[4].content_type, Some("text/plain".to_string())); + assert_eq!(fields[4].content_type, Some("text/plain; length=29".to_string())); assert_eq!(fields[4].filename, Some("test_file_create.txt".to_string())); assert_eq!(fields[4].data, "Test file content for create\n"); } -// #[tokio::test] -// async fn test_tracing_client_submit_run_update() { -// let mut server = Server::new_async().await; -// let m = server -// .mock("POST", "/") -// .match_header( -// "content-type", -// Matcher::Regex(r"multipart/form-data.*".to_string()), -// ) -// .match_body(Matcher::AllOf(vec![ -// Matcher::Regex("patch\\.test_id".to_string()), -// Matcher::Regex("patch\\.test_id\\.outputs".to_string()), -// Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_1".to_string()), -// Matcher::Regex("patch\\.test_id\\.attachments\\.attachment_2".to_string()), -// ])) -// .with_status(200) -// .create_async() -// .await; -// -// let config = ClientConfig { -// endpoint: server.url(), -// queue_capacity: 10, -// batch_size: 1, // batch size is 1 to ensure each message is sent immediately -// batch_timeout: Duration::from_secs(1), -// }; -// -// let client = TracingClient::new(config).unwrap(); -// -// // Write a test file to disk for streaming -// let test_file_path = "test_file_update.txt"; -// let mut test_file = File::create(test_file_path).await.unwrap(); -// test_file -// .write_all(b"Test file content for update") -// .await -// .unwrap(); -// -// let mut attachments = HashMap::new(); -// attachments.insert( -// "attachment_1".to_string(), -// Attachment { -// filename: "file1.txt".to_string(), -// data: Some(vec![1, 2, 3]), -// content_type: "application/octet-stream".to_string(), -// }, -// ); -// attachments.insert( -// "attachment_2".to_string(), -// Attachment { -// filename: test_file_path.to_string(), -// data: None, // This will cause the code to read from disk -// content_type: "text/plain".to_string(), -// }, -// ); -// -// let run_update = RunUpdateWithAttachments { -// run_update: RunUpdate { -// common: RunCommon { -// id: String::from("test_id"), -// trace_id: String::from("trace_id"), -// dotted_order: String::from("1.1"), -// parent_run_id: None, -// extra: serde_json::json!({"extra_data": "value"}), -// error: None, -// serialized: serde_json::json!({"key": "value"}), -// inputs: serde_json::json!({"input": "value"}), -// events: serde_json::json!([{ "event": "event_data" }]), -// tags: serde_json::json!({"tag": "value"}), -// session_name: Some("Session Name".to_string()), -// session_id: None, -// }, -// end_time: TimeValue::UnsignedInt(1697462400000), -// outputs: Some(serde_json::json!({"output_key": "output_value"})), -// }, -// attachments, -// }; -// -// client.submit_run_update(run_update).await.unwrap(); -// -// // shutdown the client to ensure all messages are processed -// client.shutdown().await.unwrap(); -// m.assert_async().await; -// } +#[tokio::test] +async fn test_tracing_client_submit_run_update() { + // NOTE: we can't use an async mutex here because mockito doesn't support async functions + // in `with_body_from_request`. + let mut server = Server::new_async().await; + let captured_request: Arc, String)>> = + Arc::new(Mutex::new((Vec::new(), String::new()))); + let captured_request_clone = Arc::clone(&captured_request); + + let m = server + .mock("POST", "/") + .expect(1) + .with_status(200) + .with_body_from_request(move |req| { + let mut request = captured_request_clone.lock().unwrap(); + request.0 = req.body().unwrap().to_vec(); + let content_type_headers = req.header("content-type"); + let content_type_str: String = content_type_headers + .iter() + .filter_map(|h| h.to_str().ok()) + .collect::>() + .join(", "); + request.1 = content_type_str; + vec![] // return empty response body + }) + .create_async() + .await; + + let config = ClientConfig { + endpoint: server.url(), + queue_capacity: 10, + batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue + batch_timeout: Duration::from_secs(1), + }; + + let client = TracingClient::new(config).unwrap(); + + // Write a test file to disk for streaming + let tmp_dir = TempDir::new().unwrap(); + let test_file_path = tmp_dir.path().join("test_file_update.txt"); + let mut test_file = File::create(&test_file_path).unwrap(); + writeln!(test_file, "Test file content for update").unwrap(); + + let mut attachments = Vec::new(); + attachments.push(Attachment { + ref_name: "attachment_1".to_string(), + filename: "file1_update.txt".to_string(), + data: Some(vec![4, 5, 6]), + content_type: "application/octet-stream".to_string(), + }); + attachments.push(Attachment { + ref_name: "attachment_2".to_string(), + filename: test_file_path.to_string_lossy().into_owned(), + data: None, // this will cause the processor to read from disk + content_type: "text/plain".to_string(), + }); + + let run_update = RunUpdateExtended { + run_update: RunUpdate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: Some(serde_json::json!({"extra_data": "value"})), + error: None, + serialized: Some(serde_json::json!({"key": "value"})), + events: serde_json::json!([{ "event": "event_data" }]), + tags: serde_json::json!(["tag1", "tag2"]), + session_id: None, + session_name: Some("Session Name".to_string()), + }, + end_time: TimeValue::String("2024-10-16T12:00:00Z".to_string()), + }, + attachments, + io: RunIO { + inputs: None, + outputs: Some(serde_json::json!({"updated_output": "value"})), + }, + }; + + client.submit_run_update(run_update).await.unwrap(); + + // shutdown the client to ensure all messages are processed + client.shutdown().await.unwrap(); + m.assert_async().await; + + let req = captured_request.lock().unwrap().clone(); + let fields = handle_request(req.0, req.1); + + assert_eq!(fields.len(), 4); + + // assert run fields + assert_eq!(fields[0].name, "patch.test_id"); + assert_eq!(fields[0].content_type, Some("application/json; length=292".to_string())); + assert_eq!(fields[0].filename, None); + let received_run: serde_json::Value = serde_json::from_str(&fields[0].data).unwrap(); + assert_eq!(received_run["id"], "test_id"); + assert_eq!(received_run["trace_id"], "trace_id"); + assert_eq!( + received_run["extra"], + serde_json::json!({"extra_data": "value"}) + ); + assert_eq!(received_run["error"], serde_json::Value::Null); + assert_eq!( + received_run["serialized"], + serde_json::json!({"key": "value"}) + ); + assert_eq!( + received_run["events"], + serde_json::json!([{ "event": "event_data" }]) + ); + assert_eq!(received_run["tags"], serde_json::json!(["tag1", "tag2"])); + assert_eq!(received_run["session_name"], "Session Name"); + assert_eq!(received_run["end_time"], "2024-10-16T12:00:00Z"); + + // assert outputs fields + assert_eq!(fields[1].name, "patch.test_id.outputs"); + assert_eq!(fields[1].content_type, Some("application/json; length=26".to_string())); + assert_eq!(fields[1].filename, None); + let received_outputs: serde_json::Value = serde_json::from_str(&fields[1].data).unwrap(); + assert_eq!( + received_outputs, + serde_json::json!({"updated_output": "value"}) + ); + + // assert attachment_1 fields + assert_eq!(fields[2].name, "patch.test_id.attachments.attachment_1"); + assert_eq!( + fields[2].content_type, + Some("application/octet-stream; length=3".to_string()) + ); + assert_eq!(fields[2].filename, Some("file1_update.txt".to_string())); + assert_eq!(fields[2].data, "\u{4}\u{5}\u{6}"); + + // assert attachment_2 fields + assert_eq!(fields[3].name, "patch.test_id.attachments.attachment_2"); + assert_eq!(fields[3].content_type, Some("text/plain; length=29".to_string())); + assert_eq!(fields[3].filename, Some("test_file_update.txt".to_string())); + assert_eq!(fields[3].data, "Test file content for update\n"); +} From d24a87ba1688d18103769c60c25e82c90ad0fc9a Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Tue, 22 Oct 2024 00:03:45 -0700 Subject: [PATCH 21/31] add a directory for bindings --- .../langsmith_tracing_client/Cargo.lock | 584 +++++++++++++++++- .../langsmith_tracing_client/Cargo.toml | 2 + .../src/bindings/mod.rs | 4 + .../src/bindings/py_run.rs | 0 .../src/bindings/py_tracing_client.rs | 9 + .../src/client/processor.rs | 10 +- .../langsmith_tracing_client/src/lib.rs | 7 +- .../tests/test_run.rs | 4 - .../tests/tracing_client_test.rs | 35 +- 9 files changed, 625 insertions(+), 30 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/src/bindings/mod.rs create mode 100644 rust/crates/langsmith_tracing_client/src/bindings/py_run.rs create mode 100644 rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index 092a467e3..a31763cd3 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -57,12 +57,174 @@ dependencies = [ "serde_json", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.1", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log 0.4.22", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "0.1.8" @@ -121,6 +283,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "buf_redux" version = "0.8.4" @@ -184,6 +359,30 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -203,6 +402,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -219,6 +427,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -244,6 +458,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -286,6 +527,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -293,6 +549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -301,12 +558,36 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -315,7 +596,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] @@ -336,6 +617,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -364,6 +646,18 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "groupable" version = "0.2.0" @@ -382,25 +676,52 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "http" version = "1.1.0" @@ -583,6 +904,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg 1.4.0", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.6.0" @@ -590,9 +921,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "ipnet" version = "2.10.1" @@ -630,6 +973,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log 0.4.22", +] + [[package]] name = "langsmith_tracing_client" version = "0.1.0" @@ -637,6 +989,8 @@ dependencies = [ "chrono", "mockito", "multipart", + "pyo3", + "pyo3-asyncio", "reqwest", "serde", "serde_json", @@ -695,6 +1049,9 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] [[package]] name = "matches" @@ -708,6 +1065,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "mime" version = "0.2.6" @@ -760,7 +1126,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -881,7 +1247,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -923,7 +1289,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] @@ -944,6 +1310,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1030,6 +1408,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -1045,6 +1434,27 @@ dependencies = [ "typemap", ] +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1063,6 +1473,97 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-asyncio" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea6b68e93db3622f3bb3bf363246cf948ed5375afe7abff98ccbdd50b184995" +dependencies = [ + "async-std", + "clap", + "futures", + "inventory", + "once_cell", + "pin-project-lite", + "pyo3", + "pyo3-asyncio-macros", + "tokio", +] + +[[package]] +name = "pyo3-asyncio-macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c467178e1da6252c95c29ecf898b133f742e9181dca5def15dc24e19d45a39" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pyo3-build-config" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.79", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1447,7 +1948,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] @@ -1532,12 +2033,29 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.79" @@ -1579,6 +2097,12 @@ dependencies = [ "libc", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.13.0" @@ -1592,6 +2116,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + [[package]] name = "thiserror" version = "1.0.64" @@ -1609,7 +2148,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] @@ -1677,7 +2216,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] @@ -1814,6 +2353,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + [[package]] name = "unsafe-any" version = "0.4.2" @@ -1860,6 +2405,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1921,7 +2472,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -1955,7 +2506,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2005,6 +2556,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2216,7 +2776,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.79", ] [[package]] diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 237f4f981..0edb04dbd 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -14,6 +14,8 @@ thiserror = "1.0" mockito = "1.5.0" tokio-util = "0.7.12" tempfile = "3.13.0" +pyo3-asyncio = { version = "0.20.0", features = ["testing", "attributes", "async-std-runtime", "tokio-runtime"] } +pyo3 = "0.20.3" [dev-dependencies] multipart = "0.18.0" diff --git a/rust/crates/langsmith_tracing_client/src/bindings/mod.rs b/rust/crates/langsmith_tracing_client/src/bindings/mod.rs new file mode 100644 index 000000000..5bf4d6d07 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/bindings/mod.rs @@ -0,0 +1,4 @@ +mod py_tracing_client; +mod py_run; + +// use pyo3::prelude::*; diff --git a/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs b/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs new file mode 100644 index 000000000..e69de29bb diff --git a/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs b/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs new file mode 100644 index 000000000..d9727a6ca --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs @@ -0,0 +1,9 @@ +// use crate::client::tracing_client::TracingClient; +// use pyo3::prelude::*; +// use std::sync::Arc; +// use tokio::sync::RwLock; + +// #[pyclass] +// pub struct PyTracingClient { +// client: Arc>, +// } \ No newline at end of file diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index b3a76bddf..323acbcd8 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -193,7 +193,10 @@ impl RunProcessor { part_name, Part::bytes(data) .file_name(attachment.filename) - .mime_str(&format!("{}; length={}", &attachment.content_type, part_size))?, + .mime_str(&format!( + "{}; length={}", + &attachment.content_type, part_size + ))?, ); } else { // stream the file from disk to avoid loading the entire file into memory @@ -219,7 +222,10 @@ impl RunProcessor { let part = Part::stream_with_length(body, file_size) .file_name(file_name) - .mime_str(&format!("{}; length={}", &attachment.content_type, file_size))?; + .mime_str(&format!( + "{}; length={}", + &attachment.content_type, file_size + ))?; *form = std::mem::take(form).part(part_name, part); } diff --git a/rust/crates/langsmith_tracing_client/src/lib.rs b/rust/crates/langsmith_tracing_client/src/lib.rs index 2ee84bec7..f9a3804b0 100644 --- a/rust/crates/langsmith_tracing_client/src/lib.rs +++ b/rust/crates/langsmith_tracing_client/src/lib.rs @@ -1,7 +1,4 @@ +mod bindings; pub mod client; -use tokio::time::{sleep, Duration}; - -pub async fn minimal_test() { - sleep(Duration::from_secs(1)).await; -} +// use pyo3::prelude::*; \ No newline at end of file diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index 5cf6ee68f..e1348d548 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -18,7 +18,6 @@ fn test_run_common() { }; let serialized = serde_json::to_string(&run_common).unwrap(); - println!("Serialized RunCommon: {}", serialized); assert!(serialized.contains("\"dotted_order\":\"1.1\"")); } @@ -48,7 +47,6 @@ fn test_run_create_with_string_time() { }; let serialized = serde_json::to_string(&run_create).unwrap(); - println!("Serialized RunCreate (String Time): {}", serialized); assert!(serialized.contains("\"name\":\"Run Name\"")); assert!(serialized.contains("\"start_time\":\"2024-10-16T12:00:00Z\"")); } @@ -79,7 +77,6 @@ fn test_run_create_with_timestamp() { }; let serialized = serde_json::to_string(&run_create).unwrap(); - println!("Serialized RunCreate (Timestamp): {}", serialized); assert!(serialized.contains("\"name\":\"Run Name\"")); assert!(serialized.contains("\"start_time\":1697462400000")); } @@ -106,7 +103,6 @@ fn test_run_update() { }; let serialized = serde_json::to_string(&run_update).unwrap(); - println!("Serialized RunUpdate: {}", serialized); assert!(serialized.contains("\"dotted_order\":\"1.1\"")); assert!(serialized.contains("\"end_time\":\"2024-10-16T14:00:00Z\"")); } diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index 323ba0157..bdf82f1e5 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -146,7 +146,10 @@ async fn test_tracing_client_submit_run_create() { // assert run fields assert_eq!(fields[0].name, "post.test_id"); - assert_eq!(fields[0].content_type, Some("application/json; length=375".to_string())); + assert_eq!( + fields[0].content_type, + Some("application/json; length=375".to_string()) + ); assert_eq!(fields[0].filename, None); let received_run: serde_json::Value = serde_json::from_str(&fields[0].data).unwrap(); assert_eq!(received_run["id"], "test_id"); @@ -180,14 +183,20 @@ async fn test_tracing_client_submit_run_create() { // assert inputs fields assert_eq!(fields[1].name, "post.test_id.inputs"); - assert_eq!(fields[1].content_type, Some("application/json; length=17".to_string())); + assert_eq!( + fields[1].content_type, + Some("application/json; length=17".to_string()) + ); assert_eq!(fields[1].filename, None); let received_inputs: serde_json::Value = serde_json::from_str(&fields[1].data).unwrap(); assert_eq!(received_inputs, serde_json::json!({"input": "value"})); // assert outputs fields assert_eq!(fields[2].name, "post.test_id.outputs"); - assert_eq!(fields[2].content_type, Some("application/json; length=18".to_string())); + assert_eq!( + fields[2].content_type, + Some("application/json; length=18".to_string()) + ); assert_eq!(fields[2].filename, None); let received_outputs: serde_json::Value = serde_json::from_str(&fields[2].data).unwrap(); assert_eq!(received_outputs, serde_json::json!({"output": "value"})); @@ -203,7 +212,10 @@ async fn test_tracing_client_submit_run_create() { // assert attachment_2 fields assert_eq!(fields[4].name, "post.test_id.attachments.attachment_2"); - assert_eq!(fields[4].content_type, Some("text/plain; length=29".to_string())); + assert_eq!( + fields[4].content_type, + Some("text/plain; length=29".to_string()) + ); assert_eq!(fields[4].filename, Some("test_file_create.txt".to_string())); assert_eq!(fields[4].data, "Test file content for create\n"); } @@ -302,7 +314,10 @@ async fn test_tracing_client_submit_run_update() { // assert run fields assert_eq!(fields[0].name, "patch.test_id"); - assert_eq!(fields[0].content_type, Some("application/json; length=292".to_string())); + assert_eq!( + fields[0].content_type, + Some("application/json; length=292".to_string()) + ); assert_eq!(fields[0].filename, None); let received_run: serde_json::Value = serde_json::from_str(&fields[0].data).unwrap(); assert_eq!(received_run["id"], "test_id"); @@ -326,7 +341,10 @@ async fn test_tracing_client_submit_run_update() { // assert outputs fields assert_eq!(fields[1].name, "patch.test_id.outputs"); - assert_eq!(fields[1].content_type, Some("application/json; length=26".to_string())); + assert_eq!( + fields[1].content_type, + Some("application/json; length=26".to_string()) + ); assert_eq!(fields[1].filename, None); let received_outputs: serde_json::Value = serde_json::from_str(&fields[1].data).unwrap(); assert_eq!( @@ -345,7 +363,10 @@ async fn test_tracing_client_submit_run_update() { // assert attachment_2 fields assert_eq!(fields[3].name, "patch.test_id.attachments.attachment_2"); - assert_eq!(fields[3].content_type, Some("text/plain; length=29".to_string())); + assert_eq!( + fields[3].content_type, + Some("text/plain; length=29".to_string()) + ); assert_eq!(fields[3].filename, Some("test_file_update.txt".to_string())); assert_eq!(fields[3].data, "Test file content for update\n"); } From 4cc7d92886a1c7e2d7599b123430495341d3b2b3 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Tue, 22 Oct 2024 17:52:56 -0700 Subject: [PATCH 22/31] several fixes --- .../src/bindings/mod.rs | 2 +- .../src/bindings/py_run.rs | 1 + .../src/bindings/py_tracing_client.rs | 2 +- .../src/client/processor.rs | 38 +++-- .../src/client/run.rs | 8 +- .../src/client/tracing_client.rs | 2 + .../langsmith_tracing_client/src/lib.rs | 2 +- .../langsmith_tracing_client/src/main.rs | 144 ++++++++++++++++++ .../tests/test_run.rs | 16 +- .../tests/tracing_client_test.rs | 34 +++-- 10 files changed, 210 insertions(+), 39 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/src/main.rs diff --git a/rust/crates/langsmith_tracing_client/src/bindings/mod.rs b/rust/crates/langsmith_tracing_client/src/bindings/mod.rs index 5bf4d6d07..0c3fe87b9 100644 --- a/rust/crates/langsmith_tracing_client/src/bindings/mod.rs +++ b/rust/crates/langsmith_tracing_client/src/bindings/mod.rs @@ -1,4 +1,4 @@ -mod py_tracing_client; mod py_run; +mod py_tracing_client; // use pyo3::prelude::*; diff --git a/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs b/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs index e69de29bb..8b1378917 100644 --- a/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs +++ b/rust/crates/langsmith_tracing_client/src/bindings/py_run.rs @@ -0,0 +1 @@ + diff --git a/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs b/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs index d9727a6ca..f0d439d8c 100644 --- a/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/bindings/py_tracing_client.rs @@ -6,4 +6,4 @@ // #[pyclass] // pub struct PyTracingClient { // client: Arc>, -// } \ No newline at end of file +// } diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 323acbcd8..07409bdf2 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -105,8 +105,9 @@ impl RunProcessor { // Send the multipart POST request let response = self .http_client - .post(&self.config.endpoint) + .post(format!("{}/runs/multipart", self.config.endpoint)) .multipart(form) + .headers(self.config.headers.clone().unwrap_or_default()) .send() .await?; @@ -129,13 +130,22 @@ impl RunProcessor { } = run_create_extended; let run_id = &run_create.common.id; + // conditionally add the run_create and io parts to the form self.add_json_part_to_form(form, format!("post.{}", run_id), &run_create)?; - self.add_json_part_to_form(form, format!("post.{}.inputs", run_id), &io.inputs)?; - self.add_json_part_to_form(form, format!("post.{}.outputs", run_id), &io.outputs)?; - for attachment in attachments { - self.add_attachment_to_form(form, "post", run_id, attachment) - .await?; + if let Some(inputs) = io.inputs { + self.add_json_part_to_form(form, format!("post.{}.inputs", run_id), &inputs)?; + } + + if let Some(outputs) = io.outputs { + self.add_json_part_to_form(form, format!("post.{}.outputs", run_id), &outputs)?; + } + + if let Some(attachments) = attachments { + for attachment in attachments { + self.add_attachment_to_form(form, run_id, attachment) + .await?; + } } Ok(()) @@ -154,11 +164,16 @@ impl RunProcessor { let run_id = &run_update.common.id; self.add_json_part_to_form(form, format!("patch.{}", run_id), &run_update)?; - self.add_json_part_to_form(form, format!("patch.{}.outputs", run_id), &io.outputs)?; - for attachment in attachments { - self.add_attachment_to_form(form, "patch", run_id, attachment) - .await?; + if let Some(outputs) = io.outputs { + self.add_json_part_to_form(form, format!("patch.{}.outputs", run_id), &outputs)?; + } + + if let Some(attachments) = attachments { + for attachment in attachments { + self.add_attachment_to_form(form, run_id, attachment) + .await?; + } } Ok(()) @@ -182,11 +197,10 @@ impl RunProcessor { async fn add_attachment_to_form( &self, form: &mut Form, - prefix: &str, run_id: &str, attachment: Attachment, ) -> Result<(), TracingClientError> { - let part_name = format!("{}.{}.attachments.{}", prefix, run_id, attachment.ref_name); + let part_name = format!("attachment.{}.{}", run_id, attachment.ref_name); if let Some(data) = attachment.data { let part_size = data.len() as u64; *form = std::mem::take(form).part( diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index ba6309cc4..a30e8b5fa 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -31,8 +31,8 @@ pub struct RunCommon { pub extra: Option, pub error: Option, pub serialized: Option, - pub events: serde_json::Value, - pub tags: serde_json::Value, + pub events: Option, + pub tags: Option, pub session_id: Option, pub session_name: Option, } @@ -58,13 +58,13 @@ pub struct RunUpdate { pub struct RunCreateExtended { pub run_create: RunCreate, pub io: RunIO, - pub attachments: Vec, + pub attachments: Option>, } pub struct RunUpdateExtended { pub run_update: RunUpdate, pub io: RunIO, - pub attachments: Vec, + pub attachments: Option>, } pub enum QueuedRun { diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs index c210b1ebf..5fcac71b3 100644 --- a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -2,6 +2,7 @@ use crate::client::errors::TracingClientError; use crate::client::processor::RunProcessor; use crate::client::run::QueuedRun; use crate::client::run::{RunCreateExtended, RunUpdateExtended}; +use reqwest::header::HeaderMap; use std::time::Duration; use tokio::sync::mpsc::{self, Sender}; use tokio::task::JoinHandle; @@ -11,6 +12,7 @@ pub struct ClientConfig { pub queue_capacity: usize, pub batch_size: usize, pub batch_timeout: Duration, + pub headers: Option, } pub struct TracingClient { diff --git a/rust/crates/langsmith_tracing_client/src/lib.rs b/rust/crates/langsmith_tracing_client/src/lib.rs index f9a3804b0..6f46f781e 100644 --- a/rust/crates/langsmith_tracing_client/src/lib.rs +++ b/rust/crates/langsmith_tracing_client/src/lib.rs @@ -1,4 +1,4 @@ mod bindings; pub mod client; -// use pyo3::prelude::*; \ No newline at end of file +// use pyo3::prelude::*; diff --git a/rust/crates/langsmith_tracing_client/src/main.rs b/rust/crates/langsmith_tracing_client/src/main.rs new file mode 100644 index 000000000..7b180e486 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/src/main.rs @@ -0,0 +1,144 @@ +use langsmith_tracing_client::client::run::{ + Attachment, RunCommon, RunCreate, RunCreateExtended, RunIO, RunUpdate, RunUpdateExtended, + TimeValue, +}; +use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; +use reqwest::header::{HeaderMap, HeaderValue}; +use std::fs::File; +use std::io::Write; +use tempfile::TempDir; +use tokio::time::Duration; +use uuid::Uuid; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let tmp_dir = TempDir::new().unwrap(); + let test_file_path = tmp_dir.path().join("test_file_create.txt"); + let mut test_file = File::create(&test_file_path).unwrap(); + writeln!(test_file, "Test file content for create").unwrap(); + + let mut attachments = Vec::new(); + attachments.push(Attachment { + ref_name: "attachment_1".to_string(), + filename: "file1.txt".to_string(), + data: Some(vec![1, 2, 3]), + content_type: "application/octet-stream".to_string(), + }); + attachments.push(Attachment { + ref_name: "attachment_2".to_string(), + filename: test_file_path.into_os_string().into_string().unwrap(), + data: None, // this will cause the processor to read from disk + content_type: "text/plain".to_string(), + }); + + let run_id = Uuid::new_v4().to_string(); + println!("Run ID: {}", run_id); + + let run_create = RunCreateExtended { + run_create: RunCreate { + common: RunCommon { + id: String::from(&run_id), + trace_id: String::from(&run_id), + dotted_order: String::from("20241009T223747383001Z{}".to_string() + &run_id), + parent_run_id: None, + extra: Some(serde_json::json!({"extra_data": "value"})), + error: None, + serialized: None, + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!(["tag1", "tag2"])), + session_id: None, + session_name: Some("Rust Session Name".to_string()), + }, + name: String::from("Rusty"), + start_time: TimeValue::UnsignedInt(1728513467383), + end_time: Some(TimeValue::UnsignedInt(1728513468236)), + run_type: String::from("chain"), + reference_example_id: None, + }, + attachments: Some(attachments), + io: RunIO { + inputs: Some(serde_json::json!({"input": "value"})), + outputs: Some(serde_json::json!({"output": "value"})), + }, + }; + + let mut attachments_two = Vec::new(); + attachments_two.push(Attachment { + ref_name: "attachment_1".to_string(), + filename: "file1.txt".to_string(), + data: Some(vec![1, 2, 3]), + content_type: "application/octet-stream".to_string(), + }); + + let run_id_two = Uuid::new_v4().to_string(); + println!("Run ID Two: {}", run_id_two); + let run_create_two = RunCreateExtended { + run_create: RunCreate { + common: RunCommon { + id: String::from(&run_id_two), + trace_id: String::from(&run_id_two), + dotted_order: String::from("20241009T223747383001Z{}".to_string() + &run_id_two), + parent_run_id: None, + extra: Some(serde_json::json!({"extra_data": "value"})), + error: None, + serialized: None, + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!(["tag1", "tag2"])), + session_id: None, + session_name: Some("Rust Session Name".to_string()), + }, + name: String::from("Rusty two"), + start_time: TimeValue::UnsignedInt(1728513467383), + end_time: None, + run_type: String::from("chain"), + reference_example_id: None, + }, + attachments: Some(attachments_two), + io: RunIO { + inputs: Some(serde_json::json!({"input": "value"})), + outputs: None, + }, + }; + + let run_update_two = RunUpdateExtended { + run_update: RunUpdate { + common: RunCommon { + id: String::from(&run_id_two), + trace_id: String::from(&run_id_two), + dotted_order: String::from("20241009T223747383001Z{}".to_string() + &run_id_two), + parent_run_id: None, + extra: Some(serde_json::json!({"extra_data": "value"})), + error: None, + serialized: None, + events: None, + tags: Some(serde_json::json!(["tag1", "tag2"])), + session_id: None, + session_name: Some("Rust Session Name".to_string()), + }, + end_time: TimeValue::UnsignedInt(1728513468236), + }, + io: RunIO { + inputs: None, + outputs: Some(serde_json::json!({"output": "value"})), + }, + attachments: None, + }; + + let mut headers = HeaderMap::new(); + headers.insert("X-API-KEY", HeaderValue::from_static("test_key")); + let config = ClientConfig { + endpoint: String::from("http://localhost:1984"), + queue_capacity: 10, + batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue + batch_timeout: Duration::from_secs(1), + headers: None, + }; + + let client = TracingClient::new(config).unwrap(); + client.submit_run_create(run_create).await.unwrap(); + client.submit_run_create(run_create_two).await.unwrap(); + client.submit_run_update(run_update_two).await.unwrap(); + + client.shutdown().await.unwrap(); + Ok(()) +} diff --git a/rust/crates/langsmith_tracing_client/tests/test_run.rs b/rust/crates/langsmith_tracing_client/tests/test_run.rs index e1348d548..9f9efe548 100644 --- a/rust/crates/langsmith_tracing_client/tests/test_run.rs +++ b/rust/crates/langsmith_tracing_client/tests/test_run.rs @@ -11,8 +11,8 @@ fn test_run_common() { extra: Some(serde_json::json!({"extra_data": "value"})), error: Some(String::from("error message")), serialized: Some(serde_json::json!({"key": "value"})), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: None, session_id: Some("efghijkl-7654-3210-fedc-ba9876543210".to_string()), session_name: None, }; @@ -31,8 +31,8 @@ fn test_run_create_with_string_time() { extra: None, error: None, serialized: None, - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!({"tag": "value"})), session_id: None, session_name: Some("Session Name".to_string()), }; @@ -61,8 +61,8 @@ fn test_run_create_with_timestamp() { extra: Some(serde_json::json!({"extra_data": "value"})), error: None, serialized: Some(serde_json::json!({"key": "value"})), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!({"tag": "value"}), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!({"tag": "value"})), session_id: None, session_name: None, }; @@ -91,8 +91,8 @@ fn test_run_update() { extra: None, error: None, serialized: None, - events: serde_json::json!([]), - tags: serde_json::json!({"tag": "value"}), + events: None, + tags: Some(serde_json::json!({"tag": "value"})), session_id: None, session_name: None, }; diff --git a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs index bdf82f1e5..b9f683a4d 100644 --- a/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs +++ b/rust/crates/langsmith_tracing_client/tests/tracing_client_test.rs @@ -5,6 +5,7 @@ use langsmith_tracing_client::client::run::{ use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; use mockito::Server; use multipart::server::Multipart; +use reqwest::header::{HeaderMap, HeaderValue}; use std::fs::File; use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; @@ -58,7 +59,7 @@ async fn test_tracing_client_submit_run_create() { let captured_request_clone = Arc::clone(&captured_request); let m = server - .mock("POST", "/") + .mock("POST", "/runs/multipart") .expect(1) .with_status(200) .with_body_from_request(move |req| { @@ -81,6 +82,7 @@ async fn test_tracing_client_submit_run_create() { queue_capacity: 10, batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue batch_timeout: Duration::from_secs(1), + headers: None, }; let client = TracingClient::new(config).unwrap(); @@ -115,8 +117,8 @@ async fn test_tracing_client_submit_run_create() { extra: Some(serde_json::json!({"extra_data": "value"})), error: None, serialized: Some(serde_json::json!({"key": "value"})), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!(["tag1", "tag2"]), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!(["tag1", "tag2"])), session_id: None, session_name: Some("Session Name".to_string()), }, @@ -126,7 +128,7 @@ async fn test_tracing_client_submit_run_create() { run_type: String::from("chain"), reference_example_id: None, }, - attachments, + attachments: Some(attachments), io: RunIO { inputs: Some(serde_json::json!({"input": "value"})), outputs: Some(serde_json::json!({"output": "value"})), @@ -202,7 +204,7 @@ async fn test_tracing_client_submit_run_create() { assert_eq!(received_outputs, serde_json::json!({"output": "value"})); // assert attachment_1 fields - assert_eq!(fields[3].name, "post.test_id.attachments.attachment_1"); + assert_eq!(fields[3].name, "attachment.test_id.attachment_1"); assert_eq!( fields[3].content_type, Some("application/octet-stream; length=3".to_string()) @@ -211,7 +213,7 @@ async fn test_tracing_client_submit_run_create() { assert_eq!(fields[3].data, "\u{1}\u{2}\u{3}"); // assert attachment_2 fields - assert_eq!(fields[4].name, "post.test_id.attachments.attachment_2"); + assert_eq!(fields[4].name, "attachment.test_id.attachment_2"); assert_eq!( fields[4].content_type, Some("text/plain; length=29".to_string()) @@ -230,7 +232,7 @@ async fn test_tracing_client_submit_run_update() { let captured_request_clone = Arc::clone(&captured_request); let m = server - .mock("POST", "/") + .mock("POST", "/runs/multipart") .expect(1) .with_status(200) .with_body_from_request(move |req| { @@ -243,16 +245,24 @@ async fn test_tracing_client_submit_run_update() { .collect::>() .join(", "); request.1 = content_type_str; + + let auth_headers = req.header("X-API-KEY"); + assert!(auth_headers + .iter() + .any(|h| h.to_str().unwrap() == "test_key")); vec![] // return empty response body }) .create_async() .await; + let mut headers = HeaderMap::new(); + headers.insert("X-API-KEY", HeaderValue::from_static("test_key")); let config = ClientConfig { endpoint: server.url(), queue_capacity: 10, batch_size: 5, // batch size is 5 to ensure shutdown flushes the queue batch_timeout: Duration::from_secs(1), + headers: Some(headers), }; let client = TracingClient::new(config).unwrap(); @@ -287,14 +297,14 @@ async fn test_tracing_client_submit_run_update() { extra: Some(serde_json::json!({"extra_data": "value"})), error: None, serialized: Some(serde_json::json!({"key": "value"})), - events: serde_json::json!([{ "event": "event_data" }]), - tags: serde_json::json!(["tag1", "tag2"]), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!(["tag1", "tag2"])), session_id: None, session_name: Some("Session Name".to_string()), }, end_time: TimeValue::String("2024-10-16T12:00:00Z".to_string()), }, - attachments, + attachments: Some(attachments), io: RunIO { inputs: None, outputs: Some(serde_json::json!({"updated_output": "value"})), @@ -353,7 +363,7 @@ async fn test_tracing_client_submit_run_update() { ); // assert attachment_1 fields - assert_eq!(fields[2].name, "patch.test_id.attachments.attachment_1"); + assert_eq!(fields[2].name, "attachment.test_id.attachment_1"); assert_eq!( fields[2].content_type, Some("application/octet-stream; length=3".to_string()) @@ -362,7 +372,7 @@ async fn test_tracing_client_submit_run_update() { assert_eq!(fields[2].data, "\u{4}\u{5}\u{6}"); // assert attachment_2 fields - assert_eq!(fields[3].name, "patch.test_id.attachments.attachment_2"); + assert_eq!(fields[3].name, "attachment.test_id.attachment_2"); assert_eq!( fields[3].content_type, Some("text/plain; length=29".to_string()) From d8e98d2afdb9e9108c6643659e29a252585349a4 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Tue, 22 Oct 2024 21:11:39 -0700 Subject: [PATCH 23/31] add benchmarks --- .../langsmith_tracing_client/Cargo.lock | 257 +++++++++++++++++- .../langsmith_tracing_client/Cargo.toml | 7 +- .../benches/tracing_client_benchmark.rs | 161 +++++++++++ .../src/client/processor.rs | 16 +- 4 files changed, 430 insertions(+), 11 deletions(-) create mode 100644 rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index a31763cd3..9b9f92bce 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -41,6 +41,18 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + [[package]] name = "ascii" version = "0.8.7" @@ -324,6 +336,12 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.1.30" @@ -359,6 +377,33 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "3.2.25" @@ -367,13 +412,32 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_lex", + "clap_lex 0.2.4", "indexmap 1.9.3", "strsim", "termcolor", "textwrap", ] +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstyle", + "clap_lex 0.7.2", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -383,6 +447,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cloudabi" version = "0.0.3" @@ -427,12 +497,81 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap 4.5.20", + "criterion-plot", + "futures", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -683,6 +822,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -958,6 +1107,26 @@ dependencies = [ "url 1.7.2", ] +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -987,6 +1156,7 @@ name = "langsmith_tracing_client" version = "0.1.0" dependencies = [ "chrono", + "criterion", "mockito", "multipart", "pyo3", @@ -1266,6 +1436,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "openssl" version = "0.10.68" @@ -1425,6 +1601,34 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "plugin" version = "0.2.6" @@ -1498,7 +1702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea6b68e93db3622f3bb3bf363246cf948ed5375afe7abff98ccbdd50b184995" dependencies = [ "async-std", - "clap", + "clap 3.2.25", "futures", "inventory", "once_cell", @@ -1715,6 +1919,26 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1893,6 +2117,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.26" @@ -2175,6 +2408,16 @@ dependencies = [ "url 1.7.2", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -2429,6 +2672,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 0edb04dbd..09398479d 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -11,7 +11,6 @@ serde = { version = "1.0.210", features = ["derive"] } chrono = "0.4.38" reqwest = { version = "0.12.8", features = ["multipart", "stream"] } thiserror = "1.0" -mockito = "1.5.0" tokio-util = "0.7.12" tempfile = "3.13.0" pyo3-asyncio = { version = "0.20.0", features = ["testing", "attributes", "async-std-runtime", "tokio-runtime"] } @@ -19,3 +18,9 @@ pyo3 = "0.20.3" [dev-dependencies] multipart = "0.18.0" +mockito = "1.5.0" +criterion = { version = "0.5.1", features = ["async_tokio"] } + +[[bench]] +name = "tracing_client_benchmark" +harness = false \ No newline at end of file diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs new file mode 100644 index 000000000..f63c81d1f --- /dev/null +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -0,0 +1,161 @@ +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use tokio::runtime::Runtime; +use std::time::Duration; +use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; +use langsmith_tracing_client::client::run::{RunCreateExtended, RunCreate, RunIO, Attachment, TimeValue, RunCommon}; +use mockito::{ Server}; + +fn create_mock_client_config(server_url: &str) -> ClientConfig { + ClientConfig { + endpoint: server_url.to_string(), + queue_capacity: 10000, + batch_size: 50, + batch_timeout: Duration::from_secs(1), + headers: Default::default(), + } +} + +fn create_run_create(attachments: Option>, inputs: Option, outputs: Option) -> RunCreateExtended { + RunCreateExtended { + run_create: RunCreate { + common: RunCommon { + id: String::from("test_id"), + trace_id: String::from("trace_id"), + dotted_order: String::from("1.1"), + parent_run_id: None, + extra: Some(serde_json::json!({"extra_data": "value"})), + error: None, + serialized: Some(serde_json::json!({"key": "value"})), + events: Some(serde_json::json!([{ "event": "event_data" }])), + tags: Some(serde_json::json!(["tag1", "tag2"])), + session_id: None, + session_name: Some("Session Name".to_string()), + }, + name: String::from("Run Name"), + start_time: TimeValue::UnsignedInt(1697462400000), + end_time: Some(TimeValue::UnsignedInt(1697466000000)), + run_type: String::from("chain"), + reference_example_id: None, + }, + attachments, + io: RunIO { + inputs, + outputs, + }, + } +} + +fn create_large_json() -> serde_json::Value { + let large_array: Vec = (0..1_000) + .map(|i| serde_json::json!({ + "index": i, + "data": format!("This is element number {}", i), + "nested": { + "id": i, + "value": format!("Nested value for element {}", i), + } + })) + .collect(); + + serde_json::json!({ + "name": "Huge JSON", + "description": "This is a very large JSON object for benchmarking purposes.", + "array": large_array, + "metadata": { + "created_at": "2024-10-22T19:00:00Z", + "author": "Rust Program", + "version": 1.0 + } + }) +} + +fn bench_single_request_without_attachments(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let server = rt.block_on(async { + let mut server = Server::new_async().await; + server + .mock("POST", "/runs/multipart") + .with_status(202) + .create_async() + .await; + server + }); + + let client_config = create_mock_client_config(&server.url()); + let client = rt.block_on(async { + TracingClient::new(client_config).unwrap() + }); + + c.bench_function("simple run create", |b| { + b.to_async(&rt).iter_batched( + || create_run_create(None, Some(serde_json::json!({"input": "value"})), Some(serde_json::json!({"output": "value"}))), + |run_create| async { + client.submit_run_create(black_box(run_create)).await.unwrap() + }, + BatchSize::SmallInput, + ); + }); + + c.bench_function("run create with large i/o", |b| { + b.to_async(&rt).iter_batched( + || create_run_create(None, Some(create_large_json()), Some(create_large_json())), + |run_create| async { + client.submit_run_create(black_box(run_create)).await.unwrap() + }, + BatchSize::LargeInput, + ); + }); + + rt.block_on(async { + client.shutdown().await.unwrap(); + }); +} + +fn bench_shutdown_large_buffer(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let server = rt.block_on(async { + let mut server = Server::new_async().await; + server + .mock("POST", "/runs/multipart") + .with_status(202) + .create_async() + .await; + server + }); + + let num_runs = 1000; + + c.bench_function("client shutdown with large buffer", |b| { + b.to_async(&rt).iter_batched( + || { + let runs: Vec = (0..num_runs) + .map(|i| { + let mut run = create_run_create(None, Some(create_large_json()), Some(create_large_json())); + run.run_create.common.id = format!("test_id_{}", i); + run + }) + .collect(); + let client_config = create_mock_client_config(&server.url()); + let client = TracingClient::new(client_config).unwrap(); + (client, runs) + }, + |(client, runs)| async { + for run in runs { + client.submit_run_create(run).await.unwrap(); + } + client.shutdown().await.unwrap(); + }, + BatchSize::LargeInput, + ); + }); +} + +//criterion_group!(benches, bench_single_request_without_attachments); +//criterion_group!(benches, bench_shutdown_large_buffer); +criterion_group!{ + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_shutdown_large_buffer +} + +criterion_main!(benches); \ No newline at end of file diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index 07409bdf2..bcee346bd 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -33,18 +33,18 @@ impl RunProcessor { Some(queued_run) = self.receiver.recv() => { match queued_run { QueuedRun::Shutdown => { - println!("shutdown signal received."); + // println!("shutdown signal received."); if !buffer.is_empty() { - println!("sending remaining buffer before shutdown."); + // println!("sending remaining buffer before shutdown."); self.send_and_clear_buffer(&mut buffer).await?; } break; }, _ => { - println!("received a queued run."); + // println!("received a queued run."); buffer.push(queued_run); if buffer.len() >= self.config.batch_size { - println!("batch size limit, sending batch."); + // println!("batch size limit, sending batch."); self.send_and_clear_buffer(&mut buffer).await?; last_send_time = Instant::now(); } @@ -53,22 +53,22 @@ impl RunProcessor { } _ = sleep(self.config.batch_timeout) => { if !buffer.is_empty() && last_send_time.elapsed() >= self.config.batch_timeout { - println!("batch timeout, sending batch."); + // println!("batch timeout, sending batch."); self.send_and_clear_buffer(&mut buffer).await?; last_send_time = Instant::now(); } } else => { - println!("channel closed."); + // println!("channel closed."); if !buffer.is_empty() { - println!("sending remaining buffer."); + // println!("sending remaining buffer."); self.send_and_clear_buffer(&mut buffer).await?; } break; } } } - println!("exiting loop."); + // println!("exiting loop."); Ok(()) } From 01b6305b69d637f1e57b7ed464b31ee42e195509 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Tue, 22 Oct 2024 21:55:37 -0700 Subject: [PATCH 24/31] improve benchmarks --- .../benches/tracing_client_benchmark.rs | 173 ++++++++---------- 1 file changed, 76 insertions(+), 97 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs index f63c81d1f..3646fe296 100644 --- a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -1,21 +1,27 @@ -use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; -use tokio::runtime::Runtime; -use std::time::Duration; +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use langsmith_tracing_client::client::run::{ + Attachment, RunCommon, RunCreate, RunCreateExtended, RunIO, TimeValue, +}; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; -use langsmith_tracing_client::client::run::{RunCreateExtended, RunCreate, RunIO, Attachment, TimeValue, RunCommon}; -use mockito::{ Server}; +use mockito::Server; +use std::time::Duration; +use tokio::runtime::Runtime; -fn create_mock_client_config(server_url: &str) -> ClientConfig { +fn create_mock_client_config(server_url: &str, batch_size: usize) -> ClientConfig { ClientConfig { endpoint: server_url.to_string(), - queue_capacity: 10000, - batch_size: 50, + queue_capacity: 1_000_000, + batch_size, batch_timeout: Duration::from_secs(1), headers: Default::default(), } } -fn create_run_create(attachments: Option>, inputs: Option, outputs: Option) -> RunCreateExtended { +fn create_run_create( + attachments: Option>, + inputs: Option, + outputs: Option, +) -> RunCreateExtended { RunCreateExtended { run_create: RunCreate { common: RunCommon { @@ -38,23 +44,22 @@ fn create_run_create(attachments: Option>, inputs: Option serde_json::Value { - let large_array: Vec = (0..1_000) - .map(|i| serde_json::json!({ - "index": i, - "data": format!("This is element number {}", i), - "nested": { - "id": i, - "value": format!("Nested value for element {}", i), - } - })) +fn create_large_json(len: usize) -> serde_json::Value { + let large_array: Vec = (0..len) + .map(|i| { + serde_json::json!({ + "index": i, + "data": format!("This is element number {}", i), + "nested": { + "id": i, + "value": format!("Nested value for element {}", i), + } + }) + }) .collect(); serde_json::json!({ @@ -68,50 +73,7 @@ fn create_large_json() -> serde_json::Value { } }) } - -fn bench_single_request_without_attachments(c: &mut Criterion) { - let rt = Runtime::new().unwrap(); - let server = rt.block_on(async { - let mut server = Server::new_async().await; - server - .mock("POST", "/runs/multipart") - .with_status(202) - .create_async() - .await; - server - }); - - let client_config = create_mock_client_config(&server.url()); - let client = rt.block_on(async { - TracingClient::new(client_config).unwrap() - }); - - c.bench_function("simple run create", |b| { - b.to_async(&rt).iter_batched( - || create_run_create(None, Some(serde_json::json!({"input": "value"})), Some(serde_json::json!({"output": "value"}))), - |run_create| async { - client.submit_run_create(black_box(run_create)).await.unwrap() - }, - BatchSize::SmallInput, - ); - }); - - c.bench_function("run create with large i/o", |b| { - b.to_async(&rt).iter_batched( - || create_run_create(None, Some(create_large_json()), Some(create_large_json())), - |run_create| async { - client.submit_run_create(black_box(run_create)).await.unwrap() - }, - BatchSize::LargeInput, - ); - }); - - rt.block_on(async { - client.shutdown().await.unwrap(); - }); -} - -fn bench_shutdown_large_buffer(c: &mut Criterion) { +fn bench_run_create(c: &mut Criterion) { let rt = Runtime::new().unwrap(); let server = rt.block_on(async { let mut server = Server::new_async().await; @@ -123,39 +85,56 @@ fn bench_shutdown_large_buffer(c: &mut Criterion) { server }); - let num_runs = 1000; - - c.bench_function("client shutdown with large buffer", |b| { - b.to_async(&rt).iter_batched( - || { - let runs: Vec = (0..num_runs) - .map(|i| { - let mut run = create_run_create(None, Some(create_large_json()), Some(create_large_json())); - run.run_create.common.id = format!("test_id_{}", i); - run - }) - .collect(); - let client_config = create_mock_client_config(&server.url()); - let client = TracingClient::new(client_config).unwrap(); - (client, runs) - }, - |(client, runs)| async { - for run in runs { - client.submit_run_create(run).await.unwrap(); - } - client.shutdown().await.unwrap(); - }, - BatchSize::LargeInput, - ); - }); + let mut group = c.benchmark_group("run_create"); + for batch_size in vec![50] { + for json_len in vec![1_000, 5_000] { + for num_runs in vec![500, 1_000] { + group.bench_with_input( + BenchmarkId::new( + "run_create", + format!("batch_{}_json_{}_runs_{}", batch_size, json_len, num_runs), + ), + &(batch_size, json_len, num_runs), + |b, &(batch_size, json_len, num_runs)| { + b.to_async(&rt).iter_batched( + || { + let runs: Vec = (0..num_runs) + .map(|i| { + let mut run = create_run_create( + None, + Some(create_large_json(json_len)), + Some(create_large_json(json_len)), + ); + run.run_create.common.id = format!("test_id_{}", i); + run + }) + .collect(); + let client_config = + create_mock_client_config(&server.url(), batch_size); + let client = TracingClient::new(client_config).unwrap(); + (client, runs) + }, + |(client, runs)| async { + for run in runs { + client.submit_run_create(run).await.unwrap(); + } + // shutdown the client to flush the queue + client.shutdown().await.unwrap(); + }, + BatchSize::LargeInput, + ); + }, + ); + } + } + } + group.finish(); } -//criterion_group!(benches, bench_single_request_without_attachments); -//criterion_group!(benches, bench_shutdown_large_buffer); -criterion_group!{ +criterion_group! { name = benches; config = Criterion::default().sample_size(10); - targets = bench_shutdown_large_buffer + targets = bench_run_create } -criterion_main!(benches); \ No newline at end of file +criterion_main!(benches); From 69398a2df305d8eee92b951f4c3f0657d50236aa Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 23 Oct 2024 17:21:40 -0700 Subject: [PATCH 25/31] use custom_iter --- .../benches/tracing_client_benchmark.rs | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs index 3646fe296..9a4c626bb 100644 --- a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use langsmith_tracing_client::client::run::{ Attachment, RunCommon, RunCreate, RunCreateExtended, RunIO, TimeValue, }; @@ -116,7 +116,7 @@ fn bench_run_create(c: &mut Criterion) { }, |(client, runs)| async { for run in runs { - client.submit_run_create(run).await.unwrap(); + client.submit_run_create(black_box(run)).await.unwrap(); } // shutdown the client to flush the queue client.shutdown().await.unwrap(); @@ -131,10 +131,76 @@ fn bench_run_create(c: &mut Criterion) { group.finish(); } +fn bench_run_create_iter_custom(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let server = rt.block_on(async { + let mut server = Server::new_async().await; + server + .mock("POST", "/runs/multipart") + .with_status(202) + .create_async() + .await; + server + }); + + let mut group = c.benchmark_group("run_create_custom_iter"); + let server_url = server.url(); + for batch_size in vec![50] { + for json_len in vec![1_000, 5_000] { + for num_runs in vec![500, 1_000] { + group.bench_function( + BenchmarkId::new( + "run_create", + format!("batch_{}_json_{}_runs_{}", batch_size, json_len, num_runs), + ), + |b| { + b.to_async(&rt).iter_custom(|iters| { + let mut elapsed_time = Duration::default(); + let server_url = server_url.clone(); + async move { + //let start = std::time::Instant::now(); + println!("Iterations: {}", iters); + for _ in 0..iters { + let runs: Vec = (0..num_runs) + .map(|i| { + let mut run = create_run_create( + None, + Some(create_large_json(json_len)), + Some(create_large_json(json_len)), + ); + run.run_create.common.id = format!("test_id_{}", i); + run + }) + .collect(); + let client_config = + create_mock_client_config(&server_url, batch_size); + let client = TracingClient::new(client_config).unwrap(); + + let start = std::time::Instant::now(); + for run in runs { + client.submit_run_create(black_box(run)).await.unwrap(); + } + // shutdown the client to flush the queue + client.shutdown().await.unwrap(); + println!("Elapsed time inner loop: {:?}", start.elapsed()); + elapsed_time += start.elapsed(); + } + println!("Elapsed time: {:?}", elapsed_time); + elapsed_time + } + }) + }, + ); + } + } + } + group.finish(); +} + criterion_group! { name = benches; config = Criterion::default().sample_size(10); - targets = bench_run_create + targets = bench_run_create_iter_custom } criterion_main!(benches); From 4774da2f2d30ec56863d8ec29c10e827abdd4cac Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 23 Oct 2024 17:22:34 -0700 Subject: [PATCH 26/31] use custom_iter --- .../benches/tracing_client_benchmark.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs index 9a4c626bb..fcd29740f 100644 --- a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -158,7 +158,6 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { let mut elapsed_time = Duration::default(); let server_url = server_url.clone(); async move { - //let start = std::time::Instant::now(); println!("Iterations: {}", iters); for _ in 0..iters { let runs: Vec = (0..num_runs) @@ -200,7 +199,7 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { criterion_group! { name = benches; config = Criterion::default().sample_size(10); - targets = bench_run_create_iter_custom + targets = bench_run_create_iter_custom, bench_run_create } criterion_main!(benches); From 14f5817b607ebb5e1d52d46c9de1d8b36ad13275 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Wed, 23 Oct 2024 18:29:18 -0700 Subject: [PATCH 27/31] baseline --- rust/crates/langsmith_tracing_client/Cargo.toml | 2 +- .../benches/tracing_client_benchmark.rs | 5 +---- rust/crates/langsmith_tracing_client/src/client/processor.rs | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 09398479d..2814b34b1 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -23,4 +23,4 @@ criterion = { version = "0.5.1", features = ["async_tokio"] } [[bench]] name = "tracing_client_benchmark" -harness = false \ No newline at end of file +harness = false diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs index fcd29740f..c142706c4 100644 --- a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -158,7 +158,6 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { let mut elapsed_time = Duration::default(); let server_url = server_url.clone(); async move { - println!("Iterations: {}", iters); for _ in 0..iters { let runs: Vec = (0..num_runs) .map(|i| { @@ -181,10 +180,8 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { } // shutdown the client to flush the queue client.shutdown().await.unwrap(); - println!("Elapsed time inner loop: {:?}", start.elapsed()); elapsed_time += start.elapsed(); } - println!("Elapsed time: {:?}", elapsed_time); elapsed_time } }) @@ -199,7 +196,7 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { criterion_group! { name = benches; config = Criterion::default().sample_size(10); - targets = bench_run_create_iter_custom, bench_run_create + targets = bench_run_create } criterion_main!(benches); diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index bcee346bd..de236c652 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -85,6 +85,7 @@ impl RunProcessor { async fn send_batch(&self, batch: Vec) -> Result<(), TracingClientError> { let mut form = Form::new(); + // println!("Sending batch of {} runs", batch.len()); for queued_run in batch { match queued_run { From 3a21a41ba4f3d983184749779d7fdb2de2d4d9c9 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Fri, 25 Oct 2024 15:15:33 -0700 Subject: [PATCH 28/31] benchmark for run_bytes --- .../benches/tracing_client_benchmark.rs | 92 ++++++++++++++++++- .../src/client/processor.rs | 58 +++++++++++- .../src/client/run.rs | 17 +++- .../src/client/tracing_client.rs | 11 ++- 4 files changed, 173 insertions(+), 5 deletions(-) diff --git a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs index c142706c4..3c5945861 100644 --- a/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/tracing_client_benchmark.rs @@ -1,6 +1,6 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use langsmith_tracing_client::client::run::{ - Attachment, RunCommon, RunCreate, RunCreateExtended, RunIO, TimeValue, + Attachment, EventType, RunCommon, RunCreate, RunCreateExtended, RunEventBytes, RunIO, TimeValue, }; use langsmith_tracing_client::client::tracing_client::{ClientConfig, TracingClient}; use mockito::Server; @@ -48,6 +48,32 @@ fn create_run_create( } } +fn create_run_bytes( + attachments: Option>, + inputs: Option, + outputs: Option, +) -> RunEventBytes { + let run_create = create_run_create(attachments, inputs, outputs); + let run_bytes = serde_json::to_vec(&run_create.run_create).unwrap(); + let inputs_bytes = run_create + .io + .inputs + .map(|i| serde_json::to_vec(&i).unwrap()); + let outputs_bytes = run_create + .io + .outputs + .map(|o| serde_json::to_vec(&o).unwrap()); + + RunEventBytes { + run_id: run_create.run_create.common.id, + event_type: EventType::Create, + run_bytes, + inputs_bytes, + outputs_bytes, + attachments: run_create.attachments, + } +} + fn create_large_json(len: usize) -> serde_json::Value { let large_array: Vec = (0..len) .map(|i| { @@ -73,6 +99,7 @@ fn create_large_json(len: usize) -> serde_json::Value { } }) } + fn bench_run_create(c: &mut Criterion) { let rt = Runtime::new().unwrap(); let server = rt.block_on(async { @@ -193,10 +220,71 @@ fn bench_run_create_iter_custom(c: &mut Criterion) { group.finish(); } +fn bench_run_bytes_iter_custom(c: &mut Criterion) { + let rt = Runtime::new().unwrap(); + let server = rt.block_on(async { + let mut server = Server::new_async().await; + server + .mock("POST", "/runs/multipart") + .with_status(202) + .create_async() + .await; + server + }); + + let mut group = c.benchmark_group("run_create_custom_iter"); + let server_url = server.url(); + for batch_size in vec![50] { + for json_len in vec![1_000, 5_000] { + for num_runs in vec![500, 1_000] { + group.bench_function( + BenchmarkId::new( + "run_create", + format!("batch_{}_json_{}_runs_{}", batch_size, json_len, num_runs), + ), + |b| { + b.to_async(&rt).iter_custom(|iters| { + let mut elapsed_time = Duration::default(); + let server_url = server_url.clone(); + async move { + for _ in 0..iters { + let runs: Vec = (0..num_runs) + .map(|i| { + let mut run = create_run_bytes( + None, + Some(create_large_json(json_len)), + Some(create_large_json(json_len)), + ); + run + }) + .collect(); + let client_config = + create_mock_client_config(&server_url, batch_size); + let client = TracingClient::new(client_config).unwrap(); + + let start = std::time::Instant::now(); + for run in runs { + client.submit_run_bytes(black_box(run)).await.unwrap(); + } + // shutdown the client to flush the queue + client.shutdown().await.unwrap(); + elapsed_time += start.elapsed(); + } + elapsed_time + } + }) + }, + ); + } + } + } + group.finish(); +} + criterion_group! { name = benches; config = Criterion::default().sample_size(10); - targets = bench_run_create + targets = bench_run_bytes_iter_custom } criterion_main!(benches); diff --git a/rust/crates/langsmith_tracing_client/src/client/processor.rs b/rust/crates/langsmith_tracing_client/src/client/processor.rs index de236c652..6135ce51e 100644 --- a/rust/crates/langsmith_tracing_client/src/client/processor.rs +++ b/rust/crates/langsmith_tracing_client/src/client/processor.rs @@ -1,5 +1,5 @@ use crate::client::errors::TracingClientError; -use crate::client::run::{Attachment, QueuedRun}; +use crate::client::run::{Attachment, EventType, QueuedRun, RunEventBytes}; use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use crate::client::tracing_client::ClientConfig; use reqwest::multipart::{Form, Part}; @@ -97,6 +97,9 @@ impl RunProcessor { self.consume_run_update(run_update_extended, &mut form) .await?; } + QueuedRun::RunBytes(run_event_bytes) => { + self.consume_run_bytes(run_event_bytes, &mut form).await?; + } QueuedRun::Shutdown => { return Err(TracingClientError::UnexpectedShutdown); } @@ -180,6 +183,59 @@ impl RunProcessor { Ok(()) } + async fn consume_run_bytes( + &self, + run_event_bytes: RunEventBytes, + form: &mut Form, + ) -> Result<(), TracingClientError> { + let RunEventBytes { + run_id, + event_type, + run_bytes, + inputs_bytes, + outputs_bytes, + attachments, + } = run_event_bytes; + + let event_type_str = match event_type { + EventType::Create => "post", + EventType::Update => "patch", + }; + + let part_size = run_bytes.len() as u64; + *form = std::mem::take(form).part( + format!("{}.{}", event_type_str, run_id), + Part::bytes(run_bytes).mime_str(&format!("application/json; length={}", part_size))?, + ); + + if let Some(inputs_bytes) = inputs_bytes { + let part_size = inputs_bytes.len() as u64; + *form = std::mem::take(form).part( + format!("{}.{}.inputs", event_type_str, run_id), + Part::bytes(inputs_bytes) + .mime_str(&format!("application/json; length={}", part_size))?, + ); + } + + if let Some(outputs_bytes) = outputs_bytes { + let part_size = outputs_bytes.len() as u64; + *form = std::mem::take(form).part( + format!("{}.{}.outputs", event_type_str, run_id), + Part::bytes(outputs_bytes) + .mime_str(&format!("application/json; length={}", part_size))?, + ); + } + + if let Some(attachments) = attachments { + for attachment in attachments { + self.add_attachment_to_form(form, &run_id, attachment) + .await?; + } + } + + Ok(()) + } + fn add_json_part_to_form( &self, form: &mut Form, diff --git a/rust/crates/langsmith_tracing_client/src/client/run.rs b/rust/crates/langsmith_tracing_client/src/client/run.rs index a30e8b5fa..20f9c69e4 100644 --- a/rust/crates/langsmith_tracing_client/src/client/run.rs +++ b/rust/crates/langsmith_tracing_client/src/client/run.rs @@ -67,8 +67,23 @@ pub struct RunUpdateExtended { pub attachments: Option>, } -pub enum QueuedRun { +pub struct RunEventBytes { + pub run_id: String, + pub event_type: EventType, + pub run_bytes: Vec, + pub inputs_bytes: Option>, + pub outputs_bytes: Option>, + pub attachments: Option>, +} + +pub enum EventType { + Create, + Update, +} + +pub(crate) enum QueuedRun { Create(RunCreateExtended), Update(RunUpdateExtended), + RunBytes(RunEventBytes), Shutdown, } diff --git a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs index 5fcac71b3..d89e0dca2 100644 --- a/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs +++ b/rust/crates/langsmith_tracing_client/src/client/tracing_client.rs @@ -1,6 +1,6 @@ use crate::client::errors::TracingClientError; use crate::client::processor::RunProcessor; -use crate::client::run::QueuedRun; +use crate::client::run::{QueuedRun, RunEventBytes}; use crate::client::run::{RunCreateExtended, RunUpdateExtended}; use reqwest::header::HeaderMap; use std::time::Duration; @@ -61,6 +61,15 @@ impl TracingClient { .map_err(|_| TracingClientError::QueueFull) } + pub async fn submit_run_bytes(&self, run: RunEventBytes) -> Result<(), TracingClientError> { + let queued_run = QueuedRun::RunBytes(run); + + self.sender + .send(queued_run) + .await + .map_err(|_| TracingClientError::QueueFull) + } + pub async fn shutdown(self) -> Result<(), TracingClientError> { self.sender .send(QueuedRun::Shutdown) From 29aa6a11401b7cc1940521cb7924a80b3080838d Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Fri, 25 Oct 2024 15:55:55 -0700 Subject: [PATCH 29/31] add json serialization benchmarks --- .../langsmith_tracing_client/Cargo.lock | 1 + .../langsmith_tracing_client/Cargo.toml | 5 ++ .../benches/json_serialization_benchmark.rs | 66 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs diff --git a/rust/crates/langsmith_tracing_client/Cargo.lock b/rust/crates/langsmith_tracing_client/Cargo.lock index 9b9f92bce..af0bab00c 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.lock +++ b/rust/crates/langsmith_tracing_client/Cargo.lock @@ -1161,6 +1161,7 @@ dependencies = [ "multipart", "pyo3", "pyo3-asyncio", + "rayon", "reqwest", "serde", "serde_json", diff --git a/rust/crates/langsmith_tracing_client/Cargo.toml b/rust/crates/langsmith_tracing_client/Cargo.toml index 2814b34b1..6f52ef6fb 100644 --- a/rust/crates/langsmith_tracing_client/Cargo.toml +++ b/rust/crates/langsmith_tracing_client/Cargo.toml @@ -15,6 +15,7 @@ tokio-util = "0.7.12" tempfile = "3.13.0" pyo3-asyncio = { version = "0.20.0", features = ["testing", "attributes", "async-std-runtime", "tokio-runtime"] } pyo3 = "0.20.3" +rayon = "1.10.0" [dev-dependencies] multipart = "0.18.0" @@ -24,3 +25,7 @@ criterion = { version = "0.5.1", features = ["async_tokio"] } [[bench]] name = "tracing_client_benchmark" harness = false + +[[bench]] +name = "json_serialization_benchmark" +harness = false diff --git a/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs new file mode 100644 index 000000000..67af76fb3 --- /dev/null +++ b/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs @@ -0,0 +1,66 @@ +use rayon::prelude::*; +use serde_json::Value; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn create_large_json(len: usize) -> Value { + let large_array: Vec = (0..len) + .map(|i| { + serde_json::json!({ + "index": i, + "data": format!("This is element number {}", i), + "nested": { + "id": i, + "value": format!("Nested value for element {}", i), + } + }) + }) + .collect(); + + serde_json::json!({ + "name": "Huge JSON", + "description": "This is a very large JSON object for benchmarking purposes.", + "array": large_array, + "metadata": { + "created_at": "2024-10-22T19:00:00Z", + "author": "Rust Program", + "version": 1.0 + } + }) +} + +// Sequential processing +fn benchmark_sequential(data: &[Value]) -> Vec> { + data.iter() + .map(|json| serde_json::to_vec(json).expect("Failed to serialize JSON")) + .collect() +} + +// Parallel processing +fn benchmark_parallel(data: &[Value]) -> Vec> { + data.par_iter() + .map(|json| serde_json::to_vec(json).expect("Failed to serialize JSON")) + .collect() +} + +fn json_benchmark(c: &mut Criterion) { + let num_json_objects = 1000; + let json_length = 1000; + let data: Vec = (0..num_json_objects) + .map(|_| create_large_json(json_length)) + .collect(); + + c.bench_function("sequential serialization", |b| { + b.iter(|| benchmark_sequential(&data)) + }); + + c.bench_function("parallel serialization", |b| { + b.iter(|| benchmark_parallel(&data)) + }); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = json_benchmark +} +criterion_main!(benches); \ No newline at end of file From d92f19d295599bda4743bb04b21b5bc0cdb4e351 Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 26 Oct 2024 00:38:04 -0700 Subject: [PATCH 30/31] more perf stuff --- python/bench/json_serialization.py | 63 ++++++++++ python/bench/tracing_client_bench.py | 109 ++++++++++++++++++ python/langsmith/client.py | 18 ++- .../benches/json_serialization_benchmark.rs | 20 +++- 4 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 python/bench/json_serialization.py create mode 100644 python/bench/tracing_client_bench.py diff --git a/python/bench/json_serialization.py b/python/bench/json_serialization.py new file mode 100644 index 000000000..200bfe69f --- /dev/null +++ b/python/bench/json_serialization.py @@ -0,0 +1,63 @@ +import orjson +import timeit +from concurrent.futures import ThreadPoolExecutor, as_completed + +def create_large_json(length): + large_array = [ + { + "index": i, + "data": f"This is element number {i}", + "nested": { + "id": i, + "value": f"Nested value for element {i}" + } + } + for i in range(length) + ] + + return { + "name": "Huge JSON", + "description": "This is a very large JSON object for benchmarking purposes.", + "array": large_array, + "metadata": { + "created_at": "2024-10-22T19:00:00Z", + "author": "Python Program", + "version": 1.0 + } + } + + +def benchmark_sequential(data): + return [orjson.dumps(json_obj) for json_obj in data] + + +def benchmark_parallel(data): + with ThreadPoolExecutor() as executor: + futures = [executor.submit(orjson.dumps, json_obj) for json_obj in data] + return [future.result() for future in as_completed(futures)] + + +def benchmark_with_map(data): + with ThreadPoolExecutor() as executor: + return list(executor.map(orjson.dumps, data)) + +def benchmark_no_return(data): + for json_obj in data: + orjson.dumps(json_obj) + + +num_json_objects = 2000 +json_length = 3000 +data = [create_large_json(json_length) for _ in range(num_json_objects)] + +print("Sequential serialization:") +print(timeit.timeit(lambda: benchmark_sequential(data), number=10)) + +print("Parallel serialization with ThreadPoolExecutor:") +print(timeit.timeit(lambda: benchmark_parallel(data), number=10)) + +print("Parallel serialization with map:") +print(timeit.timeit(lambda: benchmark_with_map(data), number=15)) + +print("Parallel serialization without return:") +print(timeit.timeit(lambda: benchmark_no_return(data), number=15)) diff --git a/python/bench/tracing_client_bench.py b/python/bench/tracing_client_bench.py new file mode 100644 index 000000000..5a583a579 --- /dev/null +++ b/python/bench/tracing_client_bench.py @@ -0,0 +1,109 @@ +import statistics +import time +from typing import Dict +from unittest.mock import Mock +from uuid import uuid4 + +import pytest + +from langsmith.client import Client + + +def create_large_json(length: int) -> Dict: + """Create a large JSON object for benchmarking purposes.""" + large_array = [ + { + "index": i, + "data": f"This is element number {i}", + "nested": {"id": i, "value": f"Nested value for element {i}"}, + } + for i in range(length) + ] + + return { + "name": "Huge JSON", + "description": "This is a very large JSON object for benchmarking purposes.", + "array": large_array, + "metadata": { + "created_at": "2024-10-22T19:00:00Z", + "author": "Python Program", + "version": 1.0, + }, + } + + +def create_run_data(run_id: str, json_size: int) -> Dict: + """Create a single run data object.""" + return { + "name": "Run Name", + "id": run_id, + "run_type": "chain", + "inputs": create_large_json(json_size), + "outputs": create_large_json(json_size), + "extra": {"extra_data": "value"}, + "trace_id": "trace_id", + "dotted_order": "1.1", + "tags": ["tag1", "tag2"], + "session_name": "Session Name", + } + + +def benchmark_run_creation(num_runs: int, json_size: int, samples: int = 10) -> Dict: + """ + Benchmark run creation with specified parameters. + Returns timing statistics. + """ + timings = [] + + for _ in range(samples): + runs = [create_run_data(str(uuid4()), json_size) for i in range(num_runs)] + + mock_session = Mock() + mock_response = Mock() + mock_response.status_code = 202 + mock_response.text = "Accepted" + mock_response.json.return_value = {"status": "success"} + mock_session.request.return_value = mock_response + client = Client(session=mock_session, api_key="xxx") + + start = time.perf_counter() + for run in runs: + client.create_run(**run) + + # wait for client.tracing_queue to be empty + client.tracing_queue.join() + + elapsed = time.perf_counter() - start + + timings.append(elapsed) + + return { + "mean": statistics.mean(timings), + "median": statistics.median(timings), + "stdev": statistics.stdev(timings) if len(timings) > 1 else 0, + "min": min(timings), + "max": max(timings), + } + + +json_size = 3_000 +num_runs = 1000 + +def main(json_size: int, num_runs: int): + """ + Run benchmarks with different combinations of parameters and report results. + """ + + results = benchmark_run_creation(num_runs=num_runs, json_size=json_size) + + print(f"\nBenchmark Results for {num_runs} runs with JSON size {json_size}:") + print(f"Mean time: {results['mean']:.4f} seconds") + print(f"Median time: {results['median']:.4f} seconds") + print(f"Std Dev: {results['stdev']:.4f} seconds") + print(f"Min time: {results['min']:.4f} seconds") + print(f"Max time: {results['max']:.4f} seconds") + print(f"Throughput: {num_runs / results['mean']:.2f} runs/second") + + +if __name__ == "__main__": + main(json_size, num_runs) \ No newline at end of file diff --git a/python/langsmith/client.py b/python/langsmith/client.py index c361abea9..639113393 100644 --- a/python/langsmith/client.py +++ b/python/langsmith/client.py @@ -1360,7 +1360,7 @@ def create_run( } if not self._filter_for_sampling([run_create]): return - run_create = self._run_transform(run_create, copy=True) + run_create = self._run_transform(run_create, copy=False) if revision_id is not None: run_create["extra"]["metadata"]["revision_id"] = revision_id if ( @@ -1656,6 +1656,7 @@ def multipart_ingest_runs( ("events", payload.pop("events", None)), ] # encode the main run payload + # print("Dumping payload for run in multipart ingest runs") payloadb = _dumps_json(payload) acc_parts.append( ( @@ -1672,6 +1673,7 @@ def multipart_ingest_runs( for key, value in fields: if value is None: continue + # print("Dumping payload in multipart ingest runs", event) valb = _dumps_json(value) acc_parts.append( ( @@ -5779,22 +5781,31 @@ def _tracing_thread_handle_batch( client: Client, tracing_queue: Queue, batch: List[TracingQueueItem], - use_multipart: bool, + use_multipart: bool = True, ) -> None: + print("Handling a batch of size", len(batch)) create = [it.item for it in batch if it.action == "create"] update = [it.item for it in batch if it.action == "update"] try: - if use_multipart: + if True: + print("Using multipart endpoint") + now = time.perf_counter() client.multipart_ingest_runs(create=create, update=update, pre_sampled=True) + print("Multipart ingest took", time.perf_counter() - now) else: + print("Using batch endpoint") + now = time.time() client.batch_ingest_runs(create=create, update=update, pre_sampled=True) + print("Batch ingest took", time.time() - now) except Exception: + print("Error in tracing queue") logger.error("Error in tracing queue", exc_info=True) # exceptions are logged elsewhere, but we need to make sure the # background thread continues to run pass finally: for _ in batch: + # print("marking task as done") tracing_queue.task_done() @@ -5909,6 +5920,7 @@ def _tracing_sub_thread_func( seen_successive_empty_queues += 1 # drain the queue on exit + print("Draining queue on exit") while next_batch := _tracing_thread_drain_queue( tracing_queue, limit=size_limit, block=False ): diff --git a/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs b/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs index 67af76fb3..2eef06663 100644 --- a/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs +++ b/rust/crates/langsmith_tracing_client/benches/json_serialization_benchmark.rs @@ -1,6 +1,6 @@ use rayon::prelude::*; use serde_json::Value; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn create_large_json(len: usize) -> Value { let large_array: Vec = (0..len) @@ -42,9 +42,19 @@ fn benchmark_parallel(data: &[Value]) -> Vec> { .collect() } +// into par iter +fn benchmark_into_par_iter(data: &[Value]) -> Vec> { + let start = std::time::Instant::now(); + let meow = data.into_par_iter() + .map(|json| serde_json::to_vec(&json).expect("Failed to serialize JSON")) + .collect(); + println!("into_par_iter: {:?}", start.elapsed()); + meow +} + fn json_benchmark(c: &mut Criterion) { - let num_json_objects = 1000; - let json_length = 1000; + let num_json_objects = 2000; + let json_length = 3000; let data: Vec = (0..num_json_objects) .map(|_| create_large_json(json_length)) .collect(); @@ -56,6 +66,10 @@ fn json_benchmark(c: &mut Criterion) { c.bench_function("parallel serialization", |b| { b.iter(|| benchmark_parallel(&data)) }); + + c.bench_function("into par iter serialization", |b| { + b.iter(|| benchmark_into_par_iter(black_box(&data))) + }); } criterion_group! { From df21de0eb42fd91cf020a67fd40609f5858febbf Mon Sep 17 00:00:00 2001 From: Ankush Gola Date: Sat, 26 Oct 2024 00:55:40 -0700 Subject: [PATCH 31/31] more prints --- python/bench/tracing_client_bench.py | 2 +- python/langsmith/client.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/python/bench/tracing_client_bench.py b/python/bench/tracing_client_bench.py index 5a583a579..e7ed62c5f 100644 --- a/python/bench/tracing_client_bench.py +++ b/python/bench/tracing_client_bench.py @@ -48,7 +48,7 @@ def create_run_data(run_id: str, json_size: int) -> Dict: } -def benchmark_run_creation(num_runs: int, json_size: int, samples: int = 10) -> Dict: +def benchmark_run_creation(num_runs: int, json_size: int, samples: int = 1) -> Dict: """ Benchmark run creation with specified parameters. Returns timing statistics. diff --git a/python/langsmith/client.py b/python/langsmith/client.py index 639113393..85b0a52d0 100644 --- a/python/langsmith/client.py +++ b/python/langsmith/client.py @@ -5867,17 +5867,20 @@ def _tracing_control_thread_func(client_ref: weakref.ref[Client]) -> None: len(sub_threads) < scale_up_nthreads_limit and tracing_queue.qsize() > scale_up_qsize_trigger ): + print("Scaling up...") new_thread = threading.Thread( target=_tracing_sub_thread_func, args=(weakref.ref(client), use_multipart), ) sub_threads.append(new_thread) new_thread.start() + print("Started new thread, there are now", len(sub_threads), "threads") if next_batch := _tracing_thread_drain_queue(tracing_queue, limit=size_limit): _tracing_thread_handle_batch( client, tracing_queue, next_batch, use_multipart ) # drain the queue on exit + print("Draining queue on exit") while next_batch := _tracing_thread_drain_queue( tracing_queue, limit=size_limit, block=False ):