Unverified Commit fd5cc288 authored by ishandhanani's avatar ishandhanani Committed by GitHub
Browse files

refactor(3/3): switch dynamo-protocols to upstream async-openai types (#7625)


Co-authored-by: default avatarDmitry Tokarev <dtokarev@nvidia.com>
parent d517fb80
...@@ -146,9 +146,9 @@ dependencies = [ ...@@ -146,9 +146,9 @@ dependencies = [
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.8.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
...@@ -263,14 +263,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -263,14 +263,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]] [[package]]
name = "async-openai-macros" name = "async-openai"
version = "0.1.1" version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81872a8e595e8ceceab71c6ba1f9078e313b452a1e31934e6763ef5d308705e4" checksum = "ec08254d61379df136135d3d1ac04301be7699fd7d9e57655c63ac7d650a6922"
dependencies = [ dependencies = [
"proc-macro2", "bytes",
"quote", "derive_builder",
"syn 2.0.117", "getrandom 0.3.4",
"serde",
"serde_json",
] ]
[[package]] [[package]]
...@@ -383,9 +385,9 @@ dependencies = [ ...@@ -383,9 +385,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-rs" name = "aws-lc-rs"
version = "1.16.1" version = "1.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
dependencies = [ dependencies = [
"aws-lc-sys", "aws-lc-sys",
"zeroize", "zeroize",
...@@ -393,9 +395,9 @@ dependencies = [ ...@@ -393,9 +395,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.38.0" version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399"
dependencies = [ dependencies = [
"cc", "cc",
"cmake", "cmake",
...@@ -444,7 +446,7 @@ dependencies = [ ...@@ -444,7 +446,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"itoa", "itoa",
"matchit 0.8.4", "matchit 0.8.4",
...@@ -526,7 +528,7 @@ dependencies = [ ...@@ -526,7 +528,7 @@ dependencies = [
"fs-err", "fs-err",
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"pin-project-lite", "pin-project-lite",
"rustls", "rustls",
...@@ -537,20 +539,6 @@ dependencies = [ ...@@ -537,20 +539,6 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "backoff"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"futures-core",
"getrandom 0.2.17",
"instant",
"pin-project-lite",
"rand 0.8.5",
"tokio",
]
[[package]] [[package]]
name = "backon" name = "backon"
version = "1.6.0" version = "1.6.0"
...@@ -648,7 +636,7 @@ dependencies = [ ...@@ -648,7 +636,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"rustc-hash 2.1.1", "rustc-hash 2.1.2",
"shlex", "shlex",
"syn 2.0.117", "syn 2.0.117",
] ]
...@@ -715,16 +703,16 @@ dependencies = [ ...@@ -715,16 +703,16 @@ dependencies = [
[[package]] [[package]]
name = "blake3" name = "blake3"
version = "1.8.3" version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"arrayvec", "arrayvec",
"cc", "cc",
"cfg-if", "cfg-if",
"constant_time_eq", "constant_time_eq",
"cpufeatures", "cpufeatures 0.3.0",
"memmap2", "memmap2",
"rayon-core", "rayon-core",
] ]
...@@ -846,7 +834,7 @@ checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" ...@@ -846,7 +834,7 @@ checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
dependencies = [ dependencies = [
"clap 4.6.0", "clap 4.6.0",
"heck 0.4.1", "heck 0.4.1",
"indexmap 2.13.0", "indexmap 2.13.1",
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -859,9 +847,9 @@ dependencies = [ ...@@ -859,9 +847,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.57" version = "1.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
...@@ -1005,9 +993,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" ...@@ -1005,9 +993,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.57" version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
dependencies = [ dependencies = [
"cc", "cc",
] ]
...@@ -1059,9 +1047,9 @@ dependencies = [ ...@@ -1059,9 +1047,9 @@ dependencies = [
[[package]] [[package]]
name = "config" name = "config"
version = "0.15.21" version = "0.15.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe5feec195269515c4722937cd7ffcfe7b4205d18d2e6577b7223ecb159ab00" checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"convert_case", "convert_case",
...@@ -1072,8 +1060,8 @@ dependencies = [ ...@@ -1072,8 +1060,8 @@ dependencies = [
"serde-untagged", "serde-untagged",
"serde_core", "serde_core",
"serde_json", "serde_json",
"toml 1.0.6+spec-1.1.0", "toml 1.1.2+spec-1.1.0",
"winnow", "winnow 1.0.1",
"yaml-rust2", "yaml-rust2",
] ]
...@@ -1226,6 +1214,15 @@ dependencies = [ ...@@ -1226,6 +1214,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
...@@ -1429,7 +1426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -1429,7 +1426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures 0.2.17",
"curve25519-dalek-derive", "curve25519-dalek-derive",
"digest", "digest",
"fiat-crypto", "fiat-crypto",
...@@ -1450,9 +1447,9 @@ dependencies = [ ...@@ -1450,9 +1447,9 @@ dependencies = [
[[package]] [[package]]
name = "daachorse" name = "daachorse"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b7ef7a4be509357f4804d0a22e830daddb48f19fd604e4ad32ddce04a94c36" checksum = "6f55d7153ba3b507595872a3874803f07a8a81d1e888abed8e5db7da0597d6e2"
[[package]] [[package]]
name = "darling" name = "darling"
...@@ -1746,9 +1743,9 @@ dependencies = [ ...@@ -1746,9 +1743,9 @@ dependencies = [
[[package]] [[package]]
name = "dircpy" name = "dircpy"
version = "0.3.19" version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a88521b0517f5f9d51d11925d8ab4523497dcf947073fa3231a311b63941131c" checksum = "ebcbec2b9a580ddee352ac38523d2ecd4dcaad53532957034394556909e27f4b"
dependencies = [ dependencies = [
"jwalk", "jwalk",
"log", "log",
...@@ -1878,7 +1875,7 @@ dependencies = [ ...@@ -1878,7 +1875,7 @@ dependencies = [
"rmp-serde", "rmp-serde",
"rstest 0.18.2", "rstest 0.18.2",
"rstest_reuse", "rstest_reuse",
"rustc-hash 2.1.1", "rustc-hash 2.1.2",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
...@@ -2023,7 +2020,7 @@ dependencies = [ ...@@ -2023,7 +2020,7 @@ dependencies = [
"ndarray-npy", "ndarray-npy",
"rand 0.9.2", "rand 0.9.2",
"rstest 0.18.2", "rstest 0.18.2",
"rustc-hash 2.1.1", "rustc-hash 2.1.2",
"serde", "serde",
"serde_json", "serde_json",
"slotmap", "slotmap",
...@@ -2058,28 +2055,16 @@ dependencies = [ ...@@ -2058,28 +2055,16 @@ dependencies = [
name = "dynamo-protocols" name = "dynamo-protocols"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"async-openai-macros", "async-openai",
"backoff",
"base64 0.22.1",
"bytes",
"derive_builder", "derive_builder",
"eventsource-stream",
"futures", "futures",
"rand 0.9.2",
"reqwest 0.12.28",
"reqwest-eventsource",
"secrecy",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"tokio-stream",
"tokio-test", "tokio-test",
"tokio-tungstenite",
"tokio-util",
"tracing", "tracing",
"url", "url",
"utoipa",
"uuid", "uuid",
] ]
...@@ -2344,17 +2329,6 @@ dependencies = [ ...@@ -2344,17 +2329,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "eventsource-stream"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab"
dependencies = [
"futures-core",
"nom 7.1.3",
"pin-project-lite",
]
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.0" version = "1.74.0"
...@@ -2423,11 +2397,11 @@ dependencies = [ ...@@ -2423,11 +2397,11 @@ dependencies = [
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f"
dependencies = [ dependencies = [
"getrandom 0.2.17", "getrandom 0.3.4",
] ]
[[package]] [[package]]
...@@ -2843,7 +2817,7 @@ dependencies = [ ...@@ -2843,7 +2817,7 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http 0.2.12", "http 0.2.12",
"indexmap 2.13.0", "indexmap 2.13.1",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
...@@ -2862,7 +2836,7 @@ dependencies = [ ...@@ -2862,7 +2836,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.4.0", "http 1.4.0",
"indexmap 2.13.0", "indexmap 2.13.1",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
...@@ -3123,9 +3097,9 @@ dependencies = [ ...@@ -3123,9 +3097,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.8.1" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
...@@ -3138,7 +3112,6 @@ dependencies = [ ...@@ -3138,7 +3112,6 @@ dependencies = [
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"pin-utils",
"smallvec", "smallvec",
"tokio", "tokio",
"want", "want",
...@@ -3151,7 +3124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -3151,7 +3124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
dependencies = [ dependencies = [
"http 1.4.0", "http 1.4.0",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"log", "log",
"rustls", "rustls",
...@@ -3169,7 +3142,7 @@ version = "0.5.2" ...@@ -3169,7 +3142,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [ dependencies = [
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
...@@ -3188,7 +3161,7 @@ dependencies = [ ...@@ -3188,7 +3161,7 @@ dependencies = [
"futures-util", "futures-util",
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"hyper 1.8.1", "hyper 1.9.0",
"ipnet", "ipnet",
"libc", "libc",
"percent-encoding", "percent-encoding",
...@@ -3227,12 +3200,13 @@ dependencies = [ ...@@ -3227,12 +3200,13 @@ dependencies = [
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"potential_utf", "potential_utf",
"utf8_iter",
"yoke", "yoke",
"zerofrom", "zerofrom",
"zerovec", "zerovec",
...@@ -3240,9 +3214,9 @@ dependencies = [ ...@@ -3240,9 +3214,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_locale_core" name = "icu_locale_core"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"litemap", "litemap",
...@@ -3253,9 +3227,9 @@ dependencies = [ ...@@ -3253,9 +3227,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_normalizer" name = "icu_normalizer"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_normalizer_data", "icu_normalizer_data",
...@@ -3270,15 +3244,15 @@ dependencies = [ ...@@ -3270,15 +3244,15 @@ dependencies = [
[[package]] [[package]]
name = "icu_normalizer_data" name = "icu_normalizer_data"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]] [[package]]
name = "icu_properties" name = "icu_properties"
version = "2.1.2" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_locale_core", "icu_locale_core",
...@@ -3290,15 +3264,15 @@ dependencies = [ ...@@ -3290,15 +3264,15 @@ dependencies = [
[[package]] [[package]]
name = "icu_properties_data" name = "icu_properties_data"
version = "2.1.2" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]] [[package]]
name = "icu_provider" name = "icu_provider"
version = "2.1.1" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"icu_locale_core", "icu_locale_core",
...@@ -3396,9 +3370,9 @@ dependencies = [ ...@@ -3396,9 +3370,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.16.1", "hashbrown 0.16.1",
...@@ -3460,11 +3434,11 @@ dependencies = [ ...@@ -3460,11 +3434,11 @@ dependencies = [
[[package]] [[package]]
name = "insta" name = "insta"
version = "1.46.3" version = "1.47.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e"
dependencies = [ dependencies = [
"console 0.15.11", "console 0.16.3",
"globset", "globset",
"once_cell", "once_cell",
"pest", "pest",
...@@ -3476,15 +3450,6 @@ dependencies = [ ...@@ -3476,15 +3450,6 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "interpolate_name" name = "interpolate_name"
version = "0.2.4" version = "0.2.4"
...@@ -3504,9 +3469,9 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" ...@@ -3504,9 +3469,9 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
[[package]] [[package]]
name = "iri-string" name = "iri-string"
version = "0.7.10" version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
...@@ -3588,9 +3553,9 @@ dependencies = [ ...@@ -3588,9 +3553,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.17" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]] [[package]]
name = "jiff" name = "jiff"
...@@ -3645,10 +3610,12 @@ dependencies = [ ...@@ -3645,10 +3610,12 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.91" version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [ dependencies = [
"cfg-if",
"futures-util",
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
] ]
...@@ -3809,7 +3776,7 @@ dependencies = [ ...@@ -3809,7 +3776,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-rustls", "hyper-rustls",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
...@@ -3920,7 +3887,7 @@ dependencies = [ ...@@ -3920,7 +3887,7 @@ dependencies = [
"derive_builder", "derive_builder",
"dynamo-tokens", "dynamo-tokens",
"futures", "futures",
"indexmap 2.13.0", "indexmap 2.13.1",
"lru 0.16.3", "lru 0.16.3",
"parking_lot", "parking_lot",
"prometheus", "prometheus",
...@@ -3992,9 +3959,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" ...@@ -3992,9 +3959,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.183" version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]] [[package]]
name = "libdynamo_llm" name = "libdynamo_llm"
...@@ -4052,9 +4019,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" ...@@ -4052,9 +4019,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.14" version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"libc", "libc",
...@@ -4076,15 +4043,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" ...@@ -4076,15 +4043,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]] [[package]]
name = "local-ip-address" name = "local-ip-address"
version = "0.6.10" version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79ef8c257c92ade496781a32a581d43e3d512cf8ce714ecf04ea80f93ed0ff4a" checksum = "d4a59a0cb1c7f84471ad5cd38d768c2a29390d17f1ff2827cdf49bc53e8ac70b"
dependencies = [ dependencies = [
"libc", "libc",
"neli", "neli",
...@@ -4321,20 +4288,19 @@ dependencies = [ ...@@ -4321,20 +4288,19 @@ dependencies = [
[[package]] [[package]]
name = "minijinja" name = "minijinja"
version = "2.17.1" version = "2.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea5ea1e90055f200af6b8e52a4a34e05e77e7fee953a9fb40c631efdc43cab1" checksum = "805bfd7352166bae857ee569628b52bcd85a1cecf7810861ebceb1686b72b75d"
dependencies = [ dependencies = [
"memo-map", "memo-map",
"self_cell",
"serde", "serde",
] ]
[[package]] [[package]]
name = "minijinja-contrib" name = "minijinja-contrib"
version = "2.17.1" version = "2.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fce60cb2e26ba7ddd485c8f5d3d635535e465c195bfb4af85971b428a985d0" checksum = "45092d80391870622fcf3bd82f5d2af18f99533ea60debb4bc9db0c76f0e809a"
dependencies = [ dependencies = [
"minijinja", "minijinja",
"serde", "serde",
...@@ -4380,9 +4346,9 @@ dependencies = [ ...@@ -4380,9 +4346,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.1.1" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi",
...@@ -4402,7 +4368,7 @@ dependencies = [ ...@@ -4402,7 +4368,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"log", "log",
"pin-project-lite", "pin-project-lite",
...@@ -4754,9 +4720,9 @@ dependencies = [ ...@@ -4754,9 +4720,9 @@ dependencies = [
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]] [[package]]
name = "num-derive" name = "num-derive"
...@@ -5010,7 +4976,7 @@ dependencies = [ ...@@ -5010,7 +4976,7 @@ dependencies = [
"http-body-util", "http-body-util",
"httparse", "httparse",
"humantime", "humantime",
"hyper 1.8.1", "hyper 1.9.0",
"itertools 0.14.0", "itertools 0.14.0",
"md-5", "md-5",
"parking_lot", "parking_lot",
...@@ -5165,9 +5131,9 @@ dependencies = [ ...@@ -5165,9 +5131,9 @@ dependencies = [
[[package]] [[package]]
name = "opentelemetry-otlp" name = "opentelemetry-otlp"
version = "0.31.0" version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f"
dependencies = [ dependencies = [
"http 1.4.0", "http 1.4.0",
"opentelemetry", "opentelemetry",
...@@ -5429,7 +5395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -5429,7 +5395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"indexmap 2.13.0", "indexmap 2.13.1",
] ]
[[package]] [[package]]
...@@ -5440,7 +5406,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" ...@@ -5440,7 +5406,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.13.0", "indexmap 2.13.1",
] ]
[[package]] [[package]]
...@@ -5593,9 +5559,9 @@ dependencies = [ ...@@ -5593,9 +5559,9 @@ dependencies = [
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [ dependencies = [
"zerovec", "zerovec",
] ]
...@@ -5631,7 +5597,7 @@ version = "3.5.0" ...@@ -5631,7 +5597,7 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [ dependencies = [
"toml_edit 0.25.4+spec-1.1.0", "toml_edit 0.25.10+spec-1.1.0",
] ]
[[package]] [[package]]
...@@ -5714,9 +5680,9 @@ dependencies = [ ...@@ -5714,9 +5680,9 @@ dependencies = [
[[package]] [[package]]
name = "proptest" name = "proptest"
version = "1.10.0" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
dependencies = [ dependencies = [
"bit-set 0.8.0", "bit-set 0.8.0",
"bit-vec 0.8.0", "bit-vec 0.8.0",
...@@ -5757,7 +5723,7 @@ version = "0.13.5" ...@@ -5757,7 +5723,7 @@ version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
dependencies = [ dependencies = [
"heck 0.4.1", "heck 0.5.0",
"itertools 0.14.0", "itertools 0.14.0",
"log", "log",
"multimap", "multimap",
...@@ -5777,7 +5743,7 @@ version = "0.14.3" ...@@ -5777,7 +5743,7 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
dependencies = [ dependencies = [
"heck 0.4.1", "heck 0.5.0",
"itertools 0.14.0", "itertools 0.14.0",
"log", "log",
"multimap", "multimap",
...@@ -5858,9 +5824,9 @@ dependencies = [ ...@@ -5858,9 +5824,9 @@ dependencies = [
[[package]] [[package]]
name = "pulldown-cmark" name = "pulldown-cmark"
version = "0.13.1" version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c41efbf8f90ac44de7f3a868f0867851d261b56291732d0cbf7cceaaeb55a6" checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"memchr", "memchr",
...@@ -5937,7 +5903,7 @@ dependencies = [ ...@@ -5937,7 +5903,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"quinn-proto", "quinn-proto",
"quinn-udp", "quinn-udp",
"rustc-hash 2.1.1", "rustc-hash 2.1.2",
"rustls", "rustls",
"socket2 0.6.3", "socket2 0.6.3",
"thiserror 2.0.18", "thiserror 2.0.18",
...@@ -5957,7 +5923,7 @@ dependencies = [ ...@@ -5957,7 +5923,7 @@ dependencies = [
"lru-slab", "lru-slab",
"rand 0.9.2", "rand 0.9.2",
"ring", "ring",
"rustc-hash 2.1.1", "rustc-hash 2.1.2",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"slab", "slab",
...@@ -6303,7 +6269,7 @@ dependencies = [ ...@@ -6303,7 +6269,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-rustls", "hyper-rustls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
...@@ -6334,22 +6300,6 @@ dependencies = [ ...@@ -6334,22 +6300,6 @@ dependencies = [
"webpki-roots 1.0.6", "webpki-roots 1.0.6",
] ]
[[package]]
name = "reqwest-eventsource"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde"
dependencies = [
"eventsource-stream",
"futures-core",
"futures-timer",
"mime",
"nom 7.1.3",
"pin-project-lite",
"reqwest 0.12.28",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.53" version = "0.8.53"
...@@ -6391,9 +6341,9 @@ dependencies = [ ...@@ -6391,9 +6341,9 @@ dependencies = [
[[package]] [[package]]
name = "ron" name = "ron"
version = "0.12.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"once_cell", "once_cell",
...@@ -6584,9 +6534,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" ...@@ -6584,9 +6534,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "2.1.1" version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
...@@ -6634,7 +6584,7 @@ dependencies = [ ...@@ -6634,7 +6584,7 @@ dependencies = [
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.103.9", "rustls-webpki 0.103.10",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
...@@ -6695,9 +6645,9 @@ dependencies = [ ...@@ -6695,9 +6645,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.9" version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"ring", "ring",
...@@ -6868,7 +6818,6 @@ version = "0.10.3" ...@@ -6868,7 +6818,6 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
dependencies = [ dependencies = [
"serde",
"zeroize", "zeroize",
] ]
...@@ -6908,17 +6857,11 @@ dependencies = [ ...@@ -6908,17 +6857,11 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "self_cell"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.27" version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]] [[package]]
name = "serde" name = "serde"
...@@ -7009,7 +6952,7 @@ version = "1.0.149" ...@@ -7009,7 +6952,7 @@ version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.1",
"itoa", "itoa",
"memchr", "memchr",
"serde", "serde",
...@@ -7059,9 +7002,9 @@ dependencies = [ ...@@ -7059,9 +7002,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.0.4" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
...@@ -7088,7 +7031,7 @@ dependencies = [ ...@@ -7088,7 +7031,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.13.0", "indexmap 2.13.1",
"schemars 0.9.0", "schemars 0.9.0",
"schemars 1.2.1", "schemars 1.2.1",
"serde_core", "serde_core",
...@@ -7115,7 +7058,7 @@ version = "0.9.34+deprecated" ...@@ -7115,7 +7058,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.1",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
...@@ -7155,7 +7098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -7155,7 +7098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures 0.2.17",
"digest", "digest",
] ]
...@@ -7166,7 +7109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -7166,7 +7109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures 0.2.17",
"digest", "digest",
] ]
...@@ -7225,9 +7168,9 @@ dependencies = [ ...@@ -7225,9 +7168,9 @@ dependencies = [
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.8" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]] [[package]]
name = "simd_helpers" name = "simd_helpers"
...@@ -7660,9 +7603,9 @@ dependencies = [ ...@@ -7660,9 +7603,9 @@ dependencies = [
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.2" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"zerovec", "zerovec",
...@@ -7748,7 +7691,7 @@ checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" ...@@ -7748,7 +7691,7 @@ checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [ dependencies = [
"bytes", "bytes",
"libc", "libc",
"mio 1.1.1", "mio 1.2.0",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
...@@ -7825,18 +7768,6 @@ dependencies = [ ...@@ -7825,18 +7768,6 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-tungstenite"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.18" version = "0.7.18"
...@@ -7887,15 +7818,15 @@ dependencies = [ ...@@ -7887,15 +7818,15 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "1.0.6+spec-1.1.0" version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_spanned 1.0.4", "serde_spanned 1.1.1",
"toml_datetime 1.0.0+spec-1.1.0", "toml_datetime 1.1.1+spec-1.1.0",
"toml_parser", "toml_parser",
"winnow", "winnow 1.0.1",
] ]
[[package]] [[package]]
...@@ -7909,9 +7840,9 @@ dependencies = [ ...@@ -7909,9 +7840,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "1.0.0+spec-1.1.0" version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
...@@ -7922,33 +7853,33 @@ version = "0.22.27" ...@@ -7922,33 +7853,33 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.1",
"serde", "serde",
"serde_spanned 0.6.9", "serde_spanned 0.6.9",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
"toml_write", "toml_write",
"winnow", "winnow 0.7.15",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.25.4+spec-1.1.0" version = "0.25.10+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.1",
"toml_datetime 1.0.0+spec-1.1.0", "toml_datetime 1.1.1+spec-1.1.0",
"toml_parser", "toml_parser",
"winnow", "winnow 1.0.1",
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.9+spec-1.1.0" version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [ dependencies = [
"winnow", "winnow 1.0.1",
] ]
[[package]] [[package]]
...@@ -7972,7 +7903,7 @@ dependencies = [ ...@@ -7972,7 +7903,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
"percent-encoding", "percent-encoding",
...@@ -8001,7 +7932,7 @@ dependencies = [ ...@@ -8001,7 +7932,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
"percent-encoding", "percent-encoding",
...@@ -8030,7 +7961,7 @@ dependencies = [ ...@@ -8030,7 +7961,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
"percent-encoding", "percent-encoding",
...@@ -8127,7 +8058,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" ...@@ -8127,7 +8058,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"indexmap 2.13.0", "indexmap 2.13.1",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
"sync_wrapper 1.0.2", "sync_wrapper 1.0.2",
...@@ -8279,19 +8210,6 @@ dependencies = [ ...@@ -8279,19 +8210,6 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tungstenite"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
dependencies = [
"bytes",
"log",
"rand 0.9.2",
"thiserror 2.0.18",
"utf-8",
]
[[package]] [[package]]
name = "typeid" name = "typeid"
version = "1.0.3" version = "1.0.3"
...@@ -8412,9 +8330,9 @@ dependencies = [ ...@@ -8412,9 +8330,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.12.0" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
...@@ -8518,12 +8436,6 @@ dependencies = [ ...@@ -8518,12 +8436,6 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "utf16_iter" name = "utf16_iter"
version = "1.0.5" version = "1.0.5"
...@@ -8548,7 +8460,7 @@ version = "5.4.0" ...@@ -8548,7 +8460,7 @@ version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993"
dependencies = [ dependencies = [
"indexmap 2.13.0", "indexmap 2.13.1",
"serde", "serde",
"serde_json", "serde_json",
"utoipa-gen", "utoipa-gen",
...@@ -8564,8 +8476,6 @@ dependencies = [ ...@@ -8564,8 +8476,6 @@ dependencies = [
"quote", "quote",
"regex", "regex",
"syn 2.0.117", "syn 2.0.117",
"url",
"uuid",
] ]
[[package]] [[package]]
...@@ -8588,9 +8498,9 @@ dependencies = [ ...@@ -8588,9 +8498,9 @@ dependencies = [
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.22.0" version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
dependencies = [ dependencies = [
"getrandom 0.4.2", "getrandom 0.4.2",
"js-sys", "js-sys",
...@@ -8696,7 +8606,7 @@ dependencies = [ ...@@ -8696,7 +8606,7 @@ dependencies = [
"http 1.4.0", "http 1.4.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.8.1", "hyper 1.9.0",
"hyper-util", "hyper-util",
"nix 0.30.1", "nix 0.30.1",
"parking_lot", "parking_lot",
...@@ -8800,9 +8710,9 @@ dependencies = [ ...@@ -8800,9 +8710,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.114" version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
...@@ -8813,23 +8723,19 @@ dependencies = [ ...@@ -8813,23 +8723,19 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.64" version = "0.4.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e"
dependencies = [ dependencies = [
"cfg-if",
"futures-util",
"js-sys", "js-sys",
"once_cell",
"wasm-bindgen", "wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.114" version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
...@@ -8837,9 +8743,9 @@ dependencies = [ ...@@ -8837,9 +8743,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.114" version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
...@@ -8850,9 +8756,9 @@ dependencies = [ ...@@ -8850,9 +8756,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.114" version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
...@@ -8874,7 +8780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -8874,7 +8780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"indexmap 2.13.0", "indexmap 2.13.1",
"wasm-encoder", "wasm-encoder",
"wasmparser", "wasmparser",
] ]
...@@ -8900,15 +8806,15 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" ...@@ -8900,15 +8806,15 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.13.0", "indexmap 2.13.1",
"semver", "semver",
] ]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.91" version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
...@@ -9289,6 +9195,15 @@ dependencies = [ ...@@ -9289,6 +9195,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"
...@@ -9327,7 +9242,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" ...@@ -9327,7 +9242,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck 0.5.0", "heck 0.5.0",
"indexmap 2.13.0", "indexmap 2.13.1",
"prettyplease", "prettyplease",
"syn 2.0.117", "syn 2.0.117",
"wasm-metadata", "wasm-metadata",
...@@ -9358,7 +9273,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" ...@@ -9358,7 +9273,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.11.0", "bitflags 2.11.0",
"indexmap 2.13.0", "indexmap 2.13.1",
"log", "log",
"serde", "serde",
"serde_derive", "serde_derive",
...@@ -9377,7 +9292,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" ...@@ -9377,7 +9292,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",
"indexmap 2.13.0", "indexmap 2.13.1",
"log", "log",
"semver", "semver",
"serde", "serde",
...@@ -9395,9 +9310,9 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" ...@@ -9395,9 +9310,9 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
[[package]] [[package]]
name = "writeable" name = "writeable"
version = "0.6.2" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]] [[package]]
name = "xxhash-rust" name = "xxhash-rust"
...@@ -9430,9 +9345,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" ...@@ -9430,9 +9345,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [ dependencies = [
"stable_deref_trait", "stable_deref_trait",
"yoke-derive", "yoke-derive",
...@@ -9441,9 +9356,9 @@ dependencies = [ ...@@ -9441,9 +9356,9 @@ dependencies = [
[[package]] [[package]]
name = "yoke-derive" name = "yoke-derive"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -9453,18 +9368,18 @@ dependencies = [ ...@@ -9453,18 +9368,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.42" version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.42" version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -9473,18 +9388,18 @@ dependencies = [ ...@@ -9473,18 +9388,18 @@ dependencies = [
[[package]] [[package]]
name = "zerofrom" name = "zerofrom"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [ dependencies = [
"zerofrom-derive", "zerofrom-derive",
] ]
[[package]] [[package]]
name = "zerofrom-derive" name = "zerofrom-derive"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -9510,9 +9425,9 @@ dependencies = [ ...@@ -9510,9 +9425,9 @@ dependencies = [
[[package]] [[package]]
name = "zerotrie" name = "zerotrie"
version = "0.2.3" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"yoke", "yoke",
...@@ -9521,9 +9436,9 @@ dependencies = [ ...@@ -9521,9 +9436,9 @@ dependencies = [
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.11.5" version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [ dependencies = [
"yoke", "yoke",
"zerofrom", "zerofrom",
...@@ -9532,9 +9447,9 @@ dependencies = [ ...@@ -9532,9 +9447,9 @@ dependencies = [
[[package]] [[package]]
name = "zerovec-derive" name = "zerovec-derive"
version = "0.11.2" version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
...@@ -9552,7 +9467,7 @@ dependencies = [ ...@@ -9552,7 +9467,7 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"displaydoc", "displaydoc",
"flate2", "flate2",
"indexmap 2.13.0", "indexmap 2.13.1",
"memchr", "memchr",
"thiserror 2.0.18", "thiserror 2.0.18",
"zopfli", "zopfli",
...@@ -9567,7 +9482,7 @@ dependencies = [ ...@@ -9567,7 +9482,7 @@ dependencies = [
"arbitrary", "arbitrary",
"crc32fast", "crc32fast",
"flate2", "flate2",
"indexmap 2.13.0", "indexmap 2.13.1",
"memchr", "memchr",
"zopfli", "zopfli",
] ]
...@@ -9635,9 +9550,9 @@ dependencies = [ ...@@ -9635,9 +9550,9 @@ dependencies = [
[[package]] [[package]]
name = "zune-jpeg" name = "zune-jpeg"
version = "0.5.13" version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec5f41c76397b7da451efd19915684f727d7e1d516384ca6bd0ec43ec94de23c" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [ dependencies = [
"zune-core", "zune-core",
] ]
...@@ -44,7 +44,7 @@ dynamo-tokens = { path = "lib/tokens", version = "1.0.0" } ...@@ -44,7 +44,7 @@ dynamo-tokens = { path = "lib/tokens", version = "1.0.0" }
dynamo-memory = { path = "lib/memory", version = "1.0.0" } dynamo-memory = { path = "lib/memory", version = "1.0.0" }
dynamo-mocker = { path = "lib/mocker", version = "1.0.0" } dynamo-mocker = { path = "lib/mocker", version = "1.0.0" }
dynamo-kv-router = { path = "lib/kv-router", version = "1.0.0", features = ["metrics", "runtime-protocols"] } dynamo-kv-router = { path = "lib/kv-router", version = "1.0.0", features = ["metrics", "runtime-protocols"] }
dynamo-protocols = { path = "lib/protocols", version = "1.0.0", features = ["byot"] } dynamo-protocols = { path = "lib/protocols", version = "1.0.0" }
dynamo-parsers = { path = "lib/parsers", version = "1.0.0" } dynamo-parsers = { path = "lib/parsers", version = "1.0.0" }
fastokens = { version = "0.1.0" } fastokens = { version = "0.1.0" }
......
...@@ -75,7 +75,7 @@ class NvCreateImageRequest(BaseModel): ...@@ -75,7 +75,7 @@ class NvCreateImageRequest(BaseModel):
class ImageData(BaseModel): class ImageData(BaseModel):
"""Individual image data in a response. """Individual image data in a response.
Matches the flattened Rust Image enum in lib/async-openai/src/types/image.rs. Matches the flattened Rust Image enum in lib/protocols/src/types/mod.rs.
""" """
url: Optional[str] = None url: Optional[str] = None
......
...@@ -244,14 +244,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -244,14 +244,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]] [[package]]
name = "async-openai-macros" name = "async-openai"
version = "0.1.1" version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81872a8e595e8ceceab71c6ba1f9078e313b452a1e31934e6763ef5d308705e4" checksum = "ec08254d61379df136135d3d1ac04301be7699fd7d9e57655c63ac7d650a6922"
dependencies = [ dependencies = [
"proc-macro2", "bytes",
"quote", "derive_builder",
"syn", "getrandom 0.3.4",
"serde",
"serde_json",
] ]
[[package]] [[package]]
...@@ -460,20 +462,6 @@ dependencies = [ ...@@ -460,20 +462,6 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "backoff"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"futures-core",
"getrandom 0.2.17",
"instant",
"pin-project-lite",
"rand 0.8.5",
"tokio",
]
[[package]] [[package]]
name = "backon" name = "backon"
version = "1.6.0" version = "1.6.0"
...@@ -1704,26 +1692,14 @@ dependencies = [ ...@@ -1704,26 +1692,14 @@ dependencies = [
name = "dynamo-protocols" name = "dynamo-protocols"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"async-openai-macros", "async-openai",
"backoff",
"base64 0.22.1",
"bytes",
"derive_builder", "derive_builder",
"eventsource-stream",
"futures", "futures",
"rand 0.9.2",
"reqwest",
"reqwest-eventsource",
"secrecy",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio",
"tokio-stream",
"tokio-util",
"tracing", "tracing",
"url", "url",
"utoipa",
"uuid", "uuid",
] ]
...@@ -1980,17 +1956,6 @@ dependencies = [ ...@@ -1980,17 +1956,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "eventsource-stream"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab"
dependencies = [
"futures-core",
"nom 7.1.3",
"pin-project-lite",
]
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.0" version = "1.74.0"
...@@ -2271,12 +2236,6 @@ version = "0.3.32" ...@@ -2271,12 +2236,6 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.32" version = "0.3.32"
...@@ -2932,15 +2891,6 @@ dependencies = [ ...@@ -2932,15 +2891,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "interpolate_name" name = "interpolate_name"
version = "0.2.4" version = "0.2.4"
...@@ -5533,22 +5483,6 @@ dependencies = [ ...@@ -5533,22 +5483,6 @@ dependencies = [
"webpki-roots 1.0.6", "webpki-roots 1.0.6",
] ]
[[package]]
name = "reqwest-eventsource"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde"
dependencies = [
"eventsource-stream",
"futures-core",
"futures-timer",
"mime",
"nom 7.1.3",
"pin-project-lite",
"reqwest",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.53" version = "0.8.53"
...@@ -5911,7 +5845,6 @@ version = "0.10.3" ...@@ -5911,7 +5845,6 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
dependencies = [ dependencies = [
"serde",
"zeroize", "zeroize",
] ]
...@@ -7360,8 +7293,6 @@ dependencies = [ ...@@ -7360,8 +7293,6 @@ dependencies = [
"quote", "quote",
"regex", "regex",
"syn", "syn",
"url",
"uuid",
] ]
[[package]] [[package]]
......
...@@ -131,9 +131,9 @@ dependencies = [ ...@@ -131,9 +131,9 @@ dependencies = [
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.9.0" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
...@@ -244,14 +244,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -244,14 +244,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]] [[package]]
name = "async-openai-macros" name = "async-openai"
version = "0.1.1" version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81872a8e595e8ceceab71c6ba1f9078e313b452a1e31934e6763ef5d308705e4" checksum = "ec08254d61379df136135d3d1ac04301be7699fd7d9e57655c63ac7d650a6922"
dependencies = [ dependencies = [
"proc-macro2", "bytes",
"quote", "derive_builder",
"syn", "getrandom 0.3.4",
"serde",
"serde_json",
] ]
[[package]] [[package]]
...@@ -460,20 +462,6 @@ dependencies = [ ...@@ -460,20 +462,6 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "backoff"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"futures-core",
"getrandom 0.2.17",
"instant",
"pin-project-lite",
"rand 0.8.5",
"tokio",
]
[[package]] [[package]]
name = "backon" name = "backon"
version = "1.6.0" version = "1.6.0"
...@@ -1719,26 +1707,14 @@ dependencies = [ ...@@ -1719,26 +1707,14 @@ dependencies = [
name = "dynamo-protocols" name = "dynamo-protocols"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"async-openai-macros", "async-openai",
"backoff",
"base64 0.22.1",
"bytes",
"derive_builder", "derive_builder",
"eventsource-stream",
"futures", "futures",
"rand 0.9.2",
"reqwest",
"reqwest-eventsource",
"secrecy",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio",
"tokio-stream",
"tokio-util",
"tracing", "tracing",
"url", "url",
"utoipa",
"uuid", "uuid",
] ]
...@@ -2027,17 +2003,6 @@ dependencies = [ ...@@ -2027,17 +2003,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "eventsource-stream"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab"
dependencies = [
"futures-core",
"nom 7.1.3",
"pin-project-lite",
]
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.0" version = "1.74.0"
...@@ -2096,11 +2061,11 @@ dependencies = [ ...@@ -2096,11 +2061,11 @@ dependencies = [
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f"
dependencies = [ dependencies = [
"getrandom 0.2.17", "getrandom 0.3.4",
] ]
[[package]] [[package]]
...@@ -2343,12 +2308,6 @@ version = "0.3.32" ...@@ -2343,12 +2308,6 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.32" version = "0.3.32"
...@@ -3004,15 +2963,6 @@ dependencies = [ ...@@ -3004,15 +2963,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "interpolate_name" name = "interpolate_name"
version = "0.2.4" version = "0.2.4"
...@@ -5603,22 +5553,6 @@ dependencies = [ ...@@ -5603,22 +5553,6 @@ dependencies = [
"webpki-roots 1.0.6", "webpki-roots 1.0.6",
] ]
[[package]]
name = "reqwest-eventsource"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde"
dependencies = [
"eventsource-stream",
"futures-core",
"futures-timer",
"mime",
"nom 7.1.3",
"pin-project-lite",
"reqwest",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.53" version = "0.8.53"
...@@ -5981,7 +5915,6 @@ version = "0.10.3" ...@@ -5981,7 +5915,6 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
dependencies = [ dependencies = [
"serde",
"zeroize", "zeroize",
] ]
...@@ -7430,8 +7363,6 @@ dependencies = [ ...@@ -7430,8 +7363,6 @@ dependencies = [
"quote", "quote",
"regex", "regex",
"syn", "syn",
"url",
"uuid",
] ]
[[package]] [[package]]
......
...@@ -182,7 +182,7 @@ pub fn final_response_to_one_chunk_stream( ...@@ -182,7 +182,7 @@ pub fn final_response_to_one_chunk_stream(
// Convert FunctionCall to FunctionCallStream if present // Convert FunctionCall to FunctionCallStream if present
#[allow(deprecated)] #[allow(deprecated)]
let function_call = ch.message.function_call.as_ref().map(|fc| { let function_call = ch.message.function_call.as_ref().map(|fc| {
dynamo_protocols::types::FunctionCallStream { dynamo_protocols::types::ChatCompletionStreamResponseDeltaFunctionCall {
name: Some(fc.name.clone()), name: Some(fc.name.clone()),
arguments: Some(fc.arguments.clone()), arguments: Some(fc.arguments.clone()),
} }
...@@ -197,7 +197,7 @@ pub fn final_response_to_one_chunk_stream( ...@@ -197,7 +197,7 @@ pub fn final_response_to_one_chunk_stream(
|(i, call)| dynamo_protocols::types::ChatCompletionMessageToolCallChunk { |(i, call)| dynamo_protocols::types::ChatCompletionMessageToolCallChunk {
index: i as u32, index: i as u32,
id: Some(call.id.clone()), id: Some(call.id.clone()),
r#type: Some(call.r#type.clone()), r#type: Some(dynamo_protocols::types::FunctionType::Function),
function: Some(dynamo_protocols::types::FunctionCallStream { function: Some(dynamo_protocols::types::FunctionCallStream {
name: Some(call.function.name.clone()), name: Some(call.function.name.clone()),
arguments: Some(call.function.arguments.clone()), arguments: Some(call.function.arguments.clone()),
......
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod client;
pub mod service; pub mod service;
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! HTTP clients for streaming LLM responses with performance recording
//!
//! This module provides HTTP clients that leverage async-openai with BYOT (Bring Your Own Types)
//! feature to work with OpenAI-compatible APIs. The clients support recording streaming responses
//! for performance analysis.
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use std::time::Instant;
use async_trait::async_trait;
use derive_getters::Dissolve;
use dynamo_protocols::{Client, config::OpenAIConfig, error::OpenAIError};
use futures::Stream;
use serde_json::Value;
use tokio_util::sync::CancellationToken;
use tracing;
use uuid::Uuid;
// Import our existing recording infrastructure
use crate::protocols::Annotated;
use crate::protocols::openai::chat_completions::{
NvCreateChatCompletionRequest, NvCreateChatCompletionStreamResponse,
};
use dynamo_runtime::engine::{
AsyncEngineContext, AsyncEngineContextProvider, AsyncEngineStream, Data, DataStream,
};
/// Configuration for HTTP clients
#[derive(Clone, Default)]
pub struct HttpClientConfig {
/// OpenAI API configuration
pub openai_config: OpenAIConfig,
/// Whether to enable detailed logging
pub verbose: bool,
}
/// Error types for HTTP clients
#[derive(Debug, thiserror::Error)]
pub enum HttpClientError {
#[error("OpenAI API error: {0}")]
OpenAI(#[from] OpenAIError),
#[error("Request timeout")]
Timeout,
#[error("Request cancelled")]
Cancelled,
#[error("Invalid request: {0}")]
InvalidRequest(String),
}
/// Context for HTTP client requests that supports cancellation
/// This bridges AsyncEngineContext and reqwest cancellation
#[derive(Clone)]
pub struct HttpRequestContext {
/// Unique request identifier
id: String,
/// Tokio cancellation token for reqwest integration
cancel_token: CancellationToken,
/// When this context was created
created_at: Instant,
/// Whether the request has been stopped
stopped: Arc<std::sync::atomic::AtomicBool>,
/// Child contexts to be stopped if this is stopped
child_context: Arc<Mutex<Vec<Arc<dyn AsyncEngineContext>>>>,
}
impl HttpRequestContext {
/// Create a new HTTP request context
pub fn new() -> Self {
Self {
id: Uuid::new_v4().to_string(),
cancel_token: CancellationToken::new(),
created_at: Instant::now(),
stopped: Arc::new(std::sync::atomic::AtomicBool::new(false)),
child_context: Arc::new(Mutex::new(Vec::new())),
}
}
/// Create a new context with a specific ID
pub fn with_id(id: String) -> Self {
Self {
id,
cancel_token: CancellationToken::new(),
created_at: Instant::now(),
stopped: Arc::new(std::sync::atomic::AtomicBool::new(false)),
child_context: Arc::new(Mutex::new(Vec::new())),
}
}
/// Create a child context from this parent context
/// The child will be cancelled when the parent is cancelled
pub fn child(&self) -> Self {
Self {
id: Uuid::new_v4().to_string(),
cancel_token: self.cancel_token.child_token(),
created_at: Instant::now(),
stopped: Arc::new(std::sync::atomic::AtomicBool::new(false)),
child_context: Arc::new(Mutex::new(Vec::new())),
}
}
/// Create a child context with a specific ID
pub fn child_with_id(&self, id: String) -> Self {
Self {
id,
cancel_token: self.cancel_token.child_token(),
created_at: Instant::now(),
stopped: Arc::new(std::sync::atomic::AtomicBool::new(false)),
child_context: Arc::new(Mutex::new(Vec::new())),
}
}
/// Get the cancellation token for use with reqwest
pub fn cancellation_token(&self) -> CancellationToken {
self.cancel_token.clone()
}
/// Get the elapsed time since context creation
pub fn elapsed(&self) -> std::time::Duration {
self.created_at.elapsed()
}
}
impl Default for HttpRequestContext {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for HttpRequestContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpRequestContext")
.field("id", &self.id)
.field("created_at", &self.created_at)
.field("is_stopped", &self.is_stopped())
.field("is_killed", &self.is_killed())
.field("is_cancelled", &self.cancel_token.is_cancelled())
.finish()
}
}
#[async_trait]
impl AsyncEngineContext for HttpRequestContext {
fn id(&self) -> &str {
&self.id
}
fn stop(&self) {
// Clone child Arcs to avoid deadlock if parent is accidentally linked under child
let children = self
.child_context
.lock()
.expect("Failed to lock child context")
.iter()
.cloned()
.collect::<Vec<_>>();
for child in children {
child.stop();
}
self.stopped
.store(true, std::sync::atomic::Ordering::Release);
self.cancel_token.cancel();
}
fn stop_generating(&self) {
// Clone child Arcs to avoid deadlock if parent is accidentally linked under child
let children = self
.child_context
.lock()
.expect("Failed to lock child context")
.iter()
.cloned()
.collect::<Vec<_>>();
for child in children {
child.stop_generating();
}
// For HTTP clients, stop_generating is the same as stop
self.stopped
.store(true, std::sync::atomic::Ordering::Release);
self.cancel_token.cancel();
}
fn kill(&self) {
// Clone child Arcs to avoid deadlock if parent is accidentally linked under child
let children = self
.child_context
.lock()
.expect("Failed to lock child context")
.iter()
.cloned()
.collect::<Vec<_>>();
for child in children {
child.kill();
}
self.stopped
.store(true, std::sync::atomic::Ordering::Release);
self.cancel_token.cancel();
}
fn is_stopped(&self) -> bool {
self.stopped.load(std::sync::atomic::Ordering::Acquire)
}
fn is_killed(&self) -> bool {
self.stopped.load(std::sync::atomic::Ordering::Acquire)
}
async fn stopped(&self) {
self.cancel_token.cancelled().await;
}
async fn killed(&self) {
// For HTTP clients, killed is the same as stopped
self.cancel_token.cancelled().await;
}
fn link_child(&self, child: Arc<dyn AsyncEngineContext>) {
self.child_context
.lock()
.expect("Failed to lock child context")
.push(child);
}
}
/// Base HTTP client with common functionality
pub struct BaseHttpClient {
/// async-openai client
client: Client<OpenAIConfig>,
/// Client configuration
config: HttpClientConfig,
/// Root context for this client
root_context: HttpRequestContext,
}
impl BaseHttpClient {
/// Create a new base HTTP client
pub fn new(config: HttpClientConfig) -> Self {
let client = Client::with_config(config.openai_config.clone());
Self {
client,
config,
root_context: HttpRequestContext::new(),
}
}
/// Get a reference to the underlying async-openai client
pub fn client(&self) -> &Client<OpenAIConfig> {
&self.client
}
/// Create a new request context as a child of the root context
pub fn create_context(&self) -> HttpRequestContext {
self.root_context.child()
}
/// Create a new request context with a specific ID as a child of the root context
pub fn create_context_with_id(&self, id: String) -> HttpRequestContext {
self.root_context.child_with_id(id)
}
/// Get the root context for this client
pub fn root_context(&self) -> &HttpRequestContext {
&self.root_context
}
/// Check if verbose logging is enabled
pub fn is_verbose(&self) -> bool {
self.config.verbose
}
}
/// Type alias for NV chat response stream
pub type NvChatResponseStream =
DataStream<Result<Annotated<NvCreateChatCompletionStreamResponse>, OpenAIError>>;
/// Type alias for generic BYOT response stream
pub type ByotResponseStream = DataStream<Result<Value, OpenAIError>>;
/// Type alias for pure OpenAI chat response stream
pub type OpenAIChatResponseStream =
DataStream<Result<dynamo_protocols::types::CreateChatCompletionStreamResponse, OpenAIError>>;
/// A wrapped HTTP response stream that combines a stream with its context
/// This provides a unified interface for HTTP client responses
#[derive(Dissolve)]
pub struct HttpResponseStream<T> {
/// The underlying stream of responses
pub stream: DataStream<T>,
/// The context for this request
pub context: Arc<dyn AsyncEngineContext>,
}
impl<T> HttpResponseStream<T> {
/// Create a new HttpResponseStream
pub fn new(stream: DataStream<T>, context: Arc<dyn AsyncEngineContext>) -> Self {
Self { stream, context }
}
}
impl<T: Data> Stream for HttpResponseStream<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.stream).poll_next(cx)
}
}
impl<T: Data> AsyncEngineContextProvider for HttpResponseStream<T> {
fn context(&self) -> Arc<dyn AsyncEngineContext> {
self.context.clone()
}
}
impl<T: Data> HttpResponseStream<T> {
/// Convert this HttpResponseStream to a Pin<Box<dyn AsyncEngineStream<T>>>
/// This requires the stream to be Send + Sync, which may not be true for all streams
pub fn into_async_engine_stream(self) -> Pin<Box<dyn AsyncEngineStream<T>>>
where
T: 'static,
{
// This will only work if the underlying stream is actually Send + Sync
// For now, we create a wrapper that assumes this
Box::pin(AsyncEngineStreamWrapper {
stream: self.stream,
context: self.context,
})
}
}
/// A wrapper that implements AsyncEngineStream for streams that are Send + Sync
struct AsyncEngineStreamWrapper<T> {
stream: DataStream<T>,
context: Arc<dyn AsyncEngineContext>,
}
impl<T: Data> Stream for AsyncEngineStreamWrapper<T> {
type Item = T;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut self.stream).poll_next(cx)
}
}
impl<T: Data> AsyncEngineContextProvider for AsyncEngineStreamWrapper<T> {
fn context(&self) -> Arc<dyn AsyncEngineContext> {
self.context.clone()
}
}
impl<T: Data> AsyncEngineStream<T> for AsyncEngineStreamWrapper<T> {}
impl<T> std::fmt::Debug for AsyncEngineStreamWrapper<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncEngineStreamWrapper")
.field("context", &self.context)
.finish()
}
}
impl<T: Data> std::fmt::Debug for HttpResponseStream<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpResponseStream")
.field("context", &self.context)
.finish()
}
}
/// Type alias for HttpResponseStream with NV chat completion responses
pub type NvHttpResponseStream =
HttpResponseStream<Result<Annotated<NvCreateChatCompletionStreamResponse>, OpenAIError>>;
/// Type alias for HttpResponseStream with BYOT responses
pub type ByotHttpResponseStream = HttpResponseStream<Result<Value, OpenAIError>>;
/// Type alias for HttpResponseStream with pure OpenAI responses
pub type OpenAIHttpResponseStream = HttpResponseStream<
Result<dynamo_protocols::types::CreateChatCompletionStreamResponse, OpenAIError>,
>;
/// Pure OpenAI client using standard async-openai types
pub struct PureOpenAIClient {
base: BaseHttpClient,
}
impl PureOpenAIClient {
/// Create a new pure OpenAI client
pub fn new(config: HttpClientConfig) -> Self {
Self {
base: BaseHttpClient::new(config),
}
}
/// Create streaming chat completions using standard OpenAI types
/// Uses a client-managed context
pub async fn chat_stream(
&self,
request: dynamo_protocols::types::CreateChatCompletionRequest,
) -> Result<OpenAIHttpResponseStream, HttpClientError> {
let ctx = self.base.create_context();
self.chat_stream_with_context(request, ctx).await
}
/// Create streaming chat completions with a custom context
pub async fn chat_stream_with_context(
&self,
request: dynamo_protocols::types::CreateChatCompletionRequest,
context: HttpRequestContext,
) -> Result<OpenAIHttpResponseStream, HttpClientError> {
let ctx_arc: Arc<dyn AsyncEngineContext> = Arc::new(context.clone());
if !request.stream.unwrap_or(false) {
return Err(HttpClientError::InvalidRequest(
"chat_stream requires the request to have 'stream': true".to_string(),
));
}
if self.base.is_verbose() {
tracing::info!(
"Starting pure OpenAI chat stream for request {}",
context.id()
);
}
// Create the stream with cancellation support
let stream = self
.base
.client()
.chat()
.create_stream(request)
.await
.map_err(HttpClientError::OpenAI)?;
// TODO: In Phase 3, we'll add cancellation integration with reqwest
// For now, return the stream as-is
Ok(HttpResponseStream::new(stream, ctx_arc))
}
}
/// NV Custom client using NvCreateChatCompletionRequest with Annotated responses
pub struct NvCustomClient {
base: BaseHttpClient,
}
impl NvCustomClient {
/// Create a new NV custom client
pub fn new(config: HttpClientConfig) -> Self {
Self {
base: BaseHttpClient::new(config),
}
}
/// Create streaming chat completions using NV custom types
/// Uses a client-managed context
pub async fn chat_stream(
&self,
request: NvCreateChatCompletionRequest,
) -> Result<NvHttpResponseStream, HttpClientError> {
let ctx = self.base.create_context();
self.chat_stream_with_context(request, ctx).await
}
/// Create streaming chat completions with a custom context
pub async fn chat_stream_with_context(
&self,
request: NvCreateChatCompletionRequest,
context: HttpRequestContext,
) -> Result<NvHttpResponseStream, HttpClientError> {
let ctx_arc: Arc<dyn AsyncEngineContext> = Arc::new(context.clone());
if !request.inner.stream.unwrap_or(false) {
return Err(HttpClientError::InvalidRequest(
"chat_stream requires the request to have 'stream': true".to_string(),
));
}
if self.base.is_verbose() {
tracing::info!(
"Starting NV custom chat stream for request {}",
context.id()
);
}
// Use BYOT feature to send NvCreateChatCompletionRequest
// The stream type is explicitly specified to deserialize directly into Annotated<NvCreateChatCompletionStreamResponse>
let stream = self
.base
.client()
.chat()
.create_stream_byot(request)
.await
.map_err(HttpClientError::OpenAI)?;
Ok(HttpResponseStream::new(stream, ctx_arc))
}
}
/// Generic BYOT client using serde_json::Value for maximum flexibility
pub struct GenericBYOTClient {
base: BaseHttpClient,
}
impl GenericBYOTClient {
/// Create a new generic BYOT client
pub fn new(config: HttpClientConfig) -> Self {
Self {
base: BaseHttpClient::new(config),
}
}
/// Create streaming chat completions using arbitrary JSON values
/// Uses a client-managed context
pub async fn chat_stream(
&self,
request: Value,
) -> Result<ByotHttpResponseStream, HttpClientError> {
let ctx = self.base.create_context();
self.chat_stream_with_context(request, ctx).await
}
/// Create streaming chat completions with a custom context
pub async fn chat_stream_with_context(
&self,
request: Value,
context: HttpRequestContext,
) -> Result<ByotHttpResponseStream, HttpClientError> {
let ctx_arc: Arc<dyn AsyncEngineContext> = Arc::new(context.clone());
if self.base.is_verbose() {
tracing::info!(
"Starting generic BYOT chat stream for request {}",
context.id()
);
}
// Validate that the request has stream: true
if let Some(stream_val) = request.get("stream") {
if !stream_val.as_bool().unwrap_or(false) {
return Err(HttpClientError::InvalidRequest(
"Request must have 'stream': true for streaming".to_string(),
));
}
} else {
return Err(HttpClientError::InvalidRequest(
"Request must include 'stream' field".to_string(),
));
}
// Use BYOT feature with raw JSON
// The stream type is explicitly specified to deserialize directly into serde_json::Value
let stream = self
.base
.client()
.chat()
.create_stream_byot(request)
.await
.map_err(HttpClientError::OpenAI)?;
Ok(HttpResponseStream::new(stream, ctx_arc))
}
}
// TODO: Implement recording integration in Phase 3:
// - Recording wrapper functions
// - Capacity hints from request parameters
// - Integration with existing recording infrastructure
#[cfg(test)]
mod tests {
use super::*;
use tokio::time::{Duration, sleep};
#[tokio::test]
async fn test_http_request_context_creation() {
let ctx = HttpRequestContext::new();
assert!(!ctx.id().is_empty());
assert!(!ctx.is_stopped());
assert!(!ctx.is_killed());
}
#[tokio::test]
async fn test_http_request_context_child() {
let parent = HttpRequestContext::new();
let child = parent.child();
// Child should have different ID
assert_ne!(parent.id(), child.id());
// Child should not be stopped initially
assert!(!child.is_stopped());
// When parent is stopped, child should be cancelled via token
parent.stop();
assert!(parent.is_stopped());
assert!(child.cancellation_token().is_cancelled());
}
#[tokio::test]
async fn test_http_request_context_child_with_id() {
let parent = HttpRequestContext::new();
let child_id = "test-child";
let child = parent.child_with_id(child_id.to_string());
assert_eq!(child.id(), child_id);
assert!(!child.is_stopped());
// Test hierarchical cancellation
parent.stop();
assert!(child.cancellation_token().is_cancelled());
}
#[tokio::test]
async fn test_http_request_context_cancellation() {
let ctx = HttpRequestContext::new();
let cancel_token = ctx.cancellation_token();
// Test stop functionality
assert!(!ctx.is_stopped());
ctx.stop();
assert!(ctx.is_stopped());
assert!(cancel_token.is_cancelled());
}
#[tokio::test]
async fn test_http_request_context_kill() {
let ctx = HttpRequestContext::new();
// Test kill functionality
assert!(!ctx.is_killed());
ctx.kill();
assert!(ctx.is_killed());
assert!(ctx.is_stopped());
}
#[tokio::test]
async fn test_http_request_context_async_cancellation() {
let ctx = HttpRequestContext::new();
// Test async cancellation
let ctx_clone = ctx.clone();
let task = tokio::spawn(async move {
ctx_clone.stopped().await;
});
// Give a moment for the task to start waiting
sleep(Duration::from_millis(10)).await;
// Cancel the context
ctx.stop();
// The task should complete
task.await.unwrap();
}
#[test]
fn test_base_http_client_creation() {
let config = HttpClientConfig::default();
let client = BaseHttpClient::new(config);
assert!(!client.is_verbose());
// Test that client has a root context
assert!(!client.root_context().id().is_empty());
}
#[test]
fn test_base_http_client_context_creation() {
let config = HttpClientConfig::default();
let client = BaseHttpClient::new(config);
// Test creating child contexts
let ctx1 = client.create_context();
let ctx2 = client.create_context();
// Should have different IDs
assert_ne!(ctx1.id(), ctx2.id());
// Should be children of root context
client.root_context().stop();
assert!(ctx1.cancellation_token().is_cancelled());
assert!(ctx2.cancellation_token().is_cancelled());
}
#[test]
fn test_base_http_client_context_with_id() {
let config = HttpClientConfig::default();
let client = BaseHttpClient::new(config);
let custom_id = "custom-request-id";
let ctx = client.create_context_with_id(custom_id.to_string());
assert_eq!(ctx.id(), custom_id);
// Should still be child of root
client.root_context().stop();
assert!(ctx.cancellation_token().is_cancelled());
}
#[test]
fn test_http_client_config_defaults() {
let config = HttpClientConfig::default();
assert!(!config.verbose);
}
#[test]
fn test_pure_openai_client_creation() {
let config = HttpClientConfig::default();
let _client = PureOpenAIClient::new(config);
// If we get here, creation succeeded
}
#[test]
fn test_nv_custom_client_creation() {
let config = HttpClientConfig::default();
let _client = NvCustomClient::new(config);
// If we get here, creation succeeded
}
#[test]
fn test_generic_byot_client_creation() {
let config = HttpClientConfig::default();
let _client = GenericBYOTClient::new(config);
// If we get here, creation succeeded
}
}
...@@ -1544,6 +1544,7 @@ async fn responses( ...@@ -1544,6 +1544,7 @@ async fn responses(
temperature: request.inner.temperature, temperature: request.inner.temperature,
top_p: request.inner.top_p, top_p: request.inner.top_p,
max_output_tokens: request.inner.max_output_tokens, max_output_tokens: request.inner.max_output_tokens,
parallel_tool_calls: request.inner.parallel_tool_calls,
store: request.inner.store, store: request.inner.store,
tools: request.inner.tools.clone(), tools: request.inner.tools.clone(),
tool_choice: request.inner.tool_choice.clone(), tool_choice: request.inner.tool_choice.clone(),
...@@ -1788,11 +1789,6 @@ pub fn validate_response_unsupported_fields( ...@@ -1788,11 +1789,6 @@ pub fn validate_response_unsupported_fields(
VALIDATION_PREFIX.to_string() + "`prompt` is not supported.", VALIDATION_PREFIX.to_string() + "`prompt` is not supported.",
)); ));
} }
if inner.store == Some(true) {
return Some(ErrorMessage::not_implemented_error(
VALIDATION_PREFIX.to_string() + "`store: true` is not supported.",
));
}
None None
} }
...@@ -1965,6 +1961,9 @@ async fn images( ...@@ -1965,6 +1961,9 @@ async fn images(
.map(|m| match m { .map(|m| match m {
dynamo_protocols::types::ImageModel::DallE2 => "dall-e-2".to_string(), dynamo_protocols::types::ImageModel::DallE2 => "dall-e-2".to_string(),
dynamo_protocols::types::ImageModel::DallE3 => "dall-e-3".to_string(), dynamo_protocols::types::ImageModel::DallE3 => "dall-e-3".to_string(),
dynamo_protocols::types::ImageModel::GptImage1 => "gpt-image-1".to_string(),
dynamo_protocols::types::ImageModel::GptImage1dot5 => "gpt-image-1.5".to_string(),
dynamo_protocols::types::ImageModel::GptImage1Mini => "gpt-image-1-mini".to_string(),
dynamo_protocols::types::ImageModel::Other(s) => s.clone(), dynamo_protocols::types::ImageModel::Other(s) => s.clone(),
}) })
.unwrap_or_else(|| "diffusion".to_string()); .unwrap_or_else(|| "diffusion".to_string());
...@@ -2540,6 +2539,17 @@ mod tests { ...@@ -2540,6 +2539,17 @@ mod tests {
assert!(result.is_none(), "parallel_tool_calls should be supported"); assert!(result.is_none(), "parallel_tool_calls should be supported");
} }
#[test]
fn test_validate_unsupported_fields_accepts_store() {
let mut request = make_base_request();
request.inner.store = Some(true);
let result = validate_response_unsupported_fields(&request);
assert!(
result.is_none(),
"store should be supported for audit opt-in"
);
}
#[test] #[test]
fn test_validate_unsupported_fields_detects_flags() { fn test_validate_unsupported_fields_detects_flags() {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
...@@ -2559,7 +2569,6 @@ mod tests { ...@@ -2559,7 +2569,6 @@ mod tests {
}) })
}), }),
), ),
("store", Box::new(|r| r.store = Some(true))),
]; ];
for (field, set_field) in unsupported_cases { for (field, set_field) in unsupported_cases {
...@@ -3290,8 +3299,7 @@ mod tests { ...@@ -3290,8 +3299,7 @@ mod tests {
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatChoiceStream, ChatCompletionMessageToolCallChunk, ChatCompletionStreamResponseDelta, ChatChoiceStream, ChatCompletionMessageToolCallChunk, ChatCompletionStreamResponseDelta,
ChatCompletionToolType, CreateChatCompletionStreamResponse, FinishReason, CreateChatCompletionStreamResponse, FinishReason, FunctionCallStream, FunctionType,
FunctionCallStream,
}; };
use dynamo_runtime::protocols::annotated::Annotated; use dynamo_runtime::protocols::annotated::Annotated;
...@@ -3444,7 +3452,7 @@ mod tests { ...@@ -3444,7 +3452,7 @@ mod tests {
let tool_call = ChatCompletionMessageToolCallChunk { let tool_call = ChatCompletionMessageToolCallChunk {
index: 0, index: 0,
id: id.map(|s| s.to_string()), id: id.map(|s| s.to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: name.map(|s| s.to_string()), name: name.map(|s| s.to_string()),
arguments: arguments.map(|s| s.to_string()), arguments: arguments.map(|s| s.to_string()),
...@@ -3537,7 +3545,7 @@ mod tests { ...@@ -3537,7 +3545,7 @@ mod tests {
let tc1 = ChatCompletionMessageToolCallChunk { let tc1 = ChatCompletionMessageToolCallChunk {
index: 0, index: 0,
id: Some("call_1".to_string()), id: Some("call_1".to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some("get_weather".to_string()), name: Some("get_weather".to_string()),
arguments: Some(r#"{"city":"Paris"}"#.to_string()), arguments: Some(r#"{"city":"Paris"}"#.to_string()),
...@@ -3546,7 +3554,7 @@ mod tests { ...@@ -3546,7 +3554,7 @@ mod tests {
let tc2 = ChatCompletionMessageToolCallChunk { let tc2 = ChatCompletionMessageToolCallChunk {
index: 1, index: 1,
id: Some("call_2".to_string()), id: Some("call_2".to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some("get_time".to_string()), name: Some("get_time".to_string()),
arguments: Some(r#"{"tz":"UTC"}"#.to_string()), arguments: Some(r#"{"tz":"UTC"}"#.to_string()),
...@@ -3609,7 +3617,7 @@ mod tests { ...@@ -3609,7 +3617,7 @@ mod tests {
let complete = ChatCompletionMessageToolCallChunk { let complete = ChatCompletionMessageToolCallChunk {
index: 0, index: 0,
id: Some("call_complete".to_string()), id: Some("call_complete".to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some("get_weather".to_string()), name: Some("get_weather".to_string()),
arguments: Some(r#"{"city":"Paris"}"#.to_string()), arguments: Some(r#"{"city":"Paris"}"#.to_string()),
...@@ -3618,7 +3626,7 @@ mod tests { ...@@ -3618,7 +3626,7 @@ mod tests {
let incomplete = ChatCompletionMessageToolCallChunk { let incomplete = ChatCompletionMessageToolCallChunk {
index: 1, index: 1,
id: Some("call_partial".to_string()), id: Some("call_partial".to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some("search".to_string()), name: Some("search".to_string()),
arguments: None, // still streaming arguments: None, // still streaming
...@@ -3658,7 +3666,7 @@ mod tests { ...@@ -3658,7 +3666,7 @@ mod tests {
let tool_call = ChatCompletionMessageToolCallChunk { let tool_call = ChatCompletionMessageToolCallChunk {
index: 0, index: 0,
id: Some("call_999".to_string()), id: Some("call_999".to_string()),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: None, function: None,
}; };
#[allow(deprecated)] #[allow(deprecated)]
......
...@@ -947,7 +947,6 @@ mod tests { ...@@ -947,7 +947,6 @@ mod tests {
fn create_mock_response_with_logprobs( fn create_mock_response_with_logprobs(
token_logprobs: Vec<ChatCompletionTokenLogprob>, token_logprobs: Vec<ChatCompletionTokenLogprob>,
) -> NvCreateChatCompletionStreamResponse { ) -> NvCreateChatCompletionStreamResponse {
#[expect(deprecated)]
NvCreateChatCompletionStreamResponse { NvCreateChatCompletionStreamResponse {
inner: dynamo_protocols::types::CreateChatCompletionStreamResponse { inner: dynamo_protocols::types::CreateChatCompletionStreamResponse {
id: "test_id".to_string(), id: "test_id".to_string(),
...@@ -984,7 +983,6 @@ mod tests { ...@@ -984,7 +983,6 @@ mod tests {
fn create_mock_response_with_multiple_choices( fn create_mock_response_with_multiple_choices(
choices_logprobs: Vec<Vec<ChatCompletionTokenLogprob>>, choices_logprobs: Vec<Vec<ChatCompletionTokenLogprob>>,
) -> NvCreateChatCompletionStreamResponse { ) -> NvCreateChatCompletionStreamResponse {
#[expect(deprecated)]
let choices = choices_logprobs let choices = choices_logprobs
.into_iter() .into_iter()
.enumerate() .enumerate()
...@@ -1339,7 +1337,6 @@ mod tests { ...@@ -1339,7 +1337,6 @@ mod tests {
#[test] #[test]
fn test_logprob_extractor_with_missing_data() { fn test_logprob_extractor_with_missing_data() {
// Test with choice that has no logprobs // Test with choice that has no logprobs
#[expect(deprecated)]
let response = NvCreateChatCompletionStreamResponse { let response = NvCreateChatCompletionStreamResponse {
inner: dynamo_protocols::types::CreateChatCompletionStreamResponse { inner: dynamo_protocols::types::CreateChatCompletionStreamResponse {
id: "test_id".to_string(), id: "test_id".to_string(),
......
...@@ -732,7 +732,7 @@ mod tests { ...@@ -732,7 +732,7 @@ mod tests {
use super::*; use super::*;
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatChoiceStream, ChatCompletionMessageContent, ChatCompletionMessageToolCallChunk, ChatChoiceStream, ChatCompletionMessageContent, ChatCompletionMessageToolCallChunk,
ChatCompletionStreamResponseDelta, ChatCompletionToolType, FunctionCallStream, ChatCompletionStreamResponseDelta, FunctionCallStream, FunctionType,
}; };
fn text_chunk(text: &str) -> NvCreateChatCompletionStreamResponse { fn text_chunk(text: &str) -> NvCreateChatCompletionStreamResponse {
...@@ -783,7 +783,7 @@ mod tests { ...@@ -783,7 +783,7 @@ mod tests {
tool_calls: Some(vec![ChatCompletionMessageToolCallChunk { tool_calls: Some(vec![ChatCompletionMessageToolCallChunk {
index: tc_index, index: tc_index,
id: id.map(String::from), id: id.map(String::from),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: name.map(String::from), name: name.map(String::from),
arguments: args.map(String::from), arguments: args.map(String::from),
......
...@@ -20,7 +20,7 @@ use dynamo_protocols::types::{ ...@@ -20,7 +20,7 @@ use dynamo_protocols::types::{
ChatCompletionRequestToolMessageContent, ChatCompletionRequestUserMessage, ChatCompletionRequestToolMessageContent, ChatCompletionRequestUserMessage,
ChatCompletionRequestUserMessageContent, ChatCompletionRequestUserMessageContentPart, ChatCompletionRequestUserMessageContent, ChatCompletionRequestUserMessageContentPart,
ChatCompletionTool, ChatCompletionToolChoiceOption, ChatCompletionToolType, FunctionName, ChatCompletionTool, ChatCompletionToolChoiceOption, ChatCompletionToolType, FunctionName,
FunctionObject, ImageUrl, ReasoningContent, FunctionObject, FunctionType, ImageUrl, ReasoningContent,
}; };
use uuid::Uuid; use uuid::Uuid;
...@@ -312,7 +312,7 @@ fn convert_assistant_blocks( ...@@ -312,7 +312,7 @@ fn convert_assistant_blocks(
segments.push(std::mem::take(&mut pending_reasoning)); segments.push(std::mem::take(&mut pending_reasoning));
tool_calls.push(ChatCompletionMessageToolCall { tool_calls.push(ChatCompletionMessageToolCall {
id: id.clone(), id: id.clone(),
r#type: ChatCompletionToolType::Function, r#type: FunctionType::Function,
function: dynamo_protocols::types::FunctionCall { function: dynamo_protocols::types::FunctionCall {
name: name.clone(), name: name.clone(),
arguments: serde_json::to_string(input).unwrap_or_default(), arguments: serde_json::to_string(input).unwrap_or_default(),
......
...@@ -35,6 +35,7 @@ pub use delta::DeltaGenerator; ...@@ -35,6 +35,7 @@ pub use delta::DeltaGenerator;
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvCreateChatCompletionRequest { pub struct NvCreateChatCompletionRequest {
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::CreateChatCompletionRequest, pub inner: dynamo_protocols::types::CreateChatCompletionRequest,
#[serde(flatten, default)] #[serde(flatten, default)]
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
use futures::{Stream, StreamExt}; use futures::{Stream, StreamExt};
use std::collections::HashMap; use std::collections::HashMap;
use dynamo_parsers::tool_calling::try_tool_call_parse_aggregate;
use super::{NvCreateChatCompletionResponse, NvCreateChatCompletionStreamResponse}; use super::{NvCreateChatCompletionResponse, NvCreateChatCompletionStreamResponse};
use crate::protocols::{ use crate::protocols::{
Annotated, Annotated,
...@@ -75,11 +77,11 @@ fn convert_tool_chunk_to_message_tool_call( ...@@ -75,11 +77,11 @@ fn convert_tool_chunk_to_message_tool_call(
chunk: &dynamo_protocols::types::ChatCompletionMessageToolCallChunk, chunk: &dynamo_protocols::types::ChatCompletionMessageToolCallChunk,
) -> Option<dynamo_protocols::types::ChatCompletionMessageToolCall> { ) -> Option<dynamo_protocols::types::ChatCompletionMessageToolCall> {
// Convert ChatCompletionMessageToolCallChunk to ChatCompletionMessageToolCall // Convert ChatCompletionMessageToolCallChunk to ChatCompletionMessageToolCall
if let (Some(id), Some(r#type), Some(function)) = (&chunk.id, &chunk.r#type, &chunk.function) { if let (Some(id), Some(function)) = (&chunk.id, &chunk.function) {
if let (Some(name), Some(arguments)) = (&function.name, &function.arguments) { if let (Some(name), Some(arguments)) = (&function.name, &function.arguments) {
Some(dynamo_protocols::types::ChatCompletionMessageToolCall { Some(dynamo_protocols::types::ChatCompletionMessageToolCall {
id: id.clone(), id: id.clone(),
r#type: r#type.clone(), r#type: dynamo_protocols::types::FunctionType::Function,
function: dynamo_protocols::types::FunctionCall { function: dynamo_protocols::types::FunctionCall {
name: name.clone(), name: name.clone(),
arguments: arguments.clone(), arguments: arguments.clone(),
...@@ -120,9 +122,9 @@ impl DeltaAggregator { ...@@ -120,9 +122,9 @@ impl DeltaAggregator {
/// * `Err(String)` if an error occurs during processing. /// * `Err(String)` if an error occurs during processing.
pub async fn apply( pub async fn apply(
stream: impl Stream<Item = Annotated<NvCreateChatCompletionStreamResponse>>, stream: impl Stream<Item = Annotated<NvCreateChatCompletionStreamResponse>>,
_parsing_options: ParsingOptions, parsing_options: ParsingOptions,
) -> Result<NvCreateChatCompletionResponse, String> { ) -> Result<NvCreateChatCompletionResponse, String> {
let aggregator = stream let mut aggregator = stream
.fold(DeltaAggregator::new(), |mut aggregator, delta| async move { .fold(DeltaAggregator::new(), |mut aggregator, delta| async move {
// Attempt to unwrap the delta, capturing any errors. // Attempt to unwrap the delta, capturing any errors.
let delta = match delta.ok() { let delta = match delta.ok() {
...@@ -256,6 +258,37 @@ impl DeltaAggregator { ...@@ -256,6 +258,37 @@ impl DeltaAggregator {
return Err(error); return Err(error);
} }
if let Some(parser) = parsing_options.tool_call_parser.as_deref() {
for choice in aggregator.choices.values_mut() {
if choice
.tool_calls
.as_ref()
.is_some_and(|calls| !calls.is_empty())
|| choice.text.is_empty()
{
continue;
}
let (tool_calls, content) =
match try_tool_call_parse_aggregate(&choice.text, Some(parser), None).await {
Ok(result) => result,
Err(error) => {
tracing::debug!(
error = %error,
parser,
"failed to parse aggregated chat tool calls"
);
continue;
}
};
if !tool_calls.is_empty() {
choice.tool_calls = Some(tool_calls);
choice.text = content.unwrap_or_default();
}
}
}
// Extract aggregated choices and sort them by index. // Extract aggregated choices and sort them by index.
let mut choices: Vec<_> = aggregator let mut choices: Vec<_> = aggregator
.choices .choices
...@@ -405,7 +438,7 @@ mod tests { ...@@ -405,7 +438,7 @@ mod tests {
dynamo_protocols::types::ChatCompletionMessageToolCallChunk { dynamo_protocols::types::ChatCompletionMessageToolCallChunk {
index: 0, index: 0,
id: Some("test_id".to_string()), id: Some("test_id".to_string()),
r#type: Some(dynamo_protocols::types::ChatCompletionToolType::Function), r#type: Some(dynamo_protocols::types::FunctionType::Function),
function: Some(dynamo_protocols::types::FunctionCallStream { function: Some(dynamo_protocols::types::FunctionCallStream {
name: tool_calls["name"].as_str().map(|s| s.to_string()), name: tool_calls["name"].as_str().map(|s| s.to_string()),
arguments: Some(serde_json::to_string(&tool_calls["arguments"]).unwrap()), arguments: Some(serde_json::to_string(&tool_calls["arguments"]).unwrap()),
...@@ -788,6 +821,10 @@ mod tests { ...@@ -788,6 +821,10 @@ mod tests {
assert!(choice.message.tool_calls.is_some()); assert!(choice.message.tool_calls.is_some());
let tool_calls = choice.message.tool_calls.as_ref().unwrap(); let tool_calls = choice.message.tool_calls.as_ref().unwrap();
assert_eq!(tool_calls.len(), 1); assert_eq!(tool_calls.len(), 1);
assert_eq!(
tool_calls[0].r#type,
dynamo_protocols::types::FunctionType::Function
);
// Most importantly, verify that finish reason was overridden to ToolCalls despite original being Stop // Most importantly, verify that finish reason was overridden to ToolCalls despite original being Stop
assert_eq!( assert_eq!(
...@@ -831,6 +868,10 @@ mod tests { ...@@ -831,6 +868,10 @@ mod tests {
assert!(choice.message.tool_calls.is_some()); assert!(choice.message.tool_calls.is_some());
let tool_calls = choice.message.tool_calls.as_ref().unwrap(); let tool_calls = choice.message.tool_calls.as_ref().unwrap();
assert_eq!(tool_calls.len(), 1); assert_eq!(tool_calls.len(), 1);
assert_eq!(
tool_calls[0].r#type,
dynamo_protocols::types::FunctionType::Function
);
// Verify that finish reason was overridden to ToolCalls despite original being Length // Verify that finish reason was overridden to ToolCalls despite original being Length
assert_eq!( assert_eq!(
...@@ -1073,4 +1114,75 @@ mod tests { ...@@ -1073,4 +1114,75 @@ mod tests {
assert_eq!(tool_calls.len(), 1); assert_eq!(tool_calls.len(), 1);
assert_eq!(tool_calls[0].function.name, "get_weather"); assert_eq!(tool_calls[0].function.name, "get_weather");
} }
#[tokio::test]
async fn test_parses_aggregated_tool_call_text_into_tool_calls() {
let annotated_delta = create_test_delta(
0,
"<tool_call>\n{\"name\":\"get_weather\",\"arguments\":{\"location\":\"SF\"}}\n</tool_call>",
Some(dynamo_protocols::types::Role::Assistant),
Some(dynamo_protocols::types::FinishReason::Stop),
None,
None,
);
let stream = Box::pin(stream::iter(vec![annotated_delta]));
let result = DeltaAggregator::apply(
stream,
ParsingOptions::new(Some("hermes".to_string()), None),
)
.await;
assert!(result.is_ok());
let response = result.unwrap();
let choice = &response.inner.choices[0];
assert_eq!(
choice.finish_reason,
Some(dynamo_protocols::types::FinishReason::ToolCalls)
);
assert_eq!(choice.message.content, None);
let tool_calls = choice.message.tool_calls.as_ref().unwrap();
assert_eq!(tool_calls.len(), 1);
assert_eq!(
tool_calls[0].r#type,
dynamo_protocols::types::FunctionType::Function
);
assert_eq!(tool_calls[0].function.name, "get_weather");
assert_eq!(tool_calls[0].function.arguments, "{\"location\":\"SF\"}");
}
#[tokio::test]
async fn test_preserves_non_tool_content_when_parsing_aggregated_tool_calls() {
let annotated_delta = create_test_delta(
0,
"hello\n<tool_call>\n{\"name\":\"get_weather\",\"arguments\":{\"location\":\"SF\"}}\n</tool_call>",
Some(dynamo_protocols::types::Role::Assistant),
Some(dynamo_protocols::types::FinishReason::Stop),
None,
None,
);
let stream = Box::pin(stream::iter(vec![annotated_delta]));
let result = DeltaAggregator::apply(
stream,
ParsingOptions::new(Some("hermes".to_string()), None),
)
.await;
assert!(result.is_ok());
let response = result.unwrap();
let choice = &response.inner.choices[0];
assert_eq!(
choice.message.content,
Some(ChatCompletionMessageContent::Text("hello".to_string()))
);
assert_eq!(
choice.finish_reason,
Some(dynamo_protocols::types::FinishReason::ToolCalls)
);
assert_eq!(
choice.message.tool_calls.as_ref().unwrap()[0].r#type,
dynamo_protocols::types::FunctionType::Function
);
}
} }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
use async_stream::stream; use async_stream::stream;
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatChoiceLogprobs, ChatChoiceStream, ChatCompletionMessageToolCallChunk, ChatChoiceLogprobs, ChatChoiceStream, ChatCompletionMessageToolCallChunk,
ChatCompletionStreamResponseDelta, FinishReason, FunctionCallStream, Role, ChatCompletionStreamResponseDelta, FinishReason, FunctionCallStream, FunctionType, Role,
}; };
use dynamo_parsers::tool_calling::parsers::get_tool_parser_map; use dynamo_parsers::tool_calling::parsers::get_tool_parser_map;
...@@ -902,7 +902,7 @@ impl JailedStream { ...@@ -902,7 +902,7 @@ impl JailedStream {
.map(|(idx, tool_call)| ChatCompletionMessageToolCallChunk { .map(|(idx, tool_call)| ChatCompletionMessageToolCallChunk {
index: (tool_call_offset + idx) as u32, index: (tool_call_offset + idx) as u32,
id: Some(tool_call.id), id: Some(tool_call.id),
r#type: Some(tool_call.r#type), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some(tool_call.function.name), name: Some(tool_call.function.name),
arguments: Some(tool_call.function.arguments), arguments: Some(tool_call.function.arguments),
...@@ -971,7 +971,7 @@ impl JailedStream { ...@@ -971,7 +971,7 @@ impl JailedStream {
ChatCompletionMessageToolCallChunk { ChatCompletionMessageToolCallChunk {
index, index,
id: Some(format!("call-{}", Uuid::new_v4())), id: Some(format!("call-{}", Uuid::new_v4())),
r#type: Some(dynamo_protocols::types::ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: Some(name), name: Some(name),
arguments: Some(arguments), arguments: Some(arguments),
......
...@@ -27,6 +27,7 @@ pub use delta::DeltaGenerator; ...@@ -27,6 +27,7 @@ pub use delta::DeltaGenerator;
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvCreateCompletionRequest { pub struct NvCreateCompletionRequest {
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::CreateCompletionRequest, pub inner: dynamo_protocols::types::CreateCompletionRequest,
#[serde(flatten)] #[serde(flatten)]
...@@ -47,6 +48,7 @@ pub struct NvCreateCompletionRequest { ...@@ -47,6 +48,7 @@ pub struct NvCreateCompletionRequest {
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvCreateCompletionResponse { pub struct NvCreateCompletionResponse {
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::CreateCompletionResponse, pub inner: dynamo_protocols::types::CreateCompletionResponse,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub nvext: Option<serde_json::Value>, pub nvext: Option<serde_json::Value>,
......
...@@ -15,6 +15,7 @@ pub use nvext::{NvExt, NvExtProvider}; ...@@ -15,6 +15,7 @@ pub use nvext::{NvExt, NvExtProvider};
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvCreateEmbeddingRequest { pub struct NvCreateEmbeddingRequest {
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::CreateEmbeddingRequest, pub inner: dynamo_protocols::types::CreateEmbeddingRequest,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
...@@ -30,6 +31,7 @@ pub struct NvCreateEmbeddingRequest { ...@@ -30,6 +31,7 @@ pub struct NvCreateEmbeddingRequest {
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvCreateEmbeddingResponse { pub struct NvCreateEmbeddingResponse {
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::CreateEmbeddingResponse, pub inner: dynamo_protocols::types::CreateEmbeddingResponse,
} }
......
...@@ -42,6 +42,11 @@ impl NvImagesResponse { ...@@ -42,6 +42,11 @@ impl NvImagesResponse {
inner: dynamo_protocols::types::ImagesResponse { inner: dynamo_protocols::types::ImagesResponse {
created: 0, created: 0,
data: vec![], data: vec![],
background: None,
output_format: None,
quality: None,
size: None,
usage: None,
}, },
} }
} }
......
...@@ -3,26 +3,27 @@ ...@@ -3,26 +3,27 @@
pub mod stream_converter; pub mod stream_converter;
use std::collections::HashMap;
use dynamo_protocols::types::responses::{ use dynamo_protocols::types::responses::{
AssistantRole, FunctionCallOutput, FunctionToolCall, IncludeEnum, InputContent, InputItem, AssistantRole, FunctionCallOutput, FunctionToolCall, IncludeEnum, InputContent, InputItem,
InputParam, InputRole, InputTokenDetails, Instructions, Item, MessageItem, OutputItem, InputParam, InputRole, InputTokenDetails, Instructions, Item, MessageItem, OutputItem,
OutputMessage, OutputMessageContent, OutputStatus, OutputTextContent, OutputTokenDetails, OutputMessage, OutputMessageContent, OutputStatus, OutputTextContent, OutputTokenDetails,
Reasoning, ReasoningItem, Response, ResponseTextParam, ResponseUsage, Role as ResponseRole, Reasoning, ReasoningItem, Response, ResponseTextParam, ResponseUsage, Role as ResponseRole,
ServiceTier, Status, Summary, SummaryPart, TextResponseFormatConfiguration, Tool, ServiceTier, Status, SummaryPart, SummaryTextContent, TextResponseFormatConfiguration, Tool,
ToolChoiceOptions, ToolChoiceParam, Truncation, ToolChoiceOptions, ToolChoiceParam, Truncation,
}; };
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatCompletionMessageToolCall, ChatCompletionNamedToolChoice, ChatCompletionMessageToolCall, ChatCompletionNamedToolChoice,
ChatCompletionRequestAssistantMessage, ChatCompletionRequestAssistantMessageContent, ChatCompletionRequestAssistantMessage, ChatCompletionRequestAssistantMessageContent,
ChatCompletionRequestMessage, ChatCompletionRequestMessageContentPartImage, ChatCompletionRequestMessage, ChatCompletionRequestMessageContentPartImage,
ChatCompletionRequestMessageContentPartText, ChatCompletionRequestMessageContentPartVideo, ChatCompletionRequestMessageContentPartText, ChatCompletionRequestSystemMessage,
ChatCompletionRequestSystemMessage, ChatCompletionRequestSystemMessageContent, ChatCompletionRequestSystemMessageContent, ChatCompletionRequestToolMessage,
ChatCompletionRequestToolMessage, ChatCompletionRequestToolMessageContent, ChatCompletionRequestToolMessageContent, ChatCompletionRequestUserMessage,
ChatCompletionRequestUserMessage, ChatCompletionRequestUserMessageContent, ChatCompletionRequestUserMessageContent, ChatCompletionRequestUserMessageContentPart,
ChatCompletionRequestUserMessageContentPart, ChatCompletionTool, ChatCompletionTool, ChatCompletionToolChoiceOption, ChatCompletionToolType,
ChatCompletionToolChoiceOption, ChatCompletionToolType, CreateChatCompletionRequest, CreateChatCompletionRequest, FunctionName, FunctionObject, FunctionType,
FunctionName, FunctionObject, ImageDetail as ChatImageDetail, ImageUrl, ResponseFormat, ImageDetail as ChatImageDetail, ImageUrl, ResponseFormat, ServiceTier as ChatServiceTier,
ServiceTier as ChatServiceTier, VideoUrl,
}; };
use dynamo_runtime::protocols::annotated::AnnotationsProvider; use dynamo_runtime::protocols::annotated::AnnotationsProvider;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
...@@ -38,6 +39,7 @@ use super::{OpenAISamplingOptionsProvider, OpenAIStopConditionsProvider}; ...@@ -38,6 +39,7 @@ use super::{OpenAISamplingOptionsProvider, OpenAIStopConditionsProvider};
pub struct NvCreateResponse { pub struct NvCreateResponse {
/// Flattened CreateResponse fields (model, input, temperature, etc.) /// Flattened CreateResponse fields (model, input, temperature, etc.)
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::responses::CreateResponse, pub inner: dynamo_protocols::types::responses::CreateResponse,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
...@@ -46,8 +48,9 @@ pub struct NvCreateResponse { ...@@ -46,8 +48,9 @@ pub struct NvCreateResponse {
#[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)] #[derive(ToSchema, Serialize, Deserialize, Validate, Debug, Clone)]
pub struct NvResponse { pub struct NvResponse {
/// Flattened Response fields. /// Flattened Response fields (includes upstream + extended spec fields).
#[serde(flatten)] #[serde(flatten)]
#[schema(value_type = Object)]
pub inner: dynamo_protocols::types::responses::Response, pub inner: dynamo_protocols::types::responses::Response,
/// NVIDIA extension field for response metadata (worker IDs, etc.) /// NVIDIA extension field for response metadata (worker IDs, etc.)
...@@ -143,13 +146,18 @@ impl OpenAIStopConditionsProvider for NvCreateResponse { ...@@ -143,13 +146,18 @@ impl OpenAIStopConditionsProvider for NvCreateResponse {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// Convert a Responses API ImageDetail to the Chat Completions ImageDetail. /// Convert a Responses API ImageDetail to the Chat Completions ImageDetail.
fn convert_image_detail( /// The responses module re-exports an `ImageDetail` from the upstream async-openai
detail: &dynamo_protocols::types::responses::ImageDetail, /// crate which is distinct from `dynamo_protocols::types::ImageDetail` (chat).
) -> ChatImageDetail { /// We bridge via serde to avoid direct cross-crate type dependencies.
match detail { fn convert_image_detail_str(detail: &impl serde::Serialize) -> ChatImageDetail {
dynamo_protocols::types::responses::ImageDetail::Auto => ChatImageDetail::Auto, match serde_json::to_value(detail)
dynamo_protocols::types::responses::ImageDetail::Low => ChatImageDetail::Low, .ok()
dynamo_protocols::types::responses::ImageDetail::High => ChatImageDetail::High, .and_then(|v| v.as_str().map(String::from))
.as_deref()
{
Some("low") => ChatImageDetail::Low,
Some("high") => ChatImageDetail::High,
_ => ChatImageDetail::Auto,
} }
} }
...@@ -177,52 +185,31 @@ fn convert_input_content_to_user_content( ...@@ -177,52 +185,31 @@ fn convert_input_content_to_user_content(
)); ));
} }
InputContent::InputImage(img) => { InputContent::InputImage(img) => {
let url_str = img.image_url.as_deref().unwrap_or_default(); if img.file_id.is_some() && img.image_url.is_none() {
return Err(anyhow::anyhow!(
"Image input by file_id is not yet supported"
));
}
let url_str = img
.image_url
.as_deref()
.ok_or_else(|| anyhow::anyhow!("input_image requires image_url"))?;
let url = url::Url::parse(url_str) let url = url::Url::parse(url_str)
.map_err(|e| anyhow::anyhow!("Invalid image URL '{}': {}", url_str, e))?; .map_err(|e| anyhow::anyhow!("Invalid image URL '{}': {}", url_str, e))?;
chat_parts.push(ChatCompletionRequestUserMessageContentPart::ImageUrl( chat_parts.push(ChatCompletionRequestUserMessageContentPart::ImageUrl(
ChatCompletionRequestMessageContentPartImage { ChatCompletionRequestMessageContentPartImage {
image_url: ImageUrl { image_url: ImageUrl {
url, url,
detail: Some(convert_image_detail(&img.detail)), detail: Some(convert_image_detail_str(&img.detail)),
uuid: None, uuid: None,
}, },
}, },
)); ));
} }
InputContent::InputVideo(vid) => { // TODO: handle InputVideo / InputAudio when upstream adds them
let url = url::Url::parse(&vid.video)
.map_err(|e| anyhow::anyhow!("Invalid video URL '{}': {}", vid.video, e))?;
chat_parts.push(ChatCompletionRequestUserMessageContentPart::VideoUrl(
ChatCompletionRequestMessageContentPartVideo {
video_url: VideoUrl {
url,
detail: None,
uuid: None,
},
},
));
}
InputContent::InputAudio(_) => {
return Err(anyhow::anyhow!("Audio input content is not yet supported"));
}
InputContent::InputFile(_) => { InputContent::InputFile(_) => {
return Err(anyhow::anyhow!("File input content is not yet supported")); return Err(anyhow::anyhow!("File input content is not yet supported"));
} }
InputContent::OutputText(t) => {
chat_parts.push(ChatCompletionRequestUserMessageContentPart::Text(
ChatCompletionRequestMessageContentPartText {
text: t.text.clone(),
},
));
}
InputContent::Refusal(r) => {
chat_parts.push(ChatCompletionRequestUserMessageContentPart::Text(
ChatCompletionRequestMessageContentPartText {
text: r.refusal.clone(),
},
));
}
} }
} }
Ok(ChatCompletionRequestUserMessageContent::Array(chat_parts)) Ok(ChatCompletionRequestUserMessageContent::Array(chat_parts))
...@@ -234,8 +221,6 @@ fn convert_input_content_to_text(content: &[InputContent]) -> String { ...@@ -234,8 +221,6 @@ fn convert_input_content_to_text(content: &[InputContent]) -> String {
.iter() .iter()
.filter_map(|p| match p { .filter_map(|p| match p {
InputContent::InputText(t) => Some(t.text.as_str()), InputContent::InputText(t) => Some(t.text.as_str()),
InputContent::OutputText(t) => Some(t.text.as_str()),
InputContent::Refusal(r) => Some(r.refusal.as_str()),
_ => None, _ => None,
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
...@@ -315,7 +300,7 @@ fn convert_input_items_to_messages( ...@@ -315,7 +300,7 @@ fn convert_input_items_to_messages(
audio: None, audio: None,
tool_calls: Some(vec![ChatCompletionMessageToolCall { tool_calls: Some(vec![ChatCompletionMessageToolCall {
id: fc.call_id.clone(), id: fc.call_id.clone(),
r#type: ChatCompletionToolType::Function, r#type: FunctionType::Function,
function: dynamo_protocols::types::FunctionCall { function: dynamo_protocols::types::FunctionCall {
name: fc.name.clone(), name: fc.name.clone(),
arguments: fc.arguments.clone(), arguments: fc.arguments.clone(),
...@@ -528,8 +513,13 @@ impl TryFrom<NvCreateResponse> for NvCreateChatCompletionRequest { ...@@ -528,8 +513,13 @@ impl TryFrom<NvCreateResponse> for NvCreateChatCompletionRequest {
temperature: resp.inner.temperature, temperature: resp.inner.temperature,
top_p: resp.inner.top_p, top_p: resp.inner.top_p,
max_completion_tokens: resp.inner.max_output_tokens, max_completion_tokens: resp.inner.max_output_tokens,
store: resp.inner.store,
parallel_tool_calls: resp.inner.parallel_tool_calls,
top_logprobs, top_logprobs,
metadata: resp.inner.metadata, metadata: resp
.inner
.metadata
.map(|m| serde_json::to_value(m).unwrap_or_default()),
stream, stream,
tools, tools,
tool_choice, tool_choice,
...@@ -634,6 +624,7 @@ pub struct ResponseParams { ...@@ -634,6 +624,7 @@ pub struct ResponseParams {
pub temperature: Option<f32>, pub temperature: Option<f32>,
pub top_p: Option<f32>, pub top_p: Option<f32>,
pub max_output_tokens: Option<u32>, pub max_output_tokens: Option<u32>,
pub parallel_tool_calls: Option<bool>,
pub store: Option<bool>, pub store: Option<bool>,
pub tools: Option<Vec<Tool>>, pub tools: Option<Vec<Tool>>,
pub tool_choice: Option<ToolChoiceParam>, pub tool_choice: Option<ToolChoiceParam>,
...@@ -668,9 +659,10 @@ pub(super) fn normalize_tools(tools: Vec<Tool>) -> Vec<Tool> { ...@@ -668,9 +659,10 @@ pub(super) fn normalize_tools(tools: Vec<Tool>) -> Vec<Tool> {
/// Build an assistant text message output item. /// Build an assistant text message output item.
fn make_text_message(id: String, text: String) -> OutputItem { fn make_text_message(id: String, text: String) -> OutputItem {
OutputItem::Message(OutputMessage { OutputItem::Message(OutputMessage {
id: Some(id), id,
role: AssistantRole::Assistant, role: AssistantRole::Assistant,
status: Some(OutputStatus::Completed), status: OutputStatus::Completed,
phase: None,
content: vec![OutputMessageContent::OutputText(OutputTextContent { content: vec![OutputMessageContent::OutputText(OutputTextContent {
text, text,
annotations: vec![], annotations: vec![],
...@@ -684,6 +676,7 @@ fn make_function_call(name: String, arguments: String) -> OutputItem { ...@@ -684,6 +676,7 @@ fn make_function_call(name: String, arguments: String) -> OutputItem {
OutputItem::FunctionCall(FunctionToolCall { OutputItem::FunctionCall(FunctionToolCall {
arguments, arguments,
call_id: format!("call_{}", Uuid::new_v4().simple()), call_id: format!("call_{}", Uuid::new_v4().simple()),
namespace: None,
name, name,
id: Some(format!("fc_{}", Uuid::new_v4().simple())), id: Some(format!("fc_{}", Uuid::new_v4().simple())),
status: Some(OutputStatus::Completed), status: Some(OutputStatus::Completed),
...@@ -712,6 +705,7 @@ pub fn chat_completion_to_response( ...@@ -712,6 +705,7 @@ pub fn chat_completion_to_response(
output.push(OutputItem::FunctionCall(FunctionToolCall { output.push(OutputItem::FunctionCall(FunctionToolCall {
arguments: tc.function.arguments.clone(), arguments: tc.function.arguments.clone(),
call_id: tc.id.clone(), call_id: tc.id.clone(),
namespace: None,
name: tc.function.name.clone(), name: tc.function.name.clone(),
id: Some(format!("fc_{}", Uuid::new_v4().simple())), id: Some(format!("fc_{}", Uuid::new_v4().simple())),
status: Some(OutputStatus::Completed), status: Some(OutputStatus::Completed),
...@@ -725,7 +719,7 @@ pub fn chat_completion_to_response( ...@@ -725,7 +719,7 @@ pub fn chat_completion_to_response(
{ {
output.push(OutputItem::Reasoning(ReasoningItem { output.push(OutputItem::Reasoning(ReasoningItem {
id: format!("rs_{}", Uuid::new_v4().simple()), id: format!("rs_{}", Uuid::new_v4().simple()),
summary: vec![SummaryPart::SummaryText(Summary { summary: vec![SummaryPart::SummaryText(SummaryTextContent {
text: reasoning_text, text: reasoning_text,
})], })],
content: None, content: None,
...@@ -807,16 +801,8 @@ pub fn chat_completion_to_response( ...@@ -807,16 +801,8 @@ pub fn chat_completion_to_response(
output, output,
// Spec-required defaults (OpenResponses requires these as non-null) // Spec-required defaults (OpenResponses requires these as non-null)
background: Some(false), background: Some(false),
frequency_penalty: Some(0.0), metadata: Some(HashMap::new()),
metadata: Some(serde_json::Value::Object(Default::default())), parallel_tool_calls: params.parallel_tool_calls.or(Some(true)),
parallel_tool_calls: Some(true),
presence_penalty: Some(0.0),
// Echo actual request values, falling back to spec defaults.
// store: false because this branch does not persist responses.
store: api_context
.map(|ctx| ctx.store)
.or(params.store)
.or(Some(false)),
temperature: params.temperature.or(Some(1.0)), temperature: params.temperature.or(Some(1.0)),
text: Some(params.text.clone().unwrap_or(ResponseTextParam { text: Some(params.text.clone().unwrap_or(ResponseTextParam {
format: TextResponseFormatConfiguration::Text, format: TextResponseFormatConfiguration::Text,
...@@ -842,7 +828,6 @@ pub fn chat_completion_to_response( ...@@ -842,7 +828,6 @@ pub fn chat_completion_to_response(
incomplete_details: None, incomplete_details: None,
instructions: params.instructions.clone().map(Instructions::Text), instructions: params.instructions.clone().map(Instructions::Text),
max_output_tokens: params.max_output_tokens, max_output_tokens: params.max_output_tokens,
max_tool_calls: None,
previous_response_id: api_context.and_then(|ctx| ctx.previous_response_id.clone()), previous_response_id: api_context.and_then(|ctx| ctx.previous_response_id.clone()),
prompt: None, prompt: None,
prompt_cache_key: None, prompt_cache_key: None,
...@@ -880,8 +865,8 @@ pub fn chat_completion_to_response( ...@@ -880,8 +865,8 @@ pub fn chat_completion_to_response(
mod tests { mod tests {
use dynamo_protocols::types::responses::{ use dynamo_protocols::types::responses::{
CreateResponse, FunctionCallOutput, FunctionCallOutputItemParam, FunctionTool, CreateResponse, FunctionCallOutput, FunctionCallOutputItemParam, FunctionTool,
FunctionToolCall, ImageDetail, InputContent, InputImageContent, InputItem, InputMessage, FunctionToolCall, InputContent, InputImageContent, InputItem, InputMessage, InputParam,
InputParam, InputRole, InputTextContent, Item, MessageItem, Tool, InputRole, InputTextContent, Item, MessageItem, Tool,
}; };
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatCompletionRequestMessage, ChatCompletionRequestUserMessageContent, ChatCompletionRequestMessage, ChatCompletionRequestUserMessageContent,
...@@ -962,6 +947,15 @@ mod tests { ...@@ -962,6 +947,15 @@ mod tests {
} }
} }
#[test]
fn test_store_mapped_to_chat_completion_request() {
let mut req = make_response_with_input("audit me");
req.inner.store = Some(true);
let nv_req: NvCreateChatCompletionRequest = req.try_into().unwrap();
assert_eq!(nv_req.inner.store, Some(true));
}
#[test] #[test]
fn test_instructions_prepended_as_system_message() { fn test_instructions_prepended_as_system_message() {
let req = NvCreateResponse { let req = NvCreateResponse {
...@@ -1009,9 +1003,10 @@ mod tests { ...@@ -1009,9 +1003,10 @@ mod tests {
status: None, status: None,
}))), }))),
InputItem::Item(Item::Message(MessageItem::Output(OutputMessage { InputItem::Item(Item::Message(MessageItem::Output(OutputMessage {
id: Some("msg_1".into()), id: "msg_1".into(),
role: AssistantRole::Assistant, role: AssistantRole::Assistant,
status: Some(OutputStatus::Completed), status: OutputStatus::Completed,
phase: None,
content: vec![OutputMessageContent::OutputText(OutputTextContent { content: vec![OutputMessageContent::OutputText(OutputTextContent {
text: "4".into(), text: "4".into(),
annotations: vec![], annotations: vec![],
...@@ -1058,7 +1053,7 @@ mod tests { ...@@ -1058,7 +1053,7 @@ mod tests {
text: "What is in this image?".into(), text: "What is in this image?".into(),
}), }),
InputContent::InputImage(InputImageContent { InputContent::InputImage(InputImageContent {
detail: ImageDetail::Auto, detail: Default::default(), // ImageDetail::Auto
file_id: None, file_id: None,
image_url: Some("https://example.com/cat.jpg".into()), image_url: Some("https://example.com/cat.jpg".into()),
}), }),
...@@ -1102,6 +1097,7 @@ mod tests { ...@@ -1102,6 +1097,7 @@ mod tests {
InputItem::Item(Item::FunctionCall(FunctionToolCall { InputItem::Item(Item::FunctionCall(FunctionToolCall {
arguments: r#"{"location":"SF"}"#.into(), arguments: r#"{"location":"SF"}"#.into(),
call_id: "call_123".into(), call_id: "call_123".into(),
namespace: None,
name: "get_weather".into(), name: "get_weather".into(),
id: None, id: None,
status: None, status: None,
...@@ -1147,6 +1143,7 @@ mod tests { ...@@ -1147,6 +1143,7 @@ mod tests {
})), })),
strict: Some(true), strict: Some(true),
description: Some("Get weather info".into()), description: Some("Get weather info".into()),
defer_loading: None,
})]), })]),
..Default::default() ..Default::default()
}, },
...@@ -1230,7 +1227,7 @@ mod tests { ...@@ -1230,7 +1227,7 @@ mod tests {
refusal: None, refusal: None,
tool_calls: Some(vec![ChatCompletionMessageToolCall { tool_calls: Some(vec![ChatCompletionMessageToolCall {
id: "call_abc".into(), id: "call_abc".into(),
r#type: ChatCompletionToolType::Function, r#type: FunctionType::Function,
function: dynamo_protocols::types::FunctionCall { function: dynamo_protocols::types::FunctionCall {
name: "get_weather".into(), name: "get_weather".into(),
arguments: r#"{"location":"SF"}"#.into(), arguments: r#"{"location":"SF"}"#.into(),
...@@ -1424,6 +1421,15 @@ thinking ...@@ -1424,6 +1421,15 @@ thinking
assert_eq!(chat.inner.service_tier, Some(ChatServiceTier::Priority)); assert_eq!(chat.inner.service_tier, Some(ChatServiceTier::Priority));
} }
#[test]
fn test_parallel_tool_calls_mapped_to_chat_completion() {
let mut req = make_response_with_input("parallel tools off");
req.inner.parallel_tool_calls = Some(false);
let chat: NvCreateChatCompletionRequest = req.try_into().unwrap();
assert_eq!(chat.inner.parallel_tool_calls, Some(false));
}
#[test] #[test]
fn test_response_echoes_reasoning() { fn test_response_echoes_reasoning() {
use dynamo_protocols::types::ReasoningEffort; use dynamo_protocols::types::ReasoningEffort;
...@@ -1517,25 +1523,48 @@ thinking ...@@ -1517,25 +1523,48 @@ thinking
} }
#[test] #[test]
fn test_output_message_deserializes_without_id_and_status() { fn test_response_echoes_parallel_tool_calls() {
use dynamo_protocols::types::responses::{InputItem, Item, MessageItem}; let params = ResponseParams {
parallel_tool_calls: Some(false),
..Default::default()
};
let chat_resp = NvCreateChatCompletionResponse {
inner: dynamo_protocols::types::CreateChatCompletionResponse {
choices: vec![],
created: 0,
id: "test".into(),
model: "m".into(),
service_tier: None,
system_fingerprint: None,
object: "chat.completion".into(),
usage: None,
},
nvext: None,
};
let resp = chat_completion_to_response(chat_resp, &params, None).unwrap();
assert_eq!(resp.inner.parallel_tool_calls, Some(false));
}
#[test]
fn test_output_message_without_id_and_status_fails_to_deserialize() {
use dynamo_protocols::types::responses::InputItem;
// With the upstream schema, `id` (String) and `status` (OutputStatus) are
// required on OutputMessage. An assistant message without them can't
// deserialize as either OutputMessage or InputMessage (wrong role).
let json = serde_json::json!({ let json = serde_json::json!({
"role": "assistant", "role": "assistant",
"content": [{"type": "output_text", "text": "Hello!", "annotations": []}], "content": [{"type": "output_text", "text": "Hello!", "annotations": []}],
"type": "message" "type": "message"
}); });
let item: InputItem = serde_json::from_value(json).unwrap(); let result = serde_json::from_value::<InputItem>(json);
match item { assert!(
InputItem::Item(Item::Message(MessageItem::Output(msg))) => { result.is_err(),
assert_eq!(msg.role, AssistantRole::Assistant); "Expected deserialization to fail without id and status"
assert_eq!(msg.content.len(), 1); );
assert!(msg.id.is_none());
assert_eq!(msg.status, None);
}
other => panic!("Expected Item::Message(Output), got {:?}", other),
}
} }
#[test] #[test]
...@@ -1553,8 +1582,8 @@ thinking ...@@ -1553,8 +1582,8 @@ thinking
let item: InputItem = serde_json::from_value(json).unwrap(); let item: InputItem = serde_json::from_value(json).unwrap();
match item { match item {
InputItem::Item(Item::Message(MessageItem::Output(msg))) => { InputItem::Item(Item::Message(MessageItem::Output(msg))) => {
assert_eq!(msg.id.as_deref(), Some("msg_abc123")); assert_eq!(msg.id, "msg_abc123");
assert_eq!(msg.status, Some(OutputStatus::Completed)); assert_eq!(msg.status, OutputStatus::Completed);
} }
other => panic!("Expected Item::Message(Output), got {:?}", other), other => panic!("Expected Item::Message(Output), got {:?}", other),
} }
...@@ -1664,4 +1693,42 @@ thinking ...@@ -1664,4 +1693,42 @@ thinking
let resp = chat_completion_to_response(chat_resp, &params, None).unwrap(); let resp = chat_completion_to_response(chat_resp, &params, None).unwrap();
assert_eq!(resp.inner.truncation, Some(Truncation::Disabled)); assert_eq!(resp.inner.truncation, Some(Truncation::Disabled));
} }
/// Validate the JSON wire shape of NvResponse.
///
/// The migration to upstream async-openai v0.34 removed fields that were
/// incorrectly present on our old local Response type (they belong on the
/// request, not the response, per the OpenAI Responses API spec).
#[test]
fn test_response_wire_format_shape() {
let chat_resp = make_chat_resp_with_text("hello");
let params = ResponseParams::default();
let resp = chat_completion_to_response(chat_resp, &params, None).unwrap();
let json = serde_json::to_value(&resp).unwrap();
// Fields that were on our old local type but are NOT in the OpenAI
// Responses API spec -- they are request-level, not response-level.
assert!(json.get("frequency_penalty").is_none());
assert!(json.get("presence_penalty").is_none());
assert!(json.get("store").is_none());
assert!(json.get("max_tool_calls").is_none());
// Fields that should be present with expected values
assert_eq!(json["object"], "response");
assert_eq!(json["status"], "completed");
assert_eq!(json["metadata"], serde_json::json!({}));
assert!(json["output"].is_array());
assert!(json["output"][0].get("id").is_some());
assert!(json["output"][0].get("status").is_some());
// Optional fields with None should be omitted (upstream uses skip_serializing_if)
assert!(json.get("error").is_none());
assert!(json.get("incomplete_details").is_none());
assert!(json.get("billing").is_none());
assert!(json.get("conversation").is_none());
assert!(json.get("safety_identifier").is_none());
// nvext should be omitted when None
assert!(json.get("nvext").is_none());
}
} }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
//! `response.output_text.done` -> `response.content_part.done` -> //! `response.output_text.done` -> `response.content_part.done` ->
//! `response.output_item.done` -> `response.completed` -> `[DONE]` //! `response.output_item.done` -> `response.completed` -> `[DONE]`
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use axum::response::sse::Event; use axum::response::sse::Event;
...@@ -121,17 +122,8 @@ impl ResponseStreamConverter { ...@@ -121,17 +122,8 @@ impl ResponseStreamConverter {
output, output,
// Echo request params with spec-required defaults for omitted fields // Echo request params with spec-required defaults for omitted fields
background: Some(false), background: Some(false),
frequency_penalty: Some(0.0), metadata: Some(HashMap::new()),
metadata: Some(serde_json::Value::Object(Default::default())), parallel_tool_calls: self.params.parallel_tool_calls.or(Some(true)),
parallel_tool_calls: Some(true),
presence_penalty: Some(0.0),
// store: false because this branch does not persist responses.
store: self
.api_context
.as_ref()
.map(|ctx| ctx.store)
.or(self.params.store)
.or(Some(false)),
temperature: self.params.temperature.or(Some(1.0)), temperature: self.params.temperature.or(Some(1.0)),
text: Some(self.params.text.clone().unwrap_or(ResponseTextParam { text: Some(self.params.text.clone().unwrap_or(ResponseTextParam {
format: TextResponseFormatConfiguration::Text, format: TextResponseFormatConfiguration::Text,
...@@ -158,7 +150,6 @@ impl ResponseStreamConverter { ...@@ -158,7 +150,6 @@ impl ResponseStreamConverter {
incomplete_details: None, incomplete_details: None,
instructions: self.params.instructions.clone().map(Instructions::Text), instructions: self.params.instructions.clone().map(Instructions::Text),
max_output_tokens: self.params.max_output_tokens, max_output_tokens: self.params.max_output_tokens,
max_tool_calls: None,
previous_response_id: self previous_response_id: self
.api_context .api_context
.as_ref() .as_ref()
...@@ -250,10 +241,11 @@ impl ResponseStreamConverter { ...@@ -250,10 +241,11 @@ impl ResponseStreamConverter {
sequence_number: self.next_seq(), sequence_number: self.next_seq(),
output_index, output_index,
item: OutputItem::Message(OutputMessage { item: OutputItem::Message(OutputMessage {
id: Some(self.message_item_id.clone()), id: self.message_item_id.clone(),
content: vec![], content: vec![],
role: AssistantRole::Assistant, role: AssistantRole::Assistant,
status: Some(OutputStatus::InProgress), phase: None,
status: OutputStatus::InProgress,
}), }),
}, },
); );
...@@ -333,6 +325,7 @@ impl ResponseStreamConverter { ...@@ -333,6 +325,7 @@ impl ResponseStreamConverter {
item: OutputItem::FunctionCall(FunctionToolCall { item: OutputItem::FunctionCall(FunctionToolCall {
id: Some(item_id), id: Some(item_id),
call_id, call_id,
namespace: None,
name: fc_name, name: fc_name,
arguments: String::new(), arguments: String::new(),
status: Some(OutputStatus::InProgress), status: Some(OutputStatus::InProgress),
...@@ -398,6 +391,7 @@ impl ResponseStreamConverter { ...@@ -398,6 +391,7 @@ impl ResponseStreamConverter {
item: OutputItem::FunctionCall(FunctionToolCall { item: OutputItem::FunctionCall(FunctionToolCall {
id: Some(fc_item_id), id: Some(fc_item_id),
call_id: fc_call_id, call_id: fc_call_id,
namespace: None,
name: fc_name, name: fc_name,
arguments: fc_args, arguments: fc_args,
status: Some(OutputStatus::Completed), status: Some(OutputStatus::Completed),
...@@ -450,14 +444,15 @@ impl ResponseStreamConverter { ...@@ -450,14 +444,15 @@ impl ResponseStreamConverter {
sequence_number: self.next_seq(), sequence_number: self.next_seq(),
output_index: self.message_output_index, output_index: self.message_output_index,
item: OutputItem::Message(OutputMessage { item: OutputItem::Message(OutputMessage {
id: Some(self.message_item_id.clone()), id: self.message_item_id.clone(),
content: vec![OutputMessageContent::OutputText(OutputTextContent { content: vec![OutputMessageContent::OutputText(OutputTextContent {
text: self.accumulated_text.clone(), text: self.accumulated_text.clone(),
annotations: vec![], annotations: vec![],
logprobs: Some(vec![]), logprobs: Some(vec![]),
})], })],
role: AssistantRole::Assistant, role: AssistantRole::Assistant,
status: Some(OutputStatus::Completed), phase: None,
status: OutputStatus::Completed,
}), }),
}); });
events.push(make_sse_event(&item_done)); events.push(make_sse_event(&item_done));
...@@ -497,6 +492,7 @@ impl ResponseStreamConverter { ...@@ -497,6 +492,7 @@ impl ResponseStreamConverter {
item: OutputItem::FunctionCall(FunctionToolCall { item: OutputItem::FunctionCall(FunctionToolCall {
id: Some(item_id), id: Some(item_id),
call_id, call_id,
namespace: None,
name: fc_name, name: fc_name,
arguments: accumulated_args, arguments: accumulated_args,
status: Some(OutputStatus::Completed), status: Some(OutputStatus::Completed),
...@@ -509,14 +505,15 @@ impl ResponseStreamConverter { ...@@ -509,14 +505,15 @@ impl ResponseStreamConverter {
let mut output = Vec::new(); let mut output = Vec::new();
if self.message_started { if self.message_started {
output.push(OutputItem::Message(OutputMessage { output.push(OutputItem::Message(OutputMessage {
id: Some(self.message_item_id.clone()), id: self.message_item_id.clone(),
content: vec![OutputMessageContent::OutputText(OutputTextContent { content: vec![OutputMessageContent::OutputText(OutputTextContent {
text: self.accumulated_text.clone(), text: self.accumulated_text.clone(),
annotations: vec![], annotations: vec![],
logprobs: Some(vec![]), logprobs: Some(vec![]),
})], })],
role: AssistantRole::Assistant, role: AssistantRole::Assistant,
status: Some(OutputStatus::Completed), phase: None,
status: OutputStatus::Completed,
})); }));
} }
for fc in &self.function_call_items { for fc in &self.function_call_items {
...@@ -524,6 +521,7 @@ impl ResponseStreamConverter { ...@@ -524,6 +521,7 @@ impl ResponseStreamConverter {
output.push(OutputItem::FunctionCall(FunctionToolCall { output.push(OutputItem::FunctionCall(FunctionToolCall {
id: Some(fc.item_id.clone()), id: Some(fc.item_id.clone()),
call_id: fc.call_id.clone(), call_id: fc.call_id.clone(),
namespace: None,
name: fc.name.clone(), name: fc.name.clone(),
arguments: fc.accumulated_args.clone(), arguments: fc.accumulated_args.clone(),
status: Some(OutputStatus::Completed), status: Some(OutputStatus::Completed),
...@@ -675,7 +673,7 @@ mod tests { ...@@ -675,7 +673,7 @@ mod tests {
use crate::protocols::unified::ResponsesContext; use crate::protocols::unified::ResponsesContext;
use dynamo_protocols::types::{ use dynamo_protocols::types::{
ChatChoiceStream, ChatCompletionMessageContent, ChatCompletionMessageToolCallChunk, ChatChoiceStream, ChatCompletionMessageContent, ChatCompletionMessageToolCallChunk,
ChatCompletionStreamResponseDelta, ChatCompletionToolType, FunctionCallStream, ChatCompletionStreamResponseDelta, FunctionCallStream, FunctionType,
}; };
fn default_params() -> ResponseParams { fn default_params() -> ResponseParams {
...@@ -684,6 +682,7 @@ mod tests { ...@@ -684,6 +682,7 @@ mod tests {
temperature: None, temperature: None,
top_p: None, top_p: None,
max_output_tokens: None, max_output_tokens: None,
parallel_tool_calls: None,
store: None, store: None,
tools: None, tools: None,
tool_choice: None, tool_choice: None,
...@@ -714,7 +713,7 @@ mod tests { ...@@ -714,7 +713,7 @@ mod tests {
tool_calls: Some(vec![ChatCompletionMessageToolCallChunk { tool_calls: Some(vec![ChatCompletionMessageToolCallChunk {
index: tc_index, index: tc_index,
id: id.map(String::from), id: id.map(String::from),
r#type: Some(ChatCompletionToolType::Function), r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream { function: Some(FunctionCallStream {
name: name.map(String::from), name: name.map(String::from),
arguments: args.map(String::from), arguments: args.map(String::from),
...@@ -932,7 +931,7 @@ mod tests { ...@@ -932,7 +931,7 @@ mod tests {
); );
} }
/// Verify that `with_context` populates `previous_response_id` and `store` /// Verify that `with_context` populates `previous_response_id`
/// in the generated Response objects. /// in the generated Response objects.
#[test] #[test]
fn test_with_context_enriches_response() { fn test_with_context_enriches_response() {
...@@ -949,16 +948,14 @@ mod tests { ...@@ -949,16 +948,14 @@ mod tests {
let _ = conv.process_chunk(&text_chunk("Hello")); let _ = conv.process_chunk(&text_chunk("Hello"));
let _end_events = conv.emit_end_events(); let _end_events = conv.emit_end_events();
// Verify the Response object carries the context values through
let response = conv.make_response(Status::Completed, vec![]); let response = conv.make_response(Status::Completed, vec![]);
assert_eq!( assert_eq!(
response.previous_response_id.as_deref(), response.previous_response_id.as_deref(),
Some("resp_prev_123") Some("resp_prev_123")
); );
assert_eq!(response.store, Some(true));
} }
/// Without context, previous_response_id is None and store defaults to false. /// Without context, previous_response_id is None.
#[test] #[test]
fn test_without_context_defaults() { fn test_without_context_defaults() {
let params = ResponseParams::default(); let params = ResponseParams::default();
...@@ -966,6 +963,17 @@ mod tests { ...@@ -966,6 +963,17 @@ mod tests {
let response = conv.make_response(Status::Completed, vec![]); let response = conv.make_response(Status::Completed, vec![]);
assert_eq!(response.previous_response_id, None); assert_eq!(response.previous_response_id, None);
assert_eq!(response.store, Some(false)); }
#[test]
fn test_stream_response_echoes_parallel_tool_calls() {
let params = ResponseParams {
parallel_tool_calls: Some(false),
..Default::default()
};
let conv = ResponseStreamConverter::new("test-model".into(), params);
let response = conv.make_response(Status::Completed, vec![]);
assert_eq!(response.parallel_tool_calls, Some(false));
} }
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment