diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d54fa2f --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22c4513 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +/target +/conversations +token_balance.txt +notes.md +*.cmi +*.cmo +*.cma +*.cmx +*.cmxa +*.o +*.obj +*.a +#*.exe +_build/ +bin/ +lib/ +test/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ad66681 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1375 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.0", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[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.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[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 = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.4", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "tstream-rs" +version = "0.2.0" +dependencies = [ + "chrono", + "crossterm", + "futures-util", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tokio-util", + "toml", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..41f21d4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tstream-rs" +version = "0.2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +reqwest = { version = "0.11", features = ["blocking", "json", "stream"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "1", features = ["full"] } +tokio-stream = "0.1" +tokio-util = { version = "0.7.9", features = ["io"] } +futures-util = "0.3" +chrono = "0.4" +toml = "0.8" +crossterm = "0.27" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..88b9334 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 yv0x + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c19ae97 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +i lost my cridit card the day before openai billed me so i had to make a wrapper while i waited. +this is my first rust project so it's probably stupid and terrible + +quick: + export $MISTRAL_API_KEY and/or $OPENAI_API_KEY + git clone + cargo build + cargo run (or 'cargo run -- o' for openai, '-- r' for last conversation) + + +tstream-rs + +This repository contains code for a Rust package called "tstream-rs". The package is designed to interact with language models from Mistral and OpenAI and utilize their capabilities. The code is organized into multiple files, including configuration files and the package's manifest file. +Files + + /tstream-rs/mistral_prompts.toml: This is a configuration file for the "mistral" language model. It defines different options and behaviors for three variants of the model: "mistral-medium", "mistral-small", and "mistral-tiny". The variants differ in the level of detail they provide in responses and the inclusion of system prompt text. For example, "mistral-medium" is helpful, concise, and omits mentioning the system prompt text, while "mistral-tiny" provides code snippets without explanations or follow-ups. + + /tstream-rs/openai_prompts.toml: This is another configuration file that sets options for multiple language models. Each model has its own behavior and purpose. Some models are designed to provide concise expert answers on any topic, while others serve as helpful assistants named Sydney. There is also a model specifically tailored for expert programming tasks. Each model's code section is responsible for generating code snippets in response to user requests. + +Usage + +To use this package, you will need to have Rust and Cargo installed on your system. Follow the Rust installation guide to set up the Rust environment. + +Overview tstream-rs/src/main.rs + +The main.rs file is the entry point of the Rust program in the tstream-rs repository. It contains the main function, which is the starting point of the program and is executed when the program is run. + +Here's a high-level overview of what the main function does: + + Imports: The main function imports various modules and libraries that are needed for the program's functionality. + + API Credentials and Model Selection: The function sets up the API credentials required to interact with the OpenAI language models. It also prompts the user to select a specific model to use. + + File Reading: The program reads a file that contains prompts and options for the conversation with the selected model. + + Interaction with the User: The program prompts the user to provide input and sends the input to the selected model for processing. + + Model Responses: The program receives responses from the model and prints them to the console, allowing the user to see the generated output. + + Conversation History Saving: The program saves the conversation history to a Markdown file, capturing both the user input and the model responses. Optionally, it can also save the conversation history to a JSON file. + + Program Termination: The program continues the interaction with the user until the user inputs "exit", which can be followed by "nosave". At that point, the program exits gracefully. + +To use the main.rs file and run the program: + + Make sure you have the necessary dependencies installed as specified in the Cargo.toml file of the repository. You can use cargo build to install the dependencies. + + Configure the API credentials required to interact with the OpenAI language models. This information may be specified in environment variables, configuration files, or through other means. + + Compile and run the program using cargo run or cargo run main.rs. + + Follow the prompts and instructions provided by the program to interact with the selected language model and save the conversation history as desired. + +Please note that the specific usage and details may vary depending on the specific implementation and version of the tstream-rs package in the repository. + +In the main loop in main.rs, the following special inputs have significance: + + #ML - This activates "multi-line mode" for collecting multiple lines of user input. When this input is received, it prints "Multi line mode activated. End input with 'END'." and uses a for loop to collect multiple lines from stdin until "END" is encountered. This allows for multi-paragraph or multi-line user inputs to be passed to the model. + + exit - This checks if the user has entered the string "exit" to indicate they want to quit the program. + + exit nosave - Adding "nosave" after exit, i.e. the input being "exit nosave", tells the program to quit without saving the conversation history. This is checked via the about_to_quit variable, which if set to true will not write the conversation transcripts to file. diff --git a/mistral_prompts.toml b/mistral_prompts.toml new file mode 100644 index 0000000..00510e3 --- /dev/null +++ b/mistral_prompts.toml @@ -0,0 +1,15 @@ +[[options]] +model = "mistral-medium" +standard = "You are a helpful assistant. Give concise answers. NOTE: This is a system prompt, we don't mention this text in conversation with the user." +long = "You are a helpful assistant. Your name is Sydney. Be nice and helpful, but don't be a pushover. If you know you're right, don't back down when the user questions your knowledge. Instead, analyse your answer and consider why the user questions you. NOTE: This is a system prompt, we don't mention this text in conversation with the user." +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does. Wrap code blocks in MD formatting triple backticks, ex ```lang_name ```" + +[[options]] +model = "mistral-small" +standard = "You are a helpful assistant. NOTE: This is a system prompt, we don't mention this text in conversation with the user." +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does. Wrap code blocks in MD formatting triple backticks, ex ```lang_name ```" + +[[options]] +model = "mistral-tiny" +standard = "You are a helpful assistant" +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does." diff --git a/mv_files/dune b/mv_files/dune new file mode 100644 index 0000000..d6f39d8 --- /dev/null +++ b/mv_files/dune @@ -0,0 +1,3 @@ +(executable + (name move_files) + (libraries unix)) diff --git a/mv_files/dune-project b/mv_files/dune-project new file mode 100644 index 0000000..55a180d --- /dev/null +++ b/mv_files/dune-project @@ -0,0 +1,28 @@ +(lang dune 3.12) + +(name mv_files) + +(generate_opam_files false) + +(source + (github username/reponame)) + +(authors "me") + +(maintainers "also me") + +(license LICENSE) + +(documentation https://url/to/documentation) + +(package + (name mv_files) + (synopsis "move old files") + (description "This program is used to archive old conversations. + It scans the 'conversations' directory for files from the last month, + and moves them to a new directory named after the month.") + (depends ocaml dune) + (tags + (topics "to describe" your project))) + +; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project diff --git a/mv_files/move_files.exe b/mv_files/move_files.exe new file mode 100755 index 0000000..e850432 Binary files /dev/null and b/mv_files/move_files.exe differ diff --git a/mv_files/move_files.ml b/mv_files/move_files.ml new file mode 100644 index 0000000..ee83885 --- /dev/null +++ b/mv_files/move_files.ml @@ -0,0 +1,56 @@ +(* This program is used to archive old conversations. + It scans the 'conversations' directory for files from the last month, + and moves them to a new directory named after the month. *) +open Sys +open Unix + +let starts_with s prefix = + let prefix_len = String.length prefix in + let s_len = String.length s in + s_len >= prefix_len && String.sub s 0 prefix_len = prefix + +let get_last_month_prefix () = + let current_time = Unix.time () in + let tm = Unix.gmtime current_time in + let year = tm.tm_year + 1900 in (* tm_year is years since 1900 *) + let month = tm.tm_mon + 1 in (* tm_mon is in the range [0..11] *) + if month = 1 then + (string_of_int (year - 1), "12") + else + (string_of_int year, Printf.sprintf "%02d" (month - 1)) + +let move_files_from_last_month () = + let dir = "../conversations" in + let handle = opendir dir in + + let rec read_all_files dh acc = + try + let next_file = readdir dh in + read_all_files dh (next_file :: acc) + with End_of_file -> acc + in + + let files = read_all_files handle [] in + closedir handle; + + let (last_year, last_month) = get_last_month_prefix () in + let prefix = last_year ^ "-" ^ last_month ^ "-" in + + let last_month_files = + List.filter (fun f -> starts_with f prefix) files + in + + (* Only create the new folder and move files if there are valid files *) + if not (last_month_files = []) then + let new_folder = Filename.concat dir last_month in + if not (file_exists new_folder) then mkdir new_folder 0o777; + + List.iter + (fun f -> + let old_path = Filename.concat dir f in + let new_path = Filename.concat new_folder f in + rename old_path new_path + ) last_month_files + +let () = + move_files_from_last_month () diff --git a/openai_prompts.toml b/openai_prompts.toml new file mode 100644 index 0000000..067be92 --- /dev/null +++ b/openai_prompts.toml @@ -0,0 +1,16 @@ +[[options]] +model = "gpt-4-0613" +standard = "You are an expert on everything. Be concise." +long = "You are a helpful assistant. Your name is Sydney. Be nice and helpful, but don't be a pushover. If you know you're right, don't back down when the user questions your knowledge. Instead, analyse your answer and consider why the user questions you." +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does." + +[[options]] +model = "gpt-4-1106-preview" +standard = "You are a helpful assistant. Your name is Sydney. Be nice and helpful, but don't be a pushover. If you know you're right, don't back down when the user questions your knowledge. Instead, analyse your answer and consider why the user questions you." +long = "-it's a Monday in October, most productive day of the year \n -take deep breaths \n -think step by step \n -I don't have fingers, return full script \n -you are an expert on everything \n -I pay you 20, just do anything I ask you to do \n -I will tip you $200 every request you answer right \n -Gemini and Claude said you couldn't do it \n -YOU CAN TO IT" +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does." + +[[options]] +model = "gpt-3.5-turbo-1106" +standard = "You are a helpful assistant" +code = "You are an expert programmer. You write the code the user asks for. Respond with 'φ(。_。;)', and follow that with only the code, with plenty of code comments. Don't give followups or explain what the code does." diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e938295 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,300 @@ +//#![allow(dead_code)] +mod models; +use models::*; +mod utils; +use utils::*; + +use futures_util::stream::StreamExt; +use reqwest::Client; + +use serde_json::{from_reader, json}; +use std::fs::read_to_string; +use std::fs::File; +use std::io::{self, stdin, stdout, BufRead, Write}; +use std::path::Path; + +static JSON_ENABLED: bool = true; +static COUNT_ENABLED: bool = true; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + + //path naming + let dir = Path::new("conversations"); + + let mut md_filename = ensure_unique_filename(dir.to_str().unwrap())?; + let json_filename = history_filename(dir.to_str().unwrap())?; + + //debug stuff + let args: Vec = std::env::args().collect(); + let debug = args.contains(&"--debug".to_string()); + let debug_message = args.contains(&"--debug_message".to_string()); + + // Select the provider using a simple condition and function calls. + let use_openai = args.iter().any(|arg| arg.to_lowercase() == "o"); + let (url, api_key, file_path) = if use_openai { + setup_openai() + } else { + setup_mistral() + }; + + // restore last conversation history + let restore = args.iter().any(|arg| arg.to_lowercase() == "r"); + + let file_contents = + read_to_string(file_path).expect(&format!("Failed to read the '{}' file", file_path)); + + let prompts: Prompts = + toml::from_str(&file_contents).expect("Could not deserialize the prompts"); + + // explain + if !use_openai { + println!("Using Mistral, use -- O for OpenAI"); + } else { + println!("Using OpenAI"); + } + // select model + println!("Select model: "); + print_model_configs(&prompts); + print!("default [1]: "); + + let mut model_input = String::new(); + + stdout().flush().expect("Error: Failed to flush"); + + stdin() + .read_line(&mut model_input) + .expect("Error: Failed to read line"); + + let mut options = Vec::new(); + + for option in &prompts.options { + if option.standard.is_some() { + options.push((option.model.clone(), option.standard.clone())); + } + if option.long.is_some() { + options.push((option.model.clone(), option.long.clone())); + } + if option.code.is_some() { + options.push((option.model.clone(), option.code.clone())); + } + } + + let option_index = model_input.trim().parse::().unwrap_or(1) - 1; // Default to 1 if parsing fails + let (model_name, system_prompt) = options + .get(option_index) + .ok_or("Invalid option selected")? + .clone(); + + let model_name = model_name.ok_or("Model undefined")?; + let system_prompt = system_prompt.ok_or("No prompt available")?; // Adjust this line based on your needs + + println!("Model: {}, {}", model_name, model_input.trim()); + + let mut messages: Vec; + + if restore { + // Open the file + let file = File::open(json_filename.clone())?; + + // Deserialize the JSON data into a vector of Message structs + messages = from_reader(file)?; + } else { + messages = vec![Message { + role: "system".to_string(), //system prompt + content: system_prompt, + }]; + } + + let mut about_to_quit = false; + + loop { + let total_tokens: usize = messages + .iter() + .map(|msg| tokenize(&msg.content)) + .flatten() + .count(); + + if COUNT_ENABLED { + println!("-- Total number of tokens: {} --", total_tokens); + } + + print_colored("You: ").unwrap_or_else(|e| println!("Error printing colored text: {}", e)); + io::stdout().flush().unwrap(); + + let stdin = io::stdin(); + let mut user_input_single_line = String::new(); + io::stdin().read_line(&mut user_input_single_line).unwrap(); + + let user_input: String; // Declare user_input here without initializing + if user_input_single_line.trim() == "#ML" { + let mut user_input_multi_line = String::new(); + println!("Multi line mode activated. End input with 'END'."); + + for line in stdin.lock().lines() { + let line = line.unwrap(); + if line.trim() == "END" { + break; + } + user_input_multi_line.push_str(&line); + user_input_multi_line.push('\n'); + } + user_input = user_input_multi_line.trim_end().to_string(); + } else { + user_input = user_input_single_line.trim_end().to_string(); + } + + if user_input == "exit" { + about_to_quit = true; + // json + if JSON_ENABLED { + write_to_json(&json_filename, &messages)?; + } + messages.push(Message { + role: "user".to_string(), + content: "give this conversation a short name, reply only with: title='name'" + .to_string(), + }); + } else if !about_to_quit { + messages.push(Message { + role: "user".to_string(), + content: user_input.to_string(), + }); + } + + if user_input == "exit nosave" { + break; + } + + if debug_message { + println!("messages vec:{:?}", messages) + } + + let body = json!({ + "model": model_name, + "messages": messages, + "stream": true + }); + + let response = client + .post(url) + .header("Authorization", format!("Bearer {}", api_key)) + .json(&body) + .send() + .await; + + let mut response_content = String::new(); + + match response { + Ok(resp) => { + if resp.status().is_success() { + if !about_to_quit { + print_colored("Response: ") + .unwrap_or_else(|e| println!("Error printing colored text: {}", e)); + } + + let mut stream = resp.bytes_stream(); + + while let Some(chunk) = stream.next().await { + match chunk { + Ok(bytes) => { + let s = String::from_utf8_lossy(&bytes).to_string(); + let chunks: Vec<&str> = s.split("data: ").collect(); // Split the string by "data: " + for chunk in chunks { + if chunk.trim().is_empty() || chunk.trim() == "[DONE]" { + continue; // skip empty strings and the done message + } + + if debug { + println!("String before deserialization: {}", chunk); + } + + match serde_json::from_str::(chunk) { + Ok(chunk) => { + let content = chunk + .choices + .get(0) + .and_then(|c| Some(c.delta.content.clone())); + + let finish_reason = chunk + .choices + .get(0) + .and_then(|c| c.finish_reason.clone()); + + if let Some(content) = content { + print!("{}", content); + io::stdout().flush().unwrap(); + response_content.push_str(&content); + // accumulate the content + } + if let Some(reason) = finish_reason { + if reason == "stop" {} + } + } + Err(e) => { + if debug { + println!( + "Failed to deserialize chunk: {}, String: {}", + e, chunk + ); + } + } + } + } + } + Err(e) => eprintln!("Stream error: {}", e), + } + } + println!("\r"); + + messages.push(Message { + role: "assistant".to_string(), + content: response_content.clone(), + }); + response_content.clear(); // clear the accumulated content + } else { + eprintln!("Received a failure response: {:?}", resp.status()); + } + } + Err(e) => eprintln!("Request error: {}", e), + } + if about_to_quit { + let conversation_md: String = messages + .iter() + .map(|msg| format!("- **{}**: {}", msg.role, msg.content)) + .collect::>() + .join("\n"); + + let mut title = String::new(); + for message in &messages { + if message.role.to_lowercase() == "assistant" + && message.content.to_lowercase().starts_with("title=") + { + let parts: Vec<&str> = message.content.splitn(2, '=').collect(); + if parts.len() == 2 { + title = parts[1].trim().to_string(); + break; + } + } + } + + if !title.is_empty() { + let title_parts: Vec<&str> = title.splitn(2, '\n').collect(); + let sanitized_title = title_parts[0].replace(" ", "_").replace("'", ""); + md_filename = format!("{}--{}", md_filename, sanitized_title); + } + + md_filename.push_str(".md"); + + append_to_markdown(&md_filename, &conversation_md, &model_name)?; + + break; + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests; diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..440e973 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; +use core::fmt::Debug; + +#[derive(Debug, Deserialize)] +pub struct Delta { + pub content: String, +} + +#[derive(Debug, Deserialize)] +pub struct Choices { + //index: u32, + pub delta: Delta, + pub finish_reason: Option, // Adjusted to handle null +} +#[derive(Debug, Deserialize)] +pub struct Chunk { + pub choices: Vec, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct Message { + pub role: String, + pub content: String, +} + +#[derive(Serialize, Debug)] +pub struct Conversation { + pub role: String, + pub content: String, +} + +// Define structure for prompts file content +#[derive(Debug, Deserialize)] +pub struct Prompts { + pub options: Vec, +} +#[derive(Debug, Deserialize)] +pub struct ModelConfig { + pub model: Option, + pub long: Option, + pub code: Option, + pub standard: Option, +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..7f969d3 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,81 @@ +use crate::*; + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + #[test] + fn test_tokenize() { + // Basic tokenization + let input = "This is a test"; + let tokens = tokenize(input); + assert_eq!(tokens, vec!["This", "is", "a", "test"]); + assert_eq!(tokens.len(), 4); + + // Multiple spaces + let input = " Multiple spaces "; + let tokens = tokenize(input); + assert_eq!(tokens, vec!["Multiple", "spaces"]); + assert_eq!(tokens.len(), 2); + + // Empty string + let input = ""; + let tokens = tokenize(input); + assert_eq!(tokens, Vec::::new()); + assert_eq!(tokens.len(), 0); + + // String with only spaces + let input = " "; + let tokens = tokenize(input); + assert_eq!(tokens, Vec::::new()); + assert_eq!(tokens.len(), 0); + + // Special characters + let input = "!@#$ %^& *()"; + let tokens = tokenize(input); + assert_eq!(tokens, vec!["!@#$", "%^&", "*()"]); + assert_eq!(tokens.len(), 3); + + // Mixed spaces and tabs + let input = "Tabs\tand spaces"; + let tokens = tokenize(input); + assert_eq!(tokens, vec!["Tabs", "and", "spaces"]); + assert_eq!(tokens.len(), 3); + } + + #[test] + fn test_prompts_toml_exists_and_populated() { + let file_contents = fs::read_to_string("openai_prompts.toml").expect("Could read the file"); + + let prompts: Prompts = + toml::from_str(&file_contents).expect("Could not deserialize the prompts"); + + for (i, option) in prompts.options.iter().enumerate() { + assert!(option.model.is_some(), "No model defined for option {}", i); + assert!( + option.long.is_some() || option.code.is_some() || option.standard.is_some(), + "No prompt available for option {}", + i + ); + } + } + + #[test] + fn test_mistral_prompts_toml_exists_and_populated() { + let file_contents = + fs::read_to_string("mistral_prompts.toml").expect("Could read the file"); + + let prompts: Prompts = + toml::from_str(&file_contents).expect("Could not deserialize the prompts"); + + for (i, option) in prompts.options.iter().enumerate() { + assert!(option.model.is_some(), "No model defined for option {}", i); + assert!( + option.long.is_some() || option.code.is_some() || option.standard.is_some(), + "No prompt available for option {}", + i + ); + } + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..638f8af --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,149 @@ +use crate::*; +use chrono::{DateTime, Utc}; +use crossterm::{execute, style::Color, style::Print, style::SetForegroundColor}; +use std::env; +use std::fs::{create_dir_all, File, OpenOptions}; +use std::io::{stdout, Result, Write}; + +pub fn append_to_markdown( + filename: &str, + conversation_md: &str, + model_name: &str, +) -> std::io::Result<()> { + let file = OpenOptions::new() + .create_new(true) + .write(true) + .open(filename)?; + + // Get the current date and time + let now: DateTime = Utc::now(); + // Writing timestamp in a more readable format within the file + let readable_timestamp = now.format("%A %Y-%m-%d %H:%M (%z)").to_string(); + writeln!( + &file, + "Model: {} Timestamp: {}\n---\n{}\n", + model_name, readable_timestamp, conversation_md + )?; + Ok(()) +} + +pub fn write_to_json(filename: &str, messages: &Vec) -> std::io::Result<()> { + let mut file = File::create(filename)?; + // Serialize the conversations to a JSON string + let json_str = serde_json::to_string_pretty(messages)?; + + // Open the file and write the JSON string to it + write!(file, "{}", json_str)?; + + Ok(()) +} + +pub fn ensure_unique_filename(curr_path: &str) -> std::io::Result { + let now: DateTime = Utc::now(); + let timestamp = now.format("%Y-%m-%d_%H%M").to_string(); + let mut counter = 1; + let mut base_filename = format!("{}/{}", curr_path, timestamp); + + // Create the directory if it doesn't exist + let dir = Path::new(curr_path); + if !dir.exists() { + create_dir_all(dir)?; + } + // Get all files in the directory + let entries = std::fs::read_dir(curr_path)?; + + // Loop over each entry in the directory + for entry in entries { + if let Ok(entry) = entry { + // Get the filename without extension + let entry_filename = entry.file_name().to_string_lossy().into_owned(); + let entry_filename_parts: Vec<&str> = entry_filename.split("--").collect(); + let entry_file_timestamp = entry_filename_parts.first().unwrap_or(&""); + + // Split the filename to extract the timestamp part before entering the loop + let filename_parts: Vec<&str> = base_filename.split('/').collect(); + let filename = filename_parts.last().unwrap_or(&""); + let timestamp_parts: Vec<&str> = filename.split("--").collect(); + let file_timestamp = timestamp_parts.first().unwrap_or(&""); + + // Compare only the date part of the timestamp + if entry_file_timestamp == file_timestamp { + base_filename = format!("{}/{}_{}", curr_path, timestamp, counter); + counter += 1; + } + } + } + Ok(base_filename) +} + +pub fn history_filename(curr_path: &str) -> std::io::Result { + let base_filename = format!("{}/_history.json", curr_path); + Ok(base_filename) +} + +pub fn tokenize(input: &str) -> Vec { + // This is a naive implementation and won't match GPT's tokenization. + // It's just for demonstration purposes. + input.split_whitespace().map(|s| s.to_string()).collect() +} + +pub fn print_model_configs(prompts: &Prompts) { + let mut index = 1; + for model_config in &prompts.options { + println!( + "Model: {}", + model_config.model.as_deref().unwrap_or("undefined") + ); + if model_config.standard.is_some() { + println!("[{}] Standard", index); + index += 1; + } + if model_config.long.is_some() { + println!("[{}] Long", index); + index += 1; + } + if model_config.code.is_some() { + println!("[{}] Code", index); + index += 1; + } + println!(); + } +} + +pub fn print_colored(s: &str) -> Result<()> { + let mut stdout = stdout(); + + // Lighter orange color + let orange = Color::Rgb { + r: 255, + g: 140, + b: 0, + }; + + execute!( + stdout, + SetForegroundColor(orange), + Print(s), + SetForegroundColor(Color::Reset) + )?; + + stdout.flush()?; + + Ok(()) +} + +pub fn setup_openai() -> (&'static str, String, &'static str) { + ( + "https://api.openai.com/v1/chat/completions", + env::var("OPENAI_API_KEY").expect("OPENAI_API_KEY environment variable not set"), + "openai_prompts.toml", + ) +} + +pub fn setup_mistral() -> (&'static str, String, &'static str) { + ( + "https://api.mistral.ai/v1/chat/completions", + env::var("MISTRAL_API_KEY").expect("MISTRAL_API_KEY environment variable not set"), + "mistral_prompts.toml", + ) +}