From 247f42d3140c4c22321ae5e84eed82b6a224808d Mon Sep 17 00:00:00 2001 From: Mufeed VH Date: Sun, 26 Jan 2025 21:56:15 +0530 Subject: [PATCH] init repo: upload source --- .gitattributes | 2 + .gitignore | 17 + CONTRIBUTING.md | 37 + Cargo.lock | 2449 ++++++ Cargo.toml | 45 + Dockerfile | 37 + LICENSE.md | 21 + README.md | 241 + config.toml | 30 + docker-compose.yml | 15 + frontend/.gitignore | 42 + frontend/README.md | 36 + frontend/app/chat/page.tsx | 27 + frontend/app/docs/page.tsx | 735 ++ frontend/app/favicon.ico | Bin 0 -> 25931 bytes frontend/app/globals.css | 260 + frontend/app/layout.tsx | 31 + frontend/app/page.tsx | 430 + frontend/bun.lockb | Bin 0 -> 245700 bytes frontend/components.json | 21 + frontend/components/chat.tsx | 886 ++ frontend/components/settings.tsx | 366 + frontend/components/ui/button.tsx | 57 + frontend/components/ui/card.tsx | 76 + frontend/components/ui/collapsible.tsx | 11 + frontend/components/ui/copy-button.tsx | 51 + frontend/components/ui/dialog.tsx | 122 + frontend/components/ui/form.tsx | 178 + frontend/components/ui/input.tsx | 22 + frontend/components/ui/label.tsx | 26 + frontend/components/ui/markdown-editor.tsx | 128 + frontend/components/ui/select.tsx | 159 + frontend/components/ui/sheet.tsx | 140 + frontend/components/ui/textarea.tsx | 22 + frontend/components/ui/toast.tsx | 130 + frontend/components/ui/toaster.tsx | 35 + frontend/components/ui/use-toast.ts | 191 + frontend/eslint.config.mjs | 18 + frontend/hooks/use-feature-flags.ts | 47 + frontend/lib/utils.ts | 6 + frontend/next.config.ts | 11 + frontend/package-lock.json | 7309 +++++++++++++++++ frontend/package.json | 52 + frontend/postcss.config.mjs | 8 + frontend/providers/posthog.tsx | 84 + frontend/public/asterisk.png | Bin 0 -> 100303 bytes frontend/public/deepclaude.ico | Bin 0 -> 223972 bytes frontend/public/deepclaude.png | Bin 0 -> 220229 bytes frontend/public/file.svg | 1 + frontend/public/globe.svg | 1 + frontend/public/next.svg | 1 + frontend/public/r1-plus-sonnet-benchmarks.png | Bin 0 -> 92872 bytes frontend/public/vercel.svg | 1 + frontend/public/window.svg | 1 + frontend/tailwind.config.ts | 85 + frontend/tsconfig.json | 41 + src/clients/anthropic.rs | 478 ++ src/clients/deepseek.rs | 417 + src/clients/mod.rs | 57 + src/config.rs | 140 + src/error.rs | 215 + src/handlers.rs | 598 ++ src/main.rs | 93 + src/models/mod.rs | 5 + src/models/request.rs | 123 + src/models/response.rs | 217 + 66 files changed, 17085 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 config.toml create mode 100644 docker-compose.yml create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/app/chat/page.tsx create mode 100644 frontend/app/docs/page.tsx create mode 100644 frontend/app/favicon.ico create mode 100644 frontend/app/globals.css create mode 100644 frontend/app/layout.tsx create mode 100644 frontend/app/page.tsx create mode 100755 frontend/bun.lockb create mode 100644 frontend/components.json create mode 100644 frontend/components/chat.tsx create mode 100644 frontend/components/settings.tsx create mode 100644 frontend/components/ui/button.tsx create mode 100644 frontend/components/ui/card.tsx create mode 100644 frontend/components/ui/collapsible.tsx create mode 100644 frontend/components/ui/copy-button.tsx create mode 100644 frontend/components/ui/dialog.tsx create mode 100644 frontend/components/ui/form.tsx create mode 100644 frontend/components/ui/input.tsx create mode 100644 frontend/components/ui/label.tsx create mode 100644 frontend/components/ui/markdown-editor.tsx create mode 100644 frontend/components/ui/select.tsx create mode 100644 frontend/components/ui/sheet.tsx create mode 100644 frontend/components/ui/textarea.tsx create mode 100644 frontend/components/ui/toast.tsx create mode 100644 frontend/components/ui/toaster.tsx create mode 100644 frontend/components/ui/use-toast.ts create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/hooks/use-feature-flags.ts create mode 100644 frontend/lib/utils.ts create mode 100644 frontend/next.config.ts create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.mjs create mode 100644 frontend/providers/posthog.tsx create mode 100644 frontend/public/asterisk.png create mode 100644 frontend/public/deepclaude.ico create mode 100644 frontend/public/deepclaude.png create mode 100644 frontend/public/file.svg create mode 100644 frontend/public/globe.svg create mode 100644 frontend/public/next.svg create mode 100644 frontend/public/r1-plus-sonnet-benchmarks.png create mode 100644 frontend/public/vercel.svg create mode 100644 frontend/public/window.svg create mode 100644 frontend/tailwind.config.ts create mode 100644 frontend/tsconfig.json create mode 100644 src/clients/anthropic.rs create mode 100644 src/clients/deepseek.rs create mode 100644 src/clients/mod.rs create mode 100644 src/config.rs create mode 100644 src/error.rs create mode 100644 src/handlers.rs create mode 100644 src/main.rs create mode 100644 src/models/mod.rs create mode 100644 src/models/request.rs create mode 100644 src/models/response.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..54f1495 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* linguist-vendored +*.rs linguist-vendored=false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0104787 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f33b3fb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Welcome Contributors + +We welcome contributions to enhance DeepClaude's capabilities and improve its performance. To report bugs, create a [GitHub issue](https://github.com/getasterisk/deepclaude/issues). + +> Before contributing, read through the existing issues and pull requests to see if someone else is already working on something similar. That way you can avoid duplicating efforts. + +To contribute, please follow these steps: + +1. Fork the DeepClaude repository on GitHub. +2. Create a new branch for your feature or bug fix. +3. Make your changes and ensure that the code passes all tests. +4. Submit a pull request describing your changes and their benefits. + +### Pull Request Guidelines +When submitting a pull request, please follow these guidelines: + +1. **Title**: please include following prefixes: + - `Feature:` for new features + - `Fix:` for bug fixes + - `Docs:` for documentation changes + - `Refactor:` for code refactoring + - `Improve:` for performance improvements + - `Other:` for other changes + + for example: + - `Feature: added new feature to the code` + - `Fix: fixed the bug in the code` + +2. **Description**: Provide a clear and detailed description of your changes in the pull request. Explain the problem you are solving, the approach you took, and any potential side effects or limitations of your changes. +3. **Documentation**: Update the relevant documentation to reflect your changes. This includes the README file, code comments, and any other relevant documentation. +4. **Dependencies**: If your changes require new dependencies, ensure that they are properly documented and added to the `Cargo.toml` or `package.json` files. +5. if the pull request does not meet the above guidelines, it may be closed without merging. + + +**Note**: Please ensure that you have the latest version of the code before creating a pull request. If you have an existing fork, just sync your fork with the latest version of the DeepClaude repository. + +Please adhere to the coding conventions, maintain clear documentation, and provide thorough testing for your contributions. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..96f5ca2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2449 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +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 = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +dependencies = [ + "axum-core", + "axum-macros", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "config" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e329294a796e9b22329669c1f433a746983f9e324e07f4ef135be81bb2262de4" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "winnow", + "yaml-rust2", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deepclaude" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-stream", + "axum", + "chrono", + "config", + "futures", + "once_cell", + "openssl", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.4.1+3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rust-ini" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +dependencies = [ + "cfg-if", + "ordered-multimap", + "trim-in-place", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +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 = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +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-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "bitflags", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +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-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yaml-rust2" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1a1c0bc9823338a3bdf8c61f994f23ac004c6fa32c08cd152984499b445e8d" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5ea987d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "deepclaude" +version = "0.1.0" +edition = "2021" +description = "A high-performance LLM inference API and Chat UI that integrates DeepSeek R1's CoT reasoning traces with Anthropic Claude models." +authors = ["Mufeed VH "] + +[dependencies] +# Web framework +axum = { version = "0.8", features = ["json", "macros"] } +tower = "0.5" +tower-http = { version = "0.6", features = ["trace", "cors"] } + +# Async runtime +tokio = { version = "1.4", features = ["full"] } +tokio-stream = "0.1" +futures = "0.3" +async-stream = "0.3" + +# Serialization +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# HTTP client +reqwest = { version = "0.12", features = ["json", "stream"] } + +# Error handling +anyhow = "1.0" +thiserror = "2.0" + +# Logging +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +# Configuration +config = { version = "0.15", features = ["toml"] } + +# Time +chrono = { version = "0.4", features = ["serde"] } + +# Utilities +once_cell = "1.20" + +# OpenSSL (vendored) +openssl = { version = "0.10", features = ["vendored"] } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e065faf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +# Builder stage +FROM rust:latest as builder + +WORKDIR /usr/src/deepclaude +COPY . . + +# Install build dependencies +RUN apt-get update && \ + apt-get install -y pkg-config libssl-dev && \ + rm -rf /var/lib/apt/lists/* + +# Build the application +RUN cargo build --release + +# Runtime stage +FROM debian:bookworm-slim + +WORKDIR /usr/local/bin + +# Install runtime dependencies +RUN apt-get update && \ + apt-get install -y libssl3 ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Copy the built binary +COPY --from=builder /usr/src/deepclaude/target/release/deepclaude . +COPY --from=builder /usr/src/deepclaude/config.toml . + +# Set the host and port in config +ENV DEEPCLAUDE_HOST=0.0.0.0 +ENV DEEPCLAUDE_PORT=1337 + +# Expose port 1337 +EXPOSE 1337 + +# Run the binary +CMD ["./deepclaude"] \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8d8d539 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Mufeed VH / Asterisk + +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..f77a9b6 --- /dev/null +++ b/README.md @@ -0,0 +1,241 @@ +
+ +

DeepClaude 🐬🧠

+ + + +Harness the power of DeepSeek R1's reasoning and Claude's creativity and code generation capabilities with a unified API and chat interface. + +[![GitHub license](https://img.shields.io/github/license/getasterisk/deepclaude)](https://github.com/getasterisk/deepclaude/blob/main/LICENSE.md) +[![Rust](https://img.shields.io/badge/rust-v1.75%2B-orange)](https://www.rust-lang.org/) +[![API Status](https://img.shields.io/badge/API-Stable-green)](https://deepclaude.asterisk.so) + +[Getting Started](#getting-started) • +[Features](#features) • +[API Usage](#api-usage) • +[Documentation](#documentation) • +[Self-Hosting](#self-hosting) • +[Contributing](#contributing) + +
+ +## Table of Contents +- [Overview](#overview) +- [Features](#features) +- [Why R1 + Claude?](#why-r1--claude) +- [Benchmarks](#benchmarks) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Installation](#installation) + - [Configuration](#configuration) +- [API Usage](#api-usage) + - [Basic Example](#basic-example) + - [Streaming Example](#streaming-example) +- [Configuration Options](#configuration-options) +- [Self-Hosting](#self-hosting) +- [Security](#security) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) + +## Overview + +DeepClaude is a high-performance LLM inference API that combines DeepSeek R1's Chain of Thought (CoT) reasoning capabilities with Anthropic Claude's creative and code generation prowess. It provides a unified interface for leveraging the strengths of both models while maintaining complete control over your API keys and data. + +## Features + +🚀 **Zero Latency** - Instant responses with R1's CoT followed by Claude's response in a single stream, powered by a high-performance Rust API + +🔒 **Private & Secure** - End-to-end security with local API key management. Your data stays private + +⚙️ **Highly Configurable** - Customize every aspect of the API and interface to match your needs + +🌟 **Open Source** - Free and open-source codebase. Contribute, modify, and deploy as you wish + +🤖 **Dual AI Power** - Combine DeepSeek R1's reasoning with Claude's creativity and code generation + +🔑 **Managed BYOK API** - Use your own API keys with our managed infrastructure for complete control + +## Why R1 + Claude? + +DeepSeek R1's CoT trace demonstrates deep reasoning to the point of an LLM experiencing "metacognition" - correcting itself, thinking about edge cases, and performing quasi Monte Carlo Tree Search in natural language. + +However, R1 lacks in code generation, creativity, and conversational skills. Claude 3.5 Sonnet excels in these areas, making it the perfect complement. DeepClaude combines both models to provide: + +- R1's exceptional reasoning and problem-solving capabilities +- Claude's superior code generation and creativity +- Fast streaming responses in a single API call +- Complete control with your own API keys + +## Benchmarks + +![Benchmarks](frontend/public/r1-plus-sonnet-benchmarks.png) + +According to [Aider Polyglot Benchmarks](https://aider.chat/2025/01/24/r1-sonnet.html), the combination of "R1 as architect with Sonnet as editor" has achieved: + +- New SOTA of 64.0% on the aider polyglot benchmark +- 14X cost reduction compared to previous SOTA results +- Improved accuracy across multiple programming languages + +## Getting Started + +### Prerequisites + +- Rust 1.75 or higher +- DeepSeek API key +- Anthropic API key + +### Installation + +1. Clone the repository: +```bash +git clone https://github.com/getasterisk/deepclaude.git +cd deepclaude +``` + +2. Build the project: +```bash +cargo build --release +``` + +### Configuration + +Create a `config.toml` file in the project root: + +```toml +[server] +host = "127.0.0.1" +port = 3000 + +[pricing] +# Configure pricing settings for usage tracking +``` + +## API Usage + +See [API Docs](https://deepclaude.chat) + +### Basic Example + +```python +import requests + +response = requests.post( + "http://127.0.0.1:1337/", + headers={ + "X-DeepSeek-API-Token": "", + "X-Anthropic-API-Token": "" + }, + json={ + "messages": [ + {"role": "user", "content": "How many 'r's in the word 'strawberry'?"} + ] + } +) + +print(response.json()) +``` + +### Streaming Example + +```python +import asyncio +import json +import httpx + +async def stream_response(): + async with httpx.AsyncClient() as client: + async with client.stream( + "POST", + "http://127.0.0.1:1337/", + headers={ + "X-DeepSeek-API-Token": "", + "X-Anthropic-API-Token": "" + }, + json={ + "stream": True, + "messages": [ + {"role": "user", "content": "How many 'r's in the word 'strawberry'?"} + ] + } + ) as response: + response.raise_for_status() + async for line in response.aiter_lines(): + if line: + if line.startswith('data: '): + data = line[6:] + try: + parsed_data = json.loads(data) + if 'content' in parsed_data: + content = parsed_data.get('content', '')[0]['text'] + print(content, end='',flush=True) + else: + print(data, flush=True) + except json.JSONDecodeError: + pass + +if __name__ == "__main__": + asyncio.run(stream_response()) +``` + +## Configuration Options + +The API supports extensive configuration through the request body: + +```json +{ + "stream": false, + "verbose": false, + "system": "Optional system prompt", + "messages": [...], + "deepseek_config": { + "headers": {}, + "body": {} + }, + "anthropic_config": { + "headers": {}, + "body": {} + } +} +``` + +## Self-Hosting + +DeepClaude can be self-hosted on your own infrastructure. Follow these steps: + +1. Configure environment variables or `config.toml` +2. Build the Docker image or compile from source +3. Deploy to your preferred hosting platform + +## Security + +- No data storage or logged +- BYOK (Bring Your Own Keys) architecture +- Regular security audits and updates + +## Contributing + +We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on: + +- Code of Conduct +- Development process +- Submitting pull requests +- Reporting issues + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details. + +## Acknowledgments + +DeepClaude is a free and open-source project by [Asterisk](https://asterisk.so/). Special thanks to: + +- DeepSeek for their incredible R1 model +- Anthropic for Claude's capabilities +- The open-source community for their continuous support + +--- + +
+Made with ❤️ by Asterisk +
\ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..eff690d --- /dev/null +++ b/config.toml @@ -0,0 +1,30 @@ +# Server Configuration +[server] +host = "127.0.0.1" +port = 1337 + +# Pricing Configuration (per million tokens) +[pricing] +[pricing.deepseek] +input_cache_hit_price = 0.14 +input_cache_miss_price = 0.55 +output_price = 2.19 + +[pricing.anthropic] +[pricing.anthropic.claude_3_sonnet] +input_price = 3.0 +output_price = 15.0 +cache_write_price = 3.75 +cache_read_price = 0.30 + +[pricing.anthropic.claude_3_haiku] +input_price = 0.80 +output_price = 4.0 +cache_write_price = 1.0 +cache_read_price = 0.08 + +[pricing.anthropic.claude_3_opus] +input_price = 15.0 +output_price = 75.0 +cache_write_price = 18.75 +cache_read_price = 1.50 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b90625d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + api: + build: . + container_name: deepclaude_api + restart: unless-stopped + ports: + - "127.0.0.1:1337:1337" + volumes: + - ./config.toml:/usr/local/bin/config.toml + networks: + - deepclaude_network + +networks: + deepclaude_network: + name: deepclaude_network \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..c5e8b4f --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build +/dist + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/frontend/app/chat/page.tsx b/frontend/app/chat/page.tsx new file mode 100644 index 0000000..31c0018 --- /dev/null +++ b/frontend/app/chat/page.tsx @@ -0,0 +1,27 @@ +"use client" + +import { useState } from "react" +import { Chat } from "../../components/chat" +import { Settings } from "../../components/settings" + +export default function ChatPage() { + const [selectedModel, setSelectedModel] = useState("claude-3-sonnet-20241022") + const [apiTokens, setApiTokens] = useState({ + deepseekApiToken: "", + anthropicApiToken: "" + }) + + return ( +
+ + +
+ ) +} diff --git a/frontend/app/docs/page.tsx b/frontend/app/docs/page.tsx new file mode 100644 index 0000000..513a998 --- /dev/null +++ b/frontend/app/docs/page.tsx @@ -0,0 +1,735 @@ +"use client" + +import Image from "next/image" +import { Card } from "../../components/ui/card" +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" +import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism" + +// Custom theme based on oneDark but without text background +const customTheme = { + ...oneDark, + 'code[class*="language-"]': { + ...oneDark['code[class*="language-"]'], + backgroundColor: 'transparent', + }, + 'pre[class*="language-"]': { + ...oneDark['pre[class*="language-"]'], + backgroundColor: 'transparent', + }, + 'token': { + ...oneDark['token'], + backgroundColor: 'transparent', + } +} +import { CopyButton } from "../../components/ui/copy-button" + +// Example cURL commands and responses +const examples = { + nonStreaming: { + curl: `curl -L -X POST 'https://api.deepclaude.com' +-H 'Content-Type: application/json' +-H 'Accept: application/json' +-H 'X-DeepSeek-API-Token: ' +-H 'X-Anthropic-API-Token: ' +-d '{ + "stream": false, + "verbose": false, + "messages": [ + { + "content": "How many \"r\"s in the word \"strawberry\"?", + "role": "user" + } + ], + "deepseek_config": { + "headers": { + "Add-Custom-Header-To-DeepSeek-Call": "example" + }, + "body": { + "temperature": 0 + } + }, + "anthropic_config": { + "headers": { + "Add-Custom-Header-To-Claude-Call": "example" + }, + "body": { + "temperature": 0 + } + } +}'`, + response: `{ + "created": "2025-01-26T01:46:54.654574Z", + "content": [ + { + "type": "text", + "text": "\\nOkay, let's see. The user is asking how many times the letter \"r\" appears in the word \"strawberry\". Hmm, I need to make sure I spell the word correctly first. Strawberry. S-T-R-A-W-B-E-R-R-Y. Let me break it down letter by letter.\\n\\nStarting with the first letter: S. No r there. Then T. Still no. Third letter is R. That's one. Next is A. Then W. B, E. Now after E comes R, that's the second R. Then another R right after, so that's the third R. Finally Y. Wait, let me check again. Let me write the letters out with their positions:\\n\\n1. S\\n2. T\\n3. R\\n4. A\\n5. W\\n6. B\\n7. E\\n8. R\\n9. R\\n10. Y\\n\\nWait, does strawberry have two R's after the E? Let me think. The correct spelling is S-T-R-A-W-B-E-R-R-Y. So after the E, there are two R's before the Y. So that's positions 8 and 9. So in total, the R's are at position 3, 8, and 9. That makes three R's. But wait, maybe I'm miscounting. Let me write the word again: strawberry. S-T-R-A-W-B-E-R-R-Y. So letters are S, T, R, A, W, B, E, R, R, Y. So indeed, three R's. But sometimes people might miss one. Let me confirm by another method. Maybe splitting the word into parts: \"straw\" and \"berry\". Straw has an R? Let's see: S-T-R-A-W. Yes, there's an R there. Then berry is B-E-R-R-Y. So berry has two R's. So straw (1 R) + berry (2 Rs) equals 3 R's total. Yep, that adds up. So the answer should be three.\\n" + }, + { + "type": "text", + "text": "\\n\\nThere are 3 \"r\"s in the word \"strawberry\"." + } + ], + "combined_usage": { + "total_cost": "$0.003", + "deepseek_usage": { + "input_tokens": 18, + "output_tokens": 491, + "reasoning_tokens": 430, + "cached_input_tokens": 0, + "total_tokens": 509, + "total_cost": "$0.001" + }, + "anthropic_usage": { + "input_tokens": 507, + "output_tokens": 20, + "cached_write_tokens": 0, + "cached_read_tokens": 0, + "total_tokens": 527, + "total_cost": "$0.002" + } + } +}` + }, + streaming: { + curl: `curl -L -X POST 'https://api.deepclaude.com' +-H 'Content-Type: application/json' +-H 'Accept: application/json' +-H 'X-DeepSeek-API-Token: ' +-H 'X-Anthropic-API-Token: ' +-d '{ + "stream": true, + "verbose": false, + "messages": [ + { + "content": "How many \"r\"s in the word \"strawberry\"?", + "role": "user" + } + ], + "deepseek_config": { + "headers": { + "Add-Custom-Header-To-DeepSeek-Call": "example" + }, + "body": { + "temperature": 0 + } + }, + "anthropic_config": { + "headers": { + "Add-Custom-Header-To-Claude-Call": "example" + }, + "body": { + "temperature": 0 + } + } +}'`, + response: `event: start +data: {"type":"start","created":"2025-01-26T01:54:59.529246Z"} + +event: content +data: {"type":"content","content":[{"type":"text","text":"\n"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"Okay"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":","}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" let"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"'s"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" see"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"."}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" The"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" user"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" is"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" asking"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" how"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" many"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" times"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" the"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" letter"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" \""}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"r"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"\""}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" appears"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" in"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" the"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" word"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" \""}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"st"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"raw"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"berry"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"\"."}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" Al"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"right"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":","}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" first"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":","}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" I"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" need"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" to"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" make"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" sure"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" I"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" spell"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" the"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" word"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" correctly"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"."}]} + +[... SSE events omitted for brevity ...] + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" Therefore"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":","}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" the"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" correct"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" answer"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" is"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":" "}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"3"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"."}]} + +event: content +data: {"type":"content","content":[{"type":"text","text":"\n"}]} + +event: content +data: {"type":"content","content":[]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"\nThe word \"strawberry\" contains 3 \"r"}]} + +event: content +data: {"type":"content","content":[{"type":"text_delta","text":"\"s."}]} + +event: done +data: {"type":"done"}` + }, + verbose: { + curl: `curl -L -X POST 'https://api.deepclaude.com' +-H 'Content-Type: application/json' +-H 'Accept: application/json' +-H 'X-DeepSeek-API-Token: ' +-H 'X-Anthropic-API-Token: ' +-d '{ + "stream": false, + "verbose": true, + "messages": [ + { + "content": "How many \"r\"s in the word \"strawberry\"?", + "role": "user" + } + ], + "deepseek_config": { + "headers": { + "Add-Custom-Header-To-DeepSeek-Call": "example" + }, + "body": { + "temperature": 0 + } + }, + "anthropic_config": { + "headers": { + "Add-Custom-Header-To-Claude-Call": "example" + }, + "body": { + "temperature": 0 + } + } +}'`, + response: `{ + "created": "2025-01-26T01:50:47.209411Z", + "content": [ + { + "type": "text", + "text": "\\nOkay, let's see. The user is asking how many times the letter \"r\" appears in the word \"strawberry.\" Alright, first, I need to make sure I spell the word correctly. Strawberry. S-T-R-A-W-B-E-R-R-Y. Wait, let me break it down letter by letter to count the \"r\"s.\\n\\nStarting with the first letter: S. That's an S, no R there. Next is T. Still no R. Then R. Ah, that's the first R. So R is the third letter. Then A, W, B, E. So after R, we have A, W, B, E. None of those are Rs. Then after E comes R again. That's the second R. Then another R right after? Wait, let me check again. The word is strawberry. So after E, is it R-R? Let me write it out: S-T-R-A-W-B-E-R-R-Y. Yes, after the E, there are two Rs in a row. So that's two Rs there. Then Y at the end. So total Rs: the third letter is R, and then the 8th and 9th letters are R and R. Wait, let me count the positions to be sure.\\n\\nLet me list each letter with its position:\\n\\n1. S\\n2. T\\n3. R\\n4. A\\n5. W\\n6. B\\n7. E\\n8. R\\n9. R\\n10. Y\\n\\nSo positions 3, 8, and 9. Wait, that's three Rs. Wait, but when I first thought, I had R at position 3, then two Rs at 8 and 9. So that's three Rs in total. But wait, I might be miscounting. Let me spell it again: S-T-R-A-W-B-E-R-R-Y. So after E, it's R-R-Y. So two Rs there. So total Rs would be the one at position 3 and then two more at positions 8 and 9. That makes three Rs. Wait, but I think the correct spelling is actually \"strawberry\" with two Rs. Let me check that again. Maybe I added an extra R by mistake. Let me think. The correct spelling is S-T-R-A-W-B-E-R-R-Y. So yes, two Rs after the E. So that's R at position 3 and then two Rs at positions 8 and 9. So total of three Rs. But wait, I'm confused because when I say the word out loud, \"strawberry\" is pronounced with a sort of double R sound? Or maybe not. Let me confirm the spelling. Strawberry is spelled S-T-R-A-W-B-E-R-R-Y. So yes, there are two Rs towards the end. So in total, three Rs? Wait, no. Let me count again. S (1), T (2), R (3), A (4), W (5), B (6), E (7), R (8), R (9), Y (10). So positions 3, 8, and 9. That's three Rs. But I think that's incorrect. Wait, maybe I'm miscounting. Let me check another way. Let's write the word out: s t r a w b e r r y. So after the 'e', there are two 'r's and then a 'y'. So that's two Rs at the end. Plus the R in the beginning. So that's three Rs. Hmm. But when I actually think about the word, I might have made a mistake here. Wait, maybe the correct spelling is S-T-R-A-W-B-E-R-Y. No, that's not right. Because \"strawberry\" does have two Rs. Let me verify. Yes, the correct spelling is indeed \"strawberry\" with two Rs: S-T-R-A-W-B-E-R-R-Y. So that's two Rs after the E. So total letters R: one at position 3, and two at positions 8 and 9. So three Rs in total. Wait, but that seems conflicting because when I look up the spelling, \"strawberry\" is spelled with two Rs, not three. Wait, no. Let me check again. S-T-R-A-W-B-E-R-R-Y. So the letters are S, T, R, A, W, B, E, R, R, Y. So that's 10 letters. The Rs are at positions 3, 8, and 9. So three Rs. But that can't be right because the standard spelling is \"strawberry\" with two Rs. Wait, maybe I added an extra R. Let me think again. The word is \"strawberry\". Let's break it down. The first part is \"straw\" (S-T-R-A-W), then \"berry\" (B-E-R-R-Y). So \"berry\" has two Rs. So in \"strawberry\", combining \"straw\" and \"berry\", so the Rs are in \"straw\" and \"berry\". Wait, \"straw\" is S-T-R-A-W, which has one R. Then \"berry\" is B-E-R-R-Y, which has two Rs. So combining them, it's S-T-R-A-W-B-E-R-R-Y. So total Rs: one from \"straw\" and two from \"berry\", totaling three Rs. Therefore, the answer should be three. But I'm a bit confused because I might have miscounted. Let me write the word and count manually: S, T, R, A, W, B, E, R, R, Y. Yes, that's three Rs. So the answer is three. But wait, when I actually write \"strawberry\", I think of it as S-T-R-A-W-B-E-R-R-Y, so yes, three Rs. But maybe the user is tricking me, or perhaps I'm overcomplicating. Let me confirm with another method. If I type \"strawberry\" and count the Rs: s(1), t(2), r(3), a(4), w(5), b(6), e(7), r(8), r(9), y(10). So three Rs at positions 3,8,9. Therefore, the answer is 3.\\n" + }, + { + "type": "text", + "text": "\\n\\nThere are 3 \"r\"s in the word \"strawberry\"." + } + ], + "deepseek_response": { + "status": 200, + "headers": { + "connection": "keep-alive", + "x-ds-trace-id": "", + "cf-cache-status": "DYNAMIC", + "server": "cloudflare", + "cf-ray": "", + "vary": "origin, access-control-request-method, access-control-request-headers", + "strict-transport-security": "max-age=31536000; includeSubDomains; preload", + "x-content-type-options": "nosniff", + "date": "Sun, 26 Jan 2025 01:50:17 GMT", + "set-cookie": "", + "access-control-allow-credentials": "true", + "transfer-encoding": "chunked", + "content-type": "application/json" + }, + "body": { + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "logprobs": null, + "message": { + "content": "The word \"strawberry\" contains **3** instances of the letter \"r\". Here's the breakdown:\\n\\n1. **S-T-R** (position 3) \\n2. **B-E-R** (position 8) \\n3. **R-R-Y** (position 9) \\n\\nSo, the total count of \"r\"s is **3**. 🍓", + "reasoning_content": "Okay, let's see. The user is asking how many times the letter \"r\" appears in the word \"strawberry.\" Alright, first, I need to make sure I spell the word correctly. Strawberry. S-T-R-A-W-B-E-R-R-Y. Wait, let me break it down letter by letter to count the \"r\"s.\\n\\nStarting with the first letter: S. That's an S, no R there. Next is T. Still no R. Then R. Ah, that's the first R. So R is the third letter. Then A, W, B, E. So after R, we have A, W, B, E. None of those are Rs. Then after E comes R again. That's the second R. Then another R right after? Wait, let me check again. The word is strawberry. So after E, is it R-R? Let me write it out: S-T-R-A-W-B-E-R-R-Y. Yes, after the E, there are two Rs in a row. So that's two Rs there. Then Y at the end. So total Rs: the third letter is R, and then the 8th and 9th letters are R and R. Wait, let me count the positions to be sure.\\n\\nLet me list each letter with its position:\\n\\n1. S\\n2. T\\n3. R\\n4. A\\n5. W\\n6. B\\n7. E\\n8. R\\n9. R\\n10. Y\\n\\nSo positions 3, 8, and 9. Wait, that's three Rs. Wait, but when I first thought, I had R at position 3, then two Rs at 8 and 9. So that's three Rs in total. But wait, I might be miscounting. Let me spell it again: S-T-R-A-W-B-E-R-R-Y. So after E, it's R-R-Y. So two Rs there. So total Rs would be the one at position 3 and then two more at positions 8 and 9. That makes three Rs. Wait, but I think the correct spelling is actually \"strawberry\" with two Rs. Let me check that again. Maybe I added an extra R by mistake. Let me think. The correct spelling is S-T-R-A-W-B-E-R-R-Y. So yes, two Rs after the E. So that's R at position 3 and then two Rs at positions 8 and 9. So total of three Rs. But wait, I'm confused because when I say the word out loud, \"strawberry\" is pronounced with a sort of double R sound? Or maybe not. Let me confirm the spelling. Strawberry is spelled S-T-R-A-W-B-E-R-R-Y. So yes, there are two Rs towards the end. So in total, three Rs? Wait, no. Let me count again. S (1), T (2), R (3), A (4), W (5), B (6), E (7), R (8), R (9), Y (10). So positions 3, 8, and 9. That's three Rs. But I think that's incorrect. Wait, maybe I'm miscounting. Let me check another way. Let's write the word out: s t r a w b e r r y. So after the 'e', there are two 'r's and then a 'y'. So that's two Rs at the end. Plus the R in the beginning. So that's three Rs. Hmm. But when I actually think about the word, I might have made a mistake here. Wait, maybe the correct spelling is S-T-R-A-W-B-E-R-Y. No, that's not right. Because \"strawberry\" does have two Rs. Let me verify. Yes, the correct spelling is indeed \"strawberry\" with two Rs: S-T-R-A-W-B-E-R-R-Y. So that's two Rs after the E. So total letters R: one at position 3, and two at positions 8 and 9. So three Rs in total. Wait, but that seems conflicting because when I look up the spelling, \"strawberry\" is spelled with two Rs, not three. Wait, no. Let me check again. S-T-R-A-W-B-E-R-R-Y. So the letters are S, T, R, A, W, B, E, R, R, Y. So that's 10 letters. The Rs are at positions 3, 8, and 9. So three Rs. But that can't be right because the standard spelling is \"strawberry\" with two Rs. Wait, maybe I added an extra R. Let me think again. The word is \"strawberry\". Let's break it down. The first part is \"straw\" (S-T-R-A-W), then \"berry\" (B-E-R-R-Y). So \"berry\" has two Rs. So in \"strawberry\", combining \"straw\" and \"berry\", so the Rs are in \"straw\" and \"berry\". Wait, \"straw\" is S-T-R-A-W, which has one R. Then \"berry\" is B-E-R-R-Y, which has two Rs. So combining them, it's S-T-R-A-W-B-E-R-R-Y. So total Rs: one from \"straw\" and two from \"berry\", totaling three Rs. Therefore, the answer should be three. But I'm a bit confused because I might have miscounted. Let me write the word and count manually: S, T, R, A, W, B, E, R, R, Y. Yes, that's three Rs. So the answer is three. But wait, when I actually write \"strawberry\", I think of it as S-T-R-A-W-B-E-R-R-Y, so yes, three Rs. But maybe the user is tricking me, or perhaps I'm overcomplicating. Let me confirm with another method. If I type \"strawberry\" and count the Rs: s(1), t(2), r(3), a(4), w(5), b(6), e(7), r(8), r(9), y(10). So three Rs at positions 3,8,9. Therefore, the answer is 3.", + "role": "assistant" + } + } + ], + "created": 1737856217, + "id": "", + "model": "deepseek-reasoner", + "object": "chat.completion", + "system_fingerprint": "", + "usage": { + "completion_tokens": 1423, + "completion_tokens_details": { "reasoning_tokens": 1343 }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 18, + "prompt_tokens": 18, + "prompt_tokens_details": { "cached_tokens": 0 }, + "total_tokens": 1441 + } + } + }, + "anthropic_response": { + "status": 200, + "headers": { + "anthropic-ratelimit-input-tokens-reset": "", + "anthropic-ratelimit-output-tokens-limit": "", + "anthropic-ratelimit-tokens-reset": "", + "content-type": "", + "via": "", + "anthropic-ratelimit-input-tokens-remaining": "", + "anthropic-ratelimit-output-tokens-remaining": "", + "anthropic-ratelimit-input-tokens-limit": "", + "anthropic-ratelimit-output-tokens-reset": "", + "connection": "", + "anthropic-ratelimit-requests-reset": "", + "anthropic-ratelimit-requests-limit": "", + "anthropic-ratelimit-tokens-limit": "", + "cf-ray": "", + "date": "", + "server": "", + "cf-cache-status": "", + "content-length": "", + "anthropic-ratelimit-requests-remaining": "", + "anthropic-ratelimit-tokens-remaining": "", + "request-id": "", + "x-robots-tag": "" + }, + "body": { + "content": [ + { + "text": "\\n\\nThere are 3 \"r\"s in the word \"strawberry\".", + "type": "text" + } + ], + "id": "", + "model": "claude-3-5-sonnet-20241022", + "role": "assistant", + "stop_reason": "end_turn", + "stop_sequence": null, + "type": "message", + "usage": { + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "input_tokens": 1490, + "output_tokens": 20 + } + } + }, + "combined_usage": { + "total_cost": "$0.008", + "deepseek_usage": { + "input_tokens": 18, + "output_tokens": 1423, + "reasoning_tokens": 1343, + "cached_input_tokens": 0, + "total_tokens": 1441, + "total_cost": "$0.003" + }, + "anthropic_usage": { + "input_tokens": 1490, + "output_tokens": 20, + "cached_write_tokens": 0, + "cached_read_tokens": 0, + "total_tokens": 1510, + "total_cost": "$0.005" + } + } +}` + } +} + +// Code block component with copy button +function CodeBlock({ code, language = "bash" }: { code: string; language?: string }) { + return ( +
+ +
+ + {code} + +
+
+ ) +} + +// Side by side code blocks component +function CodeBlockPair({ curl, response }: { curl: string; response: string }) { + return ( +
+ + + + + + +
+ ) +} + +export default function DocsPage() { + return ( +
+ {/* Hero Section */} +
+ {/* Background Pattern */} +
+ + {/* Gradient Overlay */} +
+ + {/* Content */} +
+
+ DeepClaude Logo +
+ +

+ DeepClaude API Documentation +

+ +

+ Integrate DeepClaude's powerful AI capabilities into your applications with a simple and flexible unified API. +

+
+
+ + {/* Base Information */} +
+
+ {/* Base URL */} +
+

Base URL

+
+
+
+
+ + + +
+

Managed API

+
+

+ Use our managed API endpoint for the quickest setup: +

+
+ https://api.deepclaude.com/ +
+
+
+
+
+ + + +
+

Self-hosted

+
+

+ For self-hosted deployments, configure your endpoint in config.toml: +

+
+ http://{'{host}'}:{'{port}'}/ +
+
+

Default Configuration:

+
    +
  • + host: 127.0.0.1 +
  • +
  • + port: 3000 +
  • +
+
+
+
+
+ + {/* Authentication */} +
+

Authentication

+
+
+
+ + + +
+
+

+ The API requires authentication tokens for both DeepSeek and Anthropic services. + Both tokens must be included in the request headers: +

+
+
+

X-DeepSeek-API-Token

+

Your DeepSeek API authentication token

+
+
+

X-Anthropic-API-Token

+

Your Anthropic API authentication token

+
+
+
+
+
+
+ + {/* Error Responses */} +
+

Error Handling

+
+
+
+ + + +
+
+

+ When an error occurs, the API returns an appropriate HTTP status code and a JSON response body with error details: +

+
+
+{`{
+  "error": {
+    "message": string,    // Human-readable error message
+    "type": string,       // Error type identifier
+    "param": string?,     // Optional parameter that caused the error
+    "code": string?       // Optional error code
+  }
+}`}
+                    
+
+
+
+ +
+

Common Error Types

+
+ {[ + { code: 400, type: 'bad_request', desc: 'Invalid request parameters or format' }, + { code: 400, type: 'missing_header', desc: 'Required API token header is missing' }, + { code: 400, type: 'invalid_system_prompt', desc: 'System prompt provided in both root and messages' }, + { code: 502, type: 'deepseek_error', desc: 'Error from DeepSeek API' }, + { code: 502, type: 'anthropic_error', desc: 'Error from Anthropic API' }, + { code: 500, type: 'internal_error', desc: 'Internal server error' } + ].map((error, i) => ( +
+
+ + {error.code} + + + {error.type} + +
+

+ {error.desc} +

+
+ ))} +
+
+
+
+
+
+ + {/* API Usage Examples */} +
+
+ {/* Non-streaming Section */} +
+

Non-streaming API Usage

+

+ Make a single request and receive a complete response. This is ideal for simple queries where you don't need real-time updates. +

+ +
+ + {/* Streaming Section */} +
+

Streaming API Usage

+
+

+ Receive responses in real-time as they're generated. Perfect for chat interfaces and live interactions. +

+

+ The streaming functionality is powered by Server-sent Events (SSE), enabling efficient real-time data transmission from server to client.{" "} + + Learn more about SSE → + +

+
+ +
+ + {/* Verbose Mode Section */} +
+

API Verbose Mode

+

+ Get detailed information about the API calls, including raw responses from both DeepSeek and Claude APIs. +

+ +
+
+
+
+ ) +} diff --git a/frontend/app/favicon.ico b/frontend/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/frontend/app/globals.css b/frontend/app/globals.css new file mode 100644 index 0000000..90904bf --- /dev/null +++ b/frontend/app/globals.css @@ -0,0 +1,260 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; + --radius: 0.5rem; + } + + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +@layer utilities { + /* Base grid pattern */ + .bg-dots-base { + background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h40v40H0z' fill='none'/%3E%3Cpath d='M0 0h1v1H0z' fill='rgb(255 255 255 / 0.15)'/%3E%3C/svg%3E"); + } + + /* Diagonal lines pattern */ + .bg-lines { + background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0l60 60M30 0l30 60M0 30l30 30' stroke='rgb(255 255 255 / 0.1)' stroke-width='1' fill='none'/%3E%3C/svg%3E"); + } + + /* Small dots pattern */ + .bg-dots-small { + background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='10' cy='10' r='1' fill='rgb(255 255 255 / 0.12)'/%3E%3C/svg%3E"); + transform: rotate(30deg); + } + + /* Combined pattern class */ + .bg-pattern-combined { + @apply bg-dots-base bg-lines bg-dots-small; + background-size: 40px 40px, 60px 60px, 20px 20px; + } + + .animate-float { + animation: float 6s ease-in-out infinite; + } +} + +@keyframes float { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(-20px); + } + 100% { + transform: translateY(0px); + } +} + +/* Markdown Styles */ +.prose pre { + @apply bg-secondary text-secondary-foreground p-4 rounded-lg overflow-x-auto; +} + +.prose code { + @apply bg-secondary text-secondary-foreground px-1.5 py-0.5 rounded; +} + +.prose pre code { + @apply bg-transparent p-0 text-sm; +} + +.prose img { + @apply rounded-lg; +} + +.prose a { + @apply text-primary underline-offset-4 hover:text-primary/80; +} + +.prose blockquote { + @apply border-l-4 border-primary/20 pl-4 italic; +} + +.prose ul { + @apply list-disc list-outside; +} + +.prose ol { + @apply list-decimal list-outside; +} + +.prose h1, .prose h2, .prose h3, .prose h4 { + @apply font-semibold text-foreground scroll-m-20; +} + +.prose h1 { + @apply text-3xl lg:text-4xl; +} + +.prose h2 { + @apply text-2xl lg:text-3xl; +} + +.prose h3 { + @apply text-xl lg:text-2xl; +} + +.prose h4 { + @apply text-lg lg:text-xl; +} + +/* Streaming text animation */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(1px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-stream { + animation: fadeIn 0.15s ease-out forwards; +} + +/* Sidebar Transitions */ +@media (max-width: 1024px) { + .sidebar-open { + overflow: hidden; + } +} + +/* Custom scrollbar for dark mode */ +/* Custom scrollbar for dark mode */ +.dark ::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.dark ::-webkit-scrollbar-track { + @apply bg-background; +} + +.dark ::-webkit-scrollbar-thumb { + @apply bg-muted rounded-full; +} + +.dark ::-webkit-scrollbar-thumb:hover { + @apply bg-muted-foreground; +} + +/* Sidebar scrollbar styling */ +.dark .sidebar-scroll::-webkit-scrollbar { + width: 2px; +} + +.dark .sidebar-scroll::-webkit-scrollbar-track { + @apply bg-background; +} + +.dark .sidebar-scroll::-webkit-scrollbar-thumb { + @apply bg-muted/30 rounded-full hover:bg-muted/50 transition-colors; +} + +/* Smooth transitions */ +.message-transition { + transition: opacity 0.2s ease-in-out, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); + opacity: 0; + transform: translateY(4px); +} + +.message-transition[data-loaded="true"] { + opacity: 1; + transform: translateY(0); +} + +.virtual-item-transition { + transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); + will-change: transform; +} + +/* Content fade transitions */ +.content-fade { + opacity: 0; + transition: opacity 0.15s ease-in-out; +} + +.content-fade[data-streaming="true"] { + opacity: 1; +} + +/* Initial state for all content */ +.message-content { + opacity: 1; +} + +/* Add this to your existing globals.css */ +@keyframes shimmer { + 0% { + background-position: -1000px 0; + } + 100% { + background-position: 1000px 0; + } +} + +.shimmer { + background: linear-gradient( + 90deg, + rgba(var(--primary) / 0.1) 0%, + rgba(var(--primary) / 0.15) 50%, + rgba(var(--primary) / 0.1) 100% + ); + background-size: 1000px 100%; + animation: shimmer 8s linear infinite; +} diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx new file mode 100644 index 0000000..cf77f57 --- /dev/null +++ b/frontend/app/layout.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from "next" +import { Inter } from "next/font/google" +import { Toaster } from "../components/ui/toaster" +import { PostHogProvider } from "../providers/posthog" +import "./globals.css" + +const inter = Inter({ subsets: ["latin"] }) + +export const metadata: Metadata = { + title: "DeepClaude", + icons: { + icon: "/deepclaude.ico" + }, +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + + {children} + + + + + ) +} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx new file mode 100644 index 0000000..a1128a2 --- /dev/null +++ b/frontend/app/page.tsx @@ -0,0 +1,430 @@ +"use client" + +import Image from "next/image" +import Link from "next/link" +import { usePostHog } from "../providers/posthog" +import { Button } from "../components/ui/button" +import { Card } from "../components/ui/card" +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../components/ui/collapsible" +import { ArrowRight, Zap, Lock, Settings2, Code2, Sparkles, Github, Megaphone, ChevronDown } from "lucide-react" + +export default function LandingPage() { + const posthog = usePostHog() + return ( +
+ {/* Hero Section */} +
+ {/* Background Pattern */} +
+ + {/* Gradient Overlay */} +
+ + {/* Announcement Banner */} +
+
+
+ + + We solved AI Code Comprehension, launching the SDK and Platform soon. Follow{' '} + + Asterisk + {' '} + on{' '} + + + + + {' '} + to catch the launch! + +
+
+ + {/* Content */} +
+
+ DeepClaude Logo +
+ +

+ DeepClaude +

+ +

+ Harness the power of DeepSeek R1's reasoning and Claude's creativity and code generation capabilities with a unified API and chat interface. +

+ +
+ { + posthog.capture('cta_click', { + location: 'hero', + target: 'chat', + timestamp: new Date().toISOString() + }) + }}> + + + { + posthog.capture('cta_click', { + location: 'hero', + target: 'docs', + timestamp: new Date().toISOString() + }) + }}> + + + { + posthog.capture('cta_click', { + location: 'hero', + target: 'github', + timestamp: new Date().toISOString() + }) + }} + > + + +
+ +
+ A free and open-source gift from + + Asterisk Logo + +
+
+ + {/* Scroll Indicator */} + +
+ +
+ {/* Features Grid */} +
+ {/* Background Pattern */} +
+
+

+ Features +

+ +
+ {/* Performance */} + { + posthog.capture('feature_view', { + feature: 'zero_latency', + timestamp: new Date().toISOString() + }) + }} + > +
+
+ +
+

Zero Latency

+

+ Instant responses of R1s CoT followed with Claude's response in a single stream powered by a high-performance streaming API written in Rust. +

+
+
+ + {/* Security */} + +
+
+ +
+

Private & Secure

+

+ Your data stays private with end-to-end security and local API key management. +

+
+
+ + {/* Configuration */} + +
+
+ +
+

Highly Configurable

+

+ Customize every aspect of the API and interface to match your needs. +

+
+
+ + {/* Open Source */} + +
+
+ +
+

Open Source

+

+ Free and open-source codebase. Contribute, modify, and deploy as you wish. +

+
+
+ + {/* AI Integration */} + +
+
+ +
+

Dual AI Power

+

+ Combine DeepSeek R1's reasoning with Claude's creativity and code generation. +

+
+
+ + {/* Managed BYOK API */} + +
+
+ + + +
+

Managed BYOK API

+

+ Use your own API keys with our managed infrastructure for complete control and flexibility. +

+
+
+
+
+
+ + {/* FAQ Section */} +
+
+ {/* Background Pattern */} +
+
+
+
+

+ Frequently Asked Questions +

+ +
+ {/* Why R1 + Claude? */} + + + { + posthog.capture('faq_interaction', { + question: 'why_r1_claude', + timestamp: new Date().toISOString() + }) + }} + > +
+

Why R1 + Claude?

+ +
+
+ +

+ DeepSeek R1's CoT trace demonstrates deep reasoning to the point of an LLM experiencing "metacognition" - correcting itself, thinking about edge cases, and so on. It's a quasi MCTS in natural language. +

+

+ But R1 lacks at code generation, creativity, and conversational skills. The model that excels at all 3 is the Claude 3.5 Sonnet New from Anthropic. So how about we combine both of them? And take the best of both worlds? Enter DeepClaude! +

+

+ With DeepClaude, you get a fast streaming R1 CoT + Claude Models in a single API call with your own API keys. +

+
+
+
+ + {/* How good is it? */} + + + +
+

How good is it?

+ +
+
+ +

+ "R1 as architect with Sonnet as editor has set a new SOTA of 64.0% on the aider polyglot benchmark. They achieve this at 14X less cost compared to the previous o1 SOTA result." - Aider Polyglot Benchmarks. +

+
+ R1 + Sonnet Benchmarks +
+
+
+
+ + {/* Is it free? */} + + + +
+

The managed API is free?

+ +
+
+ +

+ Yes, 100% free and you use your own keys. The API wraps both DeepSeek and Anthropic streaming API into one. And you get some niceties like calculating the combined usage and price for you to use. We keep no logs and it's completely open-source - you can self-host it, modify it, redistribute it, whatever. +

+

+ Feel free to use this at scale, we use this in production at Asterisk serving millions of tokens in parallel daily and it hasn't failed us "yet". And like all nice things, just don't abuse it. +

+
+
+
+
+
+
+ + {/* CTA Section */} +
+
+ {/* Background Pattern */} +
+
+
+
+

+ Start reading some AI internal monologue? +

+

+ No sign up. No credit card. No data stored. +

+
+ { + posthog.capture('cta_click', { + location: 'footer', + target: 'chat', + timestamp: new Date().toISOString() + }) + }}> + + + { + posthog.capture('cta_click', { + location: 'footer', + target: 'docs', + timestamp: new Date().toISOString() + }) + }}> + + +
+
+
+
+ + {/* Footer */} +
+
+
+ A "for fun" project by + + Asterisk Logo + +
+
+
+
+ ) +} diff --git a/frontend/bun.lockb b/frontend/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..f9ab1e69313e26beab2ff8beb4a37c5540586b00 GIT binary patch literal 245700 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 z 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 literal 0 HcmV?d00001 diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..a312865 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/frontend/components/chat.tsx b/frontend/components/chat.tsx new file mode 100644 index 0000000..595b94c --- /dev/null +++ b/frontend/components/chat.tsx @@ -0,0 +1,886 @@ +"use client" + +import * as React from "react" +import { useState, useEffect, useRef, useMemo, useCallback } from "react" +import Image from "next/image" +import { useVirtualizer } from "@tanstack/react-virtual" +import ReactMarkdown from "react-markdown" +import remarkGfm from "remark-gfm" +import rehypeRaw from "rehype-raw" +import rehypeSanitize from "rehype-sanitize" +import { Card } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Textarea } from "@/components/ui/textarea" +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Dialog, DialogContent, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog" +import { ChevronRight, ChevronDown, Loader2, Settings2, PlusCircle, Trash2, Github } from "lucide-react" +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" +import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism" +import { CopyButton } from "@/components/ui/copy-button" +import { MarkdownEditor } from "@/components/ui/markdown-editor" +import { usePostHog } from "../providers/posthog" + +interface Message { + role: "user" | "assistant" + content: string + thinking?: string +} + +interface StoredChat { + id: string + title: string + timestamp: string + messages: Message[] +} + +interface ChatProps { + selectedModel: string + onModelChange: (model: string) => void + apiTokens: { + deepseekApiToken: string + anthropicApiToken: string + } +} + +export function Chat({ selectedModel, onModelChange, apiTokens }: ChatProps) { + const posthog = usePostHog() + const [messages, setMessages] = useState([]) + const [input, setInput] = useState("") + const [isLoading, setIsLoading] = useState(false) + const [openThinking, setOpenThinking] = useState(null) + const [currentModel, setCurrentModel] = useState(selectedModel) + const parentRef = useRef(null) + const [isAutoScrollEnabled, setIsAutoScrollEnabled] = useState(true) + const [isScrolling, setIsScrolling] = useState(false) + const [chats, setChats] = useState([]) + const [currentChatId, setCurrentChatId] = useState(null) + const [isSidebarOpen, setIsSidebarOpen] = useState(true) + const [chatToDelete, setChatToDelete] = useState(null) + const [showClearConfirm, setShowClearConfirm] = useState(false) + const [thinkingStartTime, setThinkingStartTime] = useState(null) + const [elapsedTime, setElapsedTime] = useState(0) + const [isThinkingComplete, setIsThinkingComplete] = useState(false) + + // Format elapsed time into human readable string + const formatElapsedTime = (seconds: number): string => { + const minutes = Math.floor(seconds / 60) + const remainingSeconds = seconds % 60 + + if (minutes === 0) { + return `${remainingSeconds} seconds` + } + return `${minutes} minute${minutes > 1 ? 's' : ''} ${remainingSeconds} seconds` + } + + // Track elapsed time during thinking + useEffect(() => { + if (isLoading && !isThinkingComplete) { + if (!thinkingStartTime) { + setThinkingStartTime(Date.now()) + } + + const interval = setInterval(() => { + if (thinkingStartTime) { + setElapsedTime(Math.floor((Date.now() - thinkingStartTime) / 1000)) + } + }, 1000) + + return () => clearInterval(interval) + } + }, [isLoading, thinkingStartTime, isThinkingComplete]) + + // Load chats from localStorage on mount and create new chat + useEffect(() => { + const storedChats = localStorage.getItem('deepclaude-chats') + if (storedChats) { + const parsedChats = JSON.parse(storedChats) + setChats(parsedChats) + } + // Always create a new chat on mount + createNewChat() + }, []) + + // Save chats to localStorage whenever they change + useEffect(() => { + if (chats.length > 0) { + localStorage.setItem('deepclaude-chats', JSON.stringify(chats)) + } + }, [chats]) + + // Generate chat title from first message + const generateChatTitle = (firstMessage: string): string => { + return firstMessage.slice(0, 20) + } + + // Delete a chat + const deleteChat = (chatId: string) => { + posthog.capture('chat_deleted', { + chat_id: chatId, + timestamp: new Date().toISOString() + }) + setChats(prev => prev.filter(chat => chat.id !== chatId)) + if (currentChatId === chatId) { + setCurrentChatId(null) + setMessages([]) + } + setChatToDelete(null) + } + + // Clear all chats + const clearAllChats = () => { + posthog.capture('chats_cleared', { + chats_count: chats.length, + timestamp: new Date().toISOString() + }) + setChats([]) + setCurrentChatId(null) + setMessages([]) + localStorage.removeItem('deepclaude-chats') + setShowClearConfirm(false) + } + + // Generate UUID v4 + const generateUUID = () => { + // Fallback UUID generator for older browsers + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0 + const v = c === 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) + } + + // Create a new chat + const createNewChat = () => { + const chatId = typeof crypto.randomUUID === 'function' + ? crypto.randomUUID() + : generateUUID() + const newChat: StoredChat = { + id: chatId, + title: 'New Chat', + timestamp: new Date().toISOString(), + messages: [] + } + setChats(prev => [...prev, newChat]) + setCurrentChatId(chatId) + setMessages([]) + + posthog.capture('chat_created', { + chat_id: chatId, + timestamp: new Date().toISOString() + }) + } + + // Update current chat + const updateCurrentChat = useCallback(() => { + if (currentChatId && messages.length > 0) { + setChats(prev => { + const updatedChats = prev.map(chat => { + if (chat.id === currentChatId) { + return { + ...chat, + messages, + title: chat.messages.length === 0 && messages[0] ? + generateChatTitle(messages[0].content) : + chat.title + } + } + return chat + }) + return updatedChats + }) + } + }, [currentChatId, messages]) + + // Update chat whenever messages change + useEffect(() => { + updateCurrentChat() + }, [messages, updateCurrentChat]) + + // Ref for current message + const currentMessageRef = useRef(null) + const scrollRef = useRef(null) + + // Track model changes + useEffect(() => { + if (currentModel !== selectedModel) { + posthog.capture('model_changed', { + from_model: currentModel, + to_model: selectedModel, + chat_id: currentChatId, + timestamp: new Date().toISOString() + }) + setCurrentModel(selectedModel) + } + }, [selectedModel, currentModel, currentChatId, posthog]) + + // Memoized renderers for code blocks + const renderers = useMemo(() => { + const CodeRenderer = React.memo(({ node, inline, className, children, ...props }: any) => { + const match = /language-(\w+)/.exec(className || "") + const language = match ? match[1] : "text" + const content = String(children).replace(/\n$/, "") + + // Check if it's a code block (has language or multiple lines) + const isCodeBlock = match || content.includes("\n") + + if (!inline && isCodeBlock) { + return ( +

+ {!isLoading && } + + {content} + +
+ ) + } + + // For inline code or single backticks + return ( + + {children} + + ) + }) + CodeRenderer.displayName = 'CodeRenderer' + + return { + code: CodeRenderer + } + }, [isLoading]) + + // Optimized virtual list with dynamic sizing and performance tweaks + const rowVirtualizer = useVirtualizer({ + count: messages.length, + getScrollElement: () => document.getElementById('chat-container'), + estimateSize: useCallback(() => 100, []), // Lower initial estimate for faster first render + overscan: 2, // Reduced overscan for better performance + paddingStart: 20, // Add padding for smoother scrolling + paddingEnd: 20, + scrollPaddingStart: 20, // Additional scroll padding for smoother experience + scrollPaddingEnd: 20 + }) + + // RAF-based scroll handler + const handleScroll = useCallback(() => { + const container = document.getElementById('chat-container') + if (!container || isScrolling) return + + const { scrollTop, scrollHeight, clientHeight } = container + const isAtBottom = scrollHeight - (scrollTop + clientHeight) < 50 + setIsAutoScrollEnabled(isAtBottom) + }, [isScrolling]) + + // Immediate scroll to bottom + const scrollToBottom = useCallback(() => { + const container = document.getElementById('chat-container') + if (!container) return + + if (scrollRef.current) { + cancelAnimationFrame(scrollRef.current) + } + + scrollRef.current = requestAnimationFrame(() => { + if (!container) return + container.scrollTo({ + top: container.scrollHeight, + behavior: "auto" + }) + scrollRef.current = null + }) + }, []) + + // Immediate auto-scroll on message updates + useEffect(() => { + if (isAutoScrollEnabled && messages.length > 0) { + scrollToBottom() + } + }, [messages, isAutoScrollEnabled, scrollToBottom]) + + // Scroll event listener with cleanup + useEffect(() => { + const container = document.getElementById('chat-container') + if (!container) return + + container.addEventListener("scroll", handleScroll, { passive: true }) + return () => { + container.removeEventListener("scroll", handleScroll) + } + }, [handleScroll]) + + // Memoized message renderer + const MessageContent = useMemo(() => { + const MemoizedMessageContent = React.memo(({ message, index }: { message: Message; index: number }) => { + if (message.role === "user") { + return ( +
+ + {message.content} + +
+ ) + } + + return ( + <> + {message.thinking && ( + setOpenThinking(open ? index : null)} + > +
+ + + + +
+ {message.thinking} +
+
+
+
+ )} +
+ {!isLoading && } + + {message.content} + +
+ + ) + }) + MemoizedMessageContent.displayName = 'MemoizedMessageContent' + return MemoizedMessageContent + }, [openThinking, renderers, isLoading, elapsedTime, isThinkingComplete]) + + const handleSubmit = async () => { + if (!input.trim() || isLoading) return + if (!apiTokens.deepseekApiToken || !apiTokens.anthropicApiToken) return + + // Track message sent + posthog.capture('message_sent', { + chat_id: currentChatId, + model: currentModel, + message_length: input.length, + has_code: input.includes('```'), + timestamp: new Date().toISOString() + }) + + // Create new chat if none exists + if (!currentChatId) { + const newChat: StoredChat = { + id: typeof crypto.randomUUID === 'function' + ? crypto.randomUUID() + : generateUUID(), + title: generateChatTitle(input), + timestamp: new Date().toISOString(), + messages: [] + } + setChats(prev => [...prev, newChat]) + setCurrentChatId(newChat.id) + } + + const userMessage: Message = { + role: "user", + content: input + } + + setMessages(prev => [...prev, userMessage]) + setInput("") + setIsLoading(true) + setThinkingStartTime(null) + setElapsedTime(0) + setIsThinkingComplete(false) + + const controller = new AbortController() + + try { + const response = await fetch("https://api.deepclaude.com", { + method: "POST", + signal: controller.signal, + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + "X-DeepSeek-API-Token": apiTokens.deepseekApiToken, + "X-Anthropic-API-Token": apiTokens.anthropicApiToken + }, + body: JSON.stringify({ + stream: true, + system: "You are a helpful AI assistant who excels at reasoning and responds in Markdown format. For code snippets, you wrap them in Markdown codeblocks with it's language specified.", + verbose: false, + messages: [...messages, { content: input, role: "user" }].map(msg => ({ + content: msg.content, + role: msg.role + })), + deepseek_config: { + headers: {}, + body: { temperature: 0 } + }, + anthropic_config: { + headers: { "anthropic-version": "2023-06-01" }, + body: { temperature: 0 } + } + }) + }) + + const reader = response.body?.getReader() + if (!reader) throw new Error("No reader available") + + // Initialize current message + currentMessageRef.current = { + role: "assistant", + content: "", + thinking: "" + } + + let isThinking = false + + const processLine = (line: string) => { + if (!line.trim() || !line.startsWith("data: ")) return + + const data = JSON.parse(line.slice(6)) + + if (data.type === "content") { + for (const content of data.content) { + if (!currentMessageRef.current) return + + if (content.type === "text") { + if (content.text.startsWith("")) { + isThinking = true + } else if (content.text.endsWith("")) { + isThinking = false + setIsThinkingComplete(true) + } else { + if (isThinking) { + currentMessageRef.current.thinking += content.text + } else { + currentMessageRef.current.content += content.text + } + + // Update message immediately with optimized state update + setMessages(prev => { + // Avoid unnecessary array operations if content hasn't changed + const lastMessage = prev[prev.length - 1] + if (lastMessage?.role === "assistant" && + lastMessage.content === currentMessageRef.current!.content && + lastMessage.thinking === currentMessageRef.current!.thinking) { + return prev + } + + // Create new array only when content has changed + if (lastMessage?.role === "assistant") { + const newMessages = [...prev] + newMessages[newMessages.length - 1] = { ...currentMessageRef.current! } + return newMessages + } + return [...prev, { ...currentMessageRef.current! }] + }) + } + } else if (content.type === "text_delta") { + if (isThinking) { + currentMessageRef.current.thinking += content.text + } else { + currentMessageRef.current.content += content.text + } + + // Update message immediately with optimized state update + setMessages(prev => { + // Avoid unnecessary array operations if content hasn't changed + const lastMessage = prev[prev.length - 1] + if (lastMessage?.role === "assistant" && + lastMessage.content === currentMessageRef.current!.content && + lastMessage.thinking === currentMessageRef.current!.thinking) { + return prev + } + + // Create new array only when content has changed + if (lastMessage?.role === "assistant") { + const newMessages = [...prev] + newMessages[newMessages.length - 1] = { ...currentMessageRef.current! } + return newMessages + } + return [...prev, { ...currentMessageRef.current! }] + }) + } + } + } + } + + while (true) { + const { done, value } = await reader.read() + if (done) break + + const chunk = new TextDecoder().decode(value) + const lines = chunk.split("\n") + + for (const line of lines) { + processLine(line) + } + } + } catch (error) { + console.error("Error:", error) + // Track error + posthog.capture('chat_error', { + chat_id: currentChatId, + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString() + }) + } finally { + setIsLoading(false) + // Clean up + if (scrollRef.current) { + cancelAnimationFrame(scrollRef.current) + } + } + + return () => { + controller.abort() + if (scrollRef.current) { + cancelAnimationFrame(scrollRef.current) + } + } + } + + const hasApiTokens = apiTokens.deepseekApiToken && apiTokens.anthropicApiToken + + return ( +
+ {/* Delete Chat Confirmation Dialog */} + setChatToDelete(null)}> + + Delete Chat + + Are you sure you want to delete this chat? This action cannot be undone. + + + + + + + + + {/* Clear All Chats Confirmation Dialog */} + + + Clear All Chats + + Are you sure you want to clear all chats? This action cannot be undone. + + + + + + + + + {/* Backdrop */} + {isSidebarOpen && ( +
setIsSidebarOpen(false)} + /> + )} + + {/* Sidebar */} + + + {/* Toggle Sidebar Button */} + + + {/* Main Chat Area */} +
+
+
+
+ + +
+
+
+
0 ? `${rowVirtualizer.getTotalSize()}px` : '100%', + minHeight: '100%' + }} + > + {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const message = messages[virtualRow.index] + const index = virtualRow.index + return ( +
+
+
+
+ {message.role === "user" ? "You" : "Assistant"} +
+ +
+
+
+ ) + })} +
+
+
+
+ +
+
+
+
+
+ ) +} diff --git a/frontend/components/settings.tsx b/frontend/components/settings.tsx new file mode 100644 index 0000000..e4ba2b5 --- /dev/null +++ b/frontend/components/settings.tsx @@ -0,0 +1,366 @@ +"use client" + +import { useState, useEffect, useCallback, useMemo } from "react" +import { usePostHog } from "../providers/posthog" +import debounce from "lodash/debounce" +import { Settings2, RotateCcw, Save } from "lucide-react" +import { useToast } from "./ui/use-toast" +import { Button } from "./ui/button" +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" +import { Form, FormControl, FormField, FormItem, FormLabel } from "./ui/form" +import { Textarea } from "./ui/textarea" +import { Input } from "./ui/input" +import { useForm } from "react-hook-form" + +const models = [ + "claude-3-sonnet-20241022", + "claude-3-sonnet-latest", + "claude-3-haiku-20241022", + "claude-3-haiku-latest", + "claude-3-opus-20240229", + "claude-3-opus-latest" +] + +interface SettingsFormValues { + model: string + systemPrompt: string + deepseekApiToken: string + anthropicApiToken: string + deepseekHeaders: { key: string; value: string }[] + deepseekBody: { key: string; value: string }[] + anthropicHeaders: { key: string; value: string }[] + anthropicBody: { key: string; value: string }[] +} + +interface SettingsProps { + onModelChange: (model: string) => void + onSettingsChange: (settings: { deepseekApiToken: string; anthropicApiToken: string }) => void +} + +export function Settings({ onModelChange, onSettingsChange }: SettingsProps) { + const [open, setOpen] = useState(false) + const { toast } = useToast() + const posthog = usePostHog() + + const form = useForm({ + defaultValues: { + model: "claude-3-sonnet-20241022", + systemPrompt: "You are a helpful AI assistant who excels at reasoning and responds in Markdown format. For code snippets, you wrap them in Markdown codeblocks with it's language specified.", + deepseekApiToken: "", + anthropicApiToken: "", + deepseekHeaders: [], + deepseekBody: [], + anthropicHeaders: [{ key: "anthropic-version", value: "2023-06-01" }], + anthropicBody: [] + } + }) + + // Load settings from localStorage on mount + useEffect(() => { + const savedSettings = localStorage.getItem('deepclaude-settings') + if (savedSettings) { + const settings = JSON.parse(savedSettings) + form.reset(settings) + onModelChange(settings.model) + onSettingsChange({ + deepseekApiToken: settings.deepseekApiToken, + anthropicApiToken: settings.anthropicApiToken + }) + } + }, [form, onModelChange, onSettingsChange]) + + // Debounced save function + const debouncedSave = useCallback((data: SettingsFormValues) => { + localStorage.setItem('deepclaude-settings', JSON.stringify(data)) + onModelChange(data.model) + onSettingsChange({ + deepseekApiToken: data.deepseekApiToken, + anthropicApiToken: data.anthropicApiToken + }) + + // Track settings update + posthog.capture('settings_updated', { + model: data.model, + has_deepseek_token: !!data.deepseekApiToken, + has_anthropic_token: !!data.anthropicApiToken, + has_system_prompt: !!data.systemPrompt, + deepseek_headers_count: data.deepseekHeaders.length, + deepseek_body_count: data.deepseekBody.length, + anthropic_headers_count: data.anthropicHeaders.length, + anthropic_body_count: data.anthropicBody.length, + timestamp: new Date().toISOString() + }) + + toast({ + variant: "success", + description: "Settings saved to local storage", + duration: 2000, + }) + }, [onModelChange, onSettingsChange, toast, posthog]) + + const debouncedSaveCallback = useMemo( + () => debounce(debouncedSave, 1000), + [debouncedSave] + ) + + // Auto-save on form changes + useEffect(() => { + const subscription = form.watch((value) => { + const data = form.getValues() + debouncedSaveCallback(data) + }) + return () => { + subscription.unsubscribe() + debouncedSaveCallback.cancel() + } + }, [form, debouncedSaveCallback]) + + const handleReset = () => { + form.reset({ + model: "claude-3-sonnet-20241022", + systemPrompt: "You are a helpful AI assistant who excels at reasoning and responds in Markdown format. For code snippets, you wrap them in Markdown codeblocks with it's language specified.", + deepseekApiToken: "", + anthropicApiToken: "", + deepseekHeaders: [], + deepseekBody: [], + anthropicHeaders: [{ key: "anthropic-version", value: "2023-06-01" }], + anthropicBody: [] + }) + localStorage.removeItem('deepclaude-settings') + onModelChange("claude-3-sonnet-20241022") + onSettingsChange({ + deepseekApiToken: "", + anthropicApiToken: "" + }) + + // Track settings reset + posthog.capture('settings_reset', { + timestamp: new Date().toISOString() + }) + + toast({ + description: "Settings reset to defaults", + duration: 2000, + }) + } + + const KeyValuePairFields = ({ + name, + label + }: { + name: "deepseekHeaders" | "deepseekBody" | "anthropicHeaders" | "anthropicBody" + label: string + }) => { + const pairs = form.watch(name) + + return ( + ( + + {label} +
+ {pairs.map((_, index) => ( +
+ + + +
+ ))} + +
+
+ )} + /> + ) + } + + return ( + + +
+ + {!form.getValues("deepseekApiToken") || !form.getValues("anthropicApiToken") ? ( +
+ Configure API tokens to start +
+ ) : null} +
+
+ + +
{/* Spacer for close button */} +
+ Settings +
+ + +
+
+ +
+ + ( + + Model + + + )} + /> + +
+ ( + + DeepSeek API Token + + + + + )} + /> + + ( + + Anthropic API Token + + + + + )} + /> +
+ + ( + + System Prompt + +