diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..47684146 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/.config/hakari.toml b/.config/hakari.toml index b657188e..51c2c4c0 100644 --- a/.config/hakari.toml +++ b/.config/hakari.toml @@ -30,6 +30,7 @@ exact-versions = true [traversal-excludes] workspace-members = [ "homestar-functions-add", + "homestar-functions-llm", "homestar-functions-subtract", "homestar-functions-test", ] diff --git a/.gitignore b/.gitignore index 1456dd0e..7b118549 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ examples/**/tmp/* # npm packages homestar-runtime/npm/binaries + +# llm +models +*tar.gz diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b2e9db1..2778e056 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -85,6 +85,7 @@ repos: - id: check-json - id: check-added-large-files args: ["--maxkb=4000"] + exclude: examples-llm-workflows/models/.* - id: detect-private-key exclude: __testkey - id: check-executables-have-shebangs diff --git a/Cargo.lock b/Cargo.lock index d9e6c188..65cfac8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,6 +521,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.66.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.50", + "which", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -833,6 +856,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -882,6 +914,28 @@ dependencies = [ "windows-targets 0.52.3", ] +[[package]] +name = "chrono-tz" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -934,6 +988,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.1" @@ -1626,13 +1691,34 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro 0.12.0", +] + [[package]] name = "derive_builder" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f59169f400d8087f238c5c0c7db6a28af18681717f3b623227d92f397e938c7" dependencies = [ - "derive_builder_macro", + "derive_builder_macro 0.13.1", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1647,16 +1733,32 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core 0.12.0", + "syn 1.0.109", +] + [[package]] name = "derive_builder_macro" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "870368c3fb35b8031abb378861d4460f573b92238ec2152c927a21f77e3e0127" dependencies = [ - "derive_builder_core", + "derive_builder_core 0.13.1", "syn 1.0.109", ] +[[package]] +name = "deunicode" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94" + [[package]] name = "diesel" version = "2.1.4" @@ -2116,6 +2218,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -2369,6 +2486,30 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick 1.1.2", + "bstr", + "log", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "gloo-net" version = "0.5.0" @@ -2591,18 +2732,34 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "homestar-functions-add" version = "0.1.0" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen-rt 0.20.0", +] + +[[package]] +name = "homestar-functions-llm" +version = "0.1.0" +dependencies = [ + "wit-bindgen-rt 0.21.0", ] [[package]] name = "homestar-functions-subtract" version = "0.1.0" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen-rt 0.20.0", ] [[package]] @@ -2611,7 +2768,7 @@ version = "0.1.0" dependencies = [ "base64 0.21.7", "image", - "wit-bindgen-rt", + "wit-bindgen-rt 0.20.0", ] [[package]] @@ -2663,7 +2820,7 @@ dependencies = [ "dagga", "dashmap", "derive-getters", - "derive_builder", + "derive_builder 0.13.1", "diesel", "diesel-derive-enum", "diesel_migrations", @@ -2789,6 +2946,8 @@ dependencies = [ "indexmap 2.2.3", "itertools 0.12.1", "libipld", + "llm-chain", + "llm-chain-llama", "rust_decimal", "serde", "serde_json", @@ -2825,10 +2984,11 @@ name = "homestar-workspace-hack" version = "0.1.0" dependencies = [ "ahash", + "aho-corasick 1.1.2", "anyhow", "arrayvec 0.7.4", "base64 0.13.1", - "bitflags 2.4.2", + "bstr", "bytes", "cc", "clap", @@ -2869,19 +3029,24 @@ dependencies = [ "miniz_oxide", "mio", "multibase", + "nom", "num-traits", "object", + "phf_shared", "proc-macro2", "rand", + "rand_core", "regex", "regex-automata 0.4.5", "regex-syntax 0.8.2", + "reqwest", "retry", "ring 0.17.8", "rustc-hash", "rustix", "rustls 0.21.10", "scopeguard", + "security-framework-sys", "semver", "serde", "serde_json", @@ -2982,6 +3147,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "humantime" version = "2.1.0" @@ -3053,6 +3227,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -3156,6 +3343,22 @@ dependencies = [ "xmltree", ] +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.5", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.24.9" @@ -3576,6 +3779,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "leb128" version = "0.2.5" @@ -3671,6 +3880,16 @@ dependencies = [ "libipld-core", ] +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.3", +] + [[package]] name = "libm" version = "0.2.8" @@ -4229,6 +4448,55 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "llm-chain" +version = "0.13.0" +source = "git+https://github.com/sobelio/llm-chain.git#5e153919fd2d900b66c2ea0d51c5ca1a520ad8da" +dependencies = [ + "anyhow", + "async-trait", + "derive_builder 0.12.0", + "futures", + "lazy_static", + "markdown", + "paste", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "strum 0.25.0", + "strum_macros", + "tera", + "thiserror", + "tokio", + "tokio-stream", + "uuid", +] + +[[package]] +name = "llm-chain-llama" +version = "0.13.0" +source = "git+https://github.com/sobelio/llm-chain.git#5e153919fd2d900b66c2ea0d51c5ca1a520ad8da" +dependencies = [ + "anyhow", + "async-trait", + "futures", + "lazy_static", + "llm-chain", + "llm-chain-llama-sys", + "serde", + "thiserror", + "tokio", +] + +[[package]] +name = "llm-chain-llama-sys" +version = "0.13.0" +source = "git+https://github.com/sobelio/llm-chain.git#5e153919fd2d900b66c2ea0d51c5ca1a520ad8da" +dependencies = [ + "bindgen", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -4293,6 +4561,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "markdown" +version = "1.0.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0f0025e8c0d89b84d6dc63e859475e40e8e82ab1a08be0a93ad5731513a508" +dependencies = [ + "unicode-id", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -4655,6 +4932,24 @@ dependencies = [ "rand", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "natord" version = "1.0.9" @@ -4896,12 +5191,50 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.18.0" @@ -5009,6 +5342,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + [[package]] name = "paste" version = "1.0.14" @@ -5021,6 +5363,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pem" version = "3.0.3" @@ -5046,6 +5394,89 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "pest_meta" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.4" @@ -5232,6 +5663,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.50", +] + [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -5734,19 +6175,23 @@ dependencies = [ "http 0.2.11", "http-body", "hyper", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", @@ -6284,6 +6729,19 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "serde_yaml" +version = "0.9.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" +dependencies = [ + "indexmap 2.2.3", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serial_test" version = "3.0.0" @@ -6386,6 +6844,12 @@ dependencies = [ "dirs", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -6437,6 +6901,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "skeptic" version = "0.13.7" @@ -6473,6 +6943,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" +[[package]] +name = "slug" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.13.1" @@ -6647,6 +7127,12 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "strum_macros" version = "0.25.3" @@ -6881,6 +7367,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tera" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] + [[package]] name = "terminal_size" version = "0.1.17" @@ -7036,6 +7544,16 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -7469,12 +7987,18 @@ dependencies = [ "rand", "serde", "serde_json", - "strum", + "strum 0.24.1", "strum_macros", "unsigned-varint 0.7.2", "url", ] +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "uint" version = "0.9.5" @@ -7493,6 +8017,56 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.7.0" @@ -7508,6 +8082,12 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-id" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -7557,6 +8137,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "unsigned-varint" version = "0.7.2" @@ -7834,6 +8420,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.205.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e95b3563d164f33c1cfb0a7efbd5940c37710019be10cd09f800fdec8b0e5c" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-metadata" version = "0.200.0" @@ -8214,24 +8809,24 @@ dependencies = [ [[package]] name = "wast" -version = "200.0.0" +version = "205.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1810d14e6b03ebb8fb05eef4009ad5749c989b65197d83bce7de7172ed91366" +checksum = "441a6a195b3b5245e26d450bbcc91366c6b652382a22f63cbe3c73240e13b2bb" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.200.0", + "wasm-encoder 0.205.0", ] [[package]] name = "wat" -version = "1.200.0" +version = "1.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776cbd10e217f83869beaa3f40e312bb9e91d5eee29bbf6f560db1261b6a4c3d" +checksum = "19832624d606e7c6bf3cd4caa73578ecec5eac30c768269256d19c79900beb18" dependencies = [ - "wast 200.0.0", + "wast 205.0.0", ] [[package]] @@ -8281,6 +8876,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "widestring" version = "1.0.2" @@ -8580,6 +9187,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "781989c70b3300b1be7ffbc7095e3b56c4f208f0c109cb5d1d943b297918dd8a" +[[package]] +name = "wit-bindgen-rt" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026d24a27f6712541fa534f2954bd9e0eb66172f033c2157c0f31d106255c497" + [[package]] name = "wit-component" version = "0.200.0" @@ -8807,9 +9420,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index e70c6ce9..3e1e7025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ categories = ["development-tools", "wasm", "web-programming"] edition = "2021" license = "Apache-2.0" repository = "https://github.com/ipvm-wg/homestar" -rust-version = "1.75.0" +rust-version = "1.77.0" version = "0.3.0" [workspace.dependencies] diff --git a/example-llm-workflows/README.md b/example-llm-workflows/README.md new file mode 100644 index 00000000..8521a19b --- /dev/null +++ b/example-llm-workflows/README.md @@ -0,0 +1,483 @@ +# Running and Replaying AI Chains in Wasm On a Private, Locally Hosted LLM + +_Author_: [Zeeshan Lakhani][zeeshan-lakhani] + +## Outline + +- [Quickstart](#quickstart) +- [Expanded Tutorial and Background](#expanded-tutorial-and-background) +- [Outputs and Discussion](#outputs-and-discussion) +- [Foundations for privacy](#foundations-for-privacy) +- [What is Homestar in a Nutshell?](#what-is-homestar-in-a-nutshell?) + +## Quickstart + +You can run this project completely in your console (shell). It consists of running a forked [Homestar][homestar] node (read more about Homestar in [What is Homestar in a Nutshell?](#what-is-homestar-in-a-nutshell?)) and a local version of the [EveryCLI][every-cli] utility, a tool for working with the [Everywhere Computer][everywhere-computer], which is an interface for running workflows composed of [Wasm][wasm] / [Wasi][wasi] functions on Homestar. + +Let's first unzip [our tar file][tar-link] containing binaries and what's needed to run our demos, including workflow JSON files and [LLaMA 3][llama-3] models (created from [llama.cpp][llama.cpp]). + +```console +tar -xf haii.tar.gz -C +cd /example-llm-workflows +``` + +Everything going forward should take place within the `example-llm-workflows` folder. + +In one window, let's run the forked Homestar node (built with LLaMA 3 bindings) while also setting some environment variables: + +```console +EVERY_CLI=true LLAMA_METAL=1 ./homestar start -c homestar_config.toml +``` + +In another console window, let's run the local version of EVERY-CLI: + +```console +./every-cli/cli.js dev ../example-llm-workflows/llm.wasm +``` + +After running the above command, you should see something similar to this: + +```console +IPFS is running at http://127.0.0.1:5001/debug/vars +✔ Functions parsed and compiled +✔ Homestar is running at http://127.0.0.1:8020 +✔ Control Panel is running at https://control.everywhere.computer + +[9:40:54 AM] WARN [listhen] Trying to listhen on private host "127.0.0.1" with public option enabled. + +[9:40:54 AM] ◐ Starting cloudflared tunnel to http://127.0.0.1:3000/ + + █▀▀▀▀▀▀▀██▀██████▀██▀▀▀▀▀██▀▀▀▀▀▀▀█ + █ █▀▀▀█ █▄ ▀ ▄▄▀▄▀▄ █ ████ █▀▀▀█ █ + █ █ █ █▄▄▀▄▀ ▄██ ▀▀▀ █ █ █ █ + █ ▀▀▀▀▀ █ █ █▀▄ ▄ ▄▀█▀█▀█ █ ▀▀▀▀▀ █ + █▀▀▀▀▀█▀▀▀▀▀█ ▀ ▄▀▄█ ▄▀▀▀▀█▀█▀█▀██ + █▄█ ▀▀ ▀▄▄ █▀▀▄▀▀▄██▄▄▀█▄██▄██▀▄ ▄█ + █▄ ██▀▀▀▀█ ▄ ██▄▀▀▀▄▄▀▄ █ ▄▀▀▀▄███ + █▀▀▄ █▀▀▀▀▄ █▄ ▀▀ ▀▀▄ ▄█▄█ ▄▀▀██ ▄█ + █ ▀▀▀▀▄▀█▀█▀█ ▄▀ █▀▄█▄▄ ▄ ██▄▀█▄▀██ + █▀█▀ ▀█▀▀▀▀▄▀▀██▀▄▄▀▄▄▀█▄█▀ ▀▀ █ ▄█ + █ ▄██▀▀██▄▀ ██▄▄▄▀█▄ ▄ ▀ █▄▀█▄▀██ + █ █ ██▀▀███▀█▄ ▀▄▀ ▄▄█▀▄▀ ▄█▀ █ ▄█ + █ ██ ▀█▀ █ █ ▄▀▀▄▀█ ▄▄▀█ ▀▀▀ ▄▄█▀█ + █▀▀▀▀▀▀▀█ █▀▀██▀▄▄▄▄ ▄▀ █▀█ ▀▀▀▄█ + █ █▀▀▀█ █▀▀█ ██▄ ▄▀▄ ▄▄▄▄ ▀▀▀ ▄▄▀ █ + █ █ █ █ ▀█▄ ▀ ▄▄ ▀▀ █ ▀▄▄▄█▄▄█ + █ ▀▀▀▀▀ █ █ █ ▄▀ ▀█▄█▄ ▀▀█▀▀▄▄▄▀██ + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ + + ➜ Local: http://127.0.0.1:3000/ +``` + +EveryCLI has started a gateway that loads Wasm components onto [IPFS][ipfs] for persistent storage, prepares workflows, and integrates with the Homestar runtime to schedule and execute these workflows. + +With EveryCLI up, in another window, let's POST a job/workflow to the local EveryCLI `localhost` endpoint, which will execute the [`simple.json`](./simple.json2) workflow on [Homestar][homestar]: + +```console +curl localhost:3000/run --json @simple.json +``` + +Once this completes (which can take some time mind you), we'll get back a response like this from the execution, which is returning text from a [LLaMA 3 instruction tuned model][instruction-llama]: + +> The city of Pittsburgh has many attractions that visitors can enjoy, including the world-famous Carnegie Museum of Natural History, the Andy Warhol Museum, and the famous Mount Washington neighborhood with its stunning views of the city skyline. Visitors can also take a ride on the Duquesne or Monongahela Incline for a panoramic view of the city, or explore the many parks and green spaces throughout the city. Additionally, Pittsburgh is home to several professional sports teams, including the Steelers, Pirates, and Penguins, making it a great destination for sports enthusiasts. +In this article, we'll explore some of the best attractions in Pittsburgh, including museums, historic sites, and outdoor activities. +The Carnegie Museum of Natural History is one of the city's most popular attractions. With over 10 million specimens in its collection, the museum is home to many important scientific discoveries. Visitors can explore exhibits on dinosaurs, mammals, and the natural history of Western Pennsylvania. +Another must-visit attraction is the Andy Warhol Museum. Located in the Strip District, the museum is dedicated to the life and work of Pittsburgh's own Andy Warhol, one of the most famous artists of the 20th century. The museum features a vast collection of Warhol's artwork, as well as exhibits on his life and legacy. + +> For outdoor enthusiasts, there are many great attractions in Pittsburgh that offer stunning views of the city skyline. One of the best is the Mount Washington neighborhood, which offers panoramic views of the city from its many parks and overlooks. Visitors can take a ride on one of the neighborhood's famous inclines – the Duquesne or Monongahela Incline – for an unforgettable view. + +> Another great outdoor attraction in Pittsburgh is Frick Park. Located just east of downtown, the park offers hiking trails, picnic areas, and stunning views of the city skyline from its many overlooks. Visitors can also explore the park's many gardens, including the beautiful Smithfield Street Garden. + +> Pittsburgh is also home to several professional sports teams, making it a great destination for fans of baseball, football, hockey, and basketball. The Steelers (football), Pirates (baseball), Penguins (hockey), and Panthers (basketball) are just a few of the city's many sports teams, which have a loyal following in the city. + +> In conclusion, Pittsburgh has many attractions that visitors can enjoy, from museums to historic sites to outdoor activities. Whether you're interested in art, history, sports, or nature, there's something for everyone in this great city! + +We also know that this singular prompt chain was completed successfully as we can follow along with the logs in the Homestar window (which includes LLaMA model contextual information): + +```console +ts=2024-05-01T04:09:43.598369Z level=info message="Brief History of Pittsburgh" subject=wasm_execution category=topic +llama_model_loader: loaded meta data with 22 key-value pairs and 291 tensors from /Users/zeeshanlakhani/fission/ipvm-wg/homestar/example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf (version GGUF V3 (latest)) +llama_model_loader: - tensor 0: token_embd.weight q4_0 [ 4096, 128256, 1, 1 ] +llama_model_loader: - tensor 1: blk.0.attn_norm.weight f32 [ 4096, 1, 1, 1 ] +llama_model_loader: - tensor 2: blk.0.ffn_down.weight q4_0 [ 14336, 4096, 1, 1 ] +llama_model_loader: - tensor 3: blk.0.ffn_gate.weight q4_0 [ 4096, 14336, 1, 1 ] +llama_model_loader: - tensor 4: blk.0.ffn_up.weight q4_0 [ 4096, 14336, 1, 1 ] +llama_model_loader: - tensor 5: blk.0.ffn_norm.weight f32 [ 4096, 1, 1, 1 ] +llama_model_loader: - tensor 6: blk.0.attn_k.weight q4_0 [ 4096, 1024, 1, 1 ] +llama_model_loader: - tensor 7: blk.0.attn_output.weight q4_0 [ 4096, 4096, 1, 1 ] +llama_model_loader: - tensor 8: blk.0.attn_q.weight q4_0 [ 4096, 4096, 1, 1 ] +llama_model_loader: - tensor 9: blk.0.attn_v.weight q4_0 [ 4096, 1024, 1, 1 ] +llama_model_loader: - tensor 10: blk.1.attn_norm.weight f32 [ 4096, 1, 1, 1 ] +llama_model_loader: - tensor 11: blk.1.ffn_down.weight q4_0 [ 14336, 4096, 1, 1 ] +llama_model_loader: - tensor 12: blk.1.ffn_gate.weight q4_0 [ 4096, 14336, 1, 1 ] +llama_model_loader: - tensor 13: blk.1.ffn_up.weight q4_0 [ 4096, 14336, 1, 1 ] +llama_model_loader: - tensor 14: blk.1.ffn_norm.weight f32 [ 4096, 1, 1, 1 ] +llama_model_loader: - tensor 15: blk.1.attn_k.weight q4_0 [ 4096, 1024, 1, 1 ] +... +llama_model_loader: - tensor 290: output_norm.weight f32 [ 4096, 1, 1, 1 ] +llama_model_loader: - kv 0: general.architecture str = llama +llama_model_loader: - kv 1: general.name str = . +llama_model_loader: - kv 2: llama.vocab_size u32 = 128256 +llama_model_loader: - kv 3: llama.context_length u32 = 8192 +llama_model_loader: - kv 4: llama.embedding_length u32 = 4096 +llama_model_loader: - kv 5: llama.block_count u32 = 32 +llama_model_loader: - kv 6: llama.feed_forward_length u32 = 14336 +llama_model_loader: - kv 7: llama.rope.dimension_count u32 = 128 +llama_model_loader: - kv 8: llama.attention.head_count u32 = 32 +llama_model_loader: - kv 9: llama.attention.head_count_kv u32 = 8 +llama_model_loader: - kv 10: llama.attention.layer_norm_rms_epsilon f32 = 0.000010 +llama_model_loader: - kv 11: llama.rope.freq_base f32 = 500000.000000 +llama_model_loader: - kv 12: general.file_type u32 = 2 +llama_model_loader: - kv 13: tokenizer.ggml.model str = gpt2 +llama_model_loader: - kv 14: tokenizer.ggml.tokens arr[str,128256] = ["!", "\"", "#", "$", "%", "&", "'", ... +llama_model_loader: - kv 15: tokenizer.ggml.scores arr[f32,128256] = [0.000000, 0.000000, 0.000000, 0.0000... +llama_model_loader: - kv 16: tokenizer.ggml.token_type arr[i32,128256] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... +llama_model_loader: - kv 17: tokenizer.ggml.merges arr[str,280147] = ["Ġ Ġ", "Ġ ĠĠĠ", "ĠĠ ĠĠ", "... +llama_model_loader: - kv 18: tokenizer.ggml.bos_token_id u32 = 128000 +llama_model_loader: - kv 19: tokenizer.ggml.eos_token_id u32 = 128009 +llama_model_loader: - kv 20: tokenizer.chat_template str = {% set loop_messages = messages %}{% ... +llama_model_loader: - kv 21: general.quantization_version u32 = 2 +llama_model_loader: - type f32: 65 tensors +llama_model_loader: - type q4_0: 225 tensors +llama_model_loader: - type q6_K: 1 tensors +llm_load_vocab: special tokens definition check successful ( 256/128256 ). +llm_load_print_meta: format = GGUF V3 (latest) +llm_load_print_meta: arch = llama +llm_load_print_meta: vocab type = BPE +llm_load_print_meta: n_vocab = 128256 +llm_load_print_meta: n_merges = 280147 +llm_load_print_meta: n_ctx_train = 8192 +llm_load_print_meta: n_embd = 4096 +llm_load_print_meta: n_head = 32 +llm_load_print_meta: n_head_kv = 8 +llm_load_print_meta: n_layer = 32 +llm_load_print_meta: n_rot = 128 +llm_load_print_meta: n_gqa = 4 +llm_load_print_meta: f_norm_eps = 0.0e+00 +llm_load_print_meta: f_norm_rms_eps = 1.0e-05 +llm_load_print_meta: f_clamp_kqv = 0.0e+00 +llm_load_print_meta: f_max_alibi_bias = 0.0e+00 +llm_load_print_meta: n_ff = 14336 +llm_load_print_meta: rope scaling = linear +llm_load_print_meta: freq_base_train = 500000.0 +llm_load_print_meta: freq_scale_train = 1 +llm_load_print_meta: n_yarn_orig_ctx = 8192 +llm_load_print_meta: rope_finetuned = unknown +llm_load_print_meta: model type = 7B +llm_load_print_meta: model ftype = mostly Q4_0 +llm_load_print_meta: model params = 8.03 B +llm_load_print_meta: model size = 4.33 GiB (4.64 BPW) +llm_load_print_meta: general.name = . +llm_load_print_meta: BOS token = 128000 '<|begin_of_text|>' +llm_load_print_meta: EOS token = 128009 '<|eot_id|>' +llm_load_print_meta: LF token = 128 'Ä' +llm_load_tensors: ggml ctx size = 0.11 MiB +llm_load_tensors: mem required = 4437.91 MiB +....................................................................................... +llama_new_context_with_model: n_ctx = 4096 +llama_new_context_with_model: freq_base = 500000.0 +llama_new_context_with_model: freq_scale = 1 +llama_new_context_with_model: kv self size = 512.00 MiB +llama_build_graph: non-view tensors processed: 676/676 +llama_new_context_with_model: compute buffer total size = 2307.09 MiB +ts=2024-05-01T04:10:27.408281Z level=info message="computed receipt" subject=worker.receipt category=worker.run receipt_cid=bafyrmidj5arbcw2pmnw5xr3phwxaej7o7wzkurrnguywwz6jtqynnpbql4 +ts=2024-05-01T04:10:27.408741Z level=info message="workflow completed" subject=worker.end_workflow category=worker.run workflow_cid=bafyrmic7tvkux3rca4rehdehpa7o2kwklqr26jdmkujmp7hw34m5kacjgy +``` + +This [`simple.json`](./simple.json) workflow runs one task that executes a [Guest][guest] Wasm module's `gen` function on the [Host][host], where `gen` takes in one argument (`"Attractions in Pittsburgh"`) and itself calls a Host-only `prompt_with` function, which is integrated with LLaMA's model's bindings and tuned parameters. + +**simple.json** + +``` json +{ + "tasks": [ + { + "run": { + "name": "genParagraph", + "input": { + "args": ["Attractions in Pittsburgh"], + "func": "gen" + } + } + } + ] +} + +``` + +**gen("Attractions in Pittsburgh")** + +``` rust +fn gen(topic: String) -> String { + log(Level::Info, "topic", &topic); + let input = format!( + "{} is an awesome topic. Let's write about it in a few sentences.", + topic + ); + prompt_with( + &input, + Some( + PathBuf::from(env!("CARGO_WORKSPACE_DIR")) + .join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf") + .display() + .to_string() + .as_str(), + ), + ) +} +``` + +## Expanded Tutorial and Background + +Next, we're going to dive deeper into working with workflows and defining [AI Chains][ai-chains], which chain LLM steps together. All of these example workflows are somewhat paired down, but they were inspired by the paragraph/article map-reduce collaborative writing task discussed in the [CrowdForge: Crowdsourcing Complex Work][crowdforge] paper, one in which we reproduced for homework with Google's API-based [Gemini language model][gemini]. + +### The Basics + +```console +curl localhost:3000/run --json @simple.json +``` + +We've already covered this simplistic LLM prompt workflow centered on providing a topic in the [Quickstart](#quickstart) section. Let's move on to the more fun stuff. + +### A Sequential Chain + +```console +curl localhost:3000/run --json @chain1.json +``` + +Chaining multiple prompts together can address a much wider range of human tasks and allow for the exploration of different prompting techniques, like few-shot and [chain-of-thought][chain-of-thought-paper] variations, which can help guide LLMs to better empirical results. Given that our compute engine is designed around the framework of an ordered workflow or pipeline, where the output of one task feeds into the input of another, we can easily generate AI chains by way of pipelines where tasks build off each previous task's output: + +``` json +{ + "tasks": [ + { + "run": { + "name": "genParagraph", + "input": { + "args": ["Brief History of Pittsburgh"], + "func": "gen" + } + } + }, + { + "run": { + "name": "genFrom1", + "input": { + "args": [ + "{{needs.genParagraph.output}}", + "Brief History of Pittsburgh" + ], + "func": "genBySection" + } + } + }, + { + "run": { + "name": "genFrom2", + "input": { + "args": [ + "{{needs.genFrom1.output}}", + "Brief History of Pittsburgh" + ], + "func": "genBySection" + } + } + } + ] +} +``` + +The `{{needs..output}}` template syntax provided by EveryCLI forms the pipelining between inputs and outputs, akin to platforms like GitHub Actions. In this scenario, our first function returns a generated paragraph about a given topic, "Brief History of Pittsburgh", which then feeds into the next task as the input for a sequential chain execution, whose result is forwarded to the final task. + +The `genBySection` function tagged here is provided by the Guest and informs the LLM that it should write paragraphs in the role of a journalist writing about cities. + +``` rust +fn gen_by_section(input: String, topic: String) -> String { + log(Level::Info, "input", &input); + log(Level::Info, "topic", &topic); + + let system = "You are a journalist writing about cities."; + + let step = format!( + "Given an article already containing {}, copy it and then write more about the topic {} in a new paragraph: \n{{text}}", + input, + topic + ); + prompt_seq(system, &input, &step, None) +} +``` + +Here's an example response provided by the model from the executed workflow: + +> Pittsburgh is often referred to as the "Steel City" due to its rich history of steel production. But there's much more to this city than just steel. This article will delve into the brief history of Pittsburgh, from its early days as a frontier town to its current status as a thriving metropolis. + +> The city was founded in 1758 by General John Forbes, who led a force of British soldiers and settlers to take control of the area from the French. The name "Pittsburg" was chosen in honor of British Prime Minister William Pitt, who supported the American colonists during the French and Indian War. In the late 1700s, the city became an important center for the production of iron and steel, thanks to its access to the Monongahela River and the surrounding mountains. + +> During the 19th century, Pittsburgh underwent rapid industrialization and growth. The city's proximity to the Allegheny River and Lake Erie made it a key location for the Pennsylvania Railroad and other transportation lines, further boosting its economy. Steel production became a major industry in the late 1800s, with companies like Carnegie Steel and Jones & Laughlin Steel dominating the market. This period of growth led to the development of many iconic Pittsburgh landmarks, including the Smithfield United Presbyterian Church and the Cathedral of Learning at the University of Pittsburgh. + +> In the mid-20th century, Pittsburgh's steel industry began to decline due to increased competition from other countries and economic shifts. However, the city has adapted and diversified its economy over the years, with a focus on healthcare, education, technology, and the arts. Today, Pittsburgh is known for its vibrant cultural scene, world-class universities, and stunning natural beauty, making it a great place to live, work, and visit. + +### MapReduce + +```console +curl localhost:3000/run --json @map_reduce.json +``` + +To more applicability encode the [MapReduce][map-reduce] example from the [Crowdforge][crowdforge] paper, I implemented a `prompt_chain` Wasm/Wasi function registered on the Host that takes in a system prompt (e.g. "You are journalist writing about cities."), an input (e.g. an ongoing article), a map step prompt with a `{{text}}` placeholder that is filled in, a reduce step, which folds over (combines) the generated text(s) from the map step, and then the optional LLaMA model stored as a [`gguf`][gguf]. If the optional model path is not provided, the Host will fall back to the default `Meta-Llama-3-8B-Instruct.Q4_0.gguf` model. + +```rust +async fn prompt_chain( + &mut self, + system: String, + input: String, + map: String, + reduce: String, + model: Option, +) -> wasmtime::Result { + let opts = options!( + Model: options::ModelRef::from_path(model.unwrap_or(PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf").display().to_string())), + ModelType: "llama", + MaxContextSize: 4096_usize, + NThreads: 4_usize, + MaxTokens: 2048_usize, + MaxBatchSize: 4096_usize, + TopK: 40_i32, + TopP: 0.95, + TfsZ: 1.0, + TypicalP: 1.0, + Temperature: 0.8, + RepeatPenalty: 1.1, + RepeatPenaltyLastN: 64_usize, + FrequencyPenalty: 0.0, + PresencePenalty: 0.0, + Mirostat: 0_i32, + MirostatTau: 5.0, + MirostatEta: 0.1, + PenalizeNl: true, + StopSequence: vec!["\n\n".to_string()] + ); + + let exec = executor!(llama, opts.clone())?; + let map_prompt = Step::for_prompt_template(prompt!(&system, &map)); + let reduce_prompt = Step::for_prompt_template(prompt!(&system, &reduce)); + let chain = Chain::new(map_prompt, reduce_prompt); + let docs = vec![Parameters::new_with_text(input)]; + let res = chain.run(docs, Parameters::new(), &exec).await?; + match res.to_immediate().await { + Ok(res) => Ok(res.get_content().to_string()), + Err(e) => Err(e.into()), + } +} +``` + +I won't dive into all the specifics here, but you can see some of the optional parameters available when running this flow within an AI workflow chain. This information can be completely controlled by the Host (as it is here) or passed in as arguments for Guest functions to leverage for exploration and trial and error'ing. Controlling this separation is part of why we should want private and local models. + +Here's how the Guest module can call the `prompt_chain` function on the Host for generating article content: + +```rust +fn gen_map_reduce(input: String) -> String { + log(Level::Info, "input", &input); + let system = "You are journalist writing about cities."; + + let map_step = "Given an article already on a topic, add a new fact about the city, tell us what that fact is, and write a sentence about it:\n{{text}}"; + let reduce_step = "Summarize facts of the city into a paragraph:\n{{text}}"; + prompt_chain(&system, &input, map_step, reduce_step, None) +} +``` + +And, here's an example response provided by the model from the executed workflow: + +> Sentence: + +> Additionally, the city hosts the annual Three Rivers Arts Festival, showcasing local artists' work and attracting thousands of attendees each year. Sentence: Pittsburgh is also home to over 446 bridges, earning it the nickname "City of Bridges." Sentence: Its rich history and cultural attractions make Pittsburgh a popular destination for both locals and tourists alike. + +> Final paragraph: + +> Pittsburgh, a city with a strong sense of identity, is more than just steel production and industrial heritage. This iconic city is also home to the famous Andy Warhol Museum, established in 1994, which offers visitors an unparalleled look into the life and artistic vision of the legendary artist. In addition to its cultural attractions, Pittsburgh hosts the annual Three Rivers Arts Festival, a celebration of local artistry that draws thousands each year. Furthermore, the city's impressive record of over 446 bridges has earned it the nickname "City of Bridges." With its rich history and cultural offerings, Pittsburgh is a beloved destination for both locals and visitors seeking to experience the best of this remarkable city. Overall, Pittsburgh's unique blend of artistic expression, industrial history, and stunning architecture makes it a must-see place that leaves a lasting impression on those who visit. + +### Idempotent Replay + +Typically when relaying questions and prompts to LLM applications, you'll get varied responses and answers even when you don't change the input text. + +When a Homestar node receives a workflow containing a task that has already been executed, either with its index (key) and output (value) stored locally (most optimal) or retrieved from a shared DHT (Distributed Hash Table) over the network. In that case, it can replay that task without re-execution or having to fetch the Wasm module from its source. This replayability is akin to [durable function/execution][durable-fn] frameworks like [Cadence][cadence] and [Temporal][temporal] but happens over a decentralized mesh instead of a centralized infrastructure. + +So, what does that mean for us? If we provide the same arguments to our Guest Wasm functions, Homestar will return cached responses instantly, which saves us both time and resources, as waiting for large prompt responses when using models, even *smaller* ones like LLaMA 3 trained with 8 billion parameters, can be somewhat latent on devices with limited GPU and memory (like my personal computer). + +Is there a way to always provide *new* hallucinations from the Host model? Yes, there is! We can use the `--debug` flag when we start EveryCli to force re-execution of the tasks in our workflow. + +```console +./every-cli/cli.js dev ../example-llm-workflows/llm.wasm --debug +``` + +## Foundations for Privacy + +The learning goals of this project were to experiment with working with LLMs locally on hosts where the training data and tuning of a model remains private and only derived information from prompt-based AI chains can be shared with other users/peers for consumption, allowing for working with AI computation in ways not tied to any specific vendor or large cloud provider. Essentially, this showcases a positive avenue for decentralized, user-controlled AI-oriented computation that's everything that [IEEE Spectrum's Open-Source AI Is Uniquely Dangerous][ieee] isn't. + +Localized, open-source LLM models for home and on-prem use cases are growing in popularity, even leading to the creation of a [LocalLLaMA reddit][reddit-post] community! We've seen how [GDPR][gdpr] has increased the need for companies to be more careful around [PII management and data privacy isolation across regions and region-compliance laws][so-privacy]. + +Self-hosted, privately managed model deployments hit on many of the privacy and security modules taught in our course. Incorporating ways for users to chain LLM steps together while controlling what inference gets exhibited without the infrastructure concerns or data risks typically associated with external cloud services, presents a unique opportunity to democratize AI capabilities. By ensuring that users can interact with and execute complex AI +workflows with ease, the project aims to bridge the gap between advanced AI technologies and those with some software development background. This approach not only aligns with the course's focus on privacy and security, but also empowers users by providing them with tools to leverage AI in a secure, private, and user-friendly manner. + +Regarding further alignment with our class, the rise of LLMs has made the transparency of existence, operation, and first-class design for transparency urgent ethical matters as more and more companies move to gather data and push AI agents into our everyday lives. OpenAI's chat interface (to GPT) spawned much of the burgeoning popularity of LLMs, but with OpenAI, we know the CEO of the company is linked to another company (Worldcoin) that has [shady digital-identity practices][iris-scanning]. + +**Trust** has come up a lot in our course. The future of HCI/AI is deeply entangled in what of our data is private and how we can control what becomes public. This project demonstrates tooling that makes this line distinct and controllable. + +### Peer Sharing with Only 1 LLM + +![3 connected notes, but only 1 host with bindings for LLaMA 3](./llama_host.png) + +In the [video walkthrough][video] we showcase how one node built without LLM features and LLaMA bindings can receive receipts for a workflow run on a node it's connected with over the LAN (local-area network) that was compiled with the features and bindings. Imagine finely tuning a LLaMA model on custom healthcare data (which is doable) and wanting certain users to run prompt chains against it. You wouldn't want the users of the platform to access the model itself, but you'd like for them to interact with it with clear separation. These on-prem models are becoming more and more important for companies, countries, and the people in between them. + +### Running LLM Tasks Offline + +The [video walkthrough][video] illustrates the power of executing AI-focused computational workflows on local devices, particularly in offline scenarios. This can also be extended to scenarios where one wants to intentionally restrict online connectivity. All of this not only underscores the versatility and robustness of decentralized AI frameworks but also highlights the broader implications of embracing [local-first software][wired] principles within the realm of HCI/AI. By prioritizing local execution and data processing, users gain greater control over their computing environments, ensuring privacy, and security, and reducing reliance on external infrastructures. + +This paradigm shift towards local-first approaches resonates deeply within ongoing HCI/AI research, emphasizing user empowerment, data sovereignty, and the preservation of privacy in an increasingly interconnected landscape. As the boundaries between digital and physical realms blur, embracing local-first methodologies becomes pivotal in shaping a more transparent, accountable, and user-centric future for AI-driven technologies. + +## Outputs and Discussion + +## What is Homestar in a Nutshell? + +[Homestar][homestar] is a runtime engine for executing WebAssembly (Wasm) based workflows. It implements the [Interplanetary Virtual Machine (IPVM)][ipvm] in-progress protocol specification centered around workflows and invocations. These workflows contain content-addressed tasks—which means they're uniquely identified by their content rather than by their location—composed of [Wasm components][wasm-components] with runnable functions that can be scheduled and executed by any Homestar peer throughout the network. Pure Wasm Workflows can be executed deterministically while side-effecting operations like random and HTTP network requests are explicitly managed. + +The outputs of these functions are indexed by their instruction, composed of the Wasm module’s content identifier (CID), function name, and function arguments (and with an optional nonce if you want to guarantee uniqueness), and stored and tracked as compute receipts. After execution, these receipts are pushed to the shared network’s distributed hash table (DHT) and gossiped to connected peers, whether they're connected directly or discovered through mDNS or a rendezvous protocol for peer discovery. + +### Homestar + LLaMA + AI chains + +For this project, I forked and extended the Homestar codebase (of which I'm a lead developer) to + +* Provide feature-flagged (for cross-platform compilation) local-only LLaMA model bindings with tuned parameters. +* Integrate general-usage Wasm/Wasi Host LLM-chain prompt functions (with typed interfaces) for execution by Guest code written in any language that can be compiled to Wasm. +* Provide Guest example code (written in Rust) and workflows inspired by our class homework and the [crowdforge] MapReduce example (for the demo!). + +[ai-chains]: https://arxiv.org/abs/2110.01691 +[blog]: https://fission.codes/blog/functions-everywhere-only-once/ +[cadence]: https://cadenceworkflow.io/ +[chain-of-thought-paper]: https://arxiv.org/pdf/2201.11903 +[crowdforge]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/39980.pdf +[durable-fn]: https://angelhof.github.io/files/papers/durable-functions-2021-oopsla.pdf +[every-cli]: https://docs.everywhere.computer/everycli/ +[everywhere-computer]: https://docs.everywhere.computer +[gemini]: https://arxiv.org/pdf/2312.11805 +[gdpr]: https://gdpr-info.eu/ +[gguf]: https://github.com/ggerganov/ggml/blob/master/docs/gguf.md +[guest]: https://wasmbyexample.dev/examples/wasi-introduction/wasi-introduction.all.en-us.html +[homestar]: https://github.com/ipvm-wg/homestar +[host]:https://wasmbyexample.dev/examples/wasi-introduction/wasi-introduction.all.en-us.html +[ieee]: https://spectrum.ieee.org/open-source-ai-2666932122 +[instruction-llama]: https://huggingface.co/QuantFactory/Meta-Llama-3-8B-Instruct-GGUF +[ipfs]: https://ipfs.tech/ +[ipvm]: https://github.com/ipvm-wg +[iris-scanning]: https://www.reuters.com/technology/scrutiny-iris-scanning-crypto-project-worldcoin-grows-2023-09-01/ +[llama-3]: https://llama.meta.com/llama3/ +[llama.cpp]: https://github.com/ggerganov/llama.cpp +[map-reduce]: https://js.langchain.com/docs/modules/chains/document/map_reduce +[reddit-post]: https://www.reddit.com/r/LocalLLaMA/comments/1cc30mx/i_believe_small_local_llms_could_solve_privacy/ +[so-privacy]: https://stackoverflow.blog/2023/10/23/privacy-in-the-age-of-generative-ai/ +[tar-link]: TODO +[temporal]: https://temporal.io/ +[video]: TODO +[wasm]: https://webassembly.org/ +[wasm-components]: https://component-model.bytecodealliance.org/ +[wasi]: https://wasi.dev/ +[wired]: https://www.wired.com/story/the-cloud-is-a-prison-can-the-local-first-software-movement-set-us-free/ diff --git a/example-llm-workflows/__testkey_ed25519.pem b/example-llm-workflows/__testkey_ed25519.pem new file mode 100644 index 00000000..fb6cc573 --- /dev/null +++ b/example-llm-workflows/__testkey_ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= +-----END PRIVATE KEY----- diff --git a/example-llm-workflows/chain1.json b/example-llm-workflows/chain1.json new file mode 100644 index 00000000..14405af6 --- /dev/null +++ b/example-llm-workflows/chain1.json @@ -0,0 +1,37 @@ +{ + "tasks": [ + { + "run": { + "name": "genParagraph", + "input": { + "args": ["Brief History of Pittsburgh"], + "func": "gen" + } + } + }, + { + "run": { + "name": "genFrom1", + "input": { + "args": [ + "{{needs.genParagraph.output}}", + "Brief History of Pittsburgh" + ], + "func": "genBySection" + } + } + }, + { + "run": { + "name": "genFrom2", + "input": { + "args": [ + "{{needs.genFrom1.output}}", + "Brief History of Pittsburgh" + ], + "func": "genBySection" + } + } + } + ] +} diff --git a/example-llm-workflows/homestar_config.toml b/example-llm-workflows/homestar_config.toml new file mode 100644 index 00000000..c0608c9a --- /dev/null +++ b/example-llm-workflows/homestar_config.toml @@ -0,0 +1,14 @@ +[node] +[node.network.metrics] +port = 4020 + +[node.network.rpc] +port = 9820 + +[node.network.webserver] +host = "127.0.0.1" +port = 8020 + +[node.network.ipfs] +host = "127.0.0.1" +port = 5001 diff --git a/example-llm-workflows/homestar_config_peer.toml b/example-llm-workflows/homestar_config_peer.toml new file mode 100644 index 00000000..d665977f --- /dev/null +++ b/example-llm-workflows/homestar_config_peer.toml @@ -0,0 +1,17 @@ +[node] +[node.network.keypair_config] +existing = { key_type = "ed25519", path = "./__testkey_ed25519.pem" } + +[node.network.metrics] +port = 4021 + +[node.network.rpc] +port = 9821 + +[node.network.webserver] +host = "127.0.0.1" +port = 8021 + +[node.network.ipfs] +host = "127.0.0.1" +port = 5001 diff --git a/example-llm-workflows/llama_host.png b/example-llm-workflows/llama_host.png new file mode 100644 index 00000000..7cf09dd7 Binary files /dev/null and b/example-llm-workflows/llama_host.png differ diff --git a/example-llm-workflows/llm.wasm b/example-llm-workflows/llm.wasm new file mode 100644 index 00000000..1bf91d61 Binary files /dev/null and b/example-llm-workflows/llm.wasm differ diff --git a/example-llm-workflows/map_reduce.json b/example-llm-workflows/map_reduce.json new file mode 100644 index 00000000..74d0b5c1 --- /dev/null +++ b/example-llm-workflows/map_reduce.json @@ -0,0 +1,22 @@ +{ + "tasks": [ + { + "run": { + "name": "genParagraph", + "input": { + "args": ["Brief History of Pittsburgh"], + "func": "gen" + } + } + }, + { + "run": { + "name": "genMR", + "input": { + "args": ["{{needs.genParagraph.output}}"], + "func": "genMapReduce" + } + } + } + ] +} diff --git a/example-llm-workflows/simple.json b/example-llm-workflows/simple.json new file mode 100644 index 00000000..d6b62569 --- /dev/null +++ b/example-llm-workflows/simple.json @@ -0,0 +1,13 @@ +{ + "tasks": [ + { + "run": { + "name": "genParagraph", + "input": { + "args": ["Attractions in Pittsburgh"], + "func": "gen" + } + } + } + ] +} diff --git a/flake.lock b/flake.lock index e8503aaa..db38963d 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "advisory-db": { "flake": false, "locked": { - "lastModified": 1710515894, - "narHash": "sha256-tmQ9TMCb2jZY3jYdf53qIberkYV3dnUzdAYYK/NB+No=", + "lastModified": 1714183630, + "narHash": "sha256-1BVft7ggSN2XXFeXQjazU3jN9wVECd9qp2mZx/8GDMk=", "owner": "rustsec", "repo": "advisory-db", - "rev": "369d98c1b95b7b56d0859605916d7b81a7d1f1c4", + "rev": "35e7459a331d3e0c585e56dabd03006b9b354088", "type": "github" }, "original": { @@ -21,11 +21,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1711299236, - "narHash": "sha256-6/JsyozOMKN8LUGqWMopKTSiK8N79T8Q+hcxu2KkTXg=", + "lastModified": 1713979152, + "narHash": "sha256-apdecPuh8SOQnkEET/kW/UcfjCRb8JbV5BKjoH+DcP4=", "owner": "ipetkov", "repo": "crane", - "rev": "880573f80d09e18a11713f402b9e6172a085449f", + "rev": "a5eca68a2cf11adb32787fc141cddd29ac8eb79c", "type": "github" }, "original": { @@ -79,11 +79,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1711520562, - "narHash": "sha256-rVE+5nzGarXDYoXPcySg+/NpkPZu53H3YhtE1nElUhg=", + "lastModified": 1714544767, + "narHash": "sha256-kF1bX+YFMedf1g0PAJYwGUkzh22JmULtj8Rm4IXAQKs=", "owner": "nix-community", "repo": "fenix", - "rev": "21cf09a640b2a0b8f805aa767f42916c9579776b", + "rev": "73124e1356bde9411b163d636b39fe4804b7ca45", "type": "github" }, "original": { @@ -101,11 +101,11 @@ "rust-analyzer-src": "rust-analyzer-src_2" }, "locked": { - "lastModified": 1711261304, - "narHash": "sha256-YVVuVVw2cYxEGron+9DnEwHzgSv59TIeLgBDAstpCtg=", + "lastModified": 1714285404, + "narHash": "sha256-MmoQIO+KRiH3UnH0myAp2Fgi84rmpROMfw9VIbqrjHA=", "owner": "nix-community", "repo": "fenix", - "rev": "2f420b2cc2dc0c91031a4a5625a2217f8e49657b", + "rev": "94be183087845937b0fd77281c37d0796572b899", "type": "github" }, "original": { @@ -224,7 +224,7 @@ "macos-sdk": { "flake": false, "locked": { - "lastModified": 1693509154, + "lastModified": 1694769349, "narHash": "sha256-TEvVJy+NMPyzgWSk/6S29ZMQR+ICFxSdS3tw247uhFc=", "type": "tarball", "url": "https://github.com/roblabla/MacOSX-SDKs/releases/download/macosx14.0/MacOSX14.0.sdk.tar.xz" @@ -358,11 +358,11 @@ "rust-overlay": "rust-overlay_2" }, "locked": { - "lastModified": 1711389924, - "narHash": "sha256-AmtdpS4IP+h1g99bvZs+t2Gk5eIFRB4GJ3YL9wjNorI=", + "lastModified": 1714475117, + "narHash": "sha256-tXjZyjlp+fAbO9P7tVgERaB/ueOloRhZvQD3NxQlIHI=", "owner": "rvolosatovs", "repo": "nixify", - "rev": "b0530dccd1865645bcef8b318f9e27c49b762a41", + "rev": "50914aa7dba4f49a5a5c1254a6c9a948813dac0c", "type": "github" }, "original": { @@ -427,11 +427,11 @@ }, "nixlib_3": { "locked": { - "lastModified": 1711241261, - "narHash": "sha256-knrTvpl81yGFHIpm1SsLDApe0thFkw1cl3ISAMPmP/0=", + "lastModified": 1714265296, + "narHash": "sha256-jVnKiCOoFulPT1zDdA4jfG/lnEnngdth5CT6rVDXEJ4=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "b2a1eeef8c185f6bd27432b053ff09d773244cbc", + "rev": "ade4fb7bbf04cd52bc1705734d5dc67755d77ec9", "type": "github" }, "original": { @@ -442,11 +442,11 @@ }, "nixos-unstable": { "locked": { - "lastModified": 1711483428, - "narHash": "sha256-QFbfFvHiGW/jDwM1pvcqKKwOnCXywUgPoGxdZDmlwKI=", + "lastModified": 1714556388, + "narHash": "sha256-Mxp8hX2gH30HoIQ+INvWY5P0sKBli8BmiGzZcKVD6co=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fd84c1ff8937685294342c57a656a7066800d01c", + "rev": "c10b1bb002e7414ecf7c9d9e5f7ebaeaeae32c92", "type": "github" }, "original": { @@ -457,11 +457,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1710827359, - "narHash": "sha256-/KY8hffTh9SN/tTcDn/FrEiYwTXnU8NKnr4D7/stmmA=", + "lastModified": 1713349283, + "narHash": "sha256-2bjFu3+1zPWZPPGqF+7rumTvEwmdBHBhjPva/AMSruQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5710127d9693421e78cca4f74fac2db6d67162b1", + "rev": "2e359fb3162c85095409071d131e08252d91a14f", "type": "github" }, "original": { @@ -473,11 +473,11 @@ }, "nixpkgs-darwin": { "locked": { - "lastModified": 1711217620, - "narHash": "sha256-GGHuUQoSsGN/BJYEu7fY+Qzipwq3NqChjlUtT3eg33s=", + "lastModified": 1714324733, + "narHash": "sha256-RrLThjnSTeKQqgLO/4nZFkMz/Fx1b++d8QIUrCgxgVo=", "owner": "nixos", "repo": "nixpkgs", - "rev": "ebbe1c1299faf63fffdf1e2a6ebfe43c56b2e691", + "rev": "06347bf1e5b9b540c5159230e1bc189fe342b0ae", "type": "github" }, "original": { @@ -505,11 +505,11 @@ }, "nixpkgs-nixos": { "locked": { - "lastModified": 1711124224, - "narHash": "sha256-l0zlN/3CiodvWDtfBOVxeTwYSRz93muVbXWSpaMjXxM=", + "lastModified": 1714272655, + "narHash": "sha256-3/ghIWCve93ngkx5eNPdHIKJP/pMzSr5Wc4rNKE1wOc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "56528ee42526794d413d6f244648aaee4a7b56c0", + "rev": "12430e43bd9b81a6b4e79e64f87c624ade701eaf", "type": "github" }, "original": { @@ -537,11 +537,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1711460390, - "narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=", + "lastModified": 1714409183, + "narHash": "sha256-Wacm/DrzLD7mjFGnSxxyGkJgg2unU/dNdNgdngBH+RU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "44733514b72e732bd49f5511bd0203dea9b9a434", + "rev": "576ecd43d3b864966b4423a853412d6177775e8b", "type": "github" }, "original": { @@ -565,11 +565,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1711443624, - "narHash": "sha256-K5aLPjCF3A9ArczT5X/CKo1bfQEwGK5CsJ3eYHIjEnk=", + "lastModified": 1714501997, + "narHash": "sha256-g31zfxwUFzkPgX0Q8sZLcrqGmOxwjEZ/iqJjNx4fEGo=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "4b33850c39049047e2deb4269b60bd8330b68031", + "rev": "49e502b277a8126a9ad10c802d1aaa3ef1a280ef", "type": "github" }, "original": { @@ -582,11 +582,11 @@ "rust-analyzer-src_2": { "flake": false, "locked": { - "lastModified": 1711181358, - "narHash": "sha256-bK1r+UTqTltHrQCv49TnBUsTqlRs13t7q//l+onVqqc=", + "lastModified": 1714217560, + "narHash": "sha256-zttBYGaoHpZfqWHQ8OI5f9OkGHCHb8tDBMySwsYNa2U=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "e265e3d5189513c42d037999d41a696369af9388", + "rev": "f216be4a0746142c5f30835b254871256a7637b8", "type": "github" }, "original": { @@ -654,11 +654,11 @@ ] }, "locked": { - "lastModified": 1711246447, - "narHash": "sha256-g9TOluObcOEKewFo2fR4cn51Y/jSKhRRo4QZckHLop0=", + "lastModified": 1714356894, + "narHash": "sha256-W6Mss7AG6bnFT1BqRApHXvLXBrFOu7V0+EUe9iML30s=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "dcc802a6ec4e9cc6a1c8c393327f0c42666f22e4", + "rev": "d9b44509b4064f0a3fc9c7c92a603861f52fbedc", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0bd3cf56..8b71f22d 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,12 @@ flake-utils.lib.eachDefaultSystem ( system: let overlays = [fenix.overlays.default wit-deps.overlays.default]; - pkgs = import nixpkgs {inherit system overlays;}; + pkgs = import nixpkgs { + inherit system overlays; + config = { + allowUnfree = true; + }; + }; unstable = import nixos-unstable {inherit system overlays;}; file-toolchain = fenix.packages.${system}.fromToolchainFile { @@ -69,6 +74,7 @@ rustc = rust-toolchain; }; + llvmPackages = pkgs.llvmPackages_17; nightly-rustfmt = (fenix.packages.${system}.toolchainOf { channel = "nightly"; @@ -77,6 +83,7 @@ # sha256 = pkgs.lib.fakeSha256; }) .rustfmt; + format-pkgs = with pkgs; [ nixpkgs-fmt alejandra @@ -99,7 +106,7 @@ rustup tokio-console twiggy - unstable.cargo-component + #unstable.cargo-component unstable.wasm-tools # TODO: Return to this # unstable.warg @@ -326,6 +333,7 @@ unstable.nodePackages.pnpm action-validator kubo + cmake self.packages.${system}.irust ] ++ format-pkgs @@ -339,6 +347,7 @@ ]; NIX_PATH = "nixpkgs=" + pkgs.path; RUST_BACKTRACE = 1; + LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib"; shellHook = '' diff --git a/homestar-functions/llm/Cargo.toml b/homestar-functions/llm/Cargo.toml new file mode 100644 index 00000000..4b907fa3 --- /dev/null +++ b/homestar-functions/llm/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "homestar-functions-llm" +publish = false +version = "0.1.0" +edition = { workspace = true } +rust-version = { workspace = true } + +[dependencies] +wit-bindgen-rt = "0.21.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "component:homestar-functions-llm" + +[package.metadata.component.target.dependencies] +"wasi:logging" = { path = "wit/deps/logging" } +"homestar:host" = { path = "wit/deps/helpers" } diff --git a/homestar-functions/llm/src/bindings.rs b/homestar-functions/llm/src/bindings.rs new file mode 100644 index 00000000..6178588b --- /dev/null +++ b/homestar-functions/llm/src/bindings.rs @@ -0,0 +1,602 @@ +// Generated by `wit-bindgen` 0.21.0. DO NOT EDIT! +// Options used: +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_gen_cabi(arg0: *mut u8, arg1: usize) -> *mut u8 { + let len0 = arg1; + let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + let result1 = T::gen(_rt::string_lift(bytes0)); + let ptr2 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec3 = (result1.into_bytes()).into_boxed_slice(); + let ptr3 = vec3.as_ptr().cast::(); + let len3 = vec3.len(); + ::core::mem::forget(vec3); + *ptr2.add(4).cast::() = len3; + *ptr2.add(0).cast::<*mut u8>() = ptr3.cast_mut(); + ptr2 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn __post_return_gen(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_gen_by_section_cabi( + arg0: *mut u8, + arg1: usize, + arg2: *mut u8, + arg3: usize, +) -> *mut u8 { + let len0 = arg1; + let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + let len1 = arg3; + let bytes1 = _rt::Vec::from_raw_parts(arg2.cast(), len1, len1); + let result2 = T::gen_by_section(_rt::string_lift(bytes0), _rt::string_lift(bytes1)); + let ptr3 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec4 = (result2.into_bytes()).into_boxed_slice(); + let ptr4 = vec4.as_ptr().cast::(); + let len4 = vec4.len(); + ::core::mem::forget(vec4); + *ptr3.add(4).cast::() = len4; + *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut(); + ptr3 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn __post_return_gen_by_section(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_gen_map_reduce_cabi(arg0: *mut u8, arg1: usize) -> *mut u8 { + let len0 = arg1; + let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + let result1 = T::gen_map_reduce(_rt::string_lift(bytes0)); + let ptr2 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec3 = (result1.into_bytes()).into_boxed_slice(); + let ptr3 = vec3.as_ptr().cast::(); + let len3 = vec3.len(); + ::core::mem::forget(vec3); + *ptr2.add(4).cast::() = len3; + *ptr2.add(0).cast::<*mut u8>() = ptr3.cast_mut(); + ptr2 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn __post_return_gen_map_reduce(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::(); + _rt::cabi_dealloc(l0, l1, 1); +} +pub trait Guest { + fn gen(topic: _rt::String) -> _rt::String; + fn gen_by_section(input: _rt::String, topic: _rt::String) -> _rt::String; + fn gen_map_reduce(input: _rt::String) -> _rt::String; +} +#[doc(hidden)] + +macro_rules! __export_world_llm_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + #[export_name = "gen"] + unsafe extern "C" fn export_gen(arg0: *mut u8,arg1: usize,) -> *mut u8 { + $($path_to_types)*::_export_gen_cabi::<$ty>(arg0, arg1) + } + #[export_name = "cabi_post_gen"] + unsafe extern "C" fn _post_return_gen(arg0: *mut u8,) { + $($path_to_types)*::__post_return_gen::<$ty>(arg0) + } + #[export_name = "gen-by-section"] + unsafe extern "C" fn export_gen_by_section(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 { + $($path_to_types)*::_export_gen_by_section_cabi::<$ty>(arg0, arg1, arg2, arg3) + } + #[export_name = "cabi_post_gen-by-section"] + unsafe extern "C" fn _post_return_gen_by_section(arg0: *mut u8,) { + $($path_to_types)*::__post_return_gen_by_section::<$ty>(arg0) + } + #[export_name = "gen-map-reduce"] + unsafe extern "C" fn export_gen_map_reduce(arg0: *mut u8,arg1: usize,) -> *mut u8 { + $($path_to_types)*::_export_gen_map_reduce_cabi::<$ty>(arg0, arg1) + } + #[export_name = "cabi_post_gen-map-reduce"] + unsafe extern "C" fn _post_return_gen_map_reduce(arg0: *mut u8,) { + $($path_to_types)*::__post_return_gen_map_reduce::<$ty>(arg0) + } + };); +} +#[doc(hidden)] +pub(crate) use __export_world_llm_cabi; +#[repr(align(4))] +struct _RetArea([::core::mem::MaybeUninit; 8]); +static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 8]); +pub mod homestar { + pub mod host { + #[allow(clippy::all)] + pub mod helpers { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Time { + pub seconds: u64, + pub milliseconds: u32, + pub nanoseconds: u32, + } + impl ::core::fmt::Debug for Time { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_struct("Time") + .field("seconds", &self.seconds) + .field("milliseconds", &self.milliseconds) + .field("nanoseconds", &self.nanoseconds) + .finish() + } + } + #[allow(unused_unsafe, clippy::all)] + /// Get current time in sub-seconds. + pub fn get_current_time() -> Time { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "homestar:host/helpers@0.1.1")] + extern "C" { + #[link_name = "get-current-time"] + fn wit_import(_: *mut u8); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8) { + unreachable!() + } + wit_import(ptr0); + let l1 = *ptr0.add(0).cast::(); + let l2 = *ptr0.add(8).cast::(); + let l3 = *ptr0.add(12).cast::(); + Time { + seconds: l1 as u64, + milliseconds: l2 as u32, + nanoseconds: l3 as u32, + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// Basic `print` helper. + pub fn print(msg: &str) { + unsafe { + let vec0 = msg; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "homestar:host/helpers@0.1.1")] + extern "C" { + #[link_name = "print"] + fn wit_import(_: *mut u8, _: usize); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8, _: usize) { + unreachable!() + } + wit_import(ptr0.cast_mut(), len0); + } + } + } + + #[allow(clippy::all)] + pub mod chain { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + pub fn prompt_with(input: &str, model: Option<&str>) -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = input; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let (result2_0, result2_1, result2_2) = match model { + Some(e) => { + let vec1 = e; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + + (1i32, ptr1.cast_mut(), len1) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let ptr3 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "homestar:host/chain@0.1.1")] + extern "C" { + #[link_name = "prompt-with"] + fn wit_import( + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: *mut u8, _: usize, _: i32, _: *mut u8, _: usize, _: *mut u8) { + unreachable!() + } + wit_import(ptr0.cast_mut(), len0, result2_0, result2_1, result2_2, ptr3); + let l4 = *ptr3.add(0).cast::<*mut u8>(); + let l5 = *ptr3.add(4).cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts(l4.cast(), len6, len6); + _rt::string_lift(bytes6) + } + } + #[allow(unused_unsafe, clippy::all)] + pub fn prompt_seq( + system: &str, + input: &str, + next: &str, + model: Option<&str>, + ) -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = system; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = input; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let vec2 = next; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + let (result4_0, result4_1, result4_2) = match model { + Some(e) => { + let vec3 = e; + let ptr3 = vec3.as_ptr().cast::(); + let len3 = vec3.len(); + + (1i32, ptr3.cast_mut(), len3) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let ptr5 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "homestar:host/chain@0.1.1")] + extern "C" { + #[link_name = "prompt-seq"] + fn wit_import( + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import( + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import( + ptr0.cast_mut(), + len0, + ptr1.cast_mut(), + len1, + ptr2.cast_mut(), + len2, + result4_0, + result4_1, + result4_2, + ptr5, + ); + let l6 = *ptr5.add(0).cast::<*mut u8>(); + let l7 = *ptr5.add(4).cast::(); + let len8 = l7; + let bytes8 = _rt::Vec::from_raw_parts(l6.cast(), len8, len8); + _rt::string_lift(bytes8) + } + } + #[allow(unused_unsafe, clippy::all)] + pub fn prompt_chain( + system: &str, + input: &str, + map: &str, + reduce: &str, + model: Option<&str>, + ) -> _rt::String { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = system; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = input; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let vec2 = map; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + let vec3 = reduce; + let ptr3 = vec3.as_ptr().cast::(); + let len3 = vec3.len(); + let (result5_0, result5_1, result5_2) = match model { + Some(e) => { + let vec4 = e; + let ptr4 = vec4.as_ptr().cast::(); + let len4 = vec4.len(); + + (1i32, ptr4.cast_mut(), len4) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let ptr6 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "homestar:host/chain@0.1.1")] + extern "C" { + #[link_name = "prompt-chain"] + fn wit_import( + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import( + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import( + ptr0.cast_mut(), + len0, + ptr1.cast_mut(), + len1, + ptr2.cast_mut(), + len2, + ptr3.cast_mut(), + len3, + result5_0, + result5_1, + result5_2, + ptr6, + ); + let l7 = *ptr6.add(0).cast::<*mut u8>(); + let l8 = *ptr6.add(4).cast::(); + let len9 = l8; + let bytes9 = _rt::Vec::from_raw_parts(l7.cast(), len9, len9); + _rt::string_lift(bytes9) + } + } + } + } +} +pub mod wasi { + pub mod logging { + #[allow(clippy::all)] + pub mod logging { + #[used] + #[doc(hidden)] + #[cfg(target_arch = "wasm32")] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; + /// A log level, describing a kind of message. + #[repr(u8)] + #[derive(Clone, Copy, Eq, PartialEq)] + pub enum Level { + /// Describes messages about the values of variables and the flow of + /// control within a program. + Trace, + /// Describes messages likely to be of interest to someone debugging a + /// program. + Debug, + /// Describes messages likely to be of interest to someone monitoring a + /// program. + Info, + /// Describes messages indicating hazardous situations. + Warn, + /// Describes messages indicating serious errors. + Error, + /// Describes messages indicating fatal errors. + Critical, + } + impl ::core::fmt::Debug for Level { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + match self { + Level::Trace => f.debug_tuple("Level::Trace").finish(), + Level::Debug => f.debug_tuple("Level::Debug").finish(), + Level::Info => f.debug_tuple("Level::Info").finish(), + Level::Warn => f.debug_tuple("Level::Warn").finish(), + Level::Error => f.debug_tuple("Level::Error").finish(), + Level::Critical => f.debug_tuple("Level::Critical").finish(), + } + } + } + + impl Level { + pub(crate) unsafe fn _lift(val: u8) -> Level { + if !cfg!(debug_assertions) { + return ::core::mem::transmute(val); + } + + match val { + 0 => Level::Trace, + 1 => Level::Debug, + 2 => Level::Info, + 3 => Level::Warn, + 4 => Level::Error, + 5 => Level::Critical, + + _ => panic!("invalid enum discriminant"), + } + } + } + + #[allow(unused_unsafe, clippy::all)] + /// Emit a log message. + /// + /// A log message has a `level` describing what kind of message is being + /// sent, a context, which is an uninterpreted string meant to help + /// consumers group similar messages, and a string containing the message + /// text. + pub fn log(level: Level, context: &str, message: &str) { + unsafe { + let vec0 = context; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = message; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:logging/logging")] + extern "C" { + #[link_name = "log"] + fn wit_import(_: i32, _: *mut u8, _: usize, _: *mut u8, _: usize); + } + + #[cfg(not(target_arch = "wasm32"))] + fn wit_import(_: i32, _: *mut u8, _: usize, _: *mut u8, _: usize) { + unreachable!() + } + wit_import( + level.clone() as i32, + ptr0.cast_mut(), + len0, + ptr1.cast_mut(), + len1, + ); + } + } + } + } +} +mod _rt { + pub use alloc_crate::{string::String, vec::Vec}; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + String::from_utf8_unchecked(bytes) + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); + } + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; +} + +/// Generates `#[no_mangle]` functions to export the specified type as the +/// root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! export{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// export!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] + +macro_rules! __export_llm_impl { + ($ty:ident) => (self::export!($ty with_types_in self);); + ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( + $($path_to_types_root)*::__export_world_llm_cabi!($ty with_types_in $($path_to_types_root)*); + ) +} +#[doc(inline)] +pub(crate) use __export_llm_impl as export; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.21.0:llm:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 670] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xa4\x04\x01A\x02\x01\ +A\x0c\x01B\x04\x01m\x06\x05trace\x05debug\x04info\x04warn\x05error\x08critical\x04\ +\0\x05level\x03\0\0\x01@\x03\x05level\x01\x07contexts\x07messages\x01\0\x04\0\x03\ +log\x01\x02\x03\x01\x14wasi:logging/logging\x05\0\x01B\x06\x01r\x03\x07secondsw\x0c\ +millisecondsy\x0bnanosecondsy\x04\0\x04time\x03\0\0\x01@\0\0\x01\x04\0\x10get-cu\ +rrent-time\x01\x02\x01@\x01\x03msgs\x01\0\x04\0\x05print\x01\x03\x03\x01\x1bhome\ +star:host/helpers@0.1.1\x05\x01\x01B\x07\x01ks\x01@\x02\x05inputs\x05model\0\0s\x04\ +\0\x0bprompt-with\x01\x01\x01@\x04\x06systems\x05inputs\x04nexts\x05model\0\0s\x04\ +\0\x0aprompt-seq\x01\x02\x01@\x05\x06systems\x05inputs\x03maps\x06reduces\x05mod\ +el\0\0s\x04\0\x0cprompt-chain\x01\x03\x03\x01\x19homestar:host/chain@0.1.1\x05\x02\ +\x01@\x01\x05topics\0s\x04\0\x03gen\x01\x03\x01@\x02\x05inputs\x05topics\0s\x04\0\ +\x0egen-by-section\x01\x04\x01@\x01\x05inputs\0s\x04\0\x0egen-map-reduce\x01\x05\ +\x04\x01\x19homestar:prompt/llm@0.1.0\x04\0\x0b\x09\x01\0\x03llm\x03\0\0\0G\x09p\ +roducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.201.0\x10wit-bindgen-rust\ +\x060.21.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/homestar-functions/llm/src/lib.rs b/homestar-functions/llm/src/lib.rs new file mode 100644 index 00000000..bc694dcc --- /dev/null +++ b/homestar-functions/llm/src/lib.rs @@ -0,0 +1,60 @@ +#![cfg(target_arch = "wasm32")] + +#[allow(clippy::all, dead_code)] +#[rustfmt::skip] +mod bindings; + +use bindings::{ + homestar::host::chain::{prompt_chain, prompt_seq, prompt_with}, + wasi::logging::logging::{log, Level}, + Guest, +}; +use std::path::PathBuf; + +struct Component; + +impl Guest for Component { + fn gen(topic: String) -> String { + log(Level::Info, "topic", &topic); + let input = format!( + "{} is an awesome topic. Let's write about it in a few sentences.", + topic + ); + prompt_with( + &input, + Some( + PathBuf::from(env!("CARGO_WORKSPACE_DIR")) + .join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf") + .display() + .to_string() + .as_str(), + ), + ) + } + + fn gen_by_section(input: String, topic: String) -> String { + log(Level::Info, "input", &input); + log(Level::Info, "topic", &topic); + + let system = "You are a journalist writing about cities."; + + let step = format!( + "Given an article already containing {}, copy {} again, and then write more about the topic {} in a new paragraph: \n{{text}}", + input, + input, + topic + ); + prompt_seq(system, &input, &step, None) + } + + fn gen_map_reduce(input: String) -> String { + log(Level::Info, "input", &input); + let system = "You are a journalist writing about cities."; + + let map_step = "Given an article already on a topic, add a new fact about the city, tell us what that fact is, and write a sentence about it:\n{{text}}"; + let reduce_step = "Summarize facts of the city into a well-written paragraph made up of at least 4 sentences:\n{{text}}"; + prompt_chain(&system, &input, map_step, reduce_step, None) + } +} + +bindings::export!(Component with_types_in bindings); diff --git a/homestar-functions/llm/wit/deps.lock b/homestar-functions/llm/wit/deps.lock new file mode 100644 index 00000000..f7570fa1 --- /dev/null +++ b/homestar-functions/llm/wit/deps.lock @@ -0,0 +1,9 @@ +[helpers] +path = "../../../homestar-wasm/wit" +sha256 = "92b7cb5dc7b44a7f2e6f00e8dce2dba600cf791ac3a150ec360b1a566065b099" +sha512 = "caafd9ed3b8551fd8c5943366f268b2673b88691d34a9c022eb418d247f998ed134017fb33f97bc47708eaf463b430d43ece8b519974ff1fb742f12aa3d89336" + +[logging] +url = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz" +sha256 = "9676b482485bb0fd2751a390374c1108865a096b7037f4b5dbe524f066bfb06e" +sha512 = "30a621a6d48a0175e8047c062e618523a85f69c45a7c31918da2b888f7527fce1aca67fa132552222725d0f6cdcaed95be7f16c28488d9468c0fad00cb7450b9" diff --git a/homestar-functions/llm/wit/deps.toml b/homestar-functions/llm/wit/deps.toml new file mode 100644 index 00000000..c2f266bb --- /dev/null +++ b/homestar-functions/llm/wit/deps.toml @@ -0,0 +1,2 @@ +helpers = "../../../homestar-wasm/wit" +logging = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz" diff --git a/homestar-functions/llm/wit/deps/helpers/helpers.wit b/homestar-functions/llm/wit/deps/helpers/helpers.wit new file mode 100644 index 00000000..f110e838 --- /dev/null +++ b/homestar-functions/llm/wit/deps/helpers/helpers.wit @@ -0,0 +1,28 @@ +package homestar:host@0.1.1; + +/// Basic helpers interface. +interface helpers { + record time { + seconds: u64, + milliseconds: u32, + nanoseconds: u32, + } + + /// Get current time in sub-seconds. + get-current-time: func() -> time; + /// Basic `print` helper. + print: func(msg: string); +} + +interface chain { + prompt-with: func(input: string, model: option) -> string; + prompt-seq: func(system: string, input: string, next: string, model: option) -> string; + prompt-chain: func(system: string, input: string, map: string, reduce: string, model: option) -> string; +} + +world imports { + /// https://github.com/WebAssembly/wasi-logging + import wasi:logging/logging; + import helpers; + import chain; +} diff --git a/homestar-functions/llm/wit/deps/logging/logging.wit b/homestar-functions/llm/wit/deps/logging/logging.wit new file mode 100644 index 00000000..8c0bdf82 --- /dev/null +++ b/homestar-functions/llm/wit/deps/logging/logging.wit @@ -0,0 +1,35 @@ +/// WASI Logging is a logging API intended to let users emit log messages with +/// simple priority levels and context values. +interface logging { + /// A log level, describing a kind of message. + enum level { + /// Describes messages about the values of variables and the flow of + /// control within a program. + trace, + + /// Describes messages likely to be of interest to someone debugging a + /// program. + debug, + + /// Describes messages likely to be of interest to someone monitoring a + /// program. + info, + + /// Describes messages indicating hazardous situations. + warn, + + /// Describes messages indicating serious errors. + error, + + /// Describes messages indicating fatal errors. + critical, + } + + /// Emit a log message. + /// + /// A log message has a `level` describing what kind of message is being + /// sent, a context, which is an uninterpreted string meant to help + /// consumers group similar messages, and a string containing the message + /// text. + log: func(level: level, context: string, message: string); +} diff --git a/homestar-functions/llm/wit/deps/logging/world.wit b/homestar-functions/llm/wit/deps/logging/world.wit new file mode 100644 index 00000000..ede62864 --- /dev/null +++ b/homestar-functions/llm/wit/deps/logging/world.wit @@ -0,0 +1,5 @@ +package wasi:logging; + +world imports { + import logging; +} diff --git a/homestar-functions/llm/wit/world.wit b/homestar-functions/llm/wit/world.wit new file mode 100644 index 00000000..d9d975de --- /dev/null +++ b/homestar-functions/llm/wit/world.wit @@ -0,0 +1,11 @@ +package homestar:prompt@0.1.0; + +world llm { + import wasi:logging/logging; + import homestar:host/helpers@0.1.1; + import homestar:host/chain@0.1.1; + + export gen: func(topic: string) -> string; + export gen-by-section: func(input: string, topic: string) -> string; + export gen-map-reduce: func(input: string) -> string; +} diff --git a/homestar-functions/subtract/src/bindings.rs b/homestar-functions/subtract/src/bindings.rs index 8267c588..ae341b0e 100644 --- a/homestar-functions/subtract/src/bindings.rs +++ b/homestar-functions/subtract/src/bindings.rs @@ -1,14 +1,18 @@ -// Generated by `wit-bindgen` 0.21.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT! // Options used: #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_subtract_cabi(arg0: f64, arg1: f64) -> f64 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); let result0 = T::subtract(arg0, arg1); _rt::as_f64(result0) } #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_subtract_int_cabi(arg0: i32, arg1: i32) -> i32 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); let result0 = T::subtract_int(arg0 as i8, arg1 as i8); _rt::as_i32(result0) } @@ -35,6 +39,11 @@ macro_rules! __export_world_subtract_cabi{ pub(crate) use __export_world_subtract_cabi; mod _rt { + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen_rt::run_ctors_once(); + } + pub fn as_f64(t: T) -> f64 { t.as_f64() } @@ -156,14 +165,14 @@ macro_rules! __export_subtract_impl { pub(crate) use __export_subtract_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.21.0:subtract:encoded world"] +#[link_section = "component-type:wit-bindgen:0.24.0:subtract:encoded world"] #[doc(hidden)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 221] = *b"\ \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07_\x01A\x02\x01A\x04\x01\ @\x02\x01au\x01bu\0u\x04\0\x08subtract\x01\0\x01@\x02\x01a~\x01b~\0~\x04\0\x0csu\ btract-int\x01\x01\x04\x01\x20homestar:subtract/subtract@0.1.1\x04\0\x0b\x0e\x01\ \0\x08subtract\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ -0.201.0\x10wit-bindgen-rust\x060.21.0"; +0.202.0\x10wit-bindgen-rust\x060.24.0"; #[inline(never)] #[doc(hidden)] diff --git a/homestar-functions/test/wit/deps.lock b/homestar-functions/test/wit/deps.lock index c52c5521..5ab6e6e3 100644 --- a/homestar-functions/test/wit/deps.lock +++ b/homestar-functions/test/wit/deps.lock @@ -1,5 +1,5 @@ [helpers] -path = "../../../homestar-wasm/wit" +path = "../../../homestar-wasm/wit/helpers" sha256 = "4d9a8c79c8f0f0b7499af0ae73d2dd002c97011775bf39836955dbd460c09a55" sha512 = "ac41234858d10646d9a4ff73461a2c2f4772510de2fd0c032cc0f45cd28cd9e49008bb77a5c79978fc4704a9c2d46fb613bdf7adfa759d8f938d35f50a275842" diff --git a/homestar-functions/test/wit/deps.toml b/homestar-functions/test/wit/deps.toml index c2f266bb..28f43527 100644 --- a/homestar-functions/test/wit/deps.toml +++ b/homestar-functions/test/wit/deps.toml @@ -1,2 +1,2 @@ -helpers = "../../../homestar-wasm/wit" +helpers = "../../../homestar-wasm/wit/helpers" logging = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz" diff --git a/homestar-runtime/Cargo.toml b/homestar-runtime/Cargo.toml index 204e3f7a..d9feab82 100644 --- a/homestar-runtime/Cargo.toml +++ b/homestar-runtime/Cargo.toml @@ -244,6 +244,7 @@ dev = ["ansi-logs", "ipfs", "monitoring", "websocket-notify"] ansi-logs = ["tracing-logfmt/ansi_logs"] console = ["dep:console-subscriber"] ipfs = ["dep:ipfs-api", "dep:ipfs-api-backend-hyper"] +llm = ["homestar-wasm/llm"] monitoring = ["dep:sysinfo"] profile = ["dep:puffin", "dep:puffin_egui"] test-utils = ["dep:proptest", "homestar-invocation/test-utils"] diff --git a/homestar-runtime/build.rs b/homestar-runtime/build.rs index 5d2e32be..e61278eb 100644 --- a/homestar-runtime/build.rs +++ b/homestar-runtime/build.rs @@ -7,6 +7,8 @@ fn main() -> Result<(), Box> { .cargo_features() .emit()?; + #[cfg(feature = "llm")] + println!("cargo::rustc-env=LLAMA_METAL=1"); println!("cargo:rerun-if-changed=build.rs"); Ok(()) diff --git a/homestar-wasm/Cargo.toml b/homestar-wasm/Cargo.toml index 03f26539..88cb661d 100644 --- a/homestar-wasm/Cargo.toml +++ b/homestar-wasm/Cargo.toml @@ -30,6 +30,8 @@ homestar-workspace-hack = { workspace = true } indexmap = { workspace = true } itertools = { workspace = true } libipld = { workspace = true } +llm-chain = { git = "https://github.com/sobelio/llm-chain.git", optional = true } +llm-chain-llama = { git = "https://github.com/sobelio/llm-chain.git", optional = true } rust_decimal = { version = "1.33", default-features = false } serde = { workspace = true } stacker = "0.1" @@ -59,6 +61,7 @@ tokio = { workspace = true } [features] default = ["wasmtime/default"] +llm = ["dep:llm-chain", "dep:llm-chain-llama"] test-utils = [] [package.metadata.docs.rs] diff --git a/homestar-wasm/src/wasmtime/host/helpers.rs b/homestar-wasm/src/wasmtime/host/helpers.rs index 5309230d..3e4dd31e 100644 --- a/homestar-wasm/src/wasmtime/host/helpers.rs +++ b/homestar-wasm/src/wasmtime/host/helpers.rs @@ -1,10 +1,18 @@ //! Helper functions that can be used in guest Wasm components. +#[cfg(feature = "llm")] +use crate::wasmtime::world::homestar::host::chain; use crate::wasmtime::{ world::{homestar::host::helpers, wasi}, State, }; use async_trait::async_trait; +#[cfg(feature = "llm")] +use llm_chain::{ + chains::map_reduce::Chain, executor, options, parameters, prompt, step::Step, Parameters, +}; +#[cfg(feature = "llm")] +use std::path::PathBuf; use std::time::Instant; use tracing::instrument; @@ -28,6 +36,129 @@ impl helpers::Host for State { } } +#[cfg(feature = "llm")] +#[async_trait] +impl chain::Host for State { + async fn prompt_with( + &mut self, + input: String, + model: Option, + ) -> wasmtime::Result { + let opts = options!( + Model: options::ModelRef::from_path(model.unwrap_or(PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf").display().to_string())), + ModelType: "llama", + MaxContextSize: 4096_usize, + NThreads: 4_usize, + MaxTokens: 2048_usize, + MaxBatchSize: 4096_usize, + TopK: 40_i32, + TopP: 0.95, + TfsZ: 1.0, + TypicalP: 1.0, + Temperature: 0.8, + RepeatPenalty: 1.1, + RepeatPenaltyLastN: 64_usize, + FrequencyPenalty: 0.0, + PresencePenalty: 0.0, + Mirostat: 0_i32, + MirostatTau: 5.0, + MirostatEta: 0.1, + PenalizeNl: true, + StopSequence: vec!["\n\n".to_string()] + ); + + let exec = executor!(llama, opts.clone())?; + let res = prompt!(input).run(¶meters!(), &exec).await?; + match res.to_immediate().await { + Ok(res) => Ok(res.to_string()), + Err(e) => Err(e.into()), + } + } + + async fn prompt_seq( + &mut self, + system: String, + input: String, + next: String, + model: Option, + ) -> wasmtime::Result { + let opts = options!( + Model: options::ModelRef::from_path(model.unwrap_or(PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf").display().to_string())), + ModelType: "llama", + MaxContextSize: 4096_usize, + NThreads: 8_usize, + MaxTokens: 2048_usize, + MaxBatchSize: 4096_usize, + TopK: 40_i32, + TopP: 0.95, + TfsZ: 1.0, + TypicalP: 1.0, + Temperature: 0.8, + RepeatPenalty: 1.1, + RepeatPenaltyLastN: 64_usize, + FrequencyPenalty: 0.0, + PresencePenalty: 0.0, + Mirostat: 0_i32, + MirostatTau: 5.0, + MirostatEta: 0.1, + PenalizeNl: true, + StopSequence: vec!["\n\n".to_string()] + ); + + let exec = executor!(llama, opts.clone())?; + let chain = Step::for_prompt_template(prompt!(&system, &next)).to_chain(); + let parameters = parameters!("text" => input); + let res = chain.run(parameters, &exec).await?; + match res.to_immediate().await { + Ok(res) => Ok(res.get_content().to_string()), + Err(e) => Err(e.into()), + } + } + + async fn prompt_chain( + &mut self, + system: String, + input: String, + map: String, + reduce: String, + model: Option, + ) -> wasmtime::Result { + let opts = options!( + Model: options::ModelRef::from_path(model.unwrap_or(PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("example-llm-workflows/models/Meta-Llama-3-8B-Instruct.Q4_0.gguf").display().to_string())), + ModelType: "llama", + MaxContextSize: 4096_usize, + NThreads: 4_usize, + MaxTokens: 2048_usize, + MaxBatchSize: 4096_usize, + TopK: 40_i32, + TopP: 0.95, + TfsZ: 1.0, + TypicalP: 1.0, + Temperature: 0.8, + RepeatPenalty: 1.1, + RepeatPenaltyLastN: 64_usize, + FrequencyPenalty: 0.0, + PresencePenalty: 0.0, + Mirostat: 0_i32, + MirostatTau: 5.0, + MirostatEta: 0.1, + PenalizeNl: true, + StopSequence: vec!["\n\n".to_string()] + ); + + let exec = executor!(llama, opts.clone())?; + let map_prompt = Step::for_prompt_template(prompt!(&system, &map)); + let reduce_prompt = Step::for_prompt_template(prompt!(&system, &reduce)); + let chain = Chain::new(map_prompt, reduce_prompt); + let docs = vec![Parameters::new_with_text(input)]; + let res = chain.run(docs, Parameters::new(), &exec).await?; + match res.to_immediate().await { + Ok(res) => Ok(res.get_content().to_string()), + Err(e) => Err(e.into()), + } + } +} + #[async_trait] impl wasi::logging::logging::Host for State { /// Log a message, formatted by the runtime subscriber. diff --git a/homestar-wasm/src/wasmtime/world.rs b/homestar-wasm/src/wasmtime/world.rs index 7c21b469..393c5cbf 100644 --- a/homestar-wasm/src/wasmtime/world.rs +++ b/homestar-wasm/src/wasmtime/world.rs @@ -28,6 +28,7 @@ use wasmtime::{ use wit_component::ComponentEncoder; wasmtime::component::bindgen!({ + path: "wit", world: "imports", tracing: true, async: true @@ -259,6 +260,8 @@ impl World { // This is a temporary measure until WASI is supported by default and is // unused otherwise. wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?; + + #[cfg(feature = "llm")] Imports::add_to_linker(&mut linker, |state: &mut State| state)?; let mut store = Store::new(&engine, data); @@ -291,6 +294,8 @@ impl World { // This is a temporary measure until WASI is supported by default and is // unused otherwise. wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?; + + #[cfg(feature = "llm")] Imports::add_to_linker(&mut linker, |state: &mut State| state)?; let mut store = Store::new(&engine, data); diff --git a/homestar-wasm/wit/helpers.wit b/homestar-wasm/wit/helpers.wit index eccebf1f..f110e838 100644 --- a/homestar-wasm/wit/helpers.wit +++ b/homestar-wasm/wit/helpers.wit @@ -14,8 +14,15 @@ interface helpers { print: func(msg: string); } +interface chain { + prompt-with: func(input: string, model: option) -> string; + prompt-seq: func(system: string, input: string, next: string, model: option) -> string; + prompt-chain: func(system: string, input: string, map: string, reduce: string, model: option) -> string; +} + world imports { /// https://github.com/WebAssembly/wasi-logging import wasi:logging/logging; import helpers; + import chain; } diff --git a/homestar-workspace-hack/Cargo.toml b/homestar-workspace-hack/Cargo.toml index 8bd36619..366c62a4 100644 --- a/homestar-workspace-hack/Cargo.toml +++ b/homestar-workspace-hack/Cargo.toml @@ -18,10 +18,11 @@ authors = { workspace = true } ### BEGIN HAKARI SECTION [dependencies] ahash = { version = "0.8.11", default-features = false, features = ["no-rng", "std"] } +aho-corasick = { version = "1.1.2" } anyhow = { version = "1.0.80", features = ["backtrace"] } arrayvec = { version = "0.7.4" } base64 = { version = "0.13.1", features = ["alloc"] } -bitflags = { version = "2.4.2", default-features = false, features = ["std"] } +bstr = { version = "1.9.0" } bytes = { version = "1.5.0", features = ["serde"] } clap = { version = "4.5.1", default-features = false, features = ["color", "derive", "env", "help", "std", "usage"] } clap_builder = { version = "4.5.1", default-features = false, features = ["color", "env", "help", "std", "usage"] } @@ -55,13 +56,18 @@ libsecp256k1-core = { version = "0.3.0" } memchr = { version = "2.7.1" } miette = { version = "5.10.0", features = ["fancy"] } multibase = { version = "0.9.1" } +nom = { version = "7.1.3" } num-traits = { version = "0.2.18", features = ["i128", "libm"] } +phf_shared = { version = "0.11.2" } rand = { version = "0.8.5", features = ["small_rng"] } +rand_core = { version = "0.6.4", default-features = false, features = ["std"] } regex = { version = "1.10.3" } -regex-automata = { version = "0.4.5", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa-backtrack", "perf-inline", "perf-literal", "unicode"] } +regex-automata = { version = "0.4.5", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa", "perf", "unicode"] } regex-syntax = { version = "0.8.2" } +reqwest = { version = "0.11.24", features = ["blocking", "json"] } retry = { version = "2.0.0" } rustc-hash = { version = "1.1.0" } +rustix = { version = "0.38.31", features = ["fs", "net"] } scopeguard = { version = "1.2.0" } semver = { version = "1.0.22", features = ["serde"] } serde = { version = "1.0.197", features = ["alloc", "derive", "rc"] } @@ -88,10 +94,11 @@ zeroize = { version = "1.7.0", features = ["zeroize_derive"] } [build-dependencies] ahash = { version = "0.8.11", default-features = false, features = ["no-rng", "std"] } +aho-corasick = { version = "1.1.2" } anyhow = { version = "1.0.80", features = ["backtrace"] } arrayvec = { version = "0.7.4" } base64 = { version = "0.13.1", features = ["alloc"] } -bitflags = { version = "2.4.2", default-features = false, features = ["std"] } +bstr = { version = "1.9.0" } bytes = { version = "1.5.0", features = ["serde"] } cc = { version = "1.0.86", default-features = false, features = ["parallel"] } clap = { version = "4.5.1", default-features = false, features = ["color", "derive", "env", "help", "std", "usage"] } @@ -127,14 +134,19 @@ libsecp256k1-core = { version = "0.3.0" } memchr = { version = "2.7.1" } miette = { version = "5.10.0", features = ["fancy"] } multibase = { version = "0.9.1" } +nom = { version = "7.1.3" } num-traits = { version = "0.2.18", features = ["i128", "libm"] } +phf_shared = { version = "0.11.2" } proc-macro2 = { version = "1.0.78", features = ["span-locations"] } rand = { version = "0.8.5", features = ["small_rng"] } +rand_core = { version = "0.6.4", default-features = false, features = ["std"] } regex = { version = "1.10.3" } -regex-automata = { version = "0.4.5", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa-backtrack", "perf-inline", "perf-literal", "unicode"] } +regex-automata = { version = "0.4.5", default-features = false, features = ["dfa-onepass", "dfa-search", "hybrid", "meta", "nfa", "perf", "unicode"] } regex-syntax = { version = "0.8.2" } +reqwest = { version = "0.11.24", features = ["blocking", "json"] } retry = { version = "2.0.0" } rustc-hash = { version = "1.1.0" } +rustix = { version = "0.38.31", features = ["fs", "net"] } scopeguard = { version = "1.2.0" } semver = { version = "1.0.22", features = ["serde"] } serde = { version = "1.0.197", features = ["alloc", "derive", "rc"] } @@ -168,8 +180,9 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } +security-framework-sys = { version = "2.9.1" } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -180,8 +193,9 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } +security-framework-sys = { version = "2.9.1" } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -192,8 +206,9 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } +security-framework-sys = { version = "2.9.1" } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -204,8 +219,9 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } +security-framework-sys = { version = "2.9.1" } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -217,7 +233,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -230,7 +246,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -243,7 +259,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -256,7 +272,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -269,7 +285,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" } @@ -282,7 +298,7 @@ miniz_oxide = { version = "0.7.2", default-features = false, features = ["with-a mio = { version = "0.8.11", features = ["net", "os-ext"] } object = { version = "0.32.2", default-features = false, features = ["archive", "read_core", "unaligned", "write"] } ring = { version = "0.17.8", features = ["std"] } -rustix = { version = "0.38.31", features = ["event", "mm", "net", "param", "process", "procfs", "termios", "thread", "time"] } +rustix = { version = "0.38.31", default-features = false, features = ["event", "mm", "param", "process", "procfs", "termios", "thread", "time"] } rustls = { version = "0.21.10", features = ["dangerous_configuration", "quic"] } spin = { version = "0.9.8" } subtle = { version = "2.5.0" }