diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore index 9de0f16..9307332 100644 --- a/.codegraph/.gitignore +++ b/.codegraph/.gitignore @@ -1,16 +1,16 @@ -# CodeGraph data files -# These are local to each machine and should not be committed - -# Database -*.db -*.db-wal -*.db-shm - -# Cache -cache/ - -# Logs -*.log - -# Hook markers -.dirty +# Codelraph data files +# These are local to each machine and should not be committed + +# Database +*.db +*.db-wal +*.db-shm + +# Cache +cache/ + +# Logs +*.log + +# Hook markers +.dirty diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..d42cf68 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,3 @@ +reviews: + auto_review: + enabled: false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ed511aa..221fd69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "OxideChat" -version = "0.1.8" +version = "0.2.0" dependencies = [ "aes-gcm", "arc-swap", @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arbitrary" @@ -140,9 +140,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc-swap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" dependencies = [ "rustversion", ] @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "async-stream" @@ -554,9 +554,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", @@ -589,9 +589,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -1079,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1422,11 +1422,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1436,9 +1434,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -1602,9 +1602,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "typenum", ] @@ -1953,9 +1953,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -1994,9 +1994,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -2194,7 +2194,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2246,7 +2246,7 @@ dependencies = [ [[package]] name = "omniference" -version = "0.1.9" +version = "0.2.0" dependencies = [ "anyhow", "async-stream", @@ -2454,7 +2454,7 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.10.1", + "rand 0.10.2", "sha2 0.11.0", "stringprep", ] @@ -2611,14 +2611,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -2632,9 +2633,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", @@ -2678,25 +2679,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] [[package]] name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2713,16 +2704,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -2734,18 +2715,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] -name = "rand_core" -version = "0.10.1" +name = "rand_pcg" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] [[package]] name = "rand_xoshiro" @@ -2984,9 +2965,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustix" @@ -3011,7 +2992,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3041,9 +3022,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -3480,7 +3461,7 @@ dependencies = [ "log", "md-5", "memchr", - "rand 0.10.1", + "rand 0.10.2", "rust_decimal", "serde", "serde_json", @@ -3644,7 +3625,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3707,9 +3688,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.51" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", "num-conv", @@ -3727,9 +3708,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.30" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -4224,9 +4205,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.3" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -4304,9 +4285,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -4318,9 +4299,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.75" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -4328,9 +4309,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4338,9 +4319,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -4351,9 +4332,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -4737,9 +4718,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -4833,7 +4814,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 45052ec..21166cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,82 +1,82 @@ -[package] -name = "OxideChat" -version = "0.1.8" -edition = "2024" -description = "A modern, performance and on customization focused AI chat application" -license = "MIT" -repository = "https://github.com/NxtCore/OxideChat" -readme = "README.md" -keywords = ["chat", "axum", "websocket", "postgres"] -categories = ["web-programming"] - -[dependencies] -axum = { version = "0.8" } -tokio = { version = "1.48", features = ["full"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -sqlx = { version = "0.9", features = [ - "postgres", - "chrono", - "tls-native-tls", - "runtime-tokio", - "macros", - "migrate", - "uuid", - "rust_decimal", -] } -dotenv = { version = "0.15" } -argon2 = { version = "0.5" } -uuid = { version = "1.0", features = ["serde", "v4"] } -chrono = { version = "0.4", features = ["serde"] } -tower-cookies = { version = "0.11" } -arc-swap = "1.7.1" -oauth2 = "5" -reqwest = { version = "0.12", default-features = false, features = [ - "rustls-tls", - "json", - "multipart", -] } -tower-http = { version = "0.5", features = ["cors"] } -omniference = "0.1.8" -aes-gcm = "0.10" -rand = "0.8" -rust_decimal = { version = "1.39.0", features = ["db-postgres", "serde"] } -futures-util = "0.3.31" -async-stream = "0.3.6" -tokio-util = "0.7.17" -extism = "1" -sha2 = "0.10" -base64 = "0.22" -thiserror = "2" -async-trait = "0.1" -infer = "0.16" - -[lints.clippy] -correctness = { level = "deny", priority = -1 } -suspicious = { level = "deny", priority = -1 } -perf = { level = "warn", priority = -1 } -style = { level = "warn", priority = -1 } -complexity = { level = "warn", priority = -1 } -must_use_candidate = "warn" -missing_errors_doc = "warn" -missing_panics_doc = "warn" -doc_markdown = "warn" -explicit_iter_loop = "warn" -explicit_into_iter_loop = "warn" -inconsistent_struct_constructor = "warn" -manual_let_else = "warn" -redundant_closure_for_method_calls = "warn" -semicolon_if_nothing_returned = "warn" -unnested_or_patterns = "warn" -unused_self = "warn" -cargo = { level = "warn", priority = -1 } -future_not_send = "warn" -fallible_impl_from = "warn" -dbg_macro = "warn" -todo = "warn" -unimplemented = "warn" -multiple_crate_versions = "allow" - -[lints.rust] -unsafe_code = "warn" -missing_docs = "allow" +[package] +name = "OxideChat" +version = "0.2.0" +edition = "2024" +description = "A modern, performance and on customization focused AI chat application" +license = "MIT" +repository = "https://github.com/NxtCore/OxideChat" +readme = "README.md" +keywords = ["chat", "axum", "websocket", "postgres"] +categories = ["web-programming"] + +[dependencies] +axum = { version = "0.8" } +tokio = { version = "1.48", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sqlx = { version = "0.9", features = [ + "postgres", + "chrono", + "tls-native-tls", + "runtime-tokio", + "macros", + "migrate", + "uuid", + "rust_decimal", +] } +dotenv = { version = "0.15" } +argon2 = { version = "0.5" } +uuid = { version = "1.0", features = ["serde", "v4"] } +chrono = { version = "0.4", features = ["serde"] } +tower-cookies = { version = "0.11" } +arc-swap = "1.7.1" +oauth2 = "5" +reqwest = { version = "0.12", default-features = false, features = [ + "rustls-tls", + "json", + "multipart", +] } +tower-http = { version = "0.5", features = ["cors"] } +omniference = "0.2" +aes-gcm = "0.10" +rand = "0.8" +rust_decimal = { version = "1.39.0", features = ["db-postgres", "serde"] } +futures-util = "0.3.31" +async-stream = "0.3.6" +tokio-util = "0.7.17" +extism = "1" +sha2 = "0.10" +base64 = "0.22" +thiserror = "2" +async-trait = "0.1" +infer = "0.16" + +[lints.clippy] +correctness = { level = "deny", priority = -1 } +suspicious = { level = "deny", priority = -1 } +perf = { level = "warn", priority = -1 } +style = { level = "warn", priority = -1 } +complexity = { level = "warn", priority = -1 } +must_use_candidate = "warn" +missing_errors_doc = "warn" +missing_panics_doc = "warn" +doc_markdown = "warn" +explicit_iter_loop = "warn" +explicit_into_iter_loop = "warn" +inconsistent_struct_constructor = "warn" +manual_let_else = "warn" +redundant_closure_for_method_calls = "warn" +semicolon_if_nothing_returned = "warn" +unnested_or_patterns = "warn" +unused_self = "warn" +cargo = { level = "warn", priority = -1 } +future_not_send = "warn" +fallible_impl_from = "warn" +dbg_macro = "warn" +todo = "warn" +unimplemented = "warn" +multiple_crate_versions = "allow" + +[lints.rust] +unsafe_code = "warn" +missing_docs = "allow" diff --git a/Dockerfile b/Dockerfile index ca3796c..2044de0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN bun run build # ----------------------------------------------------------------------------- -FROM rust:1.96-slim AS chef +FROM rust:1.96-slim-bookworm AS chef RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config libssl-dev \ diff --git a/frontend/components/AppSidebar.vue b/frontend/components/AppSidebar.vue index 74ab08e..984d1bb 100644 --- a/frontend/components/AppSidebar.vue +++ b/frontend/components/AppSidebar.vue @@ -67,6 +67,25 @@ + + @@ -88,9 +107,11 @@ import {ChevronUp, LogOut, Settings, Settings2, Layers} from 'lucide-vue-next'; import {useMainStore} from '@/stores'; import {useChatStore} from '@/stores/chatStore'; +import {useBudgetStore} from '@/stores/budgetStore'; const store = useMainStore(); const chatStore = useChatStore(); +const budgetStore = useBudgetStore(); const router = useRouter(); const showWorkspaceManager = ref(false); @@ -108,4 +129,14 @@ async function handleLogout() { await store.logout(); router.push('/auth/login'); } + +function formatMoney(value: number) { + return `$${value.toFixed(2)}`; +} + +onMounted(() => { + if (store.auth.isAuthenticated) { + budgetStore.fetchMyBudget().catch(() => {}); + } +}); diff --git a/frontend/components/chat/ModelSelector.vue b/frontend/components/chat/ModelSelector.vue index ca59c65..7fee4ea 100644 --- a/frontend/components/chat/ModelSelector.vue +++ b/frontend/components/chat/ModelSelector.vue @@ -54,7 +54,8 @@
@@ -86,6 +87,9 @@

{{ model.model_id }}

+

+ {{ store.getTranslation('settings.budgets.reached') }} +

@@ -196,7 +200,7 @@ diff --git a/frontend/components/settings/AnalyticsDashboard.vue b/frontend/components/settings/AnalyticsDashboard.vue new file mode 100644 index 0000000..c01097b --- /dev/null +++ b/frontend/components/settings/AnalyticsDashboard.vue @@ -0,0 +1,1042 @@ + + + diff --git a/frontend/components/settings/AnalyticsDateFilter.vue b/frontend/components/settings/AnalyticsDateFilter.vue new file mode 100644 index 0000000..6588732 --- /dev/null +++ b/frontend/components/settings/AnalyticsDateFilter.vue @@ -0,0 +1,152 @@ + + + diff --git a/frontend/components/settings/HorizontalAnalyticsChart.vue b/frontend/components/settings/HorizontalAnalyticsChart.vue new file mode 100644 index 0000000..dfeb73e --- /dev/null +++ b/frontend/components/settings/HorizontalAnalyticsChart.vue @@ -0,0 +1,159 @@ + + + diff --git a/frontend/components/settings/StackedAnalyticsChart.vue b/frontend/components/settings/StackedAnalyticsChart.vue new file mode 100644 index 0000000..fadc14e --- /dev/null +++ b/frontend/components/settings/StackedAnalyticsChart.vue @@ -0,0 +1,292 @@ + + + diff --git a/frontend/pages/settings.vue b/frontend/pages/settings.vue index 08460e5..171353d 100644 --- a/frontend/pages/settings.vue +++ b/frontend/pages/settings.vue @@ -45,7 +45,7 @@ diff --git a/frontend/pages/settings/budgets.vue b/frontend/pages/settings/budgets.vue new file mode 100644 index 0000000..ee13db5 --- /dev/null +++ b/frontend/pages/settings/budgets.vue @@ -0,0 +1,653 @@ + + + diff --git a/frontend/pages/settings/models/[id].vue b/frontend/pages/settings/models/[id].vue index 8ed0960..d104128 100644 --- a/frontend/pages/settings/models/[id].vue +++ b/frontend/pages/settings/models/[id].vue @@ -837,15 +837,61 @@
-
+
+
+
+

{{ store.getTranslation('settings.models.editor.pricing') }}

+

{{ store.getTranslation('settings.models.editor.pricing_desc') }}

+
+ + + +
+
+
+

{{ store.getTranslation('settings.models.editor.reported_price') }}

+

{{ formatPricingPair(pricing?.reported_input, pricing?.reported_output) }}

+
+
+

{{ store.getTranslation('settings.models.editor.effective_price') }}

+

{{ formatPricingPair(pricing?.effective_input, pricing?.effective_output) }}

+
+
+ {{ store.getTranslation('settings.models.editor.override_input') }} + +
+
+ {{ store.getTranslation('settings.models.editor.override_output') }} + +
+
+ {{ store.getTranslation('settings.models.editor.override_reasoning') }} + +
+
+ {{ store.getTranslation('settings.models.editor.override_cache_read') }} + +
+
+ {{ store.getTranslation('settings.models.editor.override_cache_write') }} + +
+
+

{{ store.getTranslation('settings.models.editor.pricing_unit') }}

+
+ {{ store.getTranslation('common.save') }} +
+
+ +
{{ store.getTranslation('settings.models.editor.providers_user_unavailable') }}
-
+
{{ store.getTranslation('settings.models.editor.providers_empty') }}
-
+
@@ -940,6 +986,14 @@ const providerOptions = ref | null>(null); const providerOptionsLoading = ref(false); const providerOptionsLoaded = ref(false); const providerParentUnavailable = computed(() => providerOptions.value?.availability_state === 'USER_UNAVAILABLE'); +const pricing = ref | null>(null); +const pricingForm = reactive({ + input: '0', + output: '0', + reasoning: '', + cache_read: '', + cache_write: '', +}); type ProviderRowState = 'user_unavailable' | 'unavailable' | 'available' | 'unknown'; @@ -975,6 +1029,61 @@ function formatPrice(opt: Record): string { return `${fmt(opt.price_input)} / ${fmt(opt.price_output)}`; } +function formatPricingPair(input: string | number | null | undefined, output: string | number | null | undefined): string { + const fmt = (value: string | number | null | undefined) => (value == null ? '-' : '$' + Number(value).toFixed(6)); + return `${fmt(input)} / ${fmt(output)}`; +} + +async function loadPricing() { + try { + pricing.value = await $customFetch(`/api/v1/admin/models/${modelId}/pricing`); + const override = pricing.value?.override_pricing ?? null; + pricingForm.input = String(override?.input ?? pricing.value?.effective_input ?? 0); + pricingForm.output = String(override?.output ?? pricing.value?.effective_output ?? 0); + pricingForm.reasoning = override?.reasoning != null ? String(override.reasoning) : ''; + pricingForm.cache_read = override?.cache_read != null ? String(override.cache_read) : ''; + pricingForm.cache_write = override?.cache_write != null ? String(override.cache_write) : ''; + } catch (e) { + console.error(e); + } +} + +async function savePricingOverride() { + const optional = (value: string): number | undefined => { + const trimmed = value.trim(); + return trimmed === '' ? undefined : Number(trimmed); + }; + const isValidPrice = (v: number | undefined): boolean => v === undefined || (Number.isFinite(v) && v >= 0); + const input = Number(pricingForm.input || 0); + const output = Number(pricingForm.output || 0); + const reasoning = optional(pricingForm.reasoning); + const cacheRead = optional(pricingForm.cache_read); + const cacheWrite = optional(pricingForm.cache_write); + if (!isValidPrice(input) || !isValidPrice(output) || !isValidPrice(reasoning) || !isValidPrice(cacheRead) || !isValidPrice(cacheWrite)) { + store.toast(store.getTranslation('common.error'), {type: 'error'}); + return; + } + const overridePricing: Record = { + input, + output, + reasoning, + cache_read: cacheRead, + cache_write: cacheWrite, + tiers: [], + }; + pricing.value = await $customFetch(`/api/v1/admin/models/${modelId}/pricing`, { + method: 'PUT', + body: {pricing: overridePricing}, + }); + store.toast(store.getTranslation('settings.models.editor.pricing_saved'), {type: 'success'}); +} + +async function deletePricingOverride() { + await $customFetch(`/api/v1/admin/models/${modelId}/pricing`, {method: 'DELETE'}); + await loadPricing(); + store.toast(store.getTranslation('settings.models.editor.pricing_deleted'), {type: 'success'}); +} + async function loadProviderOptions(force = false) { providerOptionsLoading.value = true; try { @@ -1174,6 +1283,7 @@ function insertVariable(token: string) { onMounted(async () => { await loadModel(); + await loadPricing(); }); function populateForm(data: Record) { diff --git a/frontend/pages/settings/profile.vue b/frontend/pages/settings/profile.vue index 1500499..12b4ac9 100644 --- a/frontend/pages/settings/profile.vue +++ b/frontend/pages/settings/profile.vue @@ -1,52 +1,60 @@ @@ -55,6 +63,7 @@ import {Copy} from 'lucide-vue-next'; import {useMainStore} from '@/stores'; const store = useMainStore(); +const activeProfileTab = ref('profile'); const userInitials = computed(() => { const username = store.auth.user?.username || 'U'; diff --git a/frontend/pages/settings/teams.vue b/frontend/pages/settings/teams.vue index d35eb2e..b18d539 100644 --- a/frontend/pages/settings/teams.vue +++ b/frontend/pages/settings/teams.vue @@ -86,7 +86,6 @@ {{ store.getTranslation('settings.teams.members') }} {{ store.getTranslation('settings.teams.models') }} - {{ store.getTranslation('settings.teams.budget') }} @@ -223,35 +222,6 @@ - - - -
-
- -
-
-

{{ store.getTranslation('settings.teams.budget_link_title') }}

-

{{ store.getTranslation('settings.teams.budget_link_hint') }}

-
-
- -
-
-

{{ store.getTranslation('settings.teams.budget_linked') }}

-

{{ budgetId }}

-
- - {{ store.getTranslation('settings.teams.budget_unlink') }} - -
- -
-

{{ store.getTranslation('settings.teams.budget_not_linked') }}

-

{{ store.getTranslation('settings.teams.budget_none_available') }}

-
- -
@@ -314,7 +284,7 @@